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