From a1e93482d9cc9ace5fc1494677b45d965f1ddcb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Wed, 31 May 2017 23:24:58 +0200 Subject: New upstream version 3.1 --- AUTHORS | 2 + CHANGELOG | 98 ++++++++++++++ README | 7 +- biosdecode.c | 34 ++++- dmidecode.c | 410 +++++++++++++++++++++++++++++++++++++++++++++----------- dmiopt.c | 41 ++++++ man/dmidecode.8 | 7 +- util.c | 69 +++++++--- util.h | 4 +- version.h | 2 +- 10 files changed, 569 insertions(+), 105 deletions(-) diff --git a/AUTHORS b/AUTHORS index d4badfa..748b985 100644 --- a/AUTHORS +++ b/AUTHORS @@ -19,6 +19,8 @@ Jarod Wilson Anton Arapov Roy Franz Tyler Bell +Xie XiuQi +Petr Oros MANY THANKS TO (IN CHRONOLOGICAL ORDER) Werner Heuser diff --git a/CHANGELOG b/CHANGELOG index f0a51a4..6f9123d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,101 @@ +2015-09-03 Jean Delvare + + * version.h: Set version to 3.1. + +2017-05-23 Jean Delvare + + * dmidecode.c, dmiopt.c: Add a new option to extract OEM strings, like + we already have for many other strings. + * dmidecode.8: Document the new option. + +2017-04-27 Jean Delvare + + Update to support SMBIOS specification version 3.1.1. + + * dmidecode.c: Add support for 3-digit versions. + * dmidecode.c: Add new enumerated values for processors (DMI type 4). + +2017-04-27 Jean Delvare + + Update to support SMBIOS specification version 3.1.0. + + * dmidecode.c: Add support for extended BIOS ROM size (DMI type 0). + * dmidecode.c: Add new enumerated values for chassis types + (DMI type 3). + * dmidecode.c: Add new enumerated values for processors (DMI type 4). + * dmidecode.c: Don't assume 8-bit processor family in dmi_processor_id + (DMI type 4). + * dmidecode.c: Decode the MIDR register on ARM processors + (DMI type 4). + * dmidecode.c: Add support for large cache sizes (DMI type 7). + * dmidecode.c: Add Mini PCIe system slot enumerated values + (DMI type 9). + * dmidecode.c: Clarify the memory speed unit (DMI type 17). + * dmidecode.c: Add support for structure type 43 (TPM Device). + +2017-04-11 Jean Delvare + + * util.c: Don't leak a file descriptor in function read_file. + * util.c, util.c, dmidecode.c: Let callers pass an offset to function + read_file. + * dmidecode.c: Fix reading from SMBIOS 3 dump files using a 64-bit + entry point. + +2017-04-10 Jean Delvare + + * dmidecode.c: Decode the processor ID of the Intel Core M, AMD + Athlon X4 and AMD Opteron X1000/X2000 processors (DMI type 4). + * dmidecode.c: Display the IPMI interrupt number as a decimal + number (DMI type 38). + +2017-01-20 Jean Delvare + + * biosdecode.c: Decode the entry point defined in the Intel + Multiprocessor specification. + +2017-01-20 Jean Delvare + + * dmidecode.c: Only decode one DMI table. + This fixes Savannah bug #50022: + https://savannah.nongnu.org/bugs/?50022 + +2016-09-22 Jean Delvare + + * README: Explain that we can no longer support Cygwin. + +2016-06-30 Petr Oros + + * dmidecode.c: Unmask LRDIMM in memory type detail (DMI type 17). + +2015-11-02 Jean Delvare + + * dmidecode.c, util.c, util.h: Let read_file return the actual data + size. + * dmidecode.c: Use read_file to read the DMI table from sysfs. + This fixes Savannah bug #46176: + https://savannah.nongnu.org/bugs/?46176 + * dmidecode.c: Check the sysfs entry point length. + +2015-10-21 Xie XiuQi + + * dmidecode.c: Handle SMBIOS 3.0 entry points on EFI systems. + +2015-10-20 Jean Delvare + + * dmidecode.c: Handle OEM-specific types in group associations + (DMI type 14). + +2015-10-14 Jean Delvare + + * util.c: Avoid SIGBUS on mmap failure. + This fixes Savannah bug #46066: + https://savannah.nongnu.org/bugs/?46066 + * util.c: Fix error paths in mem_chunk. + +2015-10-01 Roy Franz + + * dmiopt.c: Add "--no-sysfs" option description to -h output. + 2015-09-03 Jean Delvare * version.h: Set version to 3.0. diff --git a/README b/README index 391a5cb..f612b36 100644 --- a/README +++ b/README @@ -28,7 +28,7 @@ and other interesting material, such as a list of related projects and articles. This program was first written for Linux, and has since been reported to work -on FreeBSD, NetBSD, OpenBSD, BeOS, Cygwin and Solaris as well. +on FreeBSD, NetBSD, OpenBSD, BeOS and Solaris as well. There's no configure script, so simply run "make" to build dmidecode, and "make install" to install it. You also can use "make uninstall" to remove @@ -83,9 +83,8 @@ successfully run. CYGWIN -Dmidecode was reported to work under Cygwin. It seems that /dev/mem doesn't -work properly before version 1.5.10 though, so you will need to use at least -this version. +Dmidecode used to work under Cygwin. However the /dev/mem interface was +removed at some point in time so it no longer works. ** MISCELLANEOUS TOOLS ** diff --git a/biosdecode.c b/biosdecode.c index 3bbfe28..ad3d4bc 100644 --- a/biosdecode.c +++ b/biosdecode.c @@ -2,7 +2,7 @@ * BIOS Decode * * Copyright (C) 2000-2002 Alan Cox - * Copyright (C) 2002-2015 Jean Delvare + * Copyright (C) 2002-2017 Jean Delvare * * 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 @@ -52,6 +52,9 @@ * - Fujitsu application panel technical details * As of July 23rd, 2004 * http://apanel.sourceforge.net/tech.php + * - Intel Multiprocessor Specification + * Version 1.4 + * http://www.intel.com/design/archives/processors/pro/docs/242016.htm */ #include @@ -545,6 +548,34 @@ static int fjkeyinf_decode(const u8 *p, size_t len) return 1; } +/* + * Intel Multiprocessor + */ + +static size_t mp_length(const u8 *p) +{ + return 16 * p[8]; +} + +static int mp_decode(const u8 *p, size_t len) +{ + if (!checksum(p, len)) + return 0; + + printf("Intel Multiprocessor present.\n"); + printf("\tSpecification Revision: %s\n", + p[9] == 0x01 ? "1.1" : p[9] == 0x04 ? "1.4" : "Invalid"); + if (p[11]) + printf("\tDefault Configuration: #%d\n", p[11]); + else + printf("\tConfiguration Table Address: 0x%08X\n", + DWORD(p + 4)); + printf("\tMode: %s\n", p[12] & (1 << 7) ? + "IMCR and PIC" : "Virtual Wire"); + + return 1; +} + /* * Main */ @@ -562,6 +593,7 @@ static struct bios_entry bios_entries[] = { { "32OS", 0, 0xE0000, 0xFFFFF, compaq_length, compaq_decode }, { "\252\125VPD", 0, 0xF0000, 0xFFFFF, vpd_length, vpd_decode }, { "FJKEYINF", 0, 0xF0000, 0xFFFFF, fjkeyinf_length, fjkeyinf_decode }, + { "_MP_", 0, 0xE0000, 0xFFFFF, mp_length, mp_decode }, { NULL, 0, 0, 0, NULL, NULL } }; diff --git a/dmidecode.c b/dmidecode.c index f41c85b..6559567 100644 --- a/dmidecode.c +++ b/dmidecode.c @@ -2,7 +2,7 @@ * DMI Decode * * Copyright (C) 2000-2002 Alan Cox - * Copyright (C) 2002-2015 Jean Delvare + * Copyright (C) 2002-2017 Jean Delvare * * 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.0.0" document, + * Management BIOS Reference Specification, Version 3.1.1" document, * available from http://www.dmtf.org/standards/smbios. * * Note to contributors: @@ -50,6 +50,12 @@ * - 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 @@ -69,13 +75,14 @@ #define out_of_spec "" static const char *bad_index = ""; -#define SUPPORTED_SMBIOS_VER 0x0300 +#define SUPPORTED_SMBIOS_VER 0x030101 #define FLAG_NO_FILE_OFFSET (1 << 0) #define FLAG_STOP_AT_EOT (1 << 1) -#define SYS_ENTRY_FILE "/sys/firmware/dmi/tables/smbios_entry_point" -#define SYS_TABLE_FILE "/sys/firmware/dmi/tables/DMI" +#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 @@ -169,10 +176,13 @@ static const char *dmi_smbios_structure_type(u8 code) "Power Supply", "Additional Information", "Onboard Device", - "Management Controller Host Interface", /* 42 */ + "Management Controller Host Interface", + "TPM Device", /* 43 */ }; - if (code <= 42) + if (code >= 128) + return "OEM-specific"; + if (code <= 43) return type[code]; return out_of_spec; } @@ -292,6 +302,18 @@ static void dmi_bios_runtime_size(u32 code) 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 */ @@ -550,12 +572,16 @@ static const char *dmi_chassis_type(u8 code) "Blade Enclosing", "Tablet", "Convertible", - "Detachable" /* 0x20 */ + "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 <= 0x20) + if (code >= 0x01 && code <= 0x24) return type[code - 0x01]; return out_of_spec; } @@ -717,7 +743,9 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver) { 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" }, @@ -769,6 +797,9 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver) { 0x66, "Athlon X4" }, { 0x67, "Opteron X1000" }, { 0x68, "Opteron X2000" }, + { 0x69, "Opteron A-Series" }, + { 0x6A, "Opteron X3000" }, + { 0x6B, "Zen" }, { 0x70, "Hobbit" }, @@ -880,6 +911,8 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver) { 0xFA, "i860" }, { 0xFB, "i960" }, + { 0x100, "ARMv7" }, + { 0x101, "ARMv8" }, { 0x104, "SH-3" }, { 0x105, "SH-4" }, { 0x118, "ARM" }, @@ -891,6 +924,10 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver) { 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) @@ -943,7 +980,7 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver) } } -static void dmi_processor_id(u8 type, const u8 *p, const char *version, const char *prefix) +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] = { @@ -980,13 +1017,14 @@ static void dmi_processor_id(u8 type, const u8 *p, const char *version, const ch NULL, /* 30 */ "PBE (Pending break enabled)" /* 31 */ }; - /* - * 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. - */ + 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 @@ -1026,8 +1064,24 @@ static void dmi_processor_id(u8 type, const u8 *p, const char *version, const ch 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 <= 0x2B) /* Intel */ + || (type >= 0x28 && type <= 0x2F) /* Intel */ || (type >= 0xA1 && type <= 0xB3) /* Intel */ || type == 0xB5 /* Intel */ || (type >= 0xB9 && type <= 0xC7) /* Intel */ @@ -1039,12 +1093,14 @@ static void dmi_processor_id(u8 type, const u8 *p, const char *version, const ch || 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 @@ -1062,9 +1118,14 @@ static void dmi_processor_id(u8 type, const u8 *p, const char *version, const ch else return; } - else /* not X86-class */ + 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) @@ -1200,10 +1261,18 @@ static const char *dmi_processor_upgrade(u8 code) "Socket LGA1150", "Socket BGA1168", "Socket BGA1234", - "Socket BGA1364" /* 0x30 */ + "Socket BGA1364", + "Socket AM4", + "Socket LGA1151", + "Socket BGA1356", + "Socket BGA1440", + "Socket BGA1515", + "Socket LGA3647-1", + "Socket SP3", + "Socket SP3r2" /* 0x38 */ }; - if (code >= 0x01 && code <= 0x30) + if (code >= 0x01 && code <= 0x38) return upgrade[code - 0x01]; return out_of_spec; } @@ -1476,6 +1545,21 @@ static void dmi_cache_size(u16 code) 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 */ @@ -1712,7 +1796,10 @@ static const char *dmi_slot_type(u8 code) "MXM 3.0 Type A", "MXM 3.0 Type B", "PCI Express 2 SFF-8639", - "PCI Express 3 SFF-8639" /* 0x20 */ + "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 */ @@ -1744,7 +1831,7 @@ static const char *dmi_slot_type(u8 code) * function dmi_slot_id below needs updating too. */ - if (code >= 0x01 && code <= 0x20) + if (code >= 0x01 && code <= 0x23) return type[code - 0x01]; if (code >= 0xA0 && code <= 0xB6) return type_0xA0[code - 0xA0]; @@ -1826,6 +1913,9 @@ static void dmi_slot_id(u8 code1, u8 code2, u8 type, const char *prefix) 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 */ @@ -2274,10 +2364,13 @@ static void dmi_memory_device_extended_size(u32 code) { code &= 0x7FFFFFFFUL; - /* Use the most suitable unit depending on size */ + /* + * 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 & 0xFFFFFUL) + else if (code & 0xFFC00UL) printf(" %lu GB", (unsigned long)code >> 10); else printf(" %lu TB", (unsigned long)code >> 20); @@ -2389,7 +2482,7 @@ static void dmi_memory_device_type_detail(u16 code) "LRDIMM" /* 15 */ }; - if ((code & 0x7FFE) == 0) + if ((code & 0xFFFE) == 0) printf(" None"); else { @@ -2406,7 +2499,7 @@ static void dmi_memory_device_speed(u16 code) if (code == 0) printf(" Unknown"); else - printf(" %u MHz", code); + printf(" %u MT/s", code); } /* @@ -2946,7 +3039,7 @@ static void dmi_64bit_memory_error_address(u64 code) * 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) +static void dmi_fixup_type_34(struct dmi_header *h, int display) { u8 *p = h->data; @@ -2954,7 +3047,10 @@ static void dmi_fixup_type_34(struct dmi_header *h) if (h->length == 0x10 && is_printable(p + 0x0B, 0x10 - 0x0B)) { - printf("Invalid entry length (%u). Fixed up to %u.\n", 0x10, 0x0B); + if (!(opt.flags & FLAG_QUIET) && display) + fprintf(stderr, + "Invalid entry length (%u). Fixed up to %u.\n", + 0x10, 0x0B); h->length = 0x0B; } } @@ -3222,6 +3318,57 @@ static const char *dmi_management_controller_host_type(u8 code) 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 */ @@ -3257,8 +3404,9 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) dmi_bios_runtime_size((0x10000 - WORD(data + 0x06)) << 4); printf("\n"); } - printf("\tROM Size: %u kB\n", - (data[0x09] + 1) << 6); + 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; @@ -3382,7 +3530,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) dmi_processor_family(h, ver)); printf("\tManufacturer: %s\n", dmi_string(h, data[0x07])); - dmi_processor_id(data[0x06], data + 0x08, dmi_string(h, data[0x10]), "\t"); + dmi_processor_id(h, "\t"); printf("\tVersion: %s\n", dmi_string(h, data[0x10])); printf("\tVoltage:"); @@ -3509,10 +3657,16 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) printf("\tLocation: %s\n", dmi_cache_location((WORD(data + 0x05) >> 5) & 0x0003)); printf("\tInstalled Size:"); - dmi_cache_size(WORD(data + 0x09)); + if (h->length >= 0x1B) + dmi_cache_size_2(DWORD(data + 0x17)); + else + dmi_cache_size(WORD(data + 0x09)); printf("\n"); printf("\tMaximum Size:"); - dmi_cache_size(WORD(data + 0x07)); + 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"); @@ -4234,7 +4388,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) } if (data[0x11] != 0x00) { - printf("\tInterrupt Number: %x\n", + printf("\tInterrupt Number: %u\n", data[0x11]); } break; @@ -4331,6 +4485,43 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) } 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; @@ -4364,6 +4555,21 @@ 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; @@ -4422,9 +4628,14 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags) */ if (h.length < 4) { - printf("Invalid entry length (%u). DMI table is " - "broken! Stop.\n\n", (unsigned int)h.length); - opt.flags |= FLAG_QUIET; + 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; } @@ -4443,7 +4654,7 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags) /* Fixup a common mistake */ if (h.type == 34) - dmi_fixup_type_34(&h); + dmi_fixup_type_34(&h, display); /* look for the next handle */ next = data + h.length; @@ -4485,26 +4696,28 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags) if (!(opt.flags & FLAG_QUIET)) { if (num && i != num) - printf("Wrong DMI structures count: %d announced, " + 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)) - printf("Wrong DMI structures length: %u bytes " + 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, u16 ver, const char *devmem, +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 are not\n" + printf("# SMBIOS implementations newer than version %u.%u.%u are not\n" "# fully supported by this version of dmidecode.\n", - SUPPORTED_SMBIOS_VER >> 8, SUPPORTED_SMBIOS_VER & 0xFF); + SUPPORTED_SMBIOS_VER >> 16, + (SUPPORTED_SMBIOS_VER >> 8) & 0xFF, + SUPPORTED_SMBIOS_VER & 0xFF); } if (!(opt.flags & FLAG_QUIET)) @@ -4521,29 +4734,45 @@ static void dmi_table(off_t base, u32 len, u16 num, u16 ver, const char *devmem, printf("\n"); } - /* - * When we are reading the DMI table from sysfs, we want to print - * the address of the table (done above), but the offset of the - * data in the file is 0. When reading from /dev/mem, the offset - * in the file is the address. - */ - if (flags & FLAG_NO_FILE_OFFSET) - base = 0; + 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 = mem_chunk(base, len, devmem)) == NULL) + if (buf == NULL) { - fprintf(stderr, "Table is unreachable, sorry." + fprintf(stderr, "Failed to read table, sorry.\n"); #ifndef USE_MMAP - " Try compiling dmidecode with -DUSE_MMAP." + if (!(flags & FLAG_NO_FILE_OFFSET)) + fprintf(stderr, + "Try compiling dmidecode with -DUSE_MMAP.\n"); #endif - "\n"); return; } if (opt.flags & FLAG_DUMP_BIN) dmi_table_dump(buf, len); else - dmi_table_decode(buf, len, num, ver, flags); + dmi_table_decode(buf, len, num, ver >> 8, flags); free(buf); } @@ -4580,13 +4809,13 @@ static void overwrite_smbios3_address(u8 *buf) static int smbios3_decode(u8 *buf, const char *devmem, u32 flags) { - u16 ver; + u32 ver; u64 offset; if (!checksum(buf, buf[0x06])) return 0; - ver = (buf[0x07] << 8) + buf[0x08]; + 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]); @@ -4599,7 +4828,7 @@ static int smbios3_decode(u8 *buf, const char *devmem, u32 flags) } dmi_table(((off_t)offset.h << 32) | offset.l, - WORD(buf + 0x0C), 0, ver, devmem, flags | FLAG_STOP_AT_EOT); + DWORD(buf + 0x0C), 0, ver, devmem, flags | FLAG_STOP_AT_EOT); if (opt.flags & FLAG_DUMP_BIN) { @@ -4633,14 +4862,16 @@ static int smbios_decode(u8 *buf, const char *devmem, u32 flags) case 0x021F: case 0x0221: if (!(opt.flags & FLAG_QUIET)) - printf("SMBIOS version fixup (2.%d -> 2.%d).\n", - ver & 0xFF, 3); + fprintf(stderr, + "SMBIOS version fixup (2.%d -> 2.%d).\n", + ver & 0xFF, 3); ver = 0x0203; break; case 0x0233: if (!(opt.flags & FLAG_QUIET)) - printf("SMBIOS version fixup (2.%d -> 2.%d).\n", - 51, 6); + fprintf(stderr, + "SMBIOS version fixup (2.%d -> 2.%d).\n", + 51, 6); ver = 0x0206; break; } @@ -4649,7 +4880,7 @@ static int smbios_decode(u8 *buf, const char *devmem, u32 flags) ver >> 8, ver & 0xFF); dmi_table(DWORD(buf + 0x18), WORD(buf + 0x16), WORD(buf + 0x1C), - ver, devmem, flags); + ver << 8, devmem, flags); if (opt.flags & FLAG_DUMP_BIN) { @@ -4677,7 +4908,8 @@ static int legacy_decode(u8 *buf, const char *devmem, u32 flags) buf[0x0E] >> 4, buf[0x0E] & 0x0F); dmi_table(DWORD(buf + 0x08), WORD(buf + 0x06), WORD(buf + 0x0C), - ((buf[0x0E] & 0xF0) << 4) + (buf[0x0E] & 0x0F), devmem, flags); + ((buf[0x0E] & 0xF0) << 12) + ((buf[0x0E] & 0x0F) << 8), + devmem, flags); if (opt.flags & FLAG_DUMP_BIN) { @@ -4748,9 +4980,17 @@ 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]); @@ -4817,22 +5057,23 @@ int main(int argc, char * const argv[]) * 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(0x20, SYS_ENTRY_FILE)) != NULL) + && (buf = read_file(0, &size, SYS_ENTRY_FILE)) != NULL) { if (!(opt.flags & FLAG_QUIET)) printf("Getting SMBIOS data from sysfs.\n"); - if (memcmp(buf, "_SM3_", 5) == 0) + if (size >= 24 && memcmp(buf, "_SM3_", 5) == 0) { if (smbios3_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET)) found++; } - else if (memcmp(buf, "_SM_", 4) == 0) + else if (size >= 31 && memcmp(buf, "_SM_", 4) == 0) { if (smbios_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET)) found++; } - else if (memcmp(buf, "_DMI_", 5) == 0) + else if (size >= 15 && memcmp(buf, "_DMI_", 5) == 0) { if (legacy_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET)) found++; @@ -4864,8 +5105,16 @@ int main(int argc, char * const argv[]) goto exit_free; } - if (smbios_decode(buf, opt.devmem, 0)) - found++; + 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: @@ -4878,28 +5127,37 @@ memory_scan: goto exit_free; } - for (fp = 0; fp <= 0xFFF0; fp += 16) + /* Look for a 64-bit entry point first */ + for (fp = 0; fp <= 0xFFE0; fp += 16) { - if (memcmp(buf + fp, "_SM3_", 5) == 0 && fp <= 0xFFE0) + if (memcmp(buf + fp, "_SM3_", 5) == 0) { if (smbios3_decode(buf + fp, opt.devmem, 0)) { found++; - fp += 16; + goto done; } } - else if (memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) + } + + /* 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++; - fp += 16; + goto done; } } else if (memcmp(buf + fp, "_DMI_", 5) == 0) { if (legacy_decode(buf + fp, opt.devmem, 0)) + { found++; + goto done; + } } } diff --git a/dmiopt.c b/dmiopt.c index 0d142d2..da42546 100644 --- a/dmiopt.c +++ b/dmiopt.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include @@ -171,6 +172,10 @@ static const struct string_keyword opt_string_keyword[] = { { "processor-frequency", 4, 0x16 }, /* dmi_processor_frequency() */ }; +/* This is a template, 3rd field is set at runtime. */ +static struct string_keyword opt_oem_string_keyword = + { NULL, 11, 0x00 }; + static void print_opt_string_list(void) { unsigned int i; @@ -206,6 +211,34 @@ static int parse_opt_string(const char *arg) return -1; } +static int parse_opt_oem_string(const char *arg) +{ + unsigned long val; + char *next; + + if (opt.string) + { + fprintf(stderr, "Only one string can be specified\n"); + return -1; + } + + /* Return the number of OEM strings */ + if (strcmp(arg, "count") == 0) + goto done; + + val = strtoul(arg, &next, 10); + if (next == arg || val == 0x00 || val > 0xff) + { + fprintf(stderr, "Invalid OEM string number: %s\n", arg); + return -1; + } + + opt_oem_string_keyword.offset = val; +done: + opt.string = &opt_oem_string_keyword; + return 0; +} + /* * Command line options handling @@ -225,6 +258,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' }, + { "oem-string", required_argument, NULL, 'O' }, { "no-sysfs", no_argument, NULL, 'S' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } @@ -255,6 +289,11 @@ int parse_command_line(int argc, char * const argv[]) return -1; opt.flags |= FLAG_QUIET; break; + case 'O': + if (parse_opt_oem_string(optarg) < 0) + return -1; + opt.flags |= FLAG_QUIET; + break; case 't': opt.type = parse_opt_type(opt.type, optarg); if (opt.type == NULL) @@ -314,6 +353,8 @@ void print_help(void) " -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" + " --no-sysfs Do not attempt to read DMI data from sysfs files\n" + " --oem-string N Only display the value of the given OEM string\n" " -V, --version Display the version and exit\n"; printf("%s", help); diff --git a/man/dmidecode.8 b/man/dmidecode.8 index a64cf5d..bef204e 100644 --- a/man/dmidecode.8 +++ b/man/dmidecode.8 @@ -134,13 +134,18 @@ Read the DMI data from a binary file previously generated using Do not attempt to read DMI data from sysfs files. This is mainly useful for debugging. .TP +.BR " " " " "--oem-string N" +Only display the value of the \s-1OEM\s0 string number \fBN\fR. The first +\s-1OEM\s0 string has number 1. With special value "count", return the +number of OEM strings instead. +.TP .BR "-h" ", " "--help" Display usage information and exit .TP .BR "-V" ", " "--version" Display the version and exit .P -Options --string, --type and --dump-bin +Options --string, --type, --dump-bin and --oem-string determine the output format and are mutually exclusive. .P Please note in case of diff --git a/util.c b/util.c index 8cafe5c..0aafcb1 100644 --- a/util.c +++ b/util.c @@ -2,7 +2,7 @@ * Common "util" functions * This file is part of the dmidecode project. * - * Copyright (C) 2002-2015 Jean Delvare + * Copyright (C) 2002-2017 Jean Delvare * * 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 @@ -89,15 +89,16 @@ int checksum(const u8 *buf, size_t len) } /* - * Reads all of file, up to max_len bytes. + * Reads all of file from given offset, up to max_len bytes. * A buffer of 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 + * Returns pointer to buffer of max_len bytes, or NULL on error, and + * sets max_len to the length actually read. * */ -void *read_file(size_t max_len, const char *filename) +void *read_file(off_t base, size_t *max_len, const char *filename) { int fd; size_t r2 = 0; @@ -112,26 +113,34 @@ void *read_file(size_t max_len, const char *filename) { if (errno != ENOENT) perror(filename); - return(NULL); + return NULL; } - if ((p = malloc(max_len)) == NULL) + if (lseek(fd, base, SEEK_SET) == -1) + { + fprintf(stderr, "%s: ", filename); + perror("lseek"); + p = NULL; + goto out; + } + + if ((p = malloc(*max_len)) == NULL) { perror("malloc"); - return NULL; + goto out; } do { - r = read(fd, p + r2, max_len - r2); + r = read(fd, p + r2, *max_len - r2); if (r == -1) { if (errno != EINTR) { - close(fd); perror(filename); free(p); - return NULL; + p = NULL; + goto out; } } else @@ -139,7 +148,10 @@ void *read_file(size_t max_len, const char *filename) } while (r != 0); + *max_len = r2; +out: close(fd); + return p; } @@ -152,6 +164,7 @@ void *mem_chunk(off_t base, size_t len, const char *devmem) void *p; int fd; #ifdef USE_MMAP + struct stat statbuf; off_t mmoffset; void *mmp; #endif @@ -165,10 +178,28 @@ void *mem_chunk(off_t base, size_t len, const char *devmem) if ((p = malloc(len)) == NULL) { perror("malloc"); - return NULL; + goto out; } #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. + */ + if (S_ISREG(statbuf.st_mode) && base + (off_t)len > statbuf.st_size) + { + fprintf(stderr, "mmap: Can't map beyond end of file %s\n", + devmem); + goto err_free; + } + #ifdef _SC_PAGESIZE mmoffset = base % sysconf(_SC_PAGESIZE); #else @@ -199,19 +230,17 @@ try_read: { fprintf(stderr, "%s: ", devmem); perror("lseek"); - free(p); - return NULL; + goto err_free; } - if (myread(fd, p, len, devmem) == -1) - { - free(p); - return NULL; - } + if (myread(fd, p, len, devmem) == 0) + goto out; + +err_free: + free(p); + p = NULL; -#ifdef USE_MMAP out: -#endif if (close(fd) == -1) perror(devmem); diff --git a/util.h b/util.h index 9d409cd..3094cf8 100644 --- a/util.h +++ b/util.h @@ -1,7 +1,7 @@ /* * This file is part of the dmidecode project. * - * Copyright (C) 2003-2015 Jean Delvare + * Copyright (C) 2003-2017 Jean Delvare * * 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 @@ #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) int checksum(const u8 *buf, size_t len); -void *read_file(size_t len, const char *filename); +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); diff --git a/version.h b/version.h index f3e8666..778ae5f 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION "3.0" +#define VERSION "3.1" -- cgit v1.2.3