[PATCH 2/2] dmi: rename dmi_decode test to dmicheck
IvanHu
ivan.hu at canonical.com
Thu Jul 11 01:15:55 UTC 2013
On 07/03/2013 08:51 PM, Alex Hung wrote:
> Signed-off-by: Alex Hung <alex.hung at canonical.com>
> ---
> src/Makefile.am | 2 +-
> src/dmi/dmi_decode/dmi_decode.c | 1492 ---------------------------------------
> src/dmi/dmicheck/dmicheck.c | 1492 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1493 insertions(+), 1493 deletions(-)
> delete mode 100644 src/dmi/dmi_decode/dmi_decode.c
> create mode 100644 src/dmi/dmicheck/dmicheck.c
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 1516639..8591606 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -67,7 +67,7 @@ fwts_SOURCES = main.c \
> cpu/nx/nx.c \
> cpu/msr/msr.c \
> cpu/microcode/microcode.c \
> - dmi/dmi_decode/dmi_decode.c \
> + dmi/dmicheck/dmicheck.c \
> hotkey/hotkey/hotkey.c \
> hpet/hpet_check/hpet_check.c \
> kernel/klog/klog.c \
> diff --git a/src/dmi/dmi_decode/dmi_decode.c b/src/dmi/dmi_decode/dmi_decode.c
> deleted file mode 100644
> index 80704fe..0000000
> --- a/src/dmi/dmi_decode/dmi_decode.c
> +++ /dev/null
> @@ -1,1492 +0,0 @@
> -/*
> - * Copyright (C) 2010-2013 Canonical
> - *
> - * 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 the Free Software Foundation; either version 2
> - * of the License, or (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> - *
> - */
> -
> -#include <stdbool.h>
> -#include <string.h>
> -#include <stdlib.h>
> -#include <inttypes.h>
> -
> -#include "fwts.h"
> -
> -#ifdef FWTS_ARCH_INTEL
> -
> -#include <stdlib.h>
> -#include <stdio.h>
> -#include <string.h>
> -#include <sys/types.h>
> -#include <sys/stat.h>
> -#include <unistd.h>
> -#include <limits.h>
> -
> -#define DMI_VERSION (0x0207)
> -#define VERSION_MAJOR(v) ((v) >> 8)
> -#define VERSION_MINOR(v) ((v) & 0xff)
> -
> -#define SMBIOS_END_OF_TABLE (127)
> -
> -#define DMI_NO_TABLE "DMINoTable"
> -#define DMI_NO_TABLE_HEADER "DMINoTableHeader"
> -#define DMI_BAD_TABLE_LENGTH "DMIBadTableLength"
> -#define DMI_BAD_UUID "DMIBadUUID"
> -#define DMI_STRUCT_COUNT "DMIStructCount"
> -#define DMI_VALUE_OUT_OF_RANGE "DMIValueOutOfRange"
> -#define DMI_STRING_INDEX_OUT_OF_RANGE "DMIStringIndexOutOfRange"
> -#define DMI_ILLEGAL_MAPPED_ADDR_RANGE "DMIIllegalMappedAddrRange"
> -#define DMI_MGMT_CTRL_HOST_TYPE "DMIMgmtCtrlHostType"
> -#define DMI_INVALID_ENTRY_LENGTH "DMIInvalidEntryLength"
> -#define DMI_INVALID_HARDWARE_ENTRY "DMIInvalidHardwareEntry"
> -
> -#define GET_UINT16(x) (uint16_t)(*(const uint16_t *)(x))
> -#define GET_UINT32(x) (uint32_t)(*(const uint32_t *)(x))
> -#define GET_UINT64(x) (uint64_t)(*(const uint64_t *)(x))
> -
> -#define CHASSIS_OTHER 0x00
> -#define CHASSIS_DESKTOP 0x01
> -#define CHASSIS_WORKSTATION 0x02
> -#define CHASSIS_MOBILE 0x04
> -#define CHASSIS_SERVER 0x08
> -
> -typedef struct {
> - const char *label;
> - const char *field;
> - const char *value;
> -} fwts_dmi_pattern;
> -
> -typedef struct {
> - uint16_t old;
> - uint16_t new;
> -} fwts_dmi_version;
> -
> -typedef struct {
> - const char *name;
> - uint8_t original;
> - uint8_t mapped;
> -} fwts_chassis_type_map;
> -
> -typedef struct {
> - uint8_t type;
> - uint8_t offset;
> -} fwts_dmi_used_by_kernel;
> -
> -static const fwts_dmi_pattern dmi_patterns[] = {
> - { "DMISerialNumber", "Serial Number", "0123456789" },
> - { "DMISerialNumber", "Serial Number", "System Serial Number" },
> - { "DMISerialNumber", "Serial Number", "MB-1234567890" },
> - { "DMISerialNumber", NULL, "Chassis Serial Number" },
> - { "DMIAssetTag", "Asset Tag", "1234567890" },
> - { "DMIAssetTag", "Asset Tag", "Asset-1234567890" },
> - { "DMIChassisVendor", NULL, "Chassis Manufacture" },
> - { "DMIChassisVersion", NULL, "Chassis Version" },
> - { "DMIProductVersion", NULL, "System Version" },
> - { "DMIBadDefault", NULL, "To Be Filled By O.E.M." },
> - { NULL, NULL, NULL }
> -};
> -
> -static const char *uuid_patterns[] = {
> - "0A0A0A0A-0A0A-0A0A-0A0A-0A0A0A0A0A0A",
> - NULL,
> -};
> -
> -static const fwts_chassis_type_map fwts_dmi_chassis_type[] = {
> - { "Invalid", FWTS_SMBIOS_CHASSIS_INVALID, CHASSIS_OTHER },
> - { "Other", FWTS_SMBIOS_CHASSIS_OTHER, CHASSIS_OTHER },
> - { "Unknown", FWTS_SMBIOS_CHASSIS_UNKNOWN, CHASSIS_OTHER },
> - { "Desktop", FWTS_SMBIOS_CHASSIS_DESKTOP, CHASSIS_DESKTOP },
> - { "Low Profile Desktop",FWTS_SMBIOS_CHASSIS_LOW_PROFILE_DESKTOP, CHASSIS_DESKTOP },
> - { "Pizza Box", FWTS_SMBIOS_CHASSIS_PIZZA_BOX, CHASSIS_DESKTOP },
> - { "Mini Tower", FWTS_SMBIOS_CHASSIS_MINI_TOWER, CHASSIS_DESKTOP },
> - { "Chassis Tower", FWTS_SMBIOS_CHASSIS_TOWER, CHASSIS_DESKTOP },
> - { "Portable", FWTS_SMBIOS_CHASSIS_PORTABLE, CHASSIS_MOBILE },
> - { "Laptop", FWTS_SMBIOS_CHASSIS_LAPTOP, CHASSIS_MOBILE },
> - { "Notebook", FWTS_SMBIOS_CHASSIS_NOTEBOOK, CHASSIS_MOBILE },
> - { "Handheld", FWTS_SMBIOS_CHASSIS_HANDHELD, CHASSIS_MOBILE },
> - { "Docking Station", FWTS_SMBIOS_CHASSIS_DOCKING_STATION, CHASSIS_DESKTOP },
> - { "All In One", FWTS_SMBIOS_CHASSIS_ALL_IN_ONE, CHASSIS_DESKTOP },
> - { "Sub Notebook", FWTS_SMBIOS_CHASSIS_SUB_NOTEBOOK, CHASSIS_MOBILE },
> - { "Space Saving", FWTS_SMBIOS_CHASSIS_SPACE_SAVING, CHASSIS_DESKTOP },
> - { "Lunch Box", FWTS_SMBIOS_CHASSIS_LUNCH_BOX, CHASSIS_DESKTOP | CHASSIS_MOBILE},
> - { "Server Chassis", FWTS_SMBIOS_CHASSIS_MAIN_SERVER_CHASSIS, CHASSIS_SERVER },
> - { "Expansion Chassis", FWTS_SMBIOS_CHASSIS_EXPANISON_CHASSIS, CHASSIS_OTHER },
> - { "Sub Chassis", FWTS_SMBIOS_CHASSIS_SUB_CHASSIS, CHASSIS_OTHER },
> - { "Bus Expansion Chassis", FWTS_SMBIOS_CHASSIS_BUS_EXPANSION_CHASSIS, CHASSIS_OTHER },
> - { "Peripheral Chassis", FWTS_SMBIOS_CHASSIS_PERIPHERAL_CHASSIS, CHASSIS_OTHER },
> - { "Raid Chassis", FWTS_SMBIOS_CHASSIS_RAID_CHASSIS, CHASSIS_OTHER },
> - { "Rack Mount Chassis", FWTS_SMBIOS_CHASSIS_RACK_MOUNT_CHASSIS, CHASSIS_OTHER },
> - { "Sealed Case PC", FWTS_SMBIOS_CHASSIS_SEALED_CASE_PC, CHASSIS_DESKTOP },
> - { "Multi System Chassis",FWTS_SMBIOS_CHASSIS_MULTI_SYSTEM_CHASSIS, CHASSIS_OTHER },
> - { "Compact PCI", FWTS_SMBIOS_CHASSIS_COMPACT_PCI, CHASSIS_OTHER },
> - { "Advanced TCA", FWTS_SMBIOS_CHASSIS_ADVANCED_TCA, CHASSIS_OTHER },
> - { "Blade", FWTS_SMBIOS_CHASSIS_BLADE, CHASSIS_SERVER },
> - { "Enclosure", FWTS_SMBIOS_CHASSIS_BLASE_ENCLOSURE, CHASSIS_SERVER }
> -};
> -
> -static const fwts_chassis_type_map fwts_acpi_pm_profile_type[] = {
> - { "Unspecified", FWTS_FACP_UNSPECIFIED, CHASSIS_OTHER },
> - { "Desktop", FWTS_FACP_DESKTOP, CHASSIS_DESKTOP },
> - { "Mobile", FWTS_FACP_MOBILE, CHASSIS_MOBILE },
> - { "Workstation", FWTS_FACP_WORKSTATION, CHASSIS_WORKSTATION },
> - { "Enterprise Server", FWTS_FACP_ENTERPRISE_SERVER, CHASSIS_SERVER },
> - { "SOHO Server", FWTS_FACP_SOHO_SERVER, CHASSIS_SERVER | CHASSIS_DESKTOP },
> - { "Appliance PC", FWTS_FACP_APPLIANCE_PC, CHASSIS_DESKTOP },
> - { "Performance Server", FWTS_FACP_PERFORMANCE_SERVER, CHASSIS_SERVER },
> - { "Tablet", FWTS_FACP_TABLET, CHASSIS_MOBILE }
> -};
> -
> -/* Remapping table from buggy version numbers to correct values */
> -static const fwts_dmi_version dmi_versions[] = {
> - { 0x021f, 0x0203 },
> - { 0x0221, 0x0203 },
> - { 0x0233, 0x0206 },
> - { 0, 0 }
> -};
> -
> -#define FIELD_ANY 0xff
> -#define TYPE_EOD 0xff
> -
> -/*
> - * DMI decoded fields used by the kernel, i.e. fields
> - * we care that work,
> - * see drivers/firmware/dmi_scan.c, dmi_decode()
> - */
> -static fwts_dmi_used_by_kernel dmi_used_by_kernel_table[] = {
> - /* Type 0 BIOS Information fields */
> - { 0, 4 },
> - { 0, 5 },
> - { 0, 8 },
> - /* Type 1, System Information */
> - { 1, 4 },
> - { 1, 5 },
> - { 1, 6 },
> - { 1, 7 },
> - { 1, 8 },
> - /* Type 2, Base Board Information */
> - { 2, 4 },
> - { 2, 5 },
> - { 2, 6 },
> - { 2, 7 },
> - { 2, 8 },
> - /* Type 3, Chassis Information */
> - { 3, 4 },
> - { 3, 5 },
> - { 3, 6 },
> - { 3, 7 },
> - { 3, 8 },
> - /* Type 10, Onboard Devices Information */
> - { 10, FIELD_ANY },
> - /* Type 11, OEM Strings */
> - { 11, FIELD_ANY },
> - /* Type 38, IPMI Device Information */
> - { 38, FIELD_ANY },
> - /* Type 41, Onboard Devices Extended Information */
> - { 41, FIELD_ANY },
> - /* End */
> - { TYPE_EOD, 0xff },
> -};
> -
> -static void dmi_dump_entry(fwts_framework *fw, fwts_smbios_entry *entry, fwts_smbios_type type)
> -{
> - if (type == FWTS_SMBIOS) {
> - fwts_log_info_verbatum(fw, "SMBIOS Entry Point Structure:");
> - fwts_log_info_verbatum(fw, " Anchor String : %4.4s", entry->signature);
> - fwts_log_info_verbatum(fw, " Checksum : 0x%2.2x", entry->checksum);
> - fwts_log_info_verbatum(fw, " Entry Point Length : 0x%2.2x", entry->length);
> - fwts_log_info_verbatum(fw, " Major Version : 0x%2.2x", entry->major_version);
> - fwts_log_info_verbatum(fw, " Minor Version : 0x%2.2x", entry->minor_version);
> - fwts_log_info_verbatum(fw, " Maximum Struct Size : 0x%2.2x", entry->max_struct_size);
> - fwts_log_info_verbatum(fw, " Entry Point Revision : 0x%2.2x", entry->revision);
> - fwts_log_info_verbatum(fw, " Formatted Area : 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x",
> - entry->formatted_area[0], entry->formatted_area[1],
> - entry->formatted_area[2], entry->formatted_area[3],
> - entry->formatted_area[4]);
> - }
> - if (type == FWTS_SMBIOS_DMI_LEGACY)
> - fwts_log_info_verbatum(fw, "Legacy DMI Entry Point Structure:");
> -
> - /* Common to SMBIOS and SMBIOS_DMI_LEGACY */
> - fwts_log_info_verbatum(fw, " Intermediate Anchor : %5.5s", (char *)entry->anchor_string);
> - fwts_log_info_verbatum(fw, " Intermediate Checksum : 0x%2.2x", entry->intermediate_checksum);
> - fwts_log_info_verbatum(fw, " Structure Table Length : 0x%4.4x", entry->struct_table_length);
> - fwts_log_info_verbatum(fw, " Structure Table Address: 0x%8.8x", entry->struct_table_address);
> - fwts_log_info_verbatum(fw, " # of SMBIOS Structures : 0x%4.4x", entry->number_smbios_structures);
> - fwts_log_info_verbatum(fw, " SBMIOS BCD Revision : %2.2x", entry->smbios_bcd_revision);
> - if (entry->smbios_bcd_revision == 0)
> - fwts_log_info_verbatum(fw, " BCD Revision 00 indicates compliance with specification stated in Major/Minor Version.");
> -}
> -
> -static int dmi_sane(fwts_framework *fw, fwts_smbios_entry *entry)
> -{
> - uint8_t *table, *ptr;
> - uint8_t dmi_entry_length;
> - uint8_t dmi_entry_type = 0;
> - uint16_t i = 0;
> - uint16_t table_length = entry->struct_table_length;
> - int ret = FWTS_OK;
> -
> - ptr = table = fwts_mmap((off_t)entry->struct_table_address,
> - (size_t)table_length);
> - if (table == FWTS_MAP_FAILED) {
> - fwts_failed(fw, LOG_LEVEL_MEDIUM,
> - "SMBIOSTableAddressNotMapped",
> - "Cannot mmap SMBIOS tables from "
> - "%8.8" PRIx32 "..%8.8" PRIx32 ".",
> - entry->struct_table_address,
> - entry->struct_table_address + table_length);
> - return FWTS_ERROR;
> - }
> -
> - for (i = 0; i < entry->number_smbios_structures; i++) {
> - if (ptr > table + table_length) {
> - fwts_failed(fw, LOG_LEVEL_MEDIUM,
> - "SMBIOSTableLengthTooSmall",
> - "The size indicated by the SMBIOS table length is "
> - "smaller than the DMI data.");
> - ret = FWTS_ERROR;
> - break;
> - }
> -
> - dmi_entry_type = ptr[0];
> - dmi_entry_length = ptr[1];
> -
> - if (dmi_entry_length < 4) {
> - fwts_failed(fw, LOG_LEVEL_MEDIUM,
> - "SMBIOSIllegalTableEntry",
> - "The size of a DMI entry %" PRIu16 " is illegal, "
> - "DMI data is either wrong or the SMBIOS Table "
> - "Pointer is pointing to the wrong memory region.", i);
> - ret = FWTS_ERROR;
> - break;
> - }
> - ptr += dmi_entry_length;
> -
> - /* Scan for end of DMI entry, must be 2 zero bytes */
> - while (((ptr - table + 1) < table_length) &&
> - ((ptr[0] != 0) || (ptr[1] != 0)))
> - ptr++;
> - /* Skip over the two zero bytes */
> - ptr += 2;
> - }
> -
> - /* We found DMI end of table, are number of entries correct? */
> - if ((dmi_entry_type == 127) && (i != entry->number_smbios_structures)) {
> - fwts_failed(fw, LOG_LEVEL_MEDIUM,
> - "SMBIOSNumberOfStructures",
> - "The end of DMI table marker structure was found "
> - "but only %d DMI structures were found. The SMBIOS "
> - "entry table reported that there were %" PRIu16
> - " DMI structures in the DMI table.",
> - i, entry->number_smbios_structures);
> - /* And table length can't be correct either */
> - ret = FWTS_ERROR;
> - }
> -
> - (void)fwts_munmap(table, (size_t)entry->struct_table_length);
> -
> - return ret;
> -}
> -
> -static int dmi_decode_test1(fwts_framework *fw)
> -{
> - void *addr = 0;
> - fwts_smbios_entry entry;
> - fwts_smbios_type type;
> - uint16_t version;
> - uint8_t checksum;
> - static char warning[] =
> - "This field is not checked by the kernel, and so will not affect the system, "
> - "however it should be fixed to conform to the latest version of the "
> - "System Management BIOS (SMBIOS) Reference Specification. ";
> -
> - fwts_log_info(fw,
> - "This test tries to find and sanity check the SMBIOS "
> - "data structures.");
> -
> - if ((addr = fwts_smbios_find_entry(fw, &entry, &type, &version)) == NULL) {
> - fwts_failed(fw, LOG_LEVEL_MEDIUM,
> - "SMBIOSNoEntryPoint",
> - "Could not find SMBIOS Table Entry Point.");
> - return FWTS_OK;
> - }
> -
> - fwts_passed(fw, "Found SMBIOS Table Entry Point at %p", addr);
> - dmi_dump_entry(fw, &entry, type);
> - fwts_log_nl(fw);
> -
> - if (type == FWTS_SMBIOS) {
> - checksum = fwts_checksum((uint8_t*)&entry, sizeof(fwts_smbios_entry));
> - if (checksum != 0)
> - fwts_failed(fw, LOG_LEVEL_HIGH,
> - "SMBIOSBadChecksum",
> - "SMBIOS Table Entry Point Checksum is 0x%2.2x, should be 0x%2.2x",
> - entry.checksum, (uint8_t)(entry.checksum - checksum));
> - else
> - fwts_passed(fw, "SMBIOS Table Entry Point Checksum is valid.");
> -
> - if (entry.length != 0x1f) {
> - fwts_failed(fw, LOG_LEVEL_LOW,
> - "SMBIOSBadEntryLength",
> - "SMBIOS Table Entry Point Length is 0x%2.2x, should be 0x1f", entry.length);
> - fwts_advice(fw, "%s Note that version 2.1 of the specification incorrectly stated that the "
> - "Entry Point Length should be 0x1e when it should be 0x1f.", warning);
> - } else
> - fwts_passed(fw, "SMBIOS Table Entry Point Length is valid.");
> - }
> -
> - if (memcmp(entry.anchor_string, "_DMI_", 5) != 0) {
> - fwts_failed(fw, LOG_LEVEL_HIGH,
> - "SMBIOSBadIntermediateAnchor",
> - "SMBIOS Table Entry Intermediate Anchor String was '%5.5s' and should be '_DMI_'.",
> - entry.anchor_string);
> - fwts_advice(fw, "%s", warning);
> - } else
> - fwts_passed(fw, "SMBIOS Table Entry Intermediate Anchor String _DMI_ is valid.");
> -
> - /* Intermediate checksum for legacy DMI */
> - checksum = fwts_checksum(((uint8_t*)&entry)+16, 15);
> - if (checksum != 0)
> - fwts_failed(fw, LOG_LEVEL_HIGH,
> - "SMBIOSBadChecksum",
> - "SMBIOS Table Entry Point Intermediate Checksum is 0x%2.2x, should be 0x%2.2x",
> - entry.intermediate_checksum,
> - (uint8_t)(entry.intermediate_checksum - checksum));
> - else
> - fwts_passed(fw, "SMBIOS Table Entry Point Intermediate Checksum is valid.");
> -
> - if ((entry.struct_table_length > 0) && (entry.struct_table_address == 0)) {
> - fwts_failed(fw, LOG_LEVEL_HIGH,
> - "SMBIOSBadTableAddress",
> - "SMBIOS Table Entry Structure Table Address is NULL and should be defined.");
> - fwts_advice(fw,
> - "The address of the SMBIOS Structure Tables must be defined if the "
> - "length of these tables is defined.");
> - } else {
> - /*
> - * Now does the DMI table look sane? If not,
> - * the SMBIOS Structure Table could be bad
> - */
> - if (dmi_sane(fw, &entry) == FWTS_OK)
> - fwts_passed(fw, "SMBIOS Table Entry Structure Table Address and Length looks valid.");
> - }
> -
> - return FWTS_OK;
> -}
> -
> -static bool dmi_used_by_kernel(uint8_t type, uint8_t offset)
> -{
> - int i;
> -
> - for (i = 0; dmi_used_by_kernel_table[i].type != TYPE_EOD; i++) {
> - if (dmi_used_by_kernel_table[i].type == type)
> - if ((dmi_used_by_kernel_table[i].offset == FIELD_ANY) ||
> - (dmi_used_by_kernel_table[i].offset == offset))
> - return true;
> - }
> - return false;
> -}
> -
> -static uint16_t dmi_remap_version(fwts_framework *fw, uint16_t old)
> -{
> - int i;
> -
> - for (i=0; dmi_versions[i].old != 0; i++) {
> - if (old == dmi_versions[i].old) {
> - uint16_t new = dmi_versions[i].new;
> - fwts_warning(fw,
> - "Detected a buggy DMI version number "
> - "%" PRIu16 ".%" PRIu16 "remapping to "
> - "%" PRIu16 ".%" PRIu16,
> - VERSION_MAJOR(old), VERSION_MINOR(old),
> - VERSION_MAJOR(new), VERSION_MINOR(new));
> - return new;
> - }
> - }
> -
> - /* All OK, return original */
> - return old;
> -}
> -
> -static void dmi_out_of_range_advice(fwts_framework *fw, uint8_t type, uint8_t offset)
> -{
> - if (dmi_used_by_kernel(type, offset))
> - fwts_advice(fw,
> - "A value that is out of range is incorrect and not conforming to "
> - "the SMBIOS specification. The Linux kernel extracts and uses "
> - "this particular data item, so it is recommended that this SMBIOS "
> - "configuration is corrected even if the impact on the system "
> - "is most probably not critical.");
> - else
> - fwts_advice(fw,
> - "A value that is out of range is incorrect and not conforming to "
> - "the SMBIOS specification. This field is not currently used by "
> - "the Linux kernel, so this firmware bug shouldn't cause any "
> - "problems.");
> -}
> -
> -static void dmi_min_max_uint8_check(fwts_framework *fw,
> - const char *table,
> - uint32_t addr,
> - const char *field,
> - const fwts_dmi_header *hdr,
> - uint8_t offset,
> - uint8_t min,
> - uint8_t max)
> -{
> - uint8_t val = hdr->data[offset];
> - if ((val < min) || (val > max)) {
> - fwts_failed(fw, LOG_LEVEL_HIGH,
> - DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8
> - " (range allowed 0x%2.2" PRIx8 "..0x%2.2" PRIx8 ") "
> - "while accessing entry '%s' @ 0x%8.8" PRIx32
> - ", field '%s', offset 0x%2.2" PRIx8,
> - val, min, max, table, addr, field, offset);
> - dmi_out_of_range_advice(fw, hdr->type, offset);
> - }
> -}
> -
> -static void dmi_min_max_mask_uint8_check(fwts_framework *fw,
> - const char *table,
> - uint32_t addr,
> - const char *field,
> - const fwts_dmi_header *hdr,
> - uint8_t offset,
> - uint8_t min,
> - uint8_t max,
> - uint8_t shift,
> - uint8_t mask)
> -{
> - uint8_t val = (hdr->data[offset] >> shift) & mask;
> -
> - if ((val < min) || (val > max)) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8
> - " (range allowed 0x%2.2" PRIx8 "..0x%2.2" PRIx8 ") "
> - "while accessing entry '%s' @ 0x%8.8" PRIx32
> - ", field '%s', offset 0x%2.2" PRIx8,
> - val, min, max, table, addr, field, offset);
> - dmi_out_of_range_advice(fw, hdr->type, offset);
> - }
> -}
> -
> -static void dmi_str_check_index(fwts_framework *fw,
> - const char *table,
> - uint32_t addr,
> - const char *field,
> - const fwts_dmi_header *hdr,
> - uint8_t offset,
> - uint8_t index)
> -{
> - char *data = (char *)hdr->data;
> - uint8_t i = index;
> - bool used_by_kernel = dmi_used_by_kernel(hdr->type, offset);
> -
> - if (i > 0) {
> - int j;
> - int failed = -1;
> -
> - data += hdr->length;
> - while (i > 1 && *data) {
> - data += strlen(data) + 1;
> - i--;
> - }
> -
> - /* Sanity checks */
> - if (*data == '\0') {
> - int level = used_by_kernel ? LOG_LEVEL_HIGH : LOG_LEVEL_LOW;
> -
> - /* This entry is clearly broken so flag it as a corrupt entry */
> - fwts_failed(fw, level, DMI_STRING_INDEX_OUT_OF_RANGE,
> - "Out of range string index 0x%2.2" PRIx8
> - " while accessing entry '%s' "
> - "@ 0x%8.8" PRIx32 ", field '%s', offset 0x%2.2" PRIx8,
> - index, table, addr, field, offset);
> - if (used_by_kernel)
> - fwts_advice(fw,
> - "DMI strings are stored in a manner that uses a special "
> - "index to fetch the Nth string from the data. For this "
> - "particular DMI string the index given is not in range "
> - "which means this particular entry is broken. The Linux "
> - "kernel uses this string - hence this string should be "
> - "fixed to ensure sane data is passed to the kernel. "
> - "Note that this probably won't cause any critical system "
> - "errors.");
> - else
> - fwts_advice(fw,
> - "DMI strings are stored in a manner that uses a special "
> - "index to fetch the Nth string from the data. For this "
> - "particular DMI string the index given is not in range "
> - "which means this particular entry is broken. The Linux "
> - "kernel does not use this string, so this error will not "
> - "cause any system errors.");
> - return;
> - }
> -
> - /* Scan for known BIOS defaults that vendors forget to set */
> - for (j=0; dmi_patterns[j].label != NULL; j++) {
> - if (dmi_patterns[j].field &&
> - (strcmp(dmi_patterns[j].field, field) == 0) &&
> - (strcmp(dmi_patterns[j].value, data) == 0)) {
> - failed = j;
> - break;
> - } else if (strcmp(dmi_patterns[j].value, data) == 0) {
> - failed = j;
> - break;
> - }
> - }
> - if (failed != -1) {
> - int level = used_by_kernel ? LOG_LEVEL_MEDIUM : LOG_LEVEL_LOW;
> -
> - fwts_failed(fw, level, dmi_patterns[j].label,
> - "String index 0x%2.2" PRIx8
> - " in table entry '%s' @ 0x%8.8" PRIx32
> - ", field '%s', offset 0x%2.2" PRIx8
> - " has a default value '%s' and probably has "
> - "not been updated by the BIOS vendor.",
> - index, table, addr, field, offset, data);
> -
> - if (used_by_kernel) {
> - fwts_advice(fw,
> - "The DMI table contains data which is clearly been "
> - "left in a default setting and not been configured "
> - "for this machine. "
> - "Somebody has probably forgotten to define this "
> - "field and it basically means this field is effectively "
> - "useless. Note that the kernel uses this field so "
> - "it probably should be corrected to ensure the kernel "
> - "is using sane values.");
> - } else {
> - /* This string is broken, but we don't care about it too much */
> - fwts_advice(fw,
> - "The DMI table contains data which is clearly been "
> - "left in a default setting and not been configured "
> - "for this machine. "
> - "Somebody has probably forgotten to define this "
> - "field and it basically means this field is effectively "
> - "useless, however the kernel does not use this data "
> - "so the issue is fairly low.");
> - }
> - }
> - }
> -}
> -
> -static inline void dmi_str_check(fwts_framework *fw,
> - const char *table,
> - uint32_t addr,
> - const char *field,
> - const fwts_dmi_header *hdr,
> - uint8_t offset)
> -{
> - dmi_str_check_index(fw, table, addr, field, hdr, offset, hdr->data[offset]);
> -}
> -
> -static void dmi_uuid_check(fwts_framework *fw,
> - const char *table,
> - uint32_t addr,
> - const char *field,
> - const fwts_dmi_header *hdr,
> - uint8_t offset)
> -{
> - char guid_str[37];
> - int i;
> -
> - fwts_guid_buf_to_str(hdr->data + offset, guid_str, sizeof(guid_str));
> -
> - for (i=0; uuid_patterns[i] != NULL; i++) {
> - if (strcmp(guid_str, uuid_patterns[i]) == 0) {
> - fwts_failed(fw, LOG_LEVEL_LOW, DMI_BAD_UUID,
> - "UUID in table entry '%s' @ 0x%8.8" PRIx32
> - " field '%s', offset 0x%2.2" PRIx8
> - " has a default value '%s' and probably has "
> - "not been updated by the BIOS vendor.",
> - table, addr, field, offset, guid_str);
> - fwts_advice(fw,
> - "The DMI table contains a UUID which is clearly been "
> - "left in a default setting and not been configured "
> - "for this machine. While this is not critical it does "
> - "mean that somebody has probably forgotten to define this "
> - "field and it basically means this field is effectively "
> - "useless.");
> - }
> - }
> -}
> -
> -static void dmi_decode_entry(fwts_framework *fw,
> - uint32_t addr,
> - const fwts_dmi_header *hdr)
> -{
> - uint8_t *ptr;
> - uint8_t count;
> - uint8_t val;
> - uint8_t *data = hdr->data;
> - char tmp[64];
> - char *table;
> - int i;
> - int len;
> - uint32_t failed_count = fw->minor_tests.failed;
> - int battery_count;
> - int ret;
> - int both_ok;
> - fwts_acpi_table_info *acpi_table;
> - fwts_acpi_table_fadt *fadt;
> - bool advice_given = false;
> -
> - switch (hdr->type) {
> - case 0: /* 7.1 */
> - table = "BIOS Information (Type 0)";
> - if (hdr->length < 0x12)
> - break;
> - dmi_str_check(fw, table, addr, "Vendor", hdr, 0x4);
> - dmi_str_check(fw, table, addr, "BIOS Version", hdr, 0x5);
> - dmi_str_check(fw, table, addr, "Release Date", hdr, 0x8);
> - break;
> -
> - case 1: /* 7.2 */
> - table = "System Information (Type 1)";
> - if (hdr->length < 0x08)
> - break;
> - dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x4);
> - dmi_str_check(fw, table, addr, "Product Name", hdr, 0x5);
> - dmi_str_check(fw, table, addr, "Version", hdr, 0x6);
> - dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x7);
> - if (hdr->length < 0x19)
> - break;
> - dmi_uuid_check(fw, table, addr, "UUID", hdr, 0x8);
> - dmi_min_max_uint8_check(fw, table, addr, "Wakeup Type", hdr, 0x18, 0x0, 0x08);
> - if (hdr->length < 0x1b)
> - break;
> - dmi_str_check(fw, table, addr, "SKU Number", hdr, 0x19);
> - dmi_str_check(fw, table, addr, "Family", hdr, 0x1a);
> - break;
> -
> - case 2: /* 7.3 */
> - table = "Base Board Information (Type 2)";
> - if (hdr->length < 0x08)
> - break;
> - dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x4);
> - dmi_str_check(fw, table, addr, "Product", hdr, 0x5);
> - dmi_str_check(fw, table, addr, "Version", hdr, 0x6);
> - dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x7);
> - if (hdr->length < 0x09)
> - break;
> - dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x8);
> - if (hdr->length < 0x0f)
> - break;
> - dmi_str_check(fw, table, addr, "Location In Chassis", hdr, 0xa);
> - dmi_min_max_uint8_check(fw, table, addr, "Type", hdr, 0xd, 0x1, 0xd);
> - break;
> -
> - case 3: /* 7.4 */
> - table = "Chassis Information (Type 3)";
> - if (hdr->length < 0x09)
> - break;
> - dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x4);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Chassis Type", hdr, 0x5, 0x1, 0x1d, 0x0, 0x7f);
> - if (fwts_acpi_find_table(fw, "FACP", 0, &acpi_table) != FWTS_OK)
> - break;
> - if (acpi_table == NULL)
> - break;
> - fadt = (fwts_acpi_table_fadt *)acpi_table->data;
> -
> - if (fadt->preferred_pm_profile >=
> - (sizeof(fwts_acpi_pm_profile_type) / sizeof(fwts_chassis_type_map))) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_INVALID_HARDWARE_ENTRY,
> - "Incorrect Chassis Type "
> - "ACPI FACP reports 0x%" PRIx8,
> - fadt->preferred_pm_profile);
> - break;
> - }
> - if (data[5] >=
> - (sizeof(fwts_dmi_chassis_type) / sizeof(fwts_chassis_type_map))) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_INVALID_HARDWARE_ENTRY,
> - "Incorrect Chassis Type "
> - "SMBIOS Type 3 reports 0x%" PRIx8,
> - data[5]);
> - fwts_advice(fw,
> - "The Chassis Type in the ACPI FADT is out of range "
> - "and hence we cannot identify the preferred power "
> - "management profile for this machine.");
> - break;
> - }
> -
> - /*
> - * LP: #1021674
> - * We should only check profile and chassis types when we know for sure
> - * that we can compare valid types togther. So it is only valid to do
> - * a check if both aren't CHASSIS_OTHER types
> - */
> - both_ok = (fwts_acpi_pm_profile_type[fadt->preferred_pm_profile].mapped != CHASSIS_OTHER) &
> - (fwts_dmi_chassis_type[data[5]].mapped != CHASSIS_OTHER);
> -
> - if ( both_ok &&
> - !(fwts_acpi_pm_profile_type[fadt->preferred_pm_profile].mapped &
> - fwts_dmi_chassis_type[data[5]].mapped)) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_INVALID_HARDWARE_ENTRY,
> - "Unmatched Chassis Type: "
> - "SMBIOS Type 3 reports 0x%" PRIx8 " '%s' "
> - "ACPI FACP reports 0x%" PRIx8 " '%s'",
> - data[5],
> - fwts_dmi_chassis_type[data[5]].name,
> - fadt->preferred_pm_profile,
> - fwts_acpi_pm_profile_type[fadt->preferred_pm_profile].name);
> - /*
> - * Make it a bit more wordy to explain the ramifications
> - */
> - fwts_advice(fw,
> - "The SMBIOS System Enclosure/Chassis type is defined as "
> - "0x%" PRIx8 " '%s' where as the ACPI FACP reports the preferred "
> - "power management profile is 0x%" PRIx8 " '%s' so we possibly "
> - "have conflicting definitions of the machine's PM profile "
> - "for the type of machine. "
> - "See Table 16 of section 4.5.1 of the SMBIOS specification "
> - "and Table 5-34 of section 5.2.9 of the ACPI specification "
> - "for more details. "
> - "This kind of mismatch may lead to incorrect power "
> - "management handling for the type of workload expected "
> - "for this hardware.",
> - data[5],
> - fwts_dmi_chassis_type[data[5]].name,
> - fadt->preferred_pm_profile,
> - fwts_acpi_pm_profile_type[fadt->preferred_pm_profile].name);
> - advice_given = true;
> - }
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Chassis Lock", hdr, 0x5, 0x0, 0x1, 0x7, 0x1);
> - dmi_str_check(fw, table, addr, "Version", hdr, 0x6);
> - dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x7);
> - dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x8);
> - dmi_min_max_uint8_check(fw, table, addr, "Boot-up State", hdr, 0x9, 0x1, 0x6);
> - dmi_min_max_uint8_check(fw, table, addr, "Power Supply State", hdr, 0xa, 0x1, 0x6);
> - dmi_min_max_uint8_check(fw, table, addr, "Thermal State", hdr, 0xb, 0x1, 0x6);
> - dmi_min_max_uint8_check(fw, table, addr, "Security Status", hdr, 0xc, 0x1, 0x5);
> - if (hdr->length < 0x15 + data[0x13] * data[0x14])
> - break;
> - ptr = data + 0x15;
> - len = data[0x14];
> - if (len >= 0x3) {
> - for (i = 0; i < data[0x13]; i++) {
> - val = ptr[i * len] & 0x7f;
> - if (ptr[i * len] & 0x80) {
> - if (val > 0x42)
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8
> - " (range allowed 0x00..0x42) "
> - "while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field "
> - "'SMBIOS Structure Type %d', "
> - "offset 0x%2.2x",
> - val, table, addr, i, 0x15 + (i*len));
> - } else {
> - if ((val < 0x1) || (val > 0xd)) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8
> - " (range allowed 0x00..0x42) "
> - "while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field "
> - "'Base Board Type %d', offset 0x%2.2x",
> - val, table, addr, i, 0x15 + (i*len));
> - }
> - }
> - }
> - }
> - if (hdr->length < 0x16 + data[0x13] * data[0x14])
> - break;
> - dmi_str_check(fw, table, addr, "SKU Number", hdr, 0x15 + data[0x13] * data[0x14]);
> - break;
> -
> - case 4: /* 7.5 */
> - table = "Processor Information (Type 4)";
> - if (hdr->length < 0x1a)
> - break;
> - dmi_str_check(fw, table, addr, "Socket Designation", hdr, 0x4);
> - dmi_min_max_uint8_check(fw, table, addr, "Processor Type", hdr, 0x5, 0x1, 0x6);
> - dmi_str_check(fw, table, addr, "Processor Manufacturer", hdr, 0x7);
> - dmi_str_check(fw, table, addr, "Processor Version", hdr, 0x10);
> - dmi_min_max_uint8_check(fw, table, addr, "Upgrade", hdr, 0x19, 0x1, 0x2a);
> - if (hdr->length < 0x23)
> - break;
> - dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x20);
> - dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x21);
> - dmi_str_check(fw, table, addr, "Part Number", hdr, 0x22);
> - break;
> -
> - case 5: /* 7.6 */
> - table = "Memory Controller Information (Type 5)";
> - if (hdr->length < 0x0f)
> - break;
> - dmi_min_max_uint8_check(fw, table, addr, "Error Detecting Method", hdr, 0x4, 0x1, 0x8);
> - dmi_min_max_uint8_check(fw, table, addr, "Supported Interleave", hdr, 0x6, 0x1, 0x7);
> - dmi_min_max_uint8_check(fw, table, addr, "Current Interleave", hdr, 0x7, 0x1, 0x7);
> - break;
> -
> - case 6: /* 7.7 */
> - table = "Memory Module Information (Type 6)";
> - if (hdr->length < 0x0c)
> - break;
> - dmi_str_check(fw, table, addr, "Socket Designation", hdr, 0x4);
> - break;
> -
> - case 7: /* 7.8 */
> - table = "Cache Information (Type 7)";
> - if (hdr->length < 0x0f)
> - break;
> - dmi_str_check(fw, table, addr, "Socket Designation", hdr, 0x4);
> - if (((GET_UINT16(data + 0x05) >> 5) & 0x0003) == 0x2)
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value %x4.4" PRIx16 " "
> - "bits 5..6 set to illegal value 0x2, only allowed"
> - "0x0, 0x1, 0x3 while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> - GET_UINT16(data + 0x05),
> - table, addr, "Cache Location", 0x5);
> - if (hdr->length < 0x13)
> - break;
> - dmi_min_max_uint8_check(fw, table, addr, "Error Correction Type", hdr, 0x10, 0x1, 0x6);
> - dmi_min_max_uint8_check(fw, table, addr, "System Cache Type", hdr, 0x11, 0x1, 0x5);
> - dmi_min_max_uint8_check(fw, table, addr, "Associativity", hdr, 0x12, 0x1, 0xe);
> - break;
> -
> - case 8: /* 7.9 */
> - table = "Port Connector Information (Type 8)";
> - if (hdr->length < 0x09)
> - break;
> - dmi_str_check(fw, table, addr, "Internal Reference Designator", hdr, 0x4);
> - if (!((data[0x5] <= 0x22) ||
> - (data[0x5] == 0xff) ||
> - ((data[0x5] >= 0xa0) && (data[0x5] <= 0xa4))))
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x00..0x22, 0xa0..0xa4, 0xff) "
> - "while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> - data[0x5], table, addr, "Internal Connector Type", 0x5);
> - dmi_str_check(fw, table, addr, "External Reference Designator", hdr, 0x6);
> - if (!((data[0x7] <= 0x22) ||
> - (data[0x7] == 0xff) ||
> - ((data[0x7] >= 0xa0) && (data[0x7] <= 0xa4))))
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x00..0x22, 0xa0..0xa4, 0xff) "
> - "while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> - data[0x7], table, addr, "Internal Connector Type", 0x7);
> -
> - if (!((data[0x8] <= 0x21) ||
> - (data[0x8] == 0xff) ||
> - ((data[0x8] >= 0xa0) && (data[0x8] <= 0xa1))))
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x00..0x21, 0xa0..0xa1, 0xff) "
> - "while accessing entry '%s' @ 0x%8.8" PRIx32 ", "
> - "field '%s', offset 0x%2.2x",
> - data[0x8], table, addr, "Port Type", 0x8);
> - break;
> -
> - case 9: /* 7.10 */
> - table = "System Slot Information (Type 9)";
> - if (hdr->length < 0x0c)
> - break;
> - dmi_str_check(fw, table, addr, "Slot Designation", hdr, 0x4);
> - if (!(((data[0x5] >= 0x01) && (data[0x5] <= 0x13)) ||
> - ((data[0x5] >= 0xa0) && (data[0x5] <= 0xb6))))
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2x" PRIx8 " "
> - "(range allowed 0x01..0x08, 0xa0..0xa2) "
> - "while accessing entry '%s' @ 0x%8.8" PRIx32 ", "
> - "field '%s', offset 0x%2.2x",
> - data[0x5], table, addr, "Slot Type", 0x5);
> - dmi_min_max_uint8_check(fw, table, addr, "Slot Data Bus Width", hdr, 0x6, 0x1, 0xe);
> - dmi_min_max_uint8_check(fw, table, addr, "Current Usage", hdr, 0x7, 0x1, 0x4);
> - dmi_min_max_uint8_check(fw, table, addr, "Slot Length", hdr, 0x8, 0x1, 0x4);
> - break;
> -
> - case 10: /* 7.11 */
> - table = "On Board Devices (Type 10)";
> - count = (hdr->length - 4) / 2;
> - for (i = 0; i < count; i++) {
> - snprintf(tmp, sizeof(tmp), "Type (Device #%d)", i);
> - dmi_min_max_mask_uint8_check(fw, table, addr, tmp, hdr, 4 + (2 * i), 0x1, 0xa, 0x0, 0x7f);
> - snprintf(tmp, sizeof(tmp), "Description (Device #%d)", i);
> - dmi_str_check(fw, table, addr, tmp, hdr, 5 + (2 * i));
> - }
> - break;
> -
> - case 11: /* 7.12 */
> - table = "OEM Strings (Type 11)";
> - if (hdr->length < 0x5)
> - break;
> - for (i = 1; i <= hdr->data[4]; i++) {
> - snprintf(tmp, sizeof(tmp), "String %d", i);
> - dmi_str_check_index(fw, table, addr, tmp, hdr, 0x4, i);
> - }
> - break;
> -
> - case 12: /* 7.13 */
> - table = "System Configuration Options (Type 12)";
> - if (hdr->length < 0x5)
> - break;
> - for (i = 1; i <= hdr->data[4]; i++) {
> - snprintf(tmp, sizeof(tmp), "Option %d", i);
> - dmi_str_check_index(fw, table, addr, tmp, hdr, 0x4, i);
> - }
> - break;
> -
> - case 13: /* 7.14 */
> - table = "BIOS Language Information (Type 13)";
> - if (hdr->length < 0x16)
> - break;
> - for (i = 1; i <= hdr->data[4]; i++) {
> - snprintf(tmp, sizeof(tmp), "BIOS Language String %d", i);
> - dmi_str_check_index(fw, table, addr, tmp, hdr, 0x4, i);
> - }
> - dmi_str_check(fw, table, addr, "Currently Installed Language", hdr, 0x15);
> - break;
> -
> - case 14: /* 7.15 */
> - table = "Group Associations (Type 14)";
> - if (hdr->length < 0x05)
> - break;
> - dmi_str_check(fw, table, addr, "Name", hdr, 0x4);
> - break;
> -
> - case 15: /* 7.16 */
> - table = "System Event Log (Type 15)";
> - if (hdr->length < 0x14)
> - break;
> - val = hdr->data[0x0a];
> - if (!((val <= 0x04) || (val >= 0x80))) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x00..0x01, "
> - "0x80..0xff) while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> - val, table, addr, "Access Method", 0x0a);
> - }
> - if (hdr->length < 0x17)
> - break;
> - val = hdr->data[0x14];
> - if (!((val <= 0x01) || (val >= 0x80))) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x00..0x01, "
> - "0x80..0xff) while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> - val, table, addr, "Log Header Format", 0x14);
> - }
> - if (hdr->length < 0x17 + data[0x15] * data[0x16])
> - break;
> - if (data[0x16] >= 0x02) {
> - uint8_t *ptr = data + 0x17;
> - int i;
> - for (i = 0; i < data[0x15]; i++) {
> - int j = data[0x16] * i;
> - val = ptr[j];
> - if (!(((val >= 0x01) && (val <= 0x0e)) ||
> - ((val >= 0x10) && (val <= 0x17)) ||
> - (val >= 0x80))) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x01..0x0e, 0x10..0x17, "
> - "0x80..0xff) while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', item %d",
> - val, table, addr, "Log Descriptor Type", i);
> - }
> - val = ptr[j + 1];
> - if ((val > 0x06) && (val < 0x80)) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x00..0x06, 0x80..0xff) "
> - "while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', item %d",
> - val, table, addr, "Log Descriptor Format", i);
> - }
> - }
> - }
> - break;
> -
> - case 16: /* 7.17 */
> - table = "Physical Memory Array (Type 16)";
> - if (hdr->length < 0x0f)
> - break;
> - if (!(((data[0x4] >= 0x01) && (data[0x4] <= 0x0a)) ||
> - ((data[0x4] >= 0xa0) && (data[0x4] <= 0xa3))))
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x01..0x0a, 0xa0..0xa3) "
> - "while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> - data[0x4], table, addr, "Location", 0x4);
> - dmi_min_max_uint8_check(fw, table, addr, "Use", hdr, 0x5, 0x1, 0x7);
> - dmi_min_max_uint8_check(fw, table, addr, "Error Corrrection Type", hdr, 0x6, 0x1, 0x7);
> - break;
> -
> - case 17: /* 7.18 */
> - table = "Memory Device (Type 17)";
> - if (hdr->length < 0x15)
> - break;
> - dmi_min_max_uint8_check(fw, table, addr, "Form Factor", hdr, 0xe, 0x1, 0xf);
> - dmi_str_check(fw, table, addr, "Locator", hdr, 0x10);
> - dmi_str_check(fw, table, addr, "Bank Locator", hdr, 0x11);
> - dmi_min_max_uint8_check(fw, table, addr, "Memory Type", hdr, 0x12, 0x1, 0x19);
> - if (hdr->length < 0x1b)
> - break;
> - dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x17);
> - dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x18);
> - dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x19);
> - dmi_str_check(fw, table, addr, "Part Number", hdr, 0x1a);
> - break;
> -
> - case 18: /* 7.19 */
> - table = "32-bit Memory Error Information (Type 18)";
> - if (hdr->length < 0x17)
> - break;
> - dmi_min_max_uint8_check(fw, table, addr, "Error Type", hdr, 0x4, 0x1, 0xe);
> - dmi_min_max_uint8_check(fw, table, addr, "Error Granularity", hdr, 0x5, 0x1, 0x4);
> - dmi_min_max_uint8_check(fw, table, addr, "Error Operation", hdr, 0x6, 0x1, 0x5);
> - break;
> -
> - case 19: /* 7.20 */
> - table = "Memory Array Mapped Address (Type 19)";
> - if (hdr->length < 0x0F)
> - break;
> - if (hdr->length >= 0x1F && GET_UINT32(data + 0x04) == 0xFFFFFFFF) {
> - uint64_t start, end;
> - start = GET_UINT64(data + 0x0F);
> - end = GET_UINT64(data + 0x17);
> - if (start == end)
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> - "Extended Start and End addresses are identical "
> - "while accessing entry '%s' @ 0x%8.8" PRIx32 ", "
> - "fields 'Extended Starting Address' and 'Extended Ending Address'",
> - table, addr);
> - } else {
> - if (GET_UINT32(data + 0x08) - GET_UINT32(data + 0x04) + 1 == 0)
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> - "Illegal zero mapped address range "
> - "for entry '%s' @ 0x%8.8" PRIx32, table, addr);
> - }
> - break;
> -
> - case 20: /* 7.21 */
> - table = "Memory Device Mapped Address (Type 20)";
> - if (hdr->length < 0x13)
> - break;
> - if (hdr->length >= 0x23 && GET_UINT32(data + 0x04) == 0xFFFFFFFF) {
> - uint64_t start, end;
> - start = GET_UINT64(data + 0x13);
> - end = GET_UINT64(data + 0x1B);
> - if (start == end)
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> - "Extended Start and End addresses are identical "
> - "while accessing entry '%s' @ 0x%8.8" PRIx32 ", "
> - "fields 'Extended Starting Address' and 'Extended Ending Address'",
> - table, addr);
> - } else {
> - if (GET_UINT32(data + 0x08) - GET_UINT32(data + 0x04) + 1 == 0)
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> - "Illegal zero mapped address range "
> - "for entry '%s' @ 0x%8.8" PRIx32, table, addr);
> - }
> - if (data[0x10] == 0)
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> - "Illegal row position %2.2" PRIx8 ", "
> - "while accessing entry '%s' @ 0x%8.8" PRIx32
> - ", field '%s', offset 0x%2.2x",
> - data[0x10], table, addr, "Partial Row Position", 0x10);
> - break;
> -
> - case 21: /* 7.22 */
> - table = "Built-in Pointing Device (Type 21)";
> - if (hdr->length < 0x07)
> - break;
> - dmi_min_max_uint8_check(fw, table, addr, "Type", hdr, 0x4, 0x1, 0x9);
> - if (!(((data[0x5] >= 0x01) && (data[0x5] <= 0x08)) ||
> - ((data[0x5] >= 0xa0) && (data[0x5] <= 0xa2)))) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x01..0x08, 0xa0..0xa2) "
> - "while accessing '%s', field '%s', offset 0x%2.2x",
> - data[0x5], table, "Interface", 0x5);
> - }
> - break;
> -
> - case 22: /* 7.23 */
> - table = "Portable Battery (Type 22)";
> - if (hdr->length < 0x10)
> - break;
> - dmi_str_check(fw, table, addr, "Location", hdr, 0x4);
> - dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x5);
> - if (data[0x06] || hdr->length < 0x1A)
> - dmi_str_check(fw, table, addr, "Manufacturer Date", hdr, 0x6);
> - if (data[0x07] || hdr->length < 0x1A)
> - dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x7);
> - dmi_str_check(fw, table, addr, "Device Name", hdr, 0x8);
> - if (data[0x09] != 0x02 || hdr->length < 0x1A)
> - dmi_str_check(fw, table, addr, "Device Chemistry", hdr, 0x9);
> -
> - dmi_str_check(fw, table, addr, "SBDS Version Number", hdr, 0xe);
> - if (hdr->length < 0x1A)
> - break;
> - if (data[0x09] == 0x02)
> - dmi_str_check(fw, table, addr, "SBDS Device Chemistry", hdr, 0x14);
> -
> - ret = fwts_battery_get_count(fw, &battery_count);
> - if (ret != FWTS_OK || battery_count < 1) {
> - fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_INVALID_HARDWARE_ENTRY,
> - "Invalid Hardware Configuration "
> - "(no battery found) ");
> - }
> - break;
> - case 23: /* 7.24 */
> - table = "System Reset (Type 23)";
> - if (hdr->length < 0x0D)
> - break;
> - if (!(data[0x04] & (1 << 5)))
> - break;
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Capabilities (bits 1..2)", hdr, 0x4, 0x1, 0x3, 1, 0x3);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Capabilities (bits 3..4)", hdr, 0x4, 0x1, 0x3, 3, 0x3);
> - break;
> -
> - case 24: /* 7.25 */
> - table = "Hardware Security (Type 24)";
> - /* if (hdr->length < 0x05)
> - break; */
> - break;
> -
> - case 25: /* 7.26 */
> - table = "System Power Controls (Type 25)";
> - /* if (hdr->length < 0x9)
> - break; */
> - break;
> -
> - case 26: /* 7.27 */
> - table = "Voltage Probe (Type 26)";
> - if (hdr->length < 0x14)
> - break;
> - dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Location (bits 0..4)", hdr, 0x5, 0x1, 0xb, 0, 0x1f);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Status (bits 5..7)", hdr, 0x5, 0x1, 0x6, 5, 0x7);
> - break;
> -
> - case 27: /* 7.28 */
> - table = "Cooling Device (Type 27)";
> - if (hdr->length < 0xc)
> - break;
> - val = data[0x06] & 0x1f;
> - if (!(((val >= 0x01) && (val <= 0x09)) ||
> - ((val >= 0x10) && (val <= 0x11))))
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x01..0x09, 0x10..0x11) "
> - "while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', "
> - "offset 0x%2.2x, mask 0x%2.2x",
> - data[0x6], table, addr, "Device Type", 0x6, 0x1f);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Status (bits 5..7)", hdr, 0x6, 0x1, 0x6, 5, 0x7);
> - if (hdr->length < 0x0f)
> - break;
> - dmi_str_check(fw, table, addr, "Description", hdr, 0xe);
> - break;
> -
> - case 28: /* 7.29 */
> - table = "Temperature Probe (Type 28)";
> - if (hdr->length < 0x14)
> - break;
> - dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Location (bits 0..4)", hdr, 0x5, 0x1, 0xf, 0, 0x1f);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Status (bits 5..7)", hdr, 0x5, 0x1, 0x6, 5, 0x7);
> - break;
> -
> - case 29: /* 7.30 */
> - table = "Electrical Current Probe (Type 29)";
> - if (hdr->length < 0x14)
> - break;
> - dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Location (bits 0..4)", hdr, 0x5, 0x1, 0xb, 0, 0x1f);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Status (bits 5..7)", hdr, 0x5, 0x1, 0x6, 5, 0x7);
> - break;
> -
> - case 30: /* 7.31 */
> - table = "Out-of-band Remote Access (Type 30)";
> - if (hdr->length < 0x06)
> - break;
> - dmi_str_check(fw, table, addr, "Manufacturer Name", hdr, 0x4);
> - break;
> -
> - case 31: /* 7.32 */
> - table = "Boot Integrity Services Entry Point (Type 31)";
> - /*
> - if (hdr->length < 0x1c)
> - break;
> - */
> - break;
> -
> - case 32: /* 7.33 */
> - table = "System Boot Information (Type 32)";
> - if (hdr->length < 0xb)
> - break;
> - if ((data[0xa] > 0x8) && (data[0xa] < 128))
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x00..0x08, 0x80..0xff) "
> - "while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> - data[0xa], table, addr, "Boot Status", 0xa);
> - break;
> -
> - case 33: /* 7.34 */
> - table = "64-bit Memory Error Information (Type 33)";
> - if (hdr->length < 0x1f)
> - break;
> - dmi_min_max_uint8_check(fw, table, addr, "Error Type", hdr, 0x4, 0x1, 0xe);
> - dmi_min_max_uint8_check(fw, table, addr, "Error Granularity", hdr, 0x5, 0x1, 0x4);
> - dmi_min_max_uint8_check(fw, table, addr, "Error Operation", hdr, 0x6, 0x1, 0x5);
> - break;
> -
> - case 34: /* 7.35 */
> - table = "Management Device (Type 34)";
> - if (hdr->length < 0x0b)
> - break;
> - dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> - dmi_min_max_uint8_check(fw, table, addr, "Type", hdr, 0x5, 0x1, 0xd);
> - dmi_min_max_uint8_check(fw, table, addr, "Address Type", hdr, 0xa, 0x1, 0x5);
> - break;
> -
> - case 35: /* 7.36 */
> - table = "Management Device Component (Type 35)";
> - if (hdr->length < 0x0b)
> - break;
> - dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> - break;
> -
> - case 36: /* 7.37 */
> - table = "Management Device Threshold Data (Type 36)";
> - /*
> - if (hdr->length < 0x10)
> - break;
> - */
> - break;
> -
> - case 37: /* 7.38 */
> - table = "Memory Channel (Type 37)";
> - if (hdr->length < 0x07)
> - break;
> - dmi_min_max_uint8_check(fw, table, addr, "Type", hdr, 0x4, 0x1, 0x4);
> - break;
> -
> - case 38: /* 7.39 */
> - table = "IPMI Device Information (Type 38)";
> - dmi_min_max_uint8_check(fw, table, addr, "Interface Type", hdr, 0x4, 0x0, 0x4);
> - break;
> -
> - case 39: /* 7.40 */
> - table = "System Power Supply (Type 39)";
> - if (hdr->length < 0x10)
> - break;
> - dmi_str_check(fw, table, addr, "Location", hdr, 0x5);
> - dmi_str_check(fw, table, addr, "Device Name", hdr, 0x6);
> - dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x7);
> - dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x8);
> - dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x9);
> - dmi_str_check(fw, table, addr, "Model Part Number", hdr, 0xa);
> - dmi_str_check(fw, table, addr, "Revision Level", hdr, 0xb);
> - break;
> -
> - case 40: /* 7.41 */
> - table = "Additional Information (Type 40)";
> - break;
> -
> - case 41: /* 7.42 */
> - table = "Onboard Device (Type 41)";
> - if (hdr->length < 0xb)
> - break;
> - dmi_str_check(fw, table, addr, "Reference Designation", hdr, 0x4);
> - dmi_min_max_mask_uint8_check(fw, table, addr, "Device Type", hdr, 0x5, 0x1, 0xa, 0, 0x7f);
> - break;
> -
> - case 42: /* 7.43 */
> - table = "Management Controller Host Interface (Type 42)";
> - if (hdr->length < 0x05)
> - break;
> - if (!((data[0x04] >= 0x02 && data[0x04] <= 0x08) ||
> - (data[0x04] == 0xF0)))
> - fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_MGMT_CTRL_HOST_TYPE,
> - "Out of range value 0x%2.2" PRIx8 " "
> - "(range allowed 0x02..0x08, 0xf0) "
> - "while accessing entry '%s' @ "
> - "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> - data[0x4], table, addr, "Reference Designation", 0x4);
> - break;
> -
> - case 126: /* 7.44 */
> - table = "Inactive (Type 126)";
> - break;
> - case SMBIOS_END_OF_TABLE: /* 7.45 */
> - table = "End of Table (Type 127)";
> - break;
> - default:
> - snprintf(tmp, sizeof(tmp), "Unknown (Type %" PRId8 ")", hdr->type);
> - table = tmp;
> - break;
> - }
> - if (fw->minor_tests.failed == failed_count)
> - fwts_passed(fw, "Entry @ 0x%8.8" PRIx32 " '%s'", addr, table);
> - else if (!advice_given && hdr->type <= 42)
> - fwts_advice(fw,
> - "It may be worth checking against section 7.%" PRId8 " of the "
> - "System Management BIOS (SMBIOS) Reference Specification "
> - "(see http://www.dmtf.org/standards/smbios).", hdr->type+1);
> -}
> -
> -static int dmi_version_check(fwts_framework *fw, uint16_t version)
> -{
> - if (version > DMI_VERSION) {
> - fwts_warning(fw,
> - "SMBIOS version %" PRIu16 ".%" PRIu16
> - " is not supported by the dmi_decode "
> - "test. This test only supports SMBIOS version "
> - "%" PRIu16 ".%" PRIu16 " and lower.",
> - VERSION_MAJOR(version), VERSION_MINOR(version),
> - VERSION_MAJOR(DMI_VERSION), VERSION_MINOR(DMI_VERSION));
> - return FWTS_ERROR;
> - }
> - return FWTS_OK;
> -}
> -
> -static void dmi_scan_tables(fwts_framework *fw,
> - fwts_smbios_entry *entry,
> - uint8_t *table)
> -{
> - uint8_t *entry_data = table;
> - uint16_t table_length;
> - uint16_t struct_count;
> - int i = 0;
> -
> - table_length = entry->struct_table_length;
> - struct_count = entry->number_smbios_structures;
> -
> - while ((i < struct_count) &&
> - (entry_data <= (table + table_length - 4))) {
> - uint32_t addr = entry->struct_table_address + (entry_data - table);
> - fwts_dmi_header hdr;
> - uint8_t *next_entry;
> -
> - hdr.type = entry_data[0];
> - hdr.length = entry_data[1];
> - hdr.handle = GET_UINT16(entry_data + 2);
> - hdr.data = entry_data;
> -
> - /* Sanity check */
> - if (hdr.length < 4) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_INVALID_ENTRY_LENGTH,
> - "Invald header length of entry #%d, "
> - "length was 0x%2.2" PRIx8 ".",
> - i, hdr.length);
> - fwts_advice(fw,
> - "DMI entry header lengths must be 4 or more bytes long "
> - "so this error indicates that the DMI table is unreliable "
> - "and DMI table checking has been aborted at entry #%d.", i);
> - break;
> - }
> -
> - /* Real Physical Address */
> - next_entry = entry_data + hdr.length;
> -
> - /* Look for structure terminator, ends in two zero bytes */
> - while (((next_entry - table + 1) < table_length) &&
> - ((next_entry[0] != 0) || (next_entry[1] != 0))) {
> - next_entry++;
> - }
> -
> - /* Skip over terminating two zero bytes, see section 6.1 of spec */
> - next_entry += 2;
> -
> - if ((next_entry - table) <= table_length)
> - dmi_decode_entry(fw, addr, &hdr);
> -
> - i++;
> - entry_data = next_entry;
> - }
> -
> - if (table_length != (entry_data - table))
> - fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_BAD_TABLE_LENGTH,
> - "DMI table length was %" PRId16 " bytes (as specified by "
> - "the SMBIOS header) but the DMI entries used %td bytes.",
> - table_length, entry_data - table);
> -
> - if (struct_count != i)
> - fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_STRUCT_COUNT,
> - "DMI table was DMI %d entries in size (as specified by "
> - "the SMBIOS header) but only %d DMI entries were found.",
> - struct_count, i);
> -}
> -
> -static int dmi_decode_test2(fwts_framework *fw)
> -{
> - void *addr;
> - fwts_smbios_entry entry;
> - fwts_smbios_type type;
> - uint16_t version = 0;
> - uint8_t *table;
> -
> - addr = fwts_smbios_find_entry(fw, &entry, &type, &version);
> - if (addr == NULL) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_NO_TABLE_HEADER,
> - "Cannot find SMBIOS or DMI table entry.");
> - return FWTS_ERROR;
> - }
> - if (type == FWTS_SMBIOS_UNKNOWN) {
> - fwts_failed(fw, LOG_LEVEL_HIGH, DMI_NO_TABLE,
> - "Cannot find a valid SMBIOS or DMI table.");
> - return FWTS_ERROR;
> - }
> -
> - /* Fix broken version numbers found in the wild */
> - version = dmi_remap_version(fw, version);
> - if (dmi_version_check(fw, version) != FWTS_OK)
> - return FWTS_SKIP;
> -
> - table = fwts_mmap((off_t)entry.struct_table_address,
> - (size_t)entry.struct_table_length);
> - if (table == FWTS_MAP_FAILED) {
> - fwts_log_error(fw, "Cannot mmap SMBIOS tables from "
> - "%8.8" PRIx32 "..%8.8" PRIx32 ".",
> - entry.struct_table_address,
> - entry.struct_table_address + entry.struct_table_length);
> - return FWTS_ERROR;
> - }
> -
> - dmi_scan_tables(fw, &entry, table);
> -
> - (void)fwts_munmap(table, (size_t)entry.struct_table_length);
> -
> - return FWTS_OK;
> -}
> -
> -static fwts_framework_minor_test dmi_decode_tests[] = {
> - { dmi_decode_test1, "Find and Check SMBIOS Table Entry Point." },
> - { dmi_decode_test2, "Test DMI/SMBIOS tables for errors." },
> - { NULL, NULL }
> -};
> -
> -static fwts_framework_ops dmi_decode_ops = {
> - .description = "Test DMI/SMBIOS tables for errors.",
> - .minor_tests = dmi_decode_tests
> -};
> -
> -FWTS_REGISTER("dmi_decode", &dmi_decode_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_ROOT_PRIV);
> -
> -#endif
> diff --git a/src/dmi/dmicheck/dmicheck.c b/src/dmi/dmicheck/dmicheck.c
> new file mode 100644
> index 0000000..77022ba
> --- /dev/null
> +++ b/src/dmi/dmicheck/dmicheck.c
> @@ -0,0 +1,1492 @@
> +/*
> + * Copyright (C) 2010-2013 Canonical
> + *
> + * 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 the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + */
> +
> +#include <stdbool.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <inttypes.h>
> +
> +#include "fwts.h"
> +
> +#ifdef FWTS_ARCH_INTEL
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <limits.h>
> +
> +#define DMI_VERSION (0x0207)
> +#define VERSION_MAJOR(v) ((v) >> 8)
> +#define VERSION_MINOR(v) ((v) & 0xff)
> +
> +#define SMBIOS_END_OF_TABLE (127)
> +
> +#define DMI_NO_TABLE "DMINoTable"
> +#define DMI_NO_TABLE_HEADER "DMINoTableHeader"
> +#define DMI_BAD_TABLE_LENGTH "DMIBadTableLength"
> +#define DMI_BAD_UUID "DMIBadUUID"
> +#define DMI_STRUCT_COUNT "DMIStructCount"
> +#define DMI_VALUE_OUT_OF_RANGE "DMIValueOutOfRange"
> +#define DMI_STRING_INDEX_OUT_OF_RANGE "DMIStringIndexOutOfRange"
> +#define DMI_ILLEGAL_MAPPED_ADDR_RANGE "DMIIllegalMappedAddrRange"
> +#define DMI_MGMT_CTRL_HOST_TYPE "DMIMgmtCtrlHostType"
> +#define DMI_INVALID_ENTRY_LENGTH "DMIInvalidEntryLength"
> +#define DMI_INVALID_HARDWARE_ENTRY "DMIInvalidHardwareEntry"
> +
> +#define GET_UINT16(x) (uint16_t)(*(const uint16_t *)(x))
> +#define GET_UINT32(x) (uint32_t)(*(const uint32_t *)(x))
> +#define GET_UINT64(x) (uint64_t)(*(const uint64_t *)(x))
> +
> +#define CHASSIS_OTHER 0x00
> +#define CHASSIS_DESKTOP 0x01
> +#define CHASSIS_WORKSTATION 0x02
> +#define CHASSIS_MOBILE 0x04
> +#define CHASSIS_SERVER 0x08
> +
> +typedef struct {
> + const char *label;
> + const char *field;
> + const char *value;
> +} fwts_dmi_pattern;
> +
> +typedef struct {
> + uint16_t old;
> + uint16_t new;
> +} fwts_dmi_version;
> +
> +typedef struct {
> + const char *name;
> + uint8_t original;
> + uint8_t mapped;
> +} fwts_chassis_type_map;
> +
> +typedef struct {
> + uint8_t type;
> + uint8_t offset;
> +} fwts_dmi_used_by_kernel;
> +
> +static const fwts_dmi_pattern dmi_patterns[] = {
> + { "DMISerialNumber", "Serial Number", "0123456789" },
> + { "DMISerialNumber", "Serial Number", "System Serial Number" },
> + { "DMISerialNumber", "Serial Number", "MB-1234567890" },
> + { "DMISerialNumber", NULL, "Chassis Serial Number" },
> + { "DMIAssetTag", "Asset Tag", "1234567890" },
> + { "DMIAssetTag", "Asset Tag", "Asset-1234567890" },
> + { "DMIChassisVendor", NULL, "Chassis Manufacture" },
> + { "DMIChassisVersion", NULL, "Chassis Version" },
> + { "DMIProductVersion", NULL, "System Version" },
> + { "DMIBadDefault", NULL, "To Be Filled By O.E.M." },
> + { NULL, NULL, NULL }
> +};
> +
> +static const char *uuid_patterns[] = {
> + "0A0A0A0A-0A0A-0A0A-0A0A-0A0A0A0A0A0A",
> + NULL,
> +};
> +
> +static const fwts_chassis_type_map fwts_dmi_chassis_type[] = {
> + { "Invalid", FWTS_SMBIOS_CHASSIS_INVALID, CHASSIS_OTHER },
> + { "Other", FWTS_SMBIOS_CHASSIS_OTHER, CHASSIS_OTHER },
> + { "Unknown", FWTS_SMBIOS_CHASSIS_UNKNOWN, CHASSIS_OTHER },
> + { "Desktop", FWTS_SMBIOS_CHASSIS_DESKTOP, CHASSIS_DESKTOP },
> + { "Low Profile Desktop",FWTS_SMBIOS_CHASSIS_LOW_PROFILE_DESKTOP, CHASSIS_DESKTOP },
> + { "Pizza Box", FWTS_SMBIOS_CHASSIS_PIZZA_BOX, CHASSIS_DESKTOP },
> + { "Mini Tower", FWTS_SMBIOS_CHASSIS_MINI_TOWER, CHASSIS_DESKTOP },
> + { "Chassis Tower", FWTS_SMBIOS_CHASSIS_TOWER, CHASSIS_DESKTOP },
> + { "Portable", FWTS_SMBIOS_CHASSIS_PORTABLE, CHASSIS_MOBILE },
> + { "Laptop", FWTS_SMBIOS_CHASSIS_LAPTOP, CHASSIS_MOBILE },
> + { "Notebook", FWTS_SMBIOS_CHASSIS_NOTEBOOK, CHASSIS_MOBILE },
> + { "Handheld", FWTS_SMBIOS_CHASSIS_HANDHELD, CHASSIS_MOBILE },
> + { "Docking Station", FWTS_SMBIOS_CHASSIS_DOCKING_STATION, CHASSIS_DESKTOP },
> + { "All In One", FWTS_SMBIOS_CHASSIS_ALL_IN_ONE, CHASSIS_DESKTOP },
> + { "Sub Notebook", FWTS_SMBIOS_CHASSIS_SUB_NOTEBOOK, CHASSIS_MOBILE },
> + { "Space Saving", FWTS_SMBIOS_CHASSIS_SPACE_SAVING, CHASSIS_DESKTOP },
> + { "Lunch Box", FWTS_SMBIOS_CHASSIS_LUNCH_BOX, CHASSIS_DESKTOP | CHASSIS_MOBILE},
> + { "Server Chassis", FWTS_SMBIOS_CHASSIS_MAIN_SERVER_CHASSIS, CHASSIS_SERVER },
> + { "Expansion Chassis", FWTS_SMBIOS_CHASSIS_EXPANISON_CHASSIS, CHASSIS_OTHER },
> + { "Sub Chassis", FWTS_SMBIOS_CHASSIS_SUB_CHASSIS, CHASSIS_OTHER },
> + { "Bus Expansion Chassis", FWTS_SMBIOS_CHASSIS_BUS_EXPANSION_CHASSIS, CHASSIS_OTHER },
> + { "Peripheral Chassis", FWTS_SMBIOS_CHASSIS_PERIPHERAL_CHASSIS, CHASSIS_OTHER },
> + { "Raid Chassis", FWTS_SMBIOS_CHASSIS_RAID_CHASSIS, CHASSIS_OTHER },
> + { "Rack Mount Chassis", FWTS_SMBIOS_CHASSIS_RACK_MOUNT_CHASSIS, CHASSIS_OTHER },
> + { "Sealed Case PC", FWTS_SMBIOS_CHASSIS_SEALED_CASE_PC, CHASSIS_DESKTOP },
> + { "Multi System Chassis",FWTS_SMBIOS_CHASSIS_MULTI_SYSTEM_CHASSIS, CHASSIS_OTHER },
> + { "Compact PCI", FWTS_SMBIOS_CHASSIS_COMPACT_PCI, CHASSIS_OTHER },
> + { "Advanced TCA", FWTS_SMBIOS_CHASSIS_ADVANCED_TCA, CHASSIS_OTHER },
> + { "Blade", FWTS_SMBIOS_CHASSIS_BLADE, CHASSIS_SERVER },
> + { "Enclosure", FWTS_SMBIOS_CHASSIS_BLASE_ENCLOSURE, CHASSIS_SERVER }
> +};
> +
> +static const fwts_chassis_type_map fwts_acpi_pm_profile_type[] = {
> + { "Unspecified", FWTS_FACP_UNSPECIFIED, CHASSIS_OTHER },
> + { "Desktop", FWTS_FACP_DESKTOP, CHASSIS_DESKTOP },
> + { "Mobile", FWTS_FACP_MOBILE, CHASSIS_MOBILE },
> + { "Workstation", FWTS_FACP_WORKSTATION, CHASSIS_WORKSTATION },
> + { "Enterprise Server", FWTS_FACP_ENTERPRISE_SERVER, CHASSIS_SERVER },
> + { "SOHO Server", FWTS_FACP_SOHO_SERVER, CHASSIS_SERVER | CHASSIS_DESKTOP },
> + { "Appliance PC", FWTS_FACP_APPLIANCE_PC, CHASSIS_DESKTOP },
> + { "Performance Server", FWTS_FACP_PERFORMANCE_SERVER, CHASSIS_SERVER },
> + { "Tablet", FWTS_FACP_TABLET, CHASSIS_MOBILE }
> +};
> +
> +/* Remapping table from buggy version numbers to correct values */
> +static const fwts_dmi_version dmi_versions[] = {
> + { 0x021f, 0x0203 },
> + { 0x0221, 0x0203 },
> + { 0x0233, 0x0206 },
> + { 0, 0 }
> +};
> +
> +#define FIELD_ANY 0xff
> +#define TYPE_EOD 0xff
> +
> +/*
> + * DMI decoded fields used by the kernel, i.e. fields
> + * we care that work,
> + * see drivers/firmware/dmi_scan.c, dmi_decode()
> + */
> +static fwts_dmi_used_by_kernel dmi_used_by_kernel_table[] = {
> + /* Type 0 BIOS Information fields */
> + { 0, 4 },
> + { 0, 5 },
> + { 0, 8 },
> + /* Type 1, System Information */
> + { 1, 4 },
> + { 1, 5 },
> + { 1, 6 },
> + { 1, 7 },
> + { 1, 8 },
> + /* Type 2, Base Board Information */
> + { 2, 4 },
> + { 2, 5 },
> + { 2, 6 },
> + { 2, 7 },
> + { 2, 8 },
> + /* Type 3, Chassis Information */
> + { 3, 4 },
> + { 3, 5 },
> + { 3, 6 },
> + { 3, 7 },
> + { 3, 8 },
> + /* Type 10, Onboard Devices Information */
> + { 10, FIELD_ANY },
> + /* Type 11, OEM Strings */
> + { 11, FIELD_ANY },
> + /* Type 38, IPMI Device Information */
> + { 38, FIELD_ANY },
> + /* Type 41, Onboard Devices Extended Information */
> + { 41, FIELD_ANY },
> + /* End */
> + { TYPE_EOD, 0xff },
> +};
> +
> +static void dmi_dump_entry(fwts_framework *fw, fwts_smbios_entry *entry, fwts_smbios_type type)
> +{
> + if (type == FWTS_SMBIOS) {
> + fwts_log_info_verbatum(fw, "SMBIOS Entry Point Structure:");
> + fwts_log_info_verbatum(fw, " Anchor String : %4.4s", entry->signature);
> + fwts_log_info_verbatum(fw, " Checksum : 0x%2.2x", entry->checksum);
> + fwts_log_info_verbatum(fw, " Entry Point Length : 0x%2.2x", entry->length);
> + fwts_log_info_verbatum(fw, " Major Version : 0x%2.2x", entry->major_version);
> + fwts_log_info_verbatum(fw, " Minor Version : 0x%2.2x", entry->minor_version);
> + fwts_log_info_verbatum(fw, " Maximum Struct Size : 0x%2.2x", entry->max_struct_size);
> + fwts_log_info_verbatum(fw, " Entry Point Revision : 0x%2.2x", entry->revision);
> + fwts_log_info_verbatum(fw, " Formatted Area : 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x",
> + entry->formatted_area[0], entry->formatted_area[1],
> + entry->formatted_area[2], entry->formatted_area[3],
> + entry->formatted_area[4]);
> + }
> + if (type == FWTS_SMBIOS_DMI_LEGACY)
> + fwts_log_info_verbatum(fw, "Legacy DMI Entry Point Structure:");
> +
> + /* Common to SMBIOS and SMBIOS_DMI_LEGACY */
> + fwts_log_info_verbatum(fw, " Intermediate Anchor : %5.5s", (char *)entry->anchor_string);
> + fwts_log_info_verbatum(fw, " Intermediate Checksum : 0x%2.2x", entry->intermediate_checksum);
> + fwts_log_info_verbatum(fw, " Structure Table Length : 0x%4.4x", entry->struct_table_length);
> + fwts_log_info_verbatum(fw, " Structure Table Address: 0x%8.8x", entry->struct_table_address);
> + fwts_log_info_verbatum(fw, " # of SMBIOS Structures : 0x%4.4x", entry->number_smbios_structures);
> + fwts_log_info_verbatum(fw, " SBMIOS BCD Revision : %2.2x", entry->smbios_bcd_revision);
> + if (entry->smbios_bcd_revision == 0)
> + fwts_log_info_verbatum(fw, " BCD Revision 00 indicates compliance with specification stated in Major/Minor Version.");
> +}
> +
> +static int dmi_sane(fwts_framework *fw, fwts_smbios_entry *entry)
> +{
> + uint8_t *table, *ptr;
> + uint8_t dmi_entry_length;
> + uint8_t dmi_entry_type = 0;
> + uint16_t i = 0;
> + uint16_t table_length = entry->struct_table_length;
> + int ret = FWTS_OK;
> +
> + ptr = table = fwts_mmap((off_t)entry->struct_table_address,
> + (size_t)table_length);
> + if (table == FWTS_MAP_FAILED) {
> + fwts_failed(fw, LOG_LEVEL_MEDIUM,
> + "SMBIOSTableAddressNotMapped",
> + "Cannot mmap SMBIOS tables from "
> + "%8.8" PRIx32 "..%8.8" PRIx32 ".",
> + entry->struct_table_address,
> + entry->struct_table_address + table_length);
> + return FWTS_ERROR;
> + }
> +
> + for (i = 0; i < entry->number_smbios_structures; i++) {
> + if (ptr > table + table_length) {
> + fwts_failed(fw, LOG_LEVEL_MEDIUM,
> + "SMBIOSTableLengthTooSmall",
> + "The size indicated by the SMBIOS table length is "
> + "smaller than the DMI data.");
> + ret = FWTS_ERROR;
> + break;
> + }
> +
> + dmi_entry_type = ptr[0];
> + dmi_entry_length = ptr[1];
> +
> + if (dmi_entry_length < 4) {
> + fwts_failed(fw, LOG_LEVEL_MEDIUM,
> + "SMBIOSIllegalTableEntry",
> + "The size of a DMI entry %" PRIu16 " is illegal, "
> + "DMI data is either wrong or the SMBIOS Table "
> + "Pointer is pointing to the wrong memory region.", i);
> + ret = FWTS_ERROR;
> + break;
> + }
> + ptr += dmi_entry_length;
> +
> + /* Scan for end of DMI entry, must be 2 zero bytes */
> + while (((ptr - table + 1) < table_length) &&
> + ((ptr[0] != 0) || (ptr[1] != 0)))
> + ptr++;
> + /* Skip over the two zero bytes */
> + ptr += 2;
> + }
> +
> + /* We found DMI end of table, are number of entries correct? */
> + if ((dmi_entry_type == 127) && (i != entry->number_smbios_structures)) {
> + fwts_failed(fw, LOG_LEVEL_MEDIUM,
> + "SMBIOSNumberOfStructures",
> + "The end of DMI table marker structure was found "
> + "but only %d DMI structures were found. The SMBIOS "
> + "entry table reported that there were %" PRIu16
> + " DMI structures in the DMI table.",
> + i, entry->number_smbios_structures);
> + /* And table length can't be correct either */
> + ret = FWTS_ERROR;
> + }
> +
> + (void)fwts_munmap(table, (size_t)entry->struct_table_length);
> +
> + return ret;
> +}
> +
> +static int dmicheck_test1(fwts_framework *fw)
> +{
> + void *addr = 0;
> + fwts_smbios_entry entry;
> + fwts_smbios_type type;
> + uint16_t version;
> + uint8_t checksum;
> + static char warning[] =
> + "This field is not checked by the kernel, and so will not affect the system, "
> + "however it should be fixed to conform to the latest version of the "
> + "System Management BIOS (SMBIOS) Reference Specification. ";
> +
> + fwts_log_info(fw,
> + "This test tries to find and sanity check the SMBIOS "
> + "data structures.");
> +
> + if ((addr = fwts_smbios_find_entry(fw, &entry, &type, &version)) == NULL) {
> + fwts_failed(fw, LOG_LEVEL_MEDIUM,
> + "SMBIOSNoEntryPoint",
> + "Could not find SMBIOS Table Entry Point.");
> + return FWTS_OK;
> + }
> +
> + fwts_passed(fw, "Found SMBIOS Table Entry Point at %p", addr);
> + dmi_dump_entry(fw, &entry, type);
> + fwts_log_nl(fw);
> +
> + if (type == FWTS_SMBIOS) {
> + checksum = fwts_checksum((uint8_t *)&entry, sizeof(fwts_smbios_entry));
> + if (checksum != 0)
> + fwts_failed(fw, LOG_LEVEL_HIGH,
> + "SMBIOSBadChecksum",
> + "SMBIOS Table Entry Point Checksum is 0x%2.2x, should be 0x%2.2x",
> + entry.checksum, (uint8_t)(entry.checksum - checksum));
> + else
> + fwts_passed(fw, "SMBIOS Table Entry Point Checksum is valid.");
> +
> + if (entry.length != 0x1f) {
> + fwts_failed(fw, LOG_LEVEL_LOW,
> + "SMBIOSBadEntryLength",
> + "SMBIOS Table Entry Point Length is 0x%2.2x, should be 0x1f", entry.length);
> + fwts_advice(fw, "%s Note that version 2.1 of the specification incorrectly stated that the "
> + "Entry Point Length should be 0x1e when it should be 0x1f.", warning);
> + } else
> + fwts_passed(fw, "SMBIOS Table Entry Point Length is valid.");
> + }
> +
> + if (memcmp(entry.anchor_string, "_DMI_", 5) != 0) {
> + fwts_failed(fw, LOG_LEVEL_HIGH,
> + "SMBIOSBadIntermediateAnchor",
> + "SMBIOS Table Entry Intermediate Anchor String was '%5.5s' and should be '_DMI_'.",
> + entry.anchor_string);
> + fwts_advice(fw, "%s", warning);
> + } else
> + fwts_passed(fw, "SMBIOS Table Entry Intermediate Anchor String _DMI_ is valid.");
> +
> + /* Intermediate checksum for legacy DMI */
> + checksum = fwts_checksum(((uint8_t *)&entry)+16, 15);
> + if (checksum != 0)
> + fwts_failed(fw, LOG_LEVEL_HIGH,
> + "SMBIOSBadChecksum",
> + "SMBIOS Table Entry Point Intermediate Checksum is 0x%2.2x, should be 0x%2.2x",
> + entry.intermediate_checksum,
> + (uint8_t)(entry.intermediate_checksum - checksum));
> + else
> + fwts_passed(fw, "SMBIOS Table Entry Point Intermediate Checksum is valid.");
> +
> + if ((entry.struct_table_length > 0) && (entry.struct_table_address == 0)) {
> + fwts_failed(fw, LOG_LEVEL_HIGH,
> + "SMBIOSBadTableAddress",
> + "SMBIOS Table Entry Structure Table Address is NULL and should be defined.");
> + fwts_advice(fw,
> + "The address of the SMBIOS Structure Tables must be defined if the "
> + "length of these tables is defined.");
> + } else {
> + /*
> + * Now does the DMI table look sane? If not,
> + * the SMBIOS Structure Table could be bad
> + */
> + if (dmi_sane(fw, &entry) == FWTS_OK)
> + fwts_passed(fw, "SMBIOS Table Entry Structure Table Address and Length looks valid.");
> + }
> +
> + return FWTS_OK;
> +}
> +
> +static bool dmi_used_by_kernel(uint8_t type, uint8_t offset)
> +{
> + int i;
> +
> + for (i = 0; dmi_used_by_kernel_table[i].type != TYPE_EOD; i++) {
> + if (dmi_used_by_kernel_table[i].type == type)
> + if ((dmi_used_by_kernel_table[i].offset == FIELD_ANY) ||
> + (dmi_used_by_kernel_table[i].offset == offset))
> + return true;
> + }
> + return false;
> +}
> +
> +static uint16_t dmi_remap_version(fwts_framework *fw, uint16_t old)
> +{
> + int i;
> +
> + for (i = 0; dmi_versions[i].old != 0; i++) {
> + if (old == dmi_versions[i].old) {
> + uint16_t new = dmi_versions[i].new;
> + fwts_warning(fw,
> + "Detected a buggy DMI version number "
> + "%" PRIu16 ".%" PRIu16 "remapping to "
> + "%" PRIu16 ".%" PRIu16,
> + VERSION_MAJOR(old), VERSION_MINOR(old),
> + VERSION_MAJOR(new), VERSION_MINOR(new));
> + return new;
> + }
> + }
> +
> + /* All OK, return original */
> + return old;
> +}
> +
> +static void dmi_out_of_range_advice(fwts_framework *fw, uint8_t type, uint8_t offset)
> +{
> + if (dmi_used_by_kernel(type, offset))
> + fwts_advice(fw,
> + "A value that is out of range is incorrect and not conforming to "
> + "the SMBIOS specification. The Linux kernel extracts and uses "
> + "this particular data item, so it is recommended that this SMBIOS "
> + "configuration is corrected even if the impact on the system "
> + "is most probably not critical.");
> + else
> + fwts_advice(fw,
> + "A value that is out of range is incorrect and not conforming to "
> + "the SMBIOS specification. This field is not currently used by "
> + "the Linux kernel, so this firmware bug shouldn't cause any "
> + "problems.");
> +}
> +
> +static void dmi_min_max_uint8_check(fwts_framework *fw,
> + const char *table,
> + uint32_t addr,
> + const char *field,
> + const fwts_dmi_header *hdr,
> + uint8_t offset,
> + uint8_t min,
> + uint8_t max)
> +{
> + uint8_t val = hdr->data[offset];
> + if ((val < min) || (val > max)) {
> + fwts_failed(fw, LOG_LEVEL_HIGH,
> + DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8
> + " (range allowed 0x%2.2" PRIx8 "..0x%2.2" PRIx8 ") "
> + "while accessing entry '%s' @ 0x%8.8" PRIx32
> + ", field '%s', offset 0x%2.2" PRIx8,
> + val, min, max, table, addr, field, offset);
> + dmi_out_of_range_advice(fw, hdr->type, offset);
> + }
> +}
> +
> +static void dmi_min_max_mask_uint8_check(fwts_framework *fw,
> + const char *table,
> + uint32_t addr,
> + const char *field,
> + const fwts_dmi_header *hdr,
> + uint8_t offset,
> + uint8_t min,
> + uint8_t max,
> + uint8_t shift,
> + uint8_t mask)
> +{
> + uint8_t val = (hdr->data[offset] >> shift) & mask;
> +
> + if ((val < min) || (val > max)) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8
> + " (range allowed 0x%2.2" PRIx8 "..0x%2.2" PRIx8 ") "
> + "while accessing entry '%s' @ 0x%8.8" PRIx32
> + ", field '%s', offset 0x%2.2" PRIx8,
> + val, min, max, table, addr, field, offset);
> + dmi_out_of_range_advice(fw, hdr->type, offset);
> + }
> +}
> +
> +static void dmi_str_check_index(fwts_framework *fw,
> + const char *table,
> + uint32_t addr,
> + const char *field,
> + const fwts_dmi_header *hdr,
> + uint8_t offset,
> + uint8_t index)
> +{
> + char *data = (char *)hdr->data;
> + uint8_t i = index;
> + bool used_by_kernel = dmi_used_by_kernel(hdr->type, offset);
> +
> + if (i > 0) {
> + int j;
> + int failed = -1;
> +
> + data += hdr->length;
> + while (i > 1 && *data) {
> + data += strlen(data) + 1;
> + i--;
> + }
> +
> + /* Sanity checks */
> + if (*data == '\0') {
> + int level = used_by_kernel ? LOG_LEVEL_HIGH : LOG_LEVEL_LOW;
> +
> + /* This entry is clearly broken so flag it as a corrupt entry */
> + fwts_failed(fw, level, DMI_STRING_INDEX_OUT_OF_RANGE,
> + "Out of range string index 0x%2.2" PRIx8
> + " while accessing entry '%s' "
> + "@ 0x%8.8" PRIx32 ", field '%s', offset 0x%2.2" PRIx8,
> + index, table, addr, field, offset);
> + if (used_by_kernel)
> + fwts_advice(fw,
> + "DMI strings are stored in a manner that uses a special "
> + "index to fetch the Nth string from the data. For this "
> + "particular DMI string the index given is not in range "
> + "which means this particular entry is broken. The Linux "
> + "kernel uses this string - hence this string should be "
> + "fixed to ensure sane data is passed to the kernel. "
> + "Note that this probably won't cause any critical system "
> + "errors.");
> + else
> + fwts_advice(fw,
> + "DMI strings are stored in a manner that uses a special "
> + "index to fetch the Nth string from the data. For this "
> + "particular DMI string the index given is not in range "
> + "which means this particular entry is broken. The Linux "
> + "kernel does not use this string, so this error will not "
> + "cause any system errors.");
> + return;
> + }
> +
> + /* Scan for known BIOS defaults that vendors forget to set */
> + for (j=0; dmi_patterns[j].label != NULL; j++) {
> + if (dmi_patterns[j].field &&
> + (strcmp(dmi_patterns[j].field, field) == 0) &&
> + (strcmp(dmi_patterns[j].value, data) == 0)) {
> + failed = j;
> + break;
> + } else if (strcmp(dmi_patterns[j].value, data) == 0) {
> + failed = j;
> + break;
> + }
> + }
> + if (failed != -1) {
> + int level = used_by_kernel ? LOG_LEVEL_MEDIUM : LOG_LEVEL_LOW;
> +
> + fwts_failed(fw, level, dmi_patterns[j].label,
> + "String index 0x%2.2" PRIx8
> + " in table entry '%s' @ 0x%8.8" PRIx32
> + ", field '%s', offset 0x%2.2" PRIx8
> + " has a default value '%s' and probably has "
> + "not been updated by the BIOS vendor.",
> + index, table, addr, field, offset, data);
> +
> + if (used_by_kernel) {
> + fwts_advice(fw,
> + "The DMI table contains data which is clearly been "
> + "left in a default setting and not been configured "
> + "for this machine. "
> + "Somebody has probably forgotten to define this "
> + "field and it basically means this field is effectively "
> + "useless. Note that the kernel uses this field so "
> + "it probably should be corrected to ensure the kernel "
> + "is using sane values.");
> + } else {
> + /* This string is broken, but we don't care about it too much */
> + fwts_advice(fw,
> + "The DMI table contains data which is clearly been "
> + "left in a default setting and not been configured "
> + "for this machine. "
> + "Somebody has probably forgotten to define this "
> + "field and it basically means this field is effectively "
> + "useless, however the kernel does not use this data "
> + "so the issue is fairly low.");
> + }
> + }
> + }
> +}
> +
> +static inline void dmi_str_check(fwts_framework *fw,
> + const char *table,
> + uint32_t addr,
> + const char *field,
> + const fwts_dmi_header *hdr,
> + uint8_t offset)
> +{
> + dmi_str_check_index(fw, table, addr, field, hdr, offset, hdr->data[offset]);
> +}
> +
> +static void dmi_uuid_check(fwts_framework *fw,
> + const char *table,
> + uint32_t addr,
> + const char *field,
> + const fwts_dmi_header *hdr,
> + uint8_t offset)
> +{
> + char guid_str[37];
> + int i;
> +
> + fwts_guid_buf_to_str(hdr->data + offset, guid_str, sizeof(guid_str));
> +
> + for (i = 0; uuid_patterns[i] != NULL; i++) {
> + if (strcmp(guid_str, uuid_patterns[i]) == 0) {
> + fwts_failed(fw, LOG_LEVEL_LOW, DMI_BAD_UUID,
> + "UUID in table entry '%s' @ 0x%8.8" PRIx32
> + " field '%s', offset 0x%2.2" PRIx8
> + " has a default value '%s' and probably has "
> + "not been updated by the BIOS vendor.",
> + table, addr, field, offset, guid_str);
> + fwts_advice(fw,
> + "The DMI table contains a UUID which is clearly been "
> + "left in a default setting and not been configured "
> + "for this machine. While this is not critical it does "
> + "mean that somebody has probably forgotten to define this "
> + "field and it basically means this field is effectively "
> + "useless.");
> + }
> + }
> +}
> +
> +static void dmicheck_entry(fwts_framework *fw,
> + uint32_t addr,
> + const fwts_dmi_header *hdr)
> +{
> + uint8_t *ptr;
> + uint8_t count;
> + uint8_t val;
> + uint8_t *data = hdr->data;
> + char tmp[64];
> + char *table;
> + int i;
> + int len;
> + uint32_t failed_count = fw->minor_tests.failed;
> + int battery_count;
> + int ret;
> + int both_ok;
> + fwts_acpi_table_info *acpi_table;
> + fwts_acpi_table_fadt *fadt;
> + bool advice_given = false;
> +
> + switch (hdr->type) {
> + case 0: /* 7.1 */
> + table = "BIOS Information (Type 0)";
> + if (hdr->length < 0x12)
> + break;
> + dmi_str_check(fw, table, addr, "Vendor", hdr, 0x4);
> + dmi_str_check(fw, table, addr, "BIOS Version", hdr, 0x5);
> + dmi_str_check(fw, table, addr, "Release Date", hdr, 0x8);
> + break;
> +
> + case 1: /* 7.2 */
> + table = "System Information (Type 1)";
> + if (hdr->length < 0x08)
> + break;
> + dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x4);
> + dmi_str_check(fw, table, addr, "Product Name", hdr, 0x5);
> + dmi_str_check(fw, table, addr, "Version", hdr, 0x6);
> + dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x7);
> + if (hdr->length < 0x19)
> + break;
> + dmi_uuid_check(fw, table, addr, "UUID", hdr, 0x8);
> + dmi_min_max_uint8_check(fw, table, addr, "Wakeup Type", hdr, 0x18, 0x0, 0x08);
> + if (hdr->length < 0x1b)
> + break;
> + dmi_str_check(fw, table, addr, "SKU Number", hdr, 0x19);
> + dmi_str_check(fw, table, addr, "Family", hdr, 0x1a);
> + break;
> +
> + case 2: /* 7.3 */
> + table = "Base Board Information (Type 2)";
> + if (hdr->length < 0x08)
> + break;
> + dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x4);
> + dmi_str_check(fw, table, addr, "Product", hdr, 0x5);
> + dmi_str_check(fw, table, addr, "Version", hdr, 0x6);
> + dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x7);
> + if (hdr->length < 0x09)
> + break;
> + dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x8);
> + if (hdr->length < 0x0f)
> + break;
> + dmi_str_check(fw, table, addr, "Location In Chassis", hdr, 0xa);
> + dmi_min_max_uint8_check(fw, table, addr, "Type", hdr, 0xd, 0x1, 0xd);
> + break;
> +
> + case 3: /* 7.4 */
> + table = "Chassis Information (Type 3)";
> + if (hdr->length < 0x09)
> + break;
> + dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x4);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Chassis Type", hdr, 0x5, 0x1, 0x1d, 0x0, 0x7f);
> + if (fwts_acpi_find_table(fw, "FACP", 0, &acpi_table) != FWTS_OK)
> + break;
> + if (acpi_table == NULL)
> + break;
> + fadt = (fwts_acpi_table_fadt *)acpi_table->data;
> +
> + if (fadt->preferred_pm_profile >=
> + (sizeof(fwts_acpi_pm_profile_type) / sizeof(fwts_chassis_type_map))) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_INVALID_HARDWARE_ENTRY,
> + "Incorrect Chassis Type "
> + "ACPI FACP reports 0x%" PRIx8,
> + fadt->preferred_pm_profile);
> + break;
> + }
> + if (data[5] >=
> + (sizeof(fwts_dmi_chassis_type) / sizeof(fwts_chassis_type_map))) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_INVALID_HARDWARE_ENTRY,
> + "Incorrect Chassis Type "
> + "SMBIOS Type 3 reports 0x%" PRIx8,
> + data[5]);
> + fwts_advice(fw,
> + "The Chassis Type in the ACPI FADT is out of range "
> + "and hence we cannot identify the preferred power "
> + "management profile for this machine.");
> + break;
> + }
> +
> + /*
> + * LP: #1021674
> + * We should only check profile and chassis types when we know for sure
> + * that we can compare valid types togther. So it is only valid to do
> + * a check if both aren't CHASSIS_OTHER types
> + */
> + both_ok = (fwts_acpi_pm_profile_type[fadt->preferred_pm_profile].mapped != CHASSIS_OTHER) &
> + (fwts_dmi_chassis_type[data[5]].mapped != CHASSIS_OTHER);
> +
> + if (both_ok &&
> + !(fwts_acpi_pm_profile_type[fadt->preferred_pm_profile].mapped &
> + fwts_dmi_chassis_type[data[5]].mapped)) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_INVALID_HARDWARE_ENTRY,
> + "Unmatched Chassis Type: "
> + "SMBIOS Type 3 reports 0x%" PRIx8 " '%s' "
> + "ACPI FACP reports 0x%" PRIx8 " '%s'",
> + data[5],
> + fwts_dmi_chassis_type[data[5]].name,
> + fadt->preferred_pm_profile,
> + fwts_acpi_pm_profile_type[fadt->preferred_pm_profile].name);
> + /*
> + * Make it a bit more wordy to explain the ramifications
> + */
> + fwts_advice(fw,
> + "The SMBIOS System Enclosure/Chassis type is defined as "
> + "0x%" PRIx8 " '%s' where as the ACPI FACP reports the preferred "
> + "power management profile is 0x%" PRIx8 " '%s' so we possibly "
> + "have conflicting definitions of the machine's PM profile "
> + "for the type of machine. "
> + "See Table 16 of section 4.5.1 of the SMBIOS specification "
> + "and Table 5-34 of section 5.2.9 of the ACPI specification "
> + "for more details. "
> + "This kind of mismatch may lead to incorrect power "
> + "management handling for the type of workload expected "
> + "for this hardware.",
> + data[5],
> + fwts_dmi_chassis_type[data[5]].name,
> + fadt->preferred_pm_profile,
> + fwts_acpi_pm_profile_type[fadt->preferred_pm_profile].name);
> + advice_given = true;
> + }
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Chassis Lock", hdr, 0x5, 0x0, 0x1, 0x7, 0x1);
> + dmi_str_check(fw, table, addr, "Version", hdr, 0x6);
> + dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x7);
> + dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x8);
> + dmi_min_max_uint8_check(fw, table, addr, "Boot-up State", hdr, 0x9, 0x1, 0x6);
> + dmi_min_max_uint8_check(fw, table, addr, "Power Supply State", hdr, 0xa, 0x1, 0x6);
> + dmi_min_max_uint8_check(fw, table, addr, "Thermal State", hdr, 0xb, 0x1, 0x6);
> + dmi_min_max_uint8_check(fw, table, addr, "Security Status", hdr, 0xc, 0x1, 0x5);
> + if (hdr->length < 0x15 + data[0x13] * data[0x14])
> + break;
> + ptr = data + 0x15;
> + len = data[0x14];
> + if (len >= 0x3) {
> + for (i = 0; i < data[0x13]; i++) {
> + val = ptr[i * len] & 0x7f;
> + if (ptr[i * len] & 0x80) {
> + if (val > 0x42)
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8
> + " (range allowed 0x00..0x42) "
> + "while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field "
> + "'SMBIOS Structure Type %d', "
> + "offset 0x%2.2x",
> + val, table, addr, i, 0x15 + (i*len));
> + } else {
> + if ((val < 0x1) || (val > 0xd)) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8
> + " (range allowed 0x00..0x42) "
> + "while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field "
> + "'Base Board Type %d', offset 0x%2.2x",
> + val, table, addr, i, 0x15 + (i*len));
> + }
> + }
> + }
> + }
> + if (hdr->length < 0x16 + data[0x13] * data[0x14])
> + break;
> + dmi_str_check(fw, table, addr, "SKU Number", hdr, 0x15 + data[0x13] * data[0x14]);
> + break;
> +
> + case 4: /* 7.5 */
> + table = "Processor Information (Type 4)";
> + if (hdr->length < 0x1a)
> + break;
> + dmi_str_check(fw, table, addr, "Socket Designation", hdr, 0x4);
> + dmi_min_max_uint8_check(fw, table, addr, "Processor Type", hdr, 0x5, 0x1, 0x6);
> + dmi_str_check(fw, table, addr, "Processor Manufacturer", hdr, 0x7);
> + dmi_str_check(fw, table, addr, "Processor Version", hdr, 0x10);
> + dmi_min_max_uint8_check(fw, table, addr, "Upgrade", hdr, 0x19, 0x1, 0x2a);
> + if (hdr->length < 0x23)
> + break;
> + dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x20);
> + dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x21);
> + dmi_str_check(fw, table, addr, "Part Number", hdr, 0x22);
> + break;
> +
> + case 5: /* 7.6 */
> + table = "Memory Controller Information (Type 5)";
> + if (hdr->length < 0x0f)
> + break;
> + dmi_min_max_uint8_check(fw, table, addr, "Error Detecting Method", hdr, 0x4, 0x1, 0x8);
> + dmi_min_max_uint8_check(fw, table, addr, "Supported Interleave", hdr, 0x6, 0x1, 0x7);
> + dmi_min_max_uint8_check(fw, table, addr, "Current Interleave", hdr, 0x7, 0x1, 0x7);
> + break;
> +
> + case 6: /* 7.7 */
> + table = "Memory Module Information (Type 6)";
> + if (hdr->length < 0x0c)
> + break;
> + dmi_str_check(fw, table, addr, "Socket Designation", hdr, 0x4);
> + break;
> +
> + case 7: /* 7.8 */
> + table = "Cache Information (Type 7)";
> + if (hdr->length < 0x0f)
> + break;
> + dmi_str_check(fw, table, addr, "Socket Designation", hdr, 0x4);
> + if (((GET_UINT16(data + 0x05) >> 5) & 0x0003) == 0x2)
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value %x4.4" PRIx16 " "
> + "bits 5..6 set to illegal value 0x2, only allowed"
> + "0x0, 0x1, 0x3 while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> + GET_UINT16(data + 0x05),
> + table, addr, "Cache Location", 0x5);
> + if (hdr->length < 0x13)
> + break;
> + dmi_min_max_uint8_check(fw, table, addr, "Error Correction Type", hdr, 0x10, 0x1, 0x6);
> + dmi_min_max_uint8_check(fw, table, addr, "System Cache Type", hdr, 0x11, 0x1, 0x5);
> + dmi_min_max_uint8_check(fw, table, addr, "Associativity", hdr, 0x12, 0x1, 0xe);
> + break;
> +
> + case 8: /* 7.9 */
> + table = "Port Connector Information (Type 8)";
> + if (hdr->length < 0x09)
> + break;
> + dmi_str_check(fw, table, addr, "Internal Reference Designator", hdr, 0x4);
> + if (!((data[0x5] <= 0x22) ||
> + (data[0x5] == 0xff) ||
> + ((data[0x5] >= 0xa0) && (data[0x5] <= 0xa4))))
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x00..0x22, 0xa0..0xa4, 0xff) "
> + "while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> + data[0x5], table, addr, "Internal Connector Type", 0x5);
> + dmi_str_check(fw, table, addr, "External Reference Designator", hdr, 0x6);
> + if (!((data[0x7] <= 0x22) ||
> + (data[0x7] == 0xff) ||
> + ((data[0x7] >= 0xa0) && (data[0x7] <= 0xa4))))
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x00..0x22, 0xa0..0xa4, 0xff) "
> + "while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> + data[0x7], table, addr, "Internal Connector Type", 0x7);
> +
> + if (!((data[0x8] <= 0x21) ||
> + (data[0x8] == 0xff) ||
> + ((data[0x8] >= 0xa0) && (data[0x8] <= 0xa1))))
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x00..0x21, 0xa0..0xa1, 0xff) "
> + "while accessing entry '%s' @ 0x%8.8" PRIx32 ", "
> + "field '%s', offset 0x%2.2x",
> + data[0x8], table, addr, "Port Type", 0x8);
> + break;
> +
> + case 9: /* 7.10 */
> + table = "System Slot Information (Type 9)";
> + if (hdr->length < 0x0c)
> + break;
> + dmi_str_check(fw, table, addr, "Slot Designation", hdr, 0x4);
> + if (!(((data[0x5] >= 0x01) && (data[0x5] <= 0x13)) ||
> + ((data[0x5] >= 0xa0) && (data[0x5] <= 0xb6))))
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2x" PRIx8 " "
> + "(range allowed 0x01..0x08, 0xa0..0xa2) "
> + "while accessing entry '%s' @ 0x%8.8" PRIx32 ", "
> + "field '%s', offset 0x%2.2x",
> + data[0x5], table, addr, "Slot Type", 0x5);
> + dmi_min_max_uint8_check(fw, table, addr, "Slot Data Bus Width", hdr, 0x6, 0x1, 0xe);
> + dmi_min_max_uint8_check(fw, table, addr, "Current Usage", hdr, 0x7, 0x1, 0x4);
> + dmi_min_max_uint8_check(fw, table, addr, "Slot Length", hdr, 0x8, 0x1, 0x4);
> + break;
> +
> + case 10: /* 7.11 */
> + table = "On Board Devices (Type 10)";
> + count = (hdr->length - 4) / 2;
> + for (i = 0; i < count; i++) {
> + snprintf(tmp, sizeof(tmp), "Type (Device #%d)", i);
> + dmi_min_max_mask_uint8_check(fw, table, addr, tmp, hdr, 4 + (2 * i), 0x1, 0xa, 0x0, 0x7f);
> + snprintf(tmp, sizeof(tmp), "Description (Device #%d)", i);
> + dmi_str_check(fw, table, addr, tmp, hdr, 5 + (2 * i));
> + }
> + break;
> +
> + case 11: /* 7.12 */
> + table = "OEM Strings (Type 11)";
> + if (hdr->length < 0x5)
> + break;
> + for (i = 1; i <= hdr->data[4]; i++) {
> + snprintf(tmp, sizeof(tmp), "String %d", i);
> + dmi_str_check_index(fw, table, addr, tmp, hdr, 0x4, i);
> + }
> + break;
> +
> + case 12: /* 7.13 */
> + table = "System Configuration Options (Type 12)";
> + if (hdr->length < 0x5)
> + break;
> + for (i = 1; i <= hdr->data[4]; i++) {
> + snprintf(tmp, sizeof(tmp), "Option %d", i);
> + dmi_str_check_index(fw, table, addr, tmp, hdr, 0x4, i);
> + }
> + break;
> +
> + case 13: /* 7.14 */
> + table = "BIOS Language Information (Type 13)";
> + if (hdr->length < 0x16)
> + break;
> + for (i = 1; i <= hdr->data[4]; i++) {
> + snprintf(tmp, sizeof(tmp), "BIOS Language String %d", i);
> + dmi_str_check_index(fw, table, addr, tmp, hdr, 0x4, i);
> + }
> + dmi_str_check(fw, table, addr, "Currently Installed Language", hdr, 0x15);
> + break;
> +
> + case 14: /* 7.15 */
> + table = "Group Associations (Type 14)";
> + if (hdr->length < 0x05)
> + break;
> + dmi_str_check(fw, table, addr, "Name", hdr, 0x4);
> + break;
> +
> + case 15: /* 7.16 */
> + table = "System Event Log (Type 15)";
> + if (hdr->length < 0x14)
> + break;
> + val = hdr->data[0x0a];
> + if (!((val <= 0x04) || (val >= 0x80))) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x00..0x01, "
> + "0x80..0xff) while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> + val, table, addr, "Access Method", 0x0a);
> + }
> + if (hdr->length < 0x17)
> + break;
> + val = hdr->data[0x14];
> + if (!((val <= 0x01) || (val >= 0x80))) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x00..0x01, "
> + "0x80..0xff) while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> + val, table, addr, "Log Header Format", 0x14);
> + }
> + if (hdr->length < 0x17 + data[0x15] * data[0x16])
> + break;
> + if (data[0x16] >= 0x02) {
> + uint8_t *ptr = data + 0x17;
> + int i;
> + for (i = 0; i < data[0x15]; i++) {
> + int j = data[0x16] * i;
> + val = ptr[j];
> + if (!(((val >= 0x01) && (val <= 0x0e)) ||
> + ((val >= 0x10) && (val <= 0x17)) ||
> + (val >= 0x80))) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x01..0x0e, 0x10..0x17, "
> + "0x80..0xff) while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', item %d",
> + val, table, addr, "Log Descriptor Type", i);
> + }
> + val = ptr[j + 1];
> + if ((val > 0x06) && (val < 0x80)) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x00..0x06, 0x80..0xff) "
> + "while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', item %d",
> + val, table, addr, "Log Descriptor Format", i);
> + }
> + }
> + }
> + break;
> +
> + case 16: /* 7.17 */
> + table = "Physical Memory Array (Type 16)";
> + if (hdr->length < 0x0f)
> + break;
> + if (!(((data[0x4] >= 0x01) && (data[0x4] <= 0x0a)) ||
> + ((data[0x4] >= 0xa0) && (data[0x4] <= 0xa3))))
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x01..0x0a, 0xa0..0xa3) "
> + "while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> + data[0x4], table, addr, "Location", 0x4);
> + dmi_min_max_uint8_check(fw, table, addr, "Use", hdr, 0x5, 0x1, 0x7);
> + dmi_min_max_uint8_check(fw, table, addr, "Error Corrrection Type", hdr, 0x6, 0x1, 0x7);
> + break;
> +
> + case 17: /* 7.18 */
> + table = "Memory Device (Type 17)";
> + if (hdr->length < 0x15)
> + break;
> + dmi_min_max_uint8_check(fw, table, addr, "Form Factor", hdr, 0xe, 0x1, 0xf);
> + dmi_str_check(fw, table, addr, "Locator", hdr, 0x10);
> + dmi_str_check(fw, table, addr, "Bank Locator", hdr, 0x11);
> + dmi_min_max_uint8_check(fw, table, addr, "Memory Type", hdr, 0x12, 0x1, 0x19);
> + if (hdr->length < 0x1b)
> + break;
> + dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x17);
> + dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x18);
> + dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x19);
> + dmi_str_check(fw, table, addr, "Part Number", hdr, 0x1a);
> + break;
> +
> + case 18: /* 7.19 */
> + table = "32-bit Memory Error Information (Type 18)";
> + if (hdr->length < 0x17)
> + break;
> + dmi_min_max_uint8_check(fw, table, addr, "Error Type", hdr, 0x4, 0x1, 0xe);
> + dmi_min_max_uint8_check(fw, table, addr, "Error Granularity", hdr, 0x5, 0x1, 0x4);
> + dmi_min_max_uint8_check(fw, table, addr, "Error Operation", hdr, 0x6, 0x1, 0x5);
> + break;
> +
> + case 19: /* 7.20 */
> + table = "Memory Array Mapped Address (Type 19)";
> + if (hdr->length < 0x0F)
> + break;
> + if (hdr->length >= 0x1F && GET_UINT32(data + 0x04) == 0xFFFFFFFF) {
> + uint64_t start, end;
> + start = GET_UINT64(data + 0x0F);
> + end = GET_UINT64(data + 0x17);
> + if (start == end)
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> + "Extended Start and End addresses are identical "
> + "while accessing entry '%s' @ 0x%8.8" PRIx32 ", "
> + "fields 'Extended Starting Address' and 'Extended Ending Address'",
> + table, addr);
> + } else {
> + if (GET_UINT32(data + 0x08) - GET_UINT32(data + 0x04) + 1 == 0)
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> + "Illegal zero mapped address range "
> + "for entry '%s' @ 0x%8.8" PRIx32, table, addr);
> + }
> + break;
> +
> + case 20: /* 7.21 */
> + table = "Memory Device Mapped Address (Type 20)";
> + if (hdr->length < 0x13)
> + break;
> + if (hdr->length >= 0x23 && GET_UINT32(data + 0x04) == 0xFFFFFFFF) {
> + uint64_t start, end;
> + start = GET_UINT64(data + 0x13);
> + end = GET_UINT64(data + 0x1B);
> + if (start == end)
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> + "Extended Start and End addresses are identical "
> + "while accessing entry '%s' @ 0x%8.8" PRIx32 ", "
> + "fields 'Extended Starting Address' and 'Extended Ending Address'",
> + table, addr);
> + } else {
> + if (GET_UINT32(data + 0x08) - GET_UINT32(data + 0x04) + 1 == 0)
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> + "Illegal zero mapped address range "
> + "for entry '%s' @ 0x%8.8" PRIx32, table, addr);
> + }
> + if (data[0x10] == 0)
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_ILLEGAL_MAPPED_ADDR_RANGE,
> + "Illegal row position %2.2" PRIx8 ", "
> + "while accessing entry '%s' @ 0x%8.8" PRIx32
> + ", field '%s', offset 0x%2.2x",
> + data[0x10], table, addr, "Partial Row Position", 0x10);
> + break;
> +
> + case 21: /* 7.22 */
> + table = "Built-in Pointing Device (Type 21)";
> + if (hdr->length < 0x07)
> + break;
> + dmi_min_max_uint8_check(fw, table, addr, "Type", hdr, 0x4, 0x1, 0x9);
> + if (!(((data[0x5] >= 0x01) && (data[0x5] <= 0x08)) ||
> + ((data[0x5] >= 0xa0) && (data[0x5] <= 0xa2)))) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x01..0x08, 0xa0..0xa2) "
> + "while accessing '%s', field '%s', offset 0x%2.2x",
> + data[0x5], table, "Interface", 0x5);
> + }
> + break;
> +
> + case 22: /* 7.23 */
> + table = "Portable Battery (Type 22)";
> + if (hdr->length < 0x10)
> + break;
> + dmi_str_check(fw, table, addr, "Location", hdr, 0x4);
> + dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x5);
> + if (data[0x06] || hdr->length < 0x1A)
> + dmi_str_check(fw, table, addr, "Manufacturer Date", hdr, 0x6);
> + if (data[0x07] || hdr->length < 0x1A)
> + dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x7);
> + dmi_str_check(fw, table, addr, "Device Name", hdr, 0x8);
> + if (data[0x09] != 0x02 || hdr->length < 0x1A)
> + dmi_str_check(fw, table, addr, "Device Chemistry", hdr, 0x9);
> +
> + dmi_str_check(fw, table, addr, "SBDS Version Number", hdr, 0xe);
> + if (hdr->length < 0x1A)
> + break;
> + if (data[0x09] == 0x02)
> + dmi_str_check(fw, table, addr, "SBDS Device Chemistry", hdr, 0x14);
> +
> + ret = fwts_battery_get_count(fw, &battery_count);
> + if (ret != FWTS_OK || battery_count < 1) {
> + fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_INVALID_HARDWARE_ENTRY,
> + "Invalid Hardware Configuration "
> + "(no battery found) ");
> + }
> + break;
> + case 23: /* 7.24 */
> + table = "System Reset (Type 23)";
> + if (hdr->length < 0x0D)
> + break;
> + if (!(data[0x04] & (1 << 5)))
> + break;
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Capabilities (bits 1..2)", hdr, 0x4, 0x1, 0x3, 1, 0x3);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Capabilities (bits 3..4)", hdr, 0x4, 0x1, 0x3, 3, 0x3);
> + break;
> +
> + case 24: /* 7.25 */
> + table = "Hardware Security (Type 24)";
> + /* if (hdr->length < 0x05)
> + break; */
> + break;
> +
> + case 25: /* 7.26 */
> + table = "System Power Controls (Type 25)";
> + /* if (hdr->length < 0x9)
> + break; */
> + break;
> +
> + case 26: /* 7.27 */
> + table = "Voltage Probe (Type 26)";
> + if (hdr->length < 0x14)
> + break;
> + dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Location (bits 0..4)", hdr, 0x5, 0x1, 0xb, 0, 0x1f);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Status (bits 5..7)", hdr, 0x5, 0x1, 0x6, 5, 0x7);
> + break;
> +
> + case 27: /* 7.28 */
> + table = "Cooling Device (Type 27)";
> + if (hdr->length < 0xc)
> + break;
> + val = data[0x06] & 0x1f;
> + if (!(((val >= 0x01) && (val <= 0x09)) ||
> + ((val >= 0x10) && (val <= 0x11))))
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x01..0x09, 0x10..0x11) "
> + "while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', "
> + "offset 0x%2.2x, mask 0x%2.2x",
> + data[0x6], table, addr, "Device Type", 0x6, 0x1f);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Status (bits 5..7)", hdr, 0x6, 0x1, 0x6, 5, 0x7);
> + if (hdr->length < 0x0f)
> + break;
> + dmi_str_check(fw, table, addr, "Description", hdr, 0xe);
> + break;
> +
> + case 28: /* 7.29 */
> + table = "Temperature Probe (Type 28)";
> + if (hdr->length < 0x14)
> + break;
> + dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Location (bits 0..4)", hdr, 0x5, 0x1, 0xf, 0, 0x1f);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Status (bits 5..7)", hdr, 0x5, 0x1, 0x6, 5, 0x7);
> + break;
> +
> + case 29: /* 7.30 */
> + table = "Electrical Current Probe (Type 29)";
> + if (hdr->length < 0x14)
> + break;
> + dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Location (bits 0..4)", hdr, 0x5, 0x1, 0xb, 0, 0x1f);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Status (bits 5..7)", hdr, 0x5, 0x1, 0x6, 5, 0x7);
> + break;
> +
> + case 30: /* 7.31 */
> + table = "Out-of-band Remote Access (Type 30)";
> + if (hdr->length < 0x06)
> + break;
> + dmi_str_check(fw, table, addr, "Manufacturer Name", hdr, 0x4);
> + break;
> +
> + case 31: /* 7.32 */
> + table = "Boot Integrity Services Entry Point (Type 31)";
> + /*
> + if (hdr->length < 0x1c)
> + break;
> + */
> + break;
> +
> + case 32: /* 7.33 */
> + table = "System Boot Information (Type 32)";
> + if (hdr->length < 0xb)
> + break;
> + if ((data[0xa] > 0x8) && (data[0xa] < 128))
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x00..0x08, 0x80..0xff) "
> + "while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> + data[0xa], table, addr, "Boot Status", 0xa);
> + break;
> +
> + case 33: /* 7.34 */
> + table = "64-bit Memory Error Information (Type 33)";
> + if (hdr->length < 0x1f)
> + break;
> + dmi_min_max_uint8_check(fw, table, addr, "Error Type", hdr, 0x4, 0x1, 0xe);
> + dmi_min_max_uint8_check(fw, table, addr, "Error Granularity", hdr, 0x5, 0x1, 0x4);
> + dmi_min_max_uint8_check(fw, table, addr, "Error Operation", hdr, 0x6, 0x1, 0x5);
> + break;
> +
> + case 34: /* 7.35 */
> + table = "Management Device (Type 34)";
> + if (hdr->length < 0x0b)
> + break;
> + dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> + dmi_min_max_uint8_check(fw, table, addr, "Type", hdr, 0x5, 0x1, 0xd);
> + dmi_min_max_uint8_check(fw, table, addr, "Address Type", hdr, 0xa, 0x1, 0x5);
> + break;
> +
> + case 35: /* 7.36 */
> + table = "Management Device Component (Type 35)";
> + if (hdr->length < 0x0b)
> + break;
> + dmi_str_check(fw, table, addr, "Description", hdr, 0x4);
> + break;
> +
> + case 36: /* 7.37 */
> + table = "Management Device Threshold Data (Type 36)";
> + /*
> + if (hdr->length < 0x10)
> + break;
> + */
> + break;
> +
> + case 37: /* 7.38 */
> + table = "Memory Channel (Type 37)";
> + if (hdr->length < 0x07)
> + break;
> + dmi_min_max_uint8_check(fw, table, addr, "Type", hdr, 0x4, 0x1, 0x4);
> + break;
> +
> + case 38: /* 7.39 */
> + table = "IPMI Device Information (Type 38)";
> + dmi_min_max_uint8_check(fw, table, addr, "Interface Type", hdr, 0x4, 0x0, 0x4);
> + break;
> +
> + case 39: /* 7.40 */
> + table = "System Power Supply (Type 39)";
> + if (hdr->length < 0x10)
> + break;
> + dmi_str_check(fw, table, addr, "Location", hdr, 0x5);
> + dmi_str_check(fw, table, addr, "Device Name", hdr, 0x6);
> + dmi_str_check(fw, table, addr, "Manufacturer", hdr, 0x7);
> + dmi_str_check(fw, table, addr, "Serial Number", hdr, 0x8);
> + dmi_str_check(fw, table, addr, "Asset Tag", hdr, 0x9);
> + dmi_str_check(fw, table, addr, "Model Part Number", hdr, 0xa);
> + dmi_str_check(fw, table, addr, "Revision Level", hdr, 0xb);
> + break;
> +
> + case 40: /* 7.41 */
> + table = "Additional Information (Type 40)";
> + break;
> +
> + case 41: /* 7.42 */
> + table = "Onboard Device (Type 41)";
> + if (hdr->length < 0xb)
> + break;
> + dmi_str_check(fw, table, addr, "Reference Designation", hdr, 0x4);
> + dmi_min_max_mask_uint8_check(fw, table, addr, "Device Type", hdr, 0x5, 0x1, 0xa, 0, 0x7f);
> + break;
> +
> + case 42: /* 7.43 */
> + table = "Management Controller Host Interface (Type 42)";
> + if (hdr->length < 0x05)
> + break;
> + if (!((data[0x04] >= 0x02 && data[0x04] <= 0x08) ||
> + (data[0x04] == 0xF0)))
> + fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_MGMT_CTRL_HOST_TYPE,
> + "Out of range value 0x%2.2" PRIx8 " "
> + "(range allowed 0x02..0x08, 0xf0) "
> + "while accessing entry '%s' @ "
> + "0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> + data[0x4], table, addr, "Reference Designation", 0x4);
> + break;
> +
> + case 126: /* 7.44 */
> + table = "Inactive (Type 126)";
> + break;
> + case SMBIOS_END_OF_TABLE: /* 7.45 */
> + table = "End of Table (Type 127)";
> + break;
> + default:
> + snprintf(tmp, sizeof(tmp), "Unknown (Type %" PRId8 ")", hdr->type);
> + table = tmp;
> + break;
> + }
> + if (fw->minor_tests.failed == failed_count)
> + fwts_passed(fw, "Entry @ 0x%8.8" PRIx32 " '%s'", addr, table);
> + else if (!advice_given && hdr->type <= 42)
> + fwts_advice(fw,
> + "It may be worth checking against section 7.%" PRId8 " of the "
> + "System Management BIOS (SMBIOS) Reference Specification "
> + "(see http://www.dmtf.org/standards/smbios).", hdr->type+1);
> +}
> +
> +static int dmi_version_check(fwts_framework *fw, uint16_t version)
> +{
> + if (version > DMI_VERSION) {
> + fwts_warning(fw,
> + "SMBIOS version %" PRIu16 ".%" PRIu16
> + " is not supported by the dmicheck "
> + "test. This test only supports SMBIOS version "
> + "%" PRIu16 ".%" PRIu16 " and lower.",
> + VERSION_MAJOR(version), VERSION_MINOR(version),
> + VERSION_MAJOR(DMI_VERSION), VERSION_MINOR(DMI_VERSION));
> + return FWTS_ERROR;
> + }
> + return FWTS_OK;
> +}
> +
> +static void dmi_scan_tables(fwts_framework *fw,
> + fwts_smbios_entry *entry,
> + uint8_t *table)
> +{
> + uint8_t *entry_data = table;
> + uint16_t table_length;
> + uint16_t struct_count;
> + int i = 0;
> +
> + table_length = entry->struct_table_length;
> + struct_count = entry->number_smbios_structures;
> +
> + while ((i < struct_count) &&
> + (entry_data <= (table + table_length - 4))) {
> + uint32_t addr = entry->struct_table_address + (entry_data - table);
> + fwts_dmi_header hdr;
> + uint8_t *next_entry;
> +
> + hdr.type = entry_data[0];
> + hdr.length = entry_data[1];
> + hdr.handle = GET_UINT16(entry_data + 2);
> + hdr.data = entry_data;
> +
> + /* Sanity check */
> + if (hdr.length < 4) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_INVALID_ENTRY_LENGTH,
> + "Invald header length of entry #%d, "
> + "length was 0x%2.2" PRIx8 ".",
> + i, hdr.length);
> + fwts_advice(fw,
> + "DMI entry header lengths must be 4 or more bytes long "
> + "so this error indicates that the DMI table is unreliable "
> + "and DMI table checking has been aborted at entry #%d.", i);
> + break;
> + }
> +
> + /* Real Physical Address */
> + next_entry = entry_data + hdr.length;
> +
> + /* Look for structure terminator, ends in two zero bytes */
> + while (((next_entry - table + 1) < table_length) &&
> + ((next_entry[0] != 0) || (next_entry[1] != 0))) {
> + next_entry++;
> + }
> +
> + /* Skip over terminating two zero bytes, see section 6.1 of spec */
> + next_entry += 2;
> +
> + if ((next_entry - table) <= table_length)
> + dmicheck_entry(fw, addr, &hdr);
> +
> + i++;
> + entry_data = next_entry;
> + }
> +
> + if (table_length != (entry_data - table))
> + fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_BAD_TABLE_LENGTH,
> + "DMI table length was %" PRId16 " bytes (as specified by "
> + "the SMBIOS header) but the DMI entries used %td bytes.",
> + table_length, entry_data - table);
> +
> + if (struct_count != i)
> + fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_STRUCT_COUNT,
> + "DMI table was DMI %d entries in size (as specified by "
> + "the SMBIOS header) but only %d DMI entries were found.",
> + struct_count, i);
> +}
> +
> +static int dmicheck_test2(fwts_framework *fw)
> +{
> + void *addr;
> + fwts_smbios_entry entry;
> + fwts_smbios_type type;
> + uint16_t version = 0;
> + uint8_t *table;
> +
> + addr = fwts_smbios_find_entry(fw, &entry, &type, &version);
> + if (addr == NULL) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_NO_TABLE_HEADER,
> + "Cannot find SMBIOS or DMI table entry.");
> + return FWTS_ERROR;
> + }
> + if (type == FWTS_SMBIOS_UNKNOWN) {
> + fwts_failed(fw, LOG_LEVEL_HIGH, DMI_NO_TABLE,
> + "Cannot find a valid SMBIOS or DMI table.");
> + return FWTS_ERROR;
> + }
> +
> + /* Fix broken version numbers found in the wild */
> + version = dmi_remap_version(fw, version);
> + if (dmi_version_check(fw, version) != FWTS_OK)
> + return FWTS_SKIP;
> +
> + table = fwts_mmap((off_t)entry.struct_table_address,
> + (size_t)entry.struct_table_length);
> + if (table == FWTS_MAP_FAILED) {
> + fwts_log_error(fw, "Cannot mmap SMBIOS tables from "
> + "%8.8" PRIx32 "..%8.8" PRIx32 ".",
> + entry.struct_table_address,
> + entry.struct_table_address + entry.struct_table_length);
> + return FWTS_ERROR;
> + }
> +
> + dmi_scan_tables(fw, &entry, table);
> +
> + (void)fwts_munmap(table, (size_t)entry.struct_table_length);
> +
> + return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test dmicheck_tests[] = {
> + { dmicheck_test1, "Find and Check SMBIOS Table Entry Point." },
> + { dmicheck_test2, "Test DMI/SMBIOS tables for errors." },
> + { NULL, NULL }
> +};
> +
> +static fwts_framework_ops dmicheck_ops = {
> + .description = "Test DMI/SMBIOS tables for errors.",
> + .minor_tests = dmicheck_tests
> +};
> +
> +FWTS_REGISTER("dmicheck", &dmicheck_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_ROOT_PRIV);
> +
> +#endif
>
Acked-by: Ivan Hu <ivan.hu at canonical.com>
More information about the fwts-devel
mailing list