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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 * Copyright 2015 Joyent, Inc.
31 * x86 System Management BIOS prtdiag
33 * Most modern x86 systems support a System Management BIOS, which is a memory
34 * buffer filled in by the BIOS at boot time that describes the hardware. This
35 * data format is described by DMTF specification DSP0134 (see http://dmtf.org)
36 * This file implements a rudimentary prtdiag(1M) display using the SMBIOS.
37 * Access to the data is provided by libsmbios: see <sys/smbios.h> for info.
39 * NOTE: It is important to understand that x86 hardware varies extremely
40 * widely and that the DMTF SMBIOS specification leaves way too much latitude
41 * for implementors, and provides no standardized validation mechanism. As
42 * such, it is not uncommon to find out-of-spec SMBIOSes or fields that
43 * contain strange and possibly even incorrect information. As such, this
44 * file should not be extended to report every SMBIOS tidbit or structure in
45 * the spec unless we have good reason to believe it tends to be reliable.
47 * Similarly, the prtdiag(1M) utility itself should not be used to spit out
48 * every possible bit of x86 configuration data from every possible source;
49 * otherwise this code will become an unmaintainable and untestable disaster.
50 * Extensions to prtdiag should prefer to use more stable kernel mechanisms
51 * that actually discover the true hardware when such subsystems are available,
52 * and should generally limit themselves to commonly needed h/w data. As such,
53 * extensions to x86 prtdiag should focus on integration with the device tree.
55 * The prtdiag(1M) utility is for service personnel and system administrators:
56 * it is not your personal ACPI disassembler or CPUID decoder ring. The
57 * complete SMBIOS data is available from smbdump(1), and other specialized
58 * tools can be created to display the state of other x86 features, especially
59 * when that information is more for kernel developers than box administrators.
70 #include <fm/libtopo.h>
71 #include <fm/topo_hc.h>
72 #include <sys/fm/protocol.h>
74 static pcidb_hdl_t
*prt_php
;
78 do_procs(smbios_hdl_t
*shp
, const smbios_struct_t
*sp
, void *arg
)
86 if (sp
->smbstr_type
== SMB_TYPE_PROCESSOR
&&
87 smbios_info_processor(shp
, sp
->smbstr_id
, &p
) != SMB_ERR
&&
88 smbios_info_common(shp
, sp
->smbstr_id
, &info
) != SMB_ERR
&&
89 SMB_PRSTATUS_PRESENT(p
.smbp_status
)) {
92 * Obtaining a decent string for the type of processor is
93 * messy: the BIOS has hopefully filled in the SMBIOS record.
94 * If so, strip trailing spaces and \r (seen in some BIOSes).
95 * If not, fall back to the family name for p.smbp_family.
97 if (info
.smbi_version
!= NULL
&& *info
.smbi_version
!= '\0') {
98 n
= strlen(info
.smbi_version
);
99 v
= s
= alloca(n
+ 1);
100 (void) strcpy(s
, info
.smbi_version
);
102 if (s
[n
- 1] == '\r')
105 while (n
!= 0 && isspace(s
[n
- 1]))
108 } else if ((v
= smbios_processor_family_desc(
109 p
.smbp_family
)) == NULL
) {
110 v
= gettext("Unknown");
113 (void) printf(gettext("%-32s %s\n"), v
, info
.smbi_location
);
120 * NOTE: It would be very convenient to print the DIMM size in do_memdevs.
121 * Unfortunately, SMBIOS can only be relied upon to tell us whether a DIMM is
122 * present or not (smbmd_size == 0). Some BIOSes do fill in an accurate size
123 * for DIMMs, whereas others fill in the maximum size, and still others insert
124 * a wrong value. Sizes will need to wait for x86 memory controller interfaces
125 * or integration with IPMI, which can actually read the true DIMM SPD data.
129 do_memdevs(smbios_hdl_t
*shp
, const smbios_struct_t
*sp
, void *arg
)
131 smbios_memdevice_t md
;
133 if (sp
->smbstr_type
== SMB_TYPE_MEMDEVICE
&&
134 smbios_info_memdevice(shp
, sp
->smbstr_id
, &md
) != SMB_ERR
) {
136 const char *t
= smbios_memdevice_type_desc(md
.smbmd_type
);
139 if (md
.smbmd_set
!= (uint8_t)-1)
140 (void) snprintf(buf
, sizeof (buf
), "%u", md
.smbmd_set
);
142 (void) strcpy(buf
, "-");
144 (void) printf(gettext("%-11s %-6s %-3s %-19s %s\n"),
145 t
? t
: gettext("Unknown"),
146 md
.smbmd_size
? gettext("in use") : gettext("empty"),
147 buf
, md
.smbmd_dloc
, md
.smbmd_bloc
);
155 do_obdevs(smbios_hdl_t
*shp
, const smbios_struct_t
*sp
, void *arg
)
157 smbios_obdev_t
*argv
;
160 if (sp
->smbstr_type
== SMB_TYPE_OBDEVS
&&
161 (argc
= smbios_info_obdevs(shp
, sp
->smbstr_id
, 0, NULL
)) > 0) {
162 argv
= alloca(sizeof (smbios_obdev_t
) * argc
);
163 (void) smbios_info_obdevs(shp
, sp
->smbstr_id
, argc
, argv
);
164 for (i
= 0; i
< argc
; i
++)
165 (void) printf(gettext("%s\n"), argv
[i
].smbd_name
);
173 do_slot_mapping_cb(topo_hdl_t
*thp
, tnode_t
*node
, void *arg
)
176 nvlist_t
*rsrc
= NULL
;
177 const char *match
= arg
;
178 char *s
, *fmri
= NULL
;
179 char *didstr
= NULL
, *driver
= NULL
, *vidstr
= NULL
;
180 boolean_t printed
= B_FALSE
;
182 ret
= TOPO_WALK_NEXT
;
183 if (topo_node_resource(node
, &rsrc
, &err
) < 0)
185 if (topo_fmri_nvl2str(thp
, rsrc
, &fmri
, &err
) < 0)
188 if ((s
= strstr(fmri
, match
)) == NULL
)
190 if (s
[strlen(match
)] != '\0')
193 /* At this point we think we've found a match */
194 ret
= TOPO_WALK_TERMINATE
;
195 if (topo_prop_get_string(node
, TOPO_PGROUP_IO
, TOPO_IO_DRIVER
, &driver
,
199 if (topo_prop_get_string(node
, TOPO_PGROUP_PCI
, TOPO_PCI_VENDID
,
203 if (topo_prop_get_string(node
, TOPO_PGROUP_PCI
, TOPO_PCI_DEVID
,
207 if (prt_php
!= NULL
) {
210 vid
= strtol(vidstr
, NULL
, 16);
211 did
= strtol(didstr
, NULL
, 16);
212 if (vid
>= 0 && vid
<= UINT16_MAX
&&
213 did
>= 0 && did
<= UINT16_MAX
) {
214 pcidb_device_t
*pdev
;
216 pdev
= pcidb_lookup_device(prt_php
, vid
, did
);
218 pcidb_vendor_t
*pvend
;
219 pvend
= pcidb_device_vendor(pdev
);
220 (void) printf(gettext(", %s %s (%s)"),
221 pcidb_vendor_name(pvend
),
222 pcidb_device_name(pdev
),
223 driver
!= NULL
? driver
: "<unknown>");
229 if (printed
== B_FALSE
) {
230 (void) printf(gettext(", pci%s,%s (%s)"), vidstr
, didstr
,
231 driver
!= NULL
? driver
: "<unknown>");
234 topo_hdl_strfree(thp
, didstr
);
235 topo_hdl_strfree(thp
, driver
);
236 topo_hdl_strfree(thp
, vidstr
);
237 topo_hdl_strfree(thp
, fmri
);
243 do_slot_mapping(smbios_slot_t
*s
, topo_hdl_t
*thp
)
251 * Bits 7:3 are the device number and bits 2:0 are the function.
253 dev
= s
->smbl_df
>> 3;
254 func
= s
->smbl_df
& 0x7;
256 (void) snprintf(pciex
, sizeof (pciex
), "%s=%u/%s=%u/%s=%d",
257 PCIEX_BUS
, s
->smbl_bus
, PCIEX_DEVICE
, dev
, PCIEX_FUNCTION
, func
);
259 twp
= topo_walk_init(thp
, FM_FMRI_SCHEME_HC
, do_slot_mapping_cb
, pciex
,
264 (void) topo_walk_step(twp
, TOPO_WALK_CHILD
);
270 do_slots(smbios_hdl_t
*shp
, const smbios_struct_t
*sp
, void *arg
)
274 if (sp
->smbstr_type
== SMB_TYPE_SLOT
&&
275 smbios_info_slot(shp
, sp
->smbstr_id
, &s
) != SMB_ERR
) {
277 const char *t
= smbios_slot_type_desc(s
.smbl_type
);
278 const char *u
= smbios_slot_usage_desc(s
.smbl_usage
);
280 (void) printf(gettext("%-3u %-9s %-16s %s"),
281 s
.smbl_id
, u
? u
: gettext("Unknown"),
282 t
? t
: gettext("Unknown"), s
.smbl_name
);
285 * If the slot isn't of a type where this makes sense, then
286 * SMBIOS will populate any of these members with the value
287 * 0xff. Therefore if we find any of them set there, we just
290 if (s
.smbl_sg
!= 0xff && s
.smbl_bus
!= 0xff &&
291 s
.smbl_df
!= 0xff && arg
!= NULL
)
292 do_slot_mapping(&s
, arg
);
294 (void) printf(gettext("\n"));
302 do_prominfo(int opt_v
, char *progname
, int opt_l
, int opt_p
)
316 if ((shp
= smbios_open(NULL
, SMB_VERSION
, 0, &err
)) == NULL
) {
317 (void) fprintf(stderr
,
318 gettext("%s: failed to open SMBIOS: %s\n"),
319 progname
, smbios_errmsg(err
));
323 if ((id
= smbios_info_system(shp
, &sys
)) != SMB_ERR
&&
324 smbios_info_common(shp
, id
, &info
) != SMB_ERR
) {
325 (void) printf(gettext("System Configuration: %s %s\n"),
326 info
.smbi_manufacturer
, info
.smbi_product
);
328 (void) fprintf(stderr
,
329 gettext("%s: failed to get system info: %s\n"),
330 progname
, smbios_errmsg(smbios_errno(shp
)));
333 if (smbios_info_bios(shp
, &bios
) != SMB_ERR
) {
334 (void) printf(gettext("BIOS Configuration: %s %s %s\n"),
335 bios
.smbb_vendor
, bios
.smbb_version
, bios
.smbb_reldate
);
337 (void) fprintf(stderr
,
338 gettext("%s: failed to get bios info: %s\n"),
339 progname
, smbios_errmsg(smbios_errno(shp
)));
342 if (smbios_info_ipmi(shp
, &ipmi
) != SMB_ERR
) {
343 if ((s
= smbios_ipmi_type_desc(ipmi
.smbip_type
)) == NULL
)
344 s
= gettext("Unknown");
346 (void) printf(gettext("BMC Configuration: IPMI %u.%u (%s)\n"),
347 ipmi
.smbip_vers
.smbv_major
, ipmi
.smbip_vers
.smbv_minor
, s
);
351 * Silently swallow all libtopo and libpcidb related errors.
354 if ((thp
= topo_open(TOPO_VERSION
, NULL
, &err
)) != NULL
) {
355 if ((uuid
= topo_snap_hold(thp
, NULL
, &err
)) == NULL
) {
361 prt_php
= pcidb_open(PCIDB_VERSION
);
363 (void) printf(gettext(
364 "\n==== Processor Sockets ====================================\n"));
366 (void) printf(gettext("\n%-32s %s"), "Version", "Location Tag");
368 (void) printf(gettext(
369 "\n-------------------------------- --------------------------\n"));
370 (void) smbios_iter(shp
, do_procs
, NULL
);
372 (void) printf(gettext(
373 "\n==== Memory Device Sockets ================================\n"));
375 (void) printf(gettext("\n%-11s %-6s %-3s %-19s %s"),
376 "Type", "Status", "Set", "Device Locator", "Bank Locator");
378 (void) printf(gettext(
379 "\n----------- ------ --- ------------------- ----------------\n"));
380 (void) smbios_iter(shp
, do_memdevs
, NULL
);
382 (void) printf(gettext(
383 "\n==== On-Board Devices =====================================\n"));
384 (void) smbios_iter(shp
, do_obdevs
, NULL
);
386 (void) printf(gettext(
387 "\n==== Upgradeable Slots ====================================\n"));
389 (void) printf(gettext("\n%-3s %-9s %-16s %s"),
390 "ID", "Status", "Type", "Description");
392 (void) printf(gettext(
393 "\n--- --------- ---------------- ----------------------------\n"));
394 (void) smbios_iter(shp
, do_slots
, thp
);
398 topo_hdl_strfree(thp
, uuid
);
400 topo_snap_release(thp
);
403 pcidb_close(prt_php
);