Update upstream source from tag 'upstream/3.5'

Update to upstream version '3.5'
with Debian dir db72f513f5
This commit is contained in:
Jörg Frings-Fürst 2023-03-15 15:10:24 +01:00
commit b2d8dd95ef
9 changed files with 703 additions and 146 deletions

16
NEWS
View File

@ -1,3 +1,17 @@
Version 3.5 (Tue Mar 14 2023)
- Decode HPE OEM records 216, 224, 230, 238 and 242.
- Fortify entry point length checks.
- Add a --no-quirks option.
- Drop the CPUID exception list.
- Do not let --dump-bin overwrite an existing file.
- Ensure /dev/mem is a character device file.
- Bug fixes:
Fix segmentation fault in HPE OEM record 240
- Minor improvements:
Typo fixes
Write the whole dump file at once
Fix a build warning when USE_MMAP isn't set
Version 3.4 (Mon Jun 27 2022)
- Support for SMBIOS 3.4.0. This includes new memory device types, new
processor upgrades, new slot types and characteristics, decoding of memory
@ -7,7 +21,7 @@ Version 3.4 (Mon Jun 27 2022)
characteristics, new slot characteristics, new on-board device types, new
pointing device interface types, and a new record type (type 45 -
Firmware Inventory Information).
- Decode HPE OEM records 194, 199, 203, 236, 237, 238 ans 240.
- Decode HPE OEM records 194, 199, 203, 236, 237, 238 and 240.
- Bug fixes:
Fix OEM vendor name matching
Fix ASCII filtering of strings

View File

