diff --git a/Changelog.md b/Changelog.md index 2152168..a9cdd99 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,9 @@ Acidanthera dmidecode Changelog =============================== +#### v3.6a +- Synced with dmidecode 51b1ecc2 + #### v3.4a - Synced with dmidecode a1a2258f - Added Apple Silicon support diff --git a/Makefile b/Makefile index 7cb3ada..fd987bc 100644 --- a/Makefile +++ b/Makefile @@ -42,6 +42,7 @@ sbindir = $(prefix)/sbin mandir = $(prefix)/share/man man8dir = $(mandir)/man8 docdir = $(prefix)/share/doc/dmidecode +compdir = $(shell pkg-config --variable=completionsdir bash-completion 2>/dev/null || echo $(prefix)/etc/bash_completion.d) INSTALL := install INSTALL_DATA := $(INSTALL) -m 644 @@ -119,9 +120,9 @@ util.o : util.c types.h util.h config.h strip : $(PROGRAMS) strip $(PROGRAMS) -install : install-bin install-man install-doc +install : install-bin install-man install-doc install-completion -uninstall : uninstall-bin uninstall-man uninstall-doc +uninstall : uninstall-bin uninstall-man uninstall-doc uninstall-completion install-bin : $(PROGRAMS) $(INSTALL_DIR) $(DESTDIR)$(sbindir) diff --git a/NEWS b/NEWS index d1f9116..8c4059e 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,36 @@ +Version 3.6 (Wed Apr 24 2024) + - [PORTABILITY] Use -DALIGNMENT_WORKAROUND on arm. + - [PORTABILITY] Read SMBIOS entry point via kenv on DragonFly BSD. + - Support for SMBIOS 3.6.0. This includes new memory device types, new + processor upgrades, and Loongarch support. + - Support for SMBIOS 3.7.0. This includes new port types, new processor + upgrades, new slot characteristics and new fields for memory modules. + - Add bash completion. + - Decode HPE OEM records 197, 239 and 245. + - Implement options --list-strings and --list-types. + - Update HPE OEM records 203, 212, 216, 221, 233, 236, 237, 238 and 242. + - Update Redfish support. + - Bug fixes: + Fix option --from-dump for user root + Fix enabled slot characteristics not being printed + - Minor improvements: + Print slot width on its own line + Use standard strings for slot width + +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 diff --git a/README.md b/README.md index 2bee3fa..5879583 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ dmidecode This dmidecode version supports Apple-specific table decoding as well as native macOS SMBIOS reading through I/O Registry. Synced with -[upstream](git://git.savannah.gnu.org/dmidecode.git) up to a1a2258f. +[upstream](git://git.savannah.gnu.org/dmidecode.git) up to 51b1ecc2. **INTRODUCTION** diff --git a/completion/biosdecode.bash b/completion/biosdecode.bash new file mode 100644 index 0000000..42e0fae --- /dev/null +++ b/completion/biosdecode.bash @@ -0,0 +1,40 @@ +# bash completion for biosdecode -*- shell-script -*- + +_comp_cmd_biosdecode() { + local cur prev + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD - 1]} + + case $prev in + -d | --dev-mem) + : "${cur:=/dev/}" + local IFS=$'\n' + compopt -o filenames + COMPREPLY=($(compgen -f -- "$cur")) + return 0 + ;; + --pir) + COMPREPLY=($(compgen -W ' + full + ' -- "$cur")) + return 0 + ;; + -[hV] | --help | --version) + return 0 + ;; + esac + + if [[ $cur == -* ]]; then + COMPREPLY=($(compgen -W ' + --dev-mem + --pir + --help + --version + ' -- "$cur")) + return 0 + fi + +} && complete -F _comp_cmd_biosdecode biosdecode + +# ex: filetype=sh diff --git a/completion/dmidecode.bash b/completion/dmidecode.bash new file mode 100644 index 0000000..200d6cd --- /dev/null +++ b/completion/dmidecode.bash @@ -0,0 +1,60 @@ +# bash completion for dmidecode -*- shell-script -*- + +_comp_cmd_dmidecode() { + local cur prev + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD - 1]} + + case $prev in + -d | --dev-mem | --dump-bin | --from-dump) + if [[ $prev == -d || $prev == --dev-mem ]]; then + : "${cur:=/dev/}" + fi + local IFS=$'\n' + compopt -o filenames + COMPREPLY=($(compgen -f -- "$cur")) + return 0 + ;; + -s | --string) + COMPREPLY=($(compgen -W '$("$1" --list-strings)' -- "$cur")) + return 0 + ;; + -t | --type) + COMPREPLY=($(compgen -W '$("$1" --list-types)' -- "$cur")) + return 0 + ;; + --dump-bin | --from-dump) + local IFS=$'\n' + compopt -o filenames + COMPREPLY=($(compgen -f -- "$cur")) + return 0 + ;; + -[hVH] | --help | --version | --handle | --oem-string) + return 0 + ;; + esac + + if [[ $cur == -* ]]; then + COMPREPLY=($(compgen -W ' + --dev-mem + --help + --quiet + --string + --list-strings + --type + --list-types + --handle + --dump + --dump-bin + --from-dump + --no-sysfs + --oem-string + --version + ' -- "$cur")) + return 0 + fi + +} && complete -F _comp_cmd_dmidecode dmidecode + +# ex: filetype=sh diff --git a/completion/ownership.bash b/completion/ownership.bash new file mode 100644 index 0000000..6a25d29 --- /dev/null +++ b/completion/ownership.bash @@ -0,0 +1,33 @@ +# bash completion for ownership -*- shell-script -*- + +_comp_cmd_ownership() { + local cur prev + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD - 1]} + + case $prev in + -d | --dev-mem) + : "${cur:=/dev/}" + local IFS=$'\n' + compopt -o filenames + COMPREPLY=($(compgen -f -- "$cur")) + return 0 + ;; + -[hV] | --help | --version) + return 0 + ;; + esac + + if [[ $cur == -* ]]; then + COMPREPLY=($(compgen -W ' + --dev-mem + --help + --version + ' -- "$cur")) + return 0 + fi + +} && complete -F _comp_cmd_ownership ownership + +# ex: filetype=sh diff --git a/completion/vpddecode.bash b/completion/vpddecode.bash new file mode 100644 index 0000000..0745127 --- /dev/null +++ b/completion/vpddecode.bash @@ -0,0 +1,43 @@ +# bash completion for vpddecode -*- shell-script -*- + +_comp_cmd_vpddecode() { + local cur prev + COMPREPLY=() + cur=${COMP_WORDS[COMP_CWORD]} + prev=${COMP_WORDS[COMP_CWORD - 1]} + + case $prev in + -d | --dev-mem) + : "${cur:=/dev/}" + local IFS=$'\n' + compopt -o filenames + COMPREPLY=($(compgen -f -- "$cur")) + return 0 + ;; + -s | --string) + COMPREPLY=($(compgen -W '$( + "$1" --string 2>&1 | while IFS=\$'\\n' read -r line ; do + [[ $line == " "* ]] && printf "%s\n" "$line" + done + ' -- "$cur")) + return 0 + ;; + -[hV] | --help | --version) + return 0 + ;; + esac + + if [[ $cur == -* ]]; then + COMPREPLY=($(compgen -W ' + --dev-mem + --help + --string + --dump + --version + ' -- "$cur")) + return 0 + fi + +} && complete -F _comp_cmd_vpddecode vpddecode + +# ex: filetype=sh diff --git a/dmidecode.c b/dmidecode.c index 7a4702e..c1d743c 100644 --- a/dmidecode.c +++ b/dmidecode.c @@ -2,7 +2,7 @@ * DMI Decode * * Copyright (C) 2000-2002 Alan Cox - * Copyright (C) 2002-2020 Jean Delvare + * Copyright (C) 2002-2024 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 @@ -57,9 +57,12 @@ * 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 + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0270_1.3.0.pdf + * - LoongArch Reference Manual, volume 1 + * https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#_cpucfg */ +#include #include #include #include @@ -68,7 +71,7 @@ #include #include -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__DragonFly__) #include #include #endif @@ -87,7 +90,7 @@ static const char *bad_index = ""; enum cpuid_type cpuid_type = cpuid_none; -#define SUPPORTED_SMBIOS_VER 0x030500 +#define SUPPORTED_SMBIOS_VER 0x030700 #define FLAG_NO_FILE_OFFSET (1 << 0) #define FLAG_STOP_AT_EOT (1 << 1) @@ -785,6 +788,7 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver) { 0x13, "M2" }, { 0x14, "Celeron M" }, { 0x15, "Pentium 4 HT" }, + { 0x16, "Intel" }, { 0x18, "Duron" }, { 0x19, "K5" }, @@ -978,6 +982,8 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver) { 0x100, "ARMv7" }, { 0x101, "ARMv8" }, + { 0x102, "ARMv9" }, + { 0x103, "ARM" }, { 0x104, "SH-3" }, { 0x105, "SH-4" }, { 0x118, "ARM" }, @@ -992,6 +998,24 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver) { 0x200, "RV32" }, { 0x201, "RV64" }, { 0x202, "RV128" }, + + { 0x258, "LoongArch" }, + { 0x259, "Loongson 1" }, + { 0x25A, "Loongson 2" }, + { 0x25B, "Loongson 3" }, + { 0x25C, "Loongson 2K" }, + { 0x25D, "Loongson 3A" }, + { 0x25E, "Loongson 3B" }, + { 0x25F, "Loongson 3C" }, + { 0x260, "Loongson 3D" }, + { 0x261, "Loongson 3E" }, + { 0x262, "Dual-Core Loongson 2K 2xxx" }, + { 0x26C, "Quad-Core Loongson 3A 5xxx" }, + { 0x26D, "Multi-Core Loongson 3A 5xxx" }, + { 0x26E, "Quad-Core Loongson 3B 5xxx" }, + { 0x26F, "Multi-Core Loongson 3B 5xxx" }, + { 0x270, "Multi-Core Loongson 3C 5xxx" }, + { 0x271, "Multi-Core Loongson 3D 5xxx" }, }; /* * Note to developers: when adding entries to this list, check if @@ -1077,7 +1101,7 @@ static enum cpuid_type dmi_get_cpuid_type(const struct dmi_header *h) else return cpuid_80486; } - else if ((type >= 0x100 && type <= 0x101) /* ARM */ + else if ((type >= 0x100 && type <= 0x102) /* ARM */ || (type >= 0x118 && type <= 0x119)) /* ARM */ { /* @@ -1110,24 +1134,9 @@ 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; - } + else if ((type >= 0x258 && type <= 0x262) /* Loongarch */ + || (type >= 0x26C && type <= 0x271)) /* Loongarch */ + return cpuid_loongarch; /* neither X86 nor ARM */ return cpuid_none; @@ -1225,6 +1234,11 @@ void dmi_print_cpuid(void (*print_cb)(const char *name, const char *format, ...) ((eax >> 4) & 0xF) | (((eax >> 8) & 0xF) == 0xF ? (eax >> 12) & 0xF0 : 0), eax & 0xF); break; + + case cpuid_loongarch: /* LoongArch Reference Manual, volume 1 */ + eax = DWORD(p); + print_cb(label, "Processor Identity 0x%08x\n", eax); + break; default: return; } @@ -1437,10 +1451,27 @@ static const char *dmi_processor_upgrade(u8 code) "Socket BGA1528", "Socket LGA4189", "Socket LGA1200", - "Socket LGA4677" /* 0x3F */ + "Socket LGA4677", + "Socket LGA1700", + "Socket BGA1744", + "Socket BGA1781", + "Socket BGA1211", + "Socket BGA2422", + "Socket LGA1211", + "Socket LGA2422", + "Socket LGA5773", + "Socket BGA5773", + "Socket AM5", + "Socket SP5", + "Socket SP6", + "Socket BGA883", + "Socket BGA1190", + "Socket BGA4129", + "Socket LGA4710", + "Socket LGA7529" /* 0x50 */ }; - if (code >= 0x01 && code <= 0x3F) + if (code >= 0x01 && code <= 0x50) return upgrade[code - 0x01]; return out_of_spec; } @@ -1961,14 +1992,16 @@ static const char *dmi_port_type(u8 code) "Modem Port", "Network Port", "SATA", - "SAS" /* 0x21 */ + "SAS", + "MFDP (Multi-Function Display Port)", + "Thunderbolt" /* 0x23 */ }; static const char *type_0xA0[] = { "8251 Compatible", /* 0xA0 */ "8251 FIFO Compatible" /* 0xA1 */ }; - if (code <= 0x21) + if (code <= 0x23) return type[code]; if (code >= 0xA0 && code <= 0xA1) return type_0xA0[code - 0xA0]; @@ -2084,49 +2117,31 @@ static const char *dmi_slot_type(u8 code) return out_of_spec; } -/* If hide_unknown is set, return NULL instead of "Other" or "Unknown" */ -static const char *dmi_slot_bus_width(u8 code, int hide_unknown) +static const char *dmi_slot_bus_width(u8 code) { /* 7.10.2 */ static const char *width[] = { "Other", /* 0x01 */ "Unknown", - "8-bit", - "16-bit", - "32-bit", - "64-bit", - "128-bit", - "x1", - "x2", - "x4", - "x8", - "x12", - "x16", - "x32" /* 0x0E */ + "8 bit", + "16 bit", + "32 bit", + "64 bit", + "128 bit", + "1x or x1", + "2x or x2", + "4x or x4", + "8x or x8", + "12x or x12", + "16x or x16", + "32x or x32" /* 0x0E */ }; if (code >= 0x01 && code <= 0x0E) - { - if (code <= 0x02 && hide_unknown) - return NULL; return width[code - 0x01]; - } return out_of_spec; } -static void dmi_slot_type_with_width(u8 type, u8 width) -{ - const char *type_str, *width_str; - - type_str = dmi_slot_type(type); - width_str = dmi_slot_bus_width(width, 1); - - if (width_str) - pr_attr("Type", "%s %s", width_str, type_str); - else - pr_attr("Type", "%s", type_str); -} - static const char *dmi_slot_current_usage(u8 code) { /* 7.10.3 */ @@ -2242,12 +2257,13 @@ static void dmi_slot_characteristics(const char *attr, u8 code1, u8 code2) "PCIe slot bifurcation is supported", "Async/surprise removal is supported", "Flexbus slot, CXL 1.0 capable", - "Flexbus slot, CXL 2.0 capable" /* 6 */ + "Flexbus slot, CXL 2.0 capable", + "Flexbus slot, CXL 3.0 capable" /* 7 */ }; if (code1 & (1 << 0)) pr_attr(attr, "Unknown"); - else if ((code1 & 0xFE) == 0 && (code2 & 0x07) == 0) + else if ((code1 & 0xFE) == 0 && code2 == 0) pr_attr(attr, "None"); else { @@ -2257,7 +2273,7 @@ static void dmi_slot_characteristics(const char *attr, u8 code1, u8 code2) for (i = 1; i <= 7; i++) if (code1 & (1 << i)) pr_list_item("%s", characteristics1[i - 1]); - for (i = 0; i <= 6; i++) + for (i = 0; i <= 7; i++) if (code2 & (1 << i)) pr_list_item("%s", characteristics2[i]); pr_list_end(); @@ -2336,7 +2352,7 @@ static void dmi_slot_physical_width(u8 code) { if (code) pr_attr("Slot Physical Width", "%s", - dmi_slot_bus_width(code, 0)); + dmi_slot_bus_width(code)); } static void dmi_slot_pitch(u16 code) @@ -2662,7 +2678,7 @@ static const char *dmi_memory_array_location(u8 code) "PC-98/C24 Add-on Card", "PC-98/E Add-on Card", "PC-98/Local Bus Add-on Card", - "CXL Flexbus 1.0" /* 0xA4 */ + "CXL Add-on Card" /* 0xA4 */ }; if (code >= 0x01 && code <= 0x0A) @@ -2727,7 +2743,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); @@ -2848,10 +2864,11 @@ static const char *dmi_memory_device_type(u8 code) "HBM", "HBM2", "DDR5", - "LPDDR5" /* 0x23 */ + "LPDDR5", + "HBM3" /* 0x24 */ }; - if (code >= 0x01 && code <= 0x23) + if (code >= 0x01 && code <= 0x24) return type[code - 0x01]; return out_of_spec; } @@ -2959,6 +2976,8 @@ static void dmi_memory_manufacturer_id(const char *attr, u16 code) { /* 7.18.8 */ /* 7.18.10 */ + /* 7.18.15 */ + /* 7.17.17 */ /* LSB is 7-bit Odd Parity number of continuation codes */ if (code == 0) pr_attr(attr, "Unknown"); @@ -2989,6 +3008,45 @@ static void dmi_memory_size(const char *attr, u64 code) dmi_print_memory_size(attr, code, 0); } +static void dmi_memory_revision(const char *attr_type, u16 code, u8 mem_type) +{ + /* 7.18.16 */ + /* 7.18.18 */ + char attr[22]; + + if (code == 0xFF00) + { + snprintf(attr, sizeof(attr), "%s Revision Number", attr_type); + pr_attr(attr, "Unknown"); + } + else if (mem_type == 0x22 || mem_type == 0x23) /* DDR5 */ + { + u8 dev_type = (code >> 8) & 0x0F; + u8 dev_rev = code & 0xFF; + + if (code & 0x8000) /* Installed */ + { + snprintf(attr, sizeof(attr), "%s Device Type", + attr_type); + pr_attr(attr, "%hu", dev_type); + snprintf(attr, sizeof(attr), "%s Device Revision", + attr_type); + pr_attr(attr, "%hu.%hu", dev_rev >> 4, dev_rev & 0x0F); + } + else + { + snprintf(attr, sizeof(attr), "%s Device Type", + attr_type); + pr_attr(attr, "Not Installed"); + } + } + else /* Generic fallback */ + { + snprintf(attr, sizeof(attr), "%s Revision Number", attr_type); + pr_attr(attr, "0x%04x", code); + } +} + /* * 7.19 32-bit Memory Error Information (Type 18) */ @@ -3835,7 +3893,7 @@ static const char *dmi_protocol_record_type(u8 type) } /* - * DSP0270: 8.6: Protocol IP Assignment types + * DSP0270: 8.4.2: Protocol IP Assignment types */ static const char *dmi_protocol_assignment_type(u8 type) { @@ -3853,7 +3911,7 @@ static const char *dmi_protocol_assignment_type(u8 type) } /* - * DSP0270: 8.6: Protocol IP Address type + * DSP0270: 8.4.3: Protocol IP Address type */ static const char *dmi_address_type(u8 type) { @@ -3869,7 +3927,7 @@ static const char *dmi_address_type(u8 type) } /* - * DSP0270: 8.6 Protocol Address decode + * DSP0270: 8.4.3 Protocol Address decode */ static const char *dmi_address_decode(u8 *data, char *storage, u8 addrtype) { @@ -3881,7 +3939,7 @@ static const char *dmi_address_decode(u8 *data, char *storage, u8 addrtype) } /* - * DSP0270: 8.5: Parse the protocol record format + * DSP0270: 8.4: Parse the protocol record format */ static void dmi_parse_protocol_record(u8 *rec) { @@ -3896,11 +3954,11 @@ static void dmi_parse_protocol_record(u8 *rec) const char *hname; char attr[38]; - /* DSP0270: 8.5: Protocol Identifier */ + /* DSP0270: 8.4: Protocol Identifier */ rid = rec[0x0]; - /* DSP0270: 8.5: Protocol Record Length */ + /* DSP0270: 8.4: Protocol Record Length */ rlen = rec[0x1]; - /* DSP0270: 8.5: Protocol Record Data */ + /* DSP0270: 8.4: Protocol Record Data */ rdata = &rec[0x2]; pr_attr("Protocol ID", "%02x (%s)", rid, @@ -3909,7 +3967,7 @@ static void dmi_parse_protocol_record(u8 *rec) /* * Don't decode anything other than Redfish for now * Note 0x4 is Redfish over IP in 7.43.2 - * and DSP0270: 8.5 + * and DSP0270: 8.4 */ if (rid != 0x4) return; @@ -3923,7 +3981,7 @@ static void dmi_parse_protocol_record(u8 *rec) return; /* - * DSP0270: 8.6: Redfish Over IP Service UUID + * DSP0270: 8.4.1: 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 @@ -3936,7 +3994,7 @@ static void dmi_parse_protocol_record(u8 *rec) dmi_system_uuid(pr_subattr, "Service UUID", &rdata[0], 0x311); /* - * DSP0270: 8.6: Redfish Over IP Host IP Assignment Type + * DSP0270: 8.4.1: Redfish Over IP Host IP Assignment Type * Note, using decimal indices here, as the DSP0270 * uses decimal, so as to make it more comparable */ @@ -3944,34 +4002,34 @@ static void dmi_parse_protocol_record(u8 *rec) pr_subattr("Host IP Assignment Type", "%s", dmi_protocol_assignment_type(assign_val)); - /* DSP0270: 8.6: Redfish Over IP Host Address format */ + /* DSP0270: 8.4.1: Redfish Over IP Host Address format */ addrtype = rdata[17]; addrstr = dmi_address_type(addrtype); pr_subattr("Host IP Address Format", "%s", addrstr); - /* DSP0270: 8.6 IP Assignment types */ + /* DSP0270: 8.4.1 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 */ + /* DSP0270: 8.4.1: the Host IPv[4|6] Address */ sprintf(attr, "%s Address", addrstr); pr_subattr(attr, "%s", dmi_address_decode(&rdata[18], buf, addrtype)); - /* DSP0270: 8.6: Prints the Host IPv[4|6] Mask */ + /* DSP0270: 8.4.1: Prints the Host IPv[4|6] Mask */ sprintf(attr, "%s Mask", addrstr); pr_subattr(attr, "%s", dmi_address_decode(&rdata[34], buf, addrtype)); } - /* DSP0270: 8.6: Get the Redfish Service IP Discovery Type */ + /* DSP0270: 8.4.1: Get the Redfish Service IP Discovery Type */ assign_val = rdata[50]; /* Redfish Service IP Discovery type mirrors Host IP Assignment type */ pr_subattr("Redfish Service IP Discovery Type", "%s", dmi_protocol_assignment_type(assign_val)); - /* DSP0270: 8.6: Get the Redfish Service IP Address Format */ + /* DSP0270: 8.4.1: Get the Redfish Service IP Address Format */ addrtype = rdata[51]; addrstr = dmi_address_type(addrtype); pr_subattr("Redfish Service IP Address Format", "%s", @@ -3982,30 +4040,30 @@ static void dmi_parse_protocol_record(u8 *rec) u16 port; u32 vlan; - /* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Address */ + /* DSP0270: 8.4.1: Prints the Redfish IPv[4|6] Service Address */ sprintf(attr, "%s Redfish Service Address", addrstr); pr_subattr(attr, "%s", dmi_address_decode(&rdata[52], buf, addrtype)); - /* DSP0270: 8.6: Prints the Redfish IPv[4|6] Service Mask */ + /* DSP0270: 8.4.1: Prints the Redfish IPv[4|6] Service Mask */ sprintf(attr, "%s Redfish Service Mask", addrstr); pr_subattr(attr, "%s", dmi_address_decode(&rdata[68], buf, addrtype)); - /* DSP0270: 8.6: Redfish vlan and port info */ + /* DSP0270: 8.4.1: Redfish vlan and port info */ port = WORD(&rdata[84]); vlan = DWORD(&rdata[86]); pr_subattr("Redfish Service Port", "%hu", port); pr_subattr("Redfish Service Vlan", "%u", vlan); } - /* DSP0270: 8.6: Redfish host length and name */ + /* DSP0270: 8.4.1: Redfish host length and name */ hlen = rdata[90]; /* - * DSP0270: 8.6: The length of the host string + 91 (the minimum + * DSP0270: 8.4.1: The length of the host string + 91 (the minimum * size of a protocol record) cannot exceed the record length * (rec[0x1]) */ @@ -4026,15 +4084,39 @@ static const char *dmi_parse_device_type(u8 type) const char *devname[] = { "USB", /* 0x2 */ "PCI/PCIe", /* 0x3 */ + "USB v2", /* 0x4 */ + "PCI/PCIe v2", /* 0x5 */ }; - if (type >= 0x2 && type <= 0x3) + if (type >= 0x2 && type <= 0x5) return devname[type - 0x2]; if (type >= 0x80) return "OEM"; return out_of_spec; } +/* + * DSP0270: 8.3.7: Device Characteristics + */ +static void dmi_device_characteristics(u16 code) +{ + const char *characteristics[] = { + "Credential bootstrapping via IPMI is supported", /* 0 */ + /* Reserved */ + }; + + if ((code & 0x1) == 0) + pr_list_item("None"); + else + { + int i; + + for (i = 0; i < 1; i++) + if (code & (1 << i)) + pr_list_item("%s", characteristics[i]); + } +} + static void dmi_parse_controller_structure(const struct dmi_header *h) { int i; @@ -4077,7 +4159,7 @@ static void dmi_parse_controller_structure(const struct dmi_header *h) if (len != 0) { - /* DSP0270: 8.3 Table 2: Device Type */ + /* DSP0270: 8.3.1 Table 3: Device Type values */ type = data[0x6]; pr_attr("Device Type", "%s", @@ -4114,7 +4196,84 @@ static void dmi_parse_controller_structure(const struct dmi_header *h) pr_attr("SubDeviceID", "0x%04x", WORD(&pcidata[0x6])); } - else if (type == 0x4 && len >= 5) + else if (type == 0x4 && len >= 0x0d) + { + /* USB Device Type v2 - need at least 12 bytes */ + u8 *usbdata = &data[7]; + /* USB Device Descriptor v2: idVendor */ + pr_attr("idVendor", "0x%04x", + WORD(&usbdata[0x1])); + /* USB Device Descriptor v2: idProduct */ + pr_attr("idProduct", "0x%04x", + WORD(&usbdata[0x3])); + + /* + * USB Serial number is here, but its useless, don't + * bother decoding it + */ + + /* USB Device Descriptor v2: MAC Address */ + pr_attr("MAC Address", "%02x:%02x:%02x:%02x:%02x:%02x", + usbdata[0x6], usbdata[0x7], usbdata[0x8], + usbdata[0x9], usbdata[0xa], usbdata[0xb]); + + /* DSP0270 v1.3.0 support */ + if (len >= 0x11) + { + /* USB Device Descriptor v2: Device Characteristics */ + pr_list_start("Device Characteristics", NULL); + dmi_device_characteristics(WORD(&usbdata[0xc])); + pr_list_end(); + + /* USB Device Descriptor v2: Credential Bootstrapping Handle */ + if (WORD(&usbdata[0x0c]) & 0x1) + { + pr_attr("Credential Bootstrapping Handle", "0x%04x", + WORD(&usbdata[0xe])); + } + } + } + else if (type == 0x5 && len >= 0x14) + { + /* PCI Device Type v2 - Need at least 19 bytes */ + u8 *pcidata = &data[0x7]; + /* PCI Device Descriptor v2: VendorID */ + pr_attr("VendorID", "0x%04x", + WORD(&pcidata[0x1])); + /* PCI Device Descriptor v2: DeviceID */ + pr_attr("DeviceID", "0x%04x", + WORD(&pcidata[0x3])); + /* PCI Device Descriptor v2: PCI SubvendorID */ + pr_attr("SubVendorID", "0x%04x", + WORD(&pcidata[0x5])); + /* PCI Device Descriptor v2: PCI SubdeviceID */ + pr_attr("SubDeviceID", "0x%04x", + WORD(&pcidata[0x7])); + /* PCI Device Descriptor v2: MAC Address */ + pr_attr("MAC Address", "%02x:%02x:%02x:%02x:%02x:%02x", + pcidata[0x9], pcidata[0xa], pcidata[0xb], + pcidata[0xc], pcidata[0xd], pcidata[0xe]); + /* PCI Device Descriptor v2: + * Segment Group Number, Bus Number, Device/Function Number + */ + dmi_slot_segment_bus_func(WORD(&pcidata[0xf]), pcidata[0x11], pcidata[0x12]); + + /* DSP0270 v1.3.0 support */ + if (len >= 0x18) + { + /* PCI Device Descriptor v2: Device Characteristics */ + pr_list_start("Device Characteristics", NULL); + dmi_device_characteristics(WORD(&pcidata[0x13]) ); + pr_list_end(); + /* PCI Device Descriptor v2: Credential Bootstrapping Handle */ + if (WORD(&pcidata[0x13]) & 0x1) + { + pr_attr("Credential Bootstrapping Handle", "0x%04x", + WORD(&pcidata[0x15])); + } + } + } + else if (type >= 0x80 && len >= 5) { /* OEM Device Type - Need at least 4 bytes */ u8 *oemdata = &data[0x7]; @@ -4127,7 +4286,7 @@ static void dmi_parse_controller_structure(const struct dmi_header *h) } /* - * DSP0270: 8.2 and 8.5: Protocol record count and protocol records + * DSP0270: 8.2 and 8.4: Protocol record count and protocol records * Move to the Protocol Count. */ data = &data[total_read]; @@ -4170,7 +4329,7 @@ static void dmi_parse_controller_structure(const struct dmi_header *h) dmi_parse_protocol_record(rec); /* - * DSP0270: 8.6 + * DSP0270: 8.4.1 * 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, @@ -4473,6 +4632,9 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) pr_attr("Thread Count", "%u", h->length >= 0x30 && data[0x25] == 0xFF ? WORD(data + 0x2E) : data[0x25]); + if (h->length >= 0x32 && WORD(data + 0x30) != 0) + pr_attr("Thread Enabled", "%u", + WORD(data + 0x30)); dmi_processor_characteristics("Characteristics", WORD(data + 0x26)); break; @@ -4570,7 +4732,8 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) if (h->length < 0x0C) break; pr_attr("Designation", "%s", dmi_string(h, data[0x04])); - dmi_slot_type_with_width(data[0x05], data[0x06]); + pr_attr("Type", "%s", dmi_slot_type(data[0x05])); + pr_attr("Data Bus Width", "%s", dmi_slot_bus_width(data[0x06])); pr_attr("Current Usage", "%s", dmi_slot_current_usage(data[0x07])); pr_attr("Length", "%s", @@ -4583,7 +4746,7 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) if (h->length < 0x11) break; dmi_slot_segment_bus_func(WORD(data + 0x0D), data[0x0F], data[0x10]); if (h->length < 0x13) break; - pr_attr("Data Bus Width", "%u", data[0x11]); + pr_attr("Data Bus Width (Base)", "%u", data[0x11]); pr_attr("Peer Devices", "%u", data[0x12]); if (h->length < 0x13 + data[0x12] * 5) break; dmi_slot_peers(data[0x12], data + 0x13); @@ -4725,7 +4888,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 ? @@ -4777,6 +4940,15 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) dmi_memory_size("Cache Size", QWORD(data + 0x44)); if (h->length < 0x54) break; dmi_memory_size("Logical Size", QWORD(data + 0x4C)); + if (h->length < 0x64) break; + dmi_memory_manufacturer_id("PMIC0 Manufacturer ID", + WORD(data + 0x5C)); + dmi_memory_revision("PMIC0", WORD(data + 0x5E), + data[0x12]); + dmi_memory_manufacturer_id("RCD Manufacturer ID", + WORD(data + 0x60)); + dmi_memory_revision("RCD", WORD(data + 0x62), + data[0x12]); break; case 18: /* 7.19 32-bit Memory Error Information */ @@ -5447,12 +5619,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); @@ -5471,11 +5643,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.", 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.", len, opt.dumpfile); - write_dump(32, len, buf, opt.dumpfile, 0); + 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) @@ -5597,7 +5823,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) @@ -5638,8 +5864,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; @@ -5658,7 +5885,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); @@ -5676,19 +5903,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) { @@ -5698,15 +5925,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; } @@ -5739,13 +5960,14 @@ static void overwrite_smbios3_address(u8 *buf) buf[0x17] = 0; } -static int smbios3_decode(u8 *buf, const char *devmem, u32 flags) +static int smbios3_decode(u8 *buf, size_t buf_len, 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) + if (buf[0x06] > buf_len) { fprintf(stderr, "Entry point length too large (%u bytes, expected %u).\n", @@ -5753,7 +5975,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]; @@ -5768,8 +5991,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) { @@ -5778,59 +6005,81 @@ 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 int smbios_decode(u8 *buf, const char *devmem, u32 flags) +static void dmi_fixup_version(u16 *ver) { - 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)) - return 0; - - ver = (buf[0x06] << 8) + buf[0x07]; /* Some BIOS report weird SMBIOS version, fix that up */ - switch (ver) + 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; + *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; + *ver = 0x0206; break; } +} + +static int smbios_decode(u8 *buf, size_t buf_len, const char *devmem, u32 flags) +{ + u16 ver, num; + u32 len; + u8 *table; + + /* Don't let checksum run beyond the buffer */ + if (buf[0x05] > buf_len) + { + fprintf(stderr, + "Entry point length too large (%u bytes, expected %u).\n", + (unsigned int)buf[0x05], 0x1FU); + return 0; + } + + /* + * 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]; + 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) { @@ -5839,27 +6088,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) { @@ -5868,12 +6129,15 @@ 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; } @@ -5888,7 +6152,7 @@ static int address_from_efi(off_t *address) FILE *efi_systab; const char *filename; char linebuf[64]; -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) char addrstr[KENV_MVALLEN + 1]; #endif const char *eptype; @@ -5926,11 +6190,14 @@ 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__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) /* * On FreeBSD, SMBIOS anchor base address in UEFI mode is exposed * via kernel environment: * https://svnweb.freebsd.org/base?view=revision&revision=307326 + * + * DragonFly BSD adopted the same method as FreeBSD, see commit + * 5e488df32cb01056a5b714a522e51c69ab7b4612 */ ret = kenv(KENV_GET, "hint.smbios.0.mem", addrstr, sizeof(addrstr)); if (ret == -1) @@ -5987,6 +6254,12 @@ int main(int argc, char * const argv[]) goto exit_free; } + if (opt.flags & FLAG_LIST) + { + /* Already handled in parse_command_line() */ + goto exit_free; + } + if (opt.flags & FLAG_HELP) { print_help(); @@ -6022,25 +6295,33 @@ int main(int argc, char * const argv[]) } /* Read from dump if so instructed */ + size = 0x20; if (opt.flags & FLAG_FROM_DUMP) { if (!(opt.flags & FLAG_QUIET)) pr_info("Reading SMBIOS/DMI data from file %s.", opt.dumpfile); - if ((buf = mem_chunk(0, 0x20, opt.dumpfile)) == NULL) + if ((buf = read_file(0, &size, opt.dumpfile)) == NULL) { ret = 1; goto exit_free; } + /* Truncated entry point can't be processed */ + if (size < 0x20) + { + ret = 1; + goto done; + } + if (memcmp(buf, "_SM3_", 5) == 0) { - if (smbios3_decode(buf, opt.dumpfile, 0)) + if (smbios3_decode(buf, size, opt.dumpfile, 0)) found++; } else if (memcmp(buf, "_SM_", 4) == 0) { - if (smbios_decode(buf, opt.dumpfile, 0)) + if (smbios_decode(buf, size, opt.dumpfile, 0)) found++; } else if (memcmp(buf, "_DMI_", 5) == 0) @@ -6056,7 +6337,6 @@ 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(0, &size, SYS_ENTRY_FILE)) != NULL) { @@ -6064,12 +6344,12 @@ int main(int argc, char * const argv[]) pr_info("Getting SMBIOS data from sysfs."); if (size >= 24 && memcmp(buf, "_SM3_", 5) == 0) { - if (smbios3_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET)) + if (smbios3_decode(buf, size, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET)) found++; } else if (size >= 31 && memcmp(buf, "_SM_", 4) == 0) { - if (smbios_decode(buf, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET)) + if (smbios_decode(buf, size, SYS_TABLE_FILE, FLAG_NO_FILE_OFFSET)) found++; } else if (size >= 15 && memcmp(buf, "_DMI_", 5) == 0) @@ -6106,12 +6386,12 @@ int main(int argc, char * const argv[]) if (memcmp(buf, "_SM3_", 5) == 0) { - if (smbios3_decode(buf, opt.devmem, 0)) + if (smbios3_decode(buf, 0x20, opt.devmem, 0)) found++; } else if (memcmp(buf, "_SM_", 4) == 0) { - if (smbios_decode(buf, opt.devmem, 0)) + if (smbios_decode(buf, 0x20, opt.devmem, 0)) found++; } goto done; @@ -6132,7 +6412,7 @@ int main(int argc, char * const argv[]) { if (memcmp(buf + fp, "_SM3_", 5) == 0) { - if (smbios3_decode(buf + fp, opt.devmem, 0)) + if (smbios3_decode(buf + fp, 0x20, opt.devmem, 0)) { found++; goto done; @@ -6145,7 +6425,7 @@ int main(int argc, char * const argv[]) { if (memcmp(buf + fp, "_SM_", 4) == 0 && fp <= 0xFFE0) { - if (smbios_decode(buf + fp, opt.devmem, 0)) + if (smbios_decode(buf + fp, 0x20, opt.devmem, 0)) { found++; goto done; diff --git a/dmidecode.h b/dmidecode.h index 318cdc6..e03c957 100644 --- a/dmidecode.h +++ b/dmidecode.h @@ -1,7 +1,7 @@ /* * This file is part of the dmidecode project. * - * Copyright (C) 2005-2020 Jean Delvare + * Copyright (C) 2005-2023 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 @@ -40,6 +40,7 @@ enum cpuid_type cpuid_arm_soc_id, cpuid_x86_intel, cpuid_x86_amd, + cpuid_loongarch, }; extern enum cpuid_type cpuid_type; diff --git a/dmidecode.xcodeproj/project.pbxproj b/dmidecode.xcodeproj/project.pbxproj index fe20913..7b2aa49 100644 --- a/dmidecode.xcodeproj/project.pbxproj +++ b/dmidecode.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ 12ECFB411852EADE0028E3A9 /* biosdecode */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = biosdecode; sourceTree = ""; }; 12ECFB421852EADE0028E3A9 /* biosdecode.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = biosdecode.c; sourceTree = ""; }; 12ECFB431852EADE0028E3A9 /* biosdecode.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = biosdecode.o; sourceTree = ""; }; - 12ECFB441852EADE0028E3A9 /* CHANGELOG */ = {isa = PBXFileReference; lastKnownFileType = text; path = CHANGELOG; sourceTree = ""; }; 12ECFB451852EADE0028E3A9 /* config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = config.h; sourceTree = ""; }; 12ECFB461852EADE0028E3A9 /* dmidecode */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = dmidecode; sourceTree = ""; }; 12ECFB471852EADE0028E3A9 /* dmidecode.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = dmidecode.c; sourceTree = ""; }; @@ -30,7 +29,6 @@ 12ECFB531852EADE0028E3A9 /* ownership */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = ownership; sourceTree = ""; }; 12ECFB541852EADE0028E3A9 /* ownership.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ownership.c; sourceTree = ""; }; 12ECFB551852EADE0028E3A9 /* ownership.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = ownership.o; sourceTree = ""; }; - 12ECFB561852EADE0028E3A9 /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = ""; }; 12ECFB571852EADE0028E3A9 /* types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = ""; }; 12ECFB581852EADE0028E3A9 /* util.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = util.c; sourceTree = ""; }; 12ECFB591852EADE0028E3A9 /* util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = util.h; sourceTree = ""; }; @@ -53,7 +51,6 @@ 12ECFB411852EADE0028E3A9 /* biosdecode */, 12ECFB421852EADE0028E3A9 /* biosdecode.c */, 12ECFB431852EADE0028E3A9 /* biosdecode.o */, - 12ECFB441852EADE0028E3A9 /* CHANGELOG */, 12ECFB451852EADE0028E3A9 /* config.h */, 12ECFB461852EADE0028E3A9 /* dmidecode */, 12ECFB471852EADE0028E3A9 /* dmidecode.c */, @@ -71,7 +68,6 @@ 12ECFB531852EADE0028E3A9 /* ownership */, 12ECFB541852EADE0028E3A9 /* ownership.c */, 12ECFB551852EADE0028E3A9 /* ownership.o */, - 12ECFB561852EADE0028E3A9 /* README */, 12ECFB571852EADE0028E3A9 /* types.h */, 12ECFB581852EADE0028E3A9 /* util.c */, 12ECFB591852EADE0028E3A9 /* util.h */, @@ -109,7 +105,7 @@ 12ECFB3A1852EADE0028E3A9 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1230; + LastUpgradeCheck = 1530; }; buildConfigurationList = 12ECFB3D1852EADE0028E3A9 /* Build configuration list for PBXProject "dmidecode" */; compatibilityVersion = "Xcode 3.2"; diff --git a/dmioem.c b/dmioem.c index 0c73771..2f1f9f6 100644 --- a/dmioem.c +++ b/dmioem.c @@ -2,7 +2,8 @@ * Decoding of OEM-specific entries * This file is part of the dmidecode project. * - * Copyright (C) 2007-2020 Jean Delvare + * Copyright (C) 2007-2024 Jean Delvare + * Copyright (C) 2017-2024 Jerry Hoemann * * 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 @@ -161,11 +162,12 @@ static void dmi_print_hp_net_iface_rec(u8 id, u8 bus, u8 dev, const u8 *mac) } } -typedef enum { G6 = 6, G7, G8, G9, G10, G10P } dmi_hpegen_t; +typedef enum { G6 = 6, G7, G8, G9, G10, G10P, G11 } dmi_hpegen_t; static int dmi_hpegen(const char *s) { struct { const char *name; dmi_hpegen_t gen; } table[] = { + { "Gen11", G11 }, { "Gen10 Plus", G10P }, { "Gen10", G10 }, { "Gen9", G9 }, @@ -187,24 +189,21 @@ static int dmi_hpegen(const char *s) return (dmi_vendor == VENDOR_HPE) ? G10P : G6; } -static void dmi_hp_240_attr(u64 defined, u64 set) +static void dmi_hp_197_qdf(const u8 *qdf) { - static const char *attributes[] = { - "Updatable", - "Reset Required", - "Authentication Required", - "In Use", - "UEFI Image", - }; - unsigned int i; + char str[7]; + int i, j, len = 6; - pr_attr("Attributes Defined/Set", NULL); - for (i = 0; i < ARRAY_SIZE(attributes); i++) + if (!is_printable(qdf, len)) + return; + + for (i = 0, j = 0; i < len; i++) { - if (!(defined.l & (1UL << i))) - continue; - pr_subattr(attributes[i], "%s", set.l & (1UL << i) ? "Yes" : "No"); + if (qdf[i] != ' ') + str[j++] = qdf[i]; } + str[j] = '\0'; + pr_attr("QDF/S-SPEC", "%s", str); } static void dmi_hp_203_assoc_hndl(const char *fname, u16 num) @@ -282,7 +281,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", @@ -290,7 +289,13 @@ static void dmi_hp_203_devloc(const char *fname, unsigned int code) "USB", "Dynamic Smart Array Controller", "URL", - "NVMe Drive Bay" /* 0x0F */ + "NVMe Drive Bay", /* 0x0F */ + "NVDIMM Processor", + "NVDIMM Board", + "NVMe Riser", + "NVDIMM Name Space", + "VROC SATA", + "VROC NVMe", /* 0x15 */ }; if (code < ARRAY_SIZE(location)) @@ -299,6 +304,163 @@ 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 */ + "OCP cards", + "DC-SCM CPLD", + }; + + 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: + if (data[0] >> 7) + pr_attr(name, "0x%02X B.0x%02X", data[1] & 0x7F, data[0] & 0x7F); + else + pr_attr(name, "0x%02X", data[1] & 0x7F); + 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[] = { @@ -389,6 +551,31 @@ static void dmi_hp_224_chipid(u16 code) 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"; @@ -398,9 +585,11 @@ 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 */ + "Reserved", + "OCP USB", /* 0x09 */ }; if (code < ARRAY_SIZE(location)) @@ -440,6 +629,186 @@ static void dmi_hp_238_speed(const char *fname, unsigned int code) pr_attr(fname, "%s", str); } +static void dmi_hp_239_usb_device(u8 class, u8 subclass, u8 protocol) +{ + /* https://www.usb.org/defined-class-codes */ + /* https://www.usb.org/sites/default/files/Mass_Storage_Specification_Overview_v1.4_2-19-2010.pdf */ + const char *str = "Reserved"; + if (class == 0x08) + { + static const char * const sub_class_name[] = { + "SCSI command set not reported", /* 0x00 */ + "RBC", + "ATAPI", + "Obsolete", + "UFI", + "Obsolete", + "SCSI", + "LSD FS", + "IEEE 1667" /* 0x08 */ + }; + pr_attr("USB Class", "%s", "Mass Storage"); + if (subclass == 0xFF) + { + str = "Vendor Specific"; + } + else if (subclass < ARRAY_SIZE(sub_class_name)) + { + str = sub_class_name[subclass]; + } + pr_attr("USB SubClass", "%s", str); + + switch (protocol) { + case 0x00: + str = "CBI w/ completion interrupt"; + break; + case 0x01: + str = "CBI w/o completion interrupt"; + break; + case 0x02: + str = "Obsolete"; + break; + case 0x50: + str = "Bulk-Only"; + break; + case 0x62: + str = "UAS"; + break; + case 0xFF: + str = "Vendor Specific"; + break; + default: + str = "Reserved"; + } + pr_attr("USB Protocol", "%s", str); + } + else if (class == 0x09 && subclass == 0) + { + pr_attr("USB Class", "%s", "HUB"); + switch (protocol) { + case 0: + str = "Full Speed"; + break; + case 1: + str = "Hi-Speed w/ single TT"; + break; + case 2: + str = "Hi-Speed w/ multiple TT"; + break; + default: + str = "Reserved"; + } + pr_attr("USB Protocol", str); + } + else + { + pr_attr("USB Class", "0x%02x", class); + pr_attr("USB SubClass", "0x%02x", subclass); + pr_attr("USB Protocol", "0x%02x", protocol); + } +} + +static void dmi_hp_240_attr(u64 defined, u64 set) +{ + static const char *attributes[] = { + "Updatable", + "Reset Required", + "Authentication Required", + "In Use", + "UEFI Image", + }; + unsigned int i; + + pr_list_start("Attributes Defined/Set", NULL); + for (i = 0; i < ARRAY_SIZE(attributes); i++) + { + if (!(defined.l & (1UL << i))) + continue; + pr_list_item("%s: %s", attributes[i], set.l & (1UL << i) ? "Yes" : "No"); + } + pr_list_end(); +} + +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 */ + }; + static const char * const form2[] = { + "EDSFF Unknown", /* 0x20 */ + "EDSFF 1U Short", + "EDSFF 1U Long", + "EDSFF E3 Short", + "EDSFF E3 Long", /* 0x24 */ + }; + if (code < ARRAY_SIZE(form)) + str = form[code]; + else if (code >= 0x20 && code < 0x20 + ARRAY_SIZE(form2)) + str = form2[code - 0x20]; + + 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 void dmi_hp_245_pcie_riser(const struct dmi_header *h) +{ + const char *str = "Reserved"; + u8 *data = h->data; + + pr_attr("Board Type", "PCIe Riser"); + if (h->length < 0x09) return; + switch (data[0x05]) + { + case 1: str = "Primary"; break; + case 2: str = "Secondary"; break; + case 3: str = "Tertiary"; break; + case 4: str = "Quaternary"; break; + case 10: str = "Front"; break; + } + pr_attr("Riser Position", "%s", str); + pr_attr("Riser ID", "%d", data[0x06]); + if (data[0x07]) + { + str = (data[0x07] >> 7) ? "B." : ""; + pr_attr("CPLD Version", "%s0x%02X", str, (data[0x07] & 0x7F)); + } + pr_attr("Riser Name", dmi_string(h, data[0x08])); +} + static int dmi_decode_hp(const struct dmi_header *h) { u8 *data = h->data; @@ -475,6 +844,69 @@ static int dmi_decode_hp(const struct dmi_header *h) pr_attr("Virtual Serial Port", "%s", feat & (1 << 4) ? "Enabled" : "Disabled"); break; + case 197: + /* + * Vendor Specific: HPE Processor Specific Information + * + * Processor Information structure (Type 197) for each possibly installed + * physical processor to go along with each standard Processor Info + * Record (Type 4). The Type 197 record will be ignored for Processor + * slots that are empty (specified in the Type 4 records). + * + * Processor Wattage value will be filled in with information gotten from + * the CPUID instruction or possibly estimated based on CPU Family/Type. + * + * Designator bytes will be 0FFh if the location of the processor does not + * use it. If a system has processor slots, but no sockets, then the value + * in the Socket Designator will be 0FFh. A system would have one or the + * other, or both. + * + * Offset | Name | Width | Description + * -------+------------+-------+------------- + * 0x00 | Type | BYTE | 0xC5, Processor Information + * 0x01 | Length | BYTE | Length of structure + * 0x02 | Handle | WORD | Unique handle + * 0x04 | Assoc Dev | WORD | Handle of Associated Type 4 Record + * 0x06 | APIC ID | BYTE | Processor local APIC ID + * 0x07 | OEM Status | BYTE | Bits: 0: BSP, 1: x2APIC, 2: Therm Margining + * 0x08 | Phys Slot | BYTE | Matches silk screen + * 0x09 | Phys Socket| BYTE | Matches silk screen + * 0x0A | Max Wattage| WORD | Rated max wattage of the processor + * 0x0C | x2APIC ID | DWORD | Processor x2APIC (if OEM Status -> x2APIC) + * 0x10 | Proc UUID | QWORD | Processor Unique Identifier + * 0x18 | Conn Speed | WORD | Interconnect speed in MT/s + * 0x1A | QDF/S-SPEC |6 BYTES| Processor QDF/S-SPEC Numbers (Intel only) + * 0x20 | Reserved | DWORD | Gen11 Reserved + */ + pr_handle_name("%s Processor Specific Information", company); + if (h->length < 0x0A) break; + if (!(opt.flags & FLAG_QUIET)) + pr_attr("Associated Handle", "0x%04X", WORD(data + 0x04)); + pr_attr("APIC ID", "%u", data[0x06]); + feat = data[0x07]; + pr_attr("BSP", "%s", feat & 0x01 ? "Yes" : "No"); + pr_attr("x2APIC", "%s", feat & 0x02 ? "Yes" : "No"); + pr_attr("Advanced Thermal Margining", "%s", feat & 0x04 ? "Yes" : "No"); + if (data[0x08] != 0xFF) + pr_attr("Physical Slot", "%d", data[0x08]); + if (data[0x09] != 0xFF) + pr_attr("Physical Socket", "%d", data[0x09]); + if (h->length < 0x0C) break; + if (WORD(data + 0x0A)) + pr_attr("Maximum Power", "%d W", WORD(data + 0x0A)); + if (h->length < 0x10) break; + if (feat & 0x02) + pr_attr("x2APIC ID", "0x%08x", DWORD(data + 0x0C)); + if (h->length < 0x18) break; + if (DWORD(data + 0x10) || DWORD(data + 0x14)) + pr_attr("UUID", "0x%08x%08x", DWORD(data + 0x14), DWORD(data + 0x10)); + if (h->length < 0x1A) break; + if (WORD(data + 0x18)) + pr_attr("Interconnect Speed", "%d MT/s", WORD(data + 0x18)); + if (h->length < 0x20) break; + dmi_hp_197_qdf(data + 0x1A); + break; + case 199: /* * Vendor Specific: CPU Microcode Patch @@ -564,6 +996,10 @@ static int dmi_decode_hp(const struct dmi_header *h) } dmi_hp_203_assoc_hndl("Parent Handle", WORD(data + 0x12)); pr_attr("Flags", "0x%04X", WORD(data + 0x14)); + if (WORD(data + 0x14) & 0x01) + pr_subattr("Peer Bifurcated Device", "Yes"); + if (WORD(data + 0x14) & 0x02) + pr_subattr("Upstream Device", "Yes"); dmi_hp_203_devtyp("Device Type", data[0x16]); dmi_hp_203_devloc("Device Location", data[0x17]); pr_attr("Device Instance", "%d", data[0x18]); @@ -628,6 +1064,7 @@ static int dmi_decode_hp(const struct dmi_header *h) * * Type 221: is deprecated in the latest docs */ + if (gen >= G8 && h->type == 221) return 0; pr_handle_name("%s %s", company, h->type == 221 ? "BIOS iSCSI NIC PCI and MAC Information" : "BIOS PXE NIC PCI and MAC Information"); @@ -650,6 +1087,7 @@ static int dmi_decode_hp(const struct dmi_header *h) * * Source: hpwdt kernel driver */ + if (gen >= G9) return 0; pr_handle_name("%s 64-bit CRU Information", company); if (h->length < 0x18) break; if (is_printable(data + 0x04, 4)) @@ -671,6 +1109,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 @@ -719,6 +1202,34 @@ static int dmi_decode_hp(const struct dmi_header *h) 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 @@ -736,6 +1247,7 @@ static int dmi_decode_hp(const struct dmi_header *h) * 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 + * 0x29 | DevPath| STRING| UEFI Device Path of network port */ pr_handle_name("%s BIOS PXE NIC PCI and MAC Information", company); @@ -746,6 +1258,8 @@ static int dmi_decode_hp(const struct dmi_header *h) nic = h->length > 0x28 ? data[0x28] : 0xFF; dmi_print_hp_net_iface_rec(nic, data[0x06], data[0x07], &data[0x08]); + if (h->length < 0x2A) break; + pr_attr("UEFI Device Path", "%s", dmi_string(h, data[0x29])); break; case 236: @@ -766,6 +1280,7 @@ static int dmi_decode_hp(const struct dmi_header *h) * 0x13 | A2 Bays | BYTE | (deprecated) Number of SAS drive bays behind port 0xA2 * 0x14 | Name | STRING| (deprecated) Backplane Name */ + if (gen >= G11) return 0; pr_handle_name("%s HDD Backplane FRU Information", company); if (h->length < 0x08) break; pr_attr("FRU I2C Address", "0x%X raw(0x%X)", data[0x4] >> 1, data[0x4]); @@ -796,7 +1311,8 @@ static int dmi_decode_hp(const struct dmi_header *h) * 0x06 | Manufacture|STRING | DIMM Manufacturer * 0x07 | Part Number|STRING | DIMM Manufacturer's Part Number * 0x08 | Serial Num |STRING | DIMM Vendor Serial Number - * 0x09 | Spare Part |STRING | DIMM Spare Part Number + * 0x09 | Man Date | BYTE | DIMM Manufacture Date (YEAR) in BCD + * 0x0A | Man Date | BYTE | DIMM Manufacture Date (WEEK) in BCD */ if (gen < G9) return 0; pr_handle_name("%s DIMM Vendor Information", company); @@ -807,8 +1323,9 @@ static int dmi_decode_hp(const struct dmi_header *h) pr_attr("DIMM Manufacturer Part Number", "%s", dmi_string(h, data[0x07])); if (h->length < 0x09) break; pr_attr("DIMM Vendor Serial Number", "%s", dmi_string(h, data[0x08])); - if (h->length < 0x0A) break; - pr_attr("DIMM Spare Part Number", "%s", dmi_string(h, data[0x09])); + if (h->length < 0x0B) break; + if (WORD(data + 0x09)) + pr_attr("DIMM Manufacture Date", "20%02x-W%02x", data[0x09], data[0x0A]); break; case 238: @@ -848,6 +1365,54 @@ static int dmi_decode_hp(const struct dmi_header *h) pr_attr("Device Path", "%s", dmi_string(h, data[0xE])); break; + case 239: + /* + * Vendor Specific: HPE USB Device Correlation Record + * + * This record provides a mechanism for software to correlate USB device + * information provided in SMBIOS record Type 8 and Type 238. It + * additionally provides device specific data that is typically not + * available in SMBIOS to allow HP tools to understand how these device + * entries correlate to both UEFI and Legacy USB Boot entries. This record + * will only contain information for a device detected by the BIOS during + * POST and does not comprehend a hot plug event after the system has + * booted. This record will only be supported on UEFI Based systems. + * + * Offset | Name | Width | Description + * -------+------------+-------+------------ + * 0x00 | Type | BYTE | 0xEF, HP Device Correlation Record + * 0x01 | Length | BYTE | Length of structure + * 0x02 | Handle | WORD | Unique handle + * 0x04 | Hand Assoc | WORD | Handle to map to Type 238 + * 0x06 | Vendor ID | WORD | Vendor ID of detected USB Device + * 0x08 | Flags | WORD | Bit[0] - Indicates presence of SD card + * 0x0A | Class | BYTE | USB Device Class per USB HID Dev Spec + * 0x0B | Sub Class | BYTE | USB Device SubClass per USB HID Dev Spec + * 0x0C | Protocol | BYTE | Device Protocol per USB HID Dev Spec + * 0x0D | Product ID | WORD | USB Product ID + * 0x0F | Capacity | DWORD | USB Device Capacity (if apropos) in Mbytes + * 0x13 | Device Path| STRING| UEFI Device Path + * 0x14 | Device Name| STRING| UEFI Device Structured Name + * 0x15 | UEFI Name | STRING| Device Name + * 0x16 | Location | STRING| USB Device Location + */ + if (gen < G9) return 0; + pr_handle_name("%s USB Device Correlation Record", company); + if (h->length < 0x17) break; + if (!(opt.flags & FLAG_QUIET)) + pr_attr("Associated Handle", "0x%04X", WORD(data + 0x04)); + pr_attr("USB Vendor ID", "0x%04x", WORD(data + 0x06)); + pr_attr("Embedded SD Card", "%s", data[0x08] & 0x01 ? "Present" : "Empty"); + dmi_hp_239_usb_device(data[0x0A], data[0x0B], data[0x0C]); + pr_attr("USB Product ID", "0x%04x", WORD(data + 0x0D)); + if (DWORD(data + 0x0F)) + pr_attr("USB Capacity", "%u MB", DWORD(data + 0x0F)); + pr_attr("UEFI Device Path", "%s", dmi_string(h, data[0x13])); + pr_attr("UEFI Device Name", "%s", dmi_string(h, data[0x14])); + pr_attr("Device Name", "%s", dmi_string(h, data[0x15])); + pr_attr("Device Location", "%s", dmi_string(h, data[0x16])); + break; + case 240: /* * Vendor Specific: HPE Proliant Inventory Record @@ -890,6 +1455,116 @@ 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; + + case 245: + /* + * Vendor Specific: HPE Extension Board Inventory Record + * + * This record provides a mechanism for software to retrieve installed + * Extension Boards in system, such as Riser Cards, etc. Each extension + * board discovered at system boot time has a corresponding record + * produced in SMBIOS Type 245. This record is currently applicable + * for ML, DL and Alletra series servers in Gen11 and will be backward + * compatible with next generations + * + * This is a variant record. Definition of fields 0x05 ... vary based + * upon field 0x04 Board Type. + * + * Offset | Name | Width | Description + * --------------------------------------- + * 0x00 | Type | BYTE | 0xF5, Extension Board Inventory Record + * 0x01 | Length | BYTE | Length of structure + * 0x02 | Handle | WORD | Unique handle + * 0x04 | Board Type | WORD | 0: PCIe Riser, Other Reserved + * + * If Board Type == 0 + * 0x05 | Riser Pos | WORD | + * 0x06 | Riser ID | BYTE | + * 0x07 | CPLD Vers | BTYE | 0-> No CPLD. Bits [7][6:0] Release:Vers + * 0x08 | Riser Name | STRING| + */ + pr_handle_name("%s ProLiant Extension Board Inventory Record", company); + if (h->length < 0x05) break; + if (data[0x04] == 0) + dmi_hp_245_pcie_riser(h); + break; + default: return 0; } diff --git a/dmiopt.c b/dmiopt.c index fe0bef8..2f96a59 100644 --- a/dmiopt.c +++ b/dmiopt.c @@ -2,7 +2,7 @@ * Command line handling of dmidecode * This file is part of the dmidecode project. * - * Copyright (C) 2005-2008 Jean Delvare + * Copyright (C) 2005-2023 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 @@ -265,12 +265,14 @@ static u32 parse_opt_handle(const char *arg) int parse_command_line(int argc, char * const argv[]) { int option; + unsigned int i; const char *optstring = "d:hqs:t:uH:Vi:"; struct option longopts[] = { { "dev-mem", required_argument, NULL, 'd' }, { "input-file", required_argument, NULL, 'i' }, { "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' }, @@ -279,6 +281,8 @@ int parse_command_line(int argc, char * const argv[]) { "handle", required_argument, NULL, 'H' }, { "oem-string", required_argument, NULL, 'O' }, { "no-sysfs", no_argument, NULL, 'S' }, + { "list-strings", no_argument, NULL, 'L' }, + { "list-types", no_argument, NULL, 'T' }, { "version", no_argument, NULL, 'V' }, { NULL, 0, NULL, 0 } }; @@ -307,6 +311,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; @@ -333,6 +340,16 @@ int parse_command_line(int argc, char * const argv[]) case 'S': opt.flags |= FLAG_NO_SYSFS; break; + case 'L': + for (i = 0; i < ARRAY_SIZE(opt_string_keyword); i++) + fprintf(stdout, "%s\n", opt_string_keyword[i].keyword); + opt.flags |= FLAG_LIST; + return 0; + case 'T': + for (i = 0; i < ARRAY_SIZE(opt_type_keyword); i++) + fprintf(stdout, "%s\n", opt_type_keyword[i].keyword); + opt.flags |= FLAG_LIST; + return 0; case 'V': opt.flags |= FLAG_VERSION; break; @@ -377,8 +394,11 @@ 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" + " --list-strings List available string keywords and exit\n" " -t, --type TYPE Only display the entries of given type\n" + " --list-types List available type keywords and exit\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" diff --git a/dmiopt.h b/dmiopt.h index 501db51..752d9a3 100644 --- a/dmiopt.h +++ b/dmiopt.h @@ -2,7 +2,7 @@ * Command line handling of dmidecode * This file is part of the dmidecode project. * - * Copyright (C) 2005-2008 Jean Delvare + * Copyright (C) 2005-2023 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 @@ -46,7 +46,9 @@ extern struct opt opt; #define FLAG_DUMP_BIN (1 << 4) #define FLAG_FROM_DUMP (1 << 5) #define FLAG_NO_SYSFS (1 << 6) -#define FLAG_READFILE (1 << 7) +#define FLAG_NO_QUIRKS (1 << 7) +#define FLAG_LIST (1 << 8) +#define FLAG_READFILE (1 << 9) int parse_command_line(int argc, char * const argv[]); void print_help(void); diff --git a/man/dmidecode.8 b/man/dmidecode.8 index ed066b3..77c89a5 100644 --- a/man/dmidecode.8 +++ b/man/dmidecode.8 @@ -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: @@ -122,6 +127,10 @@ typically from files under .IR /sys/devices/virtual/dmi/id . Most of these files are even readable by regular users. .TP +.BR " " " " "--list-strings" +List available string keywords, which can then be passed to the \fB--string\fP +option. +.TP .BR "-t" ", " "--type \fITYPE\fP" Only display the entries of type \fITYPE\fP. It can be either a \s-1DMI\s0 type number, or a comma-separated list of type numbers, or a @@ -145,6 +154,10 @@ is printed and .B dmidecode exits with an error. .TP +.BR " " " " "--list-types" +List available type keywords, which can then be passed to the \fB--type\fP +option. +.TP .BR "-H" ", " "--handle \fIHANDLE\fP" Only display the entry whose handle matches \fIHANDLE\fP. \fIHANDLE\fP is a 16-bit integer. @@ -159,6 +172,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 diff --git a/util.c b/util.c index 68c8e8d..9a1c19b 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-2018 Jean Delvare + * Copyright (C) 2002-2023 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 @@ -219,6 +219,7 @@ static void *mem_chunk_ioreg(off_t base, size_t len) return NULL; } +#ifdef USE_MMAP static void safe_memcpy(void *dest, const void *src, size_t n) { #ifdef USE_SLOW_MEMCPY @@ -228,8 +229,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. @@ -237,10 +239,10 @@ 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 @@ -249,10 +251,18 @@ void *mem_chunk(off_t base, size_t len, const char *devmem) return mem_chunk_ioreg(base, len); } - 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) @@ -262,13 +272,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. @@ -327,46 +330,6 @@ void *mem_chunk(off_t base, size_t len, const char *devmem) 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) { diff --git a/util.h b/util.h index 3094cf8..0d37f35 100644 --- a/util.h +++ b/util.h @@ -1,7 +1,7 @@ /* * This file is part of the dmidecode project. * - * Copyright (C) 2003-2017 Jean Delvare + * Copyright (C) 2003-2023 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 @@ -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); diff --git a/version.h b/version.h index 9f8a413..c1e3d8e 100644 --- a/version.h +++ b/version.h @@ -1 +1 @@ -#define VERSION "3.4a" +#define VERSION "3.6a"