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