@ -60,6 +60,7 @@
* https://www.dmtf.org/sites/default/files/DSP0270_1.0.1.pdf
*/
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
@ -1105,24 +1106,6 @@ static enum cpuid_type dmi_get_cpuid_type(const struct dmi_header *h)
|| (type >= 0xB6 && type <= 0xB7) /* AMD */
|| (type >= 0xE4 && type <= 0xEF)) /* AMD */
return cpuid_x86_amd;
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)
return cpuid_x86_intel;
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)
return cpuid_x86_amd;
}
/* neither X86 nor ARM */
return cpuid_none;
@ -2722,7 +2705,7 @@ static void dmi_memory_device_width(const char *attr, u16 code)
/*
* If no memory module is present, width may be 0
*/
if (code == 0xFFFF || code == 0)
if (code == 0xFFFF || (code == 0 && !(opt.flags & FLAG_NO_QUIRKS)))
pr_attr(attr, "Unknown");
else
pr_attr(attr, "%u bits", code);
@ -4720,7 +4703,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver)
dmi_memory_device_type_detail(WORD(data + 0x13));
if (h->length < 0x17) break;
/* If no module is present, the remaining fields are irrelevant */
if (WORD(data + 0x0C) == 0)
if (WORD(data + 0x0C) == 0 && !(opt.flags & FLAG_NO_QUIRKS))
break;
dmi_memory_device_speed("Speed", WORD(data + 0x15),
h->length >= 0x5C ?
@ -5403,12 +5386,12 @@ static void dmi_table_string(const struct dmi_header *h, const u8 *data, u16 ver
switch (key)
{
case 0x015: /* -s bios-revision */
if (data[key - 1] != 0xFF && data[key] != 0xFF)
printf("%u.%u\n", data[key - 1], data[key]);
if (data[offset - 1] != 0xFF && data[offset] != 0xFF)
printf("%u.%u\n", data[offset - 1], data[offset]);
break;
case 0x017: /* -s firmware-revision */
if (data[key - 1] != 0xFF && data[key] != 0xFF)
printf("%u.%u\n", data[key - 1], data[key]);
if (data[offset - 1] != 0xFF && data[offset] != 0xFF)
printf("%u.%u\n", data[offset - 1], data[offset]);
break;
case 0x108:
dmi_system_uuid(NULL, NULL, data + offset, ver);
@ -5427,11 +5410,65 @@ static void dmi_table_string(const struct dmi_header *h, const u8 *data, u16 ver
}
}
static void dmi_table_dump(const u8 *buf, u32 len)
static int dmi_table_dump(const u8 *ep, u32 ep_len, const u8 *table,
u32 table_len)
{
int fd;
FILE *f;
fd = open(opt.dumpfile, O_WRONLY|O_CREAT|O_EXCL, 0666);
if (fd == -1)
{
fprintf(stderr, "%s: ", opt.dumpfile);
perror("open");
return -1;
}
f = fdopen(fd, "wb");
if (!f)
{
fprintf(stderr, "%s: ", opt.dumpfile);
perror("fdopen");
return -1;
}
if (!(opt.flags & FLAG_QUIET))
pr_comment("Writing %d bytes to %s.", len, opt.dumpfile);
write_dump(32, len, buf, opt.dumpfile, 0);
pr_comment("Writing %d bytes to %s.", ep_len, opt.dumpfile);
if (fwrite(ep, ep_len, 1, f) != 1)
{
fprintf(stderr, "%s: ", opt.dumpfile);
perror("fwrite");
goto err_close;
}
if (fseek(f, 32, SEEK_SET) != 0)
{
fprintf(stderr, "%s: ", opt.dumpfile);
perror("fseek");
goto err_close;
}
if (!(opt.flags & FLAG_QUIET))
pr_comment("Writing %d bytes to %s.", table_len, opt.dumpfile);
if (fwrite(table, table_len, 1, f) != 1)
{
fprintf(stderr, "%s: ", opt.dumpfile);
perror("fwrite");
goto err_close;
}
if (fclose(f))
{
fprintf(stderr, "%s: ", opt.dumpfile);
perror("fclose");
return -1;
}
return 0;
err_close:
fclose(f);
return -1;
}
static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
@ -5544,7 +5581,7 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
}
/* Fixup a common mistake */
if (h.type == 34)
if (h.type == 34 && !(opt.flags & FLAG_NO_QUIRKS))
dmi_fixup_type_34(&h, display);
if (display)
@ -5585,8 +5622,9 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags)
}
}
static void dmi_table(off_t base, u32 len, u16 num, u32 ver, const char *devmem,
u32 flags)
/* Allocates a buffer for the table, must be freed by the caller */
static u8 *dmi_table_get(off_t base, u32 *len, u16 num, u32 ver,
const char *devmem, u32 flags)
{
u8 *buf;
@ -5605,7 +5643,7 @@ static void dmi_table(off_t base, u32 len, u16 num, u32 ver, const char *devmem,
{
if (num)
pr_info("%u structures occupying %u bytes.",
num, len);
num, *len);
if (!(opt.flags & FLAG_FROM_DUMP))
pr_info("Table at 0x%08llX.",
(unsigned long long)base);
@ -5623,19 +5661,19 @@ static void dmi_table(off_t base, u32 len, u16 num, u32 ver, const char *devmem,
* would be the result of the kernel truncating the table on
* parse error.
*/
size_t size = len;
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)
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, (unsigned long)size);
}
len = size;
*len = size;
}
else
buf = mem_chunk(base, len, devmem);
buf = mem_chunk(base, *len, devmem);
if (buf == NULL)
{
@ -5645,15 +5683,9 @@ static void dmi_table(off_t base, u32 len, u16 num, u32 ver, const char *devmem,
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);
return buf;
}
@ -5688,8 +5720,9 @@ static void overwrite_smbios3_address(u8 *buf)
static int smbios3_decode(u8 *buf, const char *devmem, u32 flags)
{
u32 ver;
u32 ver, len;
u64 offset;
u8 *table;
/* Don't let checksum run beyond the buffer */
if (buf[0x06] > 0x20)
@ -5700,7 +5733,8 @@ static int smbios3_decode(u8 *buf, const char *devmem, u32 flags)
return 0;
}
if (!checksum(buf, buf[0x06]))
if (buf[0x06] < 0x18
|| !checksum(buf, buf[0x06]))
return 0;
ver = (buf[0x07] << 16) + (buf[0x08] << 8) + buf[0x09];
@ -5715,8 +5749,12 @@ static int smbios3_decode(u8 *buf, const char *devmem, u32 flags)
return 0;
}
dmi_table(((off_t)offset.h << 32) | offset.l,
DWORD(buf + 0x0C), 0, ver, devmem, flags | FLAG_STOP_AT_EOT);
/* Maximum length, may get trimmed */
len = DWORD(buf + 0x0C);
table = dmi_table_get(((off_t)offset.h << 32) | offset.l, &len, 0, ver,
devmem, flags | FLAG_STOP_AT_EOT);
if (table == NULL)
return 1;
if (opt.flags & FLAG_DUMP_BIN)
{
@ -5725,18 +5763,47 @@ static int smbios3_decode(u8 *buf, const char *devmem, u32 flags)
memcpy(crafted, buf, 32);
overwrite_smbios3_address(crafted);
if (!(opt.flags & FLAG_QUIET))
pr_comment("Writing %d bytes to %s.", crafted[0x06],
opt.dumpfile);
write_dump(0, crafted[0x06], crafted, opt.dumpfile, 1);
dmi_table_dump(crafted, crafted[0x06], table, len);
}
else
{
dmi_table_decode(table, len, 0, ver >> 8,
flags | FLAG_STOP_AT_EOT);
}
free(table);
return 1;
}
static void dmi_fixup_version(u16 *ver)
{
/* 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;
}
}
static int smbios_decode(u8 *buf, const char *devmem, u32 flags)
{
u16 ver;
u16 ver, num;
u32 len;
u8 *table;
/* Don't let checksum run beyond the buffer */
if (buf[0x05] > 0x20)
@ -5747,37 +5814,30 @@ static int smbios_decode(u8 *buf, const char *devmem, u32 flags)
return 0;
}
if (!checksum(buf, buf[0x05])
/*
* The size of this structure is 0x1F bytes, but we also accept value
* 0x1E due to a mistake in SMBIOS specification version 2.1.
*/
if (buf[0x05] < 0x1E
|| !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_NO_QUIRKS))
dmi_fixup_version(&ver);
if (!(opt.flags & FLAG_QUIET))
pr_info("SMBIOS %u.%u present.",
ver >> 8, ver & 0xFF);
dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C),
ver << 8, devmem, flags);
/* Maximum length, may get trimmed */
len = WORD(buf + 0x16);
num = WORD(buf + 0x1C);
table = dmi_table_get(DWORD(buf + 0x18), &len, num, ver << 8,
devmem, flags);
if (table == NULL)
return 1;
if (opt.flags & FLAG_DUMP_BIN)
{
@ -5786,27 +5846,39 @@ static int smbios_decode(u8 *buf, const char *devmem, u32 flags)
memcpy(crafted, buf, 32);
overwrite_dmi_address(crafted + 0x10);
if (!(opt.flags & FLAG_QUIET))
pr_comment("Writing %d bytes to %s.", crafted[0x05],
opt.dumpfile);
write_dump(0, crafted[0x05], crafted, opt.dumpfile, 1);
dmi_table_dump(crafted, crafted[0x05], table, len);
}
else
{
dmi_table_decode(table, len, num, ver, flags);
}
free(table);
return 1;
}
static int legacy_decode(u8 *buf, const char *devmem, u32 flags)
{
u16 ver, num;
u32 len;
u8 *table;
if (!checksum(buf, 0x0F))
return 0;
ver = ((buf[0x0E] & 0xF0) << 4) + (buf[0x0E] & 0x0F);
if (!(opt.flags & FLAG_QUIET))
pr_info("Legacy DMI %u.%u present.",
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);
/* Maximum length, may get trimmed */
len = WORD(buf + 0x06);
num = WORD(buf + 0x0C);
table = dmi_table_get(DWORD(buf + 0x08), &len, num, ver << 8,
devmem, flags);
if (table == NULL)
return 1;
if (opt.flags & FLAG_DUMP_BIN)
{
@ -5815,11 +5887,14 @@ static int legacy_decode(u8 *buf, const char *devmem, u32 flags)
memcpy(crafted, buf, 16);
overwrite_dmi_address(crafted);
if (!(opt.flags & FLAG_QUIET))
pr_comment("Writing %d bytes to %s.", 0x0F,
opt.dumpfile);
write_dump(0, 0x0F, crafted, opt.dumpfile, 1);
dmi_table_dump(crafted, 0x0F, table, len);
}
else
{
dmi_table_decode(table, len, num, ver, flags);
}
free(table);
return 1;
}

