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 2024 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
smbios_vergteq(smbios_version_t * v,uint_t major,uint_t minor)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
smbios_warn(smbios_hdl_t * shp,const char * format,...)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
oprintf(FILE * fp,const char * format,...)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
desc_printf(const char * d,FILE * fp,const char * format,...)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
flag_printf(FILE * fp,const char * s,uint_t flags,size_t bits,const char * (* flag_name)(uint_t),const char * (* flag_desc)(uint_t))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
flag64_printf(FILE * fp,const char * s,uint64_t flags,size_t bits,const char * (* flag_name)(uint64_t),const char * (* flag_desc)(uint64_t))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
id_printf(FILE * fp,const char * s,id_t id)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
jedec_print(FILE * fp,const char * desc,uint_t id)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
jedec_rev_print(FILE * fp,const char * desc,uint16_t raw_rev)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
u128_print(FILE * fp,const char * desc,const uint8_t * data)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
str_print_label(FILE * fp,const char * header,const char * str,boolean_t label)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
str_print_nolabel(FILE * fp,const char * ws,const char * str)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
str_print(FILE * fp,const char * header,const char * str)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
check_oem(smbios_hdl_t * shp)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
print_smbios_21(smbios_21_entry_t * ep,FILE * fp)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
print_smbios_30(smbios_30_entry_t * ep,FILE * fp)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
print_smbios(smbios_hdl_t * shp,FILE * fp)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
print_common(const smbios_info_t * ip,FILE * fp)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
print_bios(smbios_hdl_t * shp,FILE * fp)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
print_system(smbios_hdl_t * shp,FILE * fp)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
print_bboard(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_chassis(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_processor(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_cache(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_port(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_slot(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_obdevs_ext(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_obdevs(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_strtab(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_lang(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_evlog(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_bytes(const uint8_t * data,size_t size,FILE * fp)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
print_memarray(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_memdevice(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_memarrmap(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_memdevmap(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_hwsec(smbios_hdl_t * shp,FILE * fp)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
print_vprobe(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_cooldev(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_tprobe(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_iprobe(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_boot(smbios_hdl_t * shp,FILE * fp)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
print_ipmi(smbios_hdl_t * shp,FILE * fp)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
print_powersup(smbios_hdl_t * shp,id_t id,FILE * fp)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
print_addinfo(smbios_hdl_t * shp,id_t id,FILE * fp)1593 print_addinfo(smbios_hdl_t *shp, id_t id, FILE *fp)
1594 {
1595 uint_t nents, i;
1596
1597 if (smbios_info_addinfo_nents(shp, id, &nents) != 0) {
1598 smbios_warn(shp, "failed to read additional information");
1599 return;
1600 }
1601
1602 oprintf(fp, " Number of Additional Information Entries: %u\n", nents);
1603 for (i = 0; i < nents; i++) {
1604 smbios_addinfo_ent_t *ent;
1605
1606 oprintf(fp, " Additional Information Entry %u\n", i);
1607 if (smbios_info_addinfo_ent(shp, id, i, &ent) != 0) {
1608 smbios_warn(shp, "failed to read additional "
1609 "information entry %u", i);
1610 continue;
1611 }
1612
1613 oprintf(fp, " Referenced handle: %lu\n", ent->smbai_ref);
1614 oprintf(fp, " Handle offset: %u\n", ent->smbai_ref_off);
1615 if (ent->smbai_str != NULL) {
1616 str_print(fp, " Information String", ent->smbai_str);
1617 }
1618
1619 /*
1620 * As of SMBIOS 3.7, there are no extra data entries strictly
1621 * defined in the spec, but there may be something. If we find
1622 * something that's a standard integer size, then we'll
1623 * interpret it and print it as a hex value. In theory this is
1624 * supposed to refer back to some field, but hard to say how
1625 * this'll actually be used. The first time we encountered it
1626 * was just an additional string entry.
1627 */
1628 if (ent->smbai_dlen > 0) {
1629 oprintf(fp, " Data Length: %u\n", ent->smbai_dlen);
1630 switch (ent->smbai_dlen) {
1631 case 1:
1632 oprintf(fp, " Data: 0x%x\n",
1633 *(uint8_t *)ent->smbai_data);
1634 break;
1635 case 2:
1636 oprintf(fp, " Data: 0x%x\n",
1637 *(uint16_t *)ent->smbai_data);
1638 break;
1639 case 4:
1640 oprintf(fp, " Data: 0x%x\n",
1641 *(uint32_t *)ent->smbai_data);
1642 break;
1643 case 8:
1644 oprintf(fp, " Data: 0x%x\n",
1645 *(uint64_t *)ent->smbai_data);
1646 break;
1647 default:
1648 break;
1649 }
1650 }
1651
1652 smbios_info_addinfo_ent_free(shp, ent);
1653 }
1654 }
1655
1656
1657 static void
print_processor_info_riscv(smbios_hdl_t * shp,id_t id,FILE * fp)1658 print_processor_info_riscv(smbios_hdl_t *shp, id_t id, FILE *fp)
1659 {
1660 smbios_processor_info_riscv_t rv;
1661
1662 if (smbios_info_processor_riscv(shp, id, &rv) != 0) {
1663 smbios_warn(shp, "failed to read RISC-V specific processor "
1664 "information");
1665 return;
1666 }
1667
1668 if (rv.smbpirv_boothart != 0) {
1669 oprintf(fp, " Boot Hart\n");
1670 }
1671 u128_print(fp, " Hart ID", rv.smbpirv_hartid);
1672 u128_print(fp, " Vendor ID", rv.smbpirv_vendid);
1673 u128_print(fp, " Architecture ID", rv.smbpirv_archid);
1674 u128_print(fp, " Implementation ID", rv.smbpirv_machid);
1675 flag64_printf(fp, " ISA", rv.smbpirv_isa,
1676 sizeof (rv.smbpirv_isa) * NBBY, smbios_riscv_isa_name,
1677 smbios_riscv_isa_desc);
1678 flag_printf(fp, " Privilege Levels", rv.smbpirv_privlvl,
1679 sizeof (rv.smbpirv_privlvl) * NBBY, smbios_riscv_priv_name,
1680 smbios_riscv_priv_desc);
1681 u128_print(fp, " Machine Exception Trap Delegation",
1682 rv.smbpirv_metdi);
1683 u128_print(fp, " Machine Interrupt Trap Delegation",
1684 rv.smbpirv_mitdi);
1685 desc_printf(smbios_riscv_width_desc(rv.smbpirv_xlen),
1686 fp, " Register Width: 0x%x", rv.smbpirv_xlen);
1687 desc_printf(smbios_riscv_width_desc(rv.smbpirv_mxlen),
1688 fp, " M-Mode Register Width: 0x%x", rv.smbpirv_mxlen);
1689 desc_printf(smbios_riscv_width_desc(rv.smbpirv_sxlen),
1690 fp, " S-Mode Register Width: 0x%x", rv.smbpirv_sxlen);
1691 desc_printf(smbios_riscv_width_desc(rv.smbpirv_uxlen),
1692 fp, " U-Mode Register Width: 0x%x", rv.smbpirv_uxlen);
1693 }
1694
1695 static void
print_processor_info(smbios_hdl_t * shp,id_t id,FILE * fp)1696 print_processor_info(smbios_hdl_t *shp, id_t id, FILE *fp)
1697 {
1698 smbios_processor_info_t p;
1699
1700 if (smbios_info_processor_info(shp, id, &p) != 0) {
1701 smbios_warn(shp, "failed to read processor additional "
1702 "information");
1703 return;
1704 }
1705
1706 id_printf(fp, " Processor Handle: ", p.smbpi_processor);
1707 desc_printf(smbios_processor_info_type_desc(p.smbpi_ptype),
1708 fp, " Processor Type: %u", p.smbpi_ptype);
1709
1710 switch (p.smbpi_ptype) {
1711 case SMB_PROCINFO_T_RV32:
1712 case SMB_PROCINFO_T_RV64:
1713 case SMB_PROCINFO_T_RV128:
1714 oprintf(fp, " RISC-V Additional Processor Information:\n");
1715 print_processor_info_riscv(shp, id, fp);
1716 break;
1717 default:
1718 break;
1719 }
1720 }
1721
1722 static void
print_battery(smbios_hdl_t * shp,id_t id,FILE * fp)1723 print_battery(smbios_hdl_t *shp, id_t id, FILE *fp)
1724 {
1725 smbios_battery_t bat;
1726
1727 if (smbios_info_battery(shp, id, &bat) != 0) {
1728 smbios_warn(shp, "failed to read battery information");
1729 return;
1730 }
1731
1732 if (bat.smbb_date != NULL) {
1733 str_print(fp, " Manufacture Date", bat.smbb_date);
1734 }
1735
1736 if (bat.smbb_serial != NULL) {
1737 str_print(fp, " Serial Number", bat.smbb_serial);
1738 }
1739
1740 if (bat.smbb_chem != SMB_BDC_UNKNOWN) {
1741 desc_printf(smbios_battery_chem_desc(bat.smbb_chem),
1742 fp, " Battery Chemistry: 0x%x", bat.smbb_chem);
1743 }
1744
1745 if (bat.smbb_cap != 0) {
1746 oprintf(fp, " Design Capacity: %u mWh\n", bat.smbb_cap);
1747 } else {
1748 oprintf(fp, " Design Capacity: unknown\n");
1749 }
1750
1751 if (bat.smbb_volt != 0) {
1752 oprintf(fp, " Design Voltage: %u mV\n", bat.smbb_volt);
1753 } else {
1754 oprintf(fp, " Design Voltage: unknown\n");
1755 }
1756
1757 str_print(fp, " SBDS Version Number", bat.smbb_version);
1758 if (bat.smbb_err != UINT8_MAX) {
1759 oprintf(fp, " Maximum Error: %u\n", bat.smbb_err);
1760 } else {
1761 oprintf(fp, " Maximum Error: unknown\n", bat.smbb_err);
1762 }
1763 oprintf(fp, " SBDS Serial Number: %04x\n", bat.smbb_ssn);
1764 oprintf(fp, " SBDS Manufacture Date: %u-%02u-%02u\n", bat.smbb_syear,
1765 bat.smbb_smonth, bat.smbb_sday);
1766 str_print(fp, " SBDS Device Chemistry", bat.smbb_schem);
1767 oprintf(fp, " OEM-specific Information: 0x%08x\n", bat.smbb_oemdata);
1768 }
1769
1770 static void
print_pointdev(smbios_hdl_t * shp,id_t id,FILE * fp)1771 print_pointdev(smbios_hdl_t *shp, id_t id, FILE *fp)
1772 {
1773 smbios_pointdev_t pd;
1774
1775 if (smbios_info_pointdev(shp, id, &pd) != 0) {
1776 smbios_warn(shp, "failed to read pointer device information");
1777 return;
1778 }
1779
1780 desc_printf(smbios_pointdev_type_desc(pd.smbpd_type),
1781 fp, " Type: %u", pd.smbpd_type);
1782 desc_printf(smbios_pointdev_iface_desc(pd.smbpd_iface),
1783 fp, " Interface: %u", pd.smbpd_iface);
1784 oprintf(fp, " Buttons: %u\n", pd.smbpd_nbuttons);
1785 }
1786
1787 static void
print_extprocessor(smbios_hdl_t * shp,id_t id,FILE * fp)1788 print_extprocessor(smbios_hdl_t *shp, id_t id, FILE *fp)
1789 {
1790 int i;
1791 smbios_processor_ext_t ep;
1792
1793 if (check_oem(shp) != 0)
1794 return;
1795
1796 if (smbios_info_extprocessor(shp, id, &ep) != 0) {
1797 smbios_warn(shp, "failed to read extended processor "
1798 "information");
1799 return;
1800 }
1801
1802 oprintf(fp, " Processor: %u\n", ep.smbpe_processor);
1803 oprintf(fp, " FRU: %u\n", ep.smbpe_fru);
1804 oprintf(fp, " Initial APIC ID count: %u\n\n", ep.smbpe_n);
1805
1806 for (i = 0; i < ep.smbpe_n; i++) {
1807 oprintf(fp, " Logical Strand %u: Initial APIC ID: %u\n", i,
1808 ep.smbpe_apicid[i]);
1809 }
1810 }
1811
1812 static void
print_extport(smbios_hdl_t * shp,id_t id,FILE * fp)1813 print_extport(smbios_hdl_t *shp, id_t id, FILE *fp)
1814 {
1815 smbios_port_ext_t epo;
1816
1817 if (check_oem(shp) != 0)
1818 return;
1819
1820 if (smbios_info_extport(shp, id, &epo) != 0) {
1821 smbios_warn(shp, "failed to read extended port information");
1822 return;
1823 }
1824
1825 oprintf(fp, " Chassis Handle: %u\n", epo.smbporte_chassis);
1826 oprintf(fp, " Port Connector Handle: %u\n", epo.smbporte_port);
1827 oprintf(fp, " Device Type: %u\n", epo.smbporte_dtype);
1828 oprintf(fp, " Device Handle: %u\n", epo.smbporte_devhdl);
1829 oprintf(fp, " PHY: %u\n", epo.smbporte_phy);
1830 }
1831
1832 static void
print_pciexrc(smbios_hdl_t * shp,id_t id,FILE * fp)1833 print_pciexrc(smbios_hdl_t *shp, id_t id, FILE *fp)
1834 {
1835 smbios_pciexrc_t pcie;
1836
1837 if (check_oem(shp) != 0)
1838 return;
1839
1840 if (smbios_info_pciexrc(shp, id, &pcie) != 0) {
1841 smbios_warn(shp, "failed to read pciexrc information");
1842 return;
1843 }
1844
1845 oprintf(fp, " Component ID: %u\n", pcie.smbpcie_bb);
1846 oprintf(fp, " BDF: 0x%x\n", pcie.smbpcie_bdf);
1847 }
1848
1849 static void
print_extmemarray(smbios_hdl_t * shp,id_t id,FILE * fp)1850 print_extmemarray(smbios_hdl_t *shp, id_t id, FILE *fp)
1851 {
1852 smbios_memarray_ext_t em;
1853
1854 if (check_oem(shp) != 0)
1855 return;
1856
1857 if (smbios_info_extmemarray(shp, id, &em) != 0) {
1858 smbios_warn(shp, "failed to read extmemarray information");
1859 return;
1860 }
1861
1862 oprintf(fp, " Physical Memory Array Handle: %u\n", em.smbmae_ma);
1863 oprintf(fp, " Component Parent Handle: %u\n", em.smbmae_comp);
1864 oprintf(fp, " BDF: 0x%x\n", em.smbmae_bdf);
1865 }
1866
1867 static void
print_extmemdevice(smbios_hdl_t * shp,id_t id,FILE * fp)1868 print_extmemdevice(smbios_hdl_t *shp, id_t id, FILE *fp)
1869 {
1870 uint_t i, ncs;
1871 uint8_t *cs;
1872 smbios_memdevice_ext_t emd;
1873
1874 if (check_oem(shp) != 0)
1875 return;
1876
1877 if (smbios_info_extmemdevice(shp, id, &emd) != 0) {
1878 smbios_warn(shp, "failed to read extmemdevice information");
1879 return;
1880 }
1881
1882 oprintf(fp, " Memory Device Handle: %u\n", emd.smbmdeve_md);
1883 oprintf(fp, " DRAM Channel: %u\n", emd.smbmdeve_drch);
1884 oprintf(fp, " Number of Chip Selects: %u\n", emd.smbmdeve_ncs);
1885
1886 if (emd.smbmdeve_ncs == 0)
1887 return;
1888
1889 if (smbios_info_extmemdevice_cs(shp, id, &ncs, &cs) != 0) {
1890 smbios_warn(shp, "failed to read extmemdevice cs information");
1891 return;
1892 }
1893
1894 for (i = 0; i < ncs; i++) {
1895 oprintf(fp, " Chip Select: %u\n", cs[i]);
1896 }
1897 smbios_info_extmemdevice_cs_free(shp, ncs, cs);
1898 }
1899
1900 static void
print_strprop_info(smbios_hdl_t * shp,id_t id,FILE * fp)1901 print_strprop_info(smbios_hdl_t *shp, id_t id, FILE *fp)
1902 {
1903 smbios_strprop_t prop;
1904
1905 if (smbios_info_strprop(shp, id, &prop) != 0) {
1906 smbios_warn(shp, "failed to read string property information");
1907 return;
1908 }
1909
1910 desc_printf(smbios_strprop_id_desc(prop.smbsp_prop_id), fp,
1911 " Property ID: %u", prop.smbsp_prop_id);
1912 if (prop.smbsp_prop_val != NULL) {
1913 str_print(fp, " Property Value", prop.smbsp_prop_val);
1914 }
1915 id_printf(fp, " Parent Handle: ", prop.smbsp_parent);
1916 }
1917
1918 static void
print_fwinfo(smbios_hdl_t * shp,id_t id,FILE * fp)1919 print_fwinfo(smbios_hdl_t *shp, id_t id, FILE *fp)
1920 {
1921 smbios_fwinfo_t fw;
1922 smbios_fwinfo_comp_t *comps;
1923 uint_t ncomps, i;
1924
1925 if (smbios_info_fwinfo(shp, id, &fw) != 0) {
1926 smbios_warn(shp, "failed to read firmware inventory");
1927 return;
1928 }
1929
1930 str_print(fp, " Component Name", fw.smbfw_name);
1931 str_print(fp, " ID", fw.smbfw_id);
1932 str_print(fp, " Release Date", fw.smbfw_reldate);
1933 str_print(fp, " Lowest Supported Version", fw.smbfw_lsv);
1934 desc_printf(smbios_fwinfo_vers_desc(fw.smbfw_vers_fmt), fp,
1935 " Version Format: %u", fw.smbfw_vers_fmt);
1936 desc_printf(smbios_fwinfo_id_desc(fw.smbfw_id_fmt), fp,
1937 " ID Format: %u", fw.smbfw_id_fmt);
1938 if (fw.smbfw_imgsz != UINT64_MAX) {
1939 oprintf(fp, " Image Size: %" PRIu64 "\n", fw.smbfw_imgsz);
1940 } else {
1941 oprintf(fp, " Image Size: unknown\n");
1942 }
1943
1944 flag_printf(fp, "Characteristics", fw.smbfw_chars,
1945 sizeof (fw.smbfw_chars) * NBBY, smbios_fwinfo_ch_name,
1946 smbios_fwinfo_ch_desc);
1947
1948 desc_printf(smbios_fwinfo_state_desc(fw.smbfw_state), fp, " State: %u",
1949 fw.smbfw_state);
1950 oprintf(fp, " Number of Associated Components: %u\n",
1951 fw.smbfw_ncomps);
1952
1953 if (fw.smbfw_ncomps == 0)
1954 return;
1955
1956 if (smbios_info_fwinfo_comps(shp, id, &ncomps, &comps) == -1) {
1957 smbios_warn(shp, "failed to read firmware inventory "
1958 "components");
1959 return;
1960 }
1961
1962 oprintf(fp, "\n Component Handles:\n");
1963 for (i = 0; i < ncomps; i++) {
1964 oprintf(fp, " %ld\n", comps[i]);
1965 }
1966 }
1967
1968 static int
print_struct(smbios_hdl_t * shp,const smbios_struct_t * sp,void * fp)1969 print_struct(smbios_hdl_t *shp, const smbios_struct_t *sp, void *fp)
1970 {
1971 smbios_info_t info;
1972 int hex = opt_x;
1973 const char *s;
1974
1975 if (opt_t != -1 && opt_t != sp->smbstr_type)
1976 return (0); /* skip struct if type doesn't match -t */
1977
1978 if (!opt_O && (sp->smbstr_type == SMB_TYPE_MEMCTL ||
1979 sp->smbstr_type == SMB_TYPE_MEMMOD))
1980 return (0); /* skip struct if type is obsolete */
1981
1982 if (g_hdr++ == 0 || !opt_s)
1983 oprintf(fp, "%-5s %-4s %s\n", "ID", "SIZE", "TYPE");
1984
1985 oprintf(fp, "%-5u %-4lu",
1986 (uint_t)sp->smbstr_id, (ulong_t)sp->smbstr_size);
1987
1988 if ((s = smbios_type_name(sp->smbstr_type)) != NULL)
1989 oprintf(fp, " %s (type %u)", s, sp->smbstr_type);
1990 else if (sp->smbstr_type > SMB_TYPE_OEM_LO &&
1991 sp->smbstr_type < SMB_TYPE_OEM_HI)
1992 oprintf(fp, " %s+%u (type %u)", "SMB_TYPE_OEM_LO",
1993 sp->smbstr_type - SMB_TYPE_OEM_LO, sp->smbstr_type);
1994 else
1995 oprintf(fp, " %u", sp->smbstr_type);
1996
1997 if ((s = smbios_type_desc(sp->smbstr_type)) != NULL)
1998 oprintf(fp, " (%s)\n", s);
1999 else
2000 oprintf(fp, "\n");
2001
2002 if (opt_s)
2003 return (0); /* only print header line if -s specified */
2004
2005 if (smbios_info_common(shp, sp->smbstr_id, &info) == 0) {
2006 oprintf(fp, "\n");
2007 print_common(&info, fp);
2008 }
2009
2010 switch (sp->smbstr_type) {
2011 case SMB_TYPE_BIOS:
2012 oprintf(fp, "\n");
2013 print_bios(shp, fp);
2014 break;
2015 case SMB_TYPE_SYSTEM:
2016 oprintf(fp, "\n");
2017 print_system(shp, fp);
2018 break;
2019 case SMB_TYPE_BASEBOARD:
2020 oprintf(fp, "\n");
2021 print_bboard(shp, sp->smbstr_id, fp);
2022 break;
2023 case SMB_TYPE_CHASSIS:
2024 oprintf(fp, "\n");
2025 print_chassis(shp, sp->smbstr_id, fp);
2026 break;
2027 case SMB_TYPE_PROCESSOR:
2028 oprintf(fp, "\n");
2029 print_processor(shp, sp->smbstr_id, fp);
2030 break;
2031 case SMB_TYPE_CACHE:
2032 oprintf(fp, "\n");
2033 print_cache(shp, sp->smbstr_id, fp);
2034 break;
2035 case SMB_TYPE_PORT:
2036 oprintf(fp, "\n");
2037 print_port(shp, sp->smbstr_id, fp);
2038 break;
2039 case SMB_TYPE_SLOT:
2040 oprintf(fp, "\n");
2041 print_slot(shp, sp->smbstr_id, fp);
2042 break;
2043 case SMB_TYPE_OBDEVS:
2044 oprintf(fp, "\n");
2045 print_obdevs(shp, sp->smbstr_id, fp);
2046 break;
2047 case SMB_TYPE_OEMSTR:
2048 case SMB_TYPE_SYSCONFSTR:
2049 oprintf(fp, "\n");
2050 print_strtab(shp, sp->smbstr_id, fp);
2051 break;
2052 case SMB_TYPE_LANG:
2053 oprintf(fp, "\n");
2054 print_lang(shp, sp->smbstr_id, fp);
2055 break;
2056 case SMB_TYPE_EVENTLOG:
2057 oprintf(fp, "\n");
2058 print_evlog(shp, sp->smbstr_id, fp);
2059 break;
2060 case SMB_TYPE_MEMARRAY:
2061 oprintf(fp, "\n");
2062 print_memarray(shp, sp->smbstr_id, fp);
2063 break;
2064 case SMB_TYPE_MEMDEVICE:
2065 oprintf(fp, "\n");
2066 print_memdevice(shp, sp->smbstr_id, fp);
2067 break;
2068 case SMB_TYPE_MEMARRAYMAP:
2069 oprintf(fp, "\n");
2070 print_memarrmap(shp, sp->smbstr_id, fp);
2071 break;
2072 case SMB_TYPE_MEMDEVICEMAP:
2073 oprintf(fp, "\n");
2074 print_memdevmap(shp, sp->smbstr_id, fp);
2075 break;
2076 case SMB_TYPE_BATTERY:
2077 oprintf(fp, "\n");
2078 print_battery(shp, sp->smbstr_id, fp);
2079 break;
2080 case SMB_TYPE_POINTDEV:
2081 oprintf(fp, "\n");
2082 print_pointdev(shp, sp->smbstr_id, fp);
2083 break;
2084 case SMB_TYPE_SECURITY:
2085 oprintf(fp, "\n");
2086 print_hwsec(shp, fp);
2087 break;
2088 case SMB_TYPE_VPROBE:
2089 oprintf(fp, "\n");
2090 print_vprobe(shp, sp->smbstr_id, fp);
2091 break;
2092 case SMB_TYPE_COOLDEV:
2093 oprintf(fp, "\n");
2094 print_cooldev(shp, sp->smbstr_id, fp);
2095 break;
2096 case SMB_TYPE_TPROBE:
2097 oprintf(fp, "\n");
2098 print_tprobe(shp, sp->smbstr_id, fp);
2099 break;
2100 case SMB_TYPE_IPROBE:
2101 oprintf(fp, "\n");
2102 print_iprobe(shp, sp->smbstr_id, fp);
2103 break;
2104 case SMB_TYPE_BOOT:
2105 oprintf(fp, "\n");
2106 print_boot(shp, fp);
2107 break;
2108 case SMB_TYPE_IPMIDEV:
2109 oprintf(fp, "\n");
2110 print_ipmi(shp, fp);
2111 break;
2112 case SMB_TYPE_POWERSUP:
2113 oprintf(fp, "\n");
2114 print_powersup(shp, sp->smbstr_id, fp);
2115 break;
2116 case SMB_TYPE_ADDINFO:
2117 oprintf(fp, "\n");
2118 print_addinfo(shp, sp->smbstr_id, fp);
2119 break;
2120 case SMB_TYPE_OBDEVEXT:
2121 oprintf(fp, "\n");
2122 print_obdevs_ext(shp, sp->smbstr_id, fp);
2123 break;
2124 case SMB_TYPE_PROCESSOR_INFO:
2125 oprintf(fp, "\n");
2126 print_processor_info(shp, sp->smbstr_id, fp);
2127 break;
2128 case SMB_TYPE_STRPROP:
2129 oprintf(fp, "\n");
2130 print_strprop_info(shp, sp->smbstr_id, fp);
2131 break;
2132 case SMB_TYPE_FWINFO:
2133 oprintf(fp, "\n");
2134 print_fwinfo(shp, sp->smbstr_id, fp);
2135 break;
2136 case SUN_OEM_EXT_PROCESSOR:
2137 oprintf(fp, "\n");
2138 print_extprocessor(shp, sp->smbstr_id, fp);
2139 break;
2140 case SUN_OEM_EXT_PORT:
2141 oprintf(fp, "\n");
2142 print_extport(shp, sp->smbstr_id, fp);
2143 break;
2144 case SUN_OEM_PCIEXRC:
2145 oprintf(fp, "\n");
2146 print_pciexrc(shp, sp->smbstr_id, fp);
2147 break;
2148 case SUN_OEM_EXT_MEMARRAY:
2149 oprintf(fp, "\n");
2150 print_extmemarray(shp, sp->smbstr_id, fp);
2151 break;
2152 case SUN_OEM_EXT_MEMDEVICE:
2153 oprintf(fp, "\n");
2154 print_extmemdevice(shp, sp->smbstr_id, fp);
2155 break;
2156 default:
2157 hex++;
2158 }
2159
2160 if (hex)
2161 print_bytes(sp->smbstr_data, sp->smbstr_size, fp);
2162 else
2163 oprintf(fp, "\n");
2164
2165 return (0);
2166 }
2167
2168 static uint16_t
getu16(const char * name,const char * s)2169 getu16(const char *name, const char *s)
2170 {
2171 u_longlong_t val;
2172 char *p;
2173
2174 errno = 0;
2175 val = strtoull(s, &p, 0);
2176
2177 if (errno != 0 || p == s || *p != '\0' || val > UINT16_MAX) {
2178 (void) fprintf(stderr, "%s: invalid %s argument -- %s\n",
2179 g_pname, name, s);
2180 exit(SMBIOS_USAGE);
2181 }
2182
2183 return ((uint16_t)val);
2184 }
2185
2186 static uint16_t
getstype(const char * name,const char * s)2187 getstype(const char *name, const char *s)
2188 {
2189 const char *ts;
2190 uint16_t t;
2191
2192 for (t = 0; t < SMB_TYPE_OEM_LO; t++) {
2193 if ((ts = smbios_type_name(t)) != NULL && strcmp(s, ts) == 0)
2194 return (t);
2195 }
2196
2197 (void) fprintf(stderr, "%s: invalid %s argument -- %s\n",
2198 g_pname, name, s);
2199
2200 exit(SMBIOS_USAGE);
2201 /*NOTREACHED*/
2202 }
2203
2204 static int
usage(FILE * fp)2205 usage(FILE *fp)
2206 {
2207 (void) fprintf(fp, "Usage: %s "
2208 "[-BeOsx] [-i id] [-t type] [-w file] [file]\n\n", g_pname);
2209
2210 (void) fprintf(fp,
2211 "\t-B disable header validation for broken BIOSes\n"
2212 "\t-e display SMBIOS entry point information\n"
2213 "\t-i display only the specified structure\n"
2214 "\t-O display obsolete structure types\n"
2215 "\t-s display only a summary of structure identifiers and types\n"
2216 "\t-t display only the specified structure type\n"
2217 "\t-w write the raw data to the specified file\n"
2218 "\t-x display raw data for structures\n");
2219
2220 return (SMBIOS_USAGE);
2221 }
2222
2223 int
main(int argc,char * argv[])2224 main(int argc, char *argv[])
2225 {
2226 const char *ifile = NULL;
2227 const char *ofile = NULL;
2228 int oflags = 0;
2229
2230 smbios_hdl_t *shp;
2231 smbios_struct_t s;
2232 int err, fd, c;
2233 char *p;
2234
2235 if ((p = strrchr(argv[0], '/')) == NULL)
2236 g_pname = argv[0];
2237 else
2238 g_pname = p + 1;
2239
2240 while (optind < argc) {
2241 while ((c = getopt(argc, argv, "Bei:Ost:w:xZ")) != EOF) {
2242 switch (c) {
2243 case 'B':
2244 oflags |= SMB_O_NOCKSUM | SMB_O_NOVERS;
2245 break;
2246 case 'e':
2247 opt_e++;
2248 break;
2249 case 'i':
2250 opt_i = getu16("struct ID", optarg);
2251 break;
2252 case 'O':
2253 opt_O++;
2254 break;
2255 case 's':
2256 opt_s++;
2257 break;
2258 case 't':
2259 if (isdigit(optarg[0]))
2260 opt_t = getu16("struct type", optarg);
2261 else
2262 opt_t = getstype("struct type", optarg);
2263 break;
2264 case 'w':
2265 ofile = optarg;
2266 break;
2267 case 'x':
2268 opt_x++;
2269 break;
2270 case 'Z':
2271 oflags |= SMB_O_ZIDS; /* undocumented */
2272 break;
2273 default:
2274 return (usage(stderr));
2275 }
2276 }
2277
2278 if (optind < argc) {
2279 if (ifile != NULL) {
2280 (void) fprintf(stderr, "%s: illegal "
2281 "argument -- %s\n", g_pname, argv[optind]);
2282 return (SMBIOS_USAGE);
2283 }
2284 ifile = argv[optind++];
2285 }
2286 }
2287
2288 if ((shp = smbios_open(ifile, SMB_VERSION, oflags, &err)) == NULL) {
2289 (void) fprintf(stderr, "%s: failed to load SMBIOS: %s\n",
2290 g_pname, smbios_errmsg(err));
2291 return (SMBIOS_ERROR);
2292 }
2293
2294 if (opt_i == -1 && opt_t == -1 && opt_e == 0 &&
2295 smbios_truncated(shp))
2296 (void) fprintf(stderr, "%s: SMBIOS table is truncated\n",
2297 g_pname);
2298
2299 if (ofile != NULL) {
2300 if ((fd = open(ofile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) == -1) {
2301 (void) fprintf(stderr, "%s: failed to open %s: %s\n",
2302 g_pname, ofile, strerror(errno));
2303 err = SMBIOS_ERROR;
2304 } else if (smbios_write(shp, fd) != 0) {
2305 (void) fprintf(stderr, "%s: failed to write %s: %s\n",
2306 g_pname, ofile, smbios_errmsg(smbios_errno(shp)));
2307 err = SMBIOS_ERROR;
2308 }
2309 smbios_close(shp);
2310 return (err);
2311 }
2312
2313 if (opt_e) {
2314 print_smbios(shp, stdout);
2315 smbios_close(shp);
2316 return (SMBIOS_SUCCESS);
2317 }
2318
2319 if (opt_O && (opt_i != -1 || opt_t != -1))
2320 opt_O++; /* -i or -t imply displaying obsolete records */
2321
2322 if (opt_i != -1)
2323 err = smbios_lookup_id(shp, opt_i, &s);
2324 else
2325 err = smbios_iter(shp, print_struct, stdout);
2326
2327 if (err != 0) {
2328 (void) fprintf(stderr, "%s: failed to access SMBIOS: %s\n",
2329 g_pname, smbios_errmsg(smbios_errno(shp)));
2330 smbios_close(shp);
2331 return (SMBIOS_ERROR);
2332 }
2333
2334 if (opt_i != -1)
2335 (void) print_struct(shp, &s, stdout);
2336
2337 smbios_close(shp);
2338 return (SMBIOS_SUCCESS);
2339 }
2340