xref: /illumos-gate/usr/src/cmd/prtdiag/i386/smbios.c (revision e9af4bc0b1cc30cea75d6ad4aa2fde97d985e9be)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * x86 System Management BIOS prtdiag
31  *
32  * Most modern x86 systems support a System Management BIOS, which is a memory
33  * buffer filled in by the BIOS at boot time that describes the hardware.  This
34  * data format is described by DMTF specification DSP0134 (see http://dmtf.org)
35  * This file implements a rudimentary prtdiag(1M) display using the SMBIOS.
36  * Access to the data is provided by libsmbios: see <sys/smbios.h> for info.
37  *
38  * NOTE: It is important to understand that x86 hardware varies extremely
39  * widely and that the DMTF SMBIOS specification leaves way too much latitude
40  * for implementors, and provides no standardized validation mechanism.  As
41  * such, it is not uncommon to find out-of-spec SMBIOSes or fields that
42  * contain strange and possibly even incorrect information.  As such, this
43  * file should not be extended to report every SMBIOS tidbit or structure in
44  * the spec unless we have good reason to believe it tends to be reliable.
45  *
46  * Similarly, the prtdiag(1M) utility itself should not be used to spit out
47  * every possible bit of x86 configuration data from every possible source;
48  * otherwise this code will become an unmaintainable and untestable disaster.
49  * Extensions to prtdiag should prefer to use more stable kernel mechanisms
50  * that actually discover the true hardware when such subsystems are available,
51  * and should generally limit themselves to commonly needed h/w data.  As such,
52  * extensions to x86 prtdiag should focus on integration with the device tree.
53  *
54  * The prtdiag(1M) utility is for service personnel and system administrators:
55  * it is not your personal ACPI disassembler or CPUID decoder ring.  The
56  * complete SMBIOS data is available from smbdump(1), and other specialized
57  * tools can be created to display the state of other x86 features, especially
58  * when that information is more for kernel developers than box administrators.
59  */
60 
61 #include <smbios.h>
62 #include <alloca.h>
63 #include <locale.h>
64 #include <strings.h>
65 #include <stdlib.h>
66 #include <stdio.h>
67 #include <ctype.h>
68 
69 /*ARGSUSED*/
70 static int
71 do_procs(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
72 {
73 	smbios_processor_t p;
74 	smbios_info_t info;
75 	const char *v;
76 	char *s;
77 	size_t n;
78 
79 	if (sp->smbstr_type == SMB_TYPE_PROCESSOR &&
80 	    smbios_info_processor(shp, sp->smbstr_id, &p) != SMB_ERR &&
81 	    smbios_info_common(shp, sp->smbstr_id, &info) != SMB_ERR &&
82 	    SMB_PRSTATUS_PRESENT(p.smbp_status)) {
83 
84 		/*
85 		 * Obtaining a decent string for the type of processor is
86 		 * messy: the BIOS has hopefully filled in the SMBIOS record.
87 		 * If so, strip trailing spaces and \r (seen in some BIOSes).
88 		 * If not, fall back to the family name for p.smbp_family.
89 		 */
90 		if (info.smbi_version != NULL && *info.smbi_version != '\0') {
91 			n = strlen(info.smbi_version);
92 			v = s = alloca(n + 1);
93 			(void) strcpy(s, info.smbi_version);
94 
95 			if (s[n - 1] == '\r')
96 				s[--n] = '\0';
97 
98 			while (n != 0 && isspace(s[n - 1]))
99 				s[--n] = '\0';
100 
101 		} else if ((v = smbios_processor_family_desc(
102 		    p.smbp_family)) == NULL) {
103 			v = gettext("Unknown");
104 		}
105 
106 		(void) printf(gettext("%-32s %s\n"), v, info.smbi_location);
107 	}
108 
109 	return (0);
110 }
111 
112 /*
113  * NOTE: It would be very convenient to print the DIMM size in do_memdevs.
114  * Unfortunately, SMBIOS can only be relied upon to tell us whether a DIMM is
115  * present or not (smbmd_size == 0).  Some BIOSes do fill in an accurate size
116  * for DIMMs, whereas others fill in the maximum size, and still others insert
117  * a wrong value.  Sizes will need to wait for x86 memory controller interfaces
118  * or integration with IPMI, which can actually read the true DIMM SPD data.
119  */
120 /*ARGSUSED*/
121 static int
122 do_memdevs(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
123 {
124 	smbios_memdevice_t md;
125 
126 	if (sp->smbstr_type == SMB_TYPE_MEMDEVICE &&
127 	    smbios_info_memdevice(shp, sp->smbstr_id, &md) != SMB_ERR) {
128 
129 		const char *t = smbios_memdevice_type_desc(md.smbmd_type);
130 		char buf[8];
131 
132 		if (md.smbmd_set != (uint8_t)-1)
133 			(void) snprintf(buf, sizeof (buf), "%u", md.smbmd_set);
134 		else
135 			(void) strcpy(buf, "-");
136 
137 		(void) printf(gettext("%-11s %-6s %-3s %-19s %s\n"),
138 		    t ? t : gettext("Unknown"),
139 		    md.smbmd_size ? gettext("in use") : gettext("empty"),
140 		    buf, md.smbmd_dloc, md.smbmd_bloc);
141 	}
142 
143 	return (0);
144 }
145 
146 /*ARGSUSED*/
147 static int
148 do_obdevs(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
149 {
150 	smbios_obdev_t *argv;
151 	int i, argc;
152 
153 	if (sp->smbstr_type == SMB_TYPE_OBDEVS &&
154 	    (argc = smbios_info_obdevs(shp, sp->smbstr_id, 0, NULL)) > 0) {
155 		argv = alloca(sizeof (smbios_obdev_t) * argc);
156 		(void) smbios_info_obdevs(shp, sp->smbstr_id, argc, argv);
157 		for (i = 0; i < argc; i++)
158 			(void) printf(gettext("%s\n"), argv[i].smbd_name);
159 	}
160 
161 	return (0);
162 }
163 
164 /*ARGSUSED*/
165 static int
166 do_slots(smbios_hdl_t *shp, const smbios_struct_t *sp, void *arg)
167 {
168 	smbios_slot_t s;
169 
170 	if (sp->smbstr_type == SMB_TYPE_SLOT &&
171 	    smbios_info_slot(shp, sp->smbstr_id, &s) != SMB_ERR) {
172 
173 		const char *t = smbios_slot_type_desc(s.smbl_type);
174 		const char *u = smbios_slot_usage_desc(s.smbl_usage);
175 
176 		(void) printf(gettext("%-3u %-9s %-16s %s\n"),
177 		    s.smbl_id, u ? u : gettext("Unknown"),
178 		    t ? t : gettext("Unknown"), s.smbl_name);
179 	}
180 
181 	return (0);
182 }
183 
184 /*ARGSUSED*/
185 int
186 do_prominfo(int opt_v, char *progname, int opt_l, int opt_p)
187 {
188 	smbios_hdl_t *shp;
189 	smbios_system_t sys;
190 	smbios_bios_t bios;
191 	smbios_ipmi_t ipmi;
192 	smbios_info_t info;
193 
194 	const char *s;
195 	id_t id;
196 	int err;
197 
198 	if ((shp = smbios_open(NULL, SMB_VERSION, 0, &err)) == NULL) {
199 		(void) fprintf(stderr,
200 		    gettext("%s: failed to open SMBIOS: %s\n"),
201 		    progname, smbios_errmsg(err));
202 		return (1);
203 	}
204 
205 	if ((id = smbios_info_system(shp, &sys)) != SMB_ERR &&
206 	    smbios_info_common(shp, id, &info) != SMB_ERR) {
207 		(void) printf(gettext("System Configuration: %s %s\n"),
208 		    info.smbi_manufacturer, info.smbi_product);
209 	} else {
210 		(void) fprintf(stderr,
211 		    gettext("%s: failed to get system info: %s\n"),
212 		    progname, smbios_errmsg(smbios_errno(shp)));
213 	}
214 
215 	if (smbios_info_bios(shp, &bios) != SMB_ERR) {
216 		(void) printf(gettext("BIOS Configuration: %s %s %s\n"),
217 		    bios.smbb_vendor, bios.smbb_version, bios.smbb_reldate);
218 	} else {
219 		(void) fprintf(stderr,
220 		    gettext("%s: failed to get bios info: %s\n"),
221 		    progname, smbios_errmsg(smbios_errno(shp)));
222 	}
223 
224 	if (smbios_info_ipmi(shp, &ipmi) != SMB_ERR) {
225 		if ((s = smbios_ipmi_type_desc(ipmi.smbip_type)) == NULL)
226 			s = gettext("Unknown");
227 
228 		(void) printf(gettext("BMC Configuration: IPMI %u.%u (%s)\n"),
229 		    ipmi.smbip_vers.smbv_major, ipmi.smbip_vers.smbv_minor, s);
230 	}
231 
232 	(void) printf(gettext(
233 	    "\n==== Processor Sockets ====================================\n"));
234 
235 	(void) printf(gettext("\n%-32s %s"), "Version", "Location Tag");
236 
237 	(void) printf(gettext(
238 	    "\n-------------------------------- --------------------------\n"));
239 	(void) smbios_iter(shp, do_procs, NULL);
240 
241 	(void) printf(gettext(
242 	    "\n==== Memory Device Sockets ================================\n"));
243 
244 	(void) printf(gettext("\n%-11s %-6s %-3s %-19s %s"),
245 	    "Type", "Status", "Set", "Device Locator", "Bank Locator");
246 
247 	(void) printf(gettext(
248 	    "\n----------- ------ --- ------------------- ----------------\n"));
249 	(void) smbios_iter(shp, do_memdevs, NULL);
250 
251 	(void) printf(gettext(
252 	    "\n==== On-Board Devices =====================================\n"));
253 	(void) smbios_iter(shp, do_obdevs, NULL);
254 
255 	(void) printf(gettext(
256 	    "\n==== Upgradeable Slots ====================================\n"));
257 
258 	(void) printf(gettext("\n%-3s %-9s %-16s %s"),
259 	    "ID", "Status", "Type", "Description");
260 
261 	(void) printf(gettext(
262 	    "\n--- --------- ---------------- ----------------------------\n"));
263 	(void) smbios_iter(shp, do_slots, NULL);
264 
265 	smbios_close(shp);
266 	return (0);
267 }
268