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