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