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