xref: /illumos-gate/usr/src/cmd/prtdiag/i386/smbios.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 /*
31  * x86 System Management BIOS prtdiag
32  *
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.
38  *
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.
46  *
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.
54  *
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.
60  */
61 
62 #include <smbios.h>
63 #include <alloca.h>
64 #include <locale.h>
65 #include <strings.h>
66 #include <stdlib.h>
67 #include <stdio.h>
68 #include <ctype.h>
69 
70 /*ARGSUSED*/
71 static int
72 do_procs(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
73 {
74 	smbios_processor_t p;
75 	smbios_info_t info;
76 	const char *v;
77 	char *s;
78 	size_t n;
79 
80 	if (sp->smbstr_type == SMB_TYPE_PROCESSOR &&
81 	    smbios_info_processor(shp, sp->smbstr_id, &p) != SMB_ERR &&
82 	    smbios_info_common(shp, sp->smbstr_id, &info) != SMB_ERR &&
83 	    SMB_PRSTATUS_PRESENT(p.smbp_status)) {
84 
85 		/*
86 		 * Obtaining a decent string for the type of processor is
87 		 * messy: the BIOS has hopefully filled in the SMBIOS record.
88 		 * If so, strip trailing spaces and \r (seen in some BIOSes).
89 		 * If not, fall back to the family name for p.smbp_family.
90 		 */
91 		if (info.smbi_version != NULL && *info.smbi_version != '\0') {
92 			n = strlen(info.smbi_version);
93 			v = s = alloca(n + 1);
94 			(void) strcpy(s, info.smbi_version);
95 
96 			if (s[n - 1] == '\r')
97 				s[--n] = '\0';
98 
99 			while (n != 0 && isspace(s[n - 1]))
100 				s[--n] = '\0';
101 
102 		} else if ((v = smbios_processor_family_desc(
103 		    p.smbp_family)) == NULL) {
104 			v = gettext("Unknown");
105 		}
106 
107 		(void) printf(gettext("%-32s %s\n"), v, info.smbi_location);
108 	}
109 
110 	return (0);
111 }
112 
113 /*
114  * NOTE: It would be very convenient to print the DIMM size in do_memdevs.
115  * Unfortunately, SMBIOS can only be relied upon to tell us whether a DIMM is
116  * present or not (smbmd_size == 0).  Some BIOSes do fill in an accurate size
117  * for DIMMs, whereas others fill in the maximum size, and still others insert
118  * a wrong value.  Sizes will need to wait for x86 memory controller interfaces
119  * or integration with IPMI, which can actually read the true DIMM SPD data.
120  */
121 /*ARGSUSED*/
122 static int
123 do_memdevs(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
124 {
125 	smbios_memdevice_t md;
126 
127 	if (sp->smbstr_type == SMB_TYPE_MEMDEVICE &&
128 	    smbios_info_memdevice(shp, sp->smbstr_id, &md) != SMB_ERR) {
129 
130 		const char *t = smbios_memdevice_type_desc(md.smbmd_type);
131 		char buf[8];
132 
133 		if (md.smbmd_set != (uint8_t)-1)
134 			(void) snprintf(buf, sizeof (buf), "%u", md.smbmd_set);
135 		else
136 			(void) strcpy(buf, "-");
137 
138 		(void) printf(gettext("%-7s %-6s %-3s %-19s %s\n"),
139 		    t ? t : gettext("Unknown"),
140 		    md.smbmd_size ? gettext("in use") : gettext("empty"),
141 		    buf, md.smbmd_dloc, md.smbmd_bloc);
142 	}
143 
144 	return (0);
145 }
146 
147 /*ARGSUSED*/
148 static int
149 do_obdevs(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
150 {
151 	smbios_obdev_t *argv;
152 	int i, argc;
153 
154 	if (sp->smbstr_type == SMB_TYPE_OBDEVS &&
155 	    (argc = smbios_info_obdevs(shp, sp->smbstr_id, 0, NULL)) > 0) {
156 		argv = alloca(sizeof (smbios_obdev_t) * argc);
157 		(void) smbios_info_obdevs(shp, sp->smbstr_id, argc, argv);
158 		for (i = 0; i < argc; i++)
159 			(void) printf(gettext("%s\n"), argv[i].smbd_name);
160 	}
161 
162 	return (0);
163 }
164 
165 /*ARGSUSED*/
166 static int
167 do_slots(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
168 {
169 	smbios_slot_t s;
170 
171 	if (sp->smbstr_type == SMB_TYPE_SLOT &&
172 	    smbios_info_slot(shp, sp->smbstr_id, &s) != SMB_ERR) {
173 
174 		const char *t = smbios_slot_type_desc(s.smbl_type);
175 		const char *u = smbios_slot_usage_desc(s.smbl_usage);
176 
177 		(void) printf(gettext("%-3u %-9s %-16s %s\n"),
178 		    s.smbl_id, u ? u : gettext("Unknown"),
179 		    t ? t : gettext("Unknown"), s.smbl_name);
180 	}
181 
182 	return (0);
183 }
184 
185 /*ARGSUSED*/
186 int
187 do_prominfo(int opt_v, char *progname, int opt_l, int opt_p)
188 {
189 	smbios_hdl_t *shp;
190 	smbios_system_t sys;
191 	smbios_bios_t bios;
192 	smbios_ipmi_t ipmi;
193 	smbios_info_t info;
194 
195 	const char *s;
196 	id_t id;
197 	int err;
198 
199 	if ((shp = smbios_open(NULL, SMB_VERSION, 0, &err)) == NULL) {
200 		(void) fprintf(stderr,
201 		    gettext("%s: failed to open SMBIOS: %s\n"),
202 		    progname, smbios_errmsg(err));
203 		return (1);
204 	}
205 
206 	if ((id = smbios_info_system(shp, &sys)) != SMB_ERR &&
207 	    smbios_info_common(shp, id, &info) != SMB_ERR) {
208 		(void) printf(gettext("System Configuration: %s %s\n"),
209 		    info.smbi_manufacturer, info.smbi_product);
210 	} else {
211 		(void) fprintf(stderr,
212 		    gettext("%s: failed to get system info: %s\n"),
213 		    progname, smbios_errmsg(smbios_errno(shp)));
214 	}
215 
216 	if (smbios_info_bios(shp, &bios) != SMB_ERR) {
217 		(void) printf(gettext("BIOS Configuration: %s %s %s\n"),
218 		    bios.smbb_vendor, bios.smbb_version, bios.smbb_reldate);
219 	} else {
220 		(void) fprintf(stderr,
221 		    gettext("%s: failed to get bios info: %s\n"),
222 		    progname, smbios_errmsg(smbios_errno(shp)));
223 	}
224 
225 	if (smbios_info_ipmi(shp, &ipmi) != SMB_ERR) {
226 		if ((s = smbios_ipmi_type_desc(ipmi.smbip_type)) == NULL)
227 			s = gettext("Unknown");
228 
229 		(void) printf(gettext("BMC Configuration: IPMI %u.%u (%s)\n"),
230 		    ipmi.smbip_vers.smbv_major, ipmi.smbip_vers.smbv_minor, s);
231 	}
232 
233 	(void) printf(gettext(
234 	    "\n==== Processor Sockets ====================================\n"));
235 
236 	(void) printf(gettext("\n%-32s %s"), "Version", "Location Tag");
237 
238 	(void) printf(gettext(
239 	    "\n-------------------------------- --------------------------\n"));
240 	(void) smbios_iter(shp, do_procs, NULL);
241 
242 	(void) printf(gettext(
243 	    "\n==== Memory Device Sockets ================================\n"));
244 
245 	(void) printf(gettext("\n%-7s %-6s %-3s %-19s %s"),
246 	    "Type", "Status", "Set", "Device Locator", "Bank Locator");
247 
248 	(void) printf(gettext(
249 	    "\n------- ------ --- ------------------- --------------------\n"));
250 	(void) smbios_iter(shp, do_memdevs, NULL);
251 
252 	(void) printf(gettext(
253 	    "\n==== On-Board Devices =====================================\n"));
254 	(void) smbios_iter(shp, do_obdevs, NULL);
255 
256 	(void) printf(gettext(
257 	    "\n==== Upgradeable Slots ====================================\n"));
258 
259 	(void) printf(gettext("\n%-3s %-9s %-16s %s"),
260 	    "ID", "Status", "Type", "Description");
261 
262 	(void) printf(gettext(
263 	    "\n--- --------- ---------------- ----------------------------\n"));
264 	(void) smbios_iter(shp, do_slots, NULL);
265 
266 	smbios_close(shp);
267 	return (0);
268 }
269