502
dmioem.c
View File

@ -198,13 +198,14 @@ static void dmi_hp_240_attr(u64 defined, u64 set)
};
unsigned int i;
pr_attr("Attributes Defined/Set", NULL);
pr_list_start("Attributes Defined/Set", NULL);
for (i = 0; i < ARRAY_SIZE(attributes); i++)
{
if (!(defined.l & (1UL << i)))
continue;
pr_subattr(attributes[i], "%s", set.l & (1UL << i) ? "Yes" : "No");
pr_list_item("%s: %s", attributes[i], set.l & (1UL << i) ? "Yes" : "No");
}
pr_list_end();
}
static void dmi_hp_203_assoc_hndl(const char *fname, u16 num)
@ -282,7 +283,7 @@ static void dmi_hp_203_devloc(const char *fname, unsigned int code)
"Rear USB Port",
"Internal USB",
"Internal SD Card",
"Internal Virutal USB (Embedded NAND)",
"Internal Virtual USB (Embedded NAND)",
"Embedded SATA Port",
"Embedded Smart Array",
"PCI Slot",
@ -299,6 +300,274 @@ static void dmi_hp_203_devloc(const char *fname, unsigned int code)
pr_attr(fname, "%s", str);
}
static void dmi_hp_216_fw_type(u16 code)
{
const char *str = "Reserved";
static const char * const type[] = {
"Reserved", /* 0x00 */
"System ROM",
"Redundant System ROM",
"System ROM Bootblock",
"Power Management Controller Firmware",
"Power Management Controller Firmware Bootloader",
"SL Chassis Firmware",
"SL Chassis Firmware Bootloader",
"Hardware PAL/CPLD",
"SPS Firmware (ME Firmware)",
"SL Chassis PAL/CPLD",
"Compatibility Support Module (CSM)",
"APML",
"Smart Storage Battery (Megacell) Firmware",
"Trusted Module (TPM or TCM) Firmware Version",
"NVMe Backplane Firmware",
"Intelligent Provisioning",
"SPI Descriptor Version",
"Innovation Engine Firmware (IE Firmware)",
"UMB Backplane Firmware",
"Reserved", /* 0x14 */
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved", /* 0x1F */
"EL Chassis Abstraction Revision",
"EL Chassis Firmware Revision",
"EL Chassis PAL/CPLD",
"EL Cartride Abstraction Revision",
"Reserved", /* 0x24 */
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved", /* 0x2F */
"Embedded Video Controller",
"PCIe Riser Programmable Logic Device",
"PCIe cards that contain a CPLD",
"Intel NVMe VROC",
"Intel SATA VROC",
"Intel SPS Firmware",
"Secondary System Programmable Logic Device",
"CPU MEZZ Programmable Logic Device", /* 0x37 */
"Intel Artic Sound -M Accelerator Models Firmware",
"Ampere System Control Processor (SCP PMPro+SMPro)",
"Intel CFR information", /* 0x3A */
};
if (code < ARRAY_SIZE(type))
str = type[code];
pr_attr("Firmware Type", "%s", str);
}
static void dmi_hp_216_version(u8 format, u8 *data)
{
const char * const name = "Version Data";
const char * const reserved = "Reserved";
int gen;
gen = dmi_hpegen(dmi_product);
switch (format) {
case 0:
pr_attr(name, "No Version Data");
break;
case 1:
pr_attr(name, "%c.%d.%d", data[0] & (1 << 7) ? 'B' : 'R',
data[0] & 0x7, data[1] & 0x7);
break;
case 2:
pr_attr(name, "%d.%d", data[0] >> 4, data[0] & 0x0f);
break;
case 4:
pr_attr(name, "%d.%d.%d", data[0] >> 4, data[0] & 0x0f, data[1] & 0x7f);
break;
case 5:
if (gen == G9) {
pr_attr(name, "%d.%d.%d", data[0] >> 4, data[0] & 0x0f, data[1] & 0x7f);
} else if (gen == G10 || gen == G10P) {
pr_attr(name, "%d.%d.%d.%d", data[1] & 0x0f, data[3] & 0x0f,
data[5] & 0x0f, data[6] & 0x0f);
} else {
pr_attr(name, "%s", reserved);
}
break;
case 6:
pr_attr(name, "%d.%d", data[1], data[0]);
break;
case 7:
pr_attr(name, "v%d.%.2d (%.2d/%.2d/%d)", data[0], data[1],
data[2], data[3], WORD(data + 4));
break;
case 8:
pr_attr(name, "%d.%d", WORD(data + 4), WORD(data));
break;
case 9:
pr_attr(name, "%d.%d.%d", data[0], data[1], WORD(data + 2));
break;
case 10:
pr_attr(name, "%d.%d.%d Build %d", data[0], data[1], data[2], data[3]);
break;
case 11:
pr_attr(name, "%d.%d %d", WORD(data + 2), WORD(data), DWORD(data + 4));
break;
case 12:
pr_attr(name, "%d.%d.%d.%d", WORD(data), WORD(data + 2),
WORD(data + 4), WORD(data + 6));
break;
case 13:
pr_attr(name, "%d", data[0]);
break;
case 14:
pr_attr(name, "%d.%d.%d.%d", data[0], data[1], data[2], data[3]);
break;
case 15:
pr_attr(name, "%d.%d.%d.%d (%.2d/%.2d/%d)",
WORD(data), WORD(data + 2), WORD(data + 4), WORD(data + 6),
data[8], data[9], WORD(data + 10));
break;
case 16:
pr_attr(name, "%c%c%c%c.%d%d",
data[0], data[1], data[2], data[3], data[4], data[5]);
break;
case 17:
pr_attr(name, "%08X", DWORD(data));
break;
case 18:
pr_attr(name, "%d.%2d", data[0], data[1]);
break;
case 3: /* fall through */
default:
pr_attr(name, "%s", reserved);
}
}
static int dmi_hp_224_status(u8 code)
{
static const char * const present[] = {
"Not Present", /* 0x00 */
"Present/Enabled",
"Present/Disabled",
"Reserved" /* 0x03 */
};
pr_attr("Status", "%s", present[code & 0x03]);
if ((code & 0x03) == 0x00)
return 0;
pr_attr("Option ROM Measuring", "%s", (code & (1 << 2)) ? "Yes" : "No");
pr_attr("Hidden", "%s", (code & (1 << 3)) ? "Yes" : "No");
return 1;
}
static void dmi_hp_224_ex_status(u8 status, u8 code)
{
const char *str = "Reserved";
static const char * const disable_reason[] = {
"Not Specified", /* 0x00 */
"User Disabled",
"Error Condition",
"Reserved" /* 0x03 */
};
static const char * const error_condition[] = {
"Not Specified", /* 0x00 */
"Self-Test", /* 0x01 */
};
if ((status & 0x03) == 0x02)
pr_attr("Disable Reason", "%s", disable_reason[code & 0x03]);
if ((code & 0x03) == 0x02) {
u8 error = (code >> 2) & 0x0f;
if (error < ARRAY_SIZE(error_condition))
str = error_condition[error];
pr_attr("Error Condition", "%s", str);
}
}
static void dmi_hp_224_module_type(u8 code)
{
const char *str = "Reserved";
static const char * const type[] = {
"Not Specified", /* 0x00 */
"TPM 1.2",
"TPM 2.0",
"Intel PTT fTPM" /* 0x03 */
};
if ((code & 0x0f) < ARRAY_SIZE(type))
str = type[code & 0x0f];
pr_attr("Type", "%s", str);
pr_attr("Standard Algorithm Supported", "%s", (code & (1 << 4)) ? "Yes" : "No");
pr_attr("Chinese Algorithm Supported", "%s", (code & (1 << 5)) ? "Yes" : "No");
}
static void dmi_hp_224_module_attr(u8 code)
{
static const char * const phys_attr[] = {
"Not Specified", /* 0x00 */
"Pluggable and Optional",
"Pluggable but Standard",
"Soldered Down on System Board" /* 0x03 */
};
static const char * const fips_attr[] = {
"Not Specified", /* 0x00 */
"Not FIPS Certified",
"FIPS Certified",
"Reserved" /* 0x03 */
};
pr_attr("Trusted Module Attributes", "%s", phys_attr[code & 0x3]);
pr_attr("FIPS Certification", "%s", fips_attr[((code >> 2) & 0x03)]);
}
static void dmi_hp_224_chipid(u16 code)
{
const char *str = "Reserved";
static const char * const chipid[] = {
"None", /* 0x00 */
"STMicroGen10 TPM",
"Intel firmware TPM (PTT)",
"Nationz TPM",
"STMicroGen10 Plus TPM",
"STMicroGen11 TPM", /* 0x05 */
};
if ((code & 0xff) < ARRAY_SIZE(chipid))
str = chipid[code & 0xff];
pr_attr("Chip Identifier", "%s", str);
}
static void dmi_hp_230_method_bus_seg_addr(u8 code, u8 bus_seg, u8 addr)
{
const char *str = "Reserved";
static const char * const method[] = {
"Not Available", /* 0x00 */
"IPMI I2C",
"iLO",
"Chassis Manager", /* 0x03 */
};
if (code < ARRAY_SIZE(method))
str = method[code];
pr_attr("Access Method", "%s", str);
if (code == 0 || code >= ARRAY_SIZE(method))
return;
if (bus_seg != 0xFF)
{
if (code == 2)
pr_attr("I2C Segment Number", "%d", bus_seg);
else
pr_attr("I2C Bus Number", "%d", bus_seg);
}
if (addr != 0xFF)
pr_attr("I2C Address", "0x%02x", addr >> 1);
}
static void dmi_hp_238_loc(const char *fname, unsigned int code)
{
const char *str = "Reserved";
@ -308,7 +577,7 @@ static void dmi_hp_238_loc(const char *fname, unsigned int code)
"Rear of Server",
"Embedded internal SD Card",
"iLO USB",
"HP NAND Controller (USX 2065 or other)",
"USB Hub for NAND Controller",
"Reserved",
"Debug Port", /* 0x07 */
};
@ -350,6 +619,52 @@ static void dmi_hp_238_speed(const char *fname, unsigned int code)
pr_attr(fname, "%s", str);
}
static void dmi_hp_242_hdd_type(u8 code)
{
const char *str = "Reserved";
static const char * const type[] = {
"Undetermined", /* 0x00 */
"NVMe SSD",
"SATA",
"SAS",
"SATA SSD",
"NVMe Manged by VROC/VMD", /* 0x05 */
};
if (code < ARRAY_SIZE(type))
str = type[code];
pr_attr("Hard Drive Type", "%s", str);
}
static void dmi_hp_242_form_factor(u8 code)
{
const char *str = "Reserved";
static const char * const form[] = {
"Reserved", /* 0x00 */
"Reserved",
"3.5\" form factor",
"2.5\" form factor",
"1.8\" form factor",
"Less than 1.8\" form factor",
"mSATA",
"M.2",
"MicroSSD",
"CFast", /* 0x09 */
};
if (code < ARRAY_SIZE(form))
str = form[code];
pr_attr("Form Factor", "%s", str);
}
static void dmi_hp_242_speed(const char *attr, u16 speed)
{
if (speed)
pr_attr(attr, "%hu Gbit/s", speed);
else
pr_attr(attr, "%s", "Unknown");
}
static int dmi_decode_hp(const struct dmi_header *h)
{
u8 *data = h->data;
@ -581,6 +896,51 @@ static int dmi_decode_hp(const struct dmi_header *h)
}
break;
case 216:
/*
* Vendor Specific: Version Indicator Record
*
* This record is used to allow determining Firmware and CPLD revisions for
* components in the system. The goal of this record is to provide a
* flexible method to communicate to software and firmware the revisions
* of these components. This record replaces much of the functionality of
* Record Type 193. OEM SMBIOS Record Type 193 was not scaling well with
* the large number of potential CPLD devices, power management controllers,
* etc. This record is flexible such that each instance of Type 216
* defines one firmware component. This record also includes the string
* name for which software should refer to the component. The record
* includes both data bytes to indicate the revision and a string value. A
* firmware component can implement either or both. If both are supported,
* it allows easy display of the revision, but prevents the need for
* software/firmware to parse strings when doing comparisons on revisions.
* As there is one Type 216 Record per firmware component, the Handle for
* the Record can be used to tie firmware components with other OEM SMBIOS
* Records in the future if needed (similar to how SMBIOS Type 17 is tied
* to other Record Types related to DIMMs)
*
* Offset | Name | Width | Description
* ------------------------------------------
* 0x00 | Type | BYTE | 0xD8, Version Indicator Record
* 0x01 | Length | BYTE | Length of structure
* 0x02 | Handle | WORD | Unique handle
* 0x04 | FW Type | WORD | Type of Firmware
* 0x06 | FW Name | STRING | Name of Firmware
* 0x07 | FW Version | STRING | Firmware Version
* 0x08 | Data Format| BYTE | Format of the Version Data
* 0x09 |Version Data|12 BYTES| Version Data in Format from field 0x08
* 0x15 | Unique ID | WORD | Unique ID for Firmware flash
*/
if (gen < G8) return 0;
pr_handle_name("%s Version Indicator", company);
if (h->length < 23) break;
dmi_hp_216_fw_type(WORD(data + 0x04));
pr_attr("Firmware Name String", "%s", dmi_string(h, data[0x06]));
pr_attr("Firmware Version String", "%s", dmi_string(h, data[0x07]));
dmi_hp_216_version(data[0x08], data + 0x09);
if (WORD(data + 0x15))
pr_attr("Unique ID", "0x%04x", WORD(data + 0x15));
break;
case 219:
/*
* Vendor Specific: HPE ProLiant Information
@ -599,6 +959,64 @@ static int dmi_decode_hp(const struct dmi_header *h)
pr_subattr("UEFI", "%s", feat & 0x1400 ? "Yes" : "No");
break;
case 224:
/*
* Vendor Specific: Trusted Module (TPM or TCM) Status
*
* Offset | Name | Width | Description
* -------------------------------------
* 0x00 | Type | BYTE | 0xE0, Trusted Module (TPM or TCM) Status
* 0x01 | Length | BYTE | Length of structure
* 0x02 | Handle | WORD | Unique handle
* 0x04 | Status | BYTE | Status Flag Byte
* 0x05 | Ex Stat| BYTE | TPM Extended Status
* 0x06 | Type | BYTE | Trusted Module Type
* 0x07 | Attrib | BYTE | Trusted Module Attributes
* 0x08 | Handle | WORD | Handle to map to Type 216
* 0x0A | Chip ID| WORD | Chip Identifier Values
*/
pr_handle_name("%s Trusted Module (TPM or TCM) Status", company);
if (h->length < 0x05) break;
if (!dmi_hp_224_status(data[0x04]))
break;
if (h->length < 0x0a) break;
dmi_hp_224_ex_status(data[0x04], data[0x05]);
dmi_hp_224_module_type(data[0x06]);
dmi_hp_224_module_attr(data[0x07]);
if (!(opt.flags & FLAG_QUIET))
pr_attr("Associated Handle", "0x%04X", WORD(data + 0x8));
if (h->length < 0x0c) break;
dmi_hp_224_chipid(WORD(data + 0x0a));
break;
case 230:
/*
* Vendor Specific: Power Supply Information OEM SMBIOS Record
*
* This record is used to communicate additional Power Supply Information
* beyond the Industry Standard System Power Supply (Type 39) Record.
*
* Offset| Name | Width | Description
* -----------------------------------------
* 0x00 | Type | BYTE | 0xE6, Power Supply Information Indicator
* 0x01 | Length | BYTE | Length of structure
* 0x02 | Handle | WORD | Unique handle
* 0x04 | Assoc Handle| WORD | Associated Handle (Type 39)
* 0x06 | Manufacturer| STRING| Actual third party manufacturer
* 0x07 | Revision | STRING| Power Supply Revision Level
* 0x08 | FRU Access | BYTE | Power Supply FRU Access Method
* 0x09 | I2C Bus Num | BYTE | I2C Bus #. Value based upon context
* 0x0A | I2C Address | BYTE | I2C Address
*/
pr_handle_name("%s Power Supply Information", company);
if (h->length < 0x0B) break;
if (!(opt.flags & FLAG_QUIET))
pr_attr("Associated Handle", "0x%04X", WORD(data + 0x4));
pr_attr("Manufacturer", "%s", dmi_string(h, data[0x06]));
pr_attr("Revision", "%s", dmi_string(h, data[0x07]));
dmi_hp_230_method_bus_seg_addr(data[0x08], data[0x09], data[0x0A]);
break;
case 233:
/*
* Vendor Specific: HPE ProLiant NIC MAC Information
@ -770,6 +1188,82 @@ static int dmi_decode_hp(const struct dmi_header *h)
pr_attr("Lowest Supported Version", "Not Available");
break;
case 242:
/*
* Vendor Specific: HPE Hard Drive Inventory Record
*
* This record provides a mechanism for software to gather information for
* NVMe and SATA drives that are directly attached to the system. This
* record does not contain drive information for drives attached to a HBA
* (i.e. a SmartArray controller). This record will only contain information
* for a hard drive detected by the BIOS during POST and does not
* comprehend a hot plug event after the system has booted.
*
* Offset | Name | Width | Description
* ---------------------------------------
* 0x00 | Type | BYTE | 0xF2, HPE Hard Drive Inventory Record
* 0x01 | Length | BYTE | Length of structure
* 0x02 | Handle | WORD | Unique handle
* 0x04 | Hndl Assoc | WORD | Handle to map to Type 203
* 0x06 | HDD Type | BYTE | Hard drive type
* 0x07 | HDD Uniq ID| QWORD | SATA-> WWID. NVMe -> IEEE Ext Uniq ID.
* 0x0F | Capacity | DWORD | Drive Capacity in Mbytes
* 0x13 | Hours | 16BYTE| Number of poweron hours
* 0x23 | Reserved | BYTE | Reserved
* 0x24 | Power | BTYE | Wattage
* 0x25 | Form Factor| BYTE | HDD Form Factor
* 0x26 | Health | BYTE | Hard Drive Health Status
* 0x27 | Serial Num | STRING| NVMe/SATA Serial Number
* 0x28 | Model Num | STRING| NVMe/SATA Model Number
* 0x29 | FW Rev | STRING| Firmware revision
* 0x2A | Location | STRING| Drive location
* 0x2B | Crypt Stat | BYTE | Drive encryption status from BIOS
* 0x2C | Capacity | QWORD | Hard Drive capacity in bytes
* 0x34 | Block Size | DWORD | Logical Block Size in bytes
* 0x38 | Rot Speed | WORD | Nominal Rotational Speed (RPM)
* 0x3A | Neg Speed | WORD | Current negotiated bus speed
* 0x3C | Cap Speed | WORD | Fastest Capable Bus Speed of drive
*/
if (gen < G10) return 0;
pr_handle_name("%s ProLiant Hard Drive Inventory Record", company);
if (h->length < 0x2C) break;
if (!(opt.flags & FLAG_QUIET))
pr_attr("Associated Handle", "0x%04X", WORD(data + 0x4));
dmi_hp_242_hdd_type(data[0x06]);
pr_attr("ID", "%llx", QWORD(data + 0x07));
if (h->length < 0x3E)
pr_attr("Capacity", "%u MB", DWORD(data + 0x0F));
else
dmi_print_memory_size("Capacity", QWORD(data + 0x2C), 0);
/* NB: Poweron low QWORD good for 2,104,351,365,926,255 years */
pr_attr("Poweron", "%ld hours", QWORD(data + 0x13));
if (data[0x24])
pr_attr("Power Wattage", "%hhu W", data[0x24]);
else
pr_attr("Power Wattage", "%s", "Unknown");
dmi_hp_242_form_factor(data[0x25]);
feat = data[0x26];
pr_attr("Health Status", "%s", (feat == 0x00) ? "OK" :
(feat == 0x01) ? "Warning" :
(feat == 0x02) ? "Critical" :
(feat == 0xFF) ? "Unknown" : "Reserved");
pr_attr("Serial Number", dmi_string(h, data[0x27]));
pr_attr("Model Number", dmi_string(h, data[0x28]));
pr_attr("Firmware Revision", dmi_string(h, data[0x29]));
pr_attr("Location", dmi_string(h, data[0x2A]));
feat = data[0x2B];
pr_attr("Encryption Status", "%s", (feat == 0) ? "Not Encrypted" :
(feat == 1) ? "Encrypted" :
(feat == 2) ? "Unknown" :
(feat == 3) ? "Not Supported" : "Reserved");
if (h->length < 0x3E) break;
pr_attr("Block Size", "%u bytes", DWORD(data + 0x34));
/* Rotational Speed: 0 -> Not Reported, 1 -> N/A (SSD) */
if (data[0x38] > 1)
pr_attr("Rotational Speed", "%hhu RPM", data[0x38]);
dmi_hp_242_speed("Negotiated Speed", WORD(data + 0x3A));
dmi_hp_242_speed("Capable Speed", WORD(data + 0x3C));
break;
default:
return 0;
}

