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