diff mbox

[04/12] sbbr/dmicheck: Add SMBIOS test cases as per SBBR.

Message ID 1488493596-3437-5-git-send-email-supreeth.venkatesh@arm.com
State Superseded
Headers show

Commit Message

Supreeth Venkatesh March 2, 2017, 10:26 p.m. UTC
Server Base Boot Requirements (SBBR) specification is intended for SBSA-
compliant 64-bit ARMv8 servers.
It defines the base firmware requirements for out-of-box support of any
ARM SBSA-compatible Operating System or hypervisor.
The requirements in this specification are expected to be minimal yet
complete for booting a multi-core ARMv8 server platform, while leaving
plenty of room for OEM or ODM innovations and design details.
For more information, download the SBBR specification here:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044b/index.html

This change introduces test cases as per SBBR specification to SMBIOS
structures. These test cases may be subset/superset of dmicheck tests
already existing. However, to preserve "sbbr" classification, new file
is created, even when most of the code is re-used from dmicheck.

Signed-off-by: Supreeth Venkatesh <supreeth.venkatesh@arm.com>
---
 src/sbbr/dmicheck/dmicheck.c | 1625 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1625 insertions(+)
 create mode 100644 src/sbbr/dmicheck/dmicheck.c

Comments

Colin Ian King March 2, 2017, 11:23 p.m. UTC | #1
I get white space errors when applying this patch:

Applying: sbbr/dmicheck: Add SMBIOS test cases as per SBBR.
.git/rebase-apply/patch:1555: space before tab in indent.
    		(entry_data < (table + table_max_length)) )
.git/rebase-apply/patch:1557: space before tab in indent.
    	hdr = (fwts_dmi_header *)(entry_data);
warning: 2 lines add whitespace errors.

This does seem like a cut-n-paste duplication of a lot of the fwts
smbios checking code. What's different about this that justifies
duplication of code?