View File

@ -270,6 +270,7 @@ int parse_command_line(int argc, char * const argv[])
{ "dev-mem", required_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "quiet", no_argument, NULL, 'q' },
{ "no-quirks", no_argument, NULL, 'Q' },
{ "string", required_argument, NULL, 's' },
{ "type", required_argument, NULL, 't' },
{ "dump", no_argument, NULL, 'u' },
@ -302,6 +303,9 @@ int parse_command_line(int argc, char * const argv[])
case 'q':
opt.flags |= FLAG_QUIET;
break;
case 'Q':
opt.flags |= FLAG_NO_QUIRKS;
break;
case 's':
if (parse_opt_string(optarg) < 0)
return -1;
@ -371,6 +375,7 @@ void print_help(void)
" -d, --dev-mem FILE Read memory from device FILE (default: " DEFAULT_MEM_DEV ")\n"
" -h, --help Display this help text and exit\n"
" -q, --quiet Less verbose output\n"
" --no-quirks Decode everything without quirks\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"

View File

@ -46,6 +46,7 @@ extern struct opt opt;
#define FLAG_DUMP_BIN (1 << 4)
#define FLAG_FROM_DUMP (1 << 5)
#define FLAG_NO_SYSFS (1 << 6)
#define FLAG_NO_QUIRKS (1 << 7)
int parse_command_line(int argc, char * const argv[]);
void print_help(void);

