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