9584 Extend smbios enumerator module to enumerate motherboard
[unleashed.git] / usr / src / lib / fm / topo / modules / common / smbios / smbios_enum.c
blob4fe3c9fe0253eda9c3bc5dd5de1e9adcc795b260
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.
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.
13 * Copyright (c) 2018, Joyent, Inc.
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>
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;
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.
44 * Caller must free the returned string.
46 static char *
47 distill_dimm_form(topo_mod_t *mod, smbios_memdevice_t *smb_md)
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);
63 static char *
64 smbios2topotype(topo_mod_t *mod, uint8_t type)
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));
89 static boolean_t
90 is_valid_string(const char *str)
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);
96 return (B_FALSE);
99 static tnode_t *
100 smbios_make_slot(smb_enum_data_t *smed, smbios_memdevice_t *smb_md)
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;
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);
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);
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);
133 nvlist_free(fmri);
134 fmri = NULL;
136 /* Create authority and system pgroups */
137 topo_pgroup_hcset(slotnode, auth);
138 nvlist_free(auth);
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);
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);
155 nvlist_free(fmri);
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);
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);
184 return (slotnode);
187 static tnode_t *
188 smbios_make_dimm(smb_enum_data_t *smed, smbios_memdevice_t *smb_md)
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;
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);
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);
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;
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;
245 /* Create authority and system pgroups */
246 topo_pgroup_hcset(dimmnode, auth);
247 nvlist_free(auth);
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;
256 nvlist_free(fmri);
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;
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;
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);
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);
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;
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);
337 static int
338 smbios_enum_memory(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
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;
346 if (sp->smbstr_type != SMB_TYPE_MEMDEVICE)
347 return (0);
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));
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.
359 if ((smed->sme_slot_form = distill_dimm_form(mod, &smb_md)) == NULL)
360 return (0);
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);
368 topo_mod_strfree(mod, smed->sme_slot_form);
369 smed->sme_slotnode = slotnode;
372 * A size of zero indicates that the DIMM slot is not populated, so
373 * we skip creating a child dimm node and return.
375 if (smb_md.smbmd_size == 0) {
376 smed->sme_slot_inst++;
377 return (0);
380 if (smbios_info_common(shp, sp->smbstr_id, &smb_info) == 0)
381 smed->sme_smb_info = &smb_info;
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);
389 * If we've exceeded our max inst then return non-zero to cause
390 * the walk to terminate.
392 if (++smed->sme_slot_inst > smed->sme_slot_maxinst)
393 return (1);
395 return (0);
398 static int
399 smbios_enum_motherboard(smbios_hdl_t *shp, smb_enum_data_t *smed)
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;
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);
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);
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;
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;
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;
470 /* Create authority and system pgroups */
471 topo_pgroup_hcset(mbnode, auth);
472 nvlist_free(auth);
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;
481 nvlist_free(fmri);
482 fmri = NULL;
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;
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);
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);
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;
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);
534 return (0);
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.
545 static boolean_t
546 has_mc_driver()
548 #ifdef __x86
549 int mc_fd;
550 mc_snapshot_info_t mcs;
552 if ((mc_fd = open("/dev/mc/mc0", O_RDONLY)) < 0)
553 return (B_FALSE);
555 if (ioctl(mc_fd, MC_IOC_SNAPSHOT_INFO, &mcs) < 0) {
556 (void) close(mc_fd);
557 return (B_FALSE);
559 (void) close(mc_fd);
560 return (B_TRUE);
561 #else
562 return (B_TRUE);
563 #endif
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)
571 smbios_hdl_t *smbh;
572 smb_enum_data_t smed = { 0 };
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));
578 smed.sme_mod = mod;
579 smed.sme_pnode = rnode;
580 smed.sme_slot_inst = min;
581 smed.sme_slot_maxinst = max;
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.
588 if (strcmp(name, SLOT) == 0) {
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.
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));
608 return (0);
611 const topo_modops_t smbios_ops = { smbios_enum, NULL };
613 const topo_modinfo_t smbios_info =
614 { "smbios", FM_FMRI_SCHEME_HC, TOPO_VERSION, &smbios_ops };
616 /*ARGSUSED*/
618 _topo_init(topo_mod_t *mod, topo_version_t version)
620 if (getenv("TOPOSMBIOSDEBUG") != NULL)
621 topo_mod_setdebug(mod);
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);
630 topo_mod_dprintf(mod, "SMBIOS enumerator initialized\n");
631 return (0);
634 void
635 _topo_fini(topo_mod_t *mod)
637 topo_mod_unregister(mod);