1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright (c) 2018, Joyent, Inc. 14 */ 15 16 #include <assert.h> 17 #include <fcntl.h> 18 #include <fm/libtopo.h> 19 #include <fm/topo_mod.h> 20 #ifdef __x86 21 #include <sys/mc.h> 22 #endif 23 #include <sys/fm/protocol.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 typedef struct smb_enum_data { 28 topo_mod_t *sme_mod; 29 tnode_t *sme_pnode; 30 tnode_t *sme_slotnode; 31 topo_instance_t sme_slot_inst; 32 topo_instance_t sme_slot_maxinst; 33 smbios_info_t *sme_smb_info; 34 char *sme_slot_form; 35 } smb_enum_data_t; 36 37 /* 38 * This function serves two purposes. It filters out memory devices that 39 * don't have a formfactor that represents a reasonably modern DIMM-like 40 * device (and hence not a device we're interested in enumerating). It also 41 * converts the numeric SMBIOS type representation to a more generic TOPO dimm 42 * type. 43 * 44 * Caller must free the returned string. 45 */ 46 static char * 47 distill_dimm_form(topo_mod_t *mod, smbios_memdevice_t *smb_md) 48 { 49 switch (smb_md->smbmd_form) { 50 case (SMB_MDFF_DIMM): 51 return (topo_mod_strdup(mod, TOPO_DIMM_SLOT_FORM_DIMM)); 52 case (SMB_MDFF_SODIMM): 53 return (topo_mod_strdup(mod, TOPO_DIMM_SLOT_FORM_SODIMM)); 54 case (SMB_MDFF_FBDIMM): 55 return (topo_mod_strdup(mod, TOPO_DIMM_SLOT_FORM_FBDIMM)); 56 default: 57 topo_mod_dprintf(mod, "skipping device with form factor 0x%x", 58 smb_md->smbmd_form); 59 return (NULL); 60 } 61 } 62 63 static char * 64 smbios2topotype(topo_mod_t *mod, uint8_t type) 65 { 66 switch (type) { 67 case (SMB_MDT_DDR): 68 return (topo_mod_strdup(mod, TOPO_DIMM_TYPE_DDR)); 69 case (SMB_MDT_DDR2): 70 case (SMB_MDT_DDR2FBDIMM): 71 return (topo_mod_strdup(mod, TOPO_DIMM_TYPE_DDR2)); 72 case (SMB_MDT_DDR3): 73 return (topo_mod_strdup(mod, TOPO_DIMM_TYPE_DDR3)); 74 case (SMB_MDT_DDR4): 75 return (topo_mod_strdup(mod, TOPO_DIMM_TYPE_DDR4)); 76 case (SMB_MDT_LPDDR): 77 return (topo_mod_strdup(mod, TOPO_DIMM_TYPE_LPDDR)); 78 case (SMB_MDT_LPDDR2): 79 return (topo_mod_strdup(mod, TOPO_DIMM_TYPE_LPDDR2)); 80 case (SMB_MDT_LPDDR3): 81 return (topo_mod_strdup(mod, TOPO_DIMM_TYPE_LPDDR3)); 82 case (SMB_MDT_LPDDR4): 83 return (topo_mod_strdup(mod, TOPO_DIMM_TYPE_LPDDR4)); 84 default: 85 return (topo_mod_strdup(mod, TOPO_DIMM_TYPE_UNKNOWN)); 86 } 87 } 88 89 static boolean_t 90 is_valid_string(const char *str) 91 { 92 if (strcmp(str, SMB_DEFAULT1) != 0 && strcmp(str, SMB_DEFAULT2) != 0 && 93 strcmp(str, SMB_DEFAULT3) != 0 && strlen(str) > 0) 94 return (B_TRUE); 95 96 return (B_FALSE); 97 } 98 99 static tnode_t * 100 smbios_make_slot(smb_enum_data_t *smed, smbios_memdevice_t *smb_md) 101 { 102 nvlist_t *auth, *fmri; 103 tnode_t *slotnode; 104 topo_mod_t *mod = smed->sme_mod; 105 topo_pgroup_info_t pgi; 106 int err; 107 108 if ((auth = topo_mod_auth(mod, smed->sme_pnode)) == NULL) { 109 topo_mod_dprintf(mod, "topo_mod_auth() failed: %s", 110 topo_mod_errmsg(mod)); 111 /* errno set */ 112 return (NULL); 113 } 114 115 if ((fmri = topo_mod_hcfmri(mod, smed->sme_pnode, FM_HC_SCHEME_VERSION, 116 SLOT, smed->sme_slot_inst, NULL, auth, NULL, NULL, NULL)) == 117 NULL) { 118 nvlist_free(auth); 119 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 120 topo_mod_errmsg(mod)); 121 /* errno set */ 122 return (NULL); 123 } 124 if ((slotnode = topo_node_bind(mod, smed->sme_pnode, SLOT, 125 smed->sme_slot_inst, fmri)) == NULL) { 126 nvlist_free(auth); 127 nvlist_free(fmri); 128 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 129 topo_mod_errmsg(mod)); 130 /* errno set */ 131 return (NULL); 132 } 133 nvlist_free(fmri); 134 fmri = NULL; 135 136 /* Create authority and system pgroups */ 137 topo_pgroup_hcset(slotnode, auth); 138 nvlist_free(auth); 139 140 if (topo_node_label_set(slotnode, (char *)smb_md->smbmd_dloc, &err) != 141 0) { 142 topo_mod_dprintf(mod, "failed to set label on %s=%d: %s", 143 SLOT, smed->sme_slot_inst, topo_strerror(err)); 144 (void) topo_mod_seterrno(mod, err); 145 return (NULL); 146 } 147 if (topo_node_fru(smed->sme_pnode, &fmri, NULL, &err) != 0 || 148 topo_node_fru_set(slotnode, fmri, NULL, &err) != 0) { 149 topo_mod_dprintf(mod, "failed to set FRU on %s=%d: %s", SLOT, 150 smed->sme_slot_inst, topo_strerror(err)); 151 nvlist_free(fmri); 152 (void) topo_mod_seterrno(mod, err); 153 return (NULL); 154 } 155 nvlist_free(fmri); 156 157 pgi.tpi_name = TOPO_PGROUP_SLOT; 158 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 159 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 160 pgi.tpi_version = TOPO_VERSION; 161 if (topo_pgroup_create(slotnode, &pgi, &err) != 0 || 162 topo_prop_set_uint32(slotnode, TOPO_PGROUP_SLOT, 163 TOPO_PROP_SLOT_TYPE, TOPO_PROP_IMMUTABLE, TOPO_SLOT_TYPE_DIMM, 164 &err)) { 165 topo_mod_dprintf(mod, "failed to create slot properties: %s", 166 topo_strerror(err)); 167 (void) topo_mod_seterrno(mod, err); 168 return (NULL); 169 } 170 171 pgi.tpi_name = TOPO_PGROUP_DIMM_SLOT; 172 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 173 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 174 pgi.tpi_version = TOPO_VERSION; 175 if (topo_pgroup_create(slotnode, &pgi, &err) != 0 || 176 topo_prop_set_string(slotnode, TOPO_PGROUP_DIMM_SLOT, 177 TOPO_PROP_DIMM_SLOT_FORM, TOPO_PROP_IMMUTABLE, smed->sme_slot_form, 178 &err)) { 179 topo_mod_dprintf(mod, "failed to create slot properties: %s", 180 topo_strerror(err)); 181 (void) topo_mod_seterrno(mod, err); 182 return (NULL); 183 } 184 return (slotnode); 185 } 186 187 static tnode_t * 188 smbios_make_dimm(smb_enum_data_t *smed, smbios_memdevice_t *smb_md) 189 { 190 nvlist_t *auth, *fmri; 191 smbios_info_t *smb_info = smed->sme_smb_info; 192 tnode_t *slotnode = smed->sme_slotnode; 193 tnode_t *dimmnode, *ret = NULL; 194 topo_mod_t *mod = smed->sme_mod; 195 topo_pgroup_info_t pgi; 196 const char *part = NULL, *rev = NULL, *serial = NULL; 197 char *type, *manuf = NULL, *prod = NULL, *asset = NULL, *loc = NULL; 198 int err, rc = 0; 199 200 if ((auth = topo_mod_auth(mod, slotnode)) == NULL) { 201 topo_mod_dprintf(mod, "topo_mod_auth() failed: %s", 202 topo_mod_errmsg(mod)); 203 /* errno set */ 204 return (NULL); 205 } 206 207 if (smed->sme_smb_info != NULL) { 208 if (is_valid_string(smb_info->smbi_part) == B_TRUE) 209 part = smb_info->smbi_part; 210 if (is_valid_string(smb_info->smbi_version) == B_TRUE) 211 rev = smb_info->smbi_version; 212 if (is_valid_string(smb_info->smbi_serial) == B_TRUE) 213 serial = smb_info->smbi_serial; 214 if (is_valid_string(smb_info->smbi_manufacturer) == B_TRUE) 215 manuf = topo_mod_clean_str(mod, 216 smb_info->smbi_manufacturer); 217 if (is_valid_string(smb_info->smbi_product) == B_TRUE) 218 prod = topo_mod_clean_str(mod, smb_info->smbi_product); 219 if (is_valid_string(smb_info->smbi_asset) == B_TRUE) 220 asset = topo_mod_clean_str(mod, smb_info->smbi_asset); 221 if (is_valid_string(smb_info->smbi_location) == B_TRUE) 222 loc = topo_mod_clean_str(mod, smb_info->smbi_location); 223 } 224 225 if ((fmri = topo_mod_hcfmri(mod, slotnode, FM_HC_SCHEME_VERSION, 226 DIMM, 0, NULL, auth, part, rev, serial)) == NULL) { 227 nvlist_free(auth); 228 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 229 topo_mod_errmsg(mod)); 230 /* errno set */ 231 goto err; 232 } 233 234 if (topo_node_range_create(mod, slotnode, DIMM, 0, 0) < 0 || 235 (dimmnode = topo_node_bind(mod, slotnode, DIMM, 0, fmri)) == 236 NULL) { 237 nvlist_free(auth); 238 nvlist_free(fmri); 239 topo_mod_dprintf(mod, "failed to bind dimm node: %s", 240 topo_mod_errmsg(mod)); 241 /* errno set */ 242 goto err; 243 } 244 245 /* Create authority and system pgroups */ 246 topo_pgroup_hcset(dimmnode, auth); 247 nvlist_free(auth); 248 249 if (topo_node_fru_set(dimmnode, fmri, NULL, &err) != 0) { 250 topo_mod_dprintf(mod, "failed to set FRU on %s: %s", 251 DIMM, topo_strerror(err)); 252 nvlist_free(fmri); 253 (void) topo_mod_seterrno(mod, err); 254 goto err; 255 } 256 nvlist_free(fmri); 257 258 if (topo_node_label_set(dimmnode, (char *)smb_md->smbmd_dloc, &err) != 259 0) { 260 topo_mod_dprintf(mod, "failed to set label on %s: %s", 261 DIMM, topo_strerror(err)); 262 (void) topo_mod_seterrno(mod, err); 263 goto err; 264 } 265 266 pgi.tpi_name = TOPO_PGROUP_DIMM_PROPS; 267 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 268 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 269 pgi.tpi_version = TOPO_VERSION; 270 if (topo_pgroup_create(dimmnode, &pgi, &err) != 0) { 271 (void) topo_mod_seterrno(mod, err); 272 goto err; 273 } 274 275 rc += topo_prop_set_uint64(dimmnode, TOPO_PGROUP_DIMM_PROPS, "size", 276 TOPO_PROP_IMMUTABLE, smb_md->smbmd_size, &err); 277 if (rc == 0 && (type = smbios2topotype(mod, smb_md->smbmd_type)) != 278 NULL) { 279 rc += topo_prop_set_string(dimmnode, TOPO_PGROUP_DIMM_PROPS, 280 "type", TOPO_PROP_IMMUTABLE, type, &err); 281 topo_mod_strfree(mod, type); 282 } 283 if (rc == 0 && smb_md->smbmd_set != 0 && smb_md->smbmd_set != 0xFF) 284 rc += topo_prop_set_uint32(dimmnode, TOPO_PGROUP_DIMM_PROPS, 285 "set", TOPO_PROP_IMMUTABLE, smb_md->smbmd_set, &err); 286 if (rc == 0 && smb_md->smbmd_rank != 0) 287 rc += topo_prop_set_uint32(dimmnode, TOPO_PGROUP_DIMM_PROPS, 288 "rank", TOPO_PROP_IMMUTABLE, smb_md->smbmd_rank, &err); 289 if (rc == 0 && smb_md->smbmd_clkspeed != 0) 290 rc += topo_prop_set_uint32(dimmnode, TOPO_PGROUP_DIMM_PROPS, 291 "configured-speed", TOPO_PROP_IMMUTABLE, 292 smb_md->smbmd_clkspeed, &err); 293 if (rc == 0 && smb_md->smbmd_speed != 0) 294 rc += topo_prop_set_uint32(dimmnode, TOPO_PGROUP_DIMM_PROPS, 295 "maximum-speed", TOPO_PROP_IMMUTABLE, smb_md->smbmd_speed, 296 &err); 297 if (rc == 0 && smb_md->smbmd_maxvolt != 0) 298 rc += topo_prop_set_double(dimmnode, TOPO_PGROUP_DIMM_PROPS, 299 "maximum-voltage", TOPO_PROP_IMMUTABLE, 300 (smb_md->smbmd_maxvolt / 1000), &err); 301 if (rc == 0 && smb_md->smbmd_minvolt != 0) 302 rc += topo_prop_set_double(dimmnode, TOPO_PGROUP_DIMM_PROPS, 303 "minimum-voltage", TOPO_PROP_IMMUTABLE, 304 (smb_md->smbmd_minvolt / 1000), &err); 305 if (rc == 0 && smb_md->smbmd_confvolt != 0) 306 rc += topo_prop_set_double(dimmnode, TOPO_PGROUP_DIMM_PROPS, 307 "configured-voltage", TOPO_PROP_IMMUTABLE, 308 (smb_md->smbmd_confvolt / 1000), &err); 309 if (rc == 0 && manuf != NULL) 310 rc += topo_prop_set_string(dimmnode, TOPO_PGROUP_DIMM_PROPS, 311 "manufacturer", TOPO_PROP_IMMUTABLE, manuf, &err); 312 if (rc == 0 && prod != NULL) 313 rc += topo_prop_set_string(dimmnode, TOPO_PGROUP_DIMM_PROPS, 314 "product", TOPO_PROP_IMMUTABLE, prod, &err); 315 if (rc == 0 && asset != NULL) 316 rc += topo_prop_set_string(dimmnode, TOPO_PGROUP_DIMM_PROPS, 317 "asset-tag", TOPO_PROP_IMMUTABLE, asset, &err); 318 if (rc == 0 && loc != NULL) 319 rc += topo_prop_set_string(dimmnode, TOPO_PGROUP_DIMM_PROPS, 320 "location", TOPO_PROP_IMMUTABLE, loc, &err); 321 322 if (rc != 0) { 323 topo_mod_dprintf(mod, "error setting properties on %s node", 324 DIMM); 325 (void) topo_mod_seterrno(mod, err); 326 goto err; 327 } 328 ret = dimmnode; 329 err: 330 topo_mod_strfree(mod, manuf); 331 topo_mod_strfree(mod, prod); 332 topo_mod_strfree(mod, asset); 333 topo_mod_strfree(mod, loc); 334 return (ret); 335 } 336 337 static int 338 smbios_enum_memory(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg) 339 { 340 smbios_info_t smb_info; 341 smbios_memdevice_t smb_md; 342 smb_enum_data_t *smed = arg; 343 topo_mod_t *mod = smed->sme_mod; 344 tnode_t *slotnode; 345 346 if (sp->smbstr_type != SMB_TYPE_MEMDEVICE) 347 return (0); 348 349 if (smbios_info_memdevice(shp, sp->smbstr_id, &smb_md) != 0) { 350 topo_mod_dprintf(mod, "libsmbios error"); 351 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 352 } 353 354 /* 355 * SMB_TYPE_MEMDEVICE records can also be used to represent memory 356 * that come in non-DIMM form factors. If we encounter something like 357 * that, then we skip over it. 358 */ 359 if ((smed->sme_slot_form = distill_dimm_form(mod, &smb_md)) == NULL) 360 return (0); 361 362 if ((slotnode = smbios_make_slot(smed, &smb_md)) == NULL) { 363 topo_mod_dprintf(mod, "failed to create %s node", SLOT); 364 topo_mod_strfree(mod, smed->sme_slot_form); 365 /* errno set */ 366 return (-1); 367 } 368 topo_mod_strfree(mod, smed->sme_slot_form); 369 smed->sme_slotnode = slotnode; 370 371 /* 372 * A size of zero indicates that the DIMM slot is not populated, so 373 * we skip creating a child dimm node and return. 374 */ 375 if (smb_md.smbmd_size == 0) { 376 smed->sme_slot_inst++; 377 return (0); 378 } 379 380 if (smbios_info_common(shp, sp->smbstr_id, &smb_info) == 0) 381 smed->sme_smb_info = &smb_info; 382 383 if (smbios_make_dimm(smed, &smb_md) == NULL) { 384 topo_mod_dprintf(mod, "failed to create %s node", DIMM); 385 /* errno set */ 386 return (-1); 387 } 388 /* 389 * If we've exceeded our max inst then return non-zero to cause 390 * the walk to terminate. 391 */ 392 if (++smed->sme_slot_inst > smed->sme_slot_maxinst) 393 return (1); 394 395 return (0); 396 } 397 398 static int 399 smbios_enum_motherboard(smbios_hdl_t *shp, smb_enum_data_t *smed) 400 { 401 smbios_struct_t sp; 402 smbios_bboard_t smb_mb; 403 smbios_bios_t smb_bios; 404 smbios_info_t smb_info; 405 const char *part = NULL, *rev = NULL, *serial = NULL; 406 char *manuf = NULL, *prod = NULL, *asset = NULL; 407 char *bios_vendor = NULL, *bios_rev = NULL, *bios_reldate = NULL; 408 nvlist_t *auth, *fmri; 409 topo_mod_t *mod = smed->sme_mod; 410 tnode_t *mbnode; 411 topo_pgroup_info_t pgi; 412 int rc = 0, err; 413 414 if (smbios_lookup_type(shp, SMB_TYPE_BASEBOARD, &sp) == 0 && 415 smbios_info_bboard(shp, sp.smbstr_id, &smb_mb) == 0 && 416 smbios_info_common(shp, sp.smbstr_id, &smb_info) == 0) { 417 if (is_valid_string(smb_info.smbi_part) == B_TRUE) 418 part = smb_info.smbi_part; 419 if (is_valid_string(smb_info.smbi_version) == B_TRUE) 420 rev = smb_info.smbi_version; 421 if (is_valid_string(smb_info.smbi_serial) == B_TRUE) 422 serial = smb_info.smbi_serial; 423 if (is_valid_string(smb_info.smbi_manufacturer) == B_TRUE) 424 manuf = topo_mod_clean_str(mod, 425 smb_info.smbi_manufacturer); 426 if (is_valid_string(smb_info.smbi_product) == B_TRUE) 427 prod = topo_mod_clean_str(mod, smb_info.smbi_product); 428 if (is_valid_string(smb_info.smbi_asset) == B_TRUE) 429 asset = topo_mod_clean_str(mod, smb_info.smbi_asset); 430 } 431 if (smbios_lookup_type(shp, SMB_TYPE_BIOS, &sp) == 0 && 432 smbios_info_bios(shp, &smb_bios) == 0) { 433 if (is_valid_string(smb_bios.smbb_vendor) == B_TRUE) 434 bios_vendor = topo_mod_clean_str(mod, 435 smb_bios.smbb_vendor); 436 if (is_valid_string(smb_bios.smbb_version) == B_TRUE) 437 bios_rev = topo_mod_clean_str(mod, 438 smb_bios.smbb_version); 439 if (is_valid_string(smb_bios.smbb_reldate) == B_TRUE) 440 bios_reldate = topo_mod_clean_str(mod, 441 smb_bios.smbb_reldate); 442 } 443 if ((auth = topo_mod_auth(mod, smed->sme_pnode)) == NULL) { 444 topo_mod_dprintf(mod, "topo_mod_auth() failed: %s", 445 topo_mod_errmsg(mod)); 446 /* errno set */ 447 goto err; 448 } 449 450 if ((fmri = topo_mod_hcfmri(mod, NULL, FM_HC_SCHEME_VERSION, 451 MOTHERBOARD, 0, NULL, auth, part, rev, serial)) == 452 NULL) { 453 nvlist_free(auth); 454 topo_mod_dprintf(mod, "topo_mod_hcfmri() failed: %s", 455 topo_mod_errmsg(mod)); 456 /* errno set */ 457 goto err; 458 } 459 460 if ((mbnode = topo_node_bind(mod, smed->sme_pnode, MOTHERBOARD, 0, 461 fmri)) == NULL) { 462 nvlist_free(auth); 463 nvlist_free(fmri); 464 topo_mod_dprintf(mod, "topo_node_bind() failed: %s", 465 topo_mod_errmsg(mod)); 466 /* errno set */ 467 goto err; 468 } 469 470 /* Create authority and system pgroups */ 471 topo_pgroup_hcset(mbnode, auth); 472 nvlist_free(auth); 473 474 if (topo_node_fru_set(mbnode, fmri, NULL, &err) != 0) { 475 topo_mod_dprintf(mod, "failed to set FRU on %s: %s", 476 MOTHERBOARD, topo_strerror(err)); 477 nvlist_free(fmri); 478 (void) topo_mod_seterrno(mod, err); 479 goto err; 480 } 481 nvlist_free(fmri); 482 fmri = NULL; 483 484 if (topo_node_label_set(mbnode, "MB", &err) != 0) { 485 topo_mod_dprintf(mod, "failed to set label on %s: %s", 486 MOTHERBOARD, topo_strerror(err)); 487 (void) topo_mod_seterrno(mod, err); 488 goto err; 489 } 490 491 pgi.tpi_name = TOPO_PGROUP_MOTHERBOARD; 492 pgi.tpi_namestab = TOPO_STABILITY_PRIVATE; 493 pgi.tpi_datastab = TOPO_STABILITY_PRIVATE; 494 pgi.tpi_version = TOPO_VERSION; 495 rc = topo_pgroup_create(mbnode, &pgi, &err); 496 497 if (rc == 0 && manuf != NULL) 498 rc += topo_prop_set_string(mbnode, TOPO_PGROUP_MOTHERBOARD, 499 TOPO_PROP_MB_MANUFACTURER, TOPO_PROP_IMMUTABLE, manuf, 500 &err); 501 if (rc == 0 && prod != NULL) 502 rc += topo_prop_set_string(mbnode, TOPO_PGROUP_MOTHERBOARD, 503 TOPO_PROP_MB_PRODUCT, TOPO_PROP_IMMUTABLE, prod, &err); 504 if (rc == 0 && asset != NULL) 505 rc += topo_prop_set_string(mbnode, TOPO_PGROUP_MOTHERBOARD, 506 TOPO_PROP_MB_ASSET, TOPO_PROP_IMMUTABLE, asset, &err); 507 if (rc == 0 && bios_vendor != NULL) 508 rc += topo_prop_set_string(mbnode, TOPO_PGROUP_MOTHERBOARD, 509 TOPO_PROP_MB_FIRMWARE_VENDOR, TOPO_PROP_IMMUTABLE, 510 bios_vendor, &err); 511 if (rc == 0 && bios_rev != NULL) 512 rc += topo_prop_set_string(mbnode, TOPO_PGROUP_MOTHERBOARD, 513 TOPO_PROP_MB_FIRMWARE_REV, TOPO_PROP_IMMUTABLE, 514 bios_rev, &err); 515 if (rc == 0 && bios_reldate != NULL) 516 rc += topo_prop_set_string(mbnode, TOPO_PGROUP_MOTHERBOARD, 517 TOPO_PROP_MB_FIRMWARE_RELDATE, TOPO_PROP_IMMUTABLE, 518 bios_reldate, &err); 519 520 if (rc != 0) { 521 topo_mod_dprintf(mod, "error setting properties on %s node", 522 MOTHERBOARD); 523 (void) topo_mod_seterrno(mod, err); 524 goto err; 525 } 526 err: 527 topo_mod_strfree(mod, manuf); 528 topo_mod_strfree(mod, prod); 529 topo_mod_strfree(mod, asset); 530 topo_mod_strfree(mod, bios_vendor); 531 topo_mod_strfree(mod, bios_rev); 532 topo_mod_strfree(mod, bios_reldate); 533 534 return (0); 535 } 536 537 /* 538 * A system with a functional memory controller driver will have one mc device 539 * node per chip instance, starting at instance 0. The driver provides an 540 * ioctl interface for retrieving a snapshot of the system's memory topology. 541 * If we're able to issue this ioctl on one of the mc device nodes then we'll 542 * return B_TRUE, indicating that this system has a minimally functional memory 543 * controller driver. 544 */ 545 static boolean_t 546 has_mc_driver() 547 { 548 #ifdef __x86 549 int mc_fd; 550 mc_snapshot_info_t mcs; 551 552 if ((mc_fd = open("/dev/mc/mc0", O_RDONLY)) < 0) 553 return (B_FALSE); 554 555 if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) < 0) { 556 (void) close(mc_fd); 557 return (B_FALSE); 558 } 559 (void) close(mc_fd); 560 return (B_TRUE); 561 #else 562 return (B_TRUE); 563 #endif 564 } 565 566 /*ARGSUSED*/ 567 static int 568 smbios_enum(topo_mod_t *mod, tnode_t *rnode, const char *name, 569 topo_instance_t min, topo_instance_t max, void *arg, void *unused) 570 { 571 smbios_hdl_t *smbh; 572 smb_enum_data_t smed = { 0 }; 573 574 if ((smbh = topo_mod_smbios(mod)) == NULL) { 575 topo_mod_dprintf(mod, "failed to get libsmbios handle"); 576 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 577 } 578 smed.sme_mod = mod; 579 smed.sme_pnode = rnode; 580 smed.sme_slot_inst = min; 581 smed.sme_slot_maxinst = max; 582 583 /* 584 * Currently we only support enumerating dimm-slot and dimm nodes, but 585 * this module could be expanded in the future to enumerate other 586 * hardware components from SMBIOS. 587 */ 588 if (strcmp(name, SLOT) == 0) { 589 /* 590 * If the system has a functional memory controller driver then 591 * we'll assume that it has responsibility for enumerating the 592 * memory topology. 593 */ 594 if (has_mc_driver() == B_TRUE) 595 return (0); 596 if (smbios_iter(smbh, smbios_enum_memory, &smed) < 0) 597 /* errno set */ 598 return (-1); 599 } else if (strcmp(name, MOTHERBOARD) == 0) { 600 if (smbios_enum_motherboard(smbh, &smed) < 0) 601 /* errno set */ 602 return (-1); 603 } else { 604 topo_mod_dprintf(mod, "smbios_enum() invoked for unsupported " 605 "node type: %s", name); 606 return (topo_mod_seterrno(mod, EMOD_UNKNOWN)); 607 } 608 return (0); 609 } 610 611 const topo_modops_t smbios_ops = { smbios_enum, NULL }; 612 613 const topo_modinfo_t smbios_info = 614 { "smbios", FM_FMRI_SCHEME_HC, TOPO_VERSION, &smbios_ops }; 615 616 /*ARGSUSED*/ 617 int 618 _topo_init(topo_mod_t *mod, topo_version_t version) 619 { 620 if (getenv("TOPOSMBIOSDEBUG") != NULL) 621 topo_mod_setdebug(mod); 622 623 if (topo_mod_register(mod, &smbios_info, TOPO_VERSION) != 0) { 624 topo_mod_dprintf(mod, "module registration failed: %s\n", 625 topo_mod_errmsg(mod)); 626 /* errno set */ 627 return (-1); 628 } 629 630 topo_mod_dprintf(mod, "SMBIOS enumerator initialized\n"); 631 return (0); 632 } 633 634 void 635 _topo_fini(topo_mod_t *mod) 636 { 637 topo_mod_unregister(mod); 638 } 639