Home | History | Annotate | Download | only in i386
      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