xref: /illumos-gate/usr/src/lib/fm/topo/modules/common/smbios/smbios_enum.c (revision fa79a855d371dfcb29461ad6ebaf48a458bf9f14)
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