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 *
distill_dimm_form(topo_mod_t * mod,smbios_memdevice_t * smb_md)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 *
smbios2topotype(topo_mod_t * mod,uint8_t type)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
is_valid_string(const char * str)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 *
smbios_make_slot(smb_enum_data_t * smed,smbios_memdevice_t * smb_md)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 *
smbios_make_dimm(smb_enum_data_t * smed,smbios_memdevice_t * smb_md)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
smbios_enum_memory(smbios_hdl_t * shp,const smbios_struct_t * sp,void * arg)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
smbios_enum_motherboard(smbios_hdl_t * shp,smb_enum_data_t * smed)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
has_mc_driver()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
smbios_enum(topo_mod_t * mod,tnode_t * rnode,const char * name,topo_instance_t min,topo_instance_t max,void * arg,void * unused)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
_topo_init(topo_mod_t * mod,topo_version_t version)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
_topo_fini(topo_mod_t * mod)682 _topo_fini(topo_mod_t *mod)
683 {
684 topo_mod_unregister(mod);
685 }
686