mirror of
https://git.jff.email/cgit/dmidecode.git
synced 2026-08-07 18:02:26 +00:00
5174 lines
120 KiB
C
5174 lines
120 KiB
C
/*
|
|
* DMI Decode
|
|
*
|
|
* Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
|
|
* Copyright (C) 2002-2017 Jean Delvare <jdelvare@suse.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* For the avoidance of doubt the "preferred form" of this code is one which
|
|
* is in an open unpatent encumbered format. Where cryptographic key signing
|
|
* forms part of the process of creating an executable the information
|
|
* including keys needed to generate an equivalently functional executable
|
|
* are deemed to be part of the source code.
|
|
*
|
|
* Unless specified otherwise, all references are aimed at the "System
|
|
* Management BIOS Reference Specification, Version 3.1.1" document,
|
|
* available from http://www.dmtf.org/standards/smbios.
|
|
*
|
|
* Note to contributors:
|
|
* Please reference every value you add or modify, especially if the
|
|
* information does not come from the above mentioned specification.
|
|
*
|
|
* Additional references:
|
|
* - Intel AP-485 revision 36
|
|
* "Intel Processor Identification and the CPUID Instruction"
|
|
* http://www.intel.com/support/processors/sb/cs-009861.htm
|
|
* - DMTF Common Information Model
|
|
* CIM Schema version 2.19.1
|
|
* http://www.dmtf.org/standards/cim/
|
|
* - IPMI 2.0 revision 1.0
|
|
* "Intelligent Platform Management Interface Specification"
|
|
* http://developer.intel.com/design/servers/ipmi/spec.htm
|
|
* - AMD publication #25481 revision 2.28
|
|
* "CPUID Specification"
|
|
* http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25481.pdf
|
|
* - BIOS Integrity Services Application Programming Interface version 1.0
|
|
* http://www.intel.com/design/archives/wfm/downloads/bisspec.htm
|
|
* - DMTF DSP0239 version 1.1.0
|
|
* "Management Component Transport Protocol (MCTP) IDs and Codes"
|
|
* http://www.dmtf.org/standards/pmci
|
|
* - "TPM Main, Part 2 TPM Structures"
|
|
* Specification version 1.2, level 2, revision 116
|
|
* https://trustedcomputinggroup.org/tpm-main-specification/
|
|
* - "PC Client Platform TPM Profile (PTP) Specification"
|
|
* Family "2.0", Level 00, Revision 00.43, January 26, 2015
|
|
* https://trustedcomputinggroup.org/pc-client-platform-tpm-profile-ptp-specification/
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "version.h"
|
|
#include "config.h"
|
|
#include "types.h"
|
|
#include "util.h"
|
|
#include "dmidecode.h"
|
|
#include "dmiopt.h"
|
|
#include "dmioem.h"
|
|
|
|
#define out_of_spec "<OUT OF SPEC>"
|
|
static const char *bad_index = "<BAD INDEX>";
|
|
|
|
#define SUPPORTED_SMBIOS_VER 0x030101
|
|
|
|
#define FLAG_NO_FILE_OFFSET (1 << 0)
|
|
#define FLAG_STOP_AT_EOT (1 << 1)
|
|
|
|
#define SYS_FIRMWARE_DIR "/sys/firmware/dmi/tables"
|
|
#define SYS_ENTRY_FILE SYS_FIRMWARE_DIR "/smbios_entry_point"
|
|
#define SYS_TABLE_FILE SYS_FIRMWARE_DIR "/DMI"
|
|
|
|
/*
|
|
* Type-independant Stuff
|
|
*/
|
|
|
|
/* Returns 1 if the buffer contains only printable ASCII characters */
|
|
int is_printable(const u8 *data, int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
if (data[i] < 32 || data[i] >= 127)
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
const char *dmi_string(const struct dmi_header *dm, u8 s)
|
|
{
|
|
char *bp = (char *)dm->data;
|
|
size_t i, len;
|
|
|
|
if (s == 0)
|
|
return "Not Specified";
|
|
|
|
bp += dm->length;
|
|
while (s > 1 && *bp)
|
|
{
|
|
bp += strlen(bp);
|
|
bp++;
|
|
s--;
|
|
}
|
|
|
|
if (!*bp)
|
|
return bad_index;
|
|
|
|
if (!(opt.flags & FLAG_DUMP))
|
|
{
|
|
/* ASCII filtering */
|
|
len = strlen(bp);
|
|
for (i = 0; i < len; i++)
|
|
if (bp[i] < 32 || bp[i] == 127)
|
|
bp[i] = '.';
|
|
}
|
|
|
|
return bp;
|
|
}
|
|
|
|
static const char *dmi_smbios_structure_type(u8 code)
|
|
{
|
|
static const char *type[] = {
|
|
"BIOS", /* 0 */
|
|
"System",
|
|
"Base Board",
|
|
"Chassis",
|
|
"Processor",
|
|
"Memory Controller",
|
|
"Memory Module",
|
|
"Cache",
|
|
"Port Connector",
|
|
"System Slots",
|
|
"On Board Devices",
|
|
"OEM Strings",
|
|
"System Configuration Options",
|
|
"BIOS Language",
|
|
"Group Associations",
|
|
"System Event Log",
|
|
"Physical Memory Array",
|
|
"Memory Device",
|
|
"32-bit Memory Error",
|
|
"Memory Array Mapped Address",
|
|
"Memory Device Mapped Address",
|
|
"Built-in Pointing Device",
|
|
"Portable Battery",
|
|
"System Reset",
|
|
"Hardware Security",
|
|
"System Power Controls",
|
|
"Voltage Probe",
|
|
"Cooling Device",
|
|
"Temperature Probe",
|
|
"Electrical Current Probe",
|
|
"Out-of-band Remote Access",
|
|
"Boot Integrity Services",
|
|
"System Boot",
|
|
"64-bit Memory Error",
|
|
"Management Device",
|
|
"Management Device Component",
|
|
"Management Device Threshold Data",
|
|
"Memory Channel",
|
|
"IPMI Device",
|
|
"Power Supply",
|
|
"Additional Information",
|
|
"Onboard Device",
|
|
"Management Controller Host Interface",
|
|
"TPM Device", /* 43 */
|
|
};
|
|
|
|
if (code >= 128)
|
|
return "OEM-specific";
|
|
if (code <= 43)
|
|
return type[code];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static int dmi_bcd_range(u8 value, u8 low, u8 high)
|
|
{
|
|
if (value > 0x99 || (value & 0x0F) > 0x09)
|
|
return 0;
|
|
if (value < low || value > high)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static void dmi_dump(const struct dmi_header *h, const char *prefix)
|
|
{
|
|
int row, i;
|
|
const char *s;
|
|
|
|
printf("%sHeader and Data:\n", prefix);
|
|
for (row = 0; row < ((h->length - 1) >> 4) + 1; row++)
|
|
{
|
|
printf("%s\t", prefix);
|
|
for (i = 0; i < 16 && i < h->length - (row << 4); i++)
|
|
printf("%s%02X", i ? " " : "",
|
|
(h->data)[(row << 4) + i]);
|
|
printf("\n");
|
|
}
|
|
|
|
if ((h->data)[h->length] || (h->data)[h->length + 1])
|
|
{
|
|
printf("%sStrings:\n", prefix);
|
|
i = 1;
|
|
while ((s = dmi_string(h, i++)) != bad_index)
|
|
{
|
|
if (opt.flags & FLAG_DUMP)
|
|
{
|
|
int j, l = strlen(s) + 1;
|
|
for (row = 0; row < ((l - 1) >> 4) + 1; row++)
|
|
{
|
|
printf("%s\t", prefix);
|
|
for (j = 0; j < 16 && j < l - (row << 4); j++)
|
|
printf("%s%02X", j ? " " : "",
|
|
(unsigned char)s[(row << 4) + j]);
|
|
printf("\n");
|
|
}
|
|
/* String isn't filtered yet so do it now */
|
|
printf("%s\t\"", prefix);
|
|
while (*s)
|
|
{
|
|
if (*s < 32 || *s == 127)
|
|
fputc('.', stdout);
|
|
else
|
|
fputc(*s, stdout);
|
|
s++;
|
|
}
|
|
printf("\"\n");
|
|
}
|
|
else
|
|
printf("%s\t%s\n", prefix, s);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* shift is 0 if the value is in bytes, 1 if it is in kilobytes */
|
|
static void dmi_print_memory_size(u64 code, int shift)
|
|
{
|
|
unsigned long capacity;
|
|
u16 split[7];
|
|
static const char *unit[8] = {
|
|
"bytes", "kB", "MB", "GB", "TB", "PB", "EB", "ZB"
|
|
};
|
|
int i;
|
|
|
|
/*
|
|
* We split the overall size in powers of thousand: EB, PB, TB, GB,
|
|
* MB, kB and B. In practice, it is expected that only one or two
|
|
* (consecutive) of these will be non-zero.
|
|
*/
|
|
split[0] = code.l & 0x3FFUL;
|
|
split[1] = (code.l >> 10) & 0x3FFUL;
|
|
split[2] = (code.l >> 20) & 0x3FFUL;
|
|
split[3] = ((code.h << 2) & 0x3FCUL) | (code.l >> 30);
|
|
split[4] = (code.h >> 8) & 0x3FFUL;
|
|
split[5] = (code.h >> 18) & 0x3FFUL;
|
|
split[6] = code.h >> 28;
|
|
|
|
/*
|
|
* Now we find the highest unit with a non-zero value. If the following
|
|
* is also non-zero, we use that as our base. If the following is zero,
|
|
* we simply display the highest unit.
|
|
*/
|
|
for (i = 6; i > 0; i--)
|
|
{
|
|
if (split[i])
|
|
break;
|
|
}
|
|
if (i > 0 && split[i - 1])
|
|
{
|
|
i--;
|
|
capacity = split[i] + (split[i + 1] << 10);
|
|
}
|
|
else
|
|
capacity = split[i];
|
|
|
|
printf(" %lu %s", capacity, unit[i + shift]);
|
|
}
|
|
|
|
/*
|
|
* 7.1 BIOS Information (Type 0)
|
|
*/
|
|
|
|
static void dmi_bios_runtime_size(u32 code)
|
|
{
|
|
if (code & 0x000003FF)
|
|
printf(" %u bytes", code);
|
|
else
|
|
printf(" %u kB", code >> 10);
|
|
}
|
|
|
|
static void dmi_bios_rom_size(u8 code1, u16 code2)
|
|
{
|
|
static const char *unit[4] = {
|
|
"MB", "GB", out_of_spec, out_of_spec
|
|
};
|
|
|
|
if (code1 != 0xFF)
|
|
printf(" %u kB", (code1 + 1) << 6);
|
|
else
|
|
printf(" %u %s", code2 & 0x3FFF, unit[code2 >> 14]);
|
|
}
|
|
|
|
static void dmi_bios_characteristics(u64 code, const char *prefix)
|
|
{
|
|
/* 7.1.1 */
|
|
static const char *characteristics[] = {
|
|
"BIOS characteristics not supported", /* 3 */
|
|
"ISA is supported",
|
|
"MCA is supported",
|
|
"EISA is supported",
|
|
"PCI is supported",
|
|
"PC Card (PCMCIA) is supported",
|
|
"PNP is supported",
|
|
"APM is supported",
|
|
"BIOS is upgradeable",
|
|
"BIOS shadowing is allowed",
|
|
"VLB is supported",
|
|
"ESCD support is available",
|
|
"Boot from CD is supported",
|
|
"Selectable boot is supported",
|
|
"BIOS ROM is socketed",
|
|
"Boot from PC Card (PCMCIA) is supported",
|
|
"EDD is supported",
|
|
"Japanese floppy for NEC 9800 1.2 MB is supported (int 13h)",
|
|
"Japanese floppy for Toshiba 1.2 MB is supported (int 13h)",
|
|
"5.25\"/360 kB floppy services are supported (int 13h)",
|
|
"5.25\"/1.2 MB floppy services are supported (int 13h)",
|
|
"3.5\"/720 kB floppy services are supported (int 13h)",
|
|
"3.5\"/2.88 MB floppy services are supported (int 13h)",
|
|
"Print screen service is supported (int 5h)",
|
|
"8042 keyboard services are supported (int 9h)",
|
|
"Serial services are supported (int 14h)",
|
|
"Printer services are supported (int 17h)",
|
|
"CGA/mono video services are supported (int 10h)",
|
|
"NEC PC-98" /* 31 */
|
|
};
|
|
int i;
|
|
|
|
/*
|
|
* This isn't very clear what this bit is supposed to mean
|
|
*/
|
|
if (code.l & (1 << 3))
|
|
{
|
|
printf("%s%s\n",
|
|
prefix, characteristics[0]);
|
|
return;
|
|
}
|
|
|
|
for (i = 4; i <= 31; i++)
|
|
if (code.l & (1 << i))
|
|
printf("%s%s\n",
|
|
prefix, characteristics[i - 3]);
|
|
}
|
|
|
|
static void dmi_bios_characteristics_x1(u8 code, const char *prefix)
|
|
{
|
|
/* 7.1.2.1 */
|
|
static const char *characteristics[] = {
|
|
"ACPI is supported", /* 0 */
|
|
"USB legacy is supported",
|
|
"AGP is supported",
|
|
"I2O boot is supported",
|
|
"LS-120 boot is supported",
|
|
"ATAPI Zip drive boot is supported",
|
|
"IEEE 1394 boot is supported",
|
|
"Smart battery is supported" /* 7 */
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i <= 7; i++)
|
|
if (code & (1 << i))
|
|
printf("%s%s\n",
|
|
prefix, characteristics[i]);
|
|
}
|
|
|
|
static void dmi_bios_characteristics_x2(u8 code, const char *prefix)
|
|
{
|
|
/* 37.1.2.2 */
|
|
static const char *characteristics[] = {
|
|
"BIOS boot specification is supported", /* 0 */
|
|
"Function key-initiated network boot is supported",
|
|
"Targeted content distribution is supported",
|
|
"UEFI is supported",
|
|
"System is a virtual machine" /* 4 */
|
|
};
|
|
int i;
|
|
|
|
for (i = 0; i <= 4; i++)
|
|
if (code & (1 << i))
|
|
printf("%s%s\n",
|
|
prefix, characteristics[i]);
|
|
}
|
|
|
|
/*
|
|
* 7.2 System Information (Type 1)
|
|
*/
|
|
|
|
static void dmi_system_uuid(const u8 *p, u16 ver)
|
|
{
|
|
int only0xFF = 1, only0x00 = 1;
|
|
int i;
|
|
|
|
for (i = 0; i < 16 && (only0x00 || only0xFF); i++)
|
|
{
|
|
if (p[i] != 0x00) only0x00 = 0;
|
|
if (p[i] != 0xFF) only0xFF = 0;
|
|
}
|
|
|
|
if (only0xFF)
|
|
{
|
|
printf("Not Present");
|
|
return;
|
|
}
|
|
if (only0x00)
|
|
{
|
|
printf("Not Settable");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* As of version 2.6 of the SMBIOS specification, the first 3
|
|
* fields of the UUID are supposed to be encoded on little-endian.
|
|
* The specification says that this is the defacto standard,
|
|
* however I've seen systems following RFC 4122 instead and use
|
|
* network byte order, so I am reluctant to apply the byte-swapping
|
|
* for older versions.
|
|
*/
|
|
if (ver >= 0x0206)
|
|
printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
|
p[3], p[2], p[1], p[0], p[5], p[4], p[7], p[6],
|
|
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
|
|
else
|
|
printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
|
p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7],
|
|
p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
|
|
}
|
|
|
|
static const char *dmi_system_wake_up_type(u8 code)
|
|
{
|
|
/* 7.2.2 */
|
|
static const char *type[] = {
|
|
"Reserved", /* 0x00 */
|
|
"Other",
|
|
"Unknown",
|
|
"APM Timer",
|
|
"Modem Ring",
|
|
"LAN Remote",
|
|
"Power Switch",
|
|
"PCI PME#",
|
|
"AC Power Restored" /* 0x08 */
|
|
};
|
|
|
|
if (code <= 0x08)
|
|
return type[code];
|
|
return out_of_spec;
|
|
}
|
|
|
|
/*
|
|
* 7.3 Base Board Information (Type 2)
|
|
*/
|
|
|
|
static void dmi_base_board_features(u8 code, const char *prefix)
|
|
{
|
|
/* 7.3.1 */
|
|
static const char *features[] = {
|
|
"Board is a hosting board", /* 0 */
|
|
"Board requires at least one daughter board",
|
|
"Board is removable",
|
|
"Board is replaceable",
|
|
"Board is hot swappable" /* 4 */
|
|
};
|
|
|
|
if ((code & 0x1F) == 0)
|
|
printf(" None\n");
|
|
else
|
|
{
|
|
int i;
|
|
|
|
printf("\n");
|
|
for (i = 0; i <= 4; i++)
|
|
if (code & (1 << i))
|
|
printf("%s%s\n",
|
|
prefix, features[i]);
|
|
}
|
|
}
|
|
|
|
static const char *dmi_base_board_type(u8 code)
|
|
{
|
|
/* 7.3.2 */
|
|
static const char *type[] = {
|
|
"Unknown", /* 0x01 */
|
|
"Other",
|
|
"Server Blade",
|
|
"Connectivity Switch",
|
|
"System Management Module",
|
|
"Processor Module",
|
|
"I/O Module",
|
|
"Memory Module",
|
|
"Daughter Board",
|
|
"Motherboard",
|
|
"Processor+Memory Module",
|
|
"Processor+I/O Module",
|
|
"Interconnect Board" /* 0x0D */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0D)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_base_board_handles(u8 count, const u8 *p, const char *prefix)
|
|
{
|
|
int i;
|
|
|
|
printf("%sContained Object Handles: %u\n",
|
|
prefix, count);
|
|
for (i = 0; i < count; i++)
|
|
printf("%s\t0x%04X\n",
|
|
prefix, WORD(p + sizeof(u16) * i));
|
|
}
|
|
|
|
/*
|
|
* 7.4 Chassis Information (Type 3)
|
|
*/
|
|
|
|
static const char *dmi_chassis_type(u8 code)
|
|
{
|
|
/* 7.4.1 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Desktop",
|
|
"Low Profile Desktop",
|
|
"Pizza Box",
|
|
"Mini Tower",
|
|
"Tower",
|
|
"Portable",
|
|
"Laptop",
|
|
"Notebook",
|
|
"Hand Held",
|
|
"Docking Station",
|
|
"All In One",
|
|
"Sub Notebook",
|
|
"Space-saving",
|
|
"Lunch Box",
|
|
"Main Server Chassis", /* CIM_Chassis.ChassisPackageType says "Main System Chassis" */
|
|
"Expansion Chassis",
|
|
"Sub Chassis",
|
|
"Bus Expansion Chassis",
|
|
"Peripheral Chassis",
|
|
"RAID Chassis",
|
|
"Rack Mount Chassis",
|
|
"Sealed-case PC",
|
|
"Multi-system",
|
|
"CompactPCI",
|
|
"AdvancedTCA",
|
|
"Blade",
|
|
"Blade Enclosing",
|
|
"Tablet",
|
|
"Convertible",
|
|
"Detachable",
|
|
"IoT Gateway",
|
|
"Embedded PC",
|
|
"Mini PC",
|
|
"Stick PC" /* 0x24 */
|
|
};
|
|
|
|
code &= 0x7F; /* bits 6:0 are chassis type, 7th bit is the lock bit */
|
|
|
|
if (code >= 0x01 && code <= 0x24)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_chassis_lock(u8 code)
|
|
{
|
|
static const char *lock[] = {
|
|
"Not Present", /* 0x00 */
|
|
"Present" /* 0x01 */
|
|
};
|
|
|
|
return lock[code];
|
|
}
|
|
|
|
static const char *dmi_chassis_state(u8 code)
|
|
{
|
|
/* 7.4.2 */
|
|
static const char *state[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Safe",
|
|
"Warning",
|
|
"Critical",
|
|
"Non-recoverable" /* 0x06 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x06)
|
|
return state[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_chassis_security_status(u8 code)
|
|
{
|
|
/* 7.4.3 */
|
|
static const char *status[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"None",
|
|
"External Interface Locked Out",
|
|
"External Interface Enabled" /* 0x05 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x05)
|
|
return status[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_chassis_height(u8 code)
|
|
{
|
|
if (code == 0x00)
|
|
printf(" Unspecified");
|
|
else
|
|
printf(" %u U", code);
|
|
}
|
|
|
|
static void dmi_chassis_power_cords(u8 code)
|
|
{
|
|
if (code == 0x00)
|
|
printf(" Unspecified");
|
|
else
|
|
printf(" %u", code);
|
|
}
|
|
|
|
static void dmi_chassis_elements(u8 count, u8 len, const u8 *p, const char *prefix)
|
|
{
|
|
int i;
|
|
|
|
printf("%sContained Elements: %u\n",
|
|
prefix, count);
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (len >= 0x03)
|
|
{
|
|
printf("%s\t%s (",
|
|
prefix, p[i * len] & 0x80 ?
|
|
dmi_smbios_structure_type(p[i * len] & 0x7F) :
|
|
dmi_base_board_type(p[i * len] & 0x7F));
|
|
if (p[1 + i * len] == p[2 + i * len])
|
|
printf("%u", p[1 + i * len]);
|
|
else
|
|
printf("%u-%u", p[1 + i * len], p[2 + i * len]);
|
|
printf(")\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 7.5 Processor Information (Type 4)
|
|
*/
|
|
|
|
static const char *dmi_processor_type(u8 code)
|
|
{
|
|
/* 7.5.1 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Central Processor",
|
|
"Math Processor",
|
|
"DSP Processor",
|
|
"Video Processor" /* 0x06 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x06)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_processor_family(const struct dmi_header *h, u16 ver)
|
|
{
|
|
const u8 *data = h->data;
|
|
unsigned int i, low, high;
|
|
u16 code;
|
|
|
|
/* 7.5.2 */
|
|
static struct {
|
|
int value;
|
|
const char *name;
|
|
} family2[] = {
|
|
{ 0x01, "Other" },
|
|
{ 0x02, "Unknown" },
|
|
{ 0x03, "8086" },
|
|
{ 0x04, "80286" },
|
|
{ 0x05, "80386" },
|
|
{ 0x06, "80486" },
|
|
{ 0x07, "8087" },
|
|
{ 0x08, "80287" },
|
|
{ 0x09, "80387" },
|
|
{ 0x0A, "80487" },
|
|
{ 0x0B, "Pentium" },
|
|
{ 0x0C, "Pentium Pro" },
|
|
{ 0x0D, "Pentium II" },
|
|
{ 0x0E, "Pentium MMX" },
|
|
{ 0x0F, "Celeron" },
|
|
{ 0x10, "Pentium II Xeon" },
|
|
{ 0x11, "Pentium III" },
|
|
{ 0x12, "M1" },
|
|
{ 0x13, "M2" },
|
|
{ 0x14, "Celeron M" },
|
|
{ 0x15, "Pentium 4 HT" },
|
|
|
|
{ 0x18, "Duron" },
|
|
{ 0x19, "K5" },
|
|
{ 0x1A, "K6" },
|
|
{ 0x1B, "K6-2" },
|
|
{ 0x1C, "K6-3" },
|
|
{ 0x1D, "Athlon" },
|
|
{ 0x1E, "AMD29000" },
|
|
{ 0x1F, "K6-2+" },
|
|
{ 0x20, "Power PC" },
|
|
{ 0x21, "Power PC 601" },
|
|
{ 0x22, "Power PC 603" },
|
|
{ 0x23, "Power PC 603+" },
|
|
{ 0x24, "Power PC 604" },
|
|
{ 0x25, "Power PC 620" },
|
|
{ 0x26, "Power PC x704" },
|
|
{ 0x27, "Power PC 750" },
|
|
{ 0x28, "Core Duo" },
|
|
{ 0x29, "Core Duo Mobile" },
|
|
{ 0x2A, "Core Solo Mobile" },
|
|
{ 0x2B, "Atom" },
|
|
{ 0x2C, "Core M" },
|
|
{ 0x2D, "Core m3" },
|
|
{ 0x2E, "Core m5" },
|
|
{ 0x2F, "Core m7" },
|
|
{ 0x30, "Alpha" },
|
|
{ 0x31, "Alpha 21064" },
|
|
{ 0x32, "Alpha 21066" },
|
|
{ 0x33, "Alpha 21164" },
|
|
{ 0x34, "Alpha 21164PC" },
|
|
{ 0x35, "Alpha 21164a" },
|
|
{ 0x36, "Alpha 21264" },
|
|
{ 0x37, "Alpha 21364" },
|
|
{ 0x38, "Turion II Ultra Dual-Core Mobile M" },
|
|
{ 0x39, "Turion II Dual-Core Mobile M" },
|
|
{ 0x3A, "Athlon II Dual-Core M" },
|
|
{ 0x3B, "Opteron 6100" },
|
|
{ 0x3C, "Opteron 4100" },
|
|
{ 0x3D, "Opteron 6200" },
|
|
{ 0x3E, "Opteron 4200" },
|
|
{ 0x3F, "FX" },
|
|
{ 0x40, "MIPS" },
|
|
{ 0x41, "MIPS R4000" },
|
|
{ 0x42, "MIPS R4200" },
|
|
{ 0x43, "MIPS R4400" },
|
|
{ 0x44, "MIPS R4600" },
|
|
{ 0x45, "MIPS R10000" },
|
|
{ 0x46, "C-Series" },
|
|
{ 0x47, "E-Series" },
|
|
{ 0x48, "A-Series" },
|
|
{ 0x49, "G-Series" },
|
|
{ 0x4A, "Z-Series" },
|
|
{ 0x4B, "R-Series" },
|
|
{ 0x4C, "Opteron 4300" },
|
|
{ 0x4D, "Opteron 6300" },
|
|
{ 0x4E, "Opteron 3300" },
|
|
{ 0x4F, "FirePro" },
|
|
{ 0x50, "SPARC" },
|
|
{ 0x51, "SuperSPARC" },
|
|
{ 0x52, "MicroSPARC II" },
|
|
{ 0x53, "MicroSPARC IIep" },
|
|
{ 0x54, "UltraSPARC" },
|
|
{ 0x55, "UltraSPARC II" },
|
|
{ 0x56, "UltraSPARC IIi" },
|
|
{ 0x57, "UltraSPARC III" },
|
|
{ 0x58, "UltraSPARC IIIi" },
|
|
|
|
{ 0x60, "68040" },
|
|
{ 0x61, "68xxx" },
|
|
{ 0x62, "68000" },
|
|
{ 0x63, "68010" },
|
|
{ 0x64, "68020" },
|
|
{ 0x65, "68030" },
|
|
{ 0x66, "Athlon X4" },
|
|
{ 0x67, "Opteron X1000" },
|
|
{ 0x68, "Opteron X2000" },
|
|
{ 0x69, "Opteron A-Series" },
|
|
{ 0x6A, "Opteron X3000" },
|
|
{ 0x6B, "Zen" },
|
|
|
|
{ 0x70, "Hobbit" },
|
|
|
|
{ 0x78, "Crusoe TM5000" },
|
|
{ 0x79, "Crusoe TM3000" },
|
|
{ 0x7A, "Efficeon TM8000" },
|
|
|
|
{ 0x80, "Weitek" },
|
|
|
|
{ 0x82, "Itanium" },
|
|
{ 0x83, "Athlon 64" },
|
|
{ 0x84, "Opteron" },
|
|
{ 0x85, "Sempron" },
|
|
{ 0x86, "Turion 64" },
|
|
{ 0x87, "Dual-Core Opteron" },
|
|
{ 0x88, "Athlon 64 X2" },
|
|
{ 0x89, "Turion 64 X2" },
|
|
{ 0x8A, "Quad-Core Opteron" },
|
|
{ 0x8B, "Third-Generation Opteron" },
|
|
{ 0x8C, "Phenom FX" },
|
|
{ 0x8D, "Phenom X4" },
|
|
{ 0x8E, "Phenom X2" },
|
|
{ 0x8F, "Athlon X2" },
|
|
{ 0x90, "PA-RISC" },
|
|
{ 0x91, "PA-RISC 8500" },
|
|
{ 0x92, "PA-RISC 8000" },
|
|
{ 0x93, "PA-RISC 7300LC" },
|
|
{ 0x94, "PA-RISC 7200" },
|
|
{ 0x95, "PA-RISC 7100LC" },
|
|
{ 0x96, "PA-RISC 7100" },
|
|
|
|
{ 0xA0, "V30" },
|
|
{ 0xA1, "Quad-Core Xeon 3200" },
|
|
{ 0xA2, "Dual-Core Xeon 3000" },
|
|
{ 0xA3, "Quad-Core Xeon 5300" },
|
|
{ 0xA4, "Dual-Core Xeon 5100" },
|
|
{ 0xA5, "Dual-Core Xeon 5000" },
|
|
{ 0xA6, "Dual-Core Xeon LV" },
|
|
{ 0xA7, "Dual-Core Xeon ULV" },
|
|
{ 0xA8, "Dual-Core Xeon 7100" },
|
|
{ 0xA9, "Quad-Core Xeon 5400" },
|
|
{ 0xAA, "Quad-Core Xeon" },
|
|
{ 0xAB, "Dual-Core Xeon 5200" },
|
|
{ 0xAC, "Dual-Core Xeon 7200" },
|
|
{ 0xAD, "Quad-Core Xeon 7300" },
|
|
{ 0xAE, "Quad-Core Xeon 7400" },
|
|
{ 0xAF, "Multi-Core Xeon 7400" },
|
|
{ 0xB0, "Pentium III Xeon" },
|
|
{ 0xB1, "Pentium III Speedstep" },
|
|
{ 0xB2, "Pentium 4" },
|
|
{ 0xB3, "Xeon" },
|
|
{ 0xB4, "AS400" },
|
|
{ 0xB5, "Xeon MP" },
|
|
{ 0xB6, "Athlon XP" },
|
|
{ 0xB7, "Athlon MP" },
|
|
{ 0xB8, "Itanium 2" },
|
|
{ 0xB9, "Pentium M" },
|
|
{ 0xBA, "Celeron D" },
|
|
{ 0xBB, "Pentium D" },
|
|
{ 0xBC, "Pentium EE" },
|
|
{ 0xBD, "Core Solo" },
|
|
/* 0xBE handled as a special case */
|
|
{ 0xBF, "Core 2 Duo" },
|
|
{ 0xC0, "Core 2 Solo" },
|
|
{ 0xC1, "Core 2 Extreme" },
|
|
{ 0xC2, "Core 2 Quad" },
|
|
{ 0xC3, "Core 2 Extreme Mobile" },
|
|
{ 0xC4, "Core 2 Duo Mobile" },
|
|
{ 0xC5, "Core 2 Solo Mobile" },
|
|
{ 0xC6, "Core i7" },
|
|
{ 0xC7, "Dual-Core Celeron" },
|
|
{ 0xC8, "IBM390" },
|
|
{ 0xC9, "G4" },
|
|
{ 0xCA, "G5" },
|
|
{ 0xCB, "ESA/390 G6" },
|
|
{ 0xCC, "z/Architecture" },
|
|
{ 0xCD, "Core i5" },
|
|
{ 0xCE, "Core i3" },
|
|
|
|
{ 0xD2, "C7-M" },
|
|
{ 0xD3, "C7-D" },
|
|
{ 0xD4, "C7" },
|
|
{ 0xD5, "Eden" },
|
|
{ 0xD6, "Multi-Core Xeon" },
|
|
{ 0xD7, "Dual-Core Xeon 3xxx" },
|
|
{ 0xD8, "Quad-Core Xeon 3xxx" },
|
|
{ 0xD9, "Nano" },
|
|
{ 0xDA, "Dual-Core Xeon 5xxx" },
|
|
{ 0xDB, "Quad-Core Xeon 5xxx" },
|
|
|
|
{ 0xDD, "Dual-Core Xeon 7xxx" },
|
|
{ 0xDE, "Quad-Core Xeon 7xxx" },
|
|
{ 0xDF, "Multi-Core Xeon 7xxx" },
|
|
{ 0xE0, "Multi-Core Xeon 3400" },
|
|
|
|
{ 0xE4, "Opteron 3000" },
|
|
{ 0xE5, "Sempron II" },
|
|
{ 0xE6, "Embedded Opteron Quad-Core" },
|
|
{ 0xE7, "Phenom Triple-Core" },
|
|
{ 0xE8, "Turion Ultra Dual-Core Mobile" },
|
|
{ 0xE9, "Turion Dual-Core Mobile" },
|
|
{ 0xEA, "Athlon Dual-Core" },
|
|
{ 0xEB, "Sempron SI" },
|
|
{ 0xEC, "Phenom II" },
|
|
{ 0xED, "Athlon II" },
|
|
{ 0xEE, "Six-Core Opteron" },
|
|
{ 0xEF, "Sempron M" },
|
|
|
|
{ 0xFA, "i860" },
|
|
{ 0xFB, "i960" },
|
|
|
|
{ 0x100, "ARMv7" },
|
|
{ 0x101, "ARMv8" },
|
|
{ 0x104, "SH-3" },
|
|
{ 0x105, "SH-4" },
|
|
{ 0x118, "ARM" },
|
|
{ 0x119, "StrongARM" },
|
|
{ 0x12C, "6x86" },
|
|
{ 0x12D, "MediaGX" },
|
|
{ 0x12E, "MII" },
|
|
{ 0x140, "WinChip" },
|
|
{ 0x15E, "DSP" },
|
|
{ 0x1F4, "Video Processor" },
|
|
};
|
|
/*
|
|
* Note to developers: when adding entries to this list, check if
|
|
* function dmi_processor_id below needs updating too.
|
|
*/
|
|
|
|
/* Special case for ambiguous value 0x30 (SMBIOS 2.0 only) */
|
|
if (ver == 0x0200 && data[0x06] == 0x30 && h->length >= 0x08)
|
|
{
|
|
const char *manufacturer = dmi_string(h, data[0x07]);
|
|
|
|
if (strstr(manufacturer, "Intel") != NULL
|
|
|| strncasecmp(manufacturer, "Intel", 5) == 0)
|
|
return "Pentium Pro";
|
|
}
|
|
|
|
code = (data[0x06] == 0xFE && h->length >= 0x2A) ?
|
|
WORD(data + 0x28) : data[0x06];
|
|
|
|
/* Special case for ambiguous value 0xBE */
|
|
if (code == 0xBE)
|
|
{
|
|
if (h->length >= 0x08)
|
|
{
|
|
const char *manufacturer = dmi_string(h, data[0x07]);
|
|
|
|
/* Best bet based on manufacturer string */
|
|
if (strstr(manufacturer, "Intel") != NULL
|
|
|| strncasecmp(manufacturer, "Intel", 5) == 0)
|
|
return "Core 2";
|
|
if (strstr(manufacturer, "AMD") != NULL
|
|
|| strncasecmp(manufacturer, "AMD", 3) == 0)
|
|
return "K7";
|
|
}
|
|
|
|
return "Core 2 or K7";
|
|
}
|
|
|
|
/* Perform a binary search */
|
|
low = 0;
|
|
high = ARRAY_SIZE(family2) - 1;
|
|
|
|
while (1)
|
|
{
|
|
i = (low + high) / 2;
|
|
if (family2[i].value == code)
|
|
return family2[i].name;
|
|
if (low == high) /* Not found */
|
|
return out_of_spec;
|
|
|
|
if (code < family2[i].value)
|
|
high = i;
|
|
else
|
|
low = i + 1;
|
|
}
|
|
}
|
|
|
|
static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
|
|
{
|
|
/* Intel AP-485 revision 36, table 2-4 */
|
|
static const char *flags[32] = {
|
|
"FPU (Floating-point unit on-chip)", /* 0 */
|
|
"VME (Virtual mode extension)",
|
|
"DE (Debugging extension)",
|
|
"PSE (Page size extension)",
|
|
"TSC (Time stamp counter)",
|
|
"MSR (Model specific registers)",
|
|
"PAE (Physical address extension)",
|
|
"MCE (Machine check exception)",
|
|
"CX8 (CMPXCHG8 instruction supported)",
|
|
"APIC (On-chip APIC hardware supported)",
|
|
NULL, /* 10 */
|
|
"SEP (Fast system call)",
|
|
"MTRR (Memory type range registers)",
|
|
"PGE (Page global enable)",
|
|
"MCA (Machine check architecture)",
|
|
"CMOV (Conditional move instruction supported)",
|
|
"PAT (Page attribute table)",
|
|
"PSE-36 (36-bit page size extension)",
|
|
"PSN (Processor serial number present and enabled)",
|
|
"CLFSH (CLFLUSH instruction supported)",
|
|
NULL, /* 20 */
|
|
"DS (Debug store)",
|
|
"ACPI (ACPI supported)",
|
|
"MMX (MMX technology supported)",
|
|
"FXSR (FXSAVE and FXSTOR instructions supported)",
|
|
"SSE (Streaming SIMD extensions)",
|
|
"SSE2 (Streaming SIMD extensions 2)",
|
|
"SS (Self-snoop)",
|
|
"HTT (Multi-threading)",
|
|
"TM (Thermal monitor supported)",
|
|
NULL, /* 30 */
|
|
"PBE (Pending break enabled)" /* 31 */
|
|
};
|
|
const u8 *data = h->data;
|
|
const u8 *p = data + 0x08;
|
|
u32 eax, edx;
|
|
int sig = 0;
|
|
u16 type;
|
|
|
|
type = (data[0x06] == 0xFE && h->length >= 0x2A) ?
|
|
WORD(data + 0x28) : data[0x06];
|
|
|
|
/*
|
|
* This might help learn about new processors supporting the
|
|
* CPUID instruction or another form of identification.
|
|
*/
|
|
printf("%sID: %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
prefix, p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
|
|
|
|
if (type == 0x05) /* 80386 */
|
|
{
|
|
u16 dx = WORD(p);
|
|
/*
|
|
* 80386 have a different signature.
|
|
*/
|
|
printf("%sSignature: Type %u, Family %u, Major Stepping %u, Minor Stepping %u\n",
|
|
prefix, dx >> 12, (dx >> 8) & 0xF,
|
|
(dx >> 4) & 0xF, dx & 0xF);
|
|
return;
|
|
}
|
|
if (type == 0x06) /* 80486 */
|
|
{
|
|
u16 dx = WORD(p);
|
|
/*
|
|
* Not all 80486 CPU support the CPUID instruction, we have to find
|
|
* wether the one we have here does or not. Note that this trick
|
|
* works only because we know that 80486 must be little-endian.
|
|
*/
|
|
if ((dx & 0x0F00) == 0x0400
|
|
&& ((dx & 0x00F0) == 0x0040 || (dx & 0x00F0) >= 0x0070)
|
|
&& ((dx & 0x000F) >= 0x0003))
|
|
sig = 1;
|
|
else
|
|
{
|
|
printf("%sSignature: Type %u, Family %u, Model %u, Stepping %u\n",
|
|
prefix, (dx >> 12) & 0x3, (dx >> 8) & 0xF,
|
|
(dx >> 4) & 0xF, dx & 0xF);
|
|
return;
|
|
}
|
|
}
|
|
else if ((type >= 0x100 && type <= 0x101) /* ARM */
|
|
|| (type >= 0x118 && type <= 0x119)) /* ARM */
|
|
{
|
|
u32 midr = DWORD(p);
|
|
/*
|
|
* The format of this field was not defined for ARM processors
|
|
* before version 3.1.0 of the SMBIOS specification, so we
|
|
* silently skip it if it reads all zeroes.
|
|
*/
|
|
if (midr == 0)
|
|
return;
|
|
printf("%sSignature: Implementor 0x%02x, Variant 0x%x, Architecture %u, Part 0x%03x, Revision %u\n",
|
|
prefix, midr >> 24, (midr >> 20) & 0xF,
|
|
(midr >> 16) & 0xF, (midr >> 4) & 0xFFF, midr & 0xF);
|
|
return;
|
|
}
|
|
else if ((type >= 0x0B && type <= 0x15) /* Intel, Cyrix */
|
|
|| (type >= 0x28 && type <= 0x2F) /* Intel */
|
|
|| (type >= 0xA1 && type <= 0xB3) /* Intel */
|
|
|| type == 0xB5 /* Intel */
|
|
|| (type >= 0xB9 && type <= 0xC7) /* Intel */
|
|
|| (type >= 0xCD && type <= 0xCE) /* Intel */
|
|
|| (type >= 0xD2 && type <= 0xDB) /* VIA, Intel */
|
|
|| (type >= 0xDD && type <= 0xE0)) /* Intel */
|
|
sig = 1;
|
|
else if ((type >= 0x18 && type <= 0x1D) /* AMD */
|
|
|| type == 0x1F /* AMD */
|
|
|| (type >= 0x38 && type <= 0x3F) /* AMD */
|
|
|| (type >= 0x46 && type <= 0x4F) /* AMD */
|
|
|| (type >= 0x66 && type <= 0x6B) /* AMD */
|
|
|| (type >= 0x83 && type <= 0x8F) /* AMD */
|
|
|| (type >= 0xB6 && type <= 0xB7) /* AMD */
|
|
|| (type >= 0xE4 && type <= 0xEF)) /* AMD */
|
|
sig = 2;
|
|
else if (type == 0x01 || type == 0x02)
|
|
{
|
|
const char *version = dmi_string(h, data[0x10]);
|
|
/*
|
|
* Some X86-class CPU have family "Other" or "Unknown". In this case,
|
|
* we use the version string to determine if they are known to
|
|
* support the CPUID instruction.
|
|
*/
|
|
if (strncmp(version, "Pentium III MMX", 15) == 0
|
|
|| strncmp(version, "Intel(R) Core(TM)2", 18) == 0
|
|
|| strncmp(version, "Intel(R) Pentium(R)", 19) == 0
|
|
|| strcmp(version, "Genuine Intel(R) CPU U1400") == 0)
|
|
sig = 1;
|
|
else if (strncmp(version, "AMD Athlon(TM)", 14) == 0
|
|
|| strncmp(version, "AMD Opteron(tm)", 15) == 0
|
|
|| strncmp(version, "Dual-Core AMD Opteron(tm)", 25) == 0)
|
|
sig = 2;
|
|
else
|
|
return;
|
|
}
|
|
else /* neither X86 nor ARM */
|
|
return;
|
|
|
|
/*
|
|
* Extra flags are now returned in the ECX register when one calls
|
|
* the CPUID instruction. Their meaning is explained in table 3-5, but
|
|
* DMI doesn't support this yet.
|
|
*/
|
|
eax = DWORD(p);
|
|
edx = DWORD(p + 4);
|
|
switch (sig)
|
|
{
|
|
case 1: /* Intel */
|
|
printf("%sSignature: Type %u, Family %u, Model %u, Stepping %u\n",
|
|
prefix, (eax >> 12) & 0x3,
|
|
((eax >> 20) & 0xFF) + ((eax >> 8) & 0x0F),
|
|
((eax >> 12) & 0xF0) + ((eax >> 4) & 0x0F),
|
|
eax & 0xF);
|
|
break;
|
|
case 2: /* AMD, publication #25481 revision 2.28 */
|
|
printf("%sSignature: Family %u, Model %u, Stepping %u\n",
|
|
prefix,
|
|
((eax >> 8) & 0xF) + (((eax >> 8) & 0xF) == 0xF ? (eax >> 20) & 0xFF : 0),
|
|
((eax >> 4) & 0xF) | (((eax >> 8) & 0xF) == 0xF ? (eax >> 12) & 0xF0 : 0),
|
|
eax & 0xF);
|
|
break;
|
|
}
|
|
|
|
edx = DWORD(p + 4);
|
|
printf("%sFlags:", prefix);
|
|
if ((edx & 0xBFEFFBFF) == 0)
|
|
printf(" None\n");
|
|
else
|
|
{
|
|
int i;
|
|
|
|
printf("\n");
|
|
for (i = 0; i <= 31; i++)
|
|
if (flags[i] != NULL && edx & (1 << i))
|
|
printf("%s\t%s\n", prefix, flags[i]);
|
|
}
|
|
}
|
|
|
|
static void dmi_processor_voltage(u8 code)
|
|
{
|
|
/* 7.5.4 */
|
|
static const char *voltage[] = {
|
|
"5.0 V", /* 0 */
|
|
"3.3 V",
|
|
"2.9 V" /* 2 */
|
|
};
|
|
int i;
|
|
|
|
if (code & 0x80)
|
|
printf(" %.1f V", (float)(code & 0x7f) / 10);
|
|
else
|
|
{
|
|
for (i = 0; i <= 2; i++)
|
|
if (code & (1 << i))
|
|
printf(" %s", voltage[i]);
|
|
if (code == 0x00)
|
|
printf(" Unknown");
|
|
}
|
|
}
|
|
|
|
static void dmi_processor_frequency(const u8 *p)
|
|
{
|
|
u16 code = WORD(p);
|
|
|
|
if (code)
|
|
printf("%u MHz", code);
|
|
else
|
|
printf("Unknown");
|
|
}
|
|
|
|
/* code is assumed to be a 3-bit value */
|
|
static const char *dmi_processor_status(u8 code)
|
|
{
|
|
static const char *status[] = {
|
|
"Unknown", /* 0x00 */
|
|
"Enabled",
|
|
"Disabled By User",
|
|
"Disabled By BIOS",
|
|
"Idle", /* 0x04 */
|
|
out_of_spec,
|
|
out_of_spec,
|
|
"Other" /* 0x07 */
|
|
};
|
|
|
|
return status[code];
|
|
}
|
|
|
|
static const char *dmi_processor_upgrade(u8 code)
|
|
{
|
|
/* 7.5.5 */
|
|
static const char *upgrade[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Daughter Board",
|
|
"ZIF Socket",
|
|
"Replaceable Piggy Back",
|
|
"None",
|
|
"LIF Socket",
|
|
"Slot 1",
|
|
"Slot 2",
|
|
"370-pin Socket",
|
|
"Slot A",
|
|
"Slot M",
|
|
"Socket 423",
|
|
"Socket A (Socket 462)",
|
|
"Socket 478",
|
|
"Socket 754",
|
|
"Socket 940",
|
|
"Socket 939",
|
|
"Socket mPGA604",
|
|
"Socket LGA771",
|
|
"Socket LGA775",
|
|
"Socket S1",
|
|
"Socket AM2",
|
|
"Socket F (1207)",
|
|
"Socket LGA1366",
|
|
"Socket G34",
|
|
"Socket AM3",
|
|
"Socket C32",
|
|
"Socket LGA1156",
|
|
"Socket LGA1567",
|
|
"Socket PGA988A",
|
|
"Socket BGA1288",
|
|
"Socket rPGA988B",
|
|
"Socket BGA1023",
|
|
"Socket BGA1224",
|
|
"Socket BGA1155",
|
|
"Socket LGA1356",
|
|
"Socket LGA2011",
|
|
"Socket FS1",
|
|
"Socket FS2",
|
|
"Socket FM1",
|
|
"Socket FM2",
|
|
"Socket LGA2011-3",
|
|
"Socket LGA1356-3",
|
|
"Socket LGA1150",
|
|
"Socket BGA1168",
|
|
"Socket BGA1234",
|
|
"Socket BGA1364",
|
|
"Socket AM4",
|
|
"Socket LGA1151",
|
|
"Socket BGA1356",
|
|
"Socket BGA1440",
|
|
"Socket BGA1515",
|
|
"Socket LGA3647-1",
|
|
"Socket SP3",
|
|
"Socket SP3r2" /* 0x38 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x38)
|
|
return upgrade[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_processor_cache(u16 code, const char *level, u16 ver)
|
|
{
|
|
if (code == 0xFFFF)
|
|
{
|
|
if (ver >= 0x0203)
|
|
printf(" Not Provided");
|
|
else
|
|
printf(" No %s Cache", level);
|
|
}
|
|
else
|
|
printf(" 0x%04X", code);
|
|
}
|
|
|
|
static void dmi_processor_characteristics(u16 code, const char *prefix)
|
|
{
|
|
/* 7.5.9 */
|
|
static const char *characteristics[] = {
|
|
"64-bit capable", /* 2 */
|
|
"Multi-Core",
|
|
"Hardware Thread",
|
|
"Execute Protection",
|
|
"Enhanced Virtualization",
|
|
"Power/Performance Control" /* 7 */
|
|
};
|
|
|
|
if ((code & 0x00FC) == 0)
|
|
printf(" None\n");
|
|
else
|
|
{
|
|
int i;
|
|
|
|
printf("\n");
|
|
for (i = 2; i <= 7; i++)
|
|
if (code & (1 << i))
|
|
printf("%s%s\n", prefix, characteristics[i - 2]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 7.6 Memory Controller Information (Type 5)
|
|
*/
|
|
|
|
static const char *dmi_memory_controller_ed_method(u8 code)
|
|
{
|
|
/* 7.6.1 */
|
|
static const char *method[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"None",
|
|
"8-bit Parity",
|
|
"32-bit ECC",
|
|
"64-bit ECC",
|
|
"128-bit ECC",
|
|
"CRC" /* 0x08 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x08)
|
|
return method[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_memory_controller_ec_capabilities(u8 code, const char *prefix)
|
|
{
|
|
/* 7.6.2 */
|
|
static const char *capabilities[] = {
|
|
"Other", /* 0 */
|
|
"Unknown",
|
|
"None",
|
|
"Single-bit Error Correcting",
|
|
"Double-bit Error Correcting",
|
|
"Error Scrubbing" /* 5 */
|
|
};
|
|
|
|
if ((code & 0x3F) == 0)
|
|
printf(" None\n");
|
|
else
|
|
{
|
|
int i;
|
|
|
|
printf("\n");
|
|
for (i = 0; i <= 5; i++)
|
|
if (code & (1 << i))
|
|
printf("%s%s\n", prefix, capabilities[i]);
|
|
}
|
|
}
|
|
|
|
static const char *dmi_memory_controller_interleave(u8 code)
|
|
{
|
|
/* 7.6.3 */
|
|
static const char *interleave[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"One-way Interleave",
|
|
"Two-way Interleave",
|
|
"Four-way Interleave",
|
|
"Eight-way Interleave",
|
|
"Sixteen-way Interleave" /* 0x07 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x07)
|
|
return interleave[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_memory_controller_speeds(u16 code, const char *prefix)
|
|
{
|
|
/* 7.6.4 */
|
|
const char *speeds[] = {
|
|
"Other", /* 0 */
|
|
"Unknown",
|
|
"70 ns",
|
|
"60 ns",
|
|
"50 ns" /* 4 */
|
|
};
|
|
|
|
if ((code & 0x001F) == 0)
|
|
printf(" None\n");
|
|
else
|
|
{
|
|
int i;
|
|
|
|
printf("\n");
|
|
for (i = 0; i <= 4; i++)
|
|
if (code & (1 << i))
|
|
printf("%s%s\n", prefix, speeds[i]);
|
|
}
|
|
}
|
|
|
|
static void dmi_memory_controller_slots(u8 count, const u8 *p, const char *prefix)
|
|
{
|
|
int i;
|
|
|
|
printf("%sAssociated Memory Slots: %u\n",
|
|
prefix, count);
|
|
for (i = 0; i < count; i++)
|
|
printf("%s\t0x%04X\n",
|
|
prefix, WORD(p + sizeof(u16) * i));
|
|
}
|
|
|
|
/*
|
|
* 7.7 Memory Module Information (Type 6)
|
|
*/
|
|
|
|
static void dmi_memory_module_types(u16 code, const char *sep)
|
|
{
|
|
/* 7.7.1 */
|
|
static const char *types[] = {
|
|
"Other", /* 0 */
|
|
"Unknown",
|
|
"Standard",
|
|
"FPM",
|
|
"EDO",
|
|
"Parity",
|
|
"ECC",
|
|
"SIMM",
|
|
"DIMM",
|
|
"Burst EDO",
|
|
"SDRAM" /* 10 */
|
|
};
|
|
|
|
if ((code & 0x07FF) == 0)
|
|
printf(" None");
|
|
else
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i <= 10; i++)
|
|
if (code & (1 << i))
|
|
printf("%s%s", sep, types[i]);
|
|
}
|
|
}
|
|
|
|
static void dmi_memory_module_connections(u8 code)
|
|
{
|
|
if (code == 0xFF)
|
|
printf(" None");
|
|
else
|
|
{
|
|
if ((code & 0xF0) != 0xF0)
|
|
printf(" %u", code >> 4);
|
|
if ((code & 0x0F) != 0x0F)
|
|
printf(" %u", code & 0x0F);
|
|
}
|
|
}
|
|
|
|
static void dmi_memory_module_speed(u8 code)
|
|
{
|
|
if (code == 0)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u ns", code);
|
|
}
|
|
|
|
static void dmi_memory_module_size(u8 code)
|
|
{
|
|
/* 7.7.2 */
|
|
switch (code & 0x7F)
|
|
{
|
|
case 0x7D:
|
|
printf(" Not Determinable");
|
|
break;
|
|
case 0x7E:
|
|
printf(" Disabled");
|
|
break;
|
|
case 0x7F:
|
|
printf(" Not Installed");
|
|
return;
|
|
default:
|
|
printf(" %u MB", 1 << (code & 0x7F));
|
|
}
|
|
|
|
if (code & 0x80)
|
|
printf(" (Double-bank Connection)");
|
|
else
|
|
printf(" (Single-bank Connection)");
|
|
}
|
|
|
|
static void dmi_memory_module_error(u8 code, const char *prefix)
|
|
{
|
|
if (code & (1 << 2))
|
|
printf(" See Event Log\n");
|
|
else
|
|
{ if ((code & 0x03) == 0)
|
|
printf(" OK\n");
|
|
if (code & (1 << 0))
|
|
printf("%sUncorrectable Errors\n", prefix);
|
|
if (code & (1 << 1))
|
|
printf("%sCorrectable Errors\n", prefix);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 7.8 Cache Information (Type 7)
|
|
*/
|
|
|
|
static const char *dmi_cache_mode(u8 code)
|
|
{
|
|
static const char *mode[] = {
|
|
"Write Through", /* 0x00 */
|
|
"Write Back",
|
|
"Varies With Memory Address",
|
|
"Unknown" /* 0x03 */
|
|
};
|
|
|
|
return mode[code];
|
|
}
|
|
|
|
/* code is assumed to be a 2-bit value */
|
|
static const char *dmi_cache_location(u8 code)
|
|
{
|
|
static const char *location[4] = {
|
|
"Internal", /* 0x00 */
|
|
"External",
|
|
out_of_spec, /* 0x02 */
|
|
"Unknown" /* 0x03 */
|
|
};
|
|
|
|
return location[code];
|
|
}
|
|
|
|
static void dmi_cache_size(u16 code)
|
|
{
|
|
if (code & 0x8000)
|
|
printf(" %u kB", (code & 0x7FFF) << 6);
|
|
else
|
|
printf(" %u kB", code);
|
|
}
|
|
|
|
static void dmi_cache_size_2(u32 code)
|
|
{
|
|
if (code & 0x80000000)
|
|
{
|
|
code &= 0x7FFFFFFFLU;
|
|
/* Use a more convenient unit for large cache size */
|
|
if (code >= 0x8000)
|
|
printf(" %u MB", code >> 4);
|
|
else
|
|
printf(" %u kB", code << 6);
|
|
}
|
|
else
|
|
printf(" %u kB", code);
|
|
}
|
|
|
|
static void dmi_cache_types(u16 code, const char *sep)
|
|
{
|
|
/* 7.8.2 */
|
|
static const char *types[] = {
|
|
"Other", /* 0 */
|
|
"Unknown",
|
|
"Non-burst",
|
|
"Burst",
|
|
"Pipeline Burst",
|
|
"Synchronous",
|
|
"Asynchronous" /* 6 */
|
|
};
|
|
|
|
if ((code & 0x007F) == 0)
|
|
printf(" None");
|
|
else
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i <= 6; i++)
|
|
if (code & (1 << i))
|
|
printf("%s%s", sep, types[i]);
|
|
}
|
|
}
|
|
|
|
static const char *dmi_cache_ec_type(u8 code)
|
|
{
|
|
/* 7.8.3 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"None",
|
|
"Parity",
|
|
"Single-bit ECC",
|
|
"Multi-bit ECC" /* 0x06 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x06)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_cache_type(u8 code)
|
|
{
|
|
/* 7.8.4 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Instruction",
|
|
"Data",
|
|
"Unified" /* 0x05 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x05)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_cache_associativity(u8 code)
|
|
{
|
|
/* 7.8.5 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Direct Mapped",
|
|
"2-way Set-associative",
|
|
"4-way Set-associative",
|
|
"Fully Associative",
|
|
"8-way Set-associative",
|
|
"16-way Set-associative",
|
|
"12-way Set-associative",
|
|
"24-way Set-associative",
|
|
"32-way Set-associative",
|
|
"48-way Set-associative",
|
|
"64-way Set-associative",
|
|
"20-way Set-associative" /* 0x0E */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0E)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
/*
|
|
* 7.9 Port Connector Information (Type 8)
|
|
*/
|
|
|
|
static const char *dmi_port_connector_type(u8 code)
|
|
{
|
|
/* 7.9.2 */
|
|
static const char *type[] = {
|
|
"None", /* 0x00 */
|
|
"Centronics",
|
|
"Mini Centronics",
|
|
"Proprietary",
|
|
"DB-25 male",
|
|
"DB-25 female",
|
|
"DB-15 male",
|
|
"DB-15 female",
|
|
"DB-9 male",
|
|
"DB-9 female",
|
|
"RJ-11",
|
|
"RJ-45",
|
|
"50 Pin MiniSCSI",
|
|
"Mini DIN",
|
|
"Micro DIN",
|
|
"PS/2",
|
|
"Infrared",
|
|
"HP-HIL",
|
|
"Access Bus (USB)",
|
|
"SSA SCSI",
|
|
"Circular DIN-8 male",
|
|
"Circular DIN-8 female",
|
|
"On Board IDE",
|
|
"On Board Floppy",
|
|
"9 Pin Dual Inline (pin 10 cut)",
|
|
"25 Pin Dual Inline (pin 26 cut)",
|
|
"50 Pin Dual Inline",
|
|
"68 Pin Dual Inline",
|
|
"On Board Sound Input From CD-ROM",
|
|
"Mini Centronics Type-14",
|
|
"Mini Centronics Type-26",
|
|
"Mini Jack (headphones)",
|
|
"BNC",
|
|
"IEEE 1394",
|
|
"SAS/SATA Plug Receptacle" /* 0x22 */
|
|
};
|
|
static const char *type_0xA0[] = {
|
|
"PC-98", /* 0xA0 */
|
|
"PC-98 Hireso",
|
|
"PC-H98",
|
|
"PC-98 Note",
|
|
"PC-98 Full" /* 0xA4 */
|
|
};
|
|
|
|
if (code <= 0x22)
|
|
return type[code];
|
|
if (code >= 0xA0 && code <= 0xA4)
|
|
return type_0xA0[code - 0xA0];
|
|
if (code == 0xFF)
|
|
return "Other";
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_port_type(u8 code)
|
|
{
|
|
/* 7.9.3 */
|
|
static const char *type[] = {
|
|
"None", /* 0x00 */
|
|
"Parallel Port XT/AT Compatible",
|
|
"Parallel Port PS/2",
|
|
"Parallel Port ECP",
|
|
"Parallel Port EPP",
|
|
"Parallel Port ECP/EPP",
|
|
"Serial Port XT/AT Compatible",
|
|
"Serial Port 16450 Compatible",
|
|
"Serial Port 16550 Compatible",
|
|
"Serial Port 16550A Compatible",
|
|
"SCSI Port",
|
|
"MIDI Port",
|
|
"Joystick Port",
|
|
"Keyboard Port",
|
|
"Mouse Port",
|
|
"SSA SCSI",
|
|
"USB",
|
|
"Firewire (IEEE P1394)",
|
|
"PCMCIA Type I",
|
|
"PCMCIA Type II",
|
|
"PCMCIA Type III",
|
|
"Cardbus",
|
|
"Access Bus Port",
|
|
"SCSI II",
|
|
"SCSI Wide",
|
|
"PC-98",
|
|
"PC-98 Hireso",
|
|
"PC-H98",
|
|
"Video Port",
|
|
"Audio Port",
|
|
"Modem Port",
|
|
"Network Port",
|
|
"SATA",
|
|
"SAS" /* 0x21 */
|
|
};
|
|
static const char *type_0xA0[] = {
|
|
"8251 Compatible", /* 0xA0 */
|
|
"8251 FIFO Compatible" /* 0xA1 */
|
|
};
|
|
|
|
if (code <= 0x21)
|
|
return type[code];
|
|
if (code >= 0xA0 && code <= 0xA1)
|
|
return type_0xA0[code - 0xA0];
|
|
if (code == 0xFF)
|
|
return "Other";
|
|
return out_of_spec;
|
|
}
|
|
|
|
/*
|
|
* 7.10 System Slots (Type 9)
|
|
*/
|
|
|
|
static const char *dmi_slot_type(u8 code)
|
|
{
|
|
/* 7.10.1 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"ISA",
|
|
"MCA",
|
|
"EISA",
|
|
"PCI",
|
|
"PC Card (PCMCIA)",
|
|
"VLB",
|
|
"Proprietary",
|
|
"Processor Card",
|
|
"Proprietary Memory Card",
|
|
"I/O Riser Card",
|
|
"NuBus",
|
|
"PCI-66",
|
|
"AGP",
|
|
"AGP 2x",
|
|
"AGP 4x",
|
|
"PCI-X",
|
|
"AGP 8x",
|
|
"M.2 Socket 1-DP",
|
|
"M.2 Socket 1-SD",
|
|
"M.2 Socket 2",
|
|
"M.2 Socket 3",
|
|
"MXM Type I",
|
|
"MXM Type II",
|
|
"MXM Type III",
|
|
"MXM Type III-HE",
|
|
"MXM Type IV",
|
|
"MXM 3.0 Type A",
|
|
"MXM 3.0 Type B",
|
|
"PCI Express 2 SFF-8639",
|
|
"PCI Express 3 SFF-8639",
|
|
"PCI Express Mini 52-pin with bottom-side keep-outs",
|
|
"PCI Express Mini 52-pin without bottom-side keep-outs",
|
|
"PCI Express Mini 76-pin" /* 0x23 */
|
|
};
|
|
static const char *type_0xA0[] = {
|
|
"PC-98/C20", /* 0xA0 */
|
|
"PC-98/C24",
|
|
"PC-98/E",
|
|
"PC-98/Local Bus",
|
|
"PC-98/Card",
|
|
"PCI Express",
|
|
"PCI Express x1",
|
|
"PCI Express x2",
|
|
"PCI Express x4",
|
|
"PCI Express x8",
|
|
"PCI Express x16",
|
|
"PCI Express 2",
|
|
"PCI Express 2 x1",
|
|
"PCI Express 2 x2",
|
|
"PCI Express 2 x4",
|
|
"PCI Express 2 x8",
|
|
"PCI Express 2 x16",
|
|
"PCI Express 3",
|
|
"PCI Express 3 x1",
|
|
"PCI Express 3 x2",
|
|
"PCI Express 3 x4",
|
|
"PCI Express 3 x8",
|
|
"PCI Express 3 x16" /* 0xB6 */
|
|
};
|
|
/*
|
|
* Note to developers: when adding entries to these lists, check if
|
|
* function dmi_slot_id below needs updating too.
|
|
*/
|
|
|
|
if (code >= 0x01 && code <= 0x23)
|
|
return type[code - 0x01];
|
|
if (code >= 0xA0 && code <= 0xB6)
|
|
return type_0xA0[code - 0xA0];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_slot_bus_width(u8 code)
|
|
{
|
|
/* 7.10.2 */
|
|
static const char *width[] = {
|
|
"", /* 0x01, "Other" */
|
|
"", /* "Unknown" */
|
|
"8-bit ",
|
|
"16-bit ",
|
|
"32-bit ",
|
|
"64-bit ",
|
|
"128-bit ",
|
|
"x1 ",
|
|
"x2 ",
|
|
"x4 ",
|
|
"x8 ",
|
|
"x12 ",
|
|
"x16 ",
|
|
"x32 " /* 0x0E */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0E)
|
|
return width[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_slot_current_usage(u8 code)
|
|
{
|
|
/* 7.10.3 */
|
|
static const char *usage[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Available",
|
|
"In Use" /* 0x04 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x04)
|
|
return usage[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_slot_length(u8 code)
|
|
{
|
|
/* 7.1O.4 */
|
|
static const char *length[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Short",
|
|
"Long" /* 0x04 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x04)
|
|
return length[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_slot_id(u8 code1, u8 code2, u8 type, const char *prefix)
|
|
{
|
|
/* 7.10.5 */
|
|
switch (type)
|
|
{
|
|
case 0x04: /* MCA */
|
|
printf("%sID: %u\n", prefix, code1);
|
|
break;
|
|
case 0x05: /* EISA */
|
|
printf("%sID: %u\n", prefix, code1);
|
|
break;
|
|
case 0x06: /* PCI */
|
|
case 0x0E: /* PCI */
|
|
case 0x0F: /* AGP */
|
|
case 0x10: /* AGP */
|
|
case 0x11: /* AGP */
|
|
case 0x12: /* PCI-X */
|
|
case 0x13: /* AGP */
|
|
case 0x1F: /* PCI Express 2 */
|
|
case 0x20: /* PCI Express 3 */
|
|
case 0x21: /* PCI Express Mini */
|
|
case 0x22: /* PCI Express Mini */
|
|
case 0x23: /* PCI Express Mini */
|
|
case 0xA5: /* PCI Express */
|
|
case 0xA6: /* PCI Express */
|
|
case 0xA7: /* PCI Express */
|
|
case 0xA8: /* PCI Express */
|
|
case 0xA9: /* PCI Express */
|
|
case 0xAA: /* PCI Express */
|
|
case 0xAB: /* PCI Express 2 */
|
|
case 0xAC: /* PCI Express 2 */
|
|
case 0xAD: /* PCI Express 2 */
|
|
case 0xAE: /* PCI Express 2 */
|
|
case 0xAF: /* PCI Express 2 */
|
|
case 0xB0: /* PCI Express 2 */
|
|
case 0xB1: /* PCI Express 3 */
|
|
case 0xB2: /* PCI Express 3 */
|
|
case 0xB3: /* PCI Express 3 */
|
|
case 0xB4: /* PCI Express 3 */
|
|
case 0xB5: /* PCI Express 3 */
|
|
case 0xB6: /* PCI Express 3 */
|
|
printf("%sID: %u\n", prefix, code1);
|
|
break;
|
|
case 0x07: /* PCMCIA */
|
|
printf("%sID: Adapter %u, Socket %u\n", prefix, code1, code2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void dmi_slot_characteristics(u8 code1, u8 code2, const char *prefix)
|
|
{
|
|
/* 7.10.6 */
|
|
static const char *characteristics1[] = {
|
|
"5.0 V is provided", /* 1 */
|
|
"3.3 V is provided",
|
|
"Opening is shared",
|
|
"PC Card-16 is supported",
|
|
"Cardbus is supported",
|
|
"Zoom Video is supported",
|
|
"Modem ring resume is supported" /* 7 */
|
|
};
|
|
/* 7.10.7 */
|
|
static const char *characteristics2[] = {
|
|
"PME signal is supported", /* 0 */
|
|
"Hot-plug devices are supported",
|
|
"SMBus signal is supported" /* 2 */
|
|
};
|
|
|
|
if (code1 & (1 << 0))
|
|
printf(" Unknown\n");
|
|
else if ((code1 & 0xFE) == 0 && (code2 & 0x07) == 0)
|
|
printf(" None\n");
|
|
else
|
|
{
|
|
int i;
|
|
|
|
printf("\n");
|
|
for (i = 1; i <= 7; i++)
|
|
if (code1 & (1 << i))
|
|
printf("%s%s\n", prefix, characteristics1[i - 1]);
|
|
for (i = 0; i <= 2; i++)
|
|
if (code2 & (1 << i))
|
|
printf("%s%s\n", prefix, characteristics2[i]);
|
|
}
|
|
}
|
|
|
|
static void dmi_slot_segment_bus_func(u16 code1, u8 code2, u8 code3, const char *prefix)
|
|
{
|
|
/* 7.10.8 */
|
|
if (!(code1 == 0xFFFF && code2 == 0xFF && code3 == 0xFF))
|
|
printf("%sBus Address: %04x:%02x:%02x.%x\n",
|
|
prefix, code1, code2, code3 >> 3, code3 & 0x7);
|
|
}
|
|
|
|
/*
|
|
* 7.11 On Board Devices Information (Type 10)
|
|
*/
|
|
|
|
static const char *dmi_on_board_devices_type(u8 code)
|
|
{
|
|
/* 7.11.1 and 7.42.2 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Video",
|
|
"SCSI Controller",
|
|
"Ethernet",
|
|
"Token Ring",
|
|
"Sound",
|
|
"PATA Controller",
|
|
"SATA Controller",
|
|
"SAS Controller" /* 0x0A */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0A)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_on_board_devices(const struct dmi_header *h, const char *prefix)
|
|
{
|
|
u8 *p = h->data + 4;
|
|
u8 count = (h->length - 0x04) / 2;
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (count == 1)
|
|
printf("%sOn Board Device Information\n",
|
|
prefix);
|
|
else
|
|
printf("%sOn Board Device %d Information\n",
|
|
prefix, i + 1);
|
|
printf("%s\tType: %s\n",
|
|
prefix, dmi_on_board_devices_type(p[2 * i] & 0x7F));
|
|
printf("%s\tStatus: %s\n",
|
|
prefix, p[2 * i] & 0x80 ? "Enabled" : "Disabled");
|
|
printf("%s\tDescription: %s\n",
|
|
prefix, dmi_string(h, p[2 * i + 1]));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 7.12 OEM Strings (Type 11)
|
|
*/
|
|
|
|
static void dmi_oem_strings(const struct dmi_header *h, const char *prefix)
|
|
{
|
|
u8 *p = h->data + 4;
|
|
u8 count = p[0x00];
|
|
int i;
|
|
|
|
for (i = 1; i <= count; i++)
|
|
printf("%sString %d: %s\n",
|
|
prefix, i, dmi_string(h, i));
|
|
}
|
|
|
|
/*
|
|
* 7.13 System Configuration Options (Type 12)
|
|
*/
|
|
|
|
static void dmi_system_configuration_options(const struct dmi_header *h, const char *prefix)
|
|
{
|
|
u8 *p = h->data + 4;
|
|
u8 count = p[0x00];
|
|
int i;
|
|
|
|
for (i = 1; i <= count; i++)
|
|
printf("%sOption %d: %s\n",
|
|
prefix, i, dmi_string(h, i));
|
|
}
|
|
|
|
/*
|
|
* 7.14 BIOS Language Information (Type 13)
|
|
*/
|
|
|
|
static void dmi_bios_languages(const struct dmi_header *h, const char *prefix)
|
|
{
|
|
u8 *p = h->data + 4;
|
|
u8 count = p[0x00];
|
|
int i;
|
|
|
|
for (i = 1; i <= count; i++)
|
|
printf("%s%s\n",
|
|
prefix, dmi_string(h, i));
|
|
}
|
|
|
|
static const char *dmi_bios_language_format(u8 code)
|
|
{
|
|
if (code & 0x01)
|
|
return "Abbreviated";
|
|
else
|
|
return "Long";
|
|
}
|
|
|
|
/*
|
|
* 7.15 Group Associations (Type 14)
|
|
*/
|
|
|
|
static void dmi_group_associations_items(u8 count, const u8 *p, const char *prefix)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
printf("%s0x%04X (%s)\n",
|
|
prefix, WORD(p + 3 * i + 1),
|
|
dmi_smbios_structure_type(p[3 * i]));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 7.16 System Event Log (Type 15)
|
|
*/
|
|
|
|
static const char *dmi_event_log_method(u8 code)
|
|
{
|
|
static const char *method[] = {
|
|
"Indexed I/O, one 8-bit index port, one 8-bit data port", /* 0x00 */
|
|
"Indexed I/O, two 8-bit index ports, one 8-bit data port",
|
|
"Indexed I/O, one 16-bit index port, one 8-bit data port",
|
|
"Memory-mapped physical 32-bit address",
|
|
"General-purpose non-volatile data functions" /* 0x04 */
|
|
};
|
|
|
|
if (code <= 0x04)
|
|
return method[code];
|
|
if (code >= 0x80)
|
|
return "OEM-specific";
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_event_log_status(u8 code)
|
|
{
|
|
static const char *valid[] = {
|
|
"Invalid", /* 0 */
|
|
"Valid" /* 1 */
|
|
};
|
|
static const char *full[] = {
|
|
"Not Full", /* 0 */
|
|
"Full" /* 1 */
|
|
};
|
|
|
|
printf(" %s, %s",
|
|
valid[(code >> 0) & 1], full[(code >> 1) & 1]);
|
|
}
|
|
|
|
static void dmi_event_log_address(u8 method, const u8 *p)
|
|
{
|
|
/* 7.16.3 */
|
|
switch (method)
|
|
{
|
|
case 0x00:
|
|
case 0x01:
|
|
case 0x02:
|
|
printf(" Index 0x%04X, Data 0x%04X", WORD(p), WORD(p + 2));
|
|
break;
|
|
case 0x03:
|
|
printf(" 0x%08X", DWORD(p));
|
|
break;
|
|
case 0x04:
|
|
printf(" 0x%04X", WORD(p));
|
|
break;
|
|
default:
|
|
printf(" Unknown");
|
|
}
|
|
}
|
|
|
|
static const char *dmi_event_log_header_type(u8 code)
|
|
{
|
|
static const char *type[] = {
|
|
"No Header", /* 0x00 */
|
|
"Type 1" /* 0x01 */
|
|
};
|
|
|
|
if (code <= 0x01)
|
|
return type[code];
|
|
if (code >= 0x80)
|
|
return "OEM-specific";
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_event_log_descriptor_type(u8 code)
|
|
{
|
|
/* 7.16.6.1 */
|
|
static const char *type[] = {
|
|
NULL, /* 0x00 */
|
|
"Single-bit ECC memory error",
|
|
"Multi-bit ECC memory error",
|
|
"Parity memory error",
|
|
"Bus timeout",
|
|
"I/O channel block",
|
|
"Software NMI",
|
|
"POST memory resize",
|
|
"POST error",
|
|
"PCI parity error",
|
|
"PCI system error",
|
|
"CPU failure",
|
|
"EISA failsafe timer timeout",
|
|
"Correctable memory log disabled",
|
|
"Logging disabled",
|
|
NULL, /* 0x0F */
|
|
"System limit exceeded",
|
|
"Asynchronous hardware timer expired",
|
|
"System configuration information",
|
|
"Hard disk information",
|
|
"System reconfigured",
|
|
"Uncorrectable CPU-complex error",
|
|
"Log area reset/cleared",
|
|
"System boot" /* 0x17 */
|
|
};
|
|
|
|
if (code <= 0x17 && type[code] != NULL)
|
|
return type[code];
|
|
if (code >= 0x80 && code <= 0xFE)
|
|
return "OEM-specific";
|
|
if (code == 0xFF)
|
|
return "End of log";
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_event_log_descriptor_format(u8 code)
|
|
{
|
|
/* 7.16.6.2 */
|
|
static const char *format[] = {
|
|
"None", /* 0x00 */
|
|
"Handle",
|
|
"Multiple-event",
|
|
"Multiple-event handle",
|
|
"POST results bitmap",
|
|
"System management",
|
|
"Multiple-event system management" /* 0x06 */
|
|
};
|
|
|
|
if (code <= 0x06)
|
|
return format[code];
|
|
if (code >= 0x80)
|
|
return "OEM-specific";
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_event_log_descriptors(u8 count, u8 len, const u8 *p, const char *prefix)
|
|
{
|
|
/* 7.16.1 */
|
|
int i;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (len >= 0x02)
|
|
{
|
|
printf("%sDescriptor %u: %s\n",
|
|
prefix, i + 1, dmi_event_log_descriptor_type(p[i * len]));
|
|
printf("%sData Format %u: %s\n",
|
|
prefix, i + 1, dmi_event_log_descriptor_format(p[i * len + 1]));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 7.17 Physical Memory Array (Type 16)
|
|
*/
|
|
|
|
static const char *dmi_memory_array_location(u8 code)
|
|
{
|
|
/* 7.17.1 */
|
|
static const char *location[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"System Board Or Motherboard",
|
|
"ISA Add-on Card",
|
|
"EISA Add-on Card",
|
|
"PCI Add-on Card",
|
|
"MCA Add-on Card",
|
|
"PCMCIA Add-on Card",
|
|
"Proprietary Add-on Card",
|
|
"NuBus" /* 0x0A */
|
|
};
|
|
static const char *location_0xA0[] = {
|
|
"PC-98/C20 Add-on Card", /* 0xA0 */
|
|
"PC-98/C24 Add-on Card",
|
|
"PC-98/E Add-on Card",
|
|
"PC-98/Local Bus Add-on Card" /* 0xA3 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0A)
|
|
return location[code - 0x01];
|
|
if (code >= 0xA0 && code <= 0xA3)
|
|
return location_0xA0[code - 0xA0];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_memory_array_use(u8 code)
|
|
{
|
|
/* 7.17.2 */
|
|
static const char *use[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"System Memory",
|
|
"Video Memory",
|
|
"Flash Memory",
|
|
"Non-volatile RAM",
|
|
"Cache Memory" /* 0x07 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x07)
|
|
return use[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_memory_array_ec_type(u8 code)
|
|
{
|
|
/* 7.17.3 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"None",
|
|
"Parity",
|
|
"Single-bit ECC",
|
|
"Multi-bit ECC",
|
|
"CRC" /* 0x07 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x07)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_memory_array_error_handle(u16 code)
|
|
{
|
|
if (code == 0xFFFE)
|
|
printf(" Not Provided");
|
|
else if (code == 0xFFFF)
|
|
printf(" No Error");
|
|
else
|
|
printf(" 0x%04X", code);
|
|
}
|
|
|
|
/*
|
|
* 7.18 Memory Device (Type 17)
|
|
*/
|
|
|
|
static void dmi_memory_device_width(u16 code)
|
|
{
|
|
/*
|
|
* If no memory module is present, width may be 0
|
|
*/
|
|
if (code == 0xFFFF || code == 0)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u bits", code);
|
|
}
|
|
|
|
static void dmi_memory_device_size(u16 code)
|
|
{
|
|
if (code == 0)
|
|
printf(" No Module Installed");
|
|
else if (code == 0xFFFF)
|
|
printf(" Unknown");
|
|
else
|
|
{
|
|
if (code & 0x8000)
|
|
printf(" %u kB", code & 0x7FFF);
|
|
else
|
|
printf(" %u MB", code);
|
|
}
|
|
}
|
|
|
|
static void dmi_memory_device_extended_size(u32 code)
|
|
{
|
|
code &= 0x7FFFFFFFUL;
|
|
|
|
/*
|
|
* Use the greatest unit for which the exact value can be displayed
|
|
* as an integer without rounding
|
|
*/
|
|
if (code & 0x3FFUL)
|
|
printf(" %lu MB", (unsigned long)code);
|
|
else if (code & 0xFFC00UL)
|
|
printf(" %lu GB", (unsigned long)code >> 10);
|
|
else
|
|
printf(" %lu TB", (unsigned long)code >> 20);
|
|
}
|
|
|
|
static void dmi_memory_voltage_value(u16 code)
|
|
{
|
|
if (code == 0)
|
|
printf(" Unknown");
|
|
else
|
|
printf(code % 100 ? " %g V" : " %.1f V", (float)code / 1000);
|
|
}
|
|
|
|
static const char *dmi_memory_device_form_factor(u8 code)
|
|
{
|
|
/* 7.18.1 */
|
|
static const char *form_factor[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"SIMM",
|
|
"SIP",
|
|
"Chip",
|
|
"DIP",
|
|
"ZIP",
|
|
"Proprietary Card",
|
|
"DIMM",
|
|
"TSOP",
|
|
"Row Of Chips",
|
|
"RIMM",
|
|
"SODIMM",
|
|
"SRIMM",
|
|
"FB-DIMM" /* 0x0F */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0F)
|
|
return form_factor[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_memory_device_set(u8 code)
|
|
{
|
|
if (code == 0)
|
|
printf(" None");
|
|
else if (code == 0xFF)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u", code);
|
|
}
|
|
|
|
static const char *dmi_memory_device_type(u8 code)
|
|
{
|
|
/* 7.18.2 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"DRAM",
|
|
"EDRAM",
|
|
"VRAM",
|
|
"SRAM",
|
|
"RAM",
|
|
"ROM",
|
|
"Flash",
|
|
"EEPROM",
|
|
"FEPROM",
|
|
"EPROM",
|
|
"CDRAM",
|
|
"3DRAM",
|
|
"SDRAM",
|
|
"SGRAM",
|
|
"RDRAM",
|
|
"DDR",
|
|
"DDR2",
|
|
"DDR2 FB-DIMM",
|
|
"Reserved",
|
|
"Reserved",
|
|
"Reserved",
|
|
"DDR3",
|
|
"FBD2",
|
|
"DDR4",
|
|
"LPDDR",
|
|
"LPDDR2",
|
|
"LPDDR3",
|
|
"LPDDR4" /* 0x1E */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x1E)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_memory_device_type_detail(u16 code)
|
|
{
|
|
/* 7.18.3 */
|
|
static const char *detail[] = {
|
|
"Other", /* 1 */
|
|
"Unknown",
|
|
"Fast-paged",
|
|
"Static Column",
|
|
"Pseudo-static",
|
|
"RAMBus",
|
|
"Synchronous",
|
|
"CMOS",
|
|
"EDO",
|
|
"Window DRAM",
|
|
"Cache DRAM",
|
|
"Non-Volatile",
|
|
"Registered (Buffered)",
|
|
"Unbuffered (Unregistered)",
|
|
"LRDIMM" /* 15 */
|
|
};
|
|
|
|
if ((code & 0xFFFE) == 0)
|
|
printf(" None");
|
|
else
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i <= 15; i++)
|
|
if (code & (1 << i))
|
|
printf(" %s", detail[i - 1]);
|
|
}
|
|
}
|
|
|
|
static void dmi_memory_device_speed(u16 code)
|
|
{
|
|
if (code == 0)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u MT/s", code);
|
|
}
|
|
|
|
/*
|
|
* 7.19 32-bit Memory Error Information (Type 18)
|
|
*/
|
|
|
|
static const char *dmi_memory_error_type(u8 code)
|
|
{
|
|
/* 7.19.1 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"OK",
|
|
"Bad Read",
|
|
"Parity Error",
|
|
"Single-bit Error",
|
|
"Double-bit Error",
|
|
"Multi-bit Error",
|
|
"Nibble Error",
|
|
"Checksum Error",
|
|
"CRC Error",
|
|
"Corrected Single-bit Error",
|
|
"Corrected Error",
|
|
"Uncorrectable Error" /* 0x0E */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0E)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_memory_error_granularity(u8 code)
|
|
{
|
|
/* 7.19.2 */
|
|
static const char *granularity[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Device Level",
|
|
"Memory Partition Level" /* 0x04 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x04)
|
|
return granularity[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_memory_error_operation(u8 code)
|
|
{
|
|
/* 7.19.3 */
|
|
static const char *operation[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Read",
|
|
"Write",
|
|
"Partial Write" /* 0x05 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x05)
|
|
return operation[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_memory_error_syndrome(u32 code)
|
|
{
|
|
if (code == 0x00000000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" 0x%08X", code);
|
|
}
|
|
|
|
static void dmi_32bit_memory_error_address(u32 code)
|
|
{
|
|
if (code == 0x80000000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" 0x%08X", code);
|
|
}
|
|
|
|
/*
|
|
* 7.20 Memory Array Mapped Address (Type 19)
|
|
*/
|
|
|
|
static void dmi_mapped_address_size(u32 code)
|
|
{
|
|
if (code == 0)
|
|
printf(" Invalid");
|
|
else
|
|
{
|
|
u64 size;
|
|
|
|
size.h = 0;
|
|
size.l = code;
|
|
dmi_print_memory_size(size, 1);
|
|
}
|
|
}
|
|
|
|
static void dmi_mapped_address_extended_size(u64 start, u64 end)
|
|
{
|
|
if (start.h == end.h && start.l == end.l)
|
|
printf(" Invalid");
|
|
else
|
|
dmi_print_memory_size(u64_range(start, end), 0);
|
|
}
|
|
|
|
/*
|
|
* 7.21 Memory Device Mapped Address (Type 20)
|
|
*/
|
|
|
|
static void dmi_mapped_address_row_position(u8 code)
|
|
{
|
|
if (code == 0)
|
|
printf(" %s", out_of_spec);
|
|
else if (code == 0xFF)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u", code);
|
|
}
|
|
|
|
static void dmi_mapped_address_interleave_position(u8 code, const char *prefix)
|
|
{
|
|
if (code != 0)
|
|
{
|
|
printf("%sInterleave Position:", prefix);
|
|
if (code == 0xFF)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u", code);
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
static void dmi_mapped_address_interleaved_data_depth(u8 code, const char *prefix)
|
|
{
|
|
if (code != 0)
|
|
{
|
|
printf("%sInterleaved Data Depth:", prefix);
|
|
if (code == 0xFF)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u", code);
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 7.22 Built-in Pointing Device (Type 21)
|
|
*/
|
|
|
|
static const char *dmi_pointing_device_type(u8 code)
|
|
{
|
|
/* 7.22.1 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Mouse",
|
|
"Track Ball",
|
|
"Track Point",
|
|
"Glide Point",
|
|
"Touch Pad",
|
|
"Touch Screen",
|
|
"Optical Sensor" /* 0x09 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x09)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_pointing_device_interface(u8 code)
|
|
{
|
|
/* 7.22.2 */
|
|
static const char *interface[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Serial",
|
|
"PS/2",
|
|
"Infrared",
|
|
"HIP-HIL",
|
|
"Bus Mouse",
|
|
"ADB (Apple Desktop Bus)" /* 0x08 */
|
|
};
|
|
static const char *interface_0xA0[] = {
|
|
"Bus Mouse DB-9", /* 0xA0 */
|
|
"Bus Mouse Micro DIN",
|
|
"USB" /* 0xA2 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x08)
|
|
return interface[code - 0x01];
|
|
if (code >= 0xA0 && code <= 0xA2)
|
|
return interface_0xA0[code - 0xA0];
|
|
return out_of_spec;
|
|
}
|
|
|
|
/*
|
|
* 7.23 Portable Battery (Type 22)
|
|
*/
|
|
|
|
static const char *dmi_battery_chemistry(u8 code)
|
|
{
|
|
/* 7.23.1 */
|
|
static const char *chemistry[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Lead Acid",
|
|
"Nickel Cadmium",
|
|
"Nickel Metal Hydride",
|
|
"Lithium Ion",
|
|
"Zinc Air",
|
|
"Lithium Polymer" /* 0x08 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x08)
|
|
return chemistry[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_battery_capacity(u16 code, u8 multiplier)
|
|
{
|
|
if (code == 0)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u mWh", code * multiplier);
|
|
}
|
|
|
|
static void dmi_battery_voltage(u16 code)
|
|
{
|
|
if (code == 0)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u mV", code);
|
|
}
|
|
|
|
static void dmi_battery_maximum_error(u8 code)
|
|
{
|
|
if (code == 0xFF)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u%%", code);
|
|
}
|
|
|
|
/*
|
|
* 7.24 System Reset (Type 23)
|
|
*/
|
|
|
|
/* code is assumed to be a 2-bit value */
|
|
static const char *dmi_system_reset_boot_option(u8 code)
|
|
{
|
|
static const char *option[] = {
|
|
out_of_spec, /* 0x0 */
|
|
"Operating System", /* 0x1 */
|
|
"System Utilities",
|
|
"Do Not Reboot" /* 0x3 */
|
|
};
|
|
|
|
return option[code];
|
|
}
|
|
|
|
static void dmi_system_reset_count(u16 code)
|
|
{
|
|
if (code == 0xFFFF)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u", code);
|
|
}
|
|
|
|
static void dmi_system_reset_timer(u16 code)
|
|
{
|
|
if (code == 0xFFFF)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u min", code);
|
|
}
|
|
|
|
/*
|
|
* 7.25 Hardware Security (Type 24)
|
|
*/
|
|
|
|
static const char *dmi_hardware_security_status(u8 code)
|
|
{
|
|
static const char *status[] = {
|
|
"Disabled", /* 0x00 */
|
|
"Enabled",
|
|
"Not Implemented",
|
|
"Unknown" /* 0x03 */
|
|
};
|
|
|
|
return status[code];
|
|
}
|
|
|
|
/*
|
|
* 7.26 System Power Controls (Type 25)
|
|
*/
|
|
|
|
static void dmi_power_controls_power_on(const u8 *p)
|
|
{
|
|
/* 7.26.1 */
|
|
if (dmi_bcd_range(p[0], 0x01, 0x12))
|
|
printf(" %02X", p[0]);
|
|
else
|
|
printf(" *");
|
|
if (dmi_bcd_range(p[1], 0x01, 0x31))
|
|
printf("-%02X", p[1]);
|
|
else
|
|
printf("-*");
|
|
if (dmi_bcd_range(p[2], 0x00, 0x23))
|
|
printf(" %02X", p[2]);
|
|
else
|
|
printf(" *");
|
|
if (dmi_bcd_range(p[3], 0x00, 0x59))
|
|
printf(":%02X", p[3]);
|
|
else
|
|
printf(":*");
|
|
if (dmi_bcd_range(p[4], 0x00, 0x59))
|
|
printf(":%02X", p[4]);
|
|
else
|
|
printf(":*");
|
|
}
|
|
|
|
/*
|
|
* 7.27 Voltage Probe (Type 26)
|
|
*/
|
|
|
|
static const char *dmi_voltage_probe_location(u8 code)
|
|
{
|
|
/* 7.27.1 */
|
|
static const char *location[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Processor",
|
|
"Disk",
|
|
"Peripheral Bay",
|
|
"System Management Module",
|
|
"Motherboard",
|
|
"Memory Module",
|
|
"Processor Module",
|
|
"Power Unit",
|
|
"Add-in Card" /* 0x0B */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0B)
|
|
return location[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_probe_status(u8 code)
|
|
{
|
|
/* 7.27.1 */
|
|
static const char *status[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"OK",
|
|
"Non-critical",
|
|
"Critical",
|
|
"Non-recoverable" /* 0x06 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x06)
|
|
return status[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_voltage_probe_value(u16 code)
|
|
{
|
|
if (code == 0x8000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %.3f V", (float)(i16)code / 1000);
|
|
}
|
|
|
|
static void dmi_voltage_probe_resolution(u16 code)
|
|
{
|
|
if (code == 0x8000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %.1f mV", (float)code / 10);
|
|
}
|
|
|
|
static void dmi_probe_accuracy(u16 code)
|
|
{
|
|
if (code == 0x8000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %.2f%%", (float)code / 100);
|
|
}
|
|
|
|
/*
|
|
* 7.28 Cooling Device (Type 27)
|
|
*/
|
|
|
|
static const char *dmi_cooling_device_type(u8 code)
|
|
{
|
|
/* 7.28.1 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Fan",
|
|
"Centrifugal Blower",
|
|
"Chip Fan",
|
|
"Cabinet Fan",
|
|
"Power Supply Fan",
|
|
"Heat Pipe",
|
|
"Integrated Refrigeration" /* 0x09 */
|
|
};
|
|
static const char *type_0x10[] = {
|
|
"Active Cooling", /* 0x10 */
|
|
"Passive Cooling" /* 0x11 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x09)
|
|
return type[code - 0x01];
|
|
if (code >= 0x10 && code <= 0x11)
|
|
return type_0x10[code - 0x10];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_cooling_device_speed(u16 code)
|
|
{
|
|
if (code == 0x8000)
|
|
printf(" Unknown Or Non-rotating");
|
|
else
|
|
printf(" %u rpm", code);
|
|
}
|
|
|
|
/*
|
|
* 7.29 Temperature Probe (Type 28)
|
|
*/
|
|
|
|
static const char *dmi_temperature_probe_location(u8 code)
|
|
{
|
|
/* 7.29.1 */
|
|
static const char *location[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Processor",
|
|
"Disk",
|
|
"Peripheral Bay",
|
|
"System Management Module",
|
|
"Motherboard",
|
|
"Memory Module",
|
|
"Processor Module",
|
|
"Power Unit",
|
|
"Add-in Card",
|
|
"Front Panel Board",
|
|
"Back Panel Board",
|
|
"Power System Board",
|
|
"Drive Back Plane" /* 0x0F */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0F)
|
|
return location[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_temperature_probe_value(u16 code)
|
|
{
|
|
if (code == 0x8000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %.1f deg C", (float)(i16)code / 10);
|
|
}
|
|
|
|
static void dmi_temperature_probe_resolution(u16 code)
|
|
{
|
|
if (code == 0x8000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %.3f deg C", (float)code / 1000);
|
|
}
|
|
|
|
/*
|
|
* 7.30 Electrical Current Probe (Type 29)
|
|
*/
|
|
|
|
static void dmi_current_probe_value(u16 code)
|
|
{
|
|
if (code == 0x8000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %.3f A", (float)(i16)code / 1000);
|
|
}
|
|
|
|
static void dmi_current_probe_resolution(u16 code)
|
|
{
|
|
if (code == 0x8000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %.1f mA", (float)code / 10);
|
|
}
|
|
|
|
/*
|
|
* 7.33 System Boot Information (Type 32)
|
|
*/
|
|
|
|
static const char *dmi_system_boot_status(u8 code)
|
|
{
|
|
static const char *status[] = {
|
|
"No errors detected", /* 0 */
|
|
"No bootable media",
|
|
"Operating system failed to load",
|
|
"Firmware-detected hardware failure",
|
|
"Operating system-detected hardware failure",
|
|
"User-requested boot",
|
|
"System security violation",
|
|
"Previously-requested image",
|
|
"System watchdog timer expired" /* 8 */
|
|
};
|
|
|
|
if (code <= 8)
|
|
return status[code];
|
|
if (code >= 128 && code <= 191)
|
|
return "OEM-specific";
|
|
if (code >= 192)
|
|
return "Product-specific";
|
|
return out_of_spec;
|
|
}
|
|
|
|
/*
|
|
* 7.34 64-bit Memory Error Information (Type 33)
|
|
*/
|
|
|
|
static void dmi_64bit_memory_error_address(u64 code)
|
|
{
|
|
if (code.h == 0x80000000 && code.l == 0x00000000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" 0x%08X%08X", code.h, code.l);
|
|
}
|
|
|
|
/*
|
|
* 7.35 Management Device (Type 34)
|
|
*/
|
|
|
|
/*
|
|
* Several boards have a bug where some type 34 structures have their
|
|
* length incorrectly set to 0x10 instead of 0x0B. This causes the
|
|
* first 5 characters of the device name to be trimmed. It's easy to
|
|
* check and fix, so do it, but warn.
|
|
*/
|
|
static void dmi_fixup_type_34(struct dmi_header *h, int display)
|
|
{
|
|
u8 *p = h->data;
|
|
|
|
/* Make sure the hidden data is ASCII only */
|
|
if (h->length == 0x10
|
|
&& is_printable(p + 0x0B, 0x10 - 0x0B))
|
|
{
|
|
if (!(opt.flags & FLAG_QUIET) && display)
|
|
fprintf(stderr,
|
|
"Invalid entry length (%u). Fixed up to %u.\n",
|
|
0x10, 0x0B);
|
|
h->length = 0x0B;
|
|
}
|
|
}
|
|
|
|
static const char *dmi_management_device_type(u8 code)
|
|
{
|
|
/* 7.35.1 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"LM75",
|
|
"LM78",
|
|
"LM79",
|
|
"LM80",
|
|
"LM81",
|
|
"ADM9240",
|
|
"DS1780",
|
|
"MAX1617",
|
|
"GL518SM",
|
|
"W83781D",
|
|
"HT82H791" /* 0x0D */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x0D)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_management_device_address_type(u8 code)
|
|
{
|
|
/* 7.35.2 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"I/O Port",
|
|
"Memory",
|
|
"SMBus" /* 0x05 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x05)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
/*
|
|
* 7.38 Memory Channel (Type 37)
|
|
*/
|
|
|
|
static const char *dmi_memory_channel_type(u8 code)
|
|
{
|
|
/* 7.38.1 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"RamBus",
|
|
"SyncLink" /* 0x04 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x04)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_memory_channel_devices(u8 count, const u8 *p, const char *prefix)
|
|
{
|
|
int i;
|
|
|
|
for (i = 1; i <= count; i++)
|
|
{
|
|
printf("%sDevice %u Load: %u\n",
|
|
prefix, i, p[3 * i]);
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("%sDevice %u Handle: 0x%04X\n",
|
|
prefix, i, WORD(p + 3 * i + 1));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 7.39 IPMI Device Information (Type 38)
|
|
*/
|
|
|
|
static const char *dmi_ipmi_interface_type(u8 code)
|
|
{
|
|
/* 7.39.1 and IPMI 2.0, appendix C1, table C1-2 */
|
|
static const char *type[] = {
|
|
"Unknown", /* 0x00 */
|
|
"KCS (Keyboard Control Style)",
|
|
"SMIC (Server Management Interface Chip)",
|
|
"BT (Block Transfer)",
|
|
"SSIF (SMBus System Interface)" /* 0x04 */
|
|
};
|
|
|
|
if (code <= 0x04)
|
|
return type[code];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static void dmi_ipmi_base_address(u8 type, const u8 *p, u8 lsb)
|
|
{
|
|
if (type == 0x04) /* SSIF */
|
|
{
|
|
printf("0x%02X (SMBus)", (*p) >> 1);
|
|
}
|
|
else
|
|
{
|
|
u64 address = QWORD(p);
|
|
printf("0x%08X%08X (%s)", address.h, (address.l & ~1) | lsb,
|
|
address.l & 1 ? "I/O" : "Memory-mapped");
|
|
}
|
|
}
|
|
|
|
/* code is assumed to be a 2-bit value */
|
|
static const char *dmi_ipmi_register_spacing(u8 code)
|
|
{
|
|
/* IPMI 2.0, appendix C1, table C1-1 */
|
|
static const char *spacing[] = {
|
|
"Successive Byte Boundaries", /* 0x00 */
|
|
"32-bit Boundaries",
|
|
"16-byte Boundaries", /* 0x02 */
|
|
out_of_spec /* 0x03 */
|
|
};
|
|
|
|
return spacing[code];
|
|
}
|
|
|
|
/*
|
|
* 7.40 System Power Supply (Type 39)
|
|
*/
|
|
|
|
static void dmi_power_supply_power(u16 code)
|
|
{
|
|
if (code == 0x8000)
|
|
printf(" Unknown");
|
|
else
|
|
printf(" %u W", (unsigned int)code);
|
|
}
|
|
|
|
static const char *dmi_power_supply_type(u8 code)
|
|
{
|
|
/* 7.40.1 */
|
|
static const char *type[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Linear",
|
|
"Switching",
|
|
"Battery",
|
|
"UPS",
|
|
"Converter",
|
|
"Regulator" /* 0x08 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x08)
|
|
return type[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_power_supply_status(u8 code)
|
|
{
|
|
/* 7.40.1 */
|
|
static const char *status[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"OK",
|
|
"Non-critical",
|
|
"Critical" /* 0x05 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x05)
|
|
return status[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
static const char *dmi_power_supply_range_switching(u8 code)
|
|
{
|
|
/* 7.40.1 */
|
|
static const char *switching[] = {
|
|
"Other", /* 0x01 */
|
|
"Unknown",
|
|
"Manual",
|
|
"Auto-switch",
|
|
"Wide Range",
|
|
"N/A" /* 0x06 */
|
|
};
|
|
|
|
if (code >= 0x01 && code <= 0x06)
|
|
return switching[code - 0x01];
|
|
return out_of_spec;
|
|
}
|
|
|
|
/*
|
|
* 7.41 Additional Information (Type 40)
|
|
*
|
|
* Proper support of this entry type would require redesigning a large part of
|
|
* the code, so I am waiting to see actual implementations of it to decide
|
|
* whether it's worth the effort.
|
|
*/
|
|
|
|
static void dmi_additional_info(const struct dmi_header *h, const char *prefix)
|
|
{
|
|
u8 *p = h->data + 4;
|
|
u8 count = *p++;
|
|
u8 length;
|
|
int i, offset = 5;
|
|
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
printf("%sAdditional Information %d\n", prefix, i + 1);
|
|
|
|
/* Check for short entries */
|
|
if (h->length < offset + 1) break;
|
|
length = p[0x00];
|
|
if (length < 0x05 || h->length < offset + length) break;
|
|
|
|
printf("%s\tReferenced Handle: 0x%04x\n",
|
|
prefix, WORD(p + 0x01));
|
|
printf("%s\tReferenced Offset: 0x%02x\n",
|
|
prefix, p[0x03]);
|
|
printf("%s\tString: %s\n",
|
|
prefix, dmi_string(h, p[0x04]));
|
|
|
|
printf("%s\tValue: ", prefix);
|
|
switch (length - 0x05)
|
|
{
|
|
case 1:
|
|
printf("0x%02x", p[0x05]);
|
|
break;
|
|
case 2:
|
|
printf("0x%04x", WORD(p + 0x05));
|
|
break;
|
|
case 4:
|
|
printf("0x%08x", DWORD(p + 0x05));
|
|
break;
|
|
default:
|
|
printf("Unexpected size");
|
|
break;
|
|
}
|
|
printf("\n");
|
|
|
|
p += length;
|
|
offset += length;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 7.43 Management Controller Host Interface (Type 42)
|
|
*/
|
|
|
|
static const char *dmi_management_controller_host_type(u8 code)
|
|
{
|
|
/* DMTF DSP0239 (MCTP) version 1.1.0 */
|
|
static const char *type[] = {
|
|
"KCS: Keyboard Controller Style", /* 0x02 */
|
|
"8250 UART Register Compatible",
|
|
"16450 UART Register Compatible",
|
|
"16550/16550A UART Register Compatible",
|
|
"16650/16650A UART Register Compatible",
|
|
"16750/16750A UART Register Compatible",
|
|
"16850/16850A UART Register Compatible" /* 0x08 */
|
|
};
|
|
|
|
if (code >= 0x02 && code <= 0x08)
|
|
return type[code - 0x02];
|
|
if (code == 0xF0)
|
|
return "OEM";
|
|
return out_of_spec;
|
|
}
|
|
|
|
/*
|
|
* 7.44 TPM Device (Type 43)
|
|
*/
|
|
|
|
static void dmi_tpm_vendor_id(const u8 *p)
|
|
{
|
|
char vendor_id[5];
|
|
int i;
|
|
|
|
/* ASCII filtering */
|
|
for (i = 0; i < 4 && p[i] != 0; i++)
|
|
{
|
|
if (p[i] < 32 || p[i] >= 127)
|
|
vendor_id[i] = '.';
|
|
else
|
|
vendor_id[i] = p[i];
|
|
}
|
|
|
|
/* Terminate the string */
|
|
vendor_id[i] = '\0';
|
|
|
|
printf(" %s", vendor_id);
|
|
}
|
|
|
|
static void dmi_tpm_characteristics(u64 code, const char *prefix)
|
|
{
|
|
/* 7.1.1 */
|
|
static const char *characteristics[] = {
|
|
"TPM Device characteristics not supported", /* 2 */
|
|
"Family configurable via firmware update",
|
|
"Family configurable via platform software support",
|
|
"Family configurable via OEM proprietary mechanism" /* 5 */
|
|
};
|
|
int i;
|
|
|
|
/*
|
|
* This isn't very clear what this bit is supposed to mean
|
|
*/
|
|
if (code.l & (1 << 2))
|
|
{
|
|
printf("%s%s\n",
|
|
prefix, characteristics[0]);
|
|
return;
|
|
}
|
|
|
|
for (i = 3; i <= 5; i++)
|
|
if (code.l & (1 << i))
|
|
printf("%s%s\n",
|
|
prefix, characteristics[i - 2]);
|
|
}
|
|
|
|
/*
|
|
* Main
|
|
*/
|
|
|
|
static void dmi_decode(const struct dmi_header *h, u16 ver)
|
|
{
|
|
const u8 *data = h->data;
|
|
|
|
/*
|
|
* Note: DMI types 37 and 42 are untested
|
|
*/
|
|
switch (h->type)
|
|
{
|
|
case 0: /* 7.1 BIOS Information */
|
|
printf("BIOS Information\n");
|
|
if (h->length < 0x12) break;
|
|
printf("\tVendor: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tVersion: %s\n",
|
|
dmi_string(h, data[0x05]));
|
|
printf("\tRelease Date: %s\n",
|
|
dmi_string(h, data[0x08]));
|
|
/*
|
|
* On IA-64, the BIOS base address will read 0 because
|
|
* there is no BIOS. Skip the base address and the
|
|
* runtime size in this case.
|
|
*/
|
|
if (WORD(data + 0x06) != 0)
|
|
{
|
|
printf("\tAddress: 0x%04X0\n",
|
|
WORD(data + 0x06));
|
|
printf("\tRuntime Size:");
|
|
dmi_bios_runtime_size((0x10000 - WORD(data + 0x06)) << 4);
|
|
printf("\n");
|
|
}
|
|
printf("\tROM Size:");
|
|
dmi_bios_rom_size(data[0x09], h->length < 0x1A ? 16 : WORD(data + 0x18));
|
|
printf("\n");
|
|
printf("\tCharacteristics:\n");
|
|
dmi_bios_characteristics(QWORD(data + 0x0A), "\t\t");
|
|
if (h->length < 0x13) break;
|
|
dmi_bios_characteristics_x1(data[0x12], "\t\t");
|
|
if (h->length < 0x14) break;
|
|
dmi_bios_characteristics_x2(data[0x13], "\t\t");
|
|
if (h->length < 0x18) break;
|
|
if (data[0x14] != 0xFF && data[0x15] != 0xFF)
|
|
printf("\tBIOS Revision: %u.%u\n",
|
|
data[0x14], data[0x15]);
|
|
if (data[0x16] != 0xFF && data[0x17] != 0xFF)
|
|
printf("\tFirmware Revision: %u.%u\n",
|
|
data[0x16], data[0x17]);
|
|
break;
|
|
|
|
case 1: /* 7.2 System Information */
|
|
printf("System Information\n");
|
|
if (h->length < 0x08) break;
|
|
printf("\tManufacturer: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tProduct Name: %s\n",
|
|
dmi_string(h, data[0x05]));
|
|
printf("\tVersion: %s\n",
|
|
dmi_string(h, data[0x06]));
|
|
printf("\tSerial Number: %s\n",
|
|
dmi_string(h, data[0x07]));
|
|
if (h->length < 0x19) break;
|
|
printf("\tUUID: ");
|
|
dmi_system_uuid(data + 0x08, ver);
|
|
printf("\n");
|
|
printf("\tWake-up Type: %s\n",
|
|
dmi_system_wake_up_type(data[0x18]));
|
|
if (h->length < 0x1B) break;
|
|
printf("\tSKU Number: %s\n",
|
|
dmi_string(h, data[0x19]));
|
|
printf("\tFamily: %s\n",
|
|
dmi_string(h, data[0x1A]));
|
|
break;
|
|
|
|
case 2: /* 7.3 Base Board Information */
|
|
printf("Base Board Information\n");
|
|
if (h->length < 0x08) break;
|
|
printf("\tManufacturer: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tProduct Name: %s\n",
|
|
dmi_string(h, data[0x05]));
|
|
printf("\tVersion: %s\n",
|
|
dmi_string(h, data[0x06]));
|
|
printf("\tSerial Number: %s\n",
|
|
dmi_string(h, data[0x07]));
|
|
if (h->length < 0x09) break;
|
|
printf("\tAsset Tag: %s\n",
|
|
dmi_string(h, data[0x08]));
|
|
if (h->length < 0x0A) break;
|
|
printf("\tFeatures:");
|
|
dmi_base_board_features(data[0x09], "\t\t");
|
|
if (h->length < 0x0E) break;
|
|
printf("\tLocation In Chassis: %s\n",
|
|
dmi_string(h, data[0x0A]));
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("\tChassis Handle: 0x%04X\n",
|
|
WORD(data + 0x0B));
|
|
printf("\tType: %s\n",
|
|
dmi_base_board_type(data[0x0D]));
|
|
if (h->length < 0x0F) break;
|
|
if (h->length < 0x0F + data[0x0E] * sizeof(u16)) break;
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
dmi_base_board_handles(data[0x0E], data + 0x0F, "\t");
|
|
break;
|
|
|
|
case 3: /* 7.4 Chassis Information */
|
|
printf("Chassis Information\n");
|
|
if (h->length < 0x09) break;
|
|
printf("\tManufacturer: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tType: %s\n",
|
|
dmi_chassis_type(data[0x05]));
|
|
printf("\tLock: %s\n",
|
|
dmi_chassis_lock(data[0x05] >> 7));
|
|
printf("\tVersion: %s\n",
|
|
dmi_string(h, data[0x06]));
|
|
printf("\tSerial Number: %s\n",
|
|
dmi_string(h, data[0x07]));
|
|
printf("\tAsset Tag: %s\n",
|
|
dmi_string(h, data[0x08]));
|
|
if (h->length < 0x0D) break;
|
|
printf("\tBoot-up State: %s\n",
|
|
dmi_chassis_state(data[0x09]));
|
|
printf("\tPower Supply State: %s\n",
|
|
dmi_chassis_state(data[0x0A]));
|
|
printf("\tThermal State: %s\n",
|
|
dmi_chassis_state(data[0x0B]));
|
|
printf("\tSecurity Status: %s\n",
|
|
dmi_chassis_security_status(data[0x0C]));
|
|
if (h->length < 0x11) break;
|
|
printf("\tOEM Information: 0x%08X\n",
|
|
DWORD(data + 0x0D));
|
|
if (h->length < 0x13) break;
|
|
printf("\tHeight:");
|
|
dmi_chassis_height(data[0x11]);
|
|
printf("\n");
|
|
printf("\tNumber Of Power Cords:");
|
|
dmi_chassis_power_cords(data[0x12]);
|
|
printf("\n");
|
|
if (h->length < 0x15) break;
|
|
if (h->length < 0x15 + data[0x13] * data[0x14]) break;
|
|
dmi_chassis_elements(data[0x13], data[0x14], data + 0x15, "\t");
|
|
if (h->length < 0x16 + data[0x13] * data[0x14]) break;
|
|
printf("\tSKU Number: %s\n",
|
|
dmi_string(h, data[0x15 + data[0x13] * data[0x14]]));
|
|
break;
|
|
|
|
case 4: /* 7.5 Processor Information */
|
|
printf("Processor Information\n");
|
|
if (h->length < 0x1A) break;
|
|
printf("\tSocket Designation: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tType: %s\n",
|
|
dmi_processor_type(data[0x05]));
|
|
printf("\tFamily: %s\n",
|
|
dmi_processor_family(h, ver));
|
|
printf("\tManufacturer: %s\n",
|
|
dmi_string(h, data[0x07]));
|
|
dmi_processor_id(h, "\t");
|
|
printf("\tVersion: %s\n",
|
|
dmi_string(h, data[0x10]));
|
|
printf("\tVoltage:");
|
|
dmi_processor_voltage(data[0x11]);
|
|
printf("\n");
|
|
printf("\tExternal Clock: ");
|
|
dmi_processor_frequency(data + 0x12);
|
|
printf("\n");
|
|
printf("\tMax Speed: ");
|
|
dmi_processor_frequency(data + 0x14);
|
|
printf("\n");
|
|
printf("\tCurrent Speed: ");
|
|
dmi_processor_frequency(data + 0x16);
|
|
printf("\n");
|
|
if (data[0x18] & (1 << 6))
|
|
printf("\tStatus: Populated, %s\n",
|
|
dmi_processor_status(data[0x18] & 0x07));
|
|
else
|
|
printf("\tStatus: Unpopulated\n");
|
|
printf("\tUpgrade: %s\n",
|
|
dmi_processor_upgrade(data[0x19]));
|
|
if (h->length < 0x20) break;
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
{
|
|
printf("\tL1 Cache Handle:");
|
|
dmi_processor_cache(WORD(data + 0x1A), "L1", ver);
|
|
printf("\n");
|
|
printf("\tL2 Cache Handle:");
|
|
dmi_processor_cache(WORD(data + 0x1C), "L2", ver);
|
|
printf("\n");
|
|
printf("\tL3 Cache Handle:");
|
|
dmi_processor_cache(WORD(data + 0x1E), "L3", ver);
|
|
printf("\n");
|
|
}
|
|
if (h->length < 0x23) break;
|
|
printf("\tSerial Number: %s\n",
|
|
dmi_string(h, data[0x20]));
|
|
printf("\tAsset Tag: %s\n",
|
|
dmi_string(h, data[0x21]));
|
|
printf("\tPart Number: %s\n",
|
|
dmi_string(h, data[0x22]));
|
|
if (h->length < 0x28) break;
|
|
if (data[0x23] != 0)
|
|
printf("\tCore Count: %u\n",
|
|
h->length >= 0x2C && data[0x23] == 0xFF ?
|
|
WORD(data + 0x2A) : data[0x23]);
|
|
if (data[0x24] != 0)
|
|
printf("\tCore Enabled: %u\n",
|
|
h->length >= 0x2E && data[0x24] == 0xFF ?
|
|
WORD(data + 0x2C) : data[0x24]);
|
|
if (data[0x25] != 0)
|
|
printf("\tThread Count: %u\n",
|
|
h->length >= 0x30 && data[0x25] == 0xFF ?
|
|
WORD(data + 0x2E) : data[0x25]);
|
|
printf("\tCharacteristics:");
|
|
dmi_processor_characteristics(WORD(data + 0x26), "\t\t");
|
|
break;
|
|
|
|
case 5: /* 7.6 Memory Controller Information */
|
|
printf("Memory Controller Information\n");
|
|
if (h->length < 0x0F) break;
|
|
printf("\tError Detecting Method: %s\n",
|
|
dmi_memory_controller_ed_method(data[0x04]));
|
|
printf("\tError Correcting Capabilities:");
|
|
dmi_memory_controller_ec_capabilities(data[0x05], "\t\t");
|
|
printf("\tSupported Interleave: %s\n",
|
|
dmi_memory_controller_interleave(data[0x06]));
|
|
printf("\tCurrent Interleave: %s\n",
|
|
dmi_memory_controller_interleave(data[0x07]));
|
|
printf("\tMaximum Memory Module Size: %u MB\n",
|
|
1 << data[0x08]);
|
|
printf("\tMaximum Total Memory Size: %u MB\n",
|
|
data[0x0E] * (1 << data[0x08]));
|
|
printf("\tSupported Speeds:");
|
|
dmi_memory_controller_speeds(WORD(data + 0x09), "\t\t");
|
|
printf("\tSupported Memory Types:");
|
|
dmi_memory_module_types(WORD(data + 0x0B), "\n\t\t");
|
|
printf("\n");
|
|
printf("\tMemory Module Voltage:");
|
|
dmi_processor_voltage(data[0x0D]);
|
|
printf("\n");
|
|
if (h->length < 0x0F + data[0x0E] * sizeof(u16)) break;
|
|
dmi_memory_controller_slots(data[0x0E], data + 0x0F, "\t");
|
|
if (h->length < 0x10 + data[0x0E] * sizeof(u16)) break;
|
|
printf("\tEnabled Error Correcting Capabilities:");
|
|
dmi_memory_controller_ec_capabilities(data[0x0F + data[0x0E] * sizeof(u16)], "\t\t");
|
|
break;
|
|
|
|
case 6: /* 7.7 Memory Module Information */
|
|
printf("Memory Module Information\n");
|
|
if (h->length < 0x0C) break;
|
|
printf("\tSocket Designation: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tBank Connections:");
|
|
dmi_memory_module_connections(data[0x05]);
|
|
printf("\n");
|
|
printf("\tCurrent Speed:");
|
|
dmi_memory_module_speed(data[0x06]);
|
|
printf("\n");
|
|
printf("\tType:");
|
|
dmi_memory_module_types(WORD(data + 0x07), " ");
|
|
printf("\n");
|
|
printf("\tInstalled Size:");
|
|
dmi_memory_module_size(data[0x09]);
|
|
printf("\n");
|
|
printf("\tEnabled Size:");
|
|
dmi_memory_module_size(data[0x0A]);
|
|
printf("\n");
|
|
printf("\tError Status:");
|
|
dmi_memory_module_error(data[0x0B], "\t\t");
|
|
break;
|
|
|
|
case 7: /* 7.8 Cache Information */
|
|
printf("Cache Information\n");
|
|
if (h->length < 0x0F) break;
|
|
printf("\tSocket Designation: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tConfiguration: %s, %s, Level %u\n",
|
|
WORD(data + 0x05) & 0x0080 ? "Enabled" : "Disabled",
|
|
WORD(data + 0x05) & 0x0008 ? "Socketed" : "Not Socketed",
|
|
(WORD(data + 0x05) & 0x0007) + 1);
|
|
printf("\tOperational Mode: %s\n",
|
|
dmi_cache_mode((WORD(data + 0x05) >> 8) & 0x0003));
|
|
printf("\tLocation: %s\n",
|
|
dmi_cache_location((WORD(data + 0x05) >> 5) & 0x0003));
|
|
printf("\tInstalled Size:");
|
|
if (h->length >= 0x1B)
|
|
dmi_cache_size_2(DWORD(data + 0x17));
|
|
else
|
|
dmi_cache_size(WORD(data + 0x09));
|
|
printf("\n");
|
|
printf("\tMaximum Size:");
|
|
if (h->length >= 0x17)
|
|
dmi_cache_size_2(DWORD(data + 0x13));
|
|
else
|
|
dmi_cache_size(WORD(data + 0x07));
|
|
printf("\n");
|
|
printf("\tSupported SRAM Types:");
|
|
dmi_cache_types(WORD(data + 0x0B), "\n\t\t");
|
|
printf("\n");
|
|
printf("\tInstalled SRAM Type:");
|
|
dmi_cache_types(WORD(data + 0x0D), " ");
|
|
printf("\n");
|
|
if (h->length < 0x13) break;
|
|
printf("\tSpeed:");
|
|
dmi_memory_module_speed(data[0x0F]);
|
|
printf("\n");
|
|
printf("\tError Correction Type: %s\n",
|
|
dmi_cache_ec_type(data[0x10]));
|
|
printf("\tSystem Type: %s\n",
|
|
dmi_cache_type(data[0x11]));
|
|
printf("\tAssociativity: %s\n",
|
|
dmi_cache_associativity(data[0x12]));
|
|
break;
|
|
|
|
case 8: /* 7.9 Port Connector Information */
|
|
printf("Port Connector Information\n");
|
|
if (h->length < 0x09) break;
|
|
printf("\tInternal Reference Designator: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tInternal Connector Type: %s\n",
|
|
dmi_port_connector_type(data[0x05]));
|
|
printf("\tExternal Reference Designator: %s\n",
|
|
dmi_string(h, data[0x06]));
|
|
printf("\tExternal Connector Type: %s\n",
|
|
dmi_port_connector_type(data[0x07]));
|
|
printf("\tPort Type: %s\n",
|
|
dmi_port_type(data[0x08]));
|
|
break;
|
|
|
|
case 9: /* 7.10 System Slots */
|
|
printf("System Slot Information\n");
|
|
if (h->length < 0x0C) break;
|
|
printf("\tDesignation: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tType: %s%s\n",
|
|
dmi_slot_bus_width(data[0x06]),
|
|
dmi_slot_type(data[0x05]));
|
|
printf("\tCurrent Usage: %s\n",
|
|
dmi_slot_current_usage(data[0x07]));
|
|
printf("\tLength: %s\n",
|
|
dmi_slot_length(data[0x08]));
|
|
dmi_slot_id(data[0x09], data[0x0A], data[0x05], "\t");
|
|
printf("\tCharacteristics:");
|
|
if (h->length < 0x0D)
|
|
dmi_slot_characteristics(data[0x0B], 0x00, "\t\t");
|
|
else
|
|
dmi_slot_characteristics(data[0x0B], data[0x0C], "\t\t");
|
|
if (h->length < 0x11) break;
|
|
dmi_slot_segment_bus_func(WORD(data + 0x0D), data[0x0F], data[0x10], "\t");
|
|
break;
|
|
|
|
case 10: /* 7.11 On Board Devices Information */
|
|
dmi_on_board_devices(h, "");
|
|
break;
|
|
|
|
case 11: /* 7.12 OEM Strings */
|
|
printf("OEM Strings\n");
|
|
if (h->length < 0x05) break;
|
|
dmi_oem_strings(h, "\t");
|
|
break;
|
|
|
|
case 12: /* 7.13 System Configuration Options */
|
|
printf("System Configuration Options\n");
|
|
if (h->length < 0x05) break;
|
|
dmi_system_configuration_options(h, "\t");
|
|
break;
|
|
|
|
case 13: /* 7.14 BIOS Language Information */
|
|
printf("BIOS Language Information\n");
|
|
if (h->length < 0x16) break;
|
|
if (ver >= 0x0201)
|
|
{
|
|
printf("\tLanguage Description Format: %s\n",
|
|
dmi_bios_language_format(data[0x05]));
|
|
}
|
|
printf("\tInstallable Languages: %u\n", data[0x04]);
|
|
dmi_bios_languages(h, "\t\t");
|
|
printf("\tCurrently Installed Language: %s\n",
|
|
dmi_string(h, data[0x15]));
|
|
break;
|
|
|
|
case 14: /* 7.15 Group Associations */
|
|
printf("Group Associations\n");
|
|
if (h->length < 0x05) break;
|
|
printf("\tName: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tItems: %u\n",
|
|
(h->length - 0x05) / 3);
|
|
dmi_group_associations_items((h->length - 0x05) / 3, data + 0x05, "\t\t");
|
|
break;
|
|
|
|
case 15: /* 7.16 System Event Log */
|
|
printf("System Event Log\n");
|
|
if (h->length < 0x14) break;
|
|
printf("\tArea Length: %u bytes\n",
|
|
WORD(data + 0x04));
|
|
printf("\tHeader Start Offset: 0x%04X\n",
|
|
WORD(data + 0x06));
|
|
if (WORD(data + 0x08) - WORD(data + 0x06))
|
|
printf("\tHeader Length: %u byte%s\n",
|
|
WORD(data + 0x08) - WORD(data + 0x06),
|
|
WORD(data + 0x08) - WORD(data + 0x06) > 1 ? "s" : "");
|
|
printf("\tData Start Offset: 0x%04X\n",
|
|
WORD(data + 0x08));
|
|
printf("\tAccess Method: %s\n",
|
|
dmi_event_log_method(data[0x0A]));
|
|
printf("\tAccess Address:");
|
|
dmi_event_log_address(data[0x0A], data + 0x10);
|
|
printf("\n");
|
|
printf("\tStatus:");
|
|
dmi_event_log_status(data[0x0B]);
|
|
printf("\n");
|
|
printf("\tChange Token: 0x%08X\n",
|
|
DWORD(data + 0x0C));
|
|
if (h->length < 0x17) break;
|
|
printf("\tHeader Format: %s\n",
|
|
dmi_event_log_header_type(data[0x14]));
|
|
printf("\tSupported Log Type Descriptors: %u\n",
|
|
data[0x15]);
|
|
if (h->length < 0x17 + data[0x15] * data[0x16]) break;
|
|
dmi_event_log_descriptors(data[0x15], data[0x16], data + 0x17, "\t");
|
|
break;
|
|
|
|
case 16: /* 7.17 Physical Memory Array */
|
|
printf("Physical Memory Array\n");
|
|
if (h->length < 0x0F) break;
|
|
printf("\tLocation: %s\n",
|
|
dmi_memory_array_location(data[0x04]));
|
|
printf("\tUse: %s\n",
|
|
dmi_memory_array_use(data[0x05]));
|
|
printf("\tError Correction Type: %s\n",
|
|
dmi_memory_array_ec_type(data[0x06]));
|
|
printf("\tMaximum Capacity:");
|
|
if (DWORD(data + 0x07) == 0x80000000)
|
|
{
|
|
if (h->length < 0x17)
|
|
printf(" Unknown");
|
|
else
|
|
dmi_print_memory_size(QWORD(data + 0x0F), 0);
|
|
}
|
|
else
|
|
{
|
|
u64 capacity;
|
|
|
|
capacity.h = 0;
|
|
capacity.l = DWORD(data + 0x07);
|
|
dmi_print_memory_size(capacity, 1);
|
|
}
|
|
printf("\n");
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
{
|
|
printf("\tError Information Handle:");
|
|
dmi_memory_array_error_handle(WORD(data + 0x0B));
|
|
printf("\n");
|
|
}
|
|
printf("\tNumber Of Devices: %u\n",
|
|
WORD(data + 0x0D));
|
|
break;
|
|
|
|
case 17: /* 7.18 Memory Device */
|
|
printf("Memory Device\n");
|
|
if (h->length < 0x15) break;
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
{
|
|
printf("\tArray Handle: 0x%04X\n",
|
|
WORD(data + 0x04));
|
|
printf("\tError Information Handle:");
|
|
dmi_memory_array_error_handle(WORD(data + 0x06));
|
|
printf("\n");
|
|
}
|
|
printf("\tTotal Width:");
|
|
dmi_memory_device_width(WORD(data + 0x08));
|
|
printf("\n");
|
|
printf("\tData Width:");
|
|
dmi_memory_device_width(WORD(data + 0x0A));
|
|
printf("\n");
|
|
printf("\tSize:");
|
|
if (h->length >= 0x20 && WORD(data + 0x0C) == 0x7FFF)
|
|
dmi_memory_device_extended_size(DWORD(data + 0x1C));
|
|
else
|
|
dmi_memory_device_size(WORD(data + 0x0C));
|
|
printf("\n");
|
|
printf("\tForm Factor: %s\n",
|
|
dmi_memory_device_form_factor(data[0x0E]));
|
|
printf("\tSet:");
|
|
dmi_memory_device_set(data[0x0F]);
|
|
printf("\n");
|
|
printf("\tLocator: %s\n",
|
|
dmi_string(h, data[0x10]));
|
|
printf("\tBank Locator: %s\n",
|
|
dmi_string(h, data[0x11]));
|
|
printf("\tType: %s\n",
|
|
dmi_memory_device_type(data[0x12]));
|
|
printf("\tType Detail:");
|
|
dmi_memory_device_type_detail(WORD(data + 0x13));
|
|
printf("\n");
|
|
if (h->length < 0x17) break;
|
|
printf("\tSpeed:");
|
|
dmi_memory_device_speed(WORD(data + 0x15));
|
|
printf("\n");
|
|
if (h->length < 0x1B) break;
|
|
printf("\tManufacturer: %s\n",
|
|
dmi_string(h, data[0x17]));
|
|
printf("\tSerial Number: %s\n",
|
|
dmi_string(h, data[0x18]));
|
|
printf("\tAsset Tag: %s\n",
|
|
dmi_string(h, data[0x19]));
|
|
printf("\tPart Number: %s\n",
|
|
dmi_string(h, data[0x1A]));
|
|
if (h->length < 0x1C) break;
|
|
printf("\tRank: ");
|
|
if ((data[0x1B] & 0x0F) == 0)
|
|
printf("Unknown");
|
|
else
|
|
printf("%u", data[0x1B] & 0x0F);
|
|
printf("\n");
|
|
if (h->length < 0x22) break;
|
|
printf("\tConfigured Clock Speed:");
|
|
dmi_memory_device_speed(WORD(data + 0x20));
|
|
printf("\n");
|
|
if (h->length < 0x28) break;
|
|
printf("\tMinimum Voltage:");
|
|
dmi_memory_voltage_value(WORD(data + 0x22));
|
|
printf("\n");
|
|
printf("\tMaximum Voltage:");
|
|
dmi_memory_voltage_value(WORD(data + 0x24));
|
|
printf("\n");
|
|
printf("\tConfigured Voltage:");
|
|
dmi_memory_voltage_value(WORD(data + 0x26));
|
|
printf("\n");
|
|
break;
|
|
|
|
case 18: /* 7.19 32-bit Memory Error Information */
|
|
printf("32-bit Memory Error Information\n");
|
|
if (h->length < 0x17) break;
|
|
printf("\tType: %s\n",
|
|
dmi_memory_error_type(data[0x04]));
|
|
printf("\tGranularity: %s\n",
|
|
dmi_memory_error_granularity(data[0x05]));
|
|
printf("\tOperation: %s\n",
|
|
dmi_memory_error_operation(data[0x06]));
|
|
printf("\tVendor Syndrome:");
|
|
dmi_memory_error_syndrome(DWORD(data + 0x07));
|
|
printf("\n");
|
|
printf("\tMemory Array Address:");
|
|
dmi_32bit_memory_error_address(DWORD(data + 0x0B));
|
|
printf("\n");
|
|
printf("\tDevice Address:");
|
|
dmi_32bit_memory_error_address(DWORD(data + 0x0F));
|
|
printf("\n");
|
|
printf("\tResolution:");
|
|
dmi_32bit_memory_error_address(DWORD(data + 0x13));
|
|
printf("\n");
|
|
break;
|
|
|
|
case 19: /* 7.20 Memory Array Mapped Address */
|
|
printf("Memory Array Mapped Address\n");
|
|
if (h->length < 0x0F) break;
|
|
if (h->length >= 0x1F && DWORD(data + 0x04) == 0xFFFFFFFF)
|
|
{
|
|
u64 start, end;
|
|
|
|
start = QWORD(data + 0x0F);
|
|
end = QWORD(data + 0x17);
|
|
|
|
printf("\tStarting Address: 0x%08X%08Xk\n",
|
|
start.h, start.l);
|
|
printf("\tEnding Address: 0x%08X%08Xk\n",
|
|
end.h, end.l);
|
|
printf("\tRange Size:");
|
|
dmi_mapped_address_extended_size(start, end);
|
|
}
|
|
else
|
|
{
|
|
printf("\tStarting Address: 0x%08X%03X\n",
|
|
DWORD(data + 0x04) >> 2,
|
|
(DWORD(data + 0x04) & 0x3) << 10);
|
|
printf("\tEnding Address: 0x%08X%03X\n",
|
|
DWORD(data + 0x08) >> 2,
|
|
((DWORD(data + 0x08) & 0x3) << 10) + 0x3FF);
|
|
printf("\tRange Size:");
|
|
dmi_mapped_address_size(DWORD(data + 0x08) - DWORD(data + 0x04) + 1);
|
|
}
|
|
printf("\n");
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("\tPhysical Array Handle: 0x%04X\n",
|
|
WORD(data + 0x0C));
|
|
printf("\tPartition Width: %u\n",
|
|
data[0x0E]);
|
|
break;
|
|
|
|
case 20: /* 7.21 Memory Device Mapped Address */
|
|
printf("Memory Device Mapped Address\n");
|
|
if (h->length < 0x13) break;
|
|
if (h->length >= 0x23 && DWORD(data + 0x04) == 0xFFFFFFFF)
|
|
{
|
|
u64 start, end;
|
|
|
|
start = QWORD(data + 0x13);
|
|
end = QWORD(data + 0x1B);
|
|
|
|
printf("\tStarting Address: 0x%08X%08Xk\n",
|
|
start.h, start.l);
|
|
printf("\tEnding Address: 0x%08X%08Xk\n",
|
|
end.h, end.l);
|
|
printf("\tRange Size:");
|
|
dmi_mapped_address_extended_size(start, end);
|
|
}
|
|
else
|
|
{
|
|
printf("\tStarting Address: 0x%08X%03X\n",
|
|
DWORD(data + 0x04) >> 2,
|
|
(DWORD(data + 0x04) & 0x3) << 10);
|
|
printf("\tEnding Address: 0x%08X%03X\n",
|
|
DWORD(data + 0x08) >> 2,
|
|
((DWORD(data + 0x08) & 0x3) << 10) + 0x3FF);
|
|
printf("\tRange Size:");
|
|
dmi_mapped_address_size(DWORD(data + 0x08) - DWORD(data + 0x04) + 1);
|
|
}
|
|
printf("\n");
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
{
|
|
printf("\tPhysical Device Handle: 0x%04X\n",
|
|
WORD(data + 0x0C));
|
|
printf("\tMemory Array Mapped Address Handle: 0x%04X\n",
|
|
WORD(data + 0x0E));
|
|
}
|
|
printf("\tPartition Row Position:");
|
|
dmi_mapped_address_row_position(data[0x10]);
|
|
printf("\n");
|
|
dmi_mapped_address_interleave_position(data[0x11], "\t");
|
|
dmi_mapped_address_interleaved_data_depth(data[0x12], "\t");
|
|
break;
|
|
|
|
case 21: /* 7.22 Built-in Pointing Device */
|
|
printf("Built-in Pointing Device\n");
|
|
if (h->length < 0x07) break;
|
|
printf("\tType: %s\n",
|
|
dmi_pointing_device_type(data[0x04]));
|
|
printf("\tInterface: %s\n",
|
|
dmi_pointing_device_interface(data[0x05]));
|
|
printf("\tButtons: %u\n",
|
|
data[0x06]);
|
|
break;
|
|
|
|
case 22: /* 7.23 Portable Battery */
|
|
printf("Portable Battery\n");
|
|
if (h->length < 0x10) break;
|
|
printf("\tLocation: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tManufacturer: %s\n",
|
|
dmi_string(h, data[0x05]));
|
|
if (data[0x06] || h->length < 0x1A)
|
|
printf("\tManufacture Date: %s\n",
|
|
dmi_string(h, data[0x06]));
|
|
if (data[0x07] || h->length < 0x1A)
|
|
printf("\tSerial Number: %s\n",
|
|
dmi_string(h, data[0x07]));
|
|
printf("\tName: %s\n",
|
|
dmi_string(h, data[0x08]));
|
|
if (data[0x09] != 0x02 || h->length < 0x1A)
|
|
printf("\tChemistry: %s\n",
|
|
dmi_battery_chemistry(data[0x09]));
|
|
printf("\tDesign Capacity:");
|
|
if (h->length < 0x16)
|
|
dmi_battery_capacity(WORD(data + 0x0A), 1);
|
|
else
|
|
dmi_battery_capacity(WORD(data + 0x0A), data[0x15]);
|
|
printf("\n");
|
|
printf("\tDesign Voltage:");
|
|
dmi_battery_voltage(WORD(data + 0x0C));
|
|
printf("\n");
|
|
printf("\tSBDS Version: %s\n",
|
|
dmi_string(h, data[0x0E]));
|
|
printf("\tMaximum Error:");
|
|
dmi_battery_maximum_error(data[0x0F]);
|
|
printf("\n");
|
|
if (h->length < 0x1A) break;
|
|
if (data[0x07] == 0)
|
|
printf("\tSBDS Serial Number: %04X\n",
|
|
WORD(data + 0x10));
|
|
if (data[0x06] == 0)
|
|
printf("\tSBDS Manufacture Date: %u-%02u-%02u\n",
|
|
1980 + (WORD(data + 0x12) >> 9),
|
|
(WORD(data + 0x12) >> 5) & 0x0F,
|
|
WORD(data + 0x12) & 0x1F);
|
|
if (data[0x09] == 0x02)
|
|
printf("\tSBDS Chemistry: %s\n",
|
|
dmi_string(h, data[0x14]));
|
|
printf("\tOEM-specific Information: 0x%08X\n",
|
|
DWORD(data + 0x16));
|
|
break;
|
|
|
|
case 23: /* 7.24 System Reset */
|
|
printf("System Reset\n");
|
|
if (h->length < 0x0D) break;
|
|
printf("\tStatus: %s\n",
|
|
data[0x04] & (1 << 0) ? "Enabled" : "Disabled");
|
|
printf("\tWatchdog Timer: %s\n",
|
|
data[0x04] & (1 << 5) ? "Present" : "Not Present");
|
|
if (!(data[0x04] & (1 << 5)))
|
|
break;
|
|
printf("\tBoot Option: %s\n",
|
|
dmi_system_reset_boot_option((data[0x04] >> 1) & 0x3));
|
|
printf("\tBoot Option On Limit: %s\n",
|
|
dmi_system_reset_boot_option((data[0x04] >> 3) & 0x3));
|
|
printf("\tReset Count:");
|
|
dmi_system_reset_count(WORD(data + 0x05));
|
|
printf("\n");
|
|
printf("\tReset Limit:");
|
|
dmi_system_reset_count(WORD(data + 0x07));
|
|
printf("\n");
|
|
printf("\tTimer Interval:");
|
|
dmi_system_reset_timer(WORD(data + 0x09));
|
|
printf("\n");
|
|
printf("\tTimeout:");
|
|
dmi_system_reset_timer(WORD(data + 0x0B));
|
|
printf("\n");
|
|
break;
|
|
|
|
case 24: /* 7.25 Hardware Security */
|
|
printf("Hardware Security\n");
|
|
if (h->length < 0x05) break;
|
|
printf("\tPower-On Password Status: %s\n",
|
|
dmi_hardware_security_status(data[0x04] >> 6));
|
|
printf("\tKeyboard Password Status: %s\n",
|
|
dmi_hardware_security_status((data[0x04] >> 4) & 0x3));
|
|
printf("\tAdministrator Password Status: %s\n",
|
|
dmi_hardware_security_status((data[0x04] >> 2) & 0x3));
|
|
printf("\tFront Panel Reset Status: %s\n",
|
|
dmi_hardware_security_status(data[0x04] & 0x3));
|
|
break;
|
|
|
|
case 25: /* 7.26 System Power Controls */
|
|
printf("\tSystem Power Controls\n");
|
|
if (h->length < 0x09) break;
|
|
printf("\tNext Scheduled Power-on:");
|
|
dmi_power_controls_power_on(data + 0x04);
|
|
printf("\n");
|
|
break;
|
|
|
|
case 26: /* 7.27 Voltage Probe */
|
|
printf("Voltage Probe\n");
|
|
if (h->length < 0x14) break;
|
|
printf("\tDescription: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tLocation: %s\n",
|
|
dmi_voltage_probe_location(data[0x05] & 0x1f));
|
|
printf("\tStatus: %s\n",
|
|
dmi_probe_status(data[0x05] >> 5));
|
|
printf("\tMaximum Value:");
|
|
dmi_voltage_probe_value(WORD(data + 0x06));
|
|
printf("\n");
|
|
printf("\tMinimum Value:");
|
|
dmi_voltage_probe_value(WORD(data + 0x08));
|
|
printf("\n");
|
|
printf("\tResolution:");
|
|
dmi_voltage_probe_resolution(WORD(data + 0x0A));
|
|
printf("\n");
|
|
printf("\tTolerance:");
|
|
dmi_voltage_probe_value(WORD(data + 0x0C));
|
|
printf("\n");
|
|
printf("\tAccuracy:");
|
|
dmi_probe_accuracy(WORD(data + 0x0E));
|
|
printf("\n");
|
|
printf("\tOEM-specific Information: 0x%08X\n",
|
|
DWORD(data + 0x10));
|
|
if (h->length < 0x16) break;
|
|
printf("\tNominal Value:");
|
|
dmi_voltage_probe_value(WORD(data + 0x14));
|
|
printf("\n");
|
|
break;
|
|
|
|
case 27: /* 7.28 Cooling Device */
|
|
printf("Cooling Device\n");
|
|
if (h->length < 0x0C) break;
|
|
if (!(opt.flags & FLAG_QUIET) && WORD(data + 0x04) != 0xFFFF)
|
|
printf("\tTemperature Probe Handle: 0x%04X\n",
|
|
WORD(data + 0x04));
|
|
printf("\tType: %s\n",
|
|
dmi_cooling_device_type(data[0x06] & 0x1f));
|
|
printf("\tStatus: %s\n",
|
|
dmi_probe_status(data[0x06] >> 5));
|
|
if (data[0x07] != 0x00)
|
|
printf("\tCooling Unit Group: %u\n",
|
|
data[0x07]);
|
|
printf("\tOEM-specific Information: 0x%08X\n",
|
|
DWORD(data + 0x08));
|
|
if (h->length < 0x0E) break;
|
|
printf("\tNominal Speed:");
|
|
dmi_cooling_device_speed(WORD(data + 0x0C));
|
|
printf("\n");
|
|
if (h->length < 0x0F) break;
|
|
printf("\tDescription: %s\n", dmi_string(h, data[0x0E]));
|
|
break;
|
|
|
|
case 28: /* 7.29 Temperature Probe */
|
|
printf("Temperature Probe\n");
|
|
if (h->length < 0x14) break;
|
|
printf("\tDescription: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tLocation: %s\n",
|
|
dmi_temperature_probe_location(data[0x05] & 0x1F));
|
|
printf("\tStatus: %s\n",
|
|
dmi_probe_status(data[0x05] >> 5));
|
|
printf("\tMaximum Value:");
|
|
dmi_temperature_probe_value(WORD(data + 0x06));
|
|
printf("\n");
|
|
printf("\tMinimum Value:");
|
|
dmi_temperature_probe_value(WORD(data + 0x08));
|
|
printf("\n");
|
|
printf("\tResolution:");
|
|
dmi_temperature_probe_resolution(WORD(data + 0x0A));
|
|
printf("\n");
|
|
printf("\tTolerance:");
|
|
dmi_temperature_probe_value(WORD(data + 0x0C));
|
|
printf("\n");
|
|
printf("\tAccuracy:");
|
|
dmi_probe_accuracy(WORD(data + 0x0E));
|
|
printf("\n");
|
|
printf("\tOEM-specific Information: 0x%08X\n",
|
|
DWORD(data + 0x10));
|
|
if (h->length < 0x16) break;
|
|
printf("\tNominal Value:");
|
|
dmi_temperature_probe_value(WORD(data + 0x14));
|
|
printf("\n");
|
|
break;
|
|
|
|
case 29: /* 7.30 Electrical Current Probe */
|
|
printf("Electrical Current Probe\n");
|
|
if (h->length < 0x14) break;
|
|
printf("\tDescription: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tLocation: %s\n",
|
|
dmi_voltage_probe_location(data[5] & 0x1F));
|
|
printf("\tStatus: %s\n",
|
|
dmi_probe_status(data[0x05] >> 5));
|
|
printf("\tMaximum Value:");
|
|
dmi_current_probe_value(WORD(data + 0x06));
|
|
printf("\n");
|
|
printf("\tMinimum Value:");
|
|
dmi_current_probe_value(WORD(data + 0x08));
|
|
printf("\n");
|
|
printf("\tResolution:");
|
|
dmi_current_probe_resolution(WORD(data + 0x0A));
|
|
printf("\n");
|
|
printf("\tTolerance:");
|
|
dmi_current_probe_value(WORD(data + 0x0C));
|
|
printf("\n");
|
|
printf("\tAccuracy:");
|
|
dmi_probe_accuracy(WORD(data + 0x0E));
|
|
printf("\n");
|
|
printf("\tOEM-specific Information: 0x%08X\n",
|
|
DWORD(data + 0x10));
|
|
if (h->length < 0x16) break;
|
|
printf("\tNominal Value:");
|
|
dmi_current_probe_value(WORD(data + 0x14));
|
|
printf("\n");
|
|
break;
|
|
|
|
case 30: /* 7.31 Out-of-band Remote Access */
|
|
printf("Out-of-band Remote Access\n");
|
|
if (h->length < 0x06) break;
|
|
printf("\tManufacturer Name: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tInbound Connection: %s\n",
|
|
data[0x05] & (1 << 0) ? "Enabled" : "Disabled");
|
|
printf("\tOutbound Connection: %s\n",
|
|
data[0x05] & (1 << 1) ? "Enabled" : "Disabled");
|
|
break;
|
|
|
|
case 31: /* 7.32 Boot Integrity Services Entry Point */
|
|
printf("Boot Integrity Services Entry Point\n");
|
|
if (h->length < 0x1C) break;
|
|
printf("\tChecksum: %s\n",
|
|
checksum(data, h->length) ? "OK" : "Invalid");
|
|
printf("\t16-bit Entry Point Address: %04X:%04X\n",
|
|
DWORD(data + 0x08) >> 16,
|
|
DWORD(data + 0x08) & 0xFFFF);
|
|
printf("\t32-bit Entry Point Address: 0x%08X\n",
|
|
DWORD(data + 0x0C));
|
|
break;
|
|
|
|
case 32: /* 7.33 System Boot Information */
|
|
printf("System Boot Information\n");
|
|
if (h->length < 0x0B) break;
|
|
printf("\tStatus: %s\n",
|
|
dmi_system_boot_status(data[0x0A]));
|
|
break;
|
|
|
|
case 33: /* 7.34 64-bit Memory Error Information */
|
|
if (h->length < 0x1F) break;
|
|
printf("64-bit Memory Error Information\n");
|
|
printf("\tType: %s\n",
|
|
dmi_memory_error_type(data[0x04]));
|
|
printf("\tGranularity: %s\n",
|
|
dmi_memory_error_granularity(data[0x05]));
|
|
printf("\tOperation: %s\n",
|
|
dmi_memory_error_operation(data[0x06]));
|
|
printf("\tVendor Syndrome:");
|
|
dmi_memory_error_syndrome(DWORD(data + 0x07));
|
|
printf("\n");
|
|
printf("\tMemory Array Address:");
|
|
dmi_64bit_memory_error_address(QWORD(data + 0x0B));
|
|
printf("\n");
|
|
printf("\tDevice Address:");
|
|
dmi_64bit_memory_error_address(QWORD(data + 0x13));
|
|
printf("\n");
|
|
printf("\tResolution:");
|
|
dmi_32bit_memory_error_address(DWORD(data + 0x1B));
|
|
printf("\n");
|
|
break;
|
|
|
|
case 34: /* 7.35 Management Device */
|
|
printf("Management Device\n");
|
|
if (h->length < 0x0B) break;
|
|
printf("\tDescription: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
printf("\tType: %s\n",
|
|
dmi_management_device_type(data[0x05]));
|
|
printf("\tAddress: 0x%08X\n",
|
|
DWORD(data + 0x06));
|
|
printf("\tAddress Type: %s\n",
|
|
dmi_management_device_address_type(data[0x0A]));
|
|
break;
|
|
|
|
case 35: /* 7.36 Management Device Component */
|
|
printf("Management Device Component\n");
|
|
if (h->length < 0x0B) break;
|
|
printf("\tDescription: %s\n",
|
|
dmi_string(h, data[0x04]));
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
{
|
|
printf("\tManagement Device Handle: 0x%04X\n",
|
|
WORD(data + 0x05));
|
|
printf("\tComponent Handle: 0x%04X\n",
|
|
WORD(data + 0x07));
|
|
if (WORD(data + 0x09) != 0xFFFF)
|
|
printf("\tThreshold Handle: 0x%04X\n",
|
|
WORD(data + 0x09));
|
|
}
|
|
break;
|
|
|
|
case 36: /* 7.37 Management Device Threshold Data */
|
|
printf("Management Device Threshold Data\n");
|
|
if (h->length < 0x10) break;
|
|
if (WORD(data + 0x04) != 0x8000)
|
|
printf("\tLower Non-critical Threshold: %d\n",
|
|
(i16)WORD(data + 0x04));
|
|
if (WORD(data + 0x06) != 0x8000)
|
|
printf("\tUpper Non-critical Threshold: %d\n",
|
|
(i16)WORD(data + 0x06));
|
|
if (WORD(data + 0x08) != 0x8000)
|
|
printf("\tLower Critical Threshold: %d\n",
|
|
(i16)WORD(data + 0x08));
|
|
if (WORD(data + 0x0A) != 0x8000)
|
|
printf("\tUpper Critical Threshold: %d\n",
|
|
(i16)WORD(data + 0x0A));
|
|
if (WORD(data + 0x0C) != 0x8000)
|
|
printf("\tLower Non-recoverable Threshold: %d\n",
|
|
(i16)WORD(data + 0x0C));
|
|
if (WORD(data + 0x0E) != 0x8000)
|
|
printf("\tUpper Non-recoverable Threshold: %d\n",
|
|
(i16)WORD(data + 0x0E));
|
|
break;
|
|
|
|
case 37: /* 7.38 Memory Channel */
|
|
printf("Memory Channel\n");
|
|
if (h->length < 0x07) break;
|
|
printf("\tType: %s\n",
|
|
dmi_memory_channel_type(data[0x04]));
|
|
printf("\tMaximal Load: %u\n",
|
|
data[0x05]);
|
|
printf("\tDevices: %u\n",
|
|
data[0x06]);
|
|
if (h->length < 0x07 + 3 * data[0x06]) break;
|
|
dmi_memory_channel_devices(data[0x06], data + 0x07, "\t");
|
|
break;
|
|
|
|
case 38: /* 7.39 IPMI Device Information */
|
|
/*
|
|
* We use the word "Version" instead of "Revision", conforming to
|
|
* the IPMI specification.
|
|
*/
|
|
printf("IPMI Device Information\n");
|
|
if (h->length < 0x10) break;
|
|
printf("\tInterface Type: %s\n",
|
|
dmi_ipmi_interface_type(data[0x04]));
|
|
printf("\tSpecification Version: %u.%u\n",
|
|
data[0x05] >> 4, data[0x05] & 0x0F);
|
|
printf("\tI2C Slave Address: 0x%02x\n",
|
|
data[0x06] >> 1);
|
|
if (data[0x07] != 0xFF)
|
|
printf("\tNV Storage Device Address: %u\n",
|
|
data[0x07]);
|
|
else
|
|
printf("\tNV Storage Device: Not Present\n");
|
|
printf("\tBase Address: ");
|
|
dmi_ipmi_base_address(data[0x04], data + 0x08,
|
|
h->length < 0x11 ? 0 : (data[0x10] >> 4) & 1);
|
|
printf("\n");
|
|
if (h->length < 0x12) break;
|
|
if (data[0x04] != 0x04)
|
|
{
|
|
printf("\tRegister Spacing: %s\n",
|
|
dmi_ipmi_register_spacing(data[0x10] >> 6));
|
|
if (data[0x10] & (1 << 3))
|
|
{
|
|
printf("\tInterrupt Polarity: %s\n",
|
|
data[0x10] & (1 << 1) ? "Active High" : "Active Low");
|
|
printf("\tInterrupt Trigger Mode: %s\n",
|
|
data[0x10] & (1 << 0) ? "Level" : "Edge");
|
|
}
|
|
}
|
|
if (data[0x11] != 0x00)
|
|
{
|
|
printf("\tInterrupt Number: %u\n",
|
|
data[0x11]);
|
|
}
|
|
break;
|
|
|
|
case 39: /* 7.40 System Power Supply */
|
|
printf("System Power Supply\n");
|
|
if (h->length < 0x10) break;
|
|
if (data[0x04] != 0x00)
|
|
printf("\tPower Unit Group: %u\n",
|
|
data[0x04]);
|
|
printf("\tLocation: %s\n",
|
|
dmi_string(h, data[0x05]));
|
|
printf("\tName: %s\n",
|
|
dmi_string(h, data[0x06]));
|
|
printf("\tManufacturer: %s\n",
|
|
dmi_string(h, data[0x07]));
|
|
printf("\tSerial Number: %s\n",
|
|
dmi_string(h, data[0x08]));
|
|
printf("\tAsset Tag: %s\n",
|
|
dmi_string(h, data[0x09]));
|
|
printf("\tModel Part Number: %s\n",
|
|
dmi_string(h, data[0x0A]));
|
|
printf("\tRevision: %s\n",
|
|
dmi_string(h, data[0x0B]));
|
|
printf("\tMax Power Capacity:");
|
|
dmi_power_supply_power(WORD(data + 0x0C));
|
|
printf("\n");
|
|
printf("\tStatus:");
|
|
if (WORD(data + 0x0E) & (1 << 1))
|
|
printf(" Present, %s",
|
|
dmi_power_supply_status((WORD(data + 0x0E) >> 7) & 0x07));
|
|
else
|
|
printf(" Not Present");
|
|
printf("\n");
|
|
printf("\tType: %s\n",
|
|
dmi_power_supply_type((WORD(data + 0x0E) >> 10) & 0x0F));
|
|
printf("\tInput Voltage Range Switching: %s\n",
|
|
dmi_power_supply_range_switching((WORD(data + 0x0E) >> 3) & 0x0F));
|
|
printf("\tPlugged: %s\n",
|
|
WORD(data + 0x0E) & (1 << 2) ? "No" : "Yes");
|
|
printf("\tHot Replaceable: %s\n",
|
|
WORD(data + 0x0E) & (1 << 0) ? "Yes" : "No");
|
|
if (h->length < 0x16) break;
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
{
|
|
if (WORD(data + 0x10) != 0xFFFF)
|
|
printf("\tInput Voltage Probe Handle: 0x%04X\n",
|
|
WORD(data + 0x10));
|
|
if (WORD(data + 0x12) != 0xFFFF)
|
|
printf("\tCooling Device Handle: 0x%04X\n",
|
|
WORD(data + 0x12));
|
|
if (WORD(data + 0x14) != 0xFFFF)
|
|
printf("\tInput Current Probe Handle: 0x%04X\n",
|
|
WORD(data + 0x14));
|
|
}
|
|
break;
|
|
|
|
case 40: /* 7.41 Additional Information */
|
|
if (h->length < 0x0B) break;
|
|
if (opt.flags & FLAG_QUIET)
|
|
return;
|
|
dmi_additional_info(h, "");
|
|
break;
|
|
|
|
case 41: /* 7.42 Onboard Device Extended Information */
|
|
printf("Onboard Device\n");
|
|
if (h->length < 0x0B) break;
|
|
printf("\tReference Designation: %s\n", dmi_string(h, data[0x04]));
|
|
printf("\tType: %s\n",
|
|
dmi_on_board_devices_type(data[0x05] & 0x7F));
|
|
printf("\tStatus: %s\n",
|
|
data[0x05] & 0x80 ? "Enabled" : "Disabled");
|
|
printf("\tType Instance: %u\n", data[0x06]);
|
|
dmi_slot_segment_bus_func(WORD(data + 0x07), data[0x09], data[0x0A], "\t");
|
|
break;
|
|
|
|
case 42: /* 7.43 Management Controller Host Interface */
|
|
printf("Management Controller Host Interface\n");
|
|
if (h->length < 0x05) break;
|
|
printf("\tInterface Type: %s\n",
|
|
dmi_management_controller_host_type(data[0x04]));
|
|
/*
|
|
* There you have a type-dependent, variable-length
|
|
* part in the middle of the structure, with no
|
|
* length specifier, so no easy way to decode the
|
|
* common, final part of the structure. What a pity.
|
|
*/
|
|
if (h->length < 0x09) break;
|
|
if (data[0x04] == 0xF0) /* OEM */
|
|
{
|
|
printf("\tVendor ID: 0x%02X%02X%02X%02X\n",
|
|
data[0x05], data[0x06], data[0x07],
|
|
data[0x08]);
|
|
}
|
|
break;
|
|
|
|
case 43: /* 7.44 TPM Device */
|
|
printf("TPM Device\n");
|
|
if (h->length < 0x1B) break;
|
|
printf("\tVendor ID:");
|
|
dmi_tpm_vendor_id(data + 0x04);
|
|
printf("\n");
|
|
printf("\tSpecification Version: %d.%d", data[0x08], data[0x09]);
|
|
switch (data[0x08])
|
|
{
|
|
case 0x01:
|
|
/*
|
|
* We skip the first 2 bytes, which are
|
|
* redundant with the above, and uncoded
|
|
* in a silly way.
|
|
*/
|
|
printf("\tFirmware Revision: %u.%u\n",
|
|
data[0x0C], data[0x0D]);
|
|
break;
|
|
case 0x02:
|
|
printf("\tFirmware Revision: %u.%u\n",
|
|
DWORD(data + 0x0A) >> 16,
|
|
DWORD(data + 0x0A) && 0xFF);
|
|
/*
|
|
* We skip the next 4 bytes, as their
|
|
* format is not standardized and their
|
|
* usefulness seems limited anyway.
|
|
*/
|
|
break;
|
|
}
|
|
printf("\tDescription: %s", dmi_string(h, data[0x12]));
|
|
printf("\tCharacteristics:\n");
|
|
dmi_tpm_characteristics(QWORD(data + 0x13), "\t\t");
|
|
if (h->length < 0x1F) break;
|
|
printf("\tOEM-specific Information: 0x%08X\n",
|
|
DWORD(data + 0x1B));
|
|
break;
|
|
|
|
case 126: /* 7.44 Inactive */
|
|
printf("Inactive\n");
|
|
break;
|
|
|
|
case 127: /* 7.45 End Of Table */
|
|
printf("End Of Table\n");
|
|
break;
|
|
|
|
default:
|
|
if (dmi_decode_oem(h))
|
|
break;
|
|
if (opt.flags & FLAG_QUIET)
|
|
return;
|
|
printf("%s Type\n",
|
|
h->type >= 128 ? "OEM-specific" : "Unknown");
|
|
dmi_dump(h, "\t");
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static void to_dmi_header(struct dmi_header *h, u8 *data)
|
|
{
|
|
h->type = data[0];
|
|
h->length = data[1];
|
|
h->handle = WORD(data + 2);
|
|
h->data = data;
|
|
}
|
|
|
|
static void dmi_table_string(const struct dmi_header *h, const u8 *data, u16 ver)
|
|
{
|
|
int key;
|
|
u8 offset = opt.string->offset;
|
|
|
|
if (opt.string->type == 11) /* OEM strings */
|
|
{
|
|
if (h->length < 5 || offset > data[4])
|
|
{
|
|
fprintf(stderr, "No OEM string number %u\n", offset);
|
|
return;
|
|
}
|
|
|
|
if (offset)
|
|
printf("%s\n", dmi_string(h, offset));
|
|
else
|
|
printf("%u\n", data[4]); /* count */
|
|
return;
|
|
}
|
|
|
|
if (offset >= h->length)
|
|
return;
|
|
|
|
key = (opt.string->type << 8) | offset;
|
|
switch (key)
|
|
{
|
|
case 0x108:
|
|
dmi_system_uuid(data + offset, ver);
|
|
printf("\n");
|
|
break;
|
|
case 0x305:
|
|
printf("%s\n", dmi_chassis_type(data[offset]));
|
|
break;
|
|
case 0x406:
|
|
printf("%s\n", dmi_processor_family(h, ver));
|
|
break;
|
|
case 0x416:
|
|
dmi_processor_frequency(data + offset);
|
|
printf("\n");
|
|
break;
|
|
default:
|
|
printf("%s\n", dmi_string(h, data[offset]));
|
|
}
|
|
}
|
|
|
|
static void dmi_table_dump(const u8 *buf, u32 len)
|
|
{
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("# Writing %d bytes to %s.\n", len, opt.dumpfile);
|
|
write_dump(32, len, buf, opt.dumpfile, 0);
|
|
}
|
|
|
|
static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
|
|
{
|
|
u8 *data;
|
|
int i = 0;
|
|
|
|
data = buf;
|
|
while ((i < num || !num)
|
|
&& data + 4 <= buf + len) /* 4 is the length of an SMBIOS structure header */
|
|
{
|
|
u8 *next;
|
|
struct dmi_header h;
|
|
int display;
|
|
|
|
to_dmi_header(&h, data);
|
|
display = ((opt.type == NULL || opt.type[h.type])
|
|
&& !((opt.flags & FLAG_QUIET) && (h.type == 126 || h.type == 127))
|
|
&& !opt.string);
|
|
|
|
/*
|
|
* If a short entry is found (less than 4 bytes), not only it
|
|
* is invalid, but we cannot reliably locate the next entry.
|
|
* Better stop at this point, and let the user know his/her
|
|
* table is broken.
|
|
*/
|
|
if (h.length < 4)
|
|
{
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
{
|
|
fprintf(stderr,
|
|
"Invalid entry length (%u). DMI table "
|
|
"is broken! Stop.\n\n",
|
|
(unsigned int)h.length);
|
|
opt.flags |= FLAG_QUIET;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* In quiet mode, stop decoding at end of table marker */
|
|
if ((opt.flags & FLAG_QUIET) && h.type == 127)
|
|
break;
|
|
|
|
if (display
|
|
&& (!(opt.flags & FLAG_QUIET) || (opt.flags & FLAG_DUMP)))
|
|
printf("Handle 0x%04X, DMI type %d, %d bytes\n",
|
|
h.handle, h.type, h.length);
|
|
|
|
/* assign vendor for vendor-specific decodes later */
|
|
if (h.type == 1 && h.length >= 5)
|
|
dmi_set_vendor(dmi_string(&h, data[0x04]));
|
|
|
|
/* Fixup a common mistake */
|
|
if (h.type == 34)
|
|
dmi_fixup_type_34(&h, display);
|
|
|
|
/* look for the next handle */
|
|
next = data + h.length;
|
|
while ((unsigned long)(next - buf + 1) < len
|
|
&& (next[0] != 0 || next[1] != 0))
|
|
next++;
|
|
next += 2;
|
|
if (display)
|
|
{
|
|
if ((unsigned long)(next - buf) <= len)
|
|
{
|
|
if (opt.flags & FLAG_DUMP)
|
|
{
|
|
dmi_dump(&h, "\t");
|
|
printf("\n");
|
|
}
|
|
else
|
|
dmi_decode(&h, ver);
|
|
}
|
|
else if (!(opt.flags & FLAG_QUIET))
|
|
printf("\t<TRUNCATED>\n\n");
|
|
}
|
|
else if (opt.string != NULL
|
|
&& opt.string->type == h.type)
|
|
dmi_table_string(&h, data, ver);
|
|
|
|
data = next;
|
|
i++;
|
|
|
|
/* SMBIOS v3 requires stopping at this marker */
|
|
if (h.type == 127 && (flags & FLAG_STOP_AT_EOT))
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* SMBIOS v3 64-bit entry points do not announce a structures count,
|
|
* and only indicate a maximum size for the table.
|
|
*/
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
{
|
|
if (num && i != num)
|
|
fprintf(stderr, "Wrong DMI structures count: %d announced, "
|
|
"only %d decoded.\n", num, i);
|
|
if ((unsigned long)(data - buf) > len
|
|
|| (num && (unsigned long)(data - buf) < len))
|
|
fprintf(stderr, "Wrong DMI structures length: %u bytes "
|
|
"announced, structures occupy %lu bytes.\n",
|
|
len, (unsigned long)(data - buf));
|
|
}
|
|
}
|
|
|
|
static void dmi_table(off_t base, u32 len, u16 num, u32 ver, const char *devmem,
|
|
u32 flags)
|
|
{
|
|
u8 *buf;
|
|
|
|
if (ver > SUPPORTED_SMBIOS_VER && !(opt.flags & FLAG_QUIET))
|
|
{
|
|
printf("# SMBIOS implementations newer than version %u.%u.%u are not\n"
|
|
"# fully supported by this version of dmidecode.\n",
|
|
SUPPORTED_SMBIOS_VER >> 16,
|
|
(SUPPORTED_SMBIOS_VER >> 8) & 0xFF,
|
|
SUPPORTED_SMBIOS_VER & 0xFF);
|
|
}
|
|
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
{
|
|
if (opt.type == NULL)
|
|
{
|
|
if (num)
|
|
printf("%u structures occupying %u bytes.\n",
|
|
num, len);
|
|
if (!(opt.flags & FLAG_FROM_DUMP))
|
|
printf("Table at 0x%08llX.\n",
|
|
(unsigned long long)base);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
if ((flags & FLAG_NO_FILE_OFFSET) || (opt.flags & FLAG_FROM_DUMP))
|
|
{
|
|
/*
|
|
* When reading from sysfs or from a dump file, the file may be
|
|
* shorter than announced. For SMBIOS v3 this is expcted, as we
|
|
* only know the maximum table size, not the actual table size.
|
|
* For older implementations (and for SMBIOS v3 too), this
|
|
* would be the result of the kernel truncating the table on
|
|
* parse error.
|
|
*/
|
|
size_t size = len;
|
|
buf = read_file(flags & FLAG_NO_FILE_OFFSET ? 0 : base,
|
|
&size, devmem);
|
|
if (!(opt.flags & FLAG_QUIET) && num && size != (size_t)len)
|
|
{
|
|
fprintf(stderr, "Wrong DMI structures length: %u bytes "
|
|
"announced, only %lu bytes available.\n",
|
|
len, (unsigned long)size);
|
|
}
|
|
len = size;
|
|
}
|
|
else
|
|
buf = mem_chunk(base, len, devmem);
|
|
|
|
if (buf == NULL)
|
|
{
|
|
fprintf(stderr, "Failed to read table, sorry.\n");
|
|
#ifndef USE_MMAP
|
|
if (!(flags & FLAG_NO_FILE_OFFSET))
|
|
fprintf(stderr,
|
|
"Try compiling dmidecode with -DUSE_MMAP.\n");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (opt.flags & FLAG_DUMP_BIN)
|
|
dmi_table_dump(buf, len);
|
|
else
|
|
dmi_table_decode(buf, len, num, ver >> 8, flags);
|
|
|
|
free(buf);
|
|
}
|
|
|
|
|
|
/*
|
|
* Build a crafted entry point with table address hard-coded to 32,
|
|
* as this is where we will put it in the output file. We adjust the
|
|
* DMI checksum appropriately. The SMBIOS checksum needs no adjustment.
|
|
*/
|
|
static void overwrite_dmi_address(u8 *buf)
|
|
{
|
|
buf[0x05] += buf[0x08] + buf[0x09] + buf[0x0A] + buf[0x0B] - 32;
|
|
buf[0x08] = 32;
|
|
buf[0x09] = 0;
|
|
buf[0x0A] = 0;
|
|
buf[0x0B] = 0;
|
|
}
|
|
|
|
/* Same thing for SMBIOS3 entry points */
|
|
static void overwrite_smbios3_address(u8 *buf)
|
|
{
|
|
buf[0x05] += buf[0x10] + buf[0x11] + buf[0x12] + buf[0x13]
|
|
+ buf[0x14] + buf[0x15] + buf[0x16] + buf[0x17] - 32;
|
|
buf[0x10] = 32;
|
|
buf[0x11] = 0;
|
|
buf[0x12] = 0;
|
|
buf[0x13] = 0;
|
|
buf[0x14] = 0;
|
|
buf[0x15] = 0;
|
|
buf[0x16] = 0;
|
|
buf[0x17] = 0;
|
|
}
|
|
|
|
static int smbios3_decode(u8 *buf, const char *devmem, u32 flags)
|
|
{
|
|
u32 ver;
|
|
u64 offset;
|
|
|
|
if (!checksum(buf, buf[0x06]))
|
|
return 0;
|
|
|
|
ver = (buf[0x07] << 16) + (buf[0x08] << 8) + buf[0x09];
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("SMBIOS %u.%u.%u present.\n",
|
|
buf[0x07], buf[0x08], buf[0x09]);
|
|
|
|
offset = QWORD(buf + 0x10);
|
|
if (!(flags & FLAG_NO_FILE_OFFSET) && offset.h && sizeof(off_t) < 8)
|
|
{
|
|
fprintf(stderr, "64-bit addresses not supported, sorry.\n");
|
|
return 0;
|
|
}
|
|
|
|
dmi_table(((off_t)offset.h << 32) | offset.l,
|
|
DWORD(buf + 0x0C), 0, ver, devmem, flags | FLAG_STOP_AT_EOT);
|
|
|
|
if (opt.flags & FLAG_DUMP_BIN)
|
|
{
|
|
u8 crafted[32];
|
|
|
|
memcpy(crafted, buf, 32);
|
|
overwrite_smbios3_address(crafted);
|
|
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("# Writing %d bytes to %s.\n", crafted[0x06],
|
|
opt.dumpfile);
|
|
write_dump(0, crafted[0x06], crafted, opt.dumpfile, 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int smbios_decode(u8 *buf, const char *devmem, u32 flags)
|
|
{
|
|
u16 ver;
|
|
|
|
if (!checksum(buf, buf[0x05])
|
|
|| memcmp(buf + 0x10, "_DMI_", 5) != 0
|
|
|| !checksum(buf + 0x10, 0x0F))
|
|
return 0;
|
|
|
|
ver = (buf[0x06] << 8) + buf[0x07];
|
|
/* Some BIOS report weird SMBIOS version, fix that up */
|
|
switch (ver)
|
|
{
|
|
case 0x021F:
|
|
case 0x0221:
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
fprintf(stderr,
|
|
"SMBIOS version fixup (2.%d -> 2.%d).\n",
|
|
ver & 0xFF, 3);
|
|
ver = 0x0203;
|
|
break;
|
|
case 0x0233:
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
fprintf(stderr,
|
|
"SMBIOS version fixup (2.%d -> 2.%d).\n",
|
|
51, 6);
|
|
ver = 0x0206;
|
|
break;
|
|
}
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("SMBIOS %u.%u present.\n",
|
|
ver >> 8, ver & 0xFF);
|
|
|
|
dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C),
|
|
ver << 8, devmem, flags);
|
|
|
|
if (opt.flags & FLAG_DUMP_BIN)
|
|
{
|
|
u8 crafted[32];
|
|
|
|
memcpy(crafted, buf, 32);
|
|
overwrite_dmi_address(crafted + 0x10);
|
|
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("# Writing %d bytes to %s.\n", crafted[0x05],
|
|
opt.dumpfile);
|
|
write_dump(0, crafted[0x05], crafted, opt.dumpfile, 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int legacy_decode(u8 *buf, const char *devmem, u32 flags)
|
|
{
|
|
if (!checksum(buf, 0x0F))
|
|
return 0;
|
|
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("Legacy DMI %u.%u present.\n",
|
|
buf[0x0E] >> 4, buf[0x0E] & 0x0F);
|
|
|
|
dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C),
|
|
((buf[0x0E] & 0xF0) << 12) + ((buf[0x0E] & 0x0F) << 8),
|
|
devmem, flags);
|
|
|
|
if (opt.flags & FLAG_DUMP_BIN)
|
|
{
|
|
u8 crafted[16];
|
|
|
|
memcpy(crafted, buf, 16);
|
|
overwrite_dmi_address(crafted);
|
|
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("# Writing %d bytes to %s.\n", 0x0F,
|
|
opt.dumpfile);
|
|
write_dump(0, 0x0F, crafted, opt.dumpfile, 1);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Probe for EFI interface
|
|
*/
|
|
#define EFI_NOT_FOUND (-1)
|
|
#define EFI_NO_SMBIOS (-2)
|
|
static int address_from_efi(off_t *address)
|
|
{
|
|
FILE *efi_systab;
|
|
const char *filename;
|
|
char linebuf[64];
|
|
int ret;
|
|
|
|
*address = 0; /* Prevent compiler warning */
|
|
|
|
/*
|
|
* Linux up to 2.6.6: /proc/efi/systab
|
|
* Linux 2.6.7 and up: /sys/firmware/efi/systab
|
|
*/
|
|
if ((efi_systab = fopen(filename = "/sys/firmware/efi/systab", "r")) == NULL
|
|
&& (efi_systab = fopen(filename = "/proc/efi/systab", "r")) == NULL)
|
|
{
|
|
/* No EFI interface, fallback to memory scan */
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
ret = EFI_NO_SMBIOS;
|
|
while ((fgets(linebuf, sizeof(linebuf) - 1, efi_systab)) != NULL)
|
|
{
|
|
char *addrp = strchr(linebuf, '=');
|
|
*(addrp++) = '\0';
|
|
if (strcmp(linebuf, "SMBIOS3") == 0
|
|
|| strcmp(linebuf, "SMBIOS") == 0)
|
|
{
|
|
*address = strtoull(addrp, NULL, 0);
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("# %s entry point at 0x%08llx\n",
|
|
linebuf, (unsigned long long)*address);
|
|
ret = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (fclose(efi_systab) != 0)
|
|
perror(filename);
|
|
|
|
if (ret == EFI_NO_SMBIOS)
|
|
fprintf(stderr, "%s: SMBIOS entry point missing\n", filename);
|
|
return ret;
|
|
}
|
|
|
|
int main(int argc, char * const argv[])
|
|
{
|
|
int ret = 0; /* Returned value */
|
|
int found = 0;
|
|
off_t fp;
|
|
size_t size;
|
|
int efi;
|
|
u8 *buf;
|
|
|
|
/*
|
|
* We don't want stdout and stderr to be mixed up if both are
|
|
* redirected to the same file.
|
|
*/
|
|
setlinebuf(stdout);
|
|
setlinebuf(stderr);
|
|
|
|
if (sizeof(u8) != 1 || sizeof(u16) != 2 || sizeof(u32) != 4 || '\0' != 0)
|
|
{
|
|
fprintf(stderr, "%s: compiler incompatibility\n", argv[0]);
|
|
exit(255);
|
|
}
|
|
|
|
/* Set default option values */
|
|
opt.devmem = DEFAULT_MEM_DEV;
|
|
opt.flags = 0;
|
|
|
|
if (parse_command_line(argc, argv)<0)
|
|
{
|
|
ret = 2;
|
|
goto exit_free;
|
|
}
|
|
|
|
if (opt.flags & FLAG_HELP)
|
|
{
|
|
print_help();
|
|
goto exit_free;
|
|
}
|
|
|
|
if (opt.flags & FLAG_VERSION)
|
|
{
|
|
printf("%s\n", VERSION);
|
|
goto exit_free;
|
|
}
|
|
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("# dmidecode %s\n", VERSION);
|
|
|
|
/* Read from dump if so instructed */
|
|
if (opt.flags & FLAG_FROM_DUMP)
|
|
{
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("Reading SMBIOS/DMI data from file %s.\n",
|
|
opt.dumpfile);
|
|
if ((buf = mem_chunk(0, 0x20, opt.dumpfile)) == NULL)
|
|
{
|
|
ret = 1;
|
|
goto exit_free;
|
|
}
|
|
|
|
if (memcmp(buf, "_SM3_", 5) == 0)
|
|
{
|
|
if (smbios3_decode(buf, opt.dumpfile, 0))
|
|
found++;
|
|
}
|
|
else if (memcmp(buf, "_SM_", 4) == 0)
|
|
{
|
|
if (smbios_decode(buf, opt.dumpfile, 0))
|
|
found++;
|
|
}
|
|
else if (memcmp(buf, "_DMI_", 5) == 0)
|
|
{
|
|
if (legacy_decode(buf, opt.dumpfile, 0))
|
|
found++;
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
/*
|
|
* First try reading from sysfs tables. The entry point file could
|
|
* contain one of several types of entry points, so read enough for
|
|
* the largest one, then determine what type it contains.
|
|
*/
|
|
size = 0x20;
|
|
if (!(opt.flags & FLAG_NO_SYSFS)
|
|
&& (buf = read_file(0, &size, SYS_ENTRY_FILE)) != NULL)
|
|
{
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("Getting SMBIOS data from sysfs.\n");
|
|
if (size >= 24 && memcmp(buf, "_SM3_", 5) == 0)
|
|
{
|
|
if (smbios3_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET))
|
|
found++;
|
|
}
|
|
else if (size >= 31 && memcmp(buf, "_SM_", 4) == 0)
|
|
{
|
|
if (smbios_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET))
|
|
found++;
|
|
}
|
|
else if (size >= 15 && memcmp(buf, "_DMI_", 5) == 0)
|
|
{
|
|
if (legacy_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET))
|
|
found++;
|
|
}
|
|
|
|
if (found)
|
|
goto done;
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("Failed to get SMBIOS data from sysfs.\n");
|
|
}
|
|
|
|
/* Next try EFI (ia64, Intel-based Mac) */
|
|
efi = address_from_efi(&fp);
|
|
switch (efi)
|
|
{
|
|
case EFI_NOT_FOUND:
|
|
goto memory_scan;
|
|
case EFI_NO_SMBIOS:
|
|
ret = 1;
|
|
goto exit_free;
|
|
}
|
|
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("Found SMBIOS entry point in EFI, reading table from %s.\n",
|
|
opt.devmem);
|
|
if ((buf = mem_chunk(fp, 0x20, opt.devmem)) == NULL)
|
|
{
|
|
ret = 1;
|
|
goto exit_free;
|
|
}
|
|
|
|
if (memcmp(buf, "_SM3_", 5) == 0)
|
|
{
|
|
if (smbios3_decode(buf, opt.devmem, 0))
|
|
found++;
|
|
}
|
|
else if (memcmp(buf, "_SM_", 4) == 0)
|
|
{
|
|
if (smbios_decode(buf, opt.devmem, 0))
|
|
found++;
|
|
}
|
|
goto done;
|
|
|
|
memory_scan:
|
|
if (!(opt.flags & FLAG_QUIET))
|
|
printf("Scanning %s for entry point.\n", opt.devmem);
|
|
/* Fallback to memory scan (x86, x86_64) */
|
|
if ((buf = mem_chunk(0xF0000, 0x10000, opt.devmem)) == NULL)
|
|
{
|
|
ret = 1;
|
|
goto exit_free;
|
|
}
|
|
|
|
/* Look for a 64-bit entry point first */
|
|
for (fp = 0; fp <= 0xFFE0; fp += 16)
|
|
{
|
|
if (memcmp(buf + fp, "_SM3_", 5) == 0)
|
|
{
|
|
if (smbios3_decode(buf + fp, opt.devmem, 0))
|
|
{
|
|
found++;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If none found, look for a 32-bit entry point */
|
|
for (fp = 0; fp <= 0xFFF0; fp += 16)
|
|
{
|
|
if (memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0)
|
|
{
|
|
if (smbios_decode(buf + fp, opt.devmem, 0))
|
|
{
|
|
found++;
|
|
goto done;
|
|
}
|
|
}
|
|
else if (memcmp(buf + fp, "_DMI_", 5) == 0)
|
|
{
|
|
if (legacy_decode(buf + fp, opt.devmem, 0))
|
|
{
|
|
found++;
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (!found && !(opt.flags & FLAG_QUIET))
|
|
printf("# No SMBIOS nor DMI entry point found, sorry.\n");
|
|
|
|
free(buf);
|
|
exit_free:
|
|
free(opt.type);
|
|
|
|
return ret;
|
|
}
|