On 02/03/17 22:26, Supreeth Venkatesh wrote:
> Server Base Boot Requirements (SBBR) specification is intended for SBSA-
> compliant 64-bit ARMv8 servers.
> It defines the base firmware requirements for out-of-box support of any
> ARM SBSA-compatible Operating System or hypervisor.
> The requirements in this specification are expected to be minimal yet
> complete for booting a multi-core ARMv8 server platform, while leaving
> plenty of room for OEM or ODM innovations and design details.
> For more information, download the SBBR specification here:
> http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044b/index.html
> 
> This change introduces test cases as per SBBR specification to SMBIOS
> structures. These test cases may be subset/superset of dmicheck tests
> already existing. However, to preserve "sbbr" classification, new file
> is created, even when most of the code is re-used from dmicheck.
> 
> Signed-off-by: Supreeth Venkatesh <supreeth.venkatesh@arm.com>
> ---
>  src/sbbr/dmicheck/dmicheck.c | 1625 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 1625 insertions(+)
>  create mode 100644 src/sbbr/dmicheck/dmicheck.c
> 
> diff --git a/src/sbbr/dmicheck/dmicheck.c b/src/sbbr/dmicheck/dmicheck.c
> new file mode 100644
> index 0000000..32728fa
> --- /dev/null
> +++ b/src/sbbr/dmicheck/dmicheck.c
> @@ -0,0 +1,1625 @@
> +/*
> + * Copyright (C) 2010-2017 Canonical
> + * Copyright (C) 2017      ARM Ltd
> + *
> + * 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"
> +
> +#if defined(FWTS_HAS_SBBR)
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <limits.h>
> +#include <fcntl.h>
> +
> +#define DMI_VERSION			(0x0300)
> +#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 DMI_RESERVED_VALUE_USED		"DMIReservedValueUsed"
> +
> +#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;
> +} fwts_chassis_type_map;
> +
> +typedef struct {
> +	uint8_t	type;
> +	uint8_t	offset;
> +} fwts_dmi_used_by_kernel;
> +
> +static bool smbios30_found = false;
> +
> +/*
> + *  Table derived by scanning thousands of DMI table dumps from bug reports
> + */
> +static const fwts_dmi_pattern dmi_patterns[] = {
> +	{ "DMISerialNumber",	"Serial Number",	"0000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"00000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"000000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"0000000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"0x00000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"0x0000000000000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"012345678" },
> +	{ "DMISerialNumber",	"Serial Number",	"0123456789" },
> +	{ "DMISerialNumber",	"Serial Number",	"01234567890" },
> +	{ "DMISerialNumber",	"Serial Number",	"012345678900" },
> +	{ "DMISerialNumber",	"Serial Number",	"0123456789000" },
> +	{ "DMISerialNumber",	"Serial Number",	"System Serial Number" },
> +	{ "DMISerialNumber",	"Serial Number",	"MB-1234567890" },
> +	{ "DMISerialNumber",	"Serial Number",	"NB-1234567890" },
> +	{ "DMISerialNumber",	"Serial Number",	"NB-0123456789" },
> +	{ "DMISerialNumber",	"Serial Number",	"Base Board Serial Number" },
> +	{ "DMISerialNumber",	"Serial Number",	"Chassis Serial Number" },
> +	{ "DMISerialNumber",	"Serial Number",	"<cut out>" },
> +	{ "DMISerialNumber",	"Serial Number",	"Empty" },
> +	{ "DMISerialNumber",	"Serial Number",	"[Empty]" },
> +	{ "DMISerialNumber",	"Serial Number",	"NA" },
> +	{ "DMISerialNumber",	"Serial Number",	"N/A" },
> +	{ "DMISerialNumber",	"Serial Number",	"None" },
> +	{ "DMISerialNumber",	"Serial Number",	"None1" },
> +	{ "DMISerialNumber",	"Serial Number",	"Not Available" },
> +	{ "DMISerialNumber",	"Serial Number",	"Not Specified" },
> +	{ "DMISerialNumber",	"Serial Number",	"NotSupport" },
> +	{ "DMISerialNumber",	"Serial Number",	"Not Supported by CPU" },
> +	{ "DMISerialNumber",	"Serial Number",	"Not Supported" },
> +	{ "DMISerialNumber",	"Serial Number",	"OEM Chassis Serial Number" },
> +	{ "DMISerialNumber",	"Serial Number",	"OEM_Define1" },
> +	{ "DMISerialNumber",	"Serial Number",	"OEM_Define2" },
> +	{ "DMISerialNumber",	"Serial Number",	"OEM_Define3" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum0" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum00" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum01" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum02" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum03" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum1" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum2" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum3" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum4" },
> +	{ "DMISerialNumber",	"Serial Number",	"TBD by ODM" },
> +	{ "DMISerialNumber",	"Serial Number",	"To Be Defined By O.E.M" },
> +	{ "DMISerialNumber",	"Serial Number",	"To be filled by O.E.M." },
> +	{ "DMISerialNumber",	"Serial Number",	"To Be Filled By O.E.M." },
> +	{ "DMISerialNumber",	"Serial Number",	"Unknow" },
> +	{ "DMISerialNumber",	"Serial Number",	"Unknown" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXX" },
> +	{ "DMISerialNumber",	NULL,			"Chassis Serial Number" },
> +	{ "DMIAssetTag",	"Asset Tag",		"0000000000" },
> +	{ "DMIAssetTag",	"Asset Tag",		"0x00000000" },
> +	{ "DMIAssetTag",	"Asset Tag",		"1234567890" },
> +	{ "DMIAssetTag",	"Asset Tag",		"123456789000" },
> +	{ "DMIAssetTag",	"Asset Tag",		"9876543210" },
> +	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum0" },
> +	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum1" },
> +	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum2" },
> +	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum3" },
> +	{ "DMIAssetTag",	"Asset Tag",		"ABCDEFGHIJKLM" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Asset-1234567890" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Asset Tag" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Asset Tag:" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum0" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum1" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum2" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum3" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum4" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Asset tracking" },
> +	{ "DMIAssetTag",	"Asset Tag",		"ATN12345678901234567" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Base Board Asset Tag#" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Base Board Asset Tag" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Chassis Asset Tag" },
> +	{ "DMIAssetTag",	"Asset Tag",		"<cut out>" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Fill By OEM" },
> +	{ "DMIAssetTag",	"Asset Tag",		"N/A" },
> +	{ "DMIAssetTag",	"Asset Tag",		"No Asset Information" },
> +	{ "DMIAssetTag",	"Asset Tag",		"No Asset Tag" },
> +	{ "DMIAssetTag",	"Asset Tag",		"None" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Not Available" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Not Specified" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define0" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define1" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define2" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define3" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define4" },
> +	{ "DMIAssetTag",	"Asset Tag",		"TBD by ODM" },
> +	{ "DMIAssetTag",	"Asset Tag",		"To Be Defined By O.E.M" },
> +	{ "DMIAssetTag",	"Asset Tag",		"To be filled by O.E.M." },
> +	{ "DMIAssetTag",	"Asset Tag",		"To Be Filled By O.E.M." },
> +	{ "DMIAssetTag",	"Asset Tag",		"Unknown" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXX" },
> +	{ "DMIChassisVendor",	NULL,			"Chassis Manufacture" },
> +	{ "DMIChassisVersion",	NULL,			"Chassis Version" },
> +	{ "DMIProductVersion",	NULL,			"System Version" },
> +	{ "DMIBadDefault",	NULL,			"To Be Defined By O.E.M" },
> +	{ "DMIBadDefault",	NULL,			"To be filled by O.E.M." },
> +	{ "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 },
> +	{ "Other",		FWTS_SMBIOS_CHASSIS_OTHER },
> +	{ "Unknown",		FWTS_SMBIOS_CHASSIS_UNKNOWN },
> +	{ "Desktop",		FWTS_SMBIOS_CHASSIS_DESKTOP },
> +	{ "Low Profile Desktop",FWTS_SMBIOS_CHASSIS_LOW_PROFILE_DESKTOP },
> +	{ "Pizza Box",		FWTS_SMBIOS_CHASSIS_PIZZA_BOX },
> +	{ "Mini Tower",		FWTS_SMBIOS_CHASSIS_MINI_TOWER },
> +	{ "Chassis Tower",	FWTS_SMBIOS_CHASSIS_TOWER },
> +	{ "Portable",		FWTS_SMBIOS_CHASSIS_PORTABLE },
> +	{ "Laptop",		FWTS_SMBIOS_CHASSIS_LAPTOP },
> +	{ "Notebook",		FWTS_SMBIOS_CHASSIS_NOTEBOOK },
> +	{ "Handheld",		FWTS_SMBIOS_CHASSIS_HANDHELD },
> +	{ "Docking Station",	FWTS_SMBIOS_CHASSIS_DOCKING_STATION },
> +	{ "All In One",		FWTS_SMBIOS_CHASSIS_ALL_IN_ONE },
> +	{ "Sub Notebook",	FWTS_SMBIOS_CHASSIS_SUB_NOTEBOOK },
> +	{ "Space Saving",	FWTS_SMBIOS_CHASSIS_SPACE_SAVING },
> +	{ "Lunch Box",		FWTS_SMBIOS_CHASSIS_LUNCH_BOX},
> +	{ "Server Chassis",	FWTS_SMBIOS_CHASSIS_MAIN_SERVER_CHASSIS },
> +	{ "Expansion Chassis",	FWTS_SMBIOS_CHASSIS_EXPANISON_CHASSIS },
> +	{ "Sub Chassis",	FWTS_SMBIOS_CHASSIS_SUB_CHASSIS },
> +	{ "Bus Expansion Chassis", FWTS_SMBIOS_CHASSIS_BUS_EXPANSION_CHASSIS },
> +	{ "Peripheral Chassis",	FWTS_SMBIOS_CHASSIS_PERIPHERAL_CHASSIS },
> +	{ "Raid Chassis",	FWTS_SMBIOS_CHASSIS_RAID_CHASSIS },
> +	{ "Rack Mount Chassis",	FWTS_SMBIOS_CHASSIS_RACK_MOUNT_CHASSIS },
> +	{ "Sealed Case PC",	FWTS_SMBIOS_CHASSIS_SEALED_CASE_PC },
> +	{ "Multi System Chassis",FWTS_SMBIOS_CHASSIS_MULTI_SYSTEM_CHASSIS },
> +	{ "Compact PCI",	FWTS_SMBIOS_CHASSIS_COMPACT_PCI },
> +	{ "Advanced TCA",	FWTS_SMBIOS_CHASSIS_ADVANCED_TCA },
> +	{ "Blade",		FWTS_SMBIOS_CHASSIS_BLADE },
> +	{ "Enclosure",		FWTS_SMBIOS_CHASSIS_BLADE_ENCLOSURE },
> +	{ "Tablet",		FWTS_SMBIOS_CHASSIS_TABLET },
> +	{ "Convertible",	FWTS_SMBIOS_CHASSIS_CONVERTIBLE },
> +	{ "Detachable",		FWTS_SMBIOS_CHASSIS_DETACHABLE },
> +};
> +
> +/* 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
> +#define ANCHOR_SIZE 8
> +
> +/*
> + *  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 int dmi_load_file(const char* filename, void *buf, size_t size)
> +{
> +	int fd;
> +	ssize_t ret;
> +
> +	if ((fd = open(filename, O_RDONLY)) < 0)
> +		return FWTS_ERROR;
> +	ret = read(fd, buf, size);
> +	(void)close(fd);
> +	if (ret != (ssize_t)size)
> +		return FWTS_ERROR;
> +	return FWTS_OK;
> +}
> +
> +static void* dmi_table_smbios30(fwts_framework *fw, fwts_smbios30_entry *entry)
> +{
> +	size_t length = (size_t)entry->struct_table_max_size;
> +	void *table;
> +	char anchor[ANCHOR_SIZE];
> +
> +	/* 64 bit entry sanity check on length */
> +	if ((length == 0) || (length > 0xffffff)) {
> +		fwts_log_info(fw, "SMBIOS table size of %zu bytes looks "
> +			"suspicious",  length);
> +		return NULL;
> +	}
> +
> +	if (dmi_load_file("/sys/firmware/dmi/tables/smbios_entry_point", anchor, sizeof("_SM3_")) == FWTS_OK
> +			&& strncmp(anchor, "_SM3_", sizeof("_SM3_")) == 0) {
> +		table = malloc(length);
> +		if (!table)
> +			return NULL;
> +		if (dmi_load_file("/sys/firmware/dmi/tables/DMI", table, length) == FWTS_OK) {
> +			fwts_log_info(fw, "SMBIOS30 table loaded from /sys/firmware/dmi/tables/DMI\n");
> +			return table;
> +		}
> +		free(table);
> +	}
> +
> +	fwts_log_error(fw, "Cannot mmap SMBIOS 3.0 table from %16.16" PRIx64 "..%16.16" PRIx64 ".",
> +			entry->struct_table_address, entry->struct_table_address + entry->struct_table_max_size);
> +	return NULL;
> +}
> +
> +static void dmi_table_free(void* table)
> +{
> +	if (table)
> +		free(table);
> +}
> +
> +
> +static void dmi_dump_entry30(fwts_framework *fw, fwts_smbios30_entry *entry)
> +{
> +
> +	fwts_log_info_verbatim(fw, "SMBIOS30 Entry Point Structure:");
> +	fwts_log_info_verbatim(fw, "  Anchor String          : %5.5s", entry->signature);
> +	fwts_log_info_verbatim(fw, "  Checksum               : 0x%2.2" PRIx8, entry->checksum);
> +	fwts_log_info_verbatim(fw, "  Entry Point Length     : 0x%2.2" PRIx8, entry->length);
> +	fwts_log_info_verbatim(fw, "  Major Version          : 0x%2.2" PRIx8, entry->major_version);
> +	fwts_log_info_verbatim(fw, "  Minor Version          : 0x%2.2" PRIx8, entry->minor_version);
> +	fwts_log_info_verbatim(fw, "  Docrev                 : 0x%2.2" PRIx8, entry->docrev);
> +	fwts_log_info_verbatim(fw, "  Entry Point Revision   : 0x%2.2" PRIx8, entry->revision);
> +	fwts_log_info_verbatim(fw, "  Reserved               : 0x%2.2" PRIx8, entry->reserved);
> +	fwts_log_info_verbatim(fw, "  Table maximum size     : 0x%8.8" PRIx32, entry->struct_table_max_size);
> +	fwts_log_info_verbatim(fw, "  Table address          : 0x%16.16" PRIx64, entry->struct_table_address);
> +
> +}
> +
> +static int dmi_smbios30_sane(fwts_framework *fw, fwts_smbios30_entry *entry)
> +{
> +	uint8_t	*table, *ptr;
> +	uint16_t i = 0;
> +	uint32_t table_length = entry->struct_table_max_size;
> +	int ret = FWTS_OK;
> +
> +	ptr = table = dmi_table_smbios30(fw, entry);
> +	if (table == NULL)
> +		return FWTS_ERROR;
> +
> +	for (;;) {
> +		uint8_t struct_length;
> +		uint8_t struct_type;
> +
> +		if (ptr > table + table_length) {
> +			fwts_failed(fw, LOG_LEVEL_MEDIUM,
> +				"SMBIOS30TableLengthTooSmall",
> +				"The maximum size indicated by the SMBIOS 3.0 table length is "
> +				"smaller than the dmi data or the DMI end of table not found.");
> +			ret = FWTS_ERROR;
> +			break;
> +		}
> +
> +		struct_type = ptr[0];
> +		struct_length = ptr[1];
> +
> +		if (struct_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 += struct_length;
> +
> +		/* Scan for end of DMI entry, must be 2 zero bytes */
> +		while (((ptr - table + 1) < (ssize_t)table_length) &&
> +		       ((ptr[0] != 0) || (ptr[1] != 0)))
> +				ptr++;
> +		/* Skip over the two zero bytes */
> +		ptr += 2;
> +
> +		/* We found DMI end of table and inside the maximum length? */
> +		if (struct_type == 127) {
> +			if (ptr <= table + table_length)
> +				break;
> +			else {
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"SMBIOS30TableLengthTooSmall",
> +					"The end of DMI table marker structure was found "
> +					"but outside the structure table maximum size");
> +				ret = FWTS_ERROR;
> +				break;
> +			}
> +		}
> +	}
> +
> +	dmi_table_free(table);
> +
> +	return ret;
> +}
> +
> +static int smbios30_entry_check(fwts_framework *fw)
> +{
> +	void *addr = 0;
> +
> +	fwts_smbios30_entry entry;
> +	uint16_t version;
> +	uint8_t checksum;
> +
> +	if ((addr = fwts_smbios30_find_entry(fw, &entry, &version)) == NULL)
> +		return FWTS_ERROR;
> +
> +	fwts_passed(fw, "Found SMBIOS30 Table Entry Point at %p", addr);
> +	dmi_dump_entry30(fw, &entry);
> +	fwts_log_nl(fw);
> +
> +	checksum = fwts_checksum((uint8_t *)&entry, sizeof(fwts_smbios30_entry));
> +	if (checksum != 0)
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"SMBIOS30BadChecksum",
> +			"SMBIOS30 Table Entry Point Checksum is 0x%2.2" PRIx8
> +			", should be 0x%2.2" PRIx8,
> +			entry.checksum, (uint8_t)(entry.checksum - checksum));
> +	else
> +		fwts_passed(fw, "SMBIOS30 Table Entry Point Checksum is valid.");
> +
> +	if (entry.length != 0x18) {
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM,
> +			"SMBIOS30BadEntryLength",
> +			"SMBIOS30 Table Entry Point Length is 0x%2.2"  PRIx8
> +			", should be 0x18", entry.length);
> +	} else
> +		fwts_passed(fw, "SMBIOS30 Table Entry Point Length is valid.");
> +
> +	if (entry.reserved)
> +		fwts_failed(fw, LOG_LEVEL_MEDIUM,
> +			"SMBIOSBadReserved",
> +			"SMBIOS30 Table Entry Point Reserved is 0x%2.2" PRIx8
> +			", should be 0", entry.reserved);
> +
> +	if ((entry.revision == 1) && (entry.struct_table_address == 0)) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"SMBIOS30BadTableAddress",
> +			"SMBIOS Table Entry Structure Table Address is NULL and should be defined.");
> +	} else {
> +		/*
> +		 * Now does the SMBIOS 3.0.0 table look sane? If not,
> +		 * the SMBIOS Structure Table could be bad
> +		 */
> +		if (dmi_smbios30_sane(fw, &entry) == FWTS_OK)
> +			fwts_passed(fw, "SMBIOS 3.0 Table Entry Structure Table Address and Length looks valid.");
> +	}
> +
> +	return FWTS_OK;
> +
> +}
> +
> +static int sbbr_dmicheck_revision_test(fwts_framework *fw)
> +{
> +
> +	if (smbios30_entry_check(fw) != FWTS_ERROR) {
> +		smbios30_found = true;
> +	}
> +
> +	if (!smbios30_found) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"SMBIOSNoEntryPoint",
> +			"Could not find any SMBIOS Table Entry Points.");
> +		return FWTS_ERROR;
> +	}
> +
> +	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 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 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;
> +	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 (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]);
> +				break;
> +			}
> +
> +			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 0x01..0x0d) "
> +								"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, 0x30);
> +			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);
> +			if (hdr->length < 0x28)
> +				break;
> +			if (GET_UINT16(data + 0x26) & 0xff00)
> +				fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_RESERVED_VALUE_USED,
> +					"Reserved bits 0x%4.4" PRIx16 " was used"
> +					"bits 8..15 would be reserved while accessing entry '%s' @ "
> +					"0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> +					GET_UINT16(data + 0x26),
> +					table, addr, "Processor Characteristics", 0x26);
> +			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] <= 0x20)) ||
> +			      ((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, 0x6);
> +			if (hdr->length < 0x0d)
> +				break;
> +			if (data[0xc] & 0xf8)
> +				fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_RESERVED_VALUE_USED,
> +					"Reserved bits 0x%2.2" PRIx8 " was used"
> +					"bits 3..7 would be reserved while accessing entry '%s' @ "
> +					"0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
> +					data[0xc],
> +					table, addr, "Slot Characteristics 2", 0xc);
> +			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 *tmpptr = data + 0x17;
> +				int k;
> +				for (k = 0; k < data[0x15]; k++) {
> +					int j = data[0x16] * k;
> +					val = tmpptr[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", k);
> +					}
> +					val = tmpptr[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", k);
> +					}
> +				}
> +			}
> +			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, 0x1e);
> +			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, 0x3);
> +			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 void dmi_scan_smbios30_table(fwts_framework *fw,
> +	fwts_smbios30_entry *entry,
> +	uint8_t  *table)
> +{
> +	uint8_t *entry_data = table;
> +	uint16_t table_max_length;
> +	int i = 0;
> +
> +	table_max_length = entry->struct_table_max_size;
> +
> +	for (i = 0; entry_data <= (table + table_max_length - 4); i++) {
> +		uint64_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;
> +
> +		/* We found DMI end of table */
> +		if (hdr.type == SMBIOS_END_OF_TABLE)
> +			break;
> +
> +		/* 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_max_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_max_length)
> +			dmicheck_entry(fw, addr, &hdr);
> +		else {
> +			fwts_failed(fw, LOG_LEVEL_HIGH, DMI_BAD_TABLE_LENGTH,
> +				"DMI table maximum size was %" PRId32 " bytes (as specified by "
> +				"the SMBIOS 3.0 header) but the DMI entries over the maximum "
> +				"length without finding the End-of-Table(Type 127).",
> +				table_max_length);
> +			break;
> +		}
> +
> +		entry_data = next_entry;
> +	}
> +
> +}
> +
> +static int sbbr_dmicheck_consistency_test(fwts_framework *fw)
> +{
> +	void *addr;
> +	fwts_smbios30_entry entry30;
> +	uint16_t version = 0;
> +	uint8_t  *table;
> +
> +	if (!smbios30_found) {
> +		fwts_skipped(fw, "Cannot find SMBIOS30 table entry, skip the test.");
> +		return FWTS_SKIP;
> +	}
> +
> +	addr = fwts_smbios30_find_entry(fw, &entry30, &version);
> +	if (addr == NULL) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, DMI_NO_TABLE_HEADER,
> +			"Cannot find SMBIOS 3.0 table entry.");
> +		return FWTS_ERROR;
> +	}
> +
> +	table = dmi_table_smbios30(fw, &entry30);
> +	if (table == NULL)
> +		return FWTS_ERROR;
> +
> +	dmi_scan_smbios30_table(fw, &entry30, table);
> +
> +	dmi_table_free(table);
> +
> +	return FWTS_OK;
> +}
> +
> +/*
> + * ARM SBBR SMBIOS Structure Test
> + */
> +
> +/* Test Entry Structure */
> +typedef struct {
> +	const char *name;
> +	const uint8_t type;
> +} sbbr_test_entry;
> +
> +/* Test Definition Array */
> +sbbr_test_entry sbbr_test[] = {
> +	{"BIOS Information", 0},
> +	{"System Information", 1},
> +	{"Baseboard Information", 2},
> +	{"System Enclosure or Chassis", 3},
> +	{"Processor Information", 4},
> +	{"Cache Information", 7},
> +	{"Port Connector Information", 8},
> +	{"System Slots", 9},
> +	{"OEM Strings", 11},
> +	{"BIOS Language Information", 13},
> +	{"System Event Log", 15},
> +	{"Physical Memory Array", 16},
> +	{"Memory Device", 17},
> +	{"Memory Array Mapped Address", 19},
> +	{"System Boot Information", 32},
> +	{"IPMI Device Information", 38},
> +	{"Onboard Devices Extended Information", 41},
> +	{0, 0}
> +};
> +
> +/* Finds SMBIOS structure of a given type in an SMBIOS30 table. */
> +static uint8_t * sbbr_smbios30_locate_structure (uint8_t *table, const int table_max_length, uint8_t type)
> +{
> +	uint8_t *entry_data;
> +	fwts_dmi_header *hdr;
> +
> +
> +	entry_data = table;
> +    while ( (entry_data != NULL) &&
> +    		(entry_data < (table + table_max_length)) )
> +    {
> +    	hdr = (fwts_dmi_header *)(entry_data);
> +
> +		/* We found the entry we're looking for. */
> +		if (hdr->type == type)
> +			return hdr->data;
> +
> +		/* We found DMI end of table */
> +		if (hdr->type == SMBIOS_END_OF_TABLE)
> +			return NULL;
> +
> +		entry_data = entry_data + hdr->length;
> +
> +		while( *((int *)(entry_data)) != 0)
> +		{
> +			entry_data++;
> +
> +		}
> +		entry_data += 2; /* Increment address to start of next structure */
> +
> +	}
> +
> +
> +	return NULL;
> +}
> +
> +/* SBBR SMBIOS structure test function. */
> +static int sbbr_smbios30_test(fwts_framework *fw)
> +{
> +	fwts_smbios30_entry entry30;
> +	fwts_dmi_header hdr;
> +	uint16_t version;
> +	void *addr;
> +	uint32_t i;
> +	uint8_t *table;
> +
> +	/* Finding SMBIOS30 entry point. */
> +	addr = fwts_smbios30_find_entry(fw, &entry30, &version);
> +	if (addr == NULL) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH, "SbbrSmbiosNoTable", "Cannot find SMBIOS30 table "
> +		            "entry.");
> +		return FWTS_ERROR;
> +	}
> +
> +	/* Getting SMBIOS table contents. */
> +	table = dmi_table_smbios30(fw, &entry30);
> +	if (table == NULL)
> +		return FWTS_ERROR;
> +
> +	/* Searching for each SMBIOS structure needed by SBBR. */
> +	for (i = 0; sbbr_test[i].name != NULL; i++) {
> +
> +		hdr.data = sbbr_smbios30_locate_structure (table, entry30.struct_table_max_size, sbbr_test[i].type);
> +
> +		if (hdr.data != NULL)
> +			dmicheck_entry(fw, (uintptr_t)hdr.data, &hdr);
> +		else
> +			fwts_failed(fw, LOG_LEVEL_HIGH, "SbbrSmbiosNoStruct", "Cannot find SMBIOS "
> +			            "structure: %s (Type %d)", sbbr_test[i].name, sbbr_test[i].type);
> +	}
> +
> +	dmi_table_free(table);
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test sbbr_dmicheck_tests[] = {
> +	{ sbbr_dmicheck_revision_test, "Find and test SMBIOS Table Entry Points." },
> +	{ sbbr_dmicheck_consistency_test, "Test DMI/SMBIOS3 tables for errors." },
> +	{ sbbr_smbios30_test, "Test ARM SBBR SMBIOS structure requirements."},
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops sbbr_dmicheck_ops = {
> +	.description = "DMI/SMBIOS table tests.",
> +	.minor_tests = sbbr_dmicheck_tests
> +};
> +
> +FWTS_REGISTER("sbbr_dmicheck", &sbbr_dmicheck_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_TEST_SBBR)
> +
> +#endif
>
Supreeth Venkatesh March 2, 2017, 11:39 p.m. UTC | #2
On Thu, 2017-03-02 at 23:23 +0000, Colin Ian King wrote:
> I get white space errors when applying this patch:
> 
> Applying: sbbr/dmicheck: Add SMBIOS test cases as per SBBR.
> .git/rebase-apply/patch:1555: space before tab in indent.
>     		(entry_data < (table + table_max_length)) )
> .git/rebase-apply/patch:1557: space before tab in indent.
>     	hdr = (fwts_dmi_header *)(entry_data);
> warning: 2 lines add whitespace errors.
> 
Will check and re-submit next version of the patch.

> This does seem like a cut-n-paste duplication of a lot of the fwts
> smbios checking code. What's different about this that justifies
> duplication of code?
> 
SBBR Starts with SMBIOS 3.0 and no need for SMBIOS 1.0 APIs.
So those are removed and In addition, there are minor changes in
pass/fail criteria. 

> On 02/03/17 22:26, Supreeth Venkatesh wrote:
> > 
> > Server Base Boot Requirements (SBBR) specification is intended for
> > SBSA-
> > compliant 64-bit ARMv8 servers.
> > It defines the base firmware requirements for out-of-box support of
> > any
> > ARM SBSA-compatible Operating System or hypervisor.
> > The requirements in this specification are expected to be minimal
> > yet
> > complete for booting a multi-core ARMv8 server platform, while
> > leaving
> > plenty of room for OEM or ODM innovations and design details.
> > For more information, download the SBBR specification here:
> > http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044
> > b/index.html
> > 
> > This change introduces test cases as per SBBR specification to
> > SMBIOS
> > structures. These test cases may be subset/superset of dmicheck
> > tests
> > already existing. However, to preserve "sbbr" classification, new
> > file
> > is created, even when most of the code is re-used from dmicheck.
> > 
> > Signed-off-by: Supreeth Venkatesh <supreeth.venkatesh@arm.com>
> > ---
> >  src/sbbr/dmicheck/dmicheck.c | 1625
> > ++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 1625 insertions(+)
> >  create mode 100644 src/sbbr/dmicheck/dmicheck.c
> > 
> > diff --git a/src/sbbr/dmicheck/dmicheck.c
> > b/src/sbbr/dmicheck/dmicheck.c
> > new file mode 100644
> > index 0000000..32728fa
> > --- /dev/null
> > +++ b/src/sbbr/dmicheck/dmicheck.c
> > @@ -0,0 +1,1625 @@
> > +/*
> > + * Copyright (C) 2010-2017 Canonical
> > + * Copyright (C) 2017      ARM Ltd
> > + *
> > + * 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"
> > +
> > +#if defined(FWTS_HAS_SBBR)
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <unistd.h>
> > +#include <limits.h>
> > +#include <fcntl.h>
> > +
> > +#define DMI_VERSION			(0x0300)
> > +#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	"DMIStringIndexOutOfR
> > ange"
> > +#define DMI_ILLEGAL_MAPPED_ADDR_RANGE	"DMIIllegalMappedAddr
> > Range"
> > +#define DMI_MGMT_CTRL_HOST_TYPE		"DMIMgmtCtrlHostTyp
> > e"
> > +#define DMI_INVALID_ENTRY_LENGTH	"DMIInvalidEntryLength"
> > +#define DMI_INVALID_HARDWARE_ENTRY	"DMIInvalidHardwareEntry
> > "
> > +#define DMI_RESERVED_VALUE_USED		"DMIReservedValueUs
> > ed"
> > +
> > +#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;
> > +} fwts_chassis_type_map;
> > +
> > +typedef struct {
> > +	uint8_t	type;
> > +	uint8_t	offset;
> > +} fwts_dmi_used_by_kernel;
> > +
> > +static bool smbios30_found = false;
> > +
> > +/*
> > + *  Table derived by scanning thousands of DMI table dumps from
> > bug reports
> > + */
> > +static const fwts_dmi_pattern dmi_patterns[] = {
> > +	{ "DMISerialNumber",	"Serial Number",	"00000
> > 00" },
> > +	{ "DMISerialNumber",	"Serial Number",	"00000
> > 000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"00000
> > 0000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"00000
> > 00000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"0x000
> > 00000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"0x000
> > 0000000000000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 5678" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 56789" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 567890" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 5678900" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 56789000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Syste
> > m Serial Number" },
> > +	{ "DMISerialNumber",	"Serial Number",	"MB-
> > 1234567890" },
> > +	{ "DMISerialNumber",	"Serial Number",	"NB-
> > 1234567890" },
> > +	{ "DMISerialNumber",	"Serial Number",	"NB-
> > 0123456789" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Base
> > Board Serial Number" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Chass
> > is Serial Number" },
> > +	{ "DMISerialNumber",	"Serial Number",	"<cut
> > out>" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Empty
> > " },
> > +	{ "DMISerialNumber",	"Serial Number",	"[Empt
> > y]" },
> > +	{ "DMISerialNumber",	"Serial Number",	"NA"
> > },
> > +	{ "DMISerialNumber",	"Serial Number",	"N/A"
> > },
> > +	{ "DMISerialNumber",	"Serial Number",	"None"
> > },
> > +	{ "DMISerialNumber",	"Serial Number",	"None1
> > " },
> > +	{ "DMISerialNumber",	"Serial Number",	"Not
> > Available" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Not
> > Specified" },
> > +	{ "DMISerialNumber",	"Serial Number",	"NotSu
> > pport" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Not
> > Supported by CPU" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Not
> > Supported" },
> > +	{ "DMISerialNumber",	"Serial Number",	"OEM
> > Chassis Serial Number" },
> > +	{ "DMISerialNumber",	"Serial Number",	"OEM_D
> > efine1" },
> > +	{ "DMISerialNumber",	"Serial Number",	"OEM_D
> > efine2" },
> > +	{ "DMISerialNumber",	"Serial Number",	"OEM_D
> > efine3" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m0" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m00" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m01" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m02" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m03" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m1" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m2" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m3" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m4" },
> > +	{ "DMISerialNumber",	"Serial Number",	"TBD
> > by ODM" },
> > +	{ "DMISerialNumber",	"Serial Number",	"To Be
> > Defined By O.E.M" },
> > +	{ "DMISerialNumber",	"Serial Number",	"To be
> > filled by O.E.M." },
> > +	{ "DMISerialNumber",	"Serial Number",	"To Be
> > Filled By O.E.M." },
> > +	{ "DMISerialNumber",	"Serial Number",	"Unkno
> > w" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Unkno
> > wn" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXXXXXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXXXXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXXXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > X" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > " },
> > +	{ "DMISerialNumber",	NULL,			"
> > Chassis Serial Number" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"00000
> > 00000" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"0x000
> > 00000" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"12345
> > 67890" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"12345
> > 6789000" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"98765
> > 43210" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"A1_As
> > setTagNum0" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"A1_As
> > setTagNum1" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"A1_As
> > setTagNum2" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"A1_As
> > setTagNum3" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"ABCDE
> > FGHIJKLM" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > -1234567890" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > Tag" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > Tag:" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum0" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum1" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum2" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum3" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum4" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > tracking" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"ATN12
> > 345678901234567" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Base
> > Board Asset Tag#" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Base
> > Board Asset Tag" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Chass
> > is Asset Tag" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"<cut
> > out>" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Fill
> > By OEM" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"N/A"
> > },
> > +	{ "DMIAssetTag",	"Asset Tag",		"No
> > Asset Information" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"No
> > Asset Tag" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"None"
> > },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Not
> > Available" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Not
> > Specified" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine0" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine1" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine2" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine3" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine4" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"TBD
> > by ODM" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"To Be
> > Defined By O.E.M" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"To be
> > filled by O.E.M." },
> > +	{ "DMIAssetTag",	"Asset Tag",		"To Be
> > Filled By O.E.M." },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Unkno
> > wn" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXXXXXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXXXXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXXXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > X" },
> > +	{ "DMIChassisVendor",	NULL,			
> > "Chassis Manufacture" },
> > +	{ "DMIChassisVersion",	NULL,			
> > "Chassis Version" },
> > +	{ "DMIProductVersion",	NULL,			
> > "System Version" },
> > +	{ "DMIBadDefault",	NULL,			"To
> > Be Defined By O.E.M" },
> > +	{ "DMIBadDefault",	NULL,			"To
> > be filled by O.E.M." },
> > +	{ "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 },
> > +	{ "Other",		FWTS_SMBIOS_CHASSIS_OTHER },
> > +	{ "Unknown",		FWTS_SMBIOS_CHASSIS_UNKNOWN },
> > +	{ "Desktop",		FWTS_SMBIOS_CHASSIS_DESKTOP },
> > +	{ "Low Profile
> > Desktop",FWTS_SMBIOS_CHASSIS_LOW_PROFILE_DESKTOP },
> > +	{ "Pizza Box",		FWTS_SMBIOS_CHASSIS_PIZZA_BO
> > X },
> > +	{ "Mini Tower",		FWTS_SMBIOS_CHASSIS_MINI_TO
> > WER },
> > +	{ "Chassis Tower",	FWTS_SMBIOS_CHASSIS_TOWER },
> > +	{ "Portable",		FWTS_SMBIOS_CHASSIS_PORTABLE
> > },
> > +	{ "Laptop",		FWTS_SMBIOS_CHASSIS_LAPTOP },
> > +	{ "Notebook",		FWTS_SMBIOS_CHASSIS_NOTEBOOK
> > },
> > +	{ "Handheld",		FWTS_SMBIOS_CHASSIS_HANDHELD
> > },
> > +	{ "Docking Station",	FWTS_SMBIOS_CHASSIS_DOCKING_ST
> > ATION },
> > +	{ "All In One",		FWTS_SMBIOS_CHASSIS_ALL_IN_
> > ONE },
> > +	{ "Sub Notebook",	FWTS_SMBIOS_CHASSIS_SUB_NOTEBOOK
> > },
> > +	{ "Space Saving",	FWTS_SMBIOS_CHASSIS_SPACE_SAVING
> > },
> > +	{ "Lunch Box",		FWTS_SMBIOS_CHASSIS_LUNCH_BO
> > X},
> > +	{ "Server Chassis",	FWTS_SMBIOS_CHASSIS_MAIN_SERVER
> > _CHASSIS },
> > +	{ "Expansion Chassis",	FWTS_SMBIOS_CHASSIS_EXPANISO
> > N_CHASSIS },
> > +	{ "Sub Chassis",	FWTS_SMBIOS_CHASSIS_SUB_CHASSIS },
> > +	{ "Bus Expansion Chassis",
> > FWTS_SMBIOS_CHASSIS_BUS_EXPANSION_CHASSIS },
> > +	{ "Peripheral Chassis",	FWTS_SMBIOS_CHASSIS_PERIPHE
> > RAL_CHASSIS },
> > +	{ "Raid Chassis",	FWTS_SMBIOS_CHASSIS_RAID_CHASSIS
> > },
> > +	{ "Rack Mount Chassis",	FWTS_SMBIOS_CHASSIS_RACK_MO
> > UNT_CHASSIS },
> > +	{ "Sealed Case PC",	FWTS_SMBIOS_CHASSIS_SEALED_CASE
> > _PC },
> > +	{ "Multi System
> > Chassis",FWTS_SMBIOS_CHASSIS_MULTI_SYSTEM_CHASSIS },
> > +	{ "Compact PCI",	FWTS_SMBIOS_CHASSIS_COMPACT_PCI },
> > +	{ "Advanced TCA",	FWTS_SMBIOS_CHASSIS_ADVANCED_TCA
> > },
> > +	{ "Blade",		FWTS_SMBIOS_CHASSIS_BLADE },
> > +	{ "Enclosure",		FWTS_SMBIOS_CHASSIS_BLADE_EN
> > CLOSURE },
> > +	{ "Tablet",		FWTS_SMBIOS_CHASSIS_TABLET },
> > +	{ "Convertible",	FWTS_SMBIOS_CHASSIS_CONVERTIBLE },
> > +	{ "Detachable",		FWTS_SMBIOS_CHASSIS_DETACHA
> > BLE },
> > +};
> > +
> > +/* 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
> > +#define ANCHOR_SIZE 8
> > +
> > +/*
> > + *  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 int dmi_load_file(const char* filename, void *buf, size_t
> > size)
> > +{
> > +	int fd;
> > +	ssize_t ret;
> > +
> > +	if ((fd = open(filename, O_RDONLY)) < 0)
> > +		return FWTS_ERROR;
> > +	ret = read(fd, buf, size);
> > +	(void)close(fd);
> > +	if (ret != (ssize_t)size)
> > +		return FWTS_ERROR;
> > +	return FWTS_OK;
> > +}
> > +
> > +static void* dmi_table_smbios30(fwts_framework *fw,
> > fwts_smbios30_entry *entry)
> > +{
> > +	size_t length = (size_t)entry->struct_table_max_size;
> > +	void *table;
> > +	char anchor[ANCHOR_SIZE];
> > +
> > +	/* 64 bit entry sanity check on length */
> > +	if ((length == 0) || (length > 0xffffff)) {
> > +		fwts_log_info(fw, "SMBIOS table size of %zu bytes
> > looks "
> > +			"suspicious",  length);
> > +		return NULL;
> > +	}
> > +
> > +	if
> > (dmi_load_file("/sys/firmware/dmi/tables/smbios_entry_point",
> > anchor, sizeof("_SM3_")) == FWTS_OK
> > +			&& strncmp(anchor, "_SM3_",
> > sizeof("_SM3_")) == 0) {
> > +		table = malloc(length);
> > +		if (!table)
> > +			return NULL;
> > +		if (dmi_load_file("/sys/firmware/dmi/tables/DMI",
> > table, length) == FWTS_OK) {
> > +			fwts_log_info(fw, "SMBIOS30 table loaded
> > from /sys/firmware/dmi/tables/DMI\n");
> > +			return table;
> > +		}
> > +		free(table);
> > +	}
> > +
> > +	fwts_log_error(fw, "Cannot mmap SMBIOS 3.0 table from
> > %16.16" PRIx64 "..%16.16" PRIx64 ".",
> > +			entry->struct_table_address, entry-
> > >struct_table_address + entry->struct_table_max_size);
> > +	return NULL;
> > +}
> > +
> > +static void dmi_table_free(void* table)
> > +{
> > +	if (table)
> > +		free(table);
> > +}
> > +
> > +
> > +static void dmi_dump_entry30(fwts_framework *fw,
> > fwts_smbios30_entry *entry)
> > +{
> > +
> > +	fwts_log_info_verbatim(fw, "SMBIOS30 Entry Point
> > Structure:");
> > +	fwts_log_info_verbatim(fw, "  Anchor String          :
> > %5.5s", entry->signature);
> > +	fwts_log_info_verbatim(fw, "  Checksum               :
> > 0x%2.2" PRIx8, entry->checksum);
> > +	fwts_log_info_verbatim(fw, "  Entry Point Length     :
> > 0x%2.2" PRIx8, entry->length);
> > +	fwts_log_info_verbatim(fw, "  Major Version          :
> > 0x%2.2" PRIx8, entry->major_version);
> > +	fwts_log_info_verbatim(fw, "  Minor Version          :
> > 0x%2.2" PRIx8, entry->minor_version);
> > +	fwts_log_info_verbatim(fw, "  Docrev                 :
> > 0x%2.2" PRIx8, entry->docrev);
> > +	fwts_log_info_verbatim(fw, "  Entry Point Revision   :
> > 0x%2.2" PRIx8, entry->revision);
> > +	fwts_log_info_verbatim(fw, "  Reserved               :
> > 0x%2.2" PRIx8, entry->reserved);
> > +	fwts_log_info_verbatim(fw, "  Table maximum size     :
> > 0x%8.8" PRIx32, entry->struct_table_max_size);
> > +	fwts_log_info_verbatim(fw, "  Table address          :
> > 0x%16.16" PRIx64, entry->struct_table_address);
> > +
> > +}
> > +
> > +static int dmi_smbios30_sane(fwts_framework *fw,
> > fwts_smbios30_entry *entry)
> > +{
> > +	uint8_t	*table, *ptr;
> > +	uint16_t i = 0;
> > +	uint32_t table_length = entry->struct_table_max_size;
> > +	int ret = FWTS_OK;
> > +
> > +	ptr = table = dmi_table_smbios30(fw, entry);
> > +	if (table == NULL)
> > +		return FWTS_ERROR;
> > +
> > +	for (;;) {
> > +		uint8_t struct_length;
> > +		uint8_t struct_type;
> > +
> > +		if (ptr > table + table_length) {
> > +			fwts_failed(fw, LOG_LEVEL_MEDIUM,
> > +				"SMBIOS30TableLengthTooSmall",
> > +				"The maximum size indicated by the
> > SMBIOS 3.0 table length is "
> > +				"smaller than the dmi data or the
> > DMI end of table not found.");
> > +			ret = FWTS_ERROR;
> > +			break;
> > +		}
> > +
> > +		struct_type = ptr[0];
> > +		struct_length = ptr[1];
> > +
> > +		if (struct_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 += struct_length;
> > +
> > +		/* Scan for end of DMI entry, must be 2 zero bytes
> > */
> > +		while (((ptr - table + 1) < (ssize_t)table_length)
> > &&
> > +		       ((ptr[0] != 0) || (ptr[1] != 0)))
> > +				ptr++;
> > +		/* Skip over the two zero bytes */
> > +		ptr += 2;
> > +
> > +		/* We found DMI end of table and inside the
> > maximum length? */
> > +		if (struct_type == 127) {
> > +			if (ptr <= table + table_length)
> > +				break;
> > +			else {
> > +				fwts_failed(fw, LOG_LEVEL_HIGH,
> > +					"SMBIOS30TableLengthTooSma
> > ll",
> > +					"The end of DMI table
> > marker structure was found "
> > +					"but outside the structure
> > table maximum size");
> > +				ret = FWTS_ERROR;
> > +				break;
> > +			}
> > +		}
> > +	}
> > +
> > +	dmi_table_free(table);
> > +
> > +	return ret;
> > +}
> > +
> > +static int smbios30_entry_check(fwts_framework *fw)
> > +{
> > +	void *addr = 0;
> > +
> > +	fwts_smbios30_entry entry;
> > +	uint16_t version;
> > +	uint8_t checksum;
> > +
> > +	if ((addr = fwts_smbios30_find_entry(fw, &entry,
> > &version)) == NULL)
> > +		return FWTS_ERROR;
> > +
> > +	fwts_passed(fw, "Found SMBIOS30 Table Entry Point at %p",
> > addr);
> > +	dmi_dump_entry30(fw, &entry);
> > +	fwts_log_nl(fw);
> > +
> > +	checksum = fwts_checksum((uint8_t *)&entry,
> > sizeof(fwts_smbios30_entry));
> > +	if (checksum != 0)
> > +		fwts_failed(fw, LOG_LEVEL_HIGH,
> > +			"SMBIOS30BadChecksum",
> > +			"SMBIOS30 Table Entry Point Checksum is
> > 0x%2.2" PRIx8
> > +			", should be 0x%2.2" PRIx8,
> > +			entry.checksum, (uint8_t)(entry.checksum -
> > checksum));
> > +	else
> > +		fwts_passed(fw, "SMBIOS30 Table Entry Point
> > Checksum is valid.");
> > +
> > +	if (entry.length != 0x18) {
> > +		fwts_failed(fw, LOG_LEVEL_MEDIUM,
> > +			"SMBIOS30BadEntryLength",
> > +			"SMBIOS30 Table Entry Point Length is
> > 0x%2.2"  PRIx8
> > +			", should be 0x18", entry.length);
> > +	} else
> > +		fwts_passed(fw, "SMBIOS30 Table Entry Point Length
> > is valid.");
> > +
> > +	if (entry.reserved)
> > +		fwts_failed(fw, LOG_LEVEL_MEDIUM,
> > +			"SMBIOSBadReserved",
> > +			"SMBIOS30 Table Entry Point Reserved is
> > 0x%2.2" PRIx8
> > +			", should be 0", entry.reserved);
> > +
> > +	if ((entry.revision == 1) && (entry.struct_table_address
> > == 0)) {
> > +		fwts_failed(fw, LOG_LEVEL_HIGH,
> > +			"SMBIOS30BadTableAddress",
> > +			"SMBIOS Table Entry Structure Table
> > Address is NULL and should be defined.");
> > +	} else {
> > +		/*
> > +		 * Now does the SMBIOS 3.0.0 table look sane? If
> > not,
> > +		 * the SMBIOS Structure Table could be bad
> > +		 */
> > +		if (dmi_smbios30_sane(fw, &entry) == FWTS_OK)
> > +			fwts_passed(fw, "SMBIOS 3.0 Table Entry
> > Structure Table Address and Length looks valid.");
> > +	}
> > +
> > +	return FWTS_OK;
> > +
> > +}
> > +
> > +static int sbbr_dmicheck_revision_test(fwts_framework *fw)
> > +{
> > +
> > +	if (smbios30_entry_check(fw) != FWTS_ERROR) {
> > +		smbios30_found = true;
> > +	}
> > +
> > +	if (!smbios30_found) {
> > +		fwts_failed(fw, LOG_LEVEL_HIGH,
> > +			"SMBIOSNoEntryPoint",
> > +			"Could not find any SMBIOS Table Entry
> > Points.");
> > +		return FWTS_ERROR;
> > +	}
> > +
> > +	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 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 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;
> > +	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 (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]);
> > +				break;
> > +			}
> > +
> > +			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_faile
> > d(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> > +								"O
> > ut of range value 0x%2.2" PRIx8
> > +								"
> > (range allowed 0x00..0x42) "
> > +								"w
> > hile accessing entry '%s' @ "
> > +								"0
> > x%8.8" PRIx32 ", field "
> > +								"'
> > SMBIOS Structure Type %d', "
> > +								"o
> > ffset 0x%2.2x",
> > +								va
> > l, table, addr, i, 0x15 + (i*len));
> > +					} else {
> > +						if ((val < 0x1) ||
> > (val > 0xd)) {
> > +							fwts_faile
> > d(fw, LOG_LEVEL_HIGH, DMI_VALUE_OUT_OF_RANGE,
> > +								"O
> > ut of range value 0x%2.2" PRIx8
> > +								"
> > (range allowed 0x01..0x0d) "
> > +								"w
> > hile accessing entry '%s' @ "
> > +								"0
> > x%8.8" PRIx32 ", field "
> > +								"'
> > Base Board Type %d', offset 0x%2.2x",
> > +								va
> > l, 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, 0x30);
> > +			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);
> > +			if (hdr->length < 0x28)
> > +				break;
> > +			if (GET_UINT16(data + 0x26) & 0xff00)
> > +				fwts_failed(fw, LOG_LEVEL_MEDIUM,
> > DMI_RESERVED_VALUE_USED,
> > +					"Reserved bits 0x%4.4"
> > PRIx16 " was used"
> > +					"bits 8..15 would be
> > reserved while accessing entry '%s' @ "
> > +					"0x%8.8" PRIx32 ", field
> > '%s', offset 0x%2.2x",
> > +					GET_UINT16(data + 0x26),
> > +					table, addr, "Processor
> > Characteristics", 0x26);
> > +			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]
> > <= 0x20)) ||
> > +			      ((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, 0x6);
> > +			if (hdr->length < 0x0d)
> > +				break;
> > +			if (data[0xc] & 0xf8)
> > +				fwts_failed(fw, LOG_LEVEL_MEDIUM,
> > DMI_RESERVED_VALUE_USED,
> > +					"Reserved bits 0x%2.2"
> > PRIx8 " was used"
> > +					"bits 3..7 would be
> > reserved while accessing entry '%s' @ "
> > +					"0x%8.8" PRIx32 ", field
> > '%s', offset 0x%2.2x",
> > +					data[0xc],
> > +					table, addr, "Slot
> > Characteristics 2", 0xc);
> > +			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 *tmpptr = data + 0x17;
> > +				int k;
> > +				for (k = 0; k < data[0x15]; k++) {
> > +					int j = data[0x16] * k;
> > +					val = tmpptr[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..0xf
> > f) while accessing entry '%s' @ "
> > +							"0x%8.8"
> > PRIx32 ", field '%s', item %d",
> > +							val,
> > table, addr, "Log Descriptor Type", k);
> > +					}
> > +					val = tmpptr[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", k);
> > +					}
> > +				}
> > +			}
> > +			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, 0x1e);
> > +			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, 0x3);
> > +			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 void dmi_scan_smbios30_table(fwts_framework *fw,
> > +	fwts_smbios30_entry *entry,
> > +	uint8_t  *table)
> > +{
> > +	uint8_t *entry_data = table;
> > +	uint16_t table_max_length;
> > +	int i = 0;
> > +
> > +	table_max_length = entry->struct_table_max_size;
> > +
> > +	for (i = 0; entry_data <= (table + table_max_length - 4);
> > i++) {
> > +		uint64_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;
> > +
> > +		/* We found DMI end of table */
> > +		if (hdr.type == SMBIOS_END_OF_TABLE)
> > +			break;
> > +
> > +		/* 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_max_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_max_length)
> > +			dmicheck_entry(fw, addr, &hdr);
> > +		else {
> > +			fwts_failed(fw, LOG_LEVEL_HIGH,
> > DMI_BAD_TABLE_LENGTH,
> > +				"DMI table maximum size was %"
> > PRId32 " bytes (as specified by "
> > +				"the SMBIOS 3.0 header) but the
> > DMI entries over the maximum "
> > +				"length without finding the End-
> > of-Table(Type 127).",
> > +				table_max_length);
> > +			break;
> > +		}
> > +
> > +		entry_data = next_entry;
> > +	}
> > +
> > +}
> > +
> > +static int sbbr_dmicheck_consistency_test(fwts_framework *fw)
> > +{
> > +	void *addr;
> > +	fwts_smbios30_entry entry30;
> > +	uint16_t version = 0;
> > +	uint8_t  *table;
> > +
> > +	if (!smbios30_found) {
> > +		fwts_skipped(fw, "Cannot find SMBIOS30 table
> > entry, skip the test.");
> > +		return FWTS_SKIP;
> > +	}
> > +
> > +	addr = fwts_smbios30_find_entry(fw, &entry30, &version);
> > +	if (addr == NULL) {
> > +		fwts_failed(fw, LOG_LEVEL_HIGH,
> > DMI_NO_TABLE_HEADER,
> > +			"Cannot find SMBIOS 3.0 table entry.");
> > +		return FWTS_ERROR;
> > +	}
> > +
> > +	table = dmi_table_smbios30(fw, &entry30);
> > +	if (table == NULL)
> > +		return FWTS_ERROR;
> > +
> > +	dmi_scan_smbios30_table(fw, &entry30, table);
> > +
> > +	dmi_table_free(table);
> > +
> > +	return FWTS_OK;
> > +}
> > +
> > +/*
> > + * ARM SBBR SMBIOS Structure Test
> > + */
> > +
> > +/* Test Entry Structure */
> > +typedef struct {
> > +	const char *name;
> > +	const uint8_t type;
> > +} sbbr_test_entry;
> > +
> > +/* Test Definition Array */
> > +sbbr_test_entry sbbr_test[] = {
> > +	{"BIOS Information", 0},
> > +	{"System Information", 1},
> > +	{"Baseboard Information", 2},
> > +	{"System Enclosure or Chassis", 3},
> > +	{"Processor Information", 4},
> > +	{"Cache Information", 7},
> > +	{"Port Connector Information", 8},
> > +	{"System Slots", 9},
> > +	{"OEM Strings", 11},
> > +	{"BIOS Language Information", 13},
> > +	{"System Event Log", 15},
> > +	{"Physical Memory Array", 16},
> > +	{"Memory Device", 17},
> > +	{"Memory Array Mapped Address", 19},
> > +	{"System Boot Information", 32},
> > +	{"IPMI Device Information", 38},
> > +	{"Onboard Devices Extended Information", 41},
> > +	{0, 0}
> > +};
> > +
> > +/* Finds SMBIOS structure of a given type in an SMBIOS30 table. */
> > +static uint8_t * sbbr_smbios30_locate_structure (uint8_t *table,
> > const int table_max_length, uint8_t type)
> > +{
> > +	uint8_t *entry_data;
> > +	fwts_dmi_header *hdr;
> > +
> > +
> > +	entry_data = table;
> > +    while ( (entry_data != NULL) &&
> > +    		(entry_data < (table + table_max_length)) )
> > +    {
> > +    	hdr = (fwts_dmi_header *)(entry_data);
> > +
> > +		/* We found the entry we're looking for. */
> > +		if (hdr->type == type)
> > +			return hdr->data;
> > +
> > +		/* We found DMI end of table */
> > +		if (hdr->type == SMBIOS_END_OF_TABLE)
> > +			return NULL;
> > +
> > +		entry_data = entry_data + hdr->length;
> > +
> > +		while( *((int *)(entry_data)) != 0)
> > +		{
> > +			entry_data++;
> > +
> > +		}
> > +		entry_data += 2; /* Increment address to start of
> > next structure */
> > +
> > +	}
> > +
> > +
> > +	return NULL;
> > +}
> > +
> > +/* SBBR SMBIOS structure test function. */
> > +static int sbbr_smbios30_test(fwts_framework *fw)
> > +{
> > +	fwts_smbios30_entry entry30;
> > +	fwts_dmi_header hdr;
> > +	uint16_t version;
> > +	void *addr;
> > +	uint32_t i;
> > +	uint8_t *table;
> > +
> > +	/* Finding SMBIOS30 entry point. */
> > +	addr = fwts_smbios30_find_entry(fw, &entry30, &version);
> > +	if (addr == NULL) {
> > +		fwts_failed(fw, LOG_LEVEL_HIGH,
> > "SbbrSmbiosNoTable", "Cannot find SMBIOS30 table "
> > +		            "entry.");
> > +		return FWTS_ERROR;
> > +	}
> > +
> > +	/* Getting SMBIOS table contents. */
> > +	table = dmi_table_smbios30(fw, &entry30);
> > +	if (table == NULL)
> > +		return FWTS_ERROR;
> > +
> > +	/* Searching for each SMBIOS structure needed by SBBR. */
> > +	for (i = 0; sbbr_test[i].name != NULL; i++) {
> > +
> > +		hdr.data = sbbr_smbios30_locate_structure (table,
> > entry30.struct_table_max_size, sbbr_test[i].type);
> > +
> > +		if (hdr.data != NULL)
> > +			dmicheck_entry(fw, (uintptr_t)hdr.data,
> > &hdr);
> > +		else
> > +			fwts_failed(fw, LOG_LEVEL_HIGH,
> > "SbbrSmbiosNoStruct", "Cannot find SMBIOS "
> > +			            "structure: %s (Type %d)",
> > sbbr_test[i].name, sbbr_test[i].type);
> > +	}
> > +
> > +	dmi_table_free(table);
> > +
> > +	return FWTS_OK;
> > +}
> > +
> > +static fwts_framework_minor_test sbbr_dmicheck_tests[] = {
> > +	{ sbbr_dmicheck_revision_test, "Find and test SMBIOS Table
> > Entry Points." },
> > +	{ sbbr_dmicheck_consistency_test, "Test DMI/SMBIOS3 tables
> > for errors." },
> > +	{ sbbr_smbios30_test, "Test ARM SBBR SMBIOS structure
> > requirements."},
> > +	{ NULL, NULL }
> > +};
> > +
> > +static fwts_framework_ops sbbr_dmicheck_ops = {
> > +	.description = "DMI/SMBIOS table tests.",
> > +	.minor_tests = sbbr_dmicheck_tests
> > +};
> > +
> > +FWTS_REGISTER("sbbr_dmicheck", &sbbr_dmicheck_ops,
> > FWTS_TEST_ANYTIME, FWTS_FLAG_TEST_SBBR)
> > +
> > +#endif
> >
Jeffrey Hugo March 3, 2017, 6:37 p.m. UTC | #3
On 3/2/2017 3:26 PM, Supreeth Venkatesh wrote:
> Server Base Boot Requirements (SBBR) specification is intended for SBSA-
> compliant 64-bit ARMv8 servers.
> It defines the base firmware requirements for out-of-box support of any
> ARM SBSA-compatible Operating System or hypervisor.
> The requirements in this specification are expected to be minimal yet
> complete for booting a multi-core ARMv8 server platform, while leaving
> plenty of room for OEM or ODM innovations and design details.
> For more information, download the SBBR specification here:
> http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044b/index.html
>
> This change introduces test cases as per SBBR specification to SMBIOS
> structures. These test cases may be subset/superset of dmicheck tests
> already existing. However, to preserve "sbbr" classification, new file
> is created, even when most of the code is re-used from dmicheck.
>
> Signed-off-by: Supreeth Venkatesh <supreeth.venkatesh@arm.com>
> ---
>  src/sbbr/dmicheck/dmicheck.c | 1625 ++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 1625 insertions(+)
>  create mode 100644 src/sbbr/dmicheck/dmicheck.c
>
> diff --git a/src/sbbr/dmicheck/dmicheck.c b/src/sbbr/dmicheck/dmicheck.c
> new file mode 100644
> index 0000000..32728fa
> --- /dev/null
> +++ b/src/sbbr/dmicheck/dmicheck.c
> @@ -0,0 +1,1625 @@
> +/*
> + * Copyright (C) 2010-2017 Canonical
> + * Copyright (C) 2017      ARM Ltd
> + *
> + * 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"
> +
> +#if defined(FWTS_HAS_SBBR)
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <unistd.h>
> +#include <limits.h>
> +#include <fcntl.h>
> +
> +#define DMI_VERSION			(0x0300)
> +#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 DMI_RESERVED_VALUE_USED		"DMIReservedValueUsed"
> +
> +#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;
> +} fwts_chassis_type_map;
> +
> +typedef struct {
> +	uint8_t	type;
> +	uint8_t	offset;
> +} fwts_dmi_used_by_kernel;
> +
> +static bool smbios30_found = false;
> +
> +/*
> + *  Table derived by scanning thousands of DMI table dumps from bug reports
> + */
> +static const fwts_dmi_pattern dmi_patterns[] = {
> +	{ "DMISerialNumber",	"Serial Number",	"0000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"00000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"000000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"0000000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"0x00000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"0x0000000000000000" },
> +	{ "DMISerialNumber",	"Serial Number",	"012345678" },
> +	{ "DMISerialNumber",	"Serial Number",	"0123456789" },
> +	{ "DMISerialNumber",	"Serial Number",	"01234567890" },
> +	{ "DMISerialNumber",	"Serial Number",	"012345678900" },
> +	{ "DMISerialNumber",	"Serial Number",	"0123456789000" },
> +	{ "DMISerialNumber",	"Serial Number",	"System Serial Number" },
> +	{ "DMISerialNumber",	"Serial Number",	"MB-1234567890" },
> +	{ "DMISerialNumber",	"Serial Number",	"NB-1234567890" },
> +	{ "DMISerialNumber",	"Serial Number",	"NB-0123456789" },
> +	{ "DMISerialNumber",	"Serial Number",	"Base Board Serial Number" },
> +	{ "DMISerialNumber",	"Serial Number",	"Chassis Serial Number" },
> +	{ "DMISerialNumber",	"Serial Number",	"<cut out>" },
> +	{ "DMISerialNumber",	"Serial Number",	"Empty" },
> +	{ "DMISerialNumber",	"Serial Number",	"[Empty]" },
> +	{ "DMISerialNumber",	"Serial Number",	"NA" },
> +	{ "DMISerialNumber",	"Serial Number",	"N/A" },
> +	{ "DMISerialNumber",	"Serial Number",	"None" },
> +	{ "DMISerialNumber",	"Serial Number",	"None1" },
> +	{ "DMISerialNumber",	"Serial Number",	"Not Available" },
> +	{ "DMISerialNumber",	"Serial Number",	"Not Specified" },
> +	{ "DMISerialNumber",	"Serial Number",	"NotSupport" },
> +	{ "DMISerialNumber",	"Serial Number",	"Not Supported by CPU" },
> +	{ "DMISerialNumber",	"Serial Number",	"Not Supported" },
> +	{ "DMISerialNumber",	"Serial Number",	"OEM Chassis Serial Number" },
> +	{ "DMISerialNumber",	"Serial Number",	"OEM_Define1" },
> +	{ "DMISerialNumber",	"Serial Number",	"OEM_Define2" },
> +	{ "DMISerialNumber",	"Serial Number",	"OEM_Define3" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum0" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum00" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum01" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum02" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum03" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum1" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum2" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum3" },
> +	{ "DMISerialNumber",	"Serial Number",	"SerNum4" },
> +	{ "DMISerialNumber",	"Serial Number",	"TBD by ODM" },
> +	{ "DMISerialNumber",	"Serial Number",	"To Be Defined By O.E.M" },
> +	{ "DMISerialNumber",	"Serial Number",	"To be filled by O.E.M." },
> +	{ "DMISerialNumber",	"Serial Number",	"To Be Filled By O.E.M." },
> +	{ "DMISerialNumber",	"Serial Number",	"Unknow" },
> +	{ "DMISerialNumber",	"Serial Number",	"Unknown" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXXX" },
> +	{ "DMISerialNumber",	"Serial Number",	"XXXXX" },
> +	{ "DMISerialNumber",	NULL,			"Chassis Serial Number" },
> +	{ "DMIAssetTag",	"Asset Tag",		"0000000000" },
> +	{ "DMIAssetTag",	"Asset Tag",		"0x00000000" },
> +	{ "DMIAssetTag",	"Asset Tag",		"1234567890" },
> +	{ "DMIAssetTag",	"Asset Tag",		"123456789000" },
> +	{ "DMIAssetTag",	"Asset Tag",		"9876543210" },
> +	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum0" },
> +	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum1" },
> +	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum2" },
> +	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum3" },
> +	{ "DMIAssetTag",	"Asset Tag",		"ABCDEFGHIJKLM" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Asset-1234567890" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Asset Tag" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Asset Tag:" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum0" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum1" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum2" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum3" },
> +	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum4" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Asset tracking" },
> +	{ "DMIAssetTag",	"Asset Tag",		"ATN12345678901234567" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Base Board Asset Tag#" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Base Board Asset Tag" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Chassis Asset Tag" },
> +	{ "DMIAssetTag",	"Asset Tag",		"<cut out>" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Fill By OEM" },
> +	{ "DMIAssetTag",	"Asset Tag",		"N/A" },
> +	{ "DMIAssetTag",	"Asset Tag",		"No Asset Information" },
> +	{ "DMIAssetTag",	"Asset Tag",		"No Asset Tag" },
> +	{ "DMIAssetTag",	"Asset Tag",		"None" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Not Available" },
> +	{ "DMIAssetTag",	"Asset Tag",		"Not Specified" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define0" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define1" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define2" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define3" },
> +	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define4" },
> +	{ "DMIAssetTag",	"Asset Tag",		"TBD by ODM" },
> +	{ "DMIAssetTag",	"Asset Tag",		"To Be Defined By O.E.M" },
> +	{ "DMIAssetTag",	"Asset Tag",		"To be filled by O.E.M." },
> +	{ "DMIAssetTag",	"Asset Tag",		"To Be Filled By O.E.M." },
> +	{ "DMIAssetTag",	"Asset Tag",		"Unknown" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXX" },
> +	{ "DMIAssetTag",	"Asset Tag",		"XXXXXX" },
> +	{ "DMIChassisVendor",	NULL,			"Chassis Manufacture" },
> +	{ "DMIChassisVersion",	NULL,			"Chassis Version" },
> +	{ "DMIProductVersion",	NULL,			"System Version" },
> +	{ "DMIBadDefault",	NULL,			"To Be Defined By O.E.M" },
> +	{ "DMIBadDefault",	NULL,			"To be filled by O.E.M." },
> +	{ "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 },
> +	{ "Other",		FWTS_SMBIOS_CHASSIS_OTHER },
> +	{ "Unknown",		FWTS_SMBIOS_CHASSIS_UNKNOWN },
> +	{ "Desktop",		FWTS_SMBIOS_CHASSIS_DESKTOP },
> +	{ "Low Profile Desktop",FWTS_SMBIOS_CHASSIS_LOW_PROFILE_DESKTOP },
> +	{ "Pizza Box",		FWTS_SMBIOS_CHASSIS_PIZZA_BOX },
> +	{ "Mini Tower",		FWTS_SMBIOS_CHASSIS_MINI_TOWER },
> +	{ "Chassis Tower",	FWTS_SMBIOS_CHASSIS_TOWER },
> +	{ "Portable",		FWTS_SMBIOS_CHASSIS_PORTABLE },
> +	{ "Laptop",		FWTS_SMBIOS_CHASSIS_LAPTOP },
> +	{ "Notebook",		FWTS_SMBIOS_CHASSIS_NOTEBOOK },
> +	{ "Handheld",		FWTS_SMBIOS_CHASSIS_HANDHELD },
> +	{ "Docking Station",	FWTS_SMBIOS_CHASSIS_DOCKING_STATION },
> +	{ "All In One",		FWTS_SMBIOS_CHASSIS_ALL_IN_ONE },
> +	{ "Sub Notebook",	FWTS_SMBIOS_CHASSIS_SUB_NOTEBOOK },
> +	{ "Space Saving",	FWTS_SMBIOS_CHASSIS_SPACE_SAVING },
> +	{ "Lunch Box",		FWTS_SMBIOS_CHASSIS_LUNCH_BOX},
> +	{ "Server Chassis",	FWTS_SMBIOS_CHASSIS_MAIN_SERVER_CHASSIS },
> +	{ "Expansion Chassis",	FWTS_SMBIOS_CHASSIS_EXPANISON_CHASSIS },
> +	{ "Sub Chassis",	FWTS_SMBIOS_CHASSIS_SUB_CHASSIS },
> +	{ "Bus Expansion Chassis", FWTS_SMBIOS_CHASSIS_BUS_EXPANSION_CHASSIS },
> +	{ "Peripheral Chassis",	FWTS_SMBIOS_CHASSIS_PERIPHERAL_CHASSIS },
> +	{ "Raid Chassis",	FWTS_SMBIOS_CHASSIS_RAID_CHASSIS },
> +	{ "Rack Mount Chassis",	FWTS_SMBIOS_CHASSIS_RACK_MOUNT_CHASSIS },
> +	{ "Sealed Case PC",	FWTS_SMBIOS_CHASSIS_SEALED_CASE_PC },
> +	{ "Multi System Chassis",FWTS_SMBIOS_CHASSIS_MULTI_SYSTEM_CHASSIS },
> +	{ "Compact PCI",	FWTS_SMBIOS_CHASSIS_COMPACT_PCI },
> +	{ "Advanced TCA",	FWTS_SMBIOS_CHASSIS_ADVANCED_TCA },
> +	{ "Blade",		FWTS_SMBIOS_CHASSIS_BLADE },
> +	{ "Enclosure",		FWTS_SMBIOS_CHASSIS_BLADE_ENCLOSURE },
> +	{ "Tablet",		FWTS_SMBIOS_CHASSIS_TABLET },
> +	{ "Convertible",	FWTS_SMBIOS_CHASSIS_CONVERTIBLE },
> +	{ "Detachable",		FWTS_SMBIOS_CHASSIS_DETACHABLE },
> +};
> +
> +/* 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
> +#define ANCHOR_SIZE 8
> +
> +/*
> + *  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 int dmi_load_file(const char* filename, void *buf, size_t size)
> +{
> +	int fd;
> +	ssize_t ret;
> +
> +	if ((fd = open(filename, O_RDONLY)) < 0)
> +		return FWTS_ERROR;
> +	ret = read(fd, buf, size);
> +	(void)close(fd);
> +	if (ret != (ssize_t)size)
> +		return FWTS_ERROR;
> +	return FWTS_OK;
> +}
> +
> +static void* dmi_table_smbios30(fwts_framework *fw, fwts_smbios30_entry *entry)
> +{
> +	size_t length = (size_t)entry->struct_table_max_size;
> +	void *table;
> +	char anchor[ANCHOR_SIZE];
> +
> +	/* 64 bit entry sanity check on length */
> +	if ((length == 0) || (length > 0xffffff)) {
> +		fwts_log_info(fw, "SMBIOS table size of %zu bytes looks "
> +			"suspicious",  length);
> +		return NULL;
> +	}
> +
> +	if (dmi_load_file("/sys/firmware/dmi/tables/smbios_entry_point", anchor, sizeof("_SM3_")) == FWTS_OK
> +			&& strncmp(anchor, "_SM3_", sizeof("_SM3_")) == 0) {
> +		table = malloc(length);
> +		if (!table)
> +			return NULL;
> +		if (dmi_load_file("/sys/firmware/dmi/tables/DMI", table, length) == FWTS_OK) {
> +			fwts_log_info(fw, "SMBIOS30 table loaded from /sys/firmware/dmi/tables/DMI\n");
> +			return table;
> +		}
> +		free(table);
> +	}
> +
> +	fwts_log_error(fw, "Cannot mmap SMBIOS 3.0 table from %16.16" PRIx64 "..%16.16" PRIx64 ".",
> +			entry->struct_table_address, entry->struct_table_address + entry->struct_table_max_size);
> +	return NULL;
> +}

This appears broken on my platform.  The standard dmitest works fine. 
This test outputs:

PASSED: Test 1, SMBIOS30 Table Entry Point Checksum is valid.
PASSED: Test 1, SMBIOS30 Table Entry Point Length is valid.
Cannot mmap SMBIOS 3.0 table from 0000000004e50000..0000000004e529c4.

Test 2 of 3: Test DMI/SMBIOS3 tables for errors.
Cannot mmap SMBIOS 3.0 table from 0000000004e50000..0000000004e529c4.

Test 3 of 3: Test ARM SBBR SMBIOS structure requirements.
Cannot mmap SMBIOS 3.0 table from 0000000004e50000..0000000004e529c4.
Supreeth Venkatesh March 6, 2017, 5:48 p.m. UTC | #4
On Thu, 2017-03-02 at 23:23 +0000, Colin Ian King wrote:
> I get white space errors when applying this patch:
> 
> Applying: sbbr/dmicheck: Add SMBIOS test cases as per SBBR.
> .git/rebase-apply/patch:1555: space before tab in indent.
>     		(entry_data < (table + table_max_length)) )
> .git/rebase-apply/patch:1557: space before tab in indent.
>     	hdr = (fwts_dmi_header *)(entry_data);
> warning: 2 lines add whitespace errors.
> 
Ok. will fix it.
> This does seem like a cut-n-paste duplication of a lot of the fwts
> smbios checking code. What's different about this that justifies
> duplication of code?
> 
> 
SBBR Starts from SMBIOS(DTMF) 3.0, hence legacy SMBIOS v1.0 APIs are
removed. also additional test cases are added.
Supreeth Venkatesh March 6, 2017, 5:56 p.m. UTC | #5
On Fri, 2017-03-03 at 11:37 -0700, Jeffrey Hugo wrote:
> On 3/2/2017 3:26 PM, Supreeth Venkatesh wrote:
> > 
> > Server Base Boot Requirements (SBBR) specification is intended for
> > SBSA-
> > compliant 64-bit ARMv8 servers.
> > It defines the base firmware requirements for out-of-box support of
> > any
> > ARM SBSA-compatible Operating System or hypervisor.
> > The requirements in this specification are expected to be minimal
> > yet
> > complete for booting a multi-core ARMv8 server platform, while
> > leaving
> > plenty of room for OEM or ODM innovations and design details.
> > For more information, download the SBBR specification here:
> > http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044
> > b/index.html
> > 
> > This change introduces test cases as per SBBR specification to
> > SMBIOS
> > structures. These test cases may be subset/superset of dmicheck
> > tests
> > already existing. However, to preserve "sbbr" classification, new
> > file
> > is created, even when most of the code is re-used from dmicheck.
> > 
> > Signed-off-by: Supreeth Venkatesh <supreeth.venkatesh@arm.com>
> > ---
> >  src/sbbr/dmicheck/dmicheck.c | 1625
> > ++++++++++++++++++++++++++++++++++++++++++
> >  1 file changed, 1625 insertions(+)
> >  create mode 100644 src/sbbr/dmicheck/dmicheck.c
> > 
> > diff --git a/src/sbbr/dmicheck/dmicheck.c
> > b/src/sbbr/dmicheck/dmicheck.c
> > new file mode 100644
> > index 0000000..32728fa
> > --- /dev/null
> > +++ b/src/sbbr/dmicheck/dmicheck.c
> > @@ -0,0 +1,1625 @@
> > +/*
> > + * Copyright (C) 2010-2017 Canonical
> > + * Copyright (C) 2017      ARM Ltd
> > + *
> > + * 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"
> > +
> > +#if defined(FWTS_HAS_SBBR)
> > +
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +#include <string.h>
> > +#include <sys/types.h>
> > +#include <sys/stat.h>
> > +#include <unistd.h>
> > +#include <limits.h>
> > +#include <fcntl.h>
> > +
> > +#define DMI_VERSION			(0x0300)
> > +#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	"DMIStringIndexOutOfR
> > ange"
> > +#define DMI_ILLEGAL_MAPPED_ADDR_RANGE	"DMIIllegalMappedAddr
> > Range"
> > +#define DMI_MGMT_CTRL_HOST_TYPE		"DMIMgmtCtrlHostTyp
> > e"
> > +#define DMI_INVALID_ENTRY_LENGTH	"DMIInvalidEntryLength"
> > +#define DMI_INVALID_HARDWARE_ENTRY	"DMIInvalidHardwareEntry
> > "
> > +#define DMI_RESERVED_VALUE_USED		"DMIReservedValueUs
> > ed"
> > +
> > +#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;
> > +} fwts_chassis_type_map;
> > +
> > +typedef struct {
> > +	uint8_t	type;
> > +	uint8_t	offset;
> > +} fwts_dmi_used_by_kernel;
> > +
> > +static bool smbios30_found = false;
> > +
> > +/*
> > + *  Table derived by scanning thousands of DMI table dumps from
> > bug reports
> > + */
> > +static const fwts_dmi_pattern dmi_patterns[] = {
> > +	{ "DMISerialNumber",	"Serial Number",	"00000
> > 00" },
> > +	{ "DMISerialNumber",	"Serial Number",	"00000
> > 000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"00000
> > 0000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"00000
> > 00000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"0x000
> > 00000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"0x000
> > 0000000000000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 5678" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 56789" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 567890" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 5678900" },
> > +	{ "DMISerialNumber",	"Serial Number",	"01234
> > 56789000" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Syste
> > m Serial Number" },
> > +	{ "DMISerialNumber",	"Serial Number",	"MB-
> > 1234567890" },
> > +	{ "DMISerialNumber",	"Serial Number",	"NB-
> > 1234567890" },
> > +	{ "DMISerialNumber",	"Serial Number",	"NB-
> > 0123456789" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Base
> > Board Serial Number" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Chass
> > is Serial Number" },
> > +	{ "DMISerialNumber",	"Serial Number",	"<cut
> > out>" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Empty
> > " },
> > +	{ "DMISerialNumber",	"Serial Number",	"[Empt
> > y]" },
> > +	{ "DMISerialNumber",	"Serial Number",	"NA"
> > },
> > +	{ "DMISerialNumber",	"Serial Number",	"N/A"
> > },
> > +	{ "DMISerialNumber",	"Serial Number",	"None"
> > },
> > +	{ "DMISerialNumber",	"Serial Number",	"None1
> > " },
> > +	{ "DMISerialNumber",	"Serial Number",	"Not
> > Available" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Not
> > Specified" },
> > +	{ "DMISerialNumber",	"Serial Number",	"NotSu
> > pport" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Not
> > Supported by CPU" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Not
> > Supported" },
> > +	{ "DMISerialNumber",	"Serial Number",	"OEM
> > Chassis Serial Number" },
> > +	{ "DMISerialNumber",	"Serial Number",	"OEM_D
> > efine1" },
> > +	{ "DMISerialNumber",	"Serial Number",	"OEM_D
> > efine2" },
> > +	{ "DMISerialNumber",	"Serial Number",	"OEM_D
> > efine3" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m0" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m00" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m01" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m02" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m03" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m1" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m2" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m3" },
> > +	{ "DMISerialNumber",	"Serial Number",	"SerNu
> > m4" },
> > +	{ "DMISerialNumber",	"Serial Number",	"TBD
> > by ODM" },
> > +	{ "DMISerialNumber",	"Serial Number",	"To Be
> > Defined By O.E.M" },
> > +	{ "DMISerialNumber",	"Serial Number",	"To be
> > filled by O.E.M." },
> > +	{ "DMISerialNumber",	"Serial Number",	"To Be
> > Filled By O.E.M." },
> > +	{ "DMISerialNumber",	"Serial Number",	"Unkno
> > w" },
> > +	{ "DMISerialNumber",	"Serial Number",	"Unkno
> > wn" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXXXXXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXXXXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXXXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > XXX" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > X" },
> > +	{ "DMISerialNumber",	"Serial Number",	"XXXXX
> > " },
> > +	{ "DMISerialNumber",	NULL,			"
> > Chassis Serial Number" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"00000
> > 00000" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"0x000
> > 00000" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"12345
> > 67890" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"12345
> > 6789000" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"98765
> > 43210" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"A1_As
> > setTagNum0" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"A1_As
> > setTagNum1" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"A1_As
> > setTagNum2" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"A1_As
> > setTagNum3" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"ABCDE
> > FGHIJKLM" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > -1234567890" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > Tag" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > Tag:" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum0" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum1" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum2" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum3" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > TagNum4" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Asset
> > tracking" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"ATN12
> > 345678901234567" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Base
> > Board Asset Tag#" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Base
> > Board Asset Tag" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Chass
> > is Asset Tag" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"<cut
> > out>" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Fill
> > By OEM" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"N/A"
> > },
> > +	{ "DMIAssetTag",	"Asset Tag",		"No
> > Asset Information" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"No
> > Asset Tag" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"None"
> > },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Not
> > Available" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Not
> > Specified" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine0" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine1" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine2" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine3" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"OEM_D
> > efine4" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"TBD
> > by ODM" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"To Be
> > Defined By O.E.M" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"To be
> > filled by O.E.M." },
> > +	{ "DMIAssetTag",	"Asset Tag",		"To Be
> > Filled By O.E.M." },
> > +	{ "DMIAssetTag",	"Asset Tag",		"Unkno
> > wn" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXXXXXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXXXXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXXXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XXX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > XX" },
> > +	{ "DMIAssetTag",	"Asset Tag",		"XXXXX
> > X" },
> > +	{ "DMIChassisVendor",	NULL,			
> > "Chassis Manufacture" },
> > +	{ "DMIChassisVersion",	NULL,			
> > "Chassis Version" },
> > +	{ "DMIProductVersion",	NULL,			
> > "System Version" },
> > +	{ "DMIBadDefault",	NULL,			"To
> > Be Defined By O.E.M" },
> > +	{ "DMIBadDefault",	NULL,			"To
> > be filled by O.E.M." },
> > +	{ "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 },
> > +	{ "Other",		FWTS_SMBIOS_CHASSIS_OTHER },
> > +	{ "Unknown",		FWTS_SMBIOS_CHASSIS_UNKNOWN },
> > +	{ "Desktop",		FWTS_SMBIOS_CHASSIS_DESKTOP },
> > +	{ "Low Profile
> > Desktop",FWTS_SMBIOS_CHASSIS_LOW_PROFILE_DESKTOP },
> > +	{ "Pizza Box",		FWTS_SMBIOS_CHASSIS_PIZZA_BO
> > X },
> > +	{ "Mini Tower",		FWTS_SMBIOS_CHASSIS_MINI_TO
> > WER },
> > +	{ "Chassis Tower",	FWTS_SMBIOS_CHASSIS_TOWER },
> > +	{ "Portable",		FWTS_SMBIOS_CHASSIS_PORTABLE
> > },
> > +	{ "Laptop",		FWTS_SMBIOS_CHASSIS_LAPTOP },
> > +	{ "Notebook",		FWTS_SMBIOS_CHASSIS_NOTEBOOK
> > },
> > +	{ "Handheld",		FWTS_SMBIOS_CHASSIS_HANDHELD
> > },
> > +	{ "Docking Station",	FWTS_SMBIOS_CHASSIS_DOCKING_ST
> > ATION },
> > +	{ "All In One",		FWTS_SMBIOS_CHASSIS_ALL_IN_
> > ONE },
> > +	{ "Sub Notebook",	FWTS_SMBIOS_CHASSIS_SUB_NOTEBOOK
> > },
> > +	{ "Space Saving",	FWTS_SMBIOS_CHASSIS_SPACE_SAVING
> > },
> > +	{ "Lunch Box",		FWTS_SMBIOS_CHASSIS_LUNCH_BO
> > X},
> > +	{ "Server Chassis",	FWTS_SMBIOS_CHASSIS_MAIN_SERVER
> > _CHASSIS },
> > +	{ "Expansion Chassis",	FWTS_SMBIOS_CHASSIS_EXPANISO
> > N_CHASSIS },
> > +	{ "Sub Chassis",	FWTS_SMBIOS_CHASSIS_SUB_CHASSIS },
> > +	{ "Bus Expansion Chassis",
> > FWTS_SMBIOS_CHASSIS_BUS_EXPANSION_CHASSIS },
> > +	{ "Peripheral Chassis",	FWTS_SMBIOS_CHASSIS_PERIPHE
> > RAL_CHASSIS },
> > +	{ "Raid Chassis",	FWTS_SMBIOS_CHASSIS_RAID_CHASSIS
> > },
> > +	{ "Rack Mount Chassis",	FWTS_SMBIOS_CHASSIS_RACK_MO
> > UNT_CHASSIS },
> > +	{ "Sealed Case PC",	FWTS_SMBIOS_CHASSIS_SEALED_CASE
> > _PC },
> > +	{ "Multi System
> > Chassis",FWTS_SMBIOS_CHASSIS_MULTI_SYSTEM_CHASSIS },
> > +	{ "Compact PCI",	FWTS_SMBIOS_CHASSIS_COMPACT_PCI },
> > +	{ "Advanced TCA",	FWTS_SMBIOS_CHASSIS_ADVANCED_TCA
> > },
> > +	{ "Blade",		FWTS_SMBIOS_CHASSIS_BLADE },
> > +	{ "Enclosure",		FWTS_SMBIOS_CHASSIS_BLADE_EN
> > CLOSURE },
> > +	{ "Tablet",		FWTS_SMBIOS_CHASSIS_TABLET },
> > +	{ "Convertible",	FWTS_SMBIOS_CHASSIS_CONVERTIBLE },
> > +	{ "Detachable",		FWTS_SMBIOS_CHASSIS_DETACHA
> > BLE },
> > +};
> > +
> > +/* 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
> > +#define ANCHOR_SIZE 8
> > +
> > +/*
> > + *  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 int dmi_load_file(const char* filename, void *buf, size_t
> > size)
> > +{
> > +	int fd;
> > +	ssize_t ret;
> > +
> > +	if ((fd = open(filename, O_RDONLY)) < 0)
> > +		return FWTS_ERROR;
> > +	ret = read(fd, buf, size);
> > +	(void)close(fd);
> > +	if (ret != (ssize_t)size)
> > +		return FWTS_ERROR;
> > +	return FWTS_OK;
> > +}
> > +
> > +static void* dmi_table_smbios30(fwts_framework *fw,
> > fwts_smbios30_entry *entry)
> > +{
> > +	size_t length = (size_t)entry->struct_table_max_size;
> > +	void *table;
> > +	char anchor[ANCHOR_SIZE];
> > +
> > +	/* 64 bit entry sanity check on length */
> > +	if ((length == 0) || (length > 0xffffff)) {
> > +		fwts_log_info(fw, "SMBIOS table size of %zu bytes
> > looks "
> > +			"suspicious",  length);
> > +		return NULL;
> > +	}
> > +
> > +	if
> > (dmi_load_file("/sys/firmware/dmi/tables/smbios_entry_point",
> > anchor, sizeof("_SM3_")) == FWTS_OK
> > +			&& strncmp(anchor, "_SM3_",
> > sizeof("_SM3_")) == 0) {
> > +		table = malloc(length);
> > +		if (!table)
> > +			return NULL;
> > +		if (dmi_load_file("/sys/firmware/dmi/tables/DMI",
> > table, length) == FWTS_OK) {
> > +			fwts_log_info(fw, "SMBIOS30 table loaded
> > from /sys/firmware/dmi/tables/DMI\n");
> > +			return table;
> > +		}
> > +		free(table);
> > +	}
> > +
> > +	fwts_log_error(fw, "Cannot mmap SMBIOS 3.0 table from
> > %16.16" PRIx64 "..%16.16" PRIx64 ".",
> > +			entry->struct_table_address, entry-
> > >struct_table_address + entry->struct_table_max_size);
> > +	return NULL;
> > +}
> This appears broken on my platform.  The standard dmitest works
> fine. 
> This test outputs:
> 
> PASSED: Test 1, SMBIOS30 Table Entry Point Checksum is valid.
> PASSED: Test 1, SMBIOS30 Table Entry Point Length is valid.
> Cannot mmap SMBIOS 3.0 table from 0000000004e50000..0000000004e529c4.
> 
> Test 2 of 3: Test DMI/SMBIOS3 tables for errors.
> Cannot mmap SMBIOS 3.0 table from 0000000004e50000..0000000004e529c4.
> 
> Test 3 of 3: Test ARM SBBR SMBIOS structure requirements.
> Cannot mmap SMBIOS 3.0 table from 0000000004e50000..0000000004e529c4.
> 

Thank you very much for testing it on your platform. Appreciate it.
As per earlier discussion, I think Leif or someone mentioned that "/sys
is definitely the preferable method to access both ACPI and SMBIOS, and
the only one that should be used on any ARM system."
Hence, I have removed mmap /dev/mem, this may be reason behind failure
you are getting in your platform. I will recheck with the kernel folks
and based on their inputs, we can add back /dev/mem support or not.
Leif Lindholm March 7, 2017, 9:13 a.m. UTC | #6
On Mon, Mar 06, 2017 at 11:56:22AM -0600, Supreeth Venkatesh wrote:
> On Fri, 2017-03-03 at 11:37 -0700, Jeffrey Hugo wrote:
> > This appears broken on my platform.  The standard dmitest works
> > fine. 
> > This test outputs:
> > 
> > PASSED: Test 1, SMBIOS30 Table Entry Point Checksum is valid.
> > PASSED: Test 1, SMBIOS30 Table Entry Point Length is valid.
> > Cannot mmap SMBIOS 3.0 table from 0000000004e50000..0000000004e529c4.
> > 
> > Test 2 of 3: Test DMI/SMBIOS3 tables for errors.
> > Cannot mmap SMBIOS 3.0 table from 0000000004e50000..0000000004e529c4.
> > 
> > Test 3 of 3: Test ARM SBBR SMBIOS structure requirements.
> > Cannot mmap SMBIOS 3.0 table from 0000000004e50000..0000000004e529c4.
> > 
> 
> Thank you very much for testing it on your platform. Appreciate it.
> As per earlier discussion, I think Leif or someone mentioned that "/sys
> is definitely the preferable method to access both ACPI and SMBIOS, and
> the only one that should be used on any ARM system."

/sys is the only valid access method on ARM systems you wish to not be
trivially DoS-able from userspace. I guess if people want to keep
/dev/mem available for development purposes, or are using kernels
predating the implementation of the complete /sys interface (4.2, I
think), it can make sense to keep the /dev/mem version around.

But I would hope it could move to being enabled only if configure is
run with --unsafe-memory-accesses, and even then only as a fallback.
Actually, I'll put such an RFC patch together to do just that.

But this also emphasises the importance of not duplicating code if at
all avoidable. The removal of the /dev/mem code path would have been
immediately obvious as a diff to the original file.

/
    Leif
diff mbox

Patch

diff --git a/src/sbbr/dmicheck/dmicheck.c b/src/sbbr/dmicheck/dmicheck.c
new file mode 100644
index 0000000..32728fa
--- /dev/null
+++ b/src/sbbr/dmicheck/dmicheck.c
@@ -0,0 +1,1625 @@ 
+/*
+ * Copyright (C) 2010-2017 Canonical
+ * Copyright (C) 2017      ARM Ltd
+ *
+ * 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"
+
+#if defined(FWTS_HAS_SBBR)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+#include <fcntl.h>
+
+#define DMI_VERSION			(0x0300)
+#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 DMI_RESERVED_VALUE_USED		"DMIReservedValueUsed"
+
+#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;
+} fwts_chassis_type_map;
+
+typedef struct {
+	uint8_t	type;
+	uint8_t	offset;
+} fwts_dmi_used_by_kernel;
+
+static bool smbios30_found = false;
+
+/*
+ *  Table derived by scanning thousands of DMI table dumps from bug reports
+ */
+static const fwts_dmi_pattern dmi_patterns[] = {
+	{ "DMISerialNumber",	"Serial Number",	"0000000" },
+	{ "DMISerialNumber",	"Serial Number",	"00000000" },
+	{ "DMISerialNumber",	"Serial Number",	"000000000" },
+	{ "DMISerialNumber",	"Serial Number",	"0000000000" },
+	{ "DMISerialNumber",	"Serial Number",	"0x00000000" },
+	{ "DMISerialNumber",	"Serial Number",	"0x0000000000000000" },
+	{ "DMISerialNumber",	"Serial Number",	"012345678" },
+	{ "DMISerialNumber",	"Serial Number",	"0123456789" },
+	{ "DMISerialNumber",	"Serial Number",	"01234567890" },
+	{ "DMISerialNumber",	"Serial Number",	"012345678900" },
+	{ "DMISerialNumber",	"Serial Number",	"0123456789000" },
+	{ "DMISerialNumber",	"Serial Number",	"System Serial Number" },
+	{ "DMISerialNumber",	"Serial Number",	"MB-1234567890" },
+	{ "DMISerialNumber",	"Serial Number",	"NB-1234567890" },
+	{ "DMISerialNumber",	"Serial Number",	"NB-0123456789" },
+	{ "DMISerialNumber",	"Serial Number",	"Base Board Serial Number" },
+	{ "DMISerialNumber",	"Serial Number",	"Chassis Serial Number" },
+	{ "DMISerialNumber",	"Serial Number",	"<cut out>" },
+	{ "DMISerialNumber",	"Serial Number",	"Empty" },
+	{ "DMISerialNumber",	"Serial Number",	"[Empty]" },
+	{ "DMISerialNumber",	"Serial Number",	"NA" },
+	{ "DMISerialNumber",	"Serial Number",	"N/A" },
+	{ "DMISerialNumber",	"Serial Number",	"None" },
+	{ "DMISerialNumber",	"Serial Number",	"None1" },
+	{ "DMISerialNumber",	"Serial Number",	"Not Available" },
+	{ "DMISerialNumber",	"Serial Number",	"Not Specified" },
+	{ "DMISerialNumber",	"Serial Number",	"NotSupport" },
+	{ "DMISerialNumber",	"Serial Number",	"Not Supported by CPU" },
+	{ "DMISerialNumber",	"Serial Number",	"Not Supported" },
+	{ "DMISerialNumber",	"Serial Number",	"OEM Chassis Serial Number" },
+	{ "DMISerialNumber",	"Serial Number",	"OEM_Define1" },
+	{ "DMISerialNumber",	"Serial Number",	"OEM_Define2" },
+	{ "DMISerialNumber",	"Serial Number",	"OEM_Define3" },
+	{ "DMISerialNumber",	"Serial Number",	"SerNum0" },
+	{ "DMISerialNumber",	"Serial Number",	"SerNum00" },
+	{ "DMISerialNumber",	"Serial Number",	"SerNum01" },
+	{ "DMISerialNumber",	"Serial Number",	"SerNum02" },
+	{ "DMISerialNumber",	"Serial Number",	"SerNum03" },
+	{ "DMISerialNumber",	"Serial Number",	"SerNum1" },
+	{ "DMISerialNumber",	"Serial Number",	"SerNum2" },
+	{ "DMISerialNumber",	"Serial Number",	"SerNum3" },
+	{ "DMISerialNumber",	"Serial Number",	"SerNum4" },
+	{ "DMISerialNumber",	"Serial Number",	"TBD by ODM" },
+	{ "DMISerialNumber",	"Serial Number",	"To Be Defined By O.E.M" },
+	{ "DMISerialNumber",	"Serial Number",	"To be filled by O.E.M." },
+	{ "DMISerialNumber",	"Serial Number",	"To Be Filled By O.E.M." },
+	{ "DMISerialNumber",	"Serial Number",	"Unknow" },
+	{ "DMISerialNumber",	"Serial Number",	"Unknown" },
+	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXXXXX" },
+	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXXXX" },
+	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXXX" },
+	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXXX" },
+	{ "DMISerialNumber",	"Serial Number",	"XXXXXXXX" },
+	{ "DMISerialNumber",	"Serial Number",	"XXXXXX" },
+	{ "DMISerialNumber",	"Serial Number",	"XXXXX" },
+	{ "DMISerialNumber",	NULL,			"Chassis Serial Number" },
+	{ "DMIAssetTag",	"Asset Tag",		"0000000000" },
+	{ "DMIAssetTag",	"Asset Tag",		"0x00000000" },
+	{ "DMIAssetTag",	"Asset Tag",		"1234567890" },
+	{ "DMIAssetTag",	"Asset Tag",		"123456789000" },
+	{ "DMIAssetTag",	"Asset Tag",		"9876543210" },
+	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum0" },
+	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum1" },
+	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum2" },
+	{ "DMIAssetTag",	"Asset Tag",		"A1_AssetTagNum3" },
+	{ "DMIAssetTag",	"Asset Tag",		"ABCDEFGHIJKLM" },
+	{ "DMIAssetTag",	"Asset Tag",		"Asset-1234567890" },
+	{ "DMIAssetTag",	"Asset Tag",		"Asset Tag" },
+	{ "DMIAssetTag",	"Asset Tag",		"Asset Tag:" },
+	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum0" },
+	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum1" },
+	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum2" },
+	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum3" },
+	{ "DMIAssetTag",	"Asset Tag",		"AssetTagNum4" },
+	{ "DMIAssetTag",	"Asset Tag",		"Asset tracking" },
+	{ "DMIAssetTag",	"Asset Tag",		"ATN12345678901234567" },
+	{ "DMIAssetTag",	"Asset Tag",		"Base Board Asset Tag#" },
+	{ "DMIAssetTag",	"Asset Tag",		"Base Board Asset Tag" },
+	{ "DMIAssetTag",	"Asset Tag",		"Chassis Asset Tag" },
+	{ "DMIAssetTag",	"Asset Tag",		"<cut out>" },
+	{ "DMIAssetTag",	"Asset Tag",		"Fill By OEM" },
+	{ "DMIAssetTag",	"Asset Tag",		"N/A" },
+	{ "DMIAssetTag",	"Asset Tag",		"No Asset Information" },
+	{ "DMIAssetTag",	"Asset Tag",		"No Asset Tag" },
+	{ "DMIAssetTag",	"Asset Tag",		"None" },
+	{ "DMIAssetTag",	"Asset Tag",		"Not Available" },
+	{ "DMIAssetTag",	"Asset Tag",		"Not Specified" },
+	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define0" },
+	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define1" },
+	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define2" },
+	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define3" },
+	{ "DMIAssetTag",	"Asset Tag",		"OEM_Define4" },
+	{ "DMIAssetTag",	"Asset Tag",		"TBD by ODM" },
+	{ "DMIAssetTag",	"Asset Tag",		"To Be Defined By O.E.M" },
+	{ "DMIAssetTag",	"Asset Tag",		"To be filled by O.E.M." },
+	{ "DMIAssetTag",	"Asset Tag",		"To Be Filled By O.E.M." },
+	{ "DMIAssetTag",	"Asset Tag",		"Unknown" },
+	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXXXXX" },
+	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXXXX" },
+	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXXX" },
+	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXXX" },
+	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXXX" },
+	{ "DMIAssetTag",	"Asset Tag",		"XXXXXXX" },
+	{ "DMIAssetTag",	"Asset Tag",		"XXXXXX" },
+	{ "DMIChassisVendor",	NULL,			"Chassis Manufacture" },
+	{ "DMIChassisVersion",	NULL,			"Chassis Version" },
+	{ "DMIProductVersion",	NULL,			"System Version" },
+	{ "DMIBadDefault",	NULL,			"To Be Defined By O.E.M" },
+	{ "DMIBadDefault",	NULL,			"To be filled by O.E.M." },
+	{ "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 },
+	{ "Other",		FWTS_SMBIOS_CHASSIS_OTHER },
+	{ "Unknown",		FWTS_SMBIOS_CHASSIS_UNKNOWN },
+	{ "Desktop",		FWTS_SMBIOS_CHASSIS_DESKTOP },
+	{ "Low Profile Desktop",FWTS_SMBIOS_CHASSIS_LOW_PROFILE_DESKTOP },
+	{ "Pizza Box",		FWTS_SMBIOS_CHASSIS_PIZZA_BOX },
+	{ "Mini Tower",		FWTS_SMBIOS_CHASSIS_MINI_TOWER },
+	{ "Chassis Tower",	FWTS_SMBIOS_CHASSIS_TOWER },
+	{ "Portable",		FWTS_SMBIOS_CHASSIS_PORTABLE },
+	{ "Laptop",		FWTS_SMBIOS_CHASSIS_LAPTOP },
+	{ "Notebook",		FWTS_SMBIOS_CHASSIS_NOTEBOOK },
+	{ "Handheld",		FWTS_SMBIOS_CHASSIS_HANDHELD },
+	{ "Docking Station",	FWTS_SMBIOS_CHASSIS_DOCKING_STATION },
+	{ "All In One",		FWTS_SMBIOS_CHASSIS_ALL_IN_ONE },
+	{ "Sub Notebook",	FWTS_SMBIOS_CHASSIS_SUB_NOTEBOOK },
+	{ "Space Saving",	FWTS_SMBIOS_CHASSIS_SPACE_SAVING },
+	{ "Lunch Box",		FWTS_SMBIOS_CHASSIS_LUNCH_BOX},
+	{ "Server Chassis",	FWTS_SMBIOS_CHASSIS_MAIN_SERVER_CHASSIS },
+	{ "Expansion Chassis",	FWTS_SMBIOS_CHASSIS_EXPANISON_CHASSIS },
+	{ "Sub Chassis",	FWTS_SMBIOS_CHASSIS_SUB_CHASSIS },
+	{ "Bus Expansion Chassis", FWTS_SMBIOS_CHASSIS_BUS_EXPANSION_CHASSIS },
+	{ "Peripheral Chassis",	FWTS_SMBIOS_CHASSIS_PERIPHERAL_CHASSIS },
+	{ "Raid Chassis",	FWTS_SMBIOS_CHASSIS_RAID_CHASSIS },
+	{ "Rack Mount Chassis",	FWTS_SMBIOS_CHASSIS_RACK_MOUNT_CHASSIS },
+	{ "Sealed Case PC",	FWTS_SMBIOS_CHASSIS_SEALED_CASE_PC },
+	{ "Multi System Chassis",FWTS_SMBIOS_CHASSIS_MULTI_SYSTEM_CHASSIS },
+	{ "Compact PCI",	FWTS_SMBIOS_CHASSIS_COMPACT_PCI },
+	{ "Advanced TCA",	FWTS_SMBIOS_CHASSIS_ADVANCED_TCA },
+	{ "Blade",		FWTS_SMBIOS_CHASSIS_BLADE },
+	{ "Enclosure",		FWTS_SMBIOS_CHASSIS_BLADE_ENCLOSURE },
+	{ "Tablet",		FWTS_SMBIOS_CHASSIS_TABLET },
+	{ "Convertible",	FWTS_SMBIOS_CHASSIS_CONVERTIBLE },
+	{ "Detachable",		FWTS_SMBIOS_CHASSIS_DETACHABLE },
+};
+
+/* 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
+#define ANCHOR_SIZE 8
+
+/*
+ *  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 int dmi_load_file(const char* filename, void *buf, size_t size)
+{
+	int fd;
+	ssize_t ret;
+
+	if ((fd = open(filename, O_RDONLY)) < 0)
+		return FWTS_ERROR;
+	ret = read(fd, buf, size);
+	(void)close(fd);
+	if (ret != (ssize_t)size)
+		return FWTS_ERROR;
+	return FWTS_OK;
+}
+
+static void* dmi_table_smbios30(fwts_framework *fw, fwts_smbios30_entry *entry)
+{
+	size_t length = (size_t)entry->struct_table_max_size;
+	void *table;
+	char anchor[ANCHOR_SIZE];
+
+	/* 64 bit entry sanity check on length */
+	if ((length == 0) || (length > 0xffffff)) {
+		fwts_log_info(fw, "SMBIOS table size of %zu bytes looks "
+			"suspicious",  length);
+		return NULL;
+	}
+
+	if (dmi_load_file("/sys/firmware/dmi/tables/smbios_entry_point", anchor, sizeof("_SM3_")) == FWTS_OK
+			&& strncmp(anchor, "_SM3_", sizeof("_SM3_")) == 0) {
+		table = malloc(length);
+		if (!table)
+			return NULL;
+		if (dmi_load_file("/sys/firmware/dmi/tables/DMI", table, length) == FWTS_OK) {
+			fwts_log_info(fw, "SMBIOS30 table loaded from /sys/firmware/dmi/tables/DMI\n");
+			return table;
+		}
+		free(table);
+	}
+
+	fwts_log_error(fw, "Cannot mmap SMBIOS 3.0 table from %16.16" PRIx64 "..%16.16" PRIx64 ".",
+			entry->struct_table_address, entry->struct_table_address + entry->struct_table_max_size);
+	return NULL;
+}
+
+static void dmi_table_free(void* table)
+{
+	if (table)
+		free(table);
+}
+
+
+static void dmi_dump_entry30(fwts_framework *fw, fwts_smbios30_entry *entry)
+{
+
+	fwts_log_info_verbatim(fw, "SMBIOS30 Entry Point Structure:");
+	fwts_log_info_verbatim(fw, "  Anchor String          : %5.5s", entry->signature);
+	fwts_log_info_verbatim(fw, "  Checksum               : 0x%2.2" PRIx8, entry->checksum);
+	fwts_log_info_verbatim(fw, "  Entry Point Length     : 0x%2.2" PRIx8, entry->length);
+	fwts_log_info_verbatim(fw, "  Major Version          : 0x%2.2" PRIx8, entry->major_version);
+	fwts_log_info_verbatim(fw, "  Minor Version          : 0x%2.2" PRIx8, entry->minor_version);
+	fwts_log_info_verbatim(fw, "  Docrev                 : 0x%2.2" PRIx8, entry->docrev);
+	fwts_log_info_verbatim(fw, "  Entry Point Revision   : 0x%2.2" PRIx8, entry->revision);
+	fwts_log_info_verbatim(fw, "  Reserved               : 0x%2.2" PRIx8, entry->reserved);
+	fwts_log_info_verbatim(fw, "  Table maximum size     : 0x%8.8" PRIx32, entry->struct_table_max_size);
+	fwts_log_info_verbatim(fw, "  Table address          : 0x%16.16" PRIx64, entry->struct_table_address);
+
+}
+
+static int dmi_smbios30_sane(fwts_framework *fw, fwts_smbios30_entry *entry)
+{
+	uint8_t	*table, *ptr;
+	uint16_t i = 0;
+	uint32_t table_length = entry->struct_table_max_size;
+	int ret = FWTS_OK;
+
+	ptr = table = dmi_table_smbios30(fw, entry);
+	if (table == NULL)
+		return FWTS_ERROR;
+
+	for (;;) {
+		uint8_t struct_length;
+		uint8_t struct_type;
+
+		if (ptr > table + table_length) {
+			fwts_failed(fw, LOG_LEVEL_MEDIUM,
+				"SMBIOS30TableLengthTooSmall",
+				"The maximum size indicated by the SMBIOS 3.0 table length is "
+				"smaller than the dmi data or the DMI end of table not found.");
+			ret = FWTS_ERROR;
+			break;
+		}
+
+		struct_type = ptr[0];
+		struct_length = ptr[1];
+
+		if (struct_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 += struct_length;
+
+		/* Scan for end of DMI entry, must be 2 zero bytes */
+		while (((ptr - table + 1) < (ssize_t)table_length) &&
+		       ((ptr[0] != 0) || (ptr[1] != 0)))
+				ptr++;
+		/* Skip over the two zero bytes */
+		ptr += 2;
+
+		/* We found DMI end of table and inside the maximum length? */
+		if (struct_type == 127) {
+			if (ptr <= table + table_length)
+				break;
+			else {
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"SMBIOS30TableLengthTooSmall",
+					"The end of DMI table marker structure was found "
+					"but outside the structure table maximum size");
+				ret = FWTS_ERROR;
+				break;
+			}
+		}
+	}
+
+	dmi_table_free(table);
+
+	return ret;
+}
+
+static int smbios30_entry_check(fwts_framework *fw)
+{
+	void *addr = 0;
+
+	fwts_smbios30_entry entry;
+	uint16_t version;
+	uint8_t checksum;
+
+	if ((addr = fwts_smbios30_find_entry(fw, &entry, &version)) == NULL)
+		return FWTS_ERROR;
+
+	fwts_passed(fw, "Found SMBIOS30 Table Entry Point at %p", addr);
+	dmi_dump_entry30(fw, &entry);
+	fwts_log_nl(fw);
+
+	checksum = fwts_checksum((uint8_t *)&entry, sizeof(fwts_smbios30_entry));
+	if (checksum != 0)
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"SMBIOS30BadChecksum",
+			"SMBIOS30 Table Entry Point Checksum is 0x%2.2" PRIx8
+			", should be 0x%2.2" PRIx8,
+			entry.checksum, (uint8_t)(entry.checksum - checksum));
+	else
+		fwts_passed(fw, "SMBIOS30 Table Entry Point Checksum is valid.");
+
+	if (entry.length != 0x18) {
+		fwts_failed(fw, LOG_LEVEL_MEDIUM,
+			"SMBIOS30BadEntryLength",
+			"SMBIOS30 Table Entry Point Length is 0x%2.2"  PRIx8
+			", should be 0x18", entry.length);
+	} else
+		fwts_passed(fw, "SMBIOS30 Table Entry Point Length is valid.");
+
+	if (entry.reserved)
+		fwts_failed(fw, LOG_LEVEL_MEDIUM,
+			"SMBIOSBadReserved",
+			"SMBIOS30 Table Entry Point Reserved is 0x%2.2" PRIx8
+			", should be 0", entry.reserved);
+
+	if ((entry.revision == 1) && (entry.struct_table_address == 0)) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"SMBIOS30BadTableAddress",
+			"SMBIOS Table Entry Structure Table Address is NULL and should be defined.");
+	} else {
+		/*
+		 * Now does the SMBIOS 3.0.0 table look sane? If not,
+		 * the SMBIOS Structure Table could be bad
+		 */
+		if (dmi_smbios30_sane(fw, &entry) == FWTS_OK)
+			fwts_passed(fw, "SMBIOS 3.0 Table Entry Structure Table Address and Length looks valid.");
+	}
+
+	return FWTS_OK;
+
+}
+
+static int sbbr_dmicheck_revision_test(fwts_framework *fw)
+{
+
+	if (smbios30_entry_check(fw) != FWTS_ERROR) {
+		smbios30_found = true;
+	}
+
+	if (!smbios30_found) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"SMBIOSNoEntryPoint",
+			"Could not find any SMBIOS Table Entry Points.");
+		return FWTS_ERROR;
+	}
+
+	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 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 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;
+	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 (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]);
+				break;
+			}
+
+			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 0x01..0x0d) "
+								"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, 0x30);
+			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);
+			if (hdr->length < 0x28)
+				break;
+			if (GET_UINT16(data + 0x26) & 0xff00)
+				fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_RESERVED_VALUE_USED,
+					"Reserved bits 0x%4.4" PRIx16 " was used"
+					"bits 8..15 would be reserved while accessing entry '%s' @ "
+					"0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
+					GET_UINT16(data + 0x26),
+					table, addr, "Processor Characteristics", 0x26);
+			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] <= 0x20)) ||
+			      ((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, 0x6);
+			if (hdr->length < 0x0d)
+				break;
+			if (data[0xc] & 0xf8)
+				fwts_failed(fw, LOG_LEVEL_MEDIUM, DMI_RESERVED_VALUE_USED,
+					"Reserved bits 0x%2.2" PRIx8 " was used"
+					"bits 3..7 would be reserved while accessing entry '%s' @ "
+					"0x%8.8" PRIx32 ", field '%s', offset 0x%2.2x",
+					data[0xc],
+					table, addr, "Slot Characteristics 2", 0xc);
+			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 *tmpptr = data + 0x17;
+				int k;
+				for (k = 0; k < data[0x15]; k++) {
+					int j = data[0x16] * k;
+					val = tmpptr[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", k);
+					}
+					val = tmpptr[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", k);
+					}
+				}
+			}
+			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, 0x1e);
+			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, 0x3);
+			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 void dmi_scan_smbios30_table(fwts_framework *fw,
+	fwts_smbios30_entry *entry,
+	uint8_t  *table)
+{
+	uint8_t *entry_data = table;
+	uint16_t table_max_length;
+	int i = 0;
+
+	table_max_length = entry->struct_table_max_size;
+
+	for (i = 0; entry_data <= (table + table_max_length - 4); i++) {
+		uint64_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;
+
+		/* We found DMI end of table */
+		if (hdr.type == SMBIOS_END_OF_TABLE)
+			break;
+
+		/* 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_max_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_max_length)
+			dmicheck_entry(fw, addr, &hdr);
+		else {
+			fwts_failed(fw, LOG_LEVEL_HIGH, DMI_BAD_TABLE_LENGTH,
+				"DMI table maximum size was %" PRId32 " bytes (as specified by "
+				"the SMBIOS 3.0 header) but the DMI entries over the maximum "
+				"length without finding the End-of-Table(Type 127).",
+				table_max_length);
+			break;
+		}
+
+		entry_data = next_entry;
+	}
+
+}
+
+static int sbbr_dmicheck_consistency_test(fwts_framework *fw)
+{
+	void *addr;
+	fwts_smbios30_entry entry30;
+	uint16_t version = 0;
+	uint8_t  *table;
+
+	if (!smbios30_found) {
+		fwts_skipped(fw, "Cannot find SMBIOS30 table entry, skip the test.");
+		return FWTS_SKIP;
+	}
+
+	addr = fwts_smbios30_find_entry(fw, &entry30, &version);
+	if (addr == NULL) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, DMI_NO_TABLE_HEADER,
+			"Cannot find SMBIOS 3.0 table entry.");
+		return FWTS_ERROR;
+	}
+
+	table = dmi_table_smbios30(fw, &entry30);
+	if (table == NULL)
+		return FWTS_ERROR;
+
+	dmi_scan_smbios30_table(fw, &entry30, table);
+
+	dmi_table_free(table);
+
+	return FWTS_OK;
+}
+
+/*
+ * ARM SBBR SMBIOS Structure Test
+ */
+
+/* Test Entry Structure */
+typedef struct {
+	const char *name;
+	const uint8_t type;
+} sbbr_test_entry;
+
+/* Test Definition Array */
+sbbr_test_entry sbbr_test[] = {
+	{"BIOS Information", 0},
+	{"System Information", 1},
+	{"Baseboard Information", 2},
+	{"System Enclosure or Chassis", 3},
+	{"Processor Information", 4},
+	{"Cache Information", 7},
+	{"Port Connector Information", 8},
+	{"System Slots", 9},
+	{"OEM Strings", 11},
+	{"BIOS Language Information", 13},
+	{"System Event Log", 15},
+	{"Physical Memory Array", 16},
+	{"Memory Device", 17},
+	{"Memory Array Mapped Address", 19},
+	{"System Boot Information", 32},
+	{"IPMI Device Information", 38},
+	{"Onboard Devices Extended Information", 41},
+	{0, 0}
+};
+
+/* Finds SMBIOS structure of a given type in an SMBIOS30 table. */
+static uint8_t * sbbr_smbios30_locate_structure (uint8_t *table, const int table_max_length, uint8_t type)
+{
+	uint8_t *entry_data;
+	fwts_dmi_header *hdr;
+
+
+	entry_data = table;
+    while ( (entry_data != NULL) &&
+    		(entry_data < (table + table_max_length)) )
+    {
+    	hdr = (fwts_dmi_header *)(entry_data);
+
+		/* We found the entry we're looking for. */
+		if (hdr->type == type)
+			return hdr->data;
+
+		/* We found DMI end of table */
+		if (hdr->type == SMBIOS_END_OF_TABLE)
+			return NULL;
+
+		entry_data = entry_data + hdr->length;
+
+		while( *((int *)(entry_data)) != 0)
+		{
+			entry_data++;
+
+		}
+		entry_data += 2; /* Increment address to start of next structure */
+
+	}
+
+
+	return NULL;
+}
+
+/* SBBR SMBIOS structure test function. */
+static int sbbr_smbios30_test(fwts_framework *fw)
+{
+	fwts_smbios30_entry entry30;
+	fwts_dmi_header hdr;
+	uint16_t version;
+	void *addr;
+	uint32_t i;
+	uint8_t *table;
+
+	/* Finding SMBIOS30 entry point. */
+	addr = fwts_smbios30_find_entry(fw, &entry30, &version);
+	if (addr == NULL) {
+		fwts_failed(fw, LOG_LEVEL_HIGH, "SbbrSmbiosNoTable", "Cannot find SMBIOS30 table "
+		            "entry.");
+		return FWTS_ERROR;
+	}
+
+	/* Getting SMBIOS table contents. */
+	table = dmi_table_smbios30(fw, &entry30);
+	if (table == NULL)
+		return FWTS_ERROR;
+
+	/* Searching for each SMBIOS structure needed by SBBR. */
+	for (i = 0; sbbr_test[i].name != NULL; i++) {
+
+		hdr.data = sbbr_smbios30_locate_structure (table, entry30.struct_table_max_size, sbbr_test[i].type);
+
+		if (hdr.data != NULL)
+			dmicheck_entry(fw, (uintptr_t)hdr.data, &hdr);
+		else
+			fwts_failed(fw, LOG_LEVEL_HIGH, "SbbrSmbiosNoStruct", "Cannot find SMBIOS "
+			            "structure: %s (Type %d)", sbbr_test[i].name, sbbr_test[i].type);
+	}
+
+	dmi_table_free(table);
+
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test sbbr_dmicheck_tests[] = {
+	{ sbbr_dmicheck_revision_test, "Find and test SMBIOS Table Entry Points." },
+	{ sbbr_dmicheck_consistency_test, "Test DMI/SMBIOS3 tables for errors." },
+	{ sbbr_smbios30_test, "Test ARM SBBR SMBIOS structure requirements."},
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops sbbr_dmicheck_ops = {
+	.description = "DMI/SMBIOS table tests.",
+	.minor_tests = sbbr_dmicheck_tests
+};
+
+FWTS_REGISTER("sbbr_dmicheck", &sbbr_dmicheck_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_TEST_SBBR)
+
+#endif