View File

@ -1,4 +1,4 @@
.TH DMIDECODE 8 "January 2019" "dmidecode"
.TH DMIDECODE 8 "February 2023" "dmidecode"
.\"
.SH NAME
dmidecode \- \s-1DMI\s0 table decoder
@ -70,6 +70,11 @@ Read memory from device \fIFILE\fP (default: \fI/dev/mem\fP)
Be less verbose. Unknown, inactive and \s-1OEM\s0-specific entries are not
displayed. Meta-data and handle references are hidden.
.TP
.BR " " " " "--no-quirks"
Decode everything exactly as it is in the table, without trying to fix up
common mistakes or hide irrelevant fields.
This mode is primarily aimed at firmware developers.
.TP
.BR "-s" ", " "--string \fIKEYWORD\fP"
Only display the value of the \s-1DMI\s0 string identified by \fIKEYWORD\fP.
It must be a keyword from the following list:
@ -159,6 +164,7 @@ hexadecimal and \s-1ASCII\s0. This option is mainly useful for debugging.
Do not decode the entries, instead dump the DMI data to a file in binary
form. The generated file is suitable to pass to \fB--from-dump\fP
later.
\fIFILE\fP must not exist.
.TP
.BR " " " " "--from-dump \fIFILE\fP"
Read the DMI data from a binary file previously generated using

