New upstream version 3.2

This commit is contained in:
Jörg Frings-Fürst 2018-10-03 08:05:58 +02:00
parent a1e93482d9
commit 7c994b7f6b
15 changed files with 1164 additions and 1928 deletions

5
.gitignore vendored
View File

@ -1,5 +0,0 @@
biosdecode
dmidecode
ownership
vpddecode
*.o

1614
CHANGELOG

File diff suppressed because it is too large Load Diff

View File

@ -134,7 +134,7 @@ uninstall-man :
install-doc :
$(INSTALL_DIR) $(DESTDIR)$(docdir)
$(INSTALL_DATA) README $(DESTDIR)$(docdir)
$(INSTALL_DATA) CHANGELOG $(DESTDIR)$(docdir)
$(INSTALL_DATA) NEWS $(DESTDIR)$(docdir)
$(INSTALL_DATA) AUTHORS $(DESTDIR)$(docdir)
uninstall-doc :

142
NEWS Normal file
View File

@ -0,0 +1,142 @@
Version 3.2 (Wed Sep 14 2018)
- [COMPATIBILITY] The UUID is now displayed using lowercase letters, per
RFC 4122 (#53569). You must ensure that any code parsing it is
case-insensitive.
- Support for SMBIOS 3.2.0. This includes new processor names, new socket
and port connector types, new system slot state and property, and support
for non-volatile memory (NVDIMM).
- Support for Redfish management controllers.
- A new command line option to query a specific structure by its handle.
- A new command line option to query the system family string.
- Support for 3 ThinkPad-specific structures (patch #9642).
- Support for HPE's new company name.
- Support UEFI on FreeBSD.
- Important bug fixes:
Fix firmware version of TPM device
Fix the HPE UEFI feature flag check
- (biosdecode) A new command line option to fully decode PIR information
(support request #109339).
Version 3.1 (Tue May 23 2017)
- Support for SMBIOS 3.1.0 and 3.1.1. This includes new chassis types, new
processor family names, new processor family upgrade names, and new slot
types, as well as support of larger BIOS ROM sizes and cache sizes, and a
new structure type (43, TPM Device.)
- A new command line option to query OEM strings.
- All error messages are now printed on stderr (#47274, #48158.)
- Several bug fixes related to 64-bit entry points (#50037 and more.)
- Important bug fixes:
#46176 (Unexpected end of file error)
#46066 (Crash with SIGBUS)
- Various minor fixes, improvements and cleanups.
Version 3.0 (Thu Sep 03 2015)
- Support for SMBIOS 3.0. This includes new chassis types, new
processor family names, new processor family upgrade names, new slot
types, and new memory device types.
- Support for the new 64-bit entry point (_SM3_) defined in SMBIOS 3.0.
- Support for the new kernel interface (as of Linux v4.2) as an
alternative to relying /dev/mem to access the entry point and DMI
table.
- Decoding of Acer-specific DMI type 170.
- Decoding of HP-specific DMI types 212, 219 and 233.
- Various minor fixes and output format cleanups.
Version 2.12 (Wed Apr 17 2013)
- Support of the SMBIOS 2.8.0 specification.
Version 2.11 (Wed Jan 19 2011)
- Support of the SMBIOS 2.7.0 specification:
- UEFI support
- Virtual machine flags in BIOS characteristics
- Limited support for the Management Controller Host Interface
- Various fixes that address stability.
Version 2.10 (Sun Nov 23 2008)
- Support for Solaris (x86 only, of course).
- Possibility to dump the SMBIOS/DMI table to a small binary file
(option --dump-bin).
- Possibility to read the SMBIOS/DMI table from such binary files
(option --from-dump).
- Support for SMBIOS 2.6. This includes new chassis types, new
processor family names, new processor family upgrade names, bus
address for system slots, and a new entry type for on-board devices,
amongst many other minor changes.
- Support for DMI entry type 31 (Boot integrity services).
- Many processor family names taken from the CIM Schema document.
- (vpddecode) No longer ask users to report broken records.
- (vpddecode) Fix --quiet option.
Version 2.9 (Mon Feb 26 2007)
- Support of the SMBIOS 2.5 specification. It adds many enumerated
values for recent hardware, as well as CPU core and thread count
reporting.
- Decoding of 3 HP-specific entries. More vendor-specific entries can
be supported later if vendors contribute code or documentation.
- Run-time detection of EFI, so that a single binary can support
Intel-based Macintosh machines and regular x86 machines.
- Better IA-64 support.
- Fixes to the decoding of individual fields, including the CPU
signature of recent CPU models.
- (biosdecode) Support of the FJKEYINF entry point type (for Fujitsu laptops).
- (vpddecode) The product name look-up table was dropped. It was unreliable
and a burden to maintain.
- biosdecode, ownership and vpddecode are no longer built on IA-64.
Version 2.8 (Sat Feb 04 2006)
- Option --string has four additional keywords available:
system-uuid, chassis-type, processor-family and processor-frequency.
These needed additional work because, technically speaking, they are
not DMI strings.
- IPMI interface type SSIF was added. This is a new interface type
defined by IPMI 2.0.
- (vpddecode) New --string option, much similar in spirit to
dmidecode's. Available keywords are bios-build-id, box-serial-number,
motherboard-serial-number, machine-type-model and bios-release-date.
- (vpddecode) 9 product names were added to the lookup table.
- A few bug fixes, cleanups and minor improvements all around the place.
Version 2.7 (Thu Aug 04 2005)
- New command line interface. For example, it is now possible to limit
the output of dmidecode to a given DMI type, or to extract a single
string from the DMI table. The documentation has been updated
accordingly.
- The default output of dmidecode was slightly modified to be more
easily readable by humans. This might break tools parsing its output.
Such tools may benefit from the new command line interface, although
this interface shouldn't be considered stable until version 2.8.
- (vpddecode) New command line interface.
- (vpddecode) 6 product names were added.
Version 2.6 (Mon Feb 28 2005)
- Fixes a 2 GB memory limit regression.
- Basic command-line handling.
- BeOS and Cygwin support.
Version 2.5 (Thu Nov 11 2004)
- Code cleanups.
- Compatibility fixes.
- Documentation updates.
Version 2.4 (Fri Mar 19 2004)
- Manual pages added.
- (vpddecode) Many improvements.
- A few fixes and minor improvements.
Version 2.3 (Sun Oct 19 2003)
- Support of x86_64 systems.
- Support of systems with 2 GB and more memory.
- Loads of bug fixes and corrections.
- New tool "vpddecode" added.
Version 2.2 (Fri Aug 08 2003)
- Support of IA-64 systems.
- Support of IBM and Fujitsu-Siemens laptops.
- Many minor bug fixes.
- New tool "ownership" added.
Version 2.1 (Tue Jun 10 2003)
- Support of the SMBIOS 2.3.4 specification.
- Better support of IPMI.
- Minor bugs fixed.
- Documentation added.

2
README
View File

@ -56,7 +56,7 @@ Each tool has a manual page, found in the "man" subdirectory. Manual pages
are installed by "make install". See these manual pages for command line
interface details and tool specific information.
For an history of the changes made to dmidecode, see the CHANGELOG file.
For an history of the changes made to dmidecode, see the NEWS file.
If you need help, your best chances are to visit the web page (see the
INSTALLATION section above) or to get in touch with the developers directly.

View File

@ -73,12 +73,16 @@ struct opt
{
const char *devmem;
unsigned int flags;
unsigned char pir;
};
static struct opt opt;
#define FLAG_VERSION (1 << 0)
#define FLAG_HELP (1 << 1)
#define PIR_SHORT 0
#define PIR_FULL 1
struct bios_entry {
const char *anchor;
size_t anchor_len; /* computed */
@ -351,7 +355,7 @@ static void pir_slot_number(u8 code)
if (code == 0)
printf(" on-board");
else
printf(" slot number %u", code);
printf(" slot %u", code);
}
static size_t pir_length(const u8 *p)
@ -359,16 +363,26 @@ static size_t pir_length(const u8 *p)
return WORD(p + 6);
}
static void pir_link_bitmap(char letter, const u8 *p)
{
if (p[0] == 0) /* Not connected */
return;
printf("\t\tINT%c#: Link 0x%02x, IRQ Bitmap", letter, p[0]);
pir_irqs(WORD(p + 1));
printf("\n");
}
static int pir_decode(const u8 *p, size_t len)
{
int i;
int i, n;
if (len < 32 || !checksum(p, WORD(p + 6)))
return 0;
printf("PCI Interrupt Routing %u.%u present.\n",
p[5], p[4]);
printf("\tRouter ID: %02x:%02x.%1x\n",
printf("\tRouter Device: %02x:%02x.%1x\n",
p[8], p[9]>>3, p[9] & 0x07);
printf("\tExclusive IRQs:");
pir_irqs(WORD(p + 10));
@ -380,38 +394,19 @@ static int pir_decode(const u8 *p, size_t len)
printf("\tMiniport Data: 0x%08X\n",
DWORD(p + 16));
for (i = 1; i <= (WORD(p + 6) - 32) / 16; i++)
n = (len - 32) / 16;
for (i = 1, p += 32; i <= n; i++, p += 16)
{
printf("\tSlot Entry %u: ID %02x:%02x,",
i, p[(i + 1) * 16], p[(i + 1) * 16 + 1] >> 3);
pir_slot_number(p[(i + 1) * 16 + 14]);
printf("\tDevice: %02x:%02x,", p[0], p[1] >> 3);
pir_slot_number(p[14]);
printf("\n");
/* printf("\tSlot Entry %u\n", i);
printf("\t\tID: %02x:%02x\n",
p[(i + 1) * 16], p[(i + 1) * 16 + 1] >> 3);
printf("\t\tLink Value for INTA#: %u\n",
p[(i + 1) * 16 + 2]);
printf("\t\tIRQ Bitmap for INTA#:");
pir_irqs(WORD(p + (i + 1) * 16 + 3));
printf("\n");
printf("\t\tLink Value for INTB#: %u\n",
p[(i + 1) * 16 + 5]);
printf("\t\tIRQ Bitmap for INTB#:");
pir_irqs(WORD(p + (i + 1) * 16 + 6));
printf("\n");
printf("\t\tLink Value for INTC#: %u\n",
p[(i + 1) * 16 + 8]);
printf("\t\tIRQ Bitmap for INTC#:");
pir_irqs(WORD(p + (i + 1) * 16 + 9));
printf("\n");
printf("\t\tLink Value for INTD#: %u\n",
p[(i + 1) * 16 + 11]);
printf("\t\tIRQ Bitmap for INTD#:");
pir_irqs(WORD(p + (i + 1) * 16 + 12));
printf("\n");
printf("\t\tSlot Number:");
pir_slot_number(p[(i + 1) * 16 + 14]);
printf("\n");*/
if (opt.pir == PIR_FULL)
{
pir_link_bitmap('A', p + 2);
pir_link_bitmap('B', p + 5);
pir_link_bitmap('C', p + 8);
pir_link_bitmap('D', p + 11);
}
}
return 1;
@ -616,6 +611,7 @@ static int parse_command_line(int argc, char * const argv[])
const char *optstring = "d:hV";
struct option longopts[] = {
{ "dev-mem", required_argument, NULL, 'd' },
{ "pir", required_argument, NULL, 'P' },
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ NULL, 0, NULL, 0 }
@ -627,6 +623,10 @@ static int parse_command_line(int argc, char * const argv[])
case 'd':
opt.devmem = optarg;
break;
case 'P':
if (strcmp(optarg, "full") == 0)
opt.pir = PIR_FULL;
break;
case 'h':
opt.flags |= FLAG_HELP;
break;
@ -646,6 +646,7 @@ static void print_help(void)
"Usage: biosdecode [OPTIONS]\n"
"Options are:\n"
" -d, --dev-mem FILE Read memory from device FILE (default: " DEFAULT_MEM_DEV ")\n"
" --pir full Decode the details of the PCI IRQ routing table\n"
" -h, --help Display this help text and exit\n"
" -V, --version Display the version and exit\n";

View File

@ -2,7 +2,7 @@
* DMI Decode
*
* Copyright (C) 2000-2002 Alan Cox <alan@redhat.com>
* Copyright (C) 2002-2017 Jean Delvare <jdelvare@suse.de>
* Copyright (C) 2002-2018 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
@ -25,7 +25,7 @@
* 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,
* Management BIOS Reference Specification, Version 3.2.0" document,
* available from http://www.dmtf.org/standards/smbios.
*
* Note to contributors:
@ -56,6 +56,8 @@
* - "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/
* - "RedFish Host Interface Specification" (DMTF DSP0270)
* https://www.dmtf.org/sites/default/files/DSP0270_1.0.1.pdf
*/
#include <stdio.h>
@ -63,6 +65,12 @@
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#ifdef __FreeBSD__
#include <errno.h>
#include <kenv.h>
#endif
#include "version.h"
#include "config.h"
@ -75,7 +83,7 @@
#define out_of_spec "<OUT OF SPEC>"
static const char *bad_index = "<BAD INDEX>";
#define SUPPORTED_SMBIOS_VER 0x030101
#define SUPPORTED_SMBIOS_VER 0x030200
#define FLAG_NO_FILE_OFFSET (1 << 0)
#define FLAG_STOP_AT_EOT (1 << 1)
@ -440,11 +448,11 @@ static void dmi_system_uuid(const u8 *p, u16 ver)
* for older versions.
*/
if (ver >= 0x0206)
printf("%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
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",
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]);
}
@ -878,6 +886,7 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver)
{ 0xCC, "z/Architecture" },
{ 0xCD, "Core i5" },
{ 0xCE, "Core i3" },
{ 0xCF, "Core i9" },
{ 0xD2, "C7-M" },
{ 0xD3, "C7-D" },
@ -1085,7 +1094,7 @@ static void dmi_processor_id(const struct dmi_header *h, const char *prefix)
|| (type >= 0xA1 && type <= 0xB3) /* Intel */
|| type == 0xB5 /* Intel */
|| (type >= 0xB9 && type <= 0xC7) /* Intel */
|| (type >= 0xCD && type <= 0xCE) /* Intel */
|| (type >= 0xCD && type <= 0xCF) /* Intel */
|| (type >= 0xD2 && type <= 0xDB) /* VIA, Intel */
|| (type >= 0xDD && type <= 0xE0)) /* Intel */
sig = 1;
@ -1269,10 +1278,14 @@ static const char *dmi_processor_upgrade(u8 code)
"Socket BGA1515",
"Socket LGA3647-1",
"Socket SP3",
"Socket SP3r2" /* 0x38 */
"Socket SP3r2",
"Socket LGA2066",
"Socket BGA1392",
"Socket BGA1510",
"Socket BGA1528" /* 0x3C */
};
if (code >= 0x01 && code <= 0x38)
if (code >= 0x01 && code <= 0x3C)
return upgrade[code - 0x01];
return out_of_spec;
}
@ -1685,7 +1698,8 @@ static const char *dmi_port_connector_type(u8 code)
"Mini Jack (headphones)",
"BNC",
"IEEE 1394",
"SAS/SATA Plug Receptacle" /* 0x22 */
"SAS/SATA Plug Receptacle",
"USB Type-C Receptacle" /* 0x23 */
};
static const char *type_0xA0[] = {
"PC-98", /* 0xA0 */
@ -1695,7 +1709,7 @@ static const char *dmi_port_connector_type(u8 code)
"PC-98 Full" /* 0xA4 */
};
if (code <= 0x22)
if (code <= 0x23)
return type[code];
if (code >= 0xA0 && code <= 0xA4)
return type_0xA0[code - 0xA0];
@ -1870,10 +1884,11 @@ static const char *dmi_slot_current_usage(u8 code)
"Other", /* 0x01 */
"Unknown",
"Available",
"In Use" /* 0x04 */
"In Use",
"Unavailable" /* 0x05 */
};
if (code >= 0x01 && code <= 0x04)
if (code >= 0x01 && code <= 0x05)
return usage[code - 0x01];
return out_of_spec;
}
@ -1958,7 +1973,8 @@ static void dmi_slot_characteristics(u8 code1, u8 code2, const char *prefix)
static const char *characteristics2[] = {
"PME signal is supported", /* 0 */
"Hot-plug devices are supported",
"SMBus signal is supported" /* 2 */
"SMBus signal is supported",
"PCIe slot bifurcation is supported" /* 3 */
};
if (code1 & (1 << 0))
@ -1973,7 +1989,7 @@ static void dmi_slot_characteristics(u8 code1, u8 code2, const char *prefix)
for (i = 1; i <= 7; i++)
if (code1 & (1 << i))
printf("%s%s\n", prefix, characteristics1[i - 1]);
for (i = 0; i <= 2; i++)
for (i = 0; i <= 3; i++)
if (code2 & (1 << i))
printf("%s%s\n", prefix, characteristics2[i]);
}
@ -2502,6 +2518,79 @@ static void dmi_memory_device_speed(u16 code)
printf(" %u MT/s", code);
}
static void dmi_memory_technology(u8 code)
{
/* 7.18.6 */
static const char * const technology[] = {
"Other", /* 0x01 */
"Unknown",
"DRAM",
"NVDIMM-N",
"NVDIMM-F",
"NVDIMM-P",
"Intel persistent memory" /* 0x07 */
};
if (code >= 0x01 && code <= 0x07)
printf(" %s", technology[code - 0x01]);
else
printf(" %s", out_of_spec);
}
static void dmi_memory_operating_mode_capability(u16 code)
{
/* 7.18.7 */
static const char * const mode[] = {
"Other", /* 1 */
"Unknown",
"Volatile memory",
"Byte-accessible persistent memory",
"Block-accessible persistent memory" /* 5 */
};
if ((code & 0xFFFE) == 0)
printf(" None");
else {
int i;
for (i = 1; i <= 5; i++)
if (code & (1 << i))
printf(" %s", mode[i - 1]);
}
}
static void dmi_memory_manufacturer_id(u16 code)
{
/* 7.18.8 */
/* 7.18.10 */
/* LSB is 7-bit Odd Parity number of continuation codes */
if (code == 0)
printf(" Unknown");
else
printf(" Bank %d, Hex 0x%02X", (code & 0x7F) + 1, code >> 8);
}
static void dmi_memory_product_id(u16 code)
{
/* 7.18.9 */
/* 7.18.11 */
if (code == 0)
printf(" Unknown");
else
printf(" 0x%04X", code);
}
static void dmi_memory_size(u64 code)
{
/* 7.18.12 */
/* 7.18.13 */
if (code.h == 0xFFFFFFFF && code.l == 0xFFFFFFFF)
printf(" Unknown");
else if (code.h == 0x0 && code.l == 0x0)
printf(" None");
else
dmi_print_memory_size(code, 0);
}
/*
* 7.19 32-bit Memory Error Information (Type 18)
*/
@ -3313,11 +3402,379 @@ static const char *dmi_management_controller_host_type(u8 code)
if (code >= 0x02 && code <= 0x08)
return type[code - 0x02];
if (code <= 0x3F)
return "MCTP";
if (code == 0x40)
return "Network";
if (code == 0xF0)
return "OEM";
return out_of_spec;
}
/*
* 7.43.2: Protocol Record Types
*/
static const char *dmi_protocol_record_type(u8 type)
{
const char *protocol[] = {
"Reserved", /* 0x0 */
"Reserved",
"IPMI",
"MCTP",
"Redfish over IP", /* 0x4 */
};
if (type <= 0x4)
return protocol[type];
if (type == 0xF0)
return "OEM";
return out_of_spec;
}
/*
* DSP0270: 8.6: Protocol IP Assignment types
*/
static const char *dmi_protocol_assignment_type(u8 type)
{
const char *assignment[] = {
"Unknown", /* 0x0 */
"Static",
"DHCP",
"AutoConf",
"Host Selected", /* 0x4 */
};
if (type <= 0x4)
return assignment[type];
return out_of_spec;
}
/*
* DSP0270: 8.6: Protocol IP Address type
*/
static const char *dmi_address_type(u8 type)
{
const char *addressformat[] = {
"Unknown", /* 0x0 */
"IPv4",
"IPv6", /* 0x2 */
};
if (type <= 0x2)
return addressformat[type];
return out_of_spec;
}
/*
* DSP0270: 8.6 Protocol Address decode
*/
static const char *dmi_address_decode(u8 *data, char *storage, u8 addrtype)
{
if (addrtype == 0x1) /* IPv4 */
return inet_ntop(AF_INET, data, storage, 64);
if (addrtype == 0x2) /* IPv6 */
return inet_ntop(AF_INET6, data, storage, 64);
return out_of_spec;
}
/*
* DSP0270: 8.5: Parse the protocol record format
*/
static void dmi_parse_protocol_record(const char *prefix, u8 *rec)
{
u8 rid;
u8 rlen;
u8 *rdata;
char buf[64];
u8 assign_val;
u8 addrtype;
u8 hlen;
const char *addrstr;
const char *hname;
/* DSP0270: 8.5: Protocol Identifier */
rid = rec[0x0];
/* DSP0270: 8.5: Protocol Record Length */
rlen = rec[0x1];
/* DSP0270: 8.5: Protocol Record Data */
rdata = &rec[0x2];
printf("%s\tProtocol ID: %02x (%s)\n", prefix, rid,
dmi_protocol_record_type(rid));
/*
* Don't decode anything other than Redfish for now
* Note 0x4 is Redfish over IP in 7.43.2
* and DSP0270: 8.5
*/
if (rid != 0x4)
return;
/*
* Ensure that the protocol record is of sufficient length
* For RedFish that means rlen must be at least 91 bytes
* other protcols will need different length checks
*/
if (rlen < 91)
return;
/*
* DSP0270: 8.6: Redfish Over IP Service UUID
* Note: ver is hardcoded to 0x311 here just for
* convenience. It could get passed from the SMBIOS
* header, but that's a lot of passing of pointers just
* to get that info, and the only thing it is used for is
* to determine the endianess of the field. Since we only
* do this parsing on versions of SMBIOS after 3.1.1, and the
* endianess of the field is always little after version 2.6.0
* we can just pick a sufficiently recent version here.
*/
printf("%s\t\tService UUID: ", prefix);
dmi_system_uuid(&rdata[0], 0x311);
printf("\n");
/*
* DSP0270: 8.6: Redfish Over IP Host IP Assignment Type
* Note, using decimal indicies here, as the DSP0270
* uses decimal, so as to make it more comparable
*/
assign_val = rdata[16];
printf("%s\t\tHost IP Assignment Type: %s\n", prefix,
dmi_protocol_assignment_type(assign_val));
/* DSP0270: 8.6: Redfish Over IP Host Address format */
addrtype = rdata[17];
addrstr = dmi_address_type(addrtype);
printf("%s\t\tHost IP Address Format: %s\n", prefix,
addrstr);
/* DSP0270: 8.6 IP Assignment types */
/* We only use the Host IP Address and Mask if the assignment type is static */
if (assign_val == 0x1 || assign_val == 0x3)
{
/* DSP0270: 8.6: the Host IPv[4|6] Address */
printf("%s\t\t%s Address: %s\n", prefix, addrstr,
dmi_address_decode(&rdata[18], buf, addrtype));
/* DSP0270: 8.6: Prints the Host IPv[4|6] Mask */
printf("%s\t\t%s Mask: %s\n", prefix, addrstr,
dmi_address_decode(&rdata[34], buf, addrtype));
}
/* DSP0270: 8.6: Get the Redfish Service IP Discovery Type */
assign_val = rdata[50];
/* Redfish Service IP Discovery type mirrors Host IP Assignment type */
printf("%s\t\tRedfish Service IP Discovery Type: %s\n", prefix,
dmi_protocol_assignment_type(assign_val));
/* DSP0270: 8.6: Get the Redfish Service IP Address Format */
addrtype = rdata[51];
addrstr = dmi_address_type(addrtype);
printf("%s\t\tRedfish Service IP Address Format: %s\n", prefix,
addrstr);
if (assign_val == 0x1 || assign_val == 0x3)
{
u16 port;
u32 vlan;
/* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Address */
printf("%s\t\t%s Redfish Service Address: %s\n", prefix,
addrstr, dmi_address_decode(&rdata[52], buf,
addrtype));
/* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Mask */
printf("%s\t\t%s Redfish Service Mask: %s\n", prefix,
addrstr, dmi_address_decode(&rdata[68], buf,
addrtype));
/* DSP0270: 8.6: Redfish vlan and port info */
port = WORD(&rdata[84]);
vlan = DWORD(&rdata[86]);
printf("%s\t\tRedfish Service Port: %hu\n", prefix, port);
printf("%s\t\tRedfish Service Vlan: %u\n", prefix, vlan);
}
/* DSP0270: 8.6: Redfish host length and name */
hlen = rdata[90];
/*
* DSP0270: 8.6: The length of the host string + 91 (the minimum
* size of a protocol record) cannot exceed the record length
* (rec[0x1])
*/
hname = (const char *)&rdata[91];
if (hlen + 91 > rlen)
{
hname = out_of_spec;
hlen = strlen(out_of_spec);
}
printf("%s\t\tRedfish Service Hostname: %*s\n", prefix, hlen, hname);
}
/*
* DSP0270: 8.3: Device type ennumeration
*/
static const char *dmi_parse_device_type(u8 type)
{
const char *devname[] = {
"USB", /* 0x2 */
"PCI/PCIe", /* 0x3 */
};
if (type >= 0x2 && type <= 0x3)
return devname[type - 0x2];
if (type >= 0x80)
return "OEM";
return out_of_spec;
}
static void dmi_parse_controller_structure(const struct dmi_header *h,
const char *prefix)
{
int i;
u8 *data = h->data;
/* Host interface type */
u8 type;
/* Host Interface specific data length */
u8 len;
u8 count;
u32 total_read;
/*
* Minimum length of this struct is 0xB bytes
*/
if (h->length < 0xB)
return;
/*
* Also need to ensure that the interface specific data length
* plus the size of the structure to that point don't exceed
* the defined length of the structure, or we will overrun its
* bounds
*/
len = data[0x5];
total_read = len + 0x6;
if (total_read > h->length)
return;
type = data[0x4];
printf("%sHost Interface Type: %s\n", prefix,
dmi_management_controller_host_type(type));
/*
* The following decodes are code for Network interface host types only
* As defined in DSP0270
*/
if (type != 0x40)
return;
if (len != 0)
{
/* DSP0270: 8.3 Table 2: Device Type */
type = data[0x6];
printf("%sDevice Type: %s\n", prefix,
dmi_parse_device_type(type));
if (type == 0x2 && len >= 5)
{
/* USB Device Type - need at least 6 bytes */
u8 *usbdata = &data[0x7];
/* USB Device Descriptor: idVendor */
printf("%s\tidVendor: 0x%04x\n", prefix,
WORD(&usbdata[0x0]));
/* USB Device Descriptor: idProduct */
printf("%s\tidProduct: 0x%04x\n", prefix,
WORD(&usbdata[0x2]));
/*
* USB Serial number is here, but its useless, don't
* bother decoding it
*/
}
else if (type == 0x3 && len >= 9)
{
/* PCI Device Type - Need at least 8 bytes */
u8 *pcidata = &data[0x7];
/* PCI Device Descriptor: VendorID */
printf("%s\tVendorID: 0x%04x\n", prefix,
WORD(&pcidata[0x0]));
/* PCI Device Descriptor: DeviceID */
printf("%s\tDeviceID: 0x%04x\n", prefix,
WORD(&pcidata[0x2]));
/* PCI Device Descriptor: PCI SubvendorID */
printf("%s\tSubVendorID: 0x%04x\n", prefix,
WORD(&pcidata[0x4]));
/* PCI Device Descriptor: PCI SubdeviceID */
printf("%s\tSubDeviceID: 0x%04x\n", prefix,
WORD(&pcidata[0x6]));
}
else if (type == 0x4 && len >= 5)
{
/* OEM Device Type - Need at least 4 bytes */
u8 *oemdata = &data[0x7];
/* OEM Device Descriptor: IANA */
printf("%s\tVendor ID: 0x%02x:0x%02x:0x%02x:0x%02x\n",
prefix, oemdata[0x0], oemdata[0x1],
oemdata[0x2], oemdata[0x3]);
}
/* Don't mess with unknown types for now */
}
/*
* DSP0270: 8.2 and 8.5: Protocol record count and protocol records
* Move to the Protocol Count.
*/
data = &data[total_read];
/*
* We've validated up to 0x6 + len bytes, but we need to validate
* the next byte below, the count value.
*/
total_read++;
if (total_read > h->length)
{
printf("%s\tWARN: Total read length %d exceeds total structure length %d\n",
prefix, total_read, h->length);
return;
}
/* Get the protocol records count */
count = data[0x0];
if (count)
{
u8 *rec = &data[0x1];
for (i = 0; i < count; i++)
{
/*
* Need to ensure that this record doesn't overrun
* the total length of the type 42 struct. Note the +2
* is added for the two leading bytes of a protocol
* record representing the type and length bytes.
*/
total_read += rec[1] + 2;
if (total_read > h->length)
{
printf("%s\tWARN: Total read length %d exceeds total structure length %d\n",
prefix, total_read, h->length);
return;
}
dmi_parse_protocol_record(prefix, rec);
/*
* DSP0270: 8.6
* Each record is rec[1] bytes long, starting at the
* data byte immediately following the length field.
* That means we need to add the byte for the rec id,
* the byte for the length field, and the value of the
* length field itself.
*/
rec += rec[1] + 2;
}
}
}
/*
* 7.44 TPM Device (Type 43)
*/
@ -3889,7 +4346,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
printf("%u", data[0x1B] & 0x0F);
printf("\n");
if (h->length < 0x22) break;
printf("\tConfigured Clock Speed:");
printf("\tConfigured Memory Speed:");
dmi_memory_device_speed(WORD(data + 0x20));
printf("\n");
if (h->length < 0x28) break;
@ -3902,6 +4359,43 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
printf("\tConfigured Voltage:");
dmi_memory_voltage_value(WORD(data + 0x26));
printf("\n");
if (h->length < 0x34) break;
printf("\tMemory Technology:");
dmi_memory_technology(data[0x28]);
printf("\n");
printf("\tMemory Operating Mode Capability:");
dmi_memory_operating_mode_capability(WORD(data + 0x29));
printf("\n");
printf("\tFirmware Version: %s\n",
dmi_string(h, data[0x2B]));
printf("\tModule Manufacturer ID:");
dmi_memory_manufacturer_id(WORD(data + 0x2C));
printf("\n");
printf("\tModule Product ID:");
dmi_memory_product_id(WORD(data + 0x2E));
printf("\n");
printf("\tMemory Subsystem Controller Manufacturer ID:");
dmi_memory_manufacturer_id(WORD(data + 0x30));
printf("\n");
printf("\tMemory Subsystem Controller Product ID:");
dmi_memory_product_id(WORD(data + 0x32));
printf("\n");
if (h->length < 0x3C) break;
printf("\tNon-Volatile Size:");
dmi_memory_size(QWORD(data + 0x34));
printf("\n");
if (h->length < 0x44) break;
printf("\tVolatile Size:");
dmi_memory_size(QWORD(data + 0x3C));
printf("\n");
if (h->length < 0x4C) break;
printf("\tCache Size:");
dmi_memory_size(QWORD(data + 0x44));
printf("\n");
if (h->length < 0x54) break;
printf("\tLogical Size:");
dmi_memory_size(QWORD(data + 0x4C));
printf("\n");
break;
case 18: /* 7.19 32-bit Memory Error Information */
@ -4467,22 +4961,27 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
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 */
if (ver < 0x0302)
{
printf("\tVendor ID: 0x%02X%02X%02X%02X\n",
data[0x05], data[0x06], data[0x07],
data[0x08]);
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]);
}
}
else
dmi_parse_controller_structure(h, "\t");
break;
case 43: /* 7.44 TPM Device */
@ -4506,7 +5005,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
case 0x02:
printf("\tFirmware Revision: %u.%u\n",
DWORD(data + 0x0A) >> 16,
DWORD(data + 0x0A) && 0xFF);
DWORD(data + 0x0A) & 0xFFFF);
/*
* We skip the next 4 bytes, as their
* format is not standardized and their
@ -4617,6 +5116,7 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
to_dmi_header(&h, data);
display = ((opt.type == NULL || opt.type[h.type])
&& (opt.handle == ~0U || opt.handle == h.handle)
&& !((opt.flags & FLAG_QUIET) && (h.type == 126 || h.type == 127))
&& !opt.string);
@ -4638,6 +5138,7 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
}
break;
}
i++;
/* In quiet mode, stop decoding at end of table marker */
if ((opt.flags & FLAG_QUIET) && h.type == 127)
@ -4648,6 +5149,22 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
printf("Handle 0x%04X, DMI type %d, %d bytes\n",
h.handle, h.type, h.length);
/* 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;
/* Make sure the whole structure fits in the table */
if ((unsigned long)(next - buf) > len)
{
if (display && !(opt.flags & FLAG_QUIET))
printf("\t<TRUNCATED>\n\n");
data = next;
break;
}
/* assign vendor for vendor-specific decodes later */
if (h.type == 1 && h.length >= 5)
dmi_set_vendor(dmi_string(&h, data[0x04]));
@ -4656,33 +5173,21 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
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)
{
if (opt.flags & FLAG_DUMP)
{
dmi_dump(&h, "\t");
printf("\n");
}
else
dmi_decode(&h, ver);
dmi_dump(&h, "\t");
printf("\n");
}
else if (!(opt.flags & FLAG_QUIET))
printf("\t<TRUNCATED>\n\n");
else
dmi_decode(&h, ver);
}
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))
@ -4812,6 +5317,15 @@ static int smbios3_decode(u8 *buf, const char *devmem, u32 flags)
u32 ver;
u64 offset;
/* Don't let checksum run beyond the buffer */
if (buf[0x06] > 0x20)
{
fprintf(stderr,
"Entry point length too large (%u bytes, expected %u).\n",
(unsigned int)buf[0x06], 0x18U);
return 0;
}
if (!checksum(buf, buf[0x06]))
return 0;
@ -4850,6 +5364,15 @@ static int smbios_decode(u8 *buf, const char *devmem, u32 flags)
{
u16 ver;
/* Don't let checksum run beyond the buffer */
if (buf[0x05] > 0x20)
{
fprintf(stderr,
"Entry point length too large (%u bytes, expected %u).\n",
(unsigned int)buf[0x05], 0x1FU);
return 0;
}
if (!checksum(buf, buf[0x05])
|| memcmp(buf + 0x10, "_DMI_", 5) != 0
|| !checksum(buf + 0x10, 0x0F))
@ -4934,13 +5457,19 @@ static int legacy_decode(u8 *buf, const char *devmem, u32 flags)
#define EFI_NO_SMBIOS (-2)
static int address_from_efi(off_t *address)
{
#if defined(__linux__)
FILE *efi_systab;
const char *filename;
char linebuf[64];
#elif defined(__FreeBSD__)
char addrstr[KENV_MVALLEN + 1];
#endif
const char *eptype;
int ret;
*address = 0; /* Prevent compiler warning */
#if defined(__linux__)
/*
* Linux up to 2.6.6: /proc/efi/systab
* Linux 2.6.7 and up: /sys/firmware/efi/systab
@ -4960,9 +5489,7 @@ static int address_from_efi(off_t *address)
|| 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);
eptype = linebuf;
ret = 0;
break;
}
@ -4972,6 +5499,31 @@ static int address_from_efi(off_t *address)
if (ret == EFI_NO_SMBIOS)
fprintf(stderr, "%s: SMBIOS entry point missing\n", filename);
#elif defined(__FreeBSD__)
/*
* On FreeBSD, SMBIOS anchor base address in UEFI mode is exposed
* via kernel environment:
* https://svnweb.freebsd.org/base?view=revision&revision=307326
*/
ret = kenv(KENV_GET, "hint.smbios.0.mem", addrstr, sizeof(addrstr));
if (ret == -1)
{
if (errno != ENOENT)
perror("kenv");
return EFI_NOT_FOUND;
}
*address = strtoull(addrstr, NULL, 0);
eptype = "SMBIOS";
ret = 0;
#else
ret = EFI_NOT_FOUND;
#endif
if (ret == 0 && !(opt.flags & FLAG_QUIET))
printf("# %s entry point at 0x%08llx\n",
eptype, (unsigned long long)*address);
return ret;
}
@ -5000,6 +5552,7 @@ int main(int argc, char * const argv[])
/* Set default option values */
opt.devmem = DEFAULT_MEM_DEV;
opt.flags = 0;
opt.handle = ~0U;
if (parse_command_line(argc, argv)<0)
{

477
dmioem.c
View File

@ -33,8 +33,11 @@
enum DMI_VENDORS
{
VENDOR_UNKNOWN,
VENDOR_HP,
VENDOR_ACER,
VENDOR_HP,
VENDOR_HPE,
VENDOR_IBM,
VENDOR_LENOVO,
};
static enum DMI_VENDORS dmi_vendor = VENDOR_UNKNOWN;
@ -56,178 +59,16 @@ void dmi_set_vendor(const char *s)
while (len && s[len - 1] == ' ')
len--;
if (strncmp(s, "HP", len) == 0 || strncmp(s, "Hewlett-Packard", len) == 0)
dmi_vendor = VENDOR_HP;
else if (strncmp(s, "Acer", len) == 0)
if (strncmp(s, "Acer", len) == 0)
dmi_vendor = VENDOR_ACER;
}
/*
* HP-specific data structures are decoded here.
*
* Code contributed by John Cagle and Tyler Bell.
*/
static void dmi_print_hp_net_iface_rec(u8 id, u8 bus, u8 dev, const u8 *mac)
{
/* Some systems do not provide an id. nic_ctr provides an artificial
* id, and assumes the records will be provided "in order". Also,
* using 0xFF marker is not future proof. 256 NICs is a lot, but
* 640K ought to be enough for anybody(said no one, ever).
* */
static u8 nic_ctr;
if (id == 0xFF)
id = ++nic_ctr;
if (dev == 0x00 && bus == 0x00)
printf("\tNIC %d: Disabled\n", id);
else if (dev == 0xFF && bus == 0xFF)
printf("\tNIC %d: Not Installed\n", id);
else
{
printf("\tNIC %d: PCI device %02x:%02x.%x, "
"MAC address %02X:%02X:%02X:%02X:%02X:%02X\n",
id, bus, dev >> 3, dev & 7,
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
}
static int dmi_decode_hp(const struct dmi_header *h)
{
u8 *data = h->data;
int nic, ptr;
u32 feat;
switch (h->type)
{
case 204:
/*
* Vendor Specific: HP ProLiant System/Rack Locator
*/
printf("HP ProLiant System/Rack Locator\n");
if (h->length < 0x0B) break;
printf("\tRack Name: %s\n", dmi_string(h, data[0x04]));
printf("\tEnclosure Name: %s\n", dmi_string(h, data[0x05]));
printf("\tEnclosure Model: %s\n", dmi_string(h, data[0x06]));
printf("\tEnclosure Serial: %s\n", dmi_string(h, data[0x0A]));
printf("\tEnclosure Bays: %d\n", data[0x08]);
printf("\tServer Bay: %s\n", dmi_string(h, data[0x07]));
printf("\tBays Filled: %d\n", data[0x09]);
break;
case 209:
case 221:
/*
* Vendor Specific: HP ProLiant NIC MAC Information
*
* This prints the BIOS NIC number,
* PCI bus/device/function, and MAC address
*
* Type 209:
* Offset | Name | Width | Description
* -------------------------------------
* 0x00 | Type | BYTE | 0xD1, MAC Info
* 0x01 | Length | BYTE | Length of structure
* 0x02 | Handle | WORD | Unique handle
* 0x04 | Dev No | BYTE | PCI Device/Function No
* 0x05 | Bus No | BYTE | PCI Bus
* 0x06 | MAC | 6B | MAC addr
* 0x0C | NIC #2 | 8B | Repeat 0x04-0x0B
*
* Type 221: is deprecated in the latest docs
*/
printf(h->type == 221 ?
"HP BIOS iSCSI NIC PCI and MAC Information\n" :
"HP BIOS PXE NIC PCI and MAC Information\n");
nic = 1;
ptr = 4;
while (h->length >= ptr + 8)
{
dmi_print_hp_net_iface_rec(nic,
data[ptr + 0x01],
data[ptr],
&data[ptr + 0x02]);
nic++;
ptr += 8;
}
break;
case 233:
/*
* Vendor Specific: HP ProLiant NIC MAC Information
*
* This prints the BIOS NIC number,
* PCI bus/device/function, and MAC address
*
* Offset | Name | Width | Description
* -------------------------------------
* 0x00 | Type | BYTE | 0xE9, NIC structure
* 0x01 | Length | BYTE | Length of structure
* 0x02 | Handle | WORD | Unique handle
* 0x04 | Grp No | WORD | 0 for single segment
* 0x06 | Bus No | BYTE | PCI Bus
* 0x07 | Dev No | BYTE | PCI Device/Function No
* 0x08 | MAC | 32B | MAC addr padded w/ 0s
* 0x28 | Port No| BYTE | Each NIC maps to a Port
*/
printf("HP BIOS PXE NIC PCI and MAC Information\n");
if (h->length < 0x0E) break;
/* If the record isn't long enough, we don't have an ID
* use 0xFF to use the internal counter.
* */
nic = h->length > 0x28 ? data[0x28] : 0xFF;
dmi_print_hp_net_iface_rec(nic, data[0x06], data[0x07],
&data[0x08]);
break;
case 212:
/*
* Vendor Specific: HP 64-bit CRU Information
*
* Source: hpwdt kernel driver
*/
printf("HP 64-bit CRU Information\n");
if (h->length < 0x18) break;
printf("\tSignature: 0x%08x", DWORD(data + 0x04));
if (is_printable(data + 0x04, 4))
printf(" (%c%c%c%c)", data[0x04], data[0x05],
data[0x06], data[0x07]);
printf("\n");
if (DWORD(data + 0x04) == 0x55524324)
{
u64 paddr = QWORD(data + 0x08);
paddr.l += DWORD(data + 0x14);
if (paddr.l < DWORD(data + 0x14))
paddr.h++;
printf("\tPhysical Address: 0x%08x%08x\n",
paddr.h, paddr.l);
printf("\tLength: 0x%08x\n", DWORD(data + 0x10));
}
break;
case 219:
/*
* Vendor Specific: HP ProLiant Information
*
* Source: hpwdt kernel driver
*/
printf("HP ProLiant Information\n");
if (h->length < 0x08) break;
printf("\tPower Features: 0x%08x\n", DWORD(data + 0x04));
if (h->length < 0x0C) break;
printf("\tOmega Features: 0x%08x\n", DWORD(data + 0x08));
if (h->length < 0x14) break;
feat = DWORD(data + 0x10);
printf("\tMisc. Features: 0x%08x\n", feat);
printf("\t\tiCRU: %s\n", feat & 0x0001 ? "Yes" : "No");
printf("\t\tUEFI: %s\n", feat & 0x0408 ? "Yes" : "No");
break;
default:
return 0;
}
return 1;
else if (strncmp(s, "HP", len) == 0 || strncmp(s, "Hewlett-Packard", len) == 0)
dmi_vendor = VENDOR_HP;
else if (strncmp(s, "HPE", len) == 0 || strncmp(s, "Hewlett Packard Enterprise", len) == 0)
dmi_vendor = VENDOR_HPE;
else if (strncmp(s, "IBM", len) == 0)
dmi_vendor = VENDOR_IBM;
else if (strncmp(s, "LENOVO", len) == 0)
dmi_vendor = VENDOR_LENOVO;
}
/*
@ -272,6 +113,294 @@ static int dmi_decode_acer(const struct dmi_header *h)
return 1;
}
/*
* HPE-specific data structures are decoded here.
*
* Code contributed by John Cagle and Tyler Bell.
*/
static void dmi_print_hp_net_iface_rec(u8 id, u8 bus, u8 dev, const u8 *mac)
{
/* Some systems do not provide an id. nic_ctr provides an artificial
* id, and assumes the records will be provided "in order". Also,
* using 0xFF marker is not future proof. 256 NICs is a lot, but
* 640K ought to be enough for anybody(said no one, ever).
* */
static u8 nic_ctr;
if (id == 0xFF)
id = ++nic_ctr;
if (dev == 0x00 && bus == 0x00)
printf("\tNIC %d: Disabled\n", id);
else if (dev == 0xFF && bus == 0xFF)
printf("\tNIC %d: Not Installed\n", id);
else
{
printf("\tNIC %d: PCI device %02x:%02x.%x, "
"MAC address %02X:%02X:%02X:%02X:%02X:%02X\n",
id, bus, dev >> 3, dev & 7,
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
}
static int dmi_decode_hp(const struct dmi_header *h)
{
u8 *data = h->data;
int nic, ptr;
u32 feat;
const char *company = (dmi_vendor == VENDOR_HP) ? "HP" : "HPE";
switch (h->type)
{
case 204:
/*
* Vendor Specific: HPE ProLiant System/Rack Locator
*/
printf("%s ProLiant System/Rack Locator\n", company);
if (h->length < 0x0B) break;
printf("\tRack Name: %s\n", dmi_string(h, data[0x04]));
printf("\tEnclosure Name: %s\n", dmi_string(h, data[0x05]));
printf("\tEnclosure Model: %s\n", dmi_string(h, data[0x06]));
printf("\tEnclosure Serial: %s\n", dmi_string(h, data[0x0A]));
printf("\tEnclosure Bays: %d\n", data[0x08]);
printf("\tServer Bay: %s\n", dmi_string(h, data[0x07]));
printf("\tBays Filled: %d\n", data[0x09]);
break;
case 209:
case 221:
/*
* Vendor Specific: HPE ProLiant NIC MAC Information
*
* This prints the BIOS NIC number,
* PCI bus/device/function, and MAC address
*
* Type 209:
* Offset | Name | Width | Description
* -------------------------------------
* 0x00 | Type | BYTE | 0xD1, MAC Info
* 0x01 | Length | BYTE | Length of structure
* 0x02 | Handle | WORD | Unique handle
* 0x04 | Dev No | BYTE | PCI Device/Function No
* 0x05 | Bus No | BYTE | PCI Bus
* 0x06 | MAC | 6B | MAC addr
* 0x0C | NIC #2 | 8B | Repeat 0x04-0x0B
*
* Type 221: is deprecated in the latest docs
*/
printf("%s %s\n", company,
h->type == 221 ?
"BIOS iSCSI NIC PCI and MAC Information" :
"BIOS PXE NIC PCI and MAC Information");
nic = 1;
ptr = 4;
while (h->length >= ptr + 8)
{
dmi_print_hp_net_iface_rec(nic,
data[ptr + 0x01],
data[ptr],
&data[ptr + 0x02]);
nic++;
ptr += 8;
}
break;
case 233:
/*
* Vendor Specific: HPE ProLiant NIC MAC Information
*
* This prints the BIOS NIC number,
* PCI bus/device/function, and MAC address
*
* Offset | Name | Width | Description
* -------------------------------------
* 0x00 | Type | BYTE | 0xE9, NIC structure
* 0x01 | Length | BYTE | Length of structure
* 0x02 | Handle | WORD | Unique handle
* 0x04 | Grp No | WORD | 0 for single segment
* 0x06 | Bus No | BYTE | PCI Bus
* 0x07 | Dev No | BYTE | PCI Device/Function No
* 0x08 | MAC | 32B | MAC addr padded w/ 0s
* 0x28 | Port No| BYTE | Each NIC maps to a Port
*/
printf("%s BIOS PXE NIC PCI and MAC Information\n", company);
if (h->length < 0x0E) break;
/* If the record isn't long enough, we don't have an ID
* use 0xFF to use the internal counter.
* */
nic = h->length > 0x28 ? data[0x28] : 0xFF;
dmi_print_hp_net_iface_rec(nic, data[0x06], data[0x07],
&data[0x08]);
break;
case 212:
/*
* Vendor Specific: HPE 64-bit CRU Information
*
* Source: hpwdt kernel driver
*/
printf("%s 64-bit CRU Information\n", company);
if (h->length < 0x18) break;
printf("\tSignature: 0x%08x", DWORD(data + 0x04));
if (is_printable(data + 0x04, 4))
printf(" (%c%c%c%c)", data[0x04], data[0x05],
data[0x06], data[0x07]);
printf("\n");
if (DWORD(data + 0x04) == 0x55524324)
{
u64 paddr = QWORD(data + 0x08);
paddr.l += DWORD(data + 0x14);
if (paddr.l < DWORD(data + 0x14))
paddr.h++;
printf("\tPhysical Address: 0x%08x%08x\n",
paddr.h, paddr.l);
printf("\tLength: 0x%08x\n", DWORD(data + 0x10));
}
break;
case 219:
/*
* Vendor Specific: HPE ProLiant Information
*
* Source: hpwdt kernel driver
*/
printf("%s ProLiant Information\n", company);
if (h->length < 0x08) break;
printf("\tPower Features: 0x%08x\n", DWORD(data + 0x04));
if (h->length < 0x0C) break;
printf("\tOmega Features: 0x%08x\n", DWORD(data + 0x08));
if (h->length < 0x14) break;
feat = DWORD(data + 0x10);
printf("\tMisc. Features: 0x%08x\n", feat);
printf("\t\tiCRU: %s\n", feat & 0x0001 ? "Yes" : "No");
printf("\t\tUEFI: %s\n", feat & 0x1400 ? "Yes" : "No");
break;
default:
return 0;
}
return 1;
}
static int dmi_decode_ibm_lenovo(const struct dmi_header *h)
{
u8 *data = h->data;
switch (h->type)
{
case 131:
/*
* Vendor Specific: ThinkVantage Technologies feature bits
*
* Source: Compal hel81 Service Manual Software Specification,
* documented under "System Management BIOS(SM BIOS)
* version 2.4 or greater"
*
* Offset | Name | Width | Description
* ----------------------------------------------
* 0x00 | Type | BYTE | 0x83
* 0x01 | Length | BYTE | 0x16
* 0x02 | Handle | WORD | Varies
* 0x04 | Version | BYTE | 0x01
* 0x05 | TVT Structure | BYTEx16 | Each of the 128 bits represents a TVT feature:
* | | | - bit 127 means diagnostics (PC Doctor) is available
* | | | (http://www.pc-doctor.com/company/pr-articles/45-lenovo-introduces-thinkvantage-toolbox)
* | | | - the rest (126-0) are reserved/unknown
*
* It must also be followed by a string containing
* "TVT-Enablement". There exist other type 131 records
* with different length and a different string, for
* other purposes.
*/
if (h->length != 0x16
|| strcmp(dmi_string(h, 1), "TVT-Enablement") != 0)
return 0;
printf("ThinkVantage Technologies\n");
printf("\tVersion: %u\n", data[0x04]);
printf("\tDiagnostics: %s\n",
data[0x14] & 0x80 ? "Available" : "No");
break;
case 135:
/*
* Vendor Specific: Device Presence Detection bits
*
* Source: Compal hel81 Service Manual Software Specification,
* documented as "SMBIOS Type 135: Bulk for Lenovo
* Mobile PC Unique OEM Data" under appendix D.
*
* Offset | Name | Width | Description
* ---------------------------------------------------
* 0x00 | Type | BYTE | 0x87
* 0x01 | Length | BYTE | 0x0A
* 0x02 | Handle | WORD | Varies
* 0x04 | Signature | WORD | 0x5054 (ASCII for "TP")
* 0x06 | OEM struct offset | BYTE | 0x07
* 0x07 | OEM struct number | BYTE | 0x03, for this structure
* 0x08 | OEM struct revision | BYTE | 0x01, for this format
* 0x09 | Device presence bits | BYTE | Each of the 8 bits indicates device presence:
* | | | - bit 0 indicates the presence of a fingerprint reader
* | | | - the rest (7-1) are reserved/unknown
*
* Other OEM struct number+rev combinations have been
* seen in the wild but we don't know how to decode
* them.
*/
if (h->length < 0x0A || data[0x04] != 'T' || data[0x05] != 'P')
return 0;
/* Bail out if not the expected format */
if (data[0x06] != 0x07 || data[0x07] != 0x03 || data[0x08] != 0x01)
return 0;
printf("ThinkPad Device Presence Detection\n");
printf("\tFingerprint Reader: %s\n",
data[0x09] & 0x01 ? "Present" : "No");
break;
case 140:
/*
* Vendor Specific: ThinkPad Embedded Controller Program
*
* Source: some guesswork, and publicly available information;
* Lenovo's BIOS update READMEs often contain the ECP IDs
* which match the first string in this type.
*
* Offset | Name | Width | Description
* ----------------------------------------------------
* 0x00 | Type | BYTE | 0x8C
* 0x01 | Length | BYTE |
* 0x02 | Handle | WORD | Varies
* 0x04 | Signature | BYTEx6 | ASCII for "LENOVO"
* 0x0A | OEM struct offset | BYTE | 0x0B
* 0x0B | OEM struct number | BYTE | 0x07, for this structure
* 0x0C | OEM struct revision | BYTE | 0x01, for this format
* 0x0D | ECP version ID | STRING |
* 0x0E | ECP release date | STRING |
*/
if (h->length < 0x0F || memcmp(data + 4, "LENOVO", 6) != 0)
return 0;
/* Bail out if not the expected format */
if (data[0x0A] != 0x0B || data[0x0B] != 0x07 || data[0x0C] != 0x01)
return 0;
printf("ThinkPad Embedded Controller Program\n");
printf("\tVersion ID: %s\n", dmi_string(h, 1));
printf("\tRelease Date: %s\n", dmi_string(h, 2));
break;
default:
return 0;
}
return 1;
}
/*
* Dispatch vendor-specific entries decoding
* Return 1 if decoding was successful, 0 otherwise
@ -281,9 +410,13 @@ int dmi_decode_oem(const struct dmi_header *h)
switch (dmi_vendor)
{
case VENDOR_HP:
case VENDOR_HPE:
return dmi_decode_hp(h);
case VENDOR_ACER:
return dmi_decode_acer(h);
case VENDOR_IBM:
case VENDOR_LENOVO:
return dmi_decode_ibm_lenovo(h);
default:
return 0;
}

View File

@ -113,7 +113,7 @@ static u8 *parse_opt_type(u8 *p, const char *arg)
char *next;
val = strtoul(arg, &next, 0);
if (next == arg)
if (next == arg || (*next != '\0' && *next != ',' && *next != ' '))
{
fprintf(stderr, "Invalid type keyword: %s\n", arg);
print_opt_type_list();
@ -156,6 +156,7 @@ static const struct string_keyword opt_string_keyword[] = {
{ "system-version", 1, 0x06 },
{ "system-serial-number", 1, 0x07 },
{ "system-uuid", 1, 0x08 }, /* dmi_system_uuid() */
{ "system-family", 1, 0x1a },
{ "baseboard-manufacturer", 2, 0x04 },
{ "baseboard-product-name", 2, 0x05 },
{ "baseboard-version", 2, 0x06 },
@ -227,7 +228,7 @@ static int parse_opt_oem_string(const char *arg)
goto done;
val = strtoul(arg, &next, 10);
if (next == arg || val == 0x00 || val > 0xff)
if (next == arg || *next != '\0' || val == 0x00 || val > 0xff)
{
fprintf(stderr, "Invalid OEM string number: %s\n", arg);
return -1;
@ -239,6 +240,19 @@ done:
return 0;
}
static u32 parse_opt_handle(const char *arg)
{
u32 val;
char *next;
val = strtoul(arg, &next, 0);
if (next == arg || *next != '\0' || val > 0xffff)
{
fprintf(stderr, "Invalid handle number: %s\n", arg);
return ~0;
}
return val;
}
/*
* Command line options handling
@ -248,7 +262,7 @@ done:
int parse_command_line(int argc, char * const argv[])
{
int option;
const char *optstring = "d:hqs:t:uV";
const char *optstring = "d:hqs:t:uH:V";
struct option longopts[] = {
{ "dev-mem", required_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
@ -258,6 +272,7 @@ int parse_command_line(int argc, char * const argv[])
{ "dump", no_argument, NULL, 'u' },
{ "dump-bin", required_argument, NULL, 'B' },
{ "from-dump", required_argument, NULL, 'F' },
{ "handle", required_argument, NULL, 'H' },
{ "oem-string", required_argument, NULL, 'O' },
{ "no-sysfs", no_argument, NULL, 'S' },
{ "version", no_argument, NULL, 'V' },
@ -299,6 +314,11 @@ int parse_command_line(int argc, char * const argv[])
if (opt.type == NULL)
return -1;
break;
case 'H':
opt.handle = parse_opt_handle(optarg);
if (opt.handle == ~0U)
return -1;
break;
case 'u':
opt.flags |= FLAG_DUMP;
break;
@ -325,9 +345,9 @@ int parse_command_line(int argc, char * const argv[])
/* Check for mutually exclusive output format options */
if ((opt.string != NULL) + (opt.type != NULL)
+ !!(opt.flags & FLAG_DUMP_BIN) > 1)
+ !!(opt.flags & FLAG_DUMP_BIN) + (opt.handle != ~0U) > 1)
{
fprintf(stderr, "Options --string, --type and --dump-bin are mutually exclusive\n");
fprintf(stderr, "Options --string, --type, --handle and --dump-bin are mutually exclusive\n");
return -1;
}
@ -350,6 +370,7 @@ void print_help(void)
" -q, --quiet Less verbose output\n"
" -s, --string KEYWORD Only display the value of the given DMI string\n"
" -t, --type TYPE Only display the entries of given type\n"
" -H, --handle HANDLE Only display the entry of given handle\n"
" -u, --dump Do not decode the entries\n"
" --dump-bin FILE Dump the DMI data to a binary file\n"
" --from-dump FILE Read the DMI data from a binary file\n"

View File

@ -35,6 +35,7 @@ struct opt
u8 *type;
const struct string_keyword *string;
char *dumpfile;
u32 handle;
};
extern struct opt opt;

View File

@ -60,6 +60,9 @@ program.
.BR "-d" ", " "--dev-mem FILE"
Read memory from device \fBFILE\fR (default: \fB/dev/mem\fR)
.TP
.BR " " " " "--pir full"
Decode the details of the PCI IRQ routing table
.TP
.BR "-h" ", " "--help"
Display usage information and exit
.TP

View File

@ -74,7 +74,7 @@ Only display the value of the \s-1DMI\s0 string identified by \fBKEYWORD\fR.
\fBbios-version\fR, \fBbios-release-date\fR,
\fBsystem-manufacturer\fR, \fBsystem-product-name\fR,
\fBsystem-version\fR, \fBsystem-serial-number\fR,
\fBsystem-uuid\fR,
\fBsystem-uuid\fR, \fBsystem-family\fR,
\fBbaseboard-manufacturer\fR, \fBbaseboard-product-name\fR,
\fBbaseboard-version\fR, \fBbaseboard-serial-number\fR,
\fBbaseboard-asset-tag\fR, \fBchassis-manufacturer\fR,
@ -115,6 +115,10 @@ is printed and
.B dmidecode
exits with an error.
.TP
.BR "-H" ", " "--handle HANDLE"
Only display the entry whose handle matches \fBHANDLE\fR. \fBHANDLE\fR
is a 16-bit integer.
.TP
.BR "-u" ", " "--dump"
Do not decode the entries, dump their contents as hexadecimal instead.
Note that this is still a text output, no binary data will be thrown upon

27
types.h
View File

@ -11,8 +11,7 @@ typedef unsigned int u32;
/*
* You may use the following defines to adjust the type definitions
* depending on the architecture:
* - Define BIGENDIAN on big-endian systems. Untested, as all target
* systems to date are little-endian.
* - Define BIGENDIAN on big-endian systems.
* - Define ALIGNMENT_WORKAROUND if your system doesn't support
* non-aligned memory access. In this case, we use a slower, but safer,
* memory access method. This should be done automatically in config.h
@ -31,7 +30,7 @@ typedef struct {
} u64;
#endif
#ifdef ALIGNMENT_WORKAROUND
#if defined(ALIGNMENT_WORKAROUND) || defined(BIGENDIAN)
static inline u64 U64(u32 low, u32 high)
{
u64 self;
@ -43,20 +42,18 @@ static inline u64 U64(u32 low, u32 high)
}
#endif
#ifdef ALIGNMENT_WORKAROUND
# ifdef BIGENDIAN
# define WORD(x) (u16)((x)[1] + ((x)[0] << 8))
# define DWORD(x) (u32)((x)[3] + ((x)[2] << 8) + ((x)[1] << 16) + ((x)[0] << 24))
# define QWORD(x) (U64(DWORD(x + 4), DWORD(x)))
# else /* BIGENDIAN */
# define WORD(x) (u16)((x)[0] + ((x)[1] << 8))
# define DWORD(x) (u32)((x)[0] + ((x)[1] << 8) + ((x)[2] << 16) + ((x)[3] << 24))
# define QWORD(x) (U64(DWORD(x), DWORD(x + 4)))
# endif /* BIGENDIAN */
#else /* ALIGNMENT_WORKAROUND */
/*
* Per SMBIOS v2.8.0 and later, all structures assume a little-endian
* ordering convention.
*/
#if defined(ALIGNMENT_WORKAROUND) || defined(BIGENDIAN)
#define WORD(x) (u16)((x)[0] + ((x)[1] << 8))
#define DWORD(x) (u32)((x)[0] + ((x)[1] << 8) + ((x)[2] << 16) + ((x)[3] << 24))
#define QWORD(x) (U64(DWORD(x), DWORD(x + 4)))
#else /* ALIGNMENT_WORKAROUND || BIGENDIAN */
#define WORD(x) (u16)(*(const u16 *)(x))
#define DWORD(x) (u32)(*(const u32 *)(x))
#define QWORD(x) (*(const u64 *)(x))
#endif /* ALIGNMENT_WORKAROUND */
#endif /* ALIGNMENT_WORKAROUND || BIGENDIAN */
#endif

60
util.c
View File

@ -2,7 +2,7 @@
* Common "util" functions
* This file is part of the dmidecode project.
*
* Copyright (C) 2002-2017 Jean Delvare <jdelvare@suse.de>
* Copyright (C) 2002-2018 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
@ -59,7 +59,6 @@ static int myread(int fd, u8 *buf, size_t count, const char *prefix)
{
if (errno != EINTR)
{
close(fd);
perror(prefix);
return -1;
}
@ -70,7 +69,6 @@ static int myread(int fd, u8 *buf, size_t count, const char *prefix)
if (r2 != count)
{
close(fd);
fprintf(stderr, "%s: Unexpected end of file\n", prefix);
return -1;
}
@ -90,19 +88,17 @@ int checksum(const u8 *buf, size_t len)
/*
* Reads all of file from given offset, up to max_len bytes.
* A buffer of max_len bytes is allocated by this function, and
* A buffer of at most max_len bytes is allocated by this function, and
* needs to be freed by the caller.
* This provides a similar usage model to mem_chunk()
*
* Returns pointer to buffer of max_len bytes, or NULL on error, and
* Returns a pointer to the allocated buffer, or NULL on error, and
* sets max_len to the length actually read.
*
*/
void *read_file(off_t base, size_t *max_len, const char *filename)
{
struct stat statbuf;
int fd;
size_t r2 = 0;
ssize_t r;
u8 *p;
/*
@ -116,12 +112,20 @@ void *read_file(off_t base, size_t *max_len, const char *filename)
return NULL;
}
if (lseek(fd, base, SEEK_SET) == -1)
/*
* Check file size, don't allocate more than can be read.
*/
if (fstat(fd, &statbuf) == 0)
{
fprintf(stderr, "%s: ", filename);
perror("lseek");
p = NULL;
goto out;
if (base >= statbuf.st_size)
{
fprintf(stderr, "%s: Can't read data beyond EOF\n",
filename);
p = NULL;
goto out;
}
if (*max_len > (size_t)statbuf.st_size - base)
*max_len = statbuf.st_size - base;
}
if ((p = malloc(*max_len)) == NULL)
@ -130,27 +134,23 @@ void *read_file(off_t base, size_t *max_len, const char *filename)
goto out;
}
do
if (lseek(fd, base, SEEK_SET) == -1)
{
r = read(fd, p + r2, *max_len - r2);
if (r == -1)
{
if (errno != EINTR)
{
perror(filename);
free(p);
p = NULL;
goto out;
}
}
else
r2 += r;
fprintf(stderr, "%s: ", filename);
perror("lseek");
goto err_free;
}
while (r != 0);
*max_len = r2;
if (myread(fd, p, *max_len, filename) == 0)
goto out;
err_free:
free(p);
p = NULL;
out:
close(fd);
if (close(fd) == -1)
perror(filename);
return p;
}

View File

@ -1 +1 @@
#define VERSION "3.1"
#define VERSION "3.2"