xref: /illumos-gate/usr/src/cmd/smbios/smbios.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2015 OmniTI Computer Consulting, Inc.  All rights reserved.
24  * Copyright (c) 2017, Joyent, Inc.
25  * Copyright 2022 Oxide Computer Company
26  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
27  * Use is subject to license terms.
28  */
29 
30 #include <sys/sysmacros.h>
31 #include <sys/param.h>
32 
33 #include <smbios.h>
34 #include <alloca.h>
35 #include <limits.h>
36 #include <unistd.h>
37 #include <strings.h>
38 #include <stdlib.h>
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <ctype.h>
44 #include <libjedec.h>
45 
46 #define	SMBIOS_SUCCESS	0
47 #define	SMBIOS_ERROR	1
48 #define	SMBIOS_USAGE	2
49 
50 static const char *g_pname;
51 static int g_hdr;
52 
53 static int opt_e;
54 static int opt_i = -1;
55 static int opt_O;
56 static int opt_s;
57 static int opt_t = -1;
58 static int opt_x;
59 
60 static boolean_t
61 smbios_vergteq(smbios_version_t *v, uint_t major, uint_t minor)
62 {
63 	if (v->smbv_major > major)
64 		return (B_TRUE);
65 	if (v->smbv_major == major &&
66 	    v->smbv_minor >= minor)
67 		return (B_TRUE);
68 	return (B_FALSE);
69 }
70 
71 /*PRINTFLIKE2*/
72 static void
73 smbios_warn(smbios_hdl_t *shp, const char *format, ...)
74 {
75 	va_list ap;
76 
77 	va_start(ap, format);
78 	(void) vfprintf(stderr, format, ap);
79 	va_end(ap);
80 
81 	if (shp != NULL) {
82 		(void) fprintf(stderr, ": %s",
83 		    smbios_errmsg(smbios_errno(shp)));
84 	}
85 
86 	(void) fprintf(stderr, "\n");
87 }
88 
89 /*PRINTFLIKE2*/
90 static void
91 oprintf(FILE *fp, const char *format, ...)
92 {
93 	va_list ap;
94 
95 	va_start(ap, format);
96 	(void) vfprintf(fp, format, ap);
97 	va_end(ap);
98 }
99 
100 /*PRINTFLIKE3*/
101 static void
102 desc_printf(const char *d, FILE *fp, const char *format, ...)
103 {
104 	va_list ap;
105 
106 	va_start(ap, format);
107 	(void) vfprintf(fp, format, ap);
108 	va_end(ap);
109 
110 	if (d != NULL)
111 		(void) fprintf(fp, " (%s)\n", d);
112 	else
113 		(void) fprintf(fp, "\n");
114 }
115 
116 static void
117 flag_printf(FILE *fp, const char *s, uint_t flags, size_t bits,
118     const char *(*flag_name)(uint_t), const char *(*flag_desc)(uint_t))
119 {
120 	size_t i;
121 
122 	oprintf(fp, "  %s: 0x%x\n", s, flags);
123 
124 	for (i = 0; i < bits; i++) {
125 		uint_t f = 1 << i;
126 		const char *n;
127 
128 		if (!(flags & f))
129 			continue;
130 
131 		if ((n = flag_name(f)) != NULL)
132 			desc_printf(flag_desc(f), fp, "\t%s", n);
133 		else
134 			desc_printf(flag_desc(f), fp, "\t0x%x", f);
135 	}
136 }
137 
138 static void
139 flag64_printf(FILE *fp, const char *s, uint64_t flags, size_t bits,
140     const char *(*flag_name)(uint64_t), const char *(*flag_desc)(uint64_t))
141 {
142 	size_t i;
143 
144 	oprintf(fp, "  %s: 0x%llx\n", s, (u_longlong_t)flags);
145 
146 	for (i = 0; i < bits; i++) {
147 		u_longlong_t f = 1ULL << i;
148 		const char *n;
149 
150 		if (!(flags & f))
151 			continue;
152 
153 		if ((n = flag_name(f)) != NULL)
154 			desc_printf(flag_desc(f), fp, "\t%s", n);
155 		else
156 			desc_printf(flag_desc(f), fp, "\t0x%llx", f);
157 	}
158 }
159 
160 static void
161 id_printf(FILE *fp, const char *s, id_t id)
162 {
163 	switch (id) {
164 	case SMB_ID_NONE:
165 		oprintf(fp, "%sNone\n", s);
166 		break;
167 	case SMB_ID_NOTSUP:
168 		oprintf(fp, "%sNot Supported\n", s);
169 		break;
170 	default:
171 		oprintf(fp, "%s%u\n", s, (uint_t)id);
172 	}
173 }
174 
175 static void
176 jedec_print(FILE *fp, const char *desc, uint_t id)
177 {
178 	const char *name;
179 	uint_t cont, vendor;
180 
181 	/*
182 	 * SMBIOS encodes data in the way that the underlying memory standard
183 	 * does. In this case, the upper byte indicates the vendor that we care
184 	 * about while the lower byte indicates the number of continuations that
185 	 * are needed. libjedec indexes this based on zero (e.g. table 1 is zero
186 	 * continuations), which is how the spec encodes it. We add one so that
187 	 * we can match how the spec describes it.
188 	 */
189 	vendor = id >> 8;
190 	cont = id & 0x7f;
191 	name = libjedec_vendor_string(cont, vendor);
192 	if (name == NULL) {
193 		oprintf(fp, "  %s: Bank: 0x%x Vendor: 0x%x\n", desc, cont + 1,
194 		    vendor);
195 	} else {
196 		oprintf(fp, "  %s: Bank: 0x%x Vendor: 0x%x (%s)\n", desc,
197 		    cont + 1, vendor, name);
198 	}
199 }
200 
201 /*
202  * Print a 128-bit data as a series of 16 hex digits.
203  */
204 static void
205 u128_print(FILE *fp, const char *desc, const uint8_t *data)
206 {
207 	uint_t i;
208 
209 	oprintf(fp, "%s: ", desc);
210 	for (i = 0; i < 16; i++) {
211 		oprintf(fp, " %02x", data[i]);
212 	}
213 	oprintf(fp, "\n");
214 }
215 
216 /*
217  * Print a string that came from an SMBIOS table. We do this character by
218  * character so we can potentially escape strings.
219  */
220 static void
221 str_print_label(FILE *fp, const char *header, const char *str, boolean_t label)
222 {
223 	const char *c;
224 
225 	oprintf(fp, header);
226 	if (label) {
227 		oprintf(fp, ": ");
228 	}
229 
230 	for (c = str; *c != '\0'; c++) {
231 		if (isprint(*c)) {
232 			oprintf(fp, "%c", *c);
233 		} else {
234 			oprintf(fp, "\\x%02x", *c);
235 		}
236 	}
237 
238 	oprintf(fp, "\n");
239 }
240 
241 static void
242 str_print_nolabel(FILE *fp, const char *ws, const char *str)
243 {
244 	return (str_print_label(fp, ws, str, B_FALSE));
245 }
246 
247 static void
248 str_print(FILE *fp, const char *header, const char *str)
249 {
250 	return (str_print_label(fp, header, str, B_TRUE));
251 }
252 
253 static int
254 check_oem(smbios_hdl_t *shp)
255 {
256 	int i;
257 	int cnt;
258 	int rv;
259 	id_t oem_id;
260 	smbios_struct_t s;
261 	const char **oem_str;
262 
263 	rv = smbios_lookup_type(shp, SMB_TYPE_OEMSTR, &s);
264 	if (rv != 0) {
265 		return (-1);
266 	}
267 
268 	oem_id = s.smbstr_id;
269 
270 	cnt = smbios_info_strtab(shp, oem_id, 0, NULL);
271 	if (cnt > 0) {
272 		oem_str =  alloca(sizeof (char *) * cnt);
273 		(void) smbios_info_strtab(shp, oem_id, cnt, oem_str);
274 
275 		for (i = 0; i < cnt; i++) {
276 			if (strncmp(oem_str[i], SMB_PRMS1,
277 			    strlen(SMB_PRMS1) + 1) == 0) {
278 				return (0);
279 			}
280 		}
281 	}
282 
283 	return (-1);
284 }
285 
286 static void
287 print_smbios_21(smbios_21_entry_t *ep, FILE *fp)
288 {
289 	int i;
290 
291 	oprintf(fp, "Entry Point Anchor Tag: %*.*s\n",
292 	    (int)sizeof (ep->smbe_eanchor), (int)sizeof (ep->smbe_eanchor),
293 	    ep->smbe_eanchor);
294 
295 	oprintf(fp, "Entry Point Checksum: 0x%x\n", ep->smbe_ecksum);
296 	oprintf(fp, "Entry Point Length: %u\n", ep->smbe_elen);
297 	oprintf(fp, "Entry Point Version: %u.%u\n",
298 	    ep->smbe_major, ep->smbe_minor);
299 	oprintf(fp, "Max Structure Size: %u\n", ep->smbe_maxssize);
300 	oprintf(fp, "Entry Point Revision: 0x%x\n", ep->smbe_revision);
301 
302 	oprintf(fp, "Entry Point Revision Data:");
303 	for (i = 0; i < sizeof (ep->smbe_format); i++)
304 		oprintf(fp, " 0x%02x", ep->smbe_format[i]);
305 	oprintf(fp, "\n");
306 
307 	oprintf(fp, "Intermediate Anchor Tag: %*.*s\n",
308 	    (int)sizeof (ep->smbe_ianchor), (int)sizeof (ep->smbe_ianchor),
309 	    ep->smbe_ianchor);
310 
311 	oprintf(fp, "Intermediate Checksum: 0x%x\n", ep->smbe_icksum);
312 	oprintf(fp, "Structure Table Length: %u\n", ep->smbe_stlen);
313 	oprintf(fp, "Structure Table Address: 0x%x\n", ep->smbe_staddr);
314 	oprintf(fp, "Structure Table Entries: %u\n", ep->smbe_stnum);
315 	oprintf(fp, "DMI BCD Revision: 0x%x\n", ep->smbe_bcdrev);
316 }
317 
318 static void
319 print_smbios_30(smbios_30_entry_t *ep, FILE *fp)
320 {
321 	oprintf(fp, "Entry Point Anchor Tag: %*.*s\n",
322 	    (int)sizeof (ep->smbe_eanchor), (int)sizeof (ep->smbe_eanchor),
323 	    ep->smbe_eanchor);
324 
325 	oprintf(fp, "Entry Point Checksum: 0x%x\n", ep->smbe_ecksum);
326 	oprintf(fp, "Entry Point Length: %u\n", ep->smbe_elen);
327 	oprintf(fp, "SMBIOS Version: %u.%u\n",
328 	    ep->smbe_major, ep->smbe_minor);
329 	oprintf(fp, "SMBIOS DocRev: 0x%x\n", ep->smbe_docrev);
330 	oprintf(fp, "Entry Point Revision: 0x%x\n", ep->smbe_revision);
331 
332 	oprintf(fp, "Structure Table Length: %u\n", ep->smbe_stlen);
333 	oprintf(fp, "Structure Table Address: 0x%" PRIx64 "\n",
334 	    ep->smbe_staddr);
335 }
336 
337 static void
338 print_smbios(smbios_hdl_t *shp, FILE *fp)
339 {
340 	smbios_entry_t ep;
341 
342 	switch (smbios_info_smbios(shp, &ep)) {
343 	case SMBIOS_ENTRY_POINT_21:
344 		print_smbios_21(&ep.ep21, fp);
345 		break;
346 	case SMBIOS_ENTRY_POINT_30:
347 		print_smbios_30(&ep.ep30, fp);
348 		break;
349 	}
350 }
351 
352 static void
353 print_common(const smbios_info_t *ip, FILE *fp)
354 {
355 	if (ip->smbi_manufacturer[0] != '\0')
356 		str_print(fp, "  Manufacturer", ip->smbi_manufacturer);
357 	if (ip->smbi_product[0] != '\0')
358 		str_print(fp, "  Product", ip->smbi_product);
359 	if (ip->smbi_version[0] != '\0')
360 		str_print(fp, "  Version", ip->smbi_version);
361 	if (ip->smbi_serial[0] != '\0')
362 		str_print(fp, "  Serial Number", ip->smbi_serial);
363 	if (ip->smbi_asset[0] != '\0')
364 		str_print(fp, "  Asset Tag", ip->smbi_asset);
365 	if (ip->smbi_location[0] != '\0')
366 		str_print(fp, "  Location Tag", ip->smbi_location);
367 	if (ip->smbi_part[0] != '\0')
368 		str_print(fp, "  Part Number", ip->smbi_part);
369 }
370 
371 static void
372 print_bios(smbios_hdl_t *shp, FILE *fp)
373 {
374 	smbios_bios_t b;
375 
376 	if (smbios_info_bios(shp, &b) == -1) {
377 		smbios_warn(shp, "failed to read BIOS information");
378 		return;
379 	}
380 
381 	str_print(fp, "  Vendor", b.smbb_vendor);
382 	str_print(fp, "  Version String", b.smbb_version);
383 	str_print(fp, "  Release Date", b.smbb_reldate);
384 	oprintf(fp, "  Address Segment: 0x%x\n", b.smbb_segment);
385 	oprintf(fp, "  ROM Size: %" PRIu64 " bytes\n", b.smbb_extromsize);
386 	oprintf(fp, "  Image Size: %u bytes\n", b.smbb_runsize);
387 
388 	flag64_printf(fp, "Characteristics",
389 	    b.smbb_cflags, sizeof (b.smbb_cflags) * NBBY,
390 	    smbios_bios_flag_name, smbios_bios_flag_desc);
391 
392 	if (b.smbb_nxcflags > SMB_BIOSXB_1) {
393 		flag_printf(fp, "Characteristics Extension Byte 1",
394 		    b.smbb_xcflags[SMB_BIOSXB_1],
395 		    sizeof (b.smbb_xcflags[SMB_BIOSXB_1]) * NBBY,
396 		    smbios_bios_xb1_name, smbios_bios_xb1_desc);
397 	}
398 
399 	if (b.smbb_nxcflags > SMB_BIOSXB_2) {
400 		flag_printf(fp, "Characteristics Extension Byte 2",
401 		    b.smbb_xcflags[SMB_BIOSXB_2],
402 		    sizeof (b.smbb_xcflags[SMB_BIOSXB_2]) * NBBY,
403 		    smbios_bios_xb2_name, smbios_bios_xb2_desc);
404 	}
405 
406 	if (b.smbb_nxcflags > SMB_BIOSXB_BIOS_MIN) {
407 		oprintf(fp, "  Version Number: %u.%u\n",
408 		    b.smbb_biosv.smbv_major, b.smbb_biosv.smbv_minor);
409 	}
410 
411 	/*
412 	 * If the major and minor versions are 0xff then that indicates that the
413 	 * embedded controller does not exist.
414 	 */
415 	if (b.smbb_nxcflags > SMB_BIOSXB_ECFW_MIN &&
416 	    b.smbb_ecfwv.smbv_major != 0xff &&
417 	    b.smbb_ecfwv.smbv_minor != 0xff) {
418 		oprintf(fp, "  Embedded Ctlr Firmware Version Number: %u.%u\n",
419 		    b.smbb_ecfwv.smbv_major, b.smbb_ecfwv.smbv_minor);
420 	}
421 }
422 
423 static void
424 print_system(smbios_hdl_t *shp, FILE *fp)
425 {
426 	smbios_system_t s;
427 	uint_t i;
428 
429 	if (smbios_info_system(shp, &s) == -1) {
430 		smbios_warn(shp, "failed to read system information");
431 		return;
432 	}
433 
434 	oprintf(fp, "  UUID: ");
435 	for (i = 0; i < s.smbs_uuidlen; i++) {
436 		oprintf(fp, "%02x", s.smbs_uuid[i]);
437 		if (i == 3 || i == 5 || i == 7 || i == 9)
438 			oprintf(fp, "-");
439 	}
440 	oprintf(fp, "\n");
441 
442 	desc_printf(smbios_system_wakeup_desc(s.smbs_wakeup),
443 	    fp, "  Wake-Up Event: 0x%x", s.smbs_wakeup);
444 
445 	str_print(fp, "  SKU Number", s.smbs_sku);
446 	str_print(fp, "  Family", s.smbs_family);
447 }
448 
449 static void
450 print_bboard(smbios_hdl_t *shp, id_t id, FILE *fp)
451 {
452 	smbios_bboard_t b;
453 	int chdl_cnt;
454 
455 	if (smbios_info_bboard(shp, id, &b) != 0) {
456 		smbios_warn(shp, "failed to read baseboard information");
457 		return;
458 	}
459 
460 	oprintf(fp, "  Chassis: %u\n", (uint_t)b.smbb_chassis);
461 
462 	flag_printf(fp, "Flags", b.smbb_flags, sizeof (b.smbb_flags) * NBBY,
463 	    smbios_bboard_flag_name, smbios_bboard_flag_desc);
464 
465 	desc_printf(smbios_bboard_type_desc(b.smbb_type),
466 	    fp, "  Board Type: 0x%x", b.smbb_type);
467 
468 	chdl_cnt = b.smbb_contn;
469 	if (chdl_cnt != 0) {
470 		id_t *chdl;
471 		uint16_t hdl;
472 		int i, n, cnt;
473 
474 		chdl = alloca(chdl_cnt * sizeof (id_t));
475 		cnt = smbios_info_contains(shp, id, chdl_cnt, chdl);
476 		if (cnt > SMB_CONT_MAX)
477 			return;
478 		n = MIN(chdl_cnt, cnt);
479 
480 		oprintf(fp, "\n");
481 		for (i = 0; i < n; i++) {
482 			hdl = (uint16_t)chdl[i];
483 			oprintf(fp, "  Contained Handle: %u\n", hdl);
484 		}
485 	}
486 }
487 
488 static void
489 print_chassis(smbios_hdl_t *shp, id_t id, FILE *fp)
490 {
491 	smbios_chassis_t c;
492 	smbios_chassis_entry_t *elts;
493 	uint_t nelts, i;
494 
495 	if (smbios_info_chassis(shp, id, &c) != 0) {
496 		smbios_warn(shp, "failed to read chassis information");
497 		return;
498 	}
499 
500 	oprintf(fp, "  OEM Data: 0x%x\n", c.smbc_oemdata);
501 	str_print(fp, "  SKU Number",
502 	    c.smbc_sku[0] == '\0' ? "<unknown>" : c.smbc_sku);
503 	oprintf(fp, "  Lock Present: %s\n", c.smbc_lock ? "Y" : "N");
504 
505 	desc_printf(smbios_chassis_type_desc(c.smbc_type),
506 	    fp, "  Chassis Type: 0x%x", c.smbc_type);
507 
508 	desc_printf(smbios_chassis_state_desc(c.smbc_bustate),
509 	    fp, "  Boot-Up State: 0x%x", c.smbc_bustate);
510 
511 	desc_printf(smbios_chassis_state_desc(c.smbc_psstate),
512 	    fp, "  Power Supply State: 0x%x", c.smbc_psstate);
513 
514 	desc_printf(smbios_chassis_state_desc(c.smbc_thstate),
515 	    fp, "  Thermal State: 0x%x", c.smbc_thstate);
516 
517 	oprintf(fp, "  Chassis Height: %uu\n", c.smbc_uheight);
518 	oprintf(fp, "  Power Cords: %u\n", c.smbc_cords);
519 
520 	oprintf(fp, "  Element Records: %u\n", c.smbc_elems);
521 
522 	if (c.smbc_elems == 0) {
523 		return;
524 	}
525 
526 	if (smbios_info_chassis_elts(shp, id, &nelts, &elts) != 0) {
527 		smbios_warn(shp, "failed to read chassis elements");
528 		return;
529 	}
530 
531 	oprintf(fp, "\n");
532 
533 	for (i = 0; i < nelts; i++) {
534 		switch (elts[i].smbce_type) {
535 		case SMB_CELT_BBOARD:
536 			desc_printf(smbios_bboard_type_desc(elts[i].smbce_elt),
537 			    fp, "  Contained SMBIOS Base Board Type: 0x%x",
538 			    elts[i].smbce_elt);
539 			break;
540 		case SMB_CELT_SMBIOS:
541 			desc_printf(smbios_type_name(elts[i].smbce_elt), fp,
542 			    "  Contained SMBIOS structure Type: %u",
543 			    elts[i].smbce_elt);
544 			break;
545 		default:
546 			oprintf(fp, "  Unknown contained Type: %u/%u\n",
547 			    elts[i].smbce_type, elts[i].smbce_elt);
548 			break;
549 		}
550 		oprintf(fp, "    Minimum number: %u\n", elts[i].smbce_min);
551 		oprintf(fp, "    Maximum number: %u\n", elts[i].smbce_max);
552 	}
553 }
554 
555 static void
556 print_processor(smbios_hdl_t *shp, id_t id, FILE *fp)
557 {
558 	smbios_processor_t p;
559 	uint_t status;
560 
561 	if (smbios_info_processor(shp, id, &p) != 0) {
562 		smbios_warn(shp, "failed to read processor information");
563 		return;
564 	}
565 	status = SMB_PRSTATUS_STATUS(p.smbp_status);
566 
567 	desc_printf(smbios_processor_family_desc(p.smbp_family),
568 	    fp, "  Family: %u", p.smbp_family);
569 
570 	oprintf(fp, "  CPUID: 0x%llx\n", (u_longlong_t)p.smbp_cpuid);
571 
572 	desc_printf(smbios_processor_type_desc(p.smbp_type),
573 	    fp, "  Type: %u", p.smbp_type);
574 
575 	desc_printf(smbios_processor_upgrade_desc(p.smbp_upgrade),
576 	    fp, "  Socket Upgrade: %u", p.smbp_upgrade);
577 
578 	oprintf(fp, "  Socket Status: %s\n",
579 	    SMB_PRSTATUS_PRESENT(p.smbp_status) ?
580 	    "Populated" : "Not Populated");
581 
582 	desc_printf(smbios_processor_status_desc(status),
583 	    fp, "  Processor Status: %u", status);
584 
585 	if (SMB_PRV_LEGACY(p.smbp_voltage)) {
586 		oprintf(fp, "  Supported Voltages:");
587 		switch (p.smbp_voltage) {
588 		case SMB_PRV_5V:
589 			oprintf(fp, " 5.0V");
590 			break;
591 		case SMB_PRV_33V:
592 			oprintf(fp, " 3.3V");
593 			break;
594 		case SMB_PRV_29V:
595 			oprintf(fp, " 2.9V");
596 			break;
597 		}
598 		oprintf(fp, "\n");
599 	} else {
600 		oprintf(fp, "  Supported Voltages: %.1fV\n",
601 		    (float)SMB_PRV_VOLTAGE(p.smbp_voltage) / 10);
602 	}
603 
604 	if (p.smbp_corecount != 0) {
605 		oprintf(fp, "  Core Count: %u\n", p.smbp_corecount);
606 	} else {
607 		oprintf(fp, "  Core Count: Unknown\n");
608 	}
609 
610 	if (p.smbp_coresenabled != 0) {
611 		oprintf(fp, "  Cores Enabled: %u\n", p.smbp_coresenabled);
612 	} else {
613 		oprintf(fp, "  Cores Enabled: Unknown\n");
614 	}
615 
616 	if (p.smbp_threadcount != 0) {
617 		oprintf(fp, "  Thread Count: %u\n", p.smbp_threadcount);
618 	} else {
619 		oprintf(fp, "  Thread Count: Unknown\n");
620 	}
621 
622 	if (p.smbp_cflags) {
623 		flag_printf(fp, "Processor Characteristics",
624 		    p.smbp_cflags, sizeof (p.smbp_cflags) * NBBY,
625 		    smbios_processor_core_flag_name,
626 		    smbios_processor_core_flag_desc);
627 	}
628 
629 	if (p.smbp_clkspeed != 0)
630 		oprintf(fp, "  External Clock Speed: %uMHz\n", p.smbp_clkspeed);
631 	else
632 		oprintf(fp, "  External Clock Speed: Unknown\n");
633 
634 	if (p.smbp_maxspeed != 0)
635 		oprintf(fp, "  Maximum Speed: %uMHz\n", p.smbp_maxspeed);
636 	else
637 		oprintf(fp, "  Maximum Speed: Unknown\n");
638 
639 	if (p.smbp_curspeed != 0)
640 		oprintf(fp, "  Current Speed: %uMHz\n", p.smbp_curspeed);
641 	else
642 		oprintf(fp, "  Current Speed: Unknown\n");
643 
644 	id_printf(fp, "  L1 Cache Handle: ", p.smbp_l1cache);
645 	id_printf(fp, "  L2 Cache Handle: ", p.smbp_l2cache);
646 	id_printf(fp, "  L3 Cache Handle: ", p.smbp_l3cache);
647 
648 	if (p.smbp_threadsenabled != 0) {
649 		oprintf(fp, "  Threads Enabled: %u\n", p.smbp_threadsenabled);
650 	} else {
651 		oprintf(fp, "  Threads Enabled: Unknown\n");
652 	}
653 }
654 
655 static void
656 print_cache(smbios_hdl_t *shp, id_t id, FILE *fp)
657 {
658 	smbios_cache_t c;
659 
660 	if (smbios_info_cache(shp, id, &c) != 0) {
661 		smbios_warn(shp, "failed to read cache information");
662 		return;
663 	}
664 
665 	oprintf(fp, "  Level: %u\n", c.smba_level);
666 	oprintf(fp, "  Maximum Installed Size: %" PRIu64 " bytes\n",
667 	    c.smba_maxsize2);
668 
669 	if (c.smba_size2 != 0) {
670 		oprintf(fp, "  Installed Size: %" PRIu64 " bytes\n",
671 		    c.smba_size2);
672 	} else {
673 		oprintf(fp, "  Installed Size: Not Installed\n");
674 	}
675 
676 	if (c.smba_speed != 0)
677 		oprintf(fp, "  Speed: %uns\n", c.smba_speed);
678 	else
679 		oprintf(fp, "  Speed: Unknown\n");
680 
681 	flag_printf(fp, "Supported SRAM Types",
682 	    c.smba_stype, sizeof (c.smba_stype) * NBBY,
683 	    smbios_cache_ctype_name, smbios_cache_ctype_desc);
684 
685 	desc_printf(smbios_cache_ctype_desc(c.smba_ctype),
686 	    fp, "  Current SRAM Type: 0x%x", c.smba_ctype);
687 
688 	desc_printf(smbios_cache_ecc_desc(c.smba_etype),
689 	    fp, "  Error Correction Type: %u", c.smba_etype);
690 
691 	desc_printf(smbios_cache_logical_desc(c.smba_ltype),
692 	    fp, "  Logical Cache Type: %u", c.smba_ltype);
693 
694 	desc_printf(smbios_cache_assoc_desc(c.smba_assoc),
695 	    fp, "  Associativity: %u", c.smba_assoc);
696 
697 	desc_printf(smbios_cache_mode_desc(c.smba_mode),
698 	    fp, "  Mode: %u", c.smba_mode);
699 
700 	desc_printf(smbios_cache_loc_desc(c.smba_location),
701 	    fp, "  Location: %u", c.smba_location);
702 
703 	flag_printf(fp, "Flags", c.smba_flags, sizeof (c.smba_flags) * NBBY,
704 	    smbios_cache_flag_name, smbios_cache_flag_desc);
705 }
706 
707 static void
708 print_port(smbios_hdl_t *shp, id_t id, FILE *fp)
709 {
710 	smbios_port_t p;
711 
712 	if (smbios_info_port(shp, id, &p) != 0) {
713 		smbios_warn(shp, "failed to read port information");
714 		return;
715 	}
716 
717 	str_print(fp, "  Internal Reference Designator", p.smbo_iref);
718 	str_print(fp, "  External Reference Designator", p.smbo_eref);
719 
720 	desc_printf(smbios_port_conn_desc(p.smbo_itype),
721 	    fp, "  Internal Connector Type: %u", p.smbo_itype);
722 
723 	desc_printf(smbios_port_conn_desc(p.smbo_etype),
724 	    fp, "  External Connector Type: %u", p.smbo_etype);
725 
726 	desc_printf(smbios_port_type_desc(p.smbo_ptype),
727 	    fp, "  Port Type: %u", p.smbo_ptype);
728 }
729 
730 static void
731 print_slot(smbios_hdl_t *shp, id_t id, FILE *fp)
732 {
733 	smbios_slot_t s;
734 	smbios_version_t v;
735 
736 	if (smbios_info_slot(shp, id, &s) != 0) {
737 		smbios_warn(shp, "failed to read slot information");
738 		return;
739 	}
740 	smbios_info_smbios_version(shp, &v);
741 
742 	str_print(fp, "  Reference Designator", s.smbl_name);
743 	oprintf(fp, "  Slot ID: 0x%x\n", s.smbl_id);
744 
745 	desc_printf(smbios_slot_type_desc(s.smbl_type),
746 	    fp, "  Type: 0x%x", s.smbl_type);
747 
748 	desc_printf(smbios_slot_width_desc(s.smbl_width),
749 	    fp, "  Width: 0x%x", s.smbl_width);
750 
751 	desc_printf(smbios_slot_usage_desc(s.smbl_usage),
752 	    fp, "  Usage: 0x%x", s.smbl_usage);
753 
754 	desc_printf(smbios_slot_length_desc(s.smbl_length),
755 	    fp, "  Length: 0x%x", s.smbl_length);
756 
757 	flag_printf(fp, "Slot Characteristics 1",
758 	    s.smbl_ch1, sizeof (s.smbl_ch1) * NBBY,
759 	    smbios_slot_ch1_name, smbios_slot_ch1_desc);
760 
761 	flag_printf(fp, "Slot Characteristics 2",
762 	    s.smbl_ch2, sizeof (s.smbl_ch2) * NBBY,
763 	    smbios_slot_ch2_name, smbios_slot_ch2_desc);
764 
765 	if (check_oem(shp) != 0 && !smbios_vergteq(&v, 2, 6))
766 		return;
767 
768 	oprintf(fp, "  Segment Group: %u\n", s.smbl_sg);
769 	oprintf(fp, "  Bus Number: %u\n", s.smbl_bus);
770 	oprintf(fp, "  Device/Function Number: %u/%u\n", s.smbl_df >> 3,
771 	    s.smbl_df & 0x7);
772 
773 	if (s.smbl_dbw != 0) {
774 		oprintf(fp, "  Data Bus Width: %d\n", s.smbl_dbw);
775 	}
776 
777 	if (s.smbl_npeers > 0) {
778 		smbios_slot_peer_t *peer;
779 		uint_t i, npeers;
780 
781 		if (smbios_info_slot_peers(shp, id, &npeers, &peer) != 0) {
782 			smbios_warn(shp, "failed to read slot peer "
783 			    "information");
784 			return;
785 		}
786 
787 		for (i = 0; i < npeers; i++) {
788 			oprintf(fp, "  Slot Peer %u:\n", i);
789 			oprintf(fp, "    Segment group: %u\n",
790 			    peer[i].smblp_group);
791 			oprintf(fp, "    Bus/Device/Function: %u/%u/%u\n",
792 			    peer[i].smblp_bus, peer[i].smblp_device,
793 			    peer[i].smblp_function);
794 			oprintf(fp, "    Electrical width: %u\n",
795 			    peer[i].smblp_data_width);
796 		}
797 
798 		smbios_info_slot_peers_free(shp, npeers, peer);
799 	}
800 
801 	if (s.smbl_info != 0) {
802 		if (s.smbl_type >= SMB_SLT_PCIE &&
803 		    s.smbl_type <= SMB_SLT_PCIEG6P) {
804 			oprintf(fp, "  PCIe Generation: %d\n", s.smbl_info);
805 		} else {
806 			oprintf(fp, "  Slot Type: 0x%x\n", s.smbl_info);
807 		}
808 	}
809 
810 	if (s.smbl_pwidth != 0) {
811 		desc_printf(smbios_slot_width_desc(s.smbl_pwidth),
812 		    fp, "  Physical Width: 0x%x", s.smbl_pwidth);
813 	}
814 
815 	if (s.smbl_pitch != 0) {
816 		oprintf(fp, "  Slot Pitch: %u.%u mm\n", s.smbl_pitch / 100,
817 		    s.smbl_pitch % 100);
818 	}
819 
820 	/*
821 	 * The slot height was introduced in SMBIOS 3.5. However, a value of
822 	 * zero here does not mean that it is unknown, but rather that the
823 	 * concept is not applicable. Therefore we cannot use a standard check
824 	 * against zero for this and instead use the version.
825 	 */
826 	if (smbios_vergteq(&v, 3, 5)) {
827 		desc_printf(smbios_slot_height_desc(s.smbl_height), fp,
828 		    "  Height: 0x%x", s.smbl_height);
829 	} else {
830 		oprintf(fp, "  Height:  unknown\n");
831 	}
832 }
833 
834 static void
835 print_obdevs_ext(smbios_hdl_t *shp, id_t id, FILE *fp)
836 {
837 	boolean_t enabled;
838 	smbios_obdev_ext_t oe;
839 	const char *type;
840 
841 	if (smbios_info_obdevs_ext(shp, id, &oe) != 0) {
842 		smbios_warn(shp, "failed to read extended on-board devices "
843 		    "information");
844 		return;
845 	}
846 
847 	/*
848 	 * Bit 7 is always whether or not the device is enabled while bits 0:6
849 	 * are the actual device type.
850 	 */
851 	enabled = oe.smboe_dtype >> 7;
852 	type = smbios_onboard_ext_type_desc(oe.smboe_dtype & 0x7f);
853 
854 	str_print(fp, "  Reference Designator", oe.smboe_name);
855 	oprintf(fp, "  Device Enabled: %s\n", enabled == B_TRUE ? "true" :
856 	    "false");
857 	oprintf(fp, "  Device Type: %s\n", type);
858 	oprintf(fp, "  Device Type Instance: %u\n", oe.smboe_dti);
859 	oprintf(fp, "  Segment Group Number: %u\n", oe.smboe_sg);
860 	oprintf(fp, "  Bus Number: %u\n", oe.smboe_bus);
861 	oprintf(fp, "  Device/Function Number: %u\n", oe.smboe_df);
862 }
863 
864 static void
865 print_obdevs(smbios_hdl_t *shp, id_t id, FILE *fp)
866 {
867 	smbios_obdev_t *argv;
868 	int i, argc;
869 
870 	if ((argc = smbios_info_obdevs(shp, id, 0, NULL)) > 0) {
871 		argv = alloca(sizeof (smbios_obdev_t) * argc);
872 		if (smbios_info_obdevs(shp, id, argc, argv) == -1) {
873 			smbios_warn(shp, "failed to read on-board device "
874 			    "information");
875 			return;
876 		}
877 		for (i = 0; i < argc; i++)
878 			str_print_nolabel(fp, "  ", argv[i].smbd_name);
879 	}
880 }
881 
882 static void
883 print_strtab(smbios_hdl_t *shp, id_t id, FILE *fp)
884 {
885 	const char **argv;
886 	int i, argc;
887 
888 	if ((argc = smbios_info_strtab(shp, id, 0, NULL)) > 0) {
889 		argv = alloca(sizeof (char *) * argc);
890 		if (smbios_info_strtab(shp, id, argc, argv) == -1) {
891 			smbios_warn(shp, "failed to read string table "
892 			    "information");
893 			return;
894 		}
895 		for (i = 0; i < argc; i++)
896 			str_print_nolabel(fp, "  ", argv[i]);
897 	}
898 }
899 
900 static void
901 print_lang(smbios_hdl_t *shp, id_t id, FILE *fp)
902 {
903 	smbios_lang_t l;
904 
905 	if (smbios_info_lang(shp, &l) == -1) {
906 		smbios_warn(shp, "failed to read language information");
907 		return;
908 	}
909 
910 	str_print(fp, "  Current Language", l.smbla_cur);
911 	oprintf(fp, "  Language String Format: %u\n", l.smbla_fmt);
912 	oprintf(fp, "  Number of Installed Languages: %u\n", l.smbla_num);
913 	oprintf(fp, "  Installed Languages:\n");
914 
915 	print_strtab(shp, id, fp);
916 }
917 
918 /*ARGSUSED*/
919 static void
920 print_evlog(smbios_hdl_t *shp, id_t id, FILE *fp)
921 {
922 	smbios_evlog_t ev;
923 	uint32_t i;
924 
925 	if (smbios_info_eventlog(shp, &ev) == -1) {
926 		smbios_warn(shp, "failed to read event log information");
927 		return;
928 	}
929 
930 	oprintf(fp, "  Log Area Size: %lu bytes\n", (ulong_t)ev.smbev_size);
931 	oprintf(fp, "  Header Offset: %lu\n", (ulong_t)ev.smbev_hdr);
932 	oprintf(fp, "  Data Offset: %lu\n", (ulong_t)ev.smbev_data);
933 
934 	desc_printf(smbios_evlog_method_desc(ev.smbev_method),
935 	    fp, "  Data Access Method: %u", ev.smbev_method);
936 
937 	flag_printf(fp, "Log Flags",
938 	    ev.smbev_flags, sizeof (ev.smbev_flags) * NBBY,
939 	    smbios_evlog_flag_name, smbios_evlog_flag_desc);
940 
941 	desc_printf(smbios_evlog_format_desc(ev.smbev_format),
942 	    fp, "  Log Header Format: %u", ev.smbev_format);
943 
944 	oprintf(fp, "  Update Token: 0x%x\n", ev.smbev_token);
945 	oprintf(fp, "  Data Access Address: ");
946 
947 	switch (ev.smbev_method) {
948 	case SMB_EVM_1x1i_1x1d:
949 	case SMB_EVM_2x1i_1x1d:
950 	case SMB_EVM_1x2i_1x1d:
951 		oprintf(fp, "Index Address 0x%x, Data Address 0x%x\n",
952 		    ev.smbev_addr.eva_io.evi_iaddr,
953 		    ev.smbev_addr.eva_io.evi_daddr);
954 		break;
955 	case SMB_EVM_GPNV:
956 		oprintf(fp, "0x%x\n", ev.smbev_addr.eva_gpnv);
957 		break;
958 	default:
959 		oprintf(fp, "0x%x\n", ev.smbev_addr.eva_addr);
960 	}
961 
962 	oprintf(fp, "  Type Descriptors:\n");
963 
964 	for (i = 0; i < ev.smbev_typec; i++) {
965 		oprintf(fp, "  %u: Log Type 0x%x, Data Type 0x%x\n", i,
966 		    ev.smbev_typev[i].smbevt_ltype,
967 		    ev.smbev_typev[i].smbevt_dtype);
968 	}
969 }
970 
971 static void
972 print_bytes(const uint8_t *data, size_t size, FILE *fp)
973 {
974 	size_t row, rows = P2ROUNDUP(size, 16) / 16;
975 	size_t col, cols;
976 
977 	char buf[17];
978 	uint8_t x;
979 
980 	oprintf(fp, "\n  offset:   0 1 2 3  4 5 6 7  8 9 a b  c d e f  "
981 	    "0123456789abcdef\n");
982 
983 	for (row = 0; row < rows; row++) {
984 		oprintf(fp, "  %#6lx: ", (ulong_t)row * 16);
985 		cols = MIN(size - row * 16, 16);
986 
987 		for (col = 0; col < cols; col++) {
988 			if (col % 4 == 0)
989 				oprintf(fp, " ");
990 			x = *data++;
991 			oprintf(fp, "%02x", x);
992 			buf[col] = x <= ' ' || x > '~' ? '.' : x;
993 		}
994 
995 		for (; col < 16; col++) {
996 			if (col % 4 == 0)
997 				oprintf(fp, " ");
998 			oprintf(fp, "  ");
999 			buf[col] = ' ';
1000 		}
1001 
1002 		buf[col] = '\0';
1003 		oprintf(fp, "  %s\n", buf);
1004 	}
1005 
1006 	oprintf(fp, "\n");
1007 }
1008 
1009 static void
1010 print_memarray(smbios_hdl_t *shp, id_t id, FILE *fp)
1011 {
1012 	smbios_memarray_t ma;
1013 
1014 	if (smbios_info_memarray(shp, id, &ma) != 0) {
1015 		smbios_warn(shp, "failed to read memarray information");
1016 		return;
1017 	}
1018 
1019 	desc_printf(smbios_memarray_loc_desc(ma.smbma_location),
1020 	    fp, "  Location: %u", ma.smbma_location);
1021 
1022 	desc_printf(smbios_memarray_use_desc(ma.smbma_use),
1023 	    fp, "  Use: %u", ma.smbma_use);
1024 
1025 	desc_printf(smbios_memarray_ecc_desc(ma.smbma_ecc),
1026 	    fp, "  ECC: %u", ma.smbma_ecc);
1027 
1028 	oprintf(fp, "  Number of Slots/Sockets: %u\n", ma.smbma_ndevs);
1029 	id_printf(fp, "  Memory Error Data: ", ma.smbma_err);
1030 	oprintf(fp, "  Max Capacity: %llu bytes\n",
1031 	    (u_longlong_t)ma.smbma_size);
1032 }
1033 
1034 static void
1035 print_memdevice(smbios_hdl_t *shp, id_t id, FILE *fp)
1036 {
1037 	smbios_memdevice_t md;
1038 
1039 	if (smbios_info_memdevice(shp, id, &md) != 0) {
1040 		smbios_warn(shp, "failed to read memory device information");
1041 		return;
1042 	}
1043 
1044 	id_printf(fp, "  Physical Memory Array: ", md.smbmd_array);
1045 	id_printf(fp, "  Memory Error Data: ", md.smbmd_error);
1046 
1047 	if (md.smbmd_twidth != -1u)
1048 		oprintf(fp, "  Total Width: %u bits\n", md.smbmd_twidth);
1049 	else
1050 		oprintf(fp, "  Total Width: Unknown\n");
1051 
1052 	if (md.smbmd_dwidth != -1u)
1053 		oprintf(fp, "  Data Width: %u bits\n", md.smbmd_dwidth);
1054 	else
1055 		oprintf(fp, "  Data Width: Unknown\n");
1056 
1057 	switch (md.smbmd_size) {
1058 	case -1ull:
1059 		oprintf(fp, "  Size: Unknown\n");
1060 		break;
1061 	case 0:
1062 		oprintf(fp, "  Size: Not Populated\n");
1063 		break;
1064 	default:
1065 		oprintf(fp, "  Size: %llu bytes\n",
1066 		    (u_longlong_t)md.smbmd_size);
1067 	}
1068 
1069 	desc_printf(smbios_memdevice_form_desc(md.smbmd_form),
1070 	    fp, "  Form Factor: %u", md.smbmd_form);
1071 
1072 	if (md.smbmd_set == 0)
1073 		oprintf(fp, "  Set: None\n");
1074 	else if (md.smbmd_set == (uint8_t)-1u)
1075 		oprintf(fp, "  Set: Unknown\n");
1076 	else
1077 		oprintf(fp, "  Set: %u\n", md.smbmd_set);
1078 
1079 	if (md.smbmd_rank != 0) {
1080 		desc_printf(smbios_memdevice_rank_desc(md.smbmd_rank),
1081 		    fp, "  Rank: %u", md.smbmd_rank);
1082 	} else {
1083 		oprintf(fp, "  Rank: Unknown\n");
1084 	}
1085 
1086 	desc_printf(smbios_memdevice_type_desc(md.smbmd_type),
1087 	    fp, "  Memory Type: %u", md.smbmd_type);
1088 
1089 	flag_printf(fp, "Flags", md.smbmd_flags, sizeof (md.smbmd_flags) * NBBY,
1090 	    smbios_memdevice_flag_name, smbios_memdevice_flag_desc);
1091 
1092 	if (md.smbmd_extspeed != 0) {
1093 		oprintf(fp, "  Speed: %" PRIu64 " MT/s\n", md.smbmd_extspeed);
1094 	} else {
1095 		oprintf(fp, "  Speed: Unknown\n");
1096 	}
1097 
1098 	if (md.smbmd_extclkspeed != 0) {
1099 		oprintf(fp, "  Configured Speed: %" PRIu64 " MT/s\n",
1100 		    md.smbmd_extclkspeed);
1101 	} else {
1102 		oprintf(fp, "  Configured Speed: Unknown\n");
1103 	}
1104 
1105 	str_print(fp, "  Device Locator", md.smbmd_dloc);
1106 	str_print(fp, "  Bank Locator", md.smbmd_bloc);
1107 
1108 	if (md.smbmd_minvolt != 0) {
1109 		oprintf(fp, "  Minimum Voltage: %.2fV\n",
1110 		    md.smbmd_minvolt / 1000.0);
1111 	} else {
1112 		oprintf(fp, "  Minimum Voltage: Unknown\n");
1113 	}
1114 
1115 	if (md.smbmd_maxvolt != 0) {
1116 		oprintf(fp, "  Maximum Voltage: %.2fV\n",
1117 		    md.smbmd_maxvolt / 1000.0);
1118 	} else {
1119 		oprintf(fp, "  Maximum Voltage: Unknown\n");
1120 	}
1121 
1122 	if (md.smbmd_confvolt != 0) {
1123 		oprintf(fp, "  Configured Voltage: %.2fV\n",
1124 		    md.smbmd_confvolt / 1000.0);
1125 	} else {
1126 		oprintf(fp, "  Configured Voltage: Unknown\n");
1127 	}
1128 
1129 	if (md.smbmd_memtech != 0) {
1130 		desc_printf(smbios_memdevice_memtech_desc(md.smbmd_memtech),
1131 		    fp, "  Memory Technology: %u", md.smbmd_memtech);
1132 	}
1133 
1134 	if (md.smbmd_opcap_flags != 0) {
1135 		flag_printf(fp, "Operating Mode Capabilities",
1136 		    md.smbmd_opcap_flags, sizeof (md.smbmd_opcap_flags) * NBBY,
1137 		    smbios_memdevice_op_capab_name,
1138 		    smbios_memdevice_op_capab_desc);
1139 	}
1140 
1141 	if (md.smbmd_firmware_rev[0] != '\0') {
1142 		str_print(fp, "  Firmware Revision", md.smbmd_firmware_rev);
1143 	}
1144 
1145 	if (md.smbmd_modmfg_id != 0) {
1146 		jedec_print(fp, "Module Manufacturer ID", md.smbmd_modmfg_id);
1147 	}
1148 
1149 	if (md.smbmd_modprod_id  != 0) {
1150 		jedec_print(fp, "Module Product ID", md.smbmd_modprod_id);
1151 	}
1152 
1153 	if (md.smbmd_cntrlmfg_id != 0) {
1154 		jedec_print(fp, "Memory Subsystem Controller Manufacturer ID",
1155 		    md.smbmd_cntrlmfg_id);
1156 	}
1157 
1158 	if (md.smbmd_cntrlprod_id != 0) {
1159 		jedec_print(fp, "Memory Subsystem Controller Product ID",
1160 		    md.smbmd_cntrlprod_id);
1161 	}
1162 
1163 	if (md.smbmd_nvsize == UINT64_MAX) {
1164 		oprintf(fp, "  Non-volatile Size: Unknown\n");
1165 	} else if (md.smbmd_nvsize != 0) {
1166 		oprintf(fp, "  Non-volatile Size: %llu bytes\n",
1167 		    (u_longlong_t)md.smbmd_nvsize);
1168 	}
1169 
1170 	if (md.smbmd_volatile_size == UINT64_MAX) {
1171 		oprintf(fp, "  Volatile Size: Unknown\n");
1172 	} else if (md.smbmd_volatile_size != 0) {
1173 		oprintf(fp, "  Volatile Size: %llu bytes\n",
1174 		    (u_longlong_t)md.smbmd_volatile_size);
1175 	}
1176 
1177 	if (md.smbmd_cache_size == UINT64_MAX) {
1178 		oprintf(fp, "  Cache Size: Unknown\n");
1179 	} else if (md.smbmd_cache_size != 0) {
1180 		oprintf(fp, "  Cache Size: %llu bytes\n",
1181 		    (u_longlong_t)md.smbmd_cache_size);
1182 	}
1183 
1184 	if (md.smbmd_logical_size == UINT64_MAX) {
1185 		oprintf(fp, "  Logical Size: Unknown\n");
1186 	} else if (md.smbmd_logical_size != 0) {
1187 		oprintf(fp, "  Logical Size: %llu bytes\n",
1188 		    (u_longlong_t)md.smbmd_logical_size);
1189 	}
1190 }
1191 
1192 static void
1193 print_memarrmap(smbios_hdl_t *shp, id_t id, FILE *fp)
1194 {
1195 	smbios_memarrmap_t ma;
1196 
1197 	if (smbios_info_memarrmap(shp, id, &ma) != 0) {
1198 		smbios_warn(shp, "failed to read memory array map information");
1199 		return;
1200 	}
1201 
1202 	id_printf(fp, "  Physical Memory Array: ", ma.smbmam_array);
1203 	oprintf(fp, "  Devices per Row: %u\n", ma.smbmam_width);
1204 
1205 	oprintf(fp, "  Physical Address: 0x%llx\n  Size: %llu bytes\n",
1206 	    (u_longlong_t)ma.smbmam_addr, (u_longlong_t)ma.smbmam_size);
1207 }
1208 
1209 static void
1210 print_memdevmap(smbios_hdl_t *shp, id_t id, FILE *fp)
1211 {
1212 	smbios_memdevmap_t md;
1213 
1214 	if (smbios_info_memdevmap(shp, id, &md) != 0) {
1215 		smbios_warn(shp, "failed to read memory device map "
1216 		    "information");
1217 		return;
1218 	}
1219 
1220 	id_printf(fp, "  Memory Device: ", md.smbmdm_device);
1221 	id_printf(fp, "  Memory Array Mapped Address: ", md.smbmdm_arrmap);
1222 
1223 	oprintf(fp, "  Physical Address: 0x%llx\n  Size: %llu bytes\n",
1224 	    (u_longlong_t)md.smbmdm_addr, (u_longlong_t)md.smbmdm_size);
1225 
1226 	oprintf(fp, "  Partition Row Position: %u\n", md.smbmdm_rpos);
1227 	oprintf(fp, "  Interleave Position: %u\n", md.smbmdm_ipos);
1228 	oprintf(fp, "  Interleave Data Depth: %u\n", md.smbmdm_idepth);
1229 }
1230 
1231 static void
1232 print_hwsec(smbios_hdl_t *shp, FILE *fp)
1233 {
1234 	smbios_hwsec_t h;
1235 
1236 	if (smbios_info_hwsec(shp, &h) == -1) {
1237 		smbios_warn(shp, "failed to read hwsec information");
1238 		return;
1239 	}
1240 
1241 	desc_printf(smbios_hwsec_desc(h.smbh_pwr_ps),
1242 	    fp, "  Power-On Password Status: %u", h.smbh_pwr_ps);
1243 	desc_printf(smbios_hwsec_desc(h.smbh_kbd_ps),
1244 	    fp, "  Keyboard Password Status: %u", h.smbh_kbd_ps);
1245 	desc_printf(smbios_hwsec_desc(h.smbh_adm_ps),
1246 	    fp, "  Administrator Password Status: %u", h.smbh_adm_ps);
1247 	desc_printf(smbios_hwsec_desc(h.smbh_pan_ps),
1248 	    fp, "  Front Panel Reset Status: %u", h.smbh_pan_ps);
1249 }
1250 
1251 static void
1252 print_vprobe(smbios_hdl_t *shp, id_t id, FILE *fp)
1253 {
1254 	smbios_vprobe_t vp;
1255 
1256 	if (smbios_info_vprobe(shp, id, &vp) != 0) {
1257 		smbios_warn(shp, "failed to read voltage probe information");
1258 		return;
1259 	}
1260 
1261 	str_print(fp, "  Description", vp.smbvp_description != NULL ?
1262 	    vp.smbvp_description : "unknown");
1263 	desc_printf(smbios_vprobe_loc_desc(vp.smbvp_location),
1264 	    fp, "  Location: %u", vp.smbvp_location);
1265 	desc_printf(smbios_vprobe_status_desc(vp.smbvp_status),
1266 	    fp, "  Status: %u", vp.smbvp_status);
1267 
1268 	if (vp.smbvp_maxval != SMB_PROBE_UNKNOWN_VALUE) {
1269 		oprintf(fp, "  Maximum Possible Voltage: %u mV\n",
1270 		    vp.smbvp_maxval);
1271 	} else {
1272 		oprintf(fp, "  Maximum Possible Voltage: unknown\n");
1273 	}
1274 
1275 	if (vp.smbvp_minval != SMB_PROBE_UNKNOWN_VALUE) {
1276 		oprintf(fp, "  Minimum Possible Voltage: %u mV\n",
1277 		    vp.smbvp_minval);
1278 	} else {
1279 		oprintf(fp, "  Minimum Possible Voltage: unknown\n");
1280 	}
1281 
1282 	if (vp.smbvp_resolution != SMB_PROBE_UNKNOWN_VALUE) {
1283 		oprintf(fp, "  Probe Resolution: %u.%u mV\n",
1284 		    vp.smbvp_resolution / 10,
1285 		    vp.smbvp_resolution % 10);
1286 	} else {
1287 		oprintf(fp, "  Probe Resolution: unknown\n");
1288 	}
1289 
1290 	if (vp.smbvp_tolerance != SMB_PROBE_UNKNOWN_VALUE) {
1291 		oprintf(fp, "  Probe Tolerance: +/-%u mV\n",
1292 		    vp.smbvp_tolerance);
1293 	} else {
1294 		oprintf(fp, "  Probe Tolerance: unknown\n");
1295 	}
1296 
1297 	if (vp.smbvp_accuracy != SMB_PROBE_UNKNOWN_VALUE) {
1298 		oprintf(fp, "  Probe Accuracy: +/-%u.%02u%%\n",
1299 		    vp.smbvp_accuracy / 100,
1300 		    vp.smbvp_accuracy % 100);
1301 	} else {
1302 		oprintf(fp, "  Probe Accuracy: unknown\n");
1303 	}
1304 
1305 	oprintf(fp, "  OEM- or BIOS- defined value: 0x%x\n", vp.smbvp_oem);
1306 
1307 	if (vp.smbvp_nominal != SMB_PROBE_UNKNOWN_VALUE) {
1308 		oprintf(fp, "  Probe Nominal Value: %u mV\n", vp.smbvp_nominal);
1309 	} else {
1310 		oprintf(fp, "  Probe Nominal Value: unknown\n");
1311 	}
1312 }
1313 
1314 static void
1315 print_cooldev(smbios_hdl_t *shp, id_t id, FILE *fp)
1316 {
1317 	smbios_cooldev_t cd;
1318 
1319 	if (smbios_info_cooldev(shp, id, &cd) != 0) {
1320 		smbios_warn(shp, "failed to read cooling device "
1321 		    "information");
1322 		return;
1323 	}
1324 
1325 	id_printf(fp, "  Temperature Probe Handle: ", cd.smbcd_tprobe);
1326 	desc_printf(smbios_cooldev_type_desc(cd.smbcd_type),
1327 	    fp, "  Device Type: %u", cd.smbcd_type);
1328 	desc_printf(smbios_cooldev_status_desc(cd.smbcd_status),
1329 	    fp, "  Status: %u", cd.smbcd_status);
1330 	oprintf(fp, "  Cooling Unit Group: %u\n", cd.smbcd_group);
1331 	oprintf(fp, "  OEM- or BIOS- defined data: 0x%x\n", cd.smbcd_oem);
1332 	if (cd.smbcd_nominal != SMB_PROBE_UNKNOWN_VALUE) {
1333 		oprintf(fp, "  Nominal Speed: %u RPM\n", cd.smbcd_nominal);
1334 	} else {
1335 		oprintf(fp, "  Nominal Speed: unknown\n");
1336 	}
1337 
1338 	if (cd.smbcd_descr != NULL && cd.smbcd_descr[0] != '\0') {
1339 		str_print(fp, "  Description", cd.smbcd_descr);
1340 	}
1341 }
1342 
1343 static void
1344 print_tprobe(smbios_hdl_t *shp, id_t id, FILE *fp)
1345 {
1346 	smbios_tprobe_t tp;
1347 
1348 	if (smbios_info_tprobe(shp, id, &tp) != 0) {
1349 		smbios_warn(shp, "failed to read temperature probe "
1350 		    "information");
1351 		return;
1352 	}
1353 
1354 	str_print(fp, "  Description", tp.smbtp_description != NULL ?
1355 	    tp.smbtp_description : "unknown");
1356 	desc_printf(smbios_tprobe_loc_desc(tp.smbtp_location),
1357 	    fp, "  Location: %u", tp.smbtp_location);
1358 	desc_printf(smbios_tprobe_status_desc(tp.smbtp_status),
1359 	    fp, "  Status: %u", tp.smbtp_status);
1360 
1361 	if (tp.smbtp_maxval != SMB_PROBE_UNKNOWN_VALUE) {
1362 		oprintf(fp, "  Maximum Possible Temperature: %u.%u C\n",
1363 		    tp.smbtp_maxval / 10, tp.smbtp_maxval % 10);
1364 	} else {
1365 		oprintf(fp, "  Maximum Possible Temperature: unknown\n");
1366 	}
1367 
1368 	if (tp.smbtp_minval != SMB_PROBE_UNKNOWN_VALUE) {
1369 		oprintf(fp, "  Minimum Possible Temperature: %u.%u C\n",
1370 		    tp.smbtp_minval / 10, tp.smbtp_minval % 10);
1371 	} else {
1372 		oprintf(fp, "  Minimum Possible Temperature: unknown\n");
1373 	}
1374 
1375 	if (tp.smbtp_resolution != SMB_PROBE_UNKNOWN_VALUE) {
1376 		oprintf(fp, "  Probe Resolution: %u.%03u C\n",
1377 		    tp.smbtp_resolution / 1000,
1378 		    tp.smbtp_resolution % 1000);
1379 	} else {
1380 		oprintf(fp, "  Probe Resolution: unknown\n");
1381 	}
1382 
1383 	if (tp.smbtp_tolerance != SMB_PROBE_UNKNOWN_VALUE) {
1384 		oprintf(fp, "  Probe Tolerance: +/-%u.%u C\n",
1385 		    tp.smbtp_tolerance / 10, tp.smbtp_tolerance % 10);
1386 	} else {
1387 		oprintf(fp, "  Probe Tolerance: unknown\n");
1388 	}
1389 
1390 	if (tp.smbtp_accuracy != SMB_PROBE_UNKNOWN_VALUE) {
1391 		oprintf(fp, "  Probe Accuracy: +/-%u.%02u%%\n",
1392 		    tp.smbtp_accuracy / 100,
1393 		    tp.smbtp_accuracy % 100);
1394 	} else {
1395 		oprintf(fp, "  Probe Accuracy: unknown\n");
1396 	}
1397 
1398 	oprintf(fp, "  OEM- or BIOS- defined value: 0x%x\n", tp.smbtp_oem);
1399 
1400 	if (tp.smbtp_nominal != SMB_PROBE_UNKNOWN_VALUE) {
1401 		oprintf(fp, "  Probe Nominal Value: %u.%u C\n",
1402 		    tp.smbtp_nominal / 10, tp.smbtp_nominal % 10);
1403 	} else {
1404 		oprintf(fp, "  Probe Nominal Value: unknown\n");
1405 	}
1406 }
1407 
1408 static void
1409 print_iprobe(smbios_hdl_t *shp, id_t id, FILE *fp)
1410 {
1411 	smbios_iprobe_t ip;
1412 
1413 	if (smbios_info_iprobe(shp, id, &ip) != 0) {
1414 		smbios_warn(shp, "failed to read current probe information");
1415 		return;
1416 	}
1417 
1418 	str_print(fp, "  Description", ip.smbip_description != NULL ?
1419 	    ip.smbip_description : "unknown");
1420 	desc_printf(smbios_iprobe_loc_desc(ip.smbip_location),
1421 	    fp, "  Location: %u", ip.smbip_location);
1422 	desc_printf(smbios_iprobe_status_desc(ip.smbip_status),
1423 	    fp, "  Status: %u", ip.smbip_status);
1424 
1425 	if (ip.smbip_maxval != SMB_PROBE_UNKNOWN_VALUE) {
1426 		oprintf(fp, "  Maximum Possible Current: %u mA\n",
1427 		    ip.smbip_maxval);
1428 	} else {
1429 		oprintf(fp, "  Maximum Possible Current: unknown\n");
1430 	}
1431 
1432 	if (ip.smbip_minval != SMB_PROBE_UNKNOWN_VALUE) {
1433 		oprintf(fp, "  Minimum Possible Current: %u mA\n",
1434 		    ip.smbip_minval);
1435 	} else {
1436 		oprintf(fp, "  Minimum Possible Current: unknown\n");
1437 	}
1438 
1439 	if (ip.smbip_resolution != SMB_PROBE_UNKNOWN_VALUE) {
1440 		oprintf(fp, "  Probe Resolution: %u.%u mA\n",
1441 		    ip.smbip_resolution / 10,
1442 		    ip.smbip_resolution % 10);
1443 	} else {
1444 		oprintf(fp, "  Probe Resolution: unknown\n");
1445 	}
1446 
1447 	if (ip.smbip_tolerance != SMB_PROBE_UNKNOWN_VALUE) {
1448 		oprintf(fp, "  Probe Tolerance: +/-%u mA\n",
1449 		    ip.smbip_tolerance);
1450 	} else {
1451 		oprintf(fp, "  Probe Tolerance: unknown\n");
1452 	}
1453 
1454 	if (ip.smbip_accuracy != SMB_PROBE_UNKNOWN_VALUE) {
1455 		oprintf(fp, "  Probe Accuracy: +/-%u.%02u%%\n",
1456 		    ip.smbip_accuracy / 100,
1457 		    ip.smbip_accuracy % 100);
1458 	} else {
1459 		oprintf(fp, "  Probe Accuracy: unknown\n");
1460 	}
1461 
1462 	oprintf(fp, "  OEM- or BIOS- defined value: 0x%x\n", ip.smbip_oem);
1463 
1464 	if (ip.smbip_nominal != SMB_PROBE_UNKNOWN_VALUE) {
1465 		oprintf(fp, "  Probe Nominal Value: %u mA\n", ip.smbip_nominal);
1466 	} else {
1467 		oprintf(fp, "  Probe Nominal Value: unknown\n");
1468 	}
1469 }
1470 
1471 
1472 static void
1473 print_boot(smbios_hdl_t *shp, FILE *fp)
1474 {
1475 	smbios_boot_t b;
1476 
1477 	if (smbios_info_boot(shp, &b) == -1) {
1478 		smbios_warn(shp, "failed to read boot information");
1479 		return;
1480 	}
1481 
1482 	desc_printf(smbios_boot_desc(b.smbt_status),
1483 	    fp, "  Boot Status Code: 0x%x", b.smbt_status);
1484 
1485 	if (b.smbt_size != 0) {
1486 		oprintf(fp, "  Boot Data (%lu bytes):\n", (ulong_t)b.smbt_size);
1487 		print_bytes(b.smbt_data, b.smbt_size, fp);
1488 	}
1489 }
1490 
1491 static void
1492 print_ipmi(smbios_hdl_t *shp, FILE *fp)
1493 {
1494 	smbios_ipmi_t i;
1495 
1496 	if (smbios_info_ipmi(shp, &i) == -1) {
1497 		smbios_warn(shp, "failed to read ipmi information");
1498 		return;
1499 	}
1500 
1501 	desc_printf(smbios_ipmi_type_desc(i.smbip_type),
1502 	    fp, "  Type: %u", i.smbip_type);
1503 
1504 	oprintf(fp, "  BMC IPMI Version: %u.%u\n",
1505 	    i.smbip_vers.smbv_major, i.smbip_vers.smbv_minor);
1506 
1507 	oprintf(fp, "  i2c Bus Slave Address: 0x%x\n", i.smbip_i2c);
1508 	oprintf(fp, "  NV Storage Device Bus ID: 0x%x\n", i.smbip_bus);
1509 	oprintf(fp, "  BMC Base Address: 0x%llx\n", (u_longlong_t)i.smbip_addr);
1510 	oprintf(fp, "  Interrupt Number: %u\n", i.smbip_intr);
1511 	oprintf(fp, "  Register Spacing: %u\n", i.smbip_regspacing);
1512 
1513 	flag_printf(fp, "Flags", i.smbip_flags, sizeof (i.smbip_flags) * NBBY,
1514 	    smbios_ipmi_flag_name, smbios_ipmi_flag_desc);
1515 }
1516 
1517 static void
1518 print_powersup(smbios_hdl_t *shp, id_t id, FILE *fp)
1519 {
1520 	smbios_powersup_t p;
1521 
1522 	if (smbios_info_powersup(shp, id, &p) != 0) {
1523 		smbios_warn(shp, "failed to read power supply information");
1524 		return;
1525 	}
1526 
1527 	oprintf(fp, "  Power Supply Group: %u\n", p.smbps_group);
1528 	if (p.smbps_maxout != 0x8000) {
1529 		oprintf(fp, "  Maximum Output: %llu mW\n", p.smbps_maxout);
1530 	} else {
1531 		oprintf(fp, "  Maximum Output: unknown\n");
1532 	}
1533 
1534 	flag_printf(fp, "Characteristics", p.smbps_flags,
1535 	    sizeof (p.smbps_flags) * NBBY, smbios_powersup_flag_name,
1536 	    smbios_powersup_flag_desc);
1537 
1538 	desc_printf(smbios_powersup_input_desc(p.smbps_ivrs),
1539 	    fp, "  Input Voltage Range Switching: %u", p.smbps_ivrs);
1540 	desc_printf(smbios_powersup_status_desc(p.smbps_status),
1541 	    fp, "  Status: %u", p.smbps_status);
1542 	desc_printf(smbios_powersup_type_desc(p.smbps_pstype),
1543 	    fp, "  Type: %u", p.smbps_pstype);
1544 
1545 	if (p.smbps_vprobe != 0xffff) {
1546 		oprintf(fp, "  Voltage Probe Handle: %lu\n", p.smbps_vprobe);
1547 	}
1548 
1549 	if (p.smbps_cooldev != 0xffff) {
1550 		oprintf(fp, "  Cooling Device Handle: %lu\n", p.smbps_cooldev);
1551 	}
1552 
1553 	if (p.smbps_iprobe != 0xffff) {
1554 		oprintf(fp, "  Current Probe Handle: %lu\n", p.smbps_iprobe);
1555 	}
1556 }
1557 
1558 static void
1559 print_processor_info_riscv(smbios_hdl_t *shp, id_t id, FILE *fp)
1560 {
1561 	smbios_processor_info_riscv_t rv;
1562 
1563 	if (smbios_info_processor_riscv(shp, id, &rv) != 0) {
1564 		smbios_warn(shp, "failed to read RISC-V specific processor "
1565 		    "information");
1566 		return;
1567 	}
1568 
1569 	if (rv.smbpirv_boothart != 0) {
1570 		oprintf(fp, "    Boot Hart\n");
1571 	}
1572 	u128_print(fp, "    Hart ID", rv.smbpirv_hartid);
1573 	u128_print(fp, "    Vendor ID", rv.smbpirv_vendid);
1574 	u128_print(fp, "    Architecture ID", rv.smbpirv_archid);
1575 	u128_print(fp, "    Implementation ID", rv.smbpirv_machid);
1576 	flag64_printf(fp, "  ISA", rv.smbpirv_isa,
1577 	    sizeof (rv.smbpirv_isa) * NBBY, smbios_riscv_isa_name,
1578 	    smbios_riscv_isa_desc);
1579 	flag_printf(fp, "  Privilege Levels", rv.smbpirv_privlvl,
1580 	    sizeof (rv.smbpirv_privlvl) * NBBY, smbios_riscv_priv_name,
1581 	    smbios_riscv_priv_desc);
1582 	u128_print(fp, "    Machine Exception Trap Delegation",
1583 	    rv.smbpirv_metdi);
1584 	u128_print(fp, "    Machine Interrupt Trap Delegation",
1585 	    rv.smbpirv_mitdi);
1586 	desc_printf(smbios_riscv_width_desc(rv.smbpirv_xlen),
1587 	    fp, "    Register Width: 0x%x", rv.smbpirv_xlen);
1588 	desc_printf(smbios_riscv_width_desc(rv.smbpirv_mxlen),
1589 	    fp, "    M-Mode Register Width: 0x%x", rv.smbpirv_mxlen);
1590 	desc_printf(smbios_riscv_width_desc(rv.smbpirv_sxlen),
1591 	    fp, "    S-Mode Register Width: 0x%x", rv.smbpirv_sxlen);
1592 	desc_printf(smbios_riscv_width_desc(rv.smbpirv_uxlen),
1593 	    fp, "    U-Mode Register Width: 0x%x", rv.smbpirv_uxlen);
1594 }
1595 
1596 static void
1597 print_processor_info(smbios_hdl_t *shp, id_t id, FILE *fp)
1598 {
1599 	smbios_processor_info_t p;
1600 
1601 	if (smbios_info_processor_info(shp, id, &p) != 0) {
1602 		smbios_warn(shp, "failed to read processor additional "
1603 		    "information");
1604 		return;
1605 	}
1606 
1607 	id_printf(fp, "  Processor Handle: ", p.smbpi_processor);
1608 	desc_printf(smbios_processor_info_type_desc(p.smbpi_ptype),
1609 	    fp, "  Processor Type: %u", p.smbpi_ptype);
1610 
1611 	switch (p.smbpi_ptype) {
1612 	case SMB_PROCINFO_T_RV32:
1613 	case SMB_PROCINFO_T_RV64:
1614 	case SMB_PROCINFO_T_RV128:
1615 		oprintf(fp, "  RISC-V Additional Processor Information:\n");
1616 		print_processor_info_riscv(shp, id, fp);
1617 		break;
1618 	default:
1619 		break;
1620 	}
1621 }
1622 
1623 static void
1624 print_battery(smbios_hdl_t *shp, id_t id, FILE *fp)
1625 {
1626 	smbios_battery_t bat;
1627 
1628 	if (smbios_info_battery(shp, id, &bat) != 0) {
1629 		smbios_warn(shp, "failed to read battery information");
1630 		return;
1631 	}
1632 
1633 	if (bat.smbb_date != NULL) {
1634 		str_print(fp, "  Manufacture Date", bat.smbb_date);
1635 	}
1636 
1637 	if (bat.smbb_serial != NULL) {
1638 		str_print(fp, "  Serial Number", bat.smbb_serial);
1639 	}
1640 
1641 	if (bat.smbb_chem != SMB_BDC_UNKNOWN) {
1642 		desc_printf(smbios_battery_chem_desc(bat.smbb_chem),
1643 		    fp, "  Battery Chemistry: 0x%x", bat.smbb_chem);
1644 	}
1645 
1646 	if (bat.smbb_cap != 0) {
1647 		oprintf(fp, "  Design Capacity: %u mWh\n", bat.smbb_cap);
1648 	} else {
1649 		oprintf(fp, "  Design Capacity: unknown\n");
1650 	}
1651 
1652 	if (bat.smbb_volt != 0) {
1653 		oprintf(fp, "  Design Voltage: %u mV\n", bat.smbb_volt);
1654 	} else {
1655 		oprintf(fp, "  Design Voltage: unknown\n");
1656 	}
1657 
1658 	str_print(fp, "  SBDS Version Number", bat.smbb_version);
1659 	if (bat.smbb_err != UINT8_MAX) {
1660 		oprintf(fp, "  Maximum Error: %u\n", bat.smbb_err);
1661 	} else {
1662 		oprintf(fp, "  Maximum Error: unknown\n", bat.smbb_err);
1663 	}
1664 	oprintf(fp, "  SBDS Serial Number: %04x\n", bat.smbb_ssn);
1665 	oprintf(fp, "  SBDS Manufacture Date: %u-%02u-%02u\n", bat.smbb_syear,
1666 	    bat.smbb_smonth, bat.smbb_sday);
1667 	str_print(fp, "  SBDS Device Chemistry", bat.smbb_schem);
1668 	oprintf(fp, "  OEM-specific Information: 0x%08x\n", bat.smbb_oemdata);
1669 }
1670 
1671 static void
1672 print_pointdev(smbios_hdl_t *shp, id_t id, FILE *fp)
1673 {
1674 	smbios_pointdev_t pd;
1675 
1676 	if (smbios_info_pointdev(shp, id, &pd) != 0) {
1677 		smbios_warn(shp, "failed to read pointer device information");
1678 		return;
1679 	}
1680 
1681 	desc_printf(smbios_pointdev_type_desc(pd.smbpd_type),
1682 	    fp, "  Type: %u", pd.smbpd_type);
1683 	desc_printf(smbios_pointdev_iface_desc(pd.smbpd_iface),
1684 	    fp, "  Interface: %u", pd.smbpd_iface);
1685 	oprintf(fp, "  Buttons: %u\n", pd.smbpd_nbuttons);
1686 }
1687 
1688 static void
1689 print_extprocessor(smbios_hdl_t *shp, id_t id, FILE *fp)
1690 {
1691 	int i;
1692 	smbios_processor_ext_t ep;
1693 
1694 	if (check_oem(shp) != 0)
1695 		return;
1696 
1697 	if (smbios_info_extprocessor(shp, id, &ep) != 0) {
1698 		smbios_warn(shp, "failed to read extended processor "
1699 		    "information");
1700 		return;
1701 	}
1702 
1703 	oprintf(fp, "  Processor: %u\n", ep.smbpe_processor);
1704 	oprintf(fp, "  FRU: %u\n", ep.smbpe_fru);
1705 	oprintf(fp, "  Initial APIC ID count: %u\n\n", ep.smbpe_n);
1706 
1707 	for (i = 0; i < ep.smbpe_n; i++) {
1708 		oprintf(fp, "  Logical Strand %u: Initial APIC ID: %u\n", i,
1709 		    ep.smbpe_apicid[i]);
1710 	}
1711 }
1712 
1713 static void
1714 print_extport(smbios_hdl_t *shp, id_t id, FILE *fp)
1715 {
1716 	smbios_port_ext_t epo;
1717 
1718 	if (check_oem(shp) != 0)
1719 		return;
1720 
1721 	if (smbios_info_extport(shp, id, &epo) != 0) {
1722 		smbios_warn(shp, "failed to read extended port information");
1723 		return;
1724 	}
1725 
1726 	oprintf(fp, "  Chassis Handle: %u\n", epo.smbporte_chassis);
1727 	oprintf(fp, "  Port Connector Handle: %u\n", epo.smbporte_port);
1728 	oprintf(fp, "  Device Type: %u\n", epo.smbporte_dtype);
1729 	oprintf(fp, "  Device Handle: %u\n", epo.smbporte_devhdl);
1730 	oprintf(fp, "  PHY: %u\n", epo.smbporte_phy);
1731 }
1732 
1733 static void
1734 print_pciexrc(smbios_hdl_t *shp, id_t id, FILE *fp)
1735 {
1736 	smbios_pciexrc_t pcie;
1737 
1738 	if (check_oem(shp) != 0)
1739 		return;
1740 
1741 	if (smbios_info_pciexrc(shp, id, &pcie) != 0) {
1742 		smbios_warn(shp, "failed to read pciexrc information");
1743 		return;
1744 	}
1745 
1746 	oprintf(fp, "  Component ID: %u\n", pcie.smbpcie_bb);
1747 	oprintf(fp, "  BDF: 0x%x\n", pcie.smbpcie_bdf);
1748 }
1749 
1750 static void
1751 print_extmemarray(smbios_hdl_t *shp, id_t id, FILE *fp)
1752 {
1753 	smbios_memarray_ext_t em;
1754 
1755 	if (check_oem(shp) != 0)
1756 		return;
1757 
1758 	if (smbios_info_extmemarray(shp, id, &em) != 0) {
1759 		smbios_warn(shp, "failed to read extmemarray information");
1760 		return;
1761 	}
1762 
1763 	oprintf(fp, "  Physical Memory Array Handle: %u\n", em.smbmae_ma);
1764 	oprintf(fp, "  Component Parent Handle: %u\n", em.smbmae_comp);
1765 	oprintf(fp, "  BDF: 0x%x\n", em.smbmae_bdf);
1766 }
1767 
1768 static void
1769 print_extmemdevice(smbios_hdl_t *shp, id_t id, FILE *fp)
1770 {
1771 	int i;
1772 	smbios_memdevice_ext_t emd;
1773 
1774 	if (check_oem(shp) != 0)
1775 		return;
1776 
1777 	if (smbios_info_extmemdevice(shp, id, &emd) != 0) {
1778 		smbios_warn(shp, "failed to read extmemdevice information");
1779 		return;
1780 	}
1781 
1782 	oprintf(fp, "  Memory Device Handle: %u\n", emd.smbmdeve_md);
1783 	oprintf(fp, "  DRAM Channel: %u\n", emd.smbmdeve_drch);
1784 	oprintf(fp, "  Number of Chip Selects: %u\n", emd.smbmdeve_ncs);
1785 
1786 	for (i = 0; i < emd.smbmdeve_ncs; i++) {
1787 		oprintf(fp, "  Chip Select: %u\n", emd.smbmdeve_cs[i]);
1788 	}
1789 }
1790 
1791 static void
1792 print_strprop_info(smbios_hdl_t *shp, id_t id, FILE *fp)
1793 {
1794 	smbios_strprop_t prop;
1795 
1796 	if (smbios_info_strprop(shp, id, &prop) != 0) {
1797 		smbios_warn(shp, "failed to read string property information");
1798 		return;
1799 	}
1800 
1801 	desc_printf(smbios_strprop_id_desc(prop.smbsp_prop_id), fp,
1802 	    "  Property ID: %u", prop.smbsp_prop_id);
1803 	if (prop.smbsp_prop_val != NULL) {
1804 		str_print(fp, "  Property Value", prop.smbsp_prop_val);
1805 	}
1806 	id_printf(fp, "  Parent Handle: ", prop.smbsp_parent);
1807 }
1808 
1809 static void
1810 print_fwinfo(smbios_hdl_t *shp, id_t id, FILE *fp)
1811 {
1812 	smbios_fwinfo_t fw;
1813 	smbios_fwinfo_comp_t *comps;
1814 	uint_t ncomps, i;
1815 
1816 	if (smbios_info_fwinfo(shp, id, &fw) != 0) {
1817 		smbios_warn(shp, "failed to read firmware inventory");
1818 		return;
1819 	}
1820 
1821 	str_print(fp, "  Component Name", fw.smbfw_name);
1822 	str_print(fp, "  ID", fw.smbfw_id);
1823 	str_print(fp, "  Release Date", fw.smbfw_reldate);
1824 	str_print(fp, "  Lowest Supported Version", fw.smbfw_lsv);
1825 	desc_printf(smbios_fwinfo_vers_desc(fw.smbfw_vers_fmt), fp,
1826 	    "  Version Format: %u", fw.smbfw_vers_fmt);
1827 	desc_printf(smbios_fwinfo_id_desc(fw.smbfw_id_fmt), fp,
1828 	    "  ID Format: %u", fw.smbfw_id_fmt);
1829 	if (fw.smbfw_imgsz != UINT64_MAX) {
1830 		oprintf(fp, "  Image Size: %" PRIu64 "\n", fw.smbfw_imgsz);
1831 	} else {
1832 		oprintf(fp, "  Image Size: unknown\n");
1833 	}
1834 
1835 	flag_printf(fp, "Characteristics", fw.smbfw_chars,
1836 	    sizeof (fw.smbfw_chars) * NBBY, smbios_fwinfo_ch_name,
1837 	    smbios_fwinfo_ch_desc);
1838 
1839 	desc_printf(smbios_fwinfo_state_desc(fw.smbfw_state), fp, "  State: %u",
1840 	    fw.smbfw_state);
1841 	oprintf(fp, "  Number of Associated Components: %u\n",
1842 	    fw.smbfw_ncomps);
1843 
1844 	if (fw.smbfw_ncomps == 0)
1845 		return;
1846 
1847 	if (smbios_info_fwinfo_comps(shp, id, &ncomps, &comps) == -1) {
1848 		smbios_warn(shp, "failed to read firmware inventory "
1849 		    "components");
1850 		return;
1851 	}
1852 
1853 	oprintf(fp, "\n  Component Handles:\n");
1854 	for (i = 0; i < ncomps; i++) {
1855 		oprintf(fp, "    %ld\n", comps[i]);
1856 	}
1857 }
1858 
1859 static int
1860 print_struct(smbios_hdl_t *shp, const smbios_struct_t *sp, void *fp)
1861 {
1862 	smbios_info_t info;
1863 	int hex = opt_x;
1864 	const char *s;
1865 
1866 	if (opt_t != -1 && opt_t != sp->smbstr_type)
1867 		return (0); /* skip struct if type doesn't match -t */
1868 
1869 	if (!opt_O && (sp->smbstr_type == SMB_TYPE_MEMCTL ||
1870 	    sp->smbstr_type == SMB_TYPE_MEMMOD))
1871 		return (0); /* skip struct if type is obsolete */
1872 
1873 	if (g_hdr++ == 0 || !opt_s)
1874 		oprintf(fp, "%-5s %-4s %s\n", "ID", "SIZE", "TYPE");
1875 
1876 	oprintf(fp, "%-5u %-4lu",
1877 	    (uint_t)sp->smbstr_id, (ulong_t)sp->smbstr_size);
1878 
1879 	if ((s = smbios_type_name(sp->smbstr_type)) != NULL)
1880 		oprintf(fp, " %s (type %u)", s, sp->smbstr_type);
1881 	else if (sp->smbstr_type > SMB_TYPE_OEM_LO &&
1882 	    sp->smbstr_type < SMB_TYPE_OEM_HI)
1883 		oprintf(fp, " %s+%u (type %u)", "SMB_TYPE_OEM_LO",
1884 		    sp->smbstr_type - SMB_TYPE_OEM_LO, sp->smbstr_type);
1885 	else
1886 		oprintf(fp, " %u", sp->smbstr_type);
1887 
1888 	if ((s = smbios_type_desc(sp->smbstr_type)) != NULL)
1889 		oprintf(fp, " (%s)\n", s);
1890 	else
1891 		oprintf(fp, "\n");
1892 
1893 	if (opt_s)
1894 		return (0); /* only print header line if -s specified */
1895 
1896 	if (smbios_info_common(shp, sp->smbstr_id, &info) == 0) {
1897 		oprintf(fp, "\n");
1898 		print_common(&info, fp);
1899 	}
1900 
1901 	switch (sp->smbstr_type) {
1902 	case SMB_TYPE_BIOS:
1903 		oprintf(fp, "\n");
1904 		print_bios(shp, fp);
1905 		break;
1906 	case SMB_TYPE_SYSTEM:
1907 		oprintf(fp, "\n");
1908 		print_system(shp, fp);
1909 		break;
1910 	case SMB_TYPE_BASEBOARD:
1911 		oprintf(fp, "\n");
1912 		print_bboard(shp, sp->smbstr_id, fp);
1913 		break;
1914 	case SMB_TYPE_CHASSIS:
1915 		oprintf(fp, "\n");
1916 		print_chassis(shp, sp->smbstr_id, fp);
1917 		break;
1918 	case SMB_TYPE_PROCESSOR:
1919 		oprintf(fp, "\n");
1920 		print_processor(shp, sp->smbstr_id, fp);
1921 		break;
1922 	case SMB_TYPE_CACHE:
1923 		oprintf(fp, "\n");
1924 		print_cache(shp, sp->smbstr_id, fp);
1925 		break;
1926 	case SMB_TYPE_PORT:
1927 		oprintf(fp, "\n");
1928 		print_port(shp, sp->smbstr_id, fp);
1929 		break;
1930 	case SMB_TYPE_SLOT:
1931 		oprintf(fp, "\n");
1932 		print_slot(shp, sp->smbstr_id, fp);
1933 		break;
1934 	case SMB_TYPE_OBDEVS:
1935 		oprintf(fp, "\n");
1936 		print_obdevs(shp, sp->smbstr_id, fp);
1937 		break;
1938 	case SMB_TYPE_OEMSTR:
1939 	case SMB_TYPE_SYSCONFSTR:
1940 		oprintf(fp, "\n");
1941 		print_strtab(shp, sp->smbstr_id, fp);
1942 		break;
1943 	case SMB_TYPE_LANG:
1944 		oprintf(fp, "\n");
1945 		print_lang(shp, sp->smbstr_id, fp);
1946 		break;
1947 	case SMB_TYPE_EVENTLOG:
1948 		oprintf(fp, "\n");
1949 		print_evlog(shp, sp->smbstr_id, fp);
1950 		break;
1951 	case SMB_TYPE_MEMARRAY:
1952 		oprintf(fp, "\n");
1953 		print_memarray(shp, sp->smbstr_id, fp);
1954 		break;
1955 	case SMB_TYPE_MEMDEVICE:
1956 		oprintf(fp, "\n");
1957 		print_memdevice(shp, sp->smbstr_id, fp);
1958 		break;
1959 	case SMB_TYPE_MEMARRAYMAP:
1960 		oprintf(fp, "\n");
1961 		print_memarrmap(shp, sp->smbstr_id, fp);
1962 		break;
1963 	case SMB_TYPE_MEMDEVICEMAP:
1964 		oprintf(fp, "\n");
1965 		print_memdevmap(shp, sp->smbstr_id, fp);
1966 		break;
1967 	case SMB_TYPE_BATTERY:
1968 		oprintf(fp, "\n");
1969 		print_battery(shp, sp->smbstr_id, fp);
1970 		break;
1971 	case SMB_TYPE_POINTDEV:
1972 		oprintf(fp, "\n");
1973 		print_pointdev(shp, sp->smbstr_id, fp);
1974 		break;
1975 	case SMB_TYPE_SECURITY:
1976 		oprintf(fp, "\n");
1977 		print_hwsec(shp, fp);
1978 		break;
1979 	case SMB_TYPE_VPROBE:
1980 		oprintf(fp, "\n");
1981 		print_vprobe(shp, sp->smbstr_id, fp);
1982 		break;
1983 	case SMB_TYPE_COOLDEV:
1984 		oprintf(fp, "\n");
1985 		print_cooldev(shp, sp->smbstr_id, fp);
1986 		break;
1987 	case SMB_TYPE_TPROBE:
1988 		oprintf(fp, "\n");
1989 		print_tprobe(shp, sp->smbstr_id, fp);
1990 		break;
1991 	case SMB_TYPE_IPROBE:
1992 		oprintf(fp, "\n");
1993 		print_iprobe(shp, sp->smbstr_id, fp);
1994 		break;
1995 	case SMB_TYPE_BOOT:
1996 		oprintf(fp, "\n");
1997 		print_boot(shp, fp);
1998 		break;
1999 	case SMB_TYPE_IPMIDEV:
2000 		oprintf(fp, "\n");
2001 		print_ipmi(shp, fp);
2002 		break;
2003 	case SMB_TYPE_POWERSUP:
2004 		oprintf(fp, "\n");
2005 		print_powersup(shp, sp->smbstr_id, fp);
2006 		break;
2007 	case SMB_TYPE_OBDEVEXT:
2008 		oprintf(fp, "\n");
2009 		print_obdevs_ext(shp, sp->smbstr_id, fp);
2010 		break;
2011 	case SMB_TYPE_PROCESSOR_INFO:
2012 		oprintf(fp, "\n");
2013 		print_processor_info(shp, sp->smbstr_id, fp);
2014 		break;
2015 	case SMB_TYPE_STRPROP:
2016 		oprintf(fp, "\n");
2017 		print_strprop_info(shp, sp->smbstr_id, fp);
2018 		break;
2019 	case SMB_TYPE_FWINFO:
2020 		oprintf(fp, "\n");
2021 		print_fwinfo(shp, sp->smbstr_id, fp);
2022 		break;
2023 	case SUN_OEM_EXT_PROCESSOR:
2024 		oprintf(fp, "\n");
2025 		print_extprocessor(shp, sp->smbstr_id, fp);
2026 		break;
2027 	case SUN_OEM_EXT_PORT:
2028 		oprintf(fp, "\n");
2029 		print_extport(shp, sp->smbstr_id, fp);
2030 		break;
2031 	case SUN_OEM_PCIEXRC:
2032 		oprintf(fp, "\n");
2033 		print_pciexrc(shp, sp->smbstr_id, fp);
2034 		break;
2035 	case SUN_OEM_EXT_MEMARRAY:
2036 		oprintf(fp, "\n");
2037 		print_extmemarray(shp, sp->smbstr_id, fp);
2038 		break;
2039 	case SUN_OEM_EXT_MEMDEVICE:
2040 		oprintf(fp, "\n");
2041 		print_extmemdevice(shp, sp->smbstr_id, fp);
2042 		break;
2043 	default:
2044 		hex++;
2045 	}
2046 
2047 	if (hex)
2048 		print_bytes(sp->smbstr_data, sp->smbstr_size, fp);
2049 	else
2050 		oprintf(fp, "\n");
2051 
2052 	return (0);
2053 }
2054 
2055 static uint16_t
2056 getu16(const char *name, const char *s)
2057 {
2058 	u_longlong_t val;
2059 	char *p;
2060 
2061 	errno = 0;
2062 	val = strtoull(s, &p, 0);
2063 
2064 	if (errno != 0 || p == s || *p != '\0' || val > UINT16_MAX) {
2065 		(void) fprintf(stderr, "%s: invalid %s argument -- %s\n",
2066 		    g_pname, name, s);
2067 		exit(SMBIOS_USAGE);
2068 	}
2069 
2070 	return ((uint16_t)val);
2071 }
2072 
2073 static uint16_t
2074 getstype(const char *name, const char *s)
2075 {
2076 	const char *ts;
2077 	uint16_t t;
2078 
2079 	for (t = 0; t < SMB_TYPE_OEM_LO; t++) {
2080 		if ((ts = smbios_type_name(t)) != NULL && strcmp(s, ts) == 0)
2081 			return (t);
2082 	}
2083 
2084 	(void) fprintf(stderr, "%s: invalid %s argument -- %s\n",
2085 	    g_pname, name, s);
2086 
2087 	exit(SMBIOS_USAGE);
2088 	/*NOTREACHED*/
2089 }
2090 
2091 static int
2092 usage(FILE *fp)
2093 {
2094 	(void) fprintf(fp, "Usage: %s "
2095 	    "[-BeOsx] [-i id] [-t type] [-w file] [file]\n\n", g_pname);
2096 
2097 	(void) fprintf(fp,
2098 	    "\t-B disable header validation for broken BIOSes\n"
2099 	    "\t-e display SMBIOS entry point information\n"
2100 	    "\t-i display only the specified structure\n"
2101 	    "\t-O display obsolete structure types\n"
2102 	    "\t-s display only a summary of structure identifiers and types\n"
2103 	    "\t-t display only the specified structure type\n"
2104 	    "\t-w write the raw data to the specified file\n"
2105 	    "\t-x display raw data for structures\n");
2106 
2107 	return (SMBIOS_USAGE);
2108 }
2109 
2110 int
2111 main(int argc, char *argv[])
2112 {
2113 	const char *ifile = NULL;
2114 	const char *ofile = NULL;
2115 	int oflags = 0;
2116 
2117 	smbios_hdl_t *shp;
2118 	smbios_struct_t s;
2119 	int err, fd, c;
2120 	char *p;
2121 
2122 	if ((p = strrchr(argv[0], '/')) == NULL)
2123 		g_pname = argv[0];
2124 	else
2125 		g_pname = p + 1;
2126 
2127 	while (optind < argc) {
2128 		while ((c = getopt(argc, argv, "Bei:Ost:w:xZ")) != EOF) {
2129 			switch (c) {
2130 			case 'B':
2131 				oflags |= SMB_O_NOCKSUM | SMB_O_NOVERS;
2132 				break;
2133 			case 'e':
2134 				opt_e++;
2135 				break;
2136 			case 'i':
2137 				opt_i = getu16("struct ID", optarg);
2138 				break;
2139 			case 'O':
2140 				opt_O++;
2141 				break;
2142 			case 's':
2143 				opt_s++;
2144 				break;
2145 			case 't':
2146 				if (isdigit(optarg[0]))
2147 					opt_t = getu16("struct type", optarg);
2148 				else
2149 					opt_t = getstype("struct type", optarg);
2150 				break;
2151 			case 'w':
2152 				ofile = optarg;
2153 				break;
2154 			case 'x':
2155 				opt_x++;
2156 				break;
2157 			case 'Z':
2158 				oflags |= SMB_O_ZIDS; /* undocumented */
2159 				break;
2160 			default:
2161 				return (usage(stderr));
2162 			}
2163 		}
2164 
2165 		if (optind < argc) {
2166 			if (ifile != NULL) {
2167 				(void) fprintf(stderr, "%s: illegal "
2168 				    "argument -- %s\n", g_pname, argv[optind]);
2169 				return (SMBIOS_USAGE);
2170 			}
2171 			ifile = argv[optind++];
2172 		}
2173 	}
2174 
2175 	if ((shp = smbios_open(ifile, SMB_VERSION, oflags, &err)) == NULL) {
2176 		(void) fprintf(stderr, "%s: failed to load SMBIOS: %s\n",
2177 		    g_pname, smbios_errmsg(err));
2178 		return (SMBIOS_ERROR);
2179 	}
2180 
2181 	if (opt_i == -1 && opt_t == -1 && opt_e == 0 &&
2182 	    smbios_truncated(shp))
2183 		(void) fprintf(stderr, "%s: SMBIOS table is truncated\n",
2184 		    g_pname);
2185 
2186 	if (ofile != NULL) {
2187 		if ((fd = open(ofile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) {
2188 			(void) fprintf(stderr, "%s: failed to open %s: %s\n",
2189 			    g_pname, ofile, strerror(errno));
2190 			err = SMBIOS_ERROR;
2191 		} else if (smbios_write(shp, fd) != 0) {
2192 			(void) fprintf(stderr, "%s: failed to write %s: %s\n",
2193 			    g_pname, ofile, smbios_errmsg(smbios_errno(shp)));
2194 			err = SMBIOS_ERROR;
2195 		}
2196 		smbios_close(shp);
2197 		return (err);
2198 	}
2199 
2200 	if (opt_e) {
2201 		print_smbios(shp, stdout);
2202 		smbios_close(shp);
2203 		return (SMBIOS_SUCCESS);
2204 	}
2205 
2206 	if (opt_O && (opt_i != -1 || opt_t != -1))
2207 		opt_O++; /* -i or -t imply displaying obsolete records */
2208 
2209 	if (opt_i != -1)
2210 		err = smbios_lookup_id(shp, opt_i, &s);
2211 	else
2212 		err = smbios_iter(shp, print_struct, stdout);
2213 
2214 	if (err != 0) {
2215 		(void) fprintf(stderr, "%s: failed to access SMBIOS: %s\n",
2216 		    g_pname, smbios_errmsg(smbios_errno(shp)));
2217 		smbios_close(shp);
2218 		return (SMBIOS_ERROR);
2219 	}
2220 
2221 	if (opt_i != -1)
2222 		(void) print_struct(shp, &s, stdout);
2223 
2224 	smbios_close(shp);
2225 	return (SMBIOS_SUCCESS);
2226 }
2227