69
util.c
View File

@ -155,6 +155,7 @@ out:
return p;
}
#ifdef USE_MMAP
static void safe_memcpy(void *dest, const void *src, size_t n)
{
#ifdef USE_SLOW_MEMCPY
@ -164,8 +165,9 @@ static void safe_memcpy(void *dest, const void *src, size_t n)
*((u8 *)dest + i) = *((const u8 *)src + i);
#else
memcpy(dest, src, n);
#endif
#endif /* USE_SLOW_MEMCPY */
}
#endif /* USE_MMAP */
/*
* Copy a physical memory chunk into a memory buffer.
@ -173,18 +175,26 @@ static void safe_memcpy(void *dest, const void *src, size_t n)
*/
void *mem_chunk(off_t base, size_t len, const char *devmem)
{
void *p;
struct stat statbuf;
void *p = NULL;
int fd;
#ifdef USE_MMAP
struct stat statbuf;
off_t mmoffset;
void *mmp;
#endif
if ((fd = open(devmem, O_RDONLY)) == -1)
/*
* Safety check: if running as root, devmem is expected to be a
* character device file.
*/
if ((fd = open(devmem, O_RDONLY)) == -1
|| fstat(fd, &statbuf) == -1
|| (geteuid() == 0 && !S_ISCHR(statbuf.st_mode)))
{
perror(devmem);
return NULL;
fprintf(stderr, "Can't read memory from %s\n", devmem);
if (fd == -1)
return NULL;
goto out;
}
if ((p = malloc(len)) == NULL)
@ -194,13 +204,6 @@ void *mem_chunk(off_t base, size_t len, const char *devmem)
}
#ifdef USE_MMAP
if (fstat(fd, &statbuf) == -1)
{
fprintf(stderr, "%s: ", devmem);
perror("stat");
goto err_free;
}
/*
* mmap() will fail with SIGBUS if trying to map beyond the end of
* the file.
@ -259,46 +262,6 @@ out:
return p;
}
int write_dump(size_t base, size_t len, const void *data, const char *dumpfile, int add)
{
FILE *f;
f = fopen(dumpfile, add ? "r+b" : "wb");
if (!f)
{
fprintf(stderr, "%s: ", dumpfile);
perror("fopen");
return -1;
}
if (fseek(f, base, SEEK_SET) != 0)
{
fprintf(stderr, "%s: ", dumpfile);
perror("fseek");
goto err_close;
}
if (fwrite(data, len, 1, f) != 1)
{
fprintf(stderr, "%s: ", dumpfile);
perror("fwrite");
goto err_close;
}
if (fclose(f))
{
fprintf(stderr, "%s: ", dumpfile);
perror("fclose");
return -1;
}
return 0;
err_close:
fclose(f);
return -1;
}
/* Returns end - start + 1, assuming start < end */
u64 u64_range(u64 start, u64 end)
{

1
util.h
View File

@ -27,5 +27,4 @@
int checksum(const u8 *buf, size_t len);
void *read_file(off_t base, size_t *len, const char *filename);
void *mem_chunk(off_t base, size_t len, const char *devmem);
int write_dump(size_t base, size_t len, const void *data, const char *dumpfile, int add);
u64 u64_range(u64 start, u64 end);

View File

@ -1 +1 @@
#define VERSION "3.4"
#define VERSION "3.5"