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
do_procs(smbios_hdl_t * shp,const smbios_struct_t * sp,void * arg)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
do_memdevs(smbios_hdl_t * shp,const smbios_struct_t * sp,void * arg)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
do_obdevs(smbios_hdl_t * shp,const smbios_struct_t * sp,void * arg)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
do_slots(smbios_hdl_t * shp,const smbios_struct_t * sp,void * arg)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
do_prominfo(int opt_v,char * progname,int opt_l,int opt_p)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