From 4461f1524993be7dfae48ed620c13d52bb6ae4ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Frings-F=C3=BCrst?= Date: Thu, 30 Jun 2022 19:34:42 +0200 Subject: New upstream version 3.4 --- dmidecode.c | 682 +++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 510 insertions(+), 172 deletions(-) (limited to 'dmidecode.c') diff --git a/dmidecode.c b/dmidecode.c index cd2b5c9..9aeff91 100644 --- a/dmidecode.c +++ b/dmidecode.c @@ -85,7 +85,9 @@ #define out_of_spec "" static const char *bad_index = ""; -#define SUPPORTED_SMBIOS_VER 0x030300 +enum cpuid_type cpuid_type = cpuid_none; + +#define SUPPORTED_SMBIOS_VER 0x030500 #define FLAG_NO_FILE_OFFSET (1 << 0) #define FLAG_STOP_AT_EOT (1 << 1) @@ -116,7 +118,7 @@ static void ascii_filter(char *bp, size_t len) size_t i; for (i = 0; i < len; i++) - if (bp[i] < 32 || bp[i] == 127) + if (bp[i] < 32 || bp[i] >= 127) bp[i] = '.'; } @@ -201,12 +203,15 @@ static const char *dmi_smbios_structure_type(u8 code) "Additional Information", "Onboard Device", "Management Controller Host Interface", - "TPM Device", /* 43 */ + "TPM Device", + "Processor", + "Firmware", + "String Property" /* 46 */ }; if (code >= 128) return "OEM-specific"; - if (code <= 43) + if (code <= 46) return type[code]; return out_of_spec; } @@ -248,9 +253,9 @@ static void dmi_dump(const struct dmi_header *h) { int j, l = strlen(s) + 1; - off = 0; for (row = 0; row < ((l - 1) >> 4) + 1; row++) { + off = 0; for (j = 0; j < 16 && j < l - (row << 4); j++) off += sprintf(raw_data + off, j ? " %02X" : "%02X", @@ -267,7 +272,7 @@ static void dmi_dump(const struct dmi_header *h) } /* shift is 0 if the value is in bytes, 1 if it is in kilobytes */ -static void dmi_print_memory_size(const char *attr, u64 code, int shift) +void dmi_print_memory_size(const char *attr, u64 code, int shift) { unsigned long capacity; u16 split[7]; @@ -424,11 +429,13 @@ static void dmi_bios_characteristics_x2(u8 code) "Function key-initiated network boot is supported", "Targeted content distribution is supported", "UEFI is supported", - "System is a virtual machine" /* 4 */ + "System is a virtual machine", + "Manufacturing mode is supported", + "Manufacturing mode is enabled" /* 6 */ }; int i; - for (i = 0; i <= 4; i++) + for (i = 0; i <= 6; i++) if (code & (1 << i)) pr_list_item("%s", characteristics[i]); } @@ -1037,108 +1044,48 @@ static const char *dmi_processor_family(const struct dmi_header *h, u16 ver) } } -static void dmi_processor_id(const struct dmi_header *h) +static enum cpuid_type dmi_get_cpuid_type(const struct dmi_header *h) { - /* Intel AP-485 revision 36, table 2-4 */ - static const char *flags[32] = { - "FPU (Floating-point unit on-chip)", /* 0 */ - "VME (Virtual mode extension)", - "DE (Debugging extension)", - "PSE (Page size extension)", - "TSC (Time stamp counter)", - "MSR (Model specific registers)", - "PAE (Physical address extension)", - "MCE (Machine check exception)", - "CX8 (CMPXCHG8 instruction supported)", - "APIC (On-chip APIC hardware supported)", - NULL, /* 10 */ - "SEP (Fast system call)", - "MTRR (Memory type range registers)", - "PGE (Page global enable)", - "MCA (Machine check architecture)", - "CMOV (Conditional move instruction supported)", - "PAT (Page attribute table)", - "PSE-36 (36-bit page size extension)", - "PSN (Processor serial number present and enabled)", - "CLFSH (CLFLUSH instruction supported)", - NULL, /* 20 */ - "DS (Debug store)", - "ACPI (ACPI supported)", - "MMX (MMX technology supported)", - "FXSR (FXSAVE and FXSTOR instructions supported)", - "SSE (Streaming SIMD extensions)", - "SSE2 (Streaming SIMD extensions 2)", - "SS (Self-snoop)", - "HTT (Multi-threading)", - "TM (Thermal monitor supported)", - NULL, /* 30 */ - "PBE (Pending break enabled)" /* 31 */ - }; const u8 *data = h->data; const u8 *p = data + 0x08; - u32 eax, edx; - int sig = 0; u16 type; type = (data[0x06] == 0xFE && h->length >= 0x2A) ? WORD(data + 0x28) : data[0x06]; - /* - * This might help learn about new processors supporting the - * CPUID instruction or another form of identification. - */ - pr_attr("ID", "%02X %02X %02X %02X %02X %02X %02X %02X", - p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); - if (type == 0x05) /* 80386 */ { - u16 dx = WORD(p); - /* - * 80386 have a different signature. - */ - pr_attr("Signature", - "Type %u, Family %u, Major Stepping %u, Minor Stepping %u", - dx >> 12, (dx >> 8) & 0xF, - (dx >> 4) & 0xF, dx & 0xF); - return; + return cpuid_80386; } - if (type == 0x06) /* 80486 */ + else if (type == 0x06) /* 80486 */ { u16 dx = WORD(p); /* * Not all 80486 CPU support the CPUID instruction, we have to find - * wether the one we have here does or not. Note that this trick + * whether the one we have here does or not. Note that this trick * works only because we know that 80486 must be little-endian. */ if ((dx & 0x0F00) == 0x0400 && ((dx & 0x00F0) == 0x0040 || (dx & 0x00F0) >= 0x0070) && ((dx & 0x000F) >= 0x0003)) - sig = 1; + return cpuid_x86_intel; else - { - pr_attr("Signature", - "Type %u, Family %u, Model %u, Stepping %u", - (dx >> 12) & 0x3, (dx >> 8) & 0xF, - (dx >> 4) & 0xF, dx & 0xF); - return; - } + return cpuid_80486; } 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. + * The field's format depends on the processor's support of + * the SMCCC_ARCH_SOC_ID architectural call. Software can determine + * the support for SoC ID by examining the Processor Characteristics field + * for "Arm64 SoC ID" bit. */ - if (midr == 0) - return; - pr_attr("Signature", - "Implementor 0x%02x, Variant 0x%x, Architecture %u, Part 0x%03x, Revision %u", - midr >> 24, (midr >> 20) & 0xF, - (midr >> 16) & 0xF, (midr >> 4) & 0xFFF, midr & 0xF); - return; + if (h->length >= 0x28 + && (WORD(data + 0x26) & (1 << 9))) + return cpuid_arm_soc_id; + else + return cpuid_arm_legacy; } else if ((type >= 0x0B && type <= 0x15) /* Intel, Cyrix */ || (type >= 0x28 && type <= 0x2F) /* Intel */ @@ -1148,7 +1095,7 @@ static void dmi_processor_id(const struct dmi_header *h) || (type >= 0xCD && type <= 0xCF) /* Intel */ || (type >= 0xD2 && type <= 0xDB) /* VIA, Intel */ || (type >= 0xDD && type <= 0xE0)) /* Intel */ - sig = 1; + return cpuid_x86_intel; else if ((type >= 0x18 && type <= 0x1D) /* AMD */ || type == 0x1F /* AMD */ || (type >= 0x38 && type <= 0x3F) /* AMD */ @@ -1157,7 +1104,7 @@ static void dmi_processor_id(const struct dmi_header *h) || (type >= 0x83 && type <= 0x8F) /* AMD */ || (type >= 0xB6 && type <= 0xB7) /* AMD */ || (type >= 0xE4 && type <= 0xEF)) /* AMD */ - sig = 2; + return cpuid_x86_amd; else if (type == 0x01 || type == 0x02) { const char *version = dmi_string(h, data[0x10]); @@ -1170,41 +1117,168 @@ static void dmi_processor_id(const struct dmi_header *h) || strncmp(version, "Intel(R) Core(TM)2", 18) == 0 || strncmp(version, "Intel(R) Pentium(R)", 19) == 0 || strcmp(version, "Genuine Intel(R) CPU U1400") == 0) - sig = 1; + 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) - sig = 2; - else - return; + return cpuid_x86_amd; } - 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); + /* neither X86 nor ARM */ + return cpuid_none; +} + +void dmi_print_cpuid(void (*print_cb)(const char *name, const char *format, ...), + const char *label, enum cpuid_type sig, const u8 *p) +{ + u32 eax, midr, jep106, soc_revision; + u16 dx; + switch (sig) { - case 1: /* Intel */ + case cpuid_80386: + dx = WORD(p); + /* + * 80386 have a different signature. + */ + print_cb(label, + "Type %u, Family %u, Major Stepping %u, Minor Stepping %u", + dx >> 12, (dx >> 8) & 0xF, + (dx >> 4) & 0xF, dx & 0xF); + return; + + case cpuid_80486: + dx = WORD(p); + print_cb(label, + "Type %u, Family %u, Model %u, Stepping %u", + (dx >> 12) & 0x3, (dx >> 8) & 0xF, + (dx >> 4) & 0xF, dx & 0xF); + return; + + case cpuid_arm_legacy: /* ARM before SOC ID */ + 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; + print_cb(label, + "Implementor 0x%02x, Variant 0x%x, Architecture %u, Part 0x%03x, Revision %u", + midr >> 24, (midr >> 20) & 0xF, + (midr >> 16) & 0xF, (midr >> 4) & 0xFFF, midr & 0xF); + return; + + case cpuid_arm_soc_id: /* ARM with SOC ID */ + /* + * If Soc ID is supported, the first DWORD is the JEP-106 code; + * the second DWORD is the SoC revision value. + */ + jep106 = DWORD(p); + soc_revision = DWORD(p + 4); + /* + * According to SMC Calling Convention (SMCCC) v1.3 specification + * (https://developer.arm.com/documentation/den0028/d/), the format + * of the values returned by the SMCCC_ARCH_SOC_ID call is as follows: + * + * JEP-106 code for the SiP (SoC_ID_type == 0) + * Bit[31] must be zero + * Bits[30:24] JEP-106 bank index for the SiP + * Bits[23:16] JEP-106 identification code with parity bit for the SiP + * Bits[15:0] Implementation defined SoC ID + * + * SoC revision (SoC_ID_type == 1) + * Bit[31] must be zero + * Bits[30:0] SoC revision + */ pr_attr("Signature", - "Type %u, Family %u, Model %u, Stepping %u", - (eax >> 12) & 0x3, - ((eax >> 20) & 0xFF) + ((eax >> 8) & 0x0F), - ((eax >> 12) & 0xF0) + ((eax >> 4) & 0x0F), - eax & 0xF); + "JEP-106 Bank 0x%02x Manufacturer 0x%02x, SoC ID 0x%04x, SoC Revision 0x%08x", + (jep106 >> 24) & 0x7F, (jep106 >> 16) & 0x7F, jep106 & 0xFFFF, soc_revision); + return; + + case cpuid_x86_intel: /* Intel */ + eax = DWORD(p); + /* + * 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. + */ + print_cb(label, + "Type %u, Family %u, Model %u, Stepping %u", + (eax >> 12) & 0x3, + ((eax >> 20) & 0xFF) + ((eax >> 8) & 0x0F), + ((eax >> 12) & 0xF0) + ((eax >> 4) & 0x0F), + eax & 0xF); break; - case 2: /* AMD, publication #25481 revision 2.28 */ - pr_attr("Signature", "Family %u, Model %u, Stepping %u", - ((eax >> 8) & 0xF) + (((eax >> 8) & 0xF) == 0xF ? (eax >> 20) & 0xFF : 0), - ((eax >> 4) & 0xF) | (((eax >> 8) & 0xF) == 0xF ? (eax >> 12) & 0xF0 : 0), - eax & 0xF); + + case cpuid_x86_amd: /* AMD, publication #25481 revision 2.28 */ + eax = DWORD(p); + print_cb(label, "Family %u, Model %u, Stepping %u", + ((eax >> 8) & 0xF) + (((eax >> 8) & 0xF) == 0xF ? (eax >> 20) & 0xFF : 0), + ((eax >> 4) & 0xF) | (((eax >> 8) & 0xF) == 0xF ? (eax >> 12) & 0xF0 : 0), + eax & 0xF); break; + default: + return; } +} + +static void dmi_processor_id(const struct dmi_header *h) +{ + /* Intel AP-485 revision 36, table 2-4 */ + static const char *flags[32] = { + "FPU (Floating-point unit on-chip)", /* 0 */ + "VME (Virtual mode extension)", + "DE (Debugging extension)", + "PSE (Page size extension)", + "TSC (Time stamp counter)", + "MSR (Model specific registers)", + "PAE (Physical address extension)", + "MCE (Machine check exception)", + "CX8 (CMPXCHG8 instruction supported)", + "APIC (On-chip APIC hardware supported)", + NULL, /* 10 */ + "SEP (Fast system call)", + "MTRR (Memory type range registers)", + "PGE (Page global enable)", + "MCA (Machine check architecture)", + "CMOV (Conditional move instruction supported)", + "PAT (Page attribute table)", + "PSE-36 (36-bit page size extension)", + "PSN (Processor serial number present and enabled)", + "CLFSH (CLFLUSH instruction supported)", + NULL, /* 20 */ + "DS (Debug store)", + "ACPI (ACPI supported)", + "MMX (MMX technology supported)", + "FXSR (FXSAVE and FXSTOR instructions supported)", + "SSE (Streaming SIMD extensions)", + "SSE2 (Streaming SIMD extensions 2)", + "SS (Self-snoop)", + "HTT (Multi-threading)", + "TM (Thermal monitor supported)", + NULL, /* 30 */ + "PBE (Pending break enabled)" /* 31 */ + }; + const u8 *data = h->data; + const u8 *p = data + 0x08; + enum cpuid_type sig = dmi_get_cpuid_type(h); + u32 edx; + + /* + * This might help learn about new processors supporting the + * CPUID instruction or another form of identification. + */ + if (!(opt.flags & FLAG_QUIET)) + pr_attr("ID", "%02X %02X %02X %02X %02X %02X %02X %02X", + p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); + + dmi_print_cpuid(pr_attr, "Signature", sig, p); + + if (sig != cpuid_x86_intel && sig != cpuid_x86_amd) + return; edx = DWORD(p + 4); if ((edx & 0xBFEFFBFF) == 0) @@ -1355,10 +1429,13 @@ static const char *dmi_processor_upgrade(u8 code) "Socket LGA2066", "Socket BGA1392", "Socket BGA1510", - "Socket BGA1528" /* 0x3C */ + "Socket BGA1528", + "Socket LGA4189", + "Socket LGA1200", + "Socket LGA4677" /* 0x3F */ }; - if (code >= 0x01 && code <= 0x3C) + if (code >= 0x01 && code <= 0x3F) return upgrade[code - 0x01]; return out_of_spec; } @@ -1386,7 +1463,9 @@ static void dmi_processor_characteristics(const char *attr, u16 code) "Hardware Thread", "Execute Protection", "Enhanced Virtualization", - "Power/Performance Control" /* 7 */ + "Power/Performance Control", + "128-bit Capable", + "Arm64 SoC ID" /* 9 */ }; if ((code & 0x00FC) == 0) @@ -1396,7 +1475,7 @@ static void dmi_processor_characteristics(const char *attr, u16 code) int i; pr_list_start(attr, NULL); - for (i = 2; i <= 7; i++) + for (i = 2; i <= 9; i++) if (code & (1 << i)) pr_list_item("%s", characteristics[i - 2]); pr_list_end(); @@ -1931,11 +2010,16 @@ static const char *dmi_slot_type(u8 code) "MXM Type IV", "MXM 3.0 Type A", "MXM 3.0 Type B", - "PCI Express 2 SFF-8639", - "PCI Express 3 SFF-8639", + "PCI Express 2 SFF-8639 (U.2)", + "PCI Express 3 SFF-8639 (U.2)", "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 */ + "PCI Express Mini 76-pin", + "PCI Express 4 SFF-8639 (U.2)", + "PCI Express 5 SFF-8639 (U.2)", + "OCP NIC 3.0 Small Form Factor (SFF)", + "OCP NIC 3.0 Large Form Factor (LFF)", + "OCP NIC Prior to 3.0" /* 0x28 */ }; static const char *type_0x30[] = { "CXL FLexbus 1.0" /* 0x30 */ @@ -1970,47 +2054,74 @@ static const char *dmi_slot_type(u8 code) "PCI Express 4 x2", "PCI Express 4 x4", "PCI Express 4 x8", - "PCI Express 4 x16" /* 0xBD */ + "PCI Express 4 x16", + "PCI Express 5", + "PCI Express 5 x1", + "PCI Express 5 x2", + "PCI Express 5 x4", + "PCI Express 5 x8", + "PCI Express 5 x16", + "PCI Express 6+", + "EDSFF E1", + "EDSFF E3" /* 0xC6 */ }; /* * Note to developers: when adding entries to these lists, check if * function dmi_slot_id below needs updating too. */ - if (code >= 0x01 && code <= 0x23) + if (code >= 0x01 && code <= 0x28) return type[code - 0x01]; if (code == 0x30) return type_0x30[code - 0x30]; - if (code >= 0xA0 && code <= 0xBD) + if (code >= 0xA0 && code <= 0xC6) return type_0xA0[code - 0xA0]; return out_of_spec; } -static const char *dmi_slot_bus_width(u8 code) +/* If hide_unknown is set, return NULL instead of "Other" or "Unknown" */ +static const char *dmi_slot_bus_width(u8 code, int hide_unknown) { /* 7.10.2 */ static const char *width[] = { - "", /* 0x01, "Other" */ - "", /* "Unknown" */ - "8-bit ", - "16-bit ", - "32-bit ", - "64-bit ", - "128-bit ", - "x1 ", - "x2 ", - "x4 ", - "x8 ", - "x12 ", - "x16 ", - "x32 " /* 0x0E */ + "Other", /* 0x01 */ + "Unknown", + "8-bit", + "16-bit", + "32-bit", + "64-bit", + "128-bit", + "x1", + "x2", + "x4", + "x8", + "x12", + "x16", + "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 */ @@ -2091,6 +2202,13 @@ static void dmi_slot_id(u8 code1, u8 code2, u8 type) case 0xBB: /* PCI Express 4 */ case 0xBC: /* PCI Express 4 */ case 0xBD: /* PCI Express 4 */ + case 0xBE: /* PCI Express 5 */ + case 0xBF: /* PCI Express 5 */ + case 0xC0: /* PCI Express 5 */ + case 0xC1: /* PCI Express 5 */ + case 0xC2: /* PCI Express 5 */ + case 0xC3: /* PCI Express 5 */ + case 0xC4: /* PCI Express 6+ */ pr_attr("ID", "%u", code1); break; case 0x07: /* PCMCIA */ @@ -2116,7 +2234,10 @@ static void dmi_slot_characteristics(const char *attr, u8 code1, u8 code2) "PME signal is supported", /* 0 */ "Hot-plug devices are supported", "SMBus signal is supported", - "PCIe slot bifurcation is supported" /* 3 */ + "PCIe slot bifurcation is supported", + "Async/surprise removal is supported", + "Flexbus slot, CXL 1.0 capable", + "Flexbus slot, CXL 2.0 capable" /* 6 */ }; if (code1 & (1 << 0)) @@ -2131,7 +2252,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 <= 3; i++) + for (i = 0; i <= 6; i++) if (code2 & (1 << i)) pr_list_item("%s", characteristics2[i]); pr_list_end(); @@ -2153,13 +2274,88 @@ static void dmi_slot_peers(u8 n, const u8 *data) for (i = 1; i <= n; i++, data += 5) { - sprintf(attr, "Peer Device %hu", i); + sprintf(attr, "Peer Device %hhu", (u8)i); pr_attr(attr, "%04x:%02x:%02x.%x (Width %u)", WORD(data), data[2], data[3] >> 3, data[3] & 0x07, data[4]); } } +static void dmi_slot_information(u8 type, u8 code) +{ + switch (type) + { + case 0x1F: /* PCI Express 2 */ + case 0x20: /* PCI Express 3 */ + case 0x21: /* PCI Express Mini */ + case 0x22: /* PCI Express Mini */ + case 0x23: /* PCI Express Mini */ + case 0xA5: /* PCI Express */ + case 0xA6: /* PCI Express */ + case 0xA7: /* PCI Express */ + case 0xA8: /* PCI Express */ + case 0xA9: /* PCI Express */ + case 0xAA: /* PCI Express */ + case 0xAB: /* PCI Express 2 */ + case 0xAC: /* PCI Express 2 */ + case 0xAD: /* PCI Express 2 */ + case 0xAE: /* PCI Express 2 */ + case 0xAF: /* PCI Express 2 */ + case 0xB0: /* PCI Express 2 */ + case 0xB1: /* PCI Express 3 */ + case 0xB2: /* PCI Express 3 */ + case 0xB3: /* PCI Express 3 */ + case 0xB4: /* PCI Express 3 */ + case 0xB5: /* PCI Express 3 */ + case 0xB6: /* PCI Express 3 */ + case 0xB8: /* PCI Express 4 */ + case 0xB9: /* PCI Express 4 */ + case 0xBA: /* PCI Express 4 */ + case 0xBB: /* PCI Express 4 */ + case 0xBC: /* PCI Express 4 */ + case 0xBD: /* PCI Express 4 */ + case 0xBE: /* PCI Express 5 */ + case 0xBF: /* PCI Express 5 */ + case 0xC0: /* PCI Express 5 */ + case 0xC1: /* PCI Express 5 */ + case 0xC2: /* PCI Express 5 */ + case 0xC3: /* PCI Express 5 */ + case 0xC4: /* PCI Express 6+ */ + if (code) + pr_attr("PCI Express Generation", "%u", code); + break; + } +} + +static void dmi_slot_physical_width(u8 code) +{ + if (code) + pr_attr("Slot Physical Width", "%s", + dmi_slot_bus_width(code, 0)); +} + +static void dmi_slot_pitch(u16 code) +{ + if (code) + pr_attr("Pitch", "%u.%02u mm", code / 100, code % 100); +} + +static const char *dmi_slot_height(u8 code) +{ + /* 7.10.3 */ + static const char *height[] = { + "Not applicable", /* 0x00 */ + "Other", + "Unknown", + "Full height", + "Low-profile" /* 0x04 */ + }; + + if (code <= 0x04) + return height[code]; + return out_of_spec; +} + /* * 7.11 On Board Devices Information (Type 10) */ @@ -2177,10 +2373,16 @@ static const char *dmi_on_board_devices_type(u8 code) "Sound", "PATA Controller", "SATA Controller", - "SAS Controller" /* 0x0A */ + "SAS Controller", + "Wireless LAN", + "Bluetooth", + "WWAN", + "eMMC", + "NVMe Controller", + "UFS Controller" /* 0x10 */ }; - if (code >= 0x01 && code <= 0x0A) + if (code >= 0x01 && code <= 0x10) return type[code - 0x01]; return out_of_spec; } @@ -2219,7 +2421,7 @@ static void dmi_oem_strings(const struct dmi_header *h) for (i = 1; i <= count; i++) { - sprintf(attr, "String %hu", i); + sprintf(attr, "String %hhu", (u8)i); pr_attr(attr, "%s",dmi_string(h, i)); } } @@ -2237,7 +2439,7 @@ static void dmi_system_configuration_options(const struct dmi_header *h) for (i = 1; i <= count; i++) { - sprintf(attr, "Option %hu", i); + sprintf(attr, "Option %hhu", (u8)i); pr_attr(attr, "%s",dmi_string(h, i)); } } @@ -2421,10 +2623,10 @@ static void dmi_event_log_descriptors(u8 count, u8 len, const u8 *p) { if (len >= 0x02) { - sprintf(attr, "Descriptor %hu", i + 1); + sprintf(attr, "Descriptor %d", i + 1); pr_attr(attr, "%s", dmi_event_log_descriptor_type(p[i * len])); - sprintf(attr, "Data Format %hu", i + 1); + sprintf(attr, "Data Format %d", i + 1); pr_attr(attr, "%s", dmi_event_log_descriptor_format(p[i * len + 1])); } @@ -2639,10 +2841,12 @@ static const char *dmi_memory_device_type(u8 code) "LPDDR4", "Logical non-volatile device", "HBM", - "HBM2" /* 0x21 */ + "HBM2", + "DDR5", + "LPDDR5" /* 0x23 */ }; - if (code >= 0x01 && code <= 0x21) + if (code >= 0x01 && code <= 0x23) return type[code - 0x01]; return out_of_spec; } @@ -2684,12 +2888,22 @@ static void dmi_memory_device_type_detail(u16 code) } } -static void dmi_memory_device_speed(const char *attr, u16 code) +static void dmi_memory_device_speed(const char *attr, u16 code1, u32 code2) { - if (code == 0) - pr_attr(attr, "Unknown"); + if (code1 == 0xFFFF) + { + if (code2 == 0) + pr_attr(attr, "Unknown"); + else + pr_attr(attr, "%lu MT/s", code2); + } else - pr_attr(attr, "%u MT/s", code); + { + if (code1 == 0) + pr_attr(attr, "Unknown"); + else + pr_attr(attr, "%u MT/s", code1); + } } static void dmi_memory_technology(u8 code) @@ -2948,12 +3162,14 @@ static const char *dmi_pointing_device_interface(u8 code) static const char *interface_0xA0[] = { "Bus Mouse DB-9", /* 0xA0 */ "Bus Mouse Micro DIN", - "USB" /* 0xA2 */ + "USB", + "I2C", + "SPI" /* 0xA4 */ }; if (code >= 0x01 && code <= 0x08) return interface[code - 0x01]; - if (code >= 0xA0 && code <= 0xA2) + if (code >= 0xA0 && code <= 0xA4) return interface_0xA0[code - 0xA0]; return out_of_spec; } @@ -3390,11 +3606,11 @@ static void dmi_memory_channel_devices(u8 count, const u8 *p) for (i = 1; i <= count; i++) { - sprintf(attr, "Device %hu Load", i); + sprintf(attr, "Device %hhu Load", (u8)i); pr_attr(attr, "%u", p[3 * i]); if (!(opt.flags & FLAG_QUIET)) { - sprintf(attr, "Device %hu Handle", i); + sprintf(attr, "Device %hhu Handle", (u8)i); pr_attr(attr, "0x%04X", WORD(p + 3 * i + 1)); } } @@ -3707,16 +3923,16 @@ static void dmi_parse_protocol_record(u8 *rec) * convenience. It could get passed from the SMBIOS * header, but that's a lot of passing of pointers just * to get that info, and the only thing it is used for is - * to determine the endianess of the field. Since we only + * to determine the endianness of the field. Since we only * do this parsing on versions of SMBIOS after 3.1.1, and the - * endianess of the field is always little after version 2.6.0 + * endianness of the field is always little after version 2.6.0 * we can just pick a sufficiently recent version here. */ dmi_system_uuid(pr_subattr, "Service UUID", &rdata[0], 0x311); /* * DSP0270: 8.6: Redfish Over IP Host IP Assignment Type - * Note, using decimal indicies here, as the DSP0270 + * Note, using decimal indices here, as the DSP0270 * uses decimal, so as to make it more comparable */ assign_val = rdata[16]; @@ -4010,6 +4226,53 @@ static void dmi_tpm_characteristics(u64 code) pr_list_item("%s", characteristics[i - 2]); } +/* + * 7.46 Firmware Inventory Information (Type 45) + */ + +static void dmi_firmware_characteristics(u16 code) +{ + /* 7.46.3 */ + static const char *characteristics[] = { + "Updatable", /* 0 */ + "Write-Protect" /* 1 */ + }; + int i; + + for (i = 0; i <= 1; i++) + pr_list_item("%s: %s", characteristics[i], + (code & (1 << i)) ? "Yes" : "No"); +} + +static const char *dmi_firmware_state(u8 code) +{ + /* 7.46.4 */ + static const char *state[] = { + "Other", /* 0x01 */ + "Unknown", + "Disabled", + "Enabled", + "Absent", + "Stand-by Offline", + "Stand-by Spare", + "Unavailable Offline" /* 0x08 */ + }; + + if (code >= 0x01 && code <= 0x08) + return state[code - 0x01]; + return out_of_spec; +} + +static void dmi_firmware_components(u8 count, const u8 *p) +{ + int i; + + pr_list_start("Associated Components", "%u", count); + for (i = 0; i < count; i++) + pr_list_item("0x%04X", WORD(p + sizeof(u16) * i)); + pr_list_end(); +} + /* * Main */ @@ -4033,9 +4296,9 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) pr_attr("Release Date", "%s", dmi_string(h, data[0x08])); /* - * On IA-64, the BIOS base address will read 0 because - * there is no BIOS. Skip the base address and the - * runtime size in this case. + * On IA-64 and UEFI-based systems, the BIOS base + * address will read 0 because there is no BIOS. Skip + * the base address and the runtime size in this case. */ if (WORD(data + 0x06) != 0) { @@ -4302,9 +4565,7 @@ 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])); - pr_attr("Type", "%s%s", - dmi_slot_bus_width(data[0x06]), - dmi_slot_type(data[0x05])); + dmi_slot_type_with_width(data[0x05], data[0x06]); pr_attr("Current Usage", "%s", dmi_slot_current_usage(data[0x07])); pr_attr("Length", "%s", @@ -4319,8 +4580,15 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) if (h->length < 0x13) break; pr_attr("Data Bus Width", "%u", data[0x11]); pr_attr("Peer Devices", "%u", data[0x12]); - if (h->length - 0x13 >= data[0x12] * 5) - dmi_slot_peers(data[0x12], data + 0x13); + if (h->length < 0x13 + data[0x12] * 5) break; + dmi_slot_peers(data[0x12], data + 0x13); + if (h->length < 0x17 + data[0x12] * 5) break; + dmi_slot_information(data[0x05], data[0x13 + data[0x12] * 5]); + dmi_slot_physical_width(data[0x14 + data[0x12] * 5]); + dmi_slot_pitch(WORD(data + 0x15 + data[0x12] * 5)); + if (h->length < 0x18 + data[0x12] * 5) break; + pr_attr("Height", "%s", + dmi_slot_height(data[0x17 + data[0x12] * 5])); break; case 10: /* 7.11 On Board Devices Information */ @@ -4451,7 +4719,12 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) dmi_memory_device_type(data[0x12])); dmi_memory_device_type_detail(WORD(data + 0x13)); if (h->length < 0x17) break; - dmi_memory_device_speed("Speed", WORD(data + 0x15)); + /* If no module is present, the remaining fields are irrelevant */ + if (WORD(data + 0x0C) == 0) + break; + dmi_memory_device_speed("Speed", WORD(data + 0x15), + h->length >= 0x5C ? + DWORD(data + 0x54) : 0); if (h->length < 0x1B) break; pr_attr("Manufacturer", "%s", dmi_string(h, data[0x17])); @@ -4468,7 +4741,9 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) pr_attr("Rank", "%u", data[0x1B] & 0x0F); if (h->length < 0x22) break; dmi_memory_device_speed("Configured Memory Speed", - WORD(data + 0x20)); + WORD(data + 0x20), + h->length >= 0x5C ? + DWORD(data + 0x58) : 0); if (h->length < 0x28) break; dmi_memory_voltage_value("Minimum Voltage", WORD(data + 0x22)); @@ -5051,11 +5326,33 @@ static void dmi_decode(const struct dmi_header *h, u16 ver) DWORD(data + 0x1B)); break; - case 126: /* 7.44 Inactive */ + case 45: /* 7.46 Firmware Inventory Information */ + pr_handle_name("Firmware Inventory Information"); + if (h->length < 0x18) break; + pr_attr("Firmware Component Name", "%s", + dmi_string(h, data[0x04])); + pr_attr("Firmware Version", "%s", + dmi_string(h, data[0x05])); + pr_attr("Firmware ID", "%s", dmi_string(h, data[0x07])); + pr_attr("Release Date", "%s", dmi_string(h, data[0x09])); + pr_attr("Manufacturer", "%s", dmi_string(h, data[0x0A])); + pr_attr("Lowest Supported Firmware Version", "%s", + dmi_string(h, data[0x0B])); + dmi_memory_size("Image Size", QWORD(data + 0x0C)); + pr_list_start("Characteristics", NULL); + dmi_firmware_characteristics(WORD(data + 0x14)); + pr_list_end(); + pr_attr("State", "%s", dmi_firmware_state(data[0x16])); + if (h->length < 0x18 + data[0x17] * 2) break; + if (!(opt.flags & FLAG_QUIET)) + dmi_firmware_components(data[0x17], data + 0x18); + break; + + case 126: pr_handle_name("Inactive"); break; - case 127: /* 7.45 End Of Table */ + case 127: pr_handle_name("End Of Table"); break; @@ -5142,6 +5439,51 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags) u8 *data; int i = 0; + /* First pass: Save specific values needed to decode OEM types */ + data = buf; + while ((i < num || !num) + && data + 4 <= buf + len) /* 4 is the length of an SMBIOS structure header */ + { + u8 *next; + struct dmi_header h; + + to_dmi_header(&h, data); + + /* + * If a short entry is found (less than 4 bytes), not only it + * is invalid, but we cannot reliably locate the next entry. + * Also stop at end-of-table marker if so instructed. + */ + if (h.length < 4 || + (h.type == 127 && + (opt.flags & (FLAG_QUIET | FLAG_STOP_AT_EOT)))) + break; + i++; + + /* Look for the next handle */ + next = data + h.length; + while ((unsigned long)(next - buf + 1) < len + && (next[0] != 0 || next[1] != 0)) + next++; + next += 2; + + /* Make sure the whole structure fits in the table */ + if ((unsigned long)(next - buf) > len) + break; + + /* Assign vendor for vendor-specific decodes later */ + if (h.type == 1 && h.length >= 6) + dmi_set_vendor(_dmi_string(&h, data[0x04], 0), + _dmi_string(&h, data[0x05], 0)); + + /* Remember CPUID type for HPE type 199 */ + if (h.type == 4 && h.length >= 0x1A && cpuid_type == cpuid_none) + cpuid_type = dmi_get_cpuid_type(&h); + data = next; + } + + /* Second pass: Actually decode the data */ + i = 0; data = buf; while ((i < num || !num) && data + 4 <= buf + len) /* 4 is the length of an SMBIOS structure header */ @@ -5201,10 +5543,6 @@ static void dmi_table_decode(u8 *buf, u32 len, u16 num, u16 ver, u32 flags) break; } - /* assign vendor for vendor-specific decodes later */ - if (h.type == 1 && h.length >= 5) - dmi_set_vendor(dmi_string(&h, data[0x04])); - /* Fixup a common mistake */ if (h.type == 34) dmi_fixup_type_34(&h, display); -- cgit v1.2.3