diff mbox

[1/2] acpi: nfit: add ACPI NFIT test

Message ID 1469616485-2810-1-git-send-email-alex.hung@canonical.com
State Accepted
Headers show

Commit Message

Alex Hung July 27, 2016, 10:48 a.m. UTC
Signed-off-by: Alex Hung <alex.hung@canonical.com>
---
 src/Makefile.am             |   1 +
 src/acpi/nfit/nfit.c        | 327 ++++++++++++++++++++++++++++++++++++++++++++
 src/lib/include/fwts_acpi.h | 110 +++++++++++++++
 3 files changed, 438 insertions(+)
 create mode 100644 src/acpi/nfit/nfit.c

Comments

Colin Ian King July 27, 2016, 12:31 p.m. UTC | #1
On 27/07/16 11:48, Alex Hung wrote:
> Signed-off-by: Alex Hung <alex.hung@canonical.com>
> ---
>  src/Makefile.am             |   1 +
>  src/acpi/nfit/nfit.c        | 327 ++++++++++++++++++++++++++++++++++++++++++++
>  src/lib/include/fwts_acpi.h | 110 +++++++++++++++
>  3 files changed, 438 insertions(+)
>  create mode 100644 src/acpi/nfit/nfit.c
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 11c65b2..bc23e8d 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -73,6 +73,7 @@ fwts_SOURCES = main.c 				\
>  	acpi/msct/msct.c 			\
>  	acpi/msdm/msdm.c 			\
>  	acpi/method/method.c 			\
> +	acpi/nfit/nfit.c 			\
>  	acpi/osilinux/osilinux.c 		\
>  	acpi/pcc/pcc.c 				\
>  	acpi/pcct/pcct.c			\
> diff --git a/src/acpi/nfit/nfit.c b/src/acpi/nfit/nfit.c
> new file mode 100644
> index 0000000..3738a50
> --- /dev/null
> +++ b/src/acpi/nfit/nfit.c
> @@ -0,0 +1,327 @@
> +/*
> + * Copyright (C) 2016 Canonical
> + *
> + * Portions of this code original from the Linux-ready Firmware Developer Kit
> + *
> + * 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 "fwts.h"
> +
> +#if defined(FWTS_HAS_ACPI)
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <inttypes.h>
> +#include <string.h>
> +#include <ctype.h>
> +
> +static fwts_acpi_table_info *table;
> +
> +static int nfit_init(fwts_framework *fw)
> +{
> +	if (fwts_acpi_find_table(fw, "NFIT", 0, &table) != FWTS_OK) {
> +		fwts_log_error(fw, "Cannot read ACPI tables.");
> +		return FWTS_ERROR;
> +	}
> +	if (table == NULL || (table && table->length == 0)) {
> +		fwts_log_error(fw, "ACPI NFIT table does not exist, skipping test");
> +		return FWTS_SKIP;
> +	}
> +	return FWTS_OK;
> +}
> +
> +/*
> + *  NFIT NVDIMM Firmware Interface Table
> + */
> +static int nfit_test1(fwts_framework *fw)
> +{
> +	fwts_acpi_table_nfit *nfit = (fwts_acpi_table_nfit*) table->data;
> +	fwts_acpi_table_nfit_struct_header *entry;
> +	size_t offset;
> +	bool passed = true;
> +
> +	fwts_log_info_verbatim(fw, "NFIT NVDIMM Firmware Interface Table:");
> +	fwts_log_info_verbatim(fw, "  Reserved:                 0x%8.8" PRIx32, nfit->reserved);
> +	fwts_log_nl(fw);
> +
> +	if (nfit->reserved != 0) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_LOW,
> +			"NFITReservedNonZero",
> +			"NFIT reserved field must be zero, got "
> +			"0x%8.8" PRIx32 " instead", nfit->reserved);
> +	}
> +
> +	offset = sizeof(fwts_acpi_table_nfit);
> +	entry = (fwts_acpi_table_nfit_struct_header *) (table->data + offset);
> +
> +	while (offset < table->length) {
> +		uint64_t reserved_passed = 0;
> +
> +		fwts_log_info_verbatim(fw, "  NFIT Subtable:");
> +		fwts_log_info_verbatim(fw, "    Type:                                   0x%4.4" PRIx16, entry->type);
> +		fwts_log_info_verbatim(fw, "    Length:                                 0x%4.4" PRIx16, entry->length);
> +
> +		if (entry->type == FWTS_ACPI_NFIT_TYPE_SYSTEM_ADDRESS) {
> +			fwts_acpi_table_nfit_system_memory *nfit_struct = (fwts_acpi_table_nfit_system_memory *) entry;
> +			char guid_str[37];
> +
> +			fwts_guid_buf_to_str(nfit_struct->range_guid, guid_str, sizeof(guid_str));
> +
> +			fwts_log_info_verbatim(fw, "    SPA Range Structure Index:              0x%4.4" PRIx16, nfit_struct->range_index);
> +			fwts_log_info_verbatim(fw, "    Flags:                                  0x%4.4" PRIx16, nfit_struct->flags);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%8.8" PRIx32, nfit_struct->reserved);
> +			fwts_log_info_verbatim(fw, "    Proximity Domain:                       0x%8.8" PRIx32, nfit_struct->proximity_domain);
> +			fwts_log_info_verbatim(fw, "    Address Range Type GUID:                %s", guid_str);
> +			fwts_log_info_verbatim(fw, "    System Physical Address Range Base:     0x%16.16" PRIx64, nfit_struct->address);
> +			fwts_log_info_verbatim(fw, "    System Physical Address Range Length:   0x%16.16" PRIx64, nfit_struct->length);
> +			fwts_log_info_verbatim(fw, "    Address Range Memory Mapping Attribute: 0x%16.16" PRIx64, nfit_struct->memory_mapping);
> +
> +			if (nfit_struct->range_index == 0) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRangeIndexZero",
> +					"NFIT SPA Range Structure Index must not be zero");
> +			}
> +
> +			if (nfit_struct->flags & ~0x03) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadFlags",
> +					"NFIT Flags's Bits[15..2] must be zero, got "
> +					"0x%8.8" PRIx32 " instead", nfit_struct->flags);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +			/* TODO check Proximity Domain with SRAT table */
> +
> +			if (nfit_struct->memory_mapping & ~0x01F01F) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadMemoryMappingAttribute",
> +					"NFIT Memory Mapping Attribute must meet UEFI Spec, got "
> +					"0x%16.16" PRIx64 " instead", nfit_struct->memory_mapping);
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_MEMORY_MAP) {
> +			fwts_acpi_table_nfit_memory_map *nfit_struct = (fwts_acpi_table_nfit_memory_map *) entry;
> +
> +			fwts_log_info_verbatim(fw, "    NFIT Device Handle:                     0x%8.8" PRIx32, nfit_struct->device_handle);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Physical ID:                     0x%4.4" PRIx16, nfit_struct->physical_id);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Region ID:                       0x%4.4" PRIx16, nfit_struct->region_id);
> +			fwts_log_info_verbatim(fw, "    SPA Range Structure Index:              0x%4.4" PRIx16, nfit_struct->range_index);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Region Size:                     0x%16.16" PRIx64, nfit_struct->region_size);
> +			fwts_log_info_verbatim(fw, "    Region Offset:                          0x%16.16" PRIx64, nfit_struct->region_offset);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Physical Address Region Base:    0x%16.16" PRIx64, nfit_struct->address);
> +			fwts_log_info_verbatim(fw, "    Interleave Structure Index:             0x%4.4" PRIx16, nfit_struct->interleave_index);
> +			fwts_log_info_verbatim(fw, "    Interleave Ways:                        0x%4.4" PRIx16, nfit_struct->interleave_ways);
> +			fwts_log_info_verbatim(fw, "    NVDIMM State Flags:                     0x%4.4" PRIx16, nfit_struct->flags);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
> +
> +			if (nfit_struct->flags & ~0x7F) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadFlags",
> +					"NFIT Flags's Bits[15..7] must be zero, got "
> +					"0x%8.8" PRIx32 " instead", nfit_struct->flags);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_INTERLEAVE) {
> +			fwts_acpi_table_nfit_interleave *nfit_struct = (fwts_acpi_table_nfit_interleave *) entry;
> +			uint32_t i;
> +
> +			fwts_log_info_verbatim(fw, "    Interleave Structure Index:             0x%4.4" PRIx16, nfit_struct->interleave_index);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
> +			fwts_log_info_verbatim(fw, "    Number of Lines Described:              0x%8.8" PRIx32, nfit_struct->line_count);
> +			fwts_log_info_verbatim(fw, "    Line Size:                              0x%8.8" PRIx32, nfit_struct->line_size);
> +
> +			for (i = 0; i < nfit_struct->line_count; i++) {
> +				fwts_log_info_verbatim(fw, "    Line Offset:                            0x%8.8"  PRIx32, nfit_struct->line_offset[i]);
> +
> +				if (nfit_struct->line_offset[i] % nfit_struct->line_size)
> +					fwts_failed(fw, LOG_LEVEL_HIGH,
> +						"NFITBadLineOffsetAlignment",
> +						"NFIT Line Offset must be aligned nfit_struct->line_size, got "
> +						"0x%8.8" PRIx32 " instead", nfit_struct->line_offset[i]);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +			if (nfit_struct->line_count == 0) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadLineCount",
> +					"NFIT Number of Lines must not be zero");
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_SMBIOS) {
> +			fwts_acpi_table_nfit_smbios *nfit_struct = (fwts_acpi_table_nfit_smbios *) entry;
> +
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%8.8" PRIx32, nfit_struct->reserved);
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_CONTROL_REGION) {
> +			fwts_acpi_table_nfit_control_range *nfit_struct = (fwts_acpi_table_nfit_control_range *) entry;
> +			uint64_t reserved1;
> +
> +			reserved1 = (uint64_t) nfit_struct->reserved1[0] + ((uint64_t) nfit_struct->reserved1[1] << 8) +
> +				   ((uint64_t) nfit_struct->reserved1[2] << 16) + ((uint64_t) nfit_struct->reserved1[3] << 24) +
> +				   ((uint64_t) nfit_struct->reserved1[4] << 32) + ((uint64_t) nfit_struct->reserved1[5] << 40);
> +
> +			if (reserved1 != 0)
> +				reserved_passed = reserved1;
> +
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
> +			fwts_log_info_verbatim(fw, "    Vendor ID:                              0x%4.4" PRIx16, nfit_struct->vendor_id);
> +			fwts_log_info_verbatim(fw, "    Device ID:                              0x%4.4" PRIx16, nfit_struct->device_id);
> +			fwts_log_info_verbatim(fw, "    Revision ID:                            0x%4.4" PRIx16, nfit_struct->revision_id);
> +			fwts_log_info_verbatim(fw, "    Subsystem Vendor ID:                    0x%4.4" PRIx16, nfit_struct->subsystem_vendor_id);
> +			fwts_log_info_verbatim(fw, "    Subsystem Device ID:                    0x%4.4" PRIx16, nfit_struct->subsystem_device_id);
> +			fwts_log_info_verbatim(fw, "    Subsystem Revision ID:                  0x%4.4" PRIx16, nfit_struct->subsystem_revision_id);
> +			fwts_log_info_verbatim(fw, "    Valid Fields:                           0x%2.2" PRIx8, nfit_struct->valid_fields);
> +			fwts_log_info_verbatim(fw, "    Manufacturing Location:                 0x%2.2" PRIx8, nfit_struct->manufacturing_location);
> +			fwts_log_info_verbatim(fw, "    Manufacturing Date:                     0x%4.4" PRIx16, nfit_struct->manufacturing_date);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
> +			fwts_log_info_verbatim(fw, "    Serial Number:                          0x%8.8" PRIx32, nfit_struct->serial_number);
> +			fwts_log_info_verbatim(fw, "    Region Format Interface Code:           0x%4.4" PRIx16, nfit_struct->interface_code);
> +			fwts_log_info_verbatim(fw, "    Number of Block Control Windows:        0x%4.4" PRIx16, nfit_struct->windows_num);
> +			fwts_log_info_verbatim(fw, "    Size of Block Control Window:           0x%16.16" PRIx64, nfit_struct->window_size);
> +			fwts_log_info_verbatim(fw, "    Command Register Offset:                0x%16.16" PRIx64, nfit_struct->command_offset);
> +			fwts_log_info_verbatim(fw, "    Size of Command Register:               0x%16.16" PRIx64, nfit_struct->command_size);
> +			fwts_log_info_verbatim(fw, "    Status RegisterOffset:                  0x%16.16" PRIx64, nfit_struct->status_offset);
> +			fwts_log_info_verbatim(fw, "    Size of Status Register:                0x%16.16" PRIx64, nfit_struct->status_size);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Flag:             0x%4.4" PRIx16, nfit_struct->flags);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%16.16" PRIx64, reserved1);
> +
> +			if (nfit_struct->revision_id & 0xFF00) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRevisionId",
> +					"NFIT Revision ID's BYTE 1 must be zero, got "
> +					"0x%4.4" PRIx16 " instead", nfit_struct->revision_id);
> +			}
> +
> +			if (nfit_struct->subsystem_revision_id & 0xFF00) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRevisionId",
> +					"NFIT Subsystem Revision ID's BYTE 1 must be zero, got "
> +					"0x%4.4" PRIx16 " instead", nfit_struct->subsystem_revision_id);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +			if (nfit_struct->valid_fields & ~0x01) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadValidField",
> +					"NFIT Valid Field's Bits[7..1] must be zero, got "
> +					"0x%2.2" PRIx8 " instead", nfit_struct->valid_fields);
> +			}
> +
> +			if (nfit_struct->flags & ~0x01) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadFlags",
> +					"NFIT Flags's Bits[15..1] must be zero, got "
> +					"0x%4.4" PRIx16 " instead", nfit_struct->flags);
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_DATA_REGION) {
> +			fwts_acpi_table_nfit_data_range *nfit_struct = (fwts_acpi_table_nfit_data_range *) entry;
> +
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
> +			fwts_log_info_verbatim(fw, "    Number of Block Data Windows:           0x%4.4" PRIx16, nfit_struct->window_num);
> +			fwts_log_info_verbatim(fw, "    Block Data Window Start Offset:         0x%16.16" PRIx64, nfit_struct->window_offset);
> +			fwts_log_info_verbatim(fw, "    Size of Block Data Window:              0x%16.16" PRIx64, nfit_struct->window_size);
> +			fwts_log_info_verbatim(fw, "    NBlock Accessible Memory Capacity:      0x%16.16" PRIx64, nfit_struct->capacity);
> +			fwts_log_info_verbatim(fw, "    Beginning address of First Block:       0x%16.16" PRIx64, nfit_struct->start_address);
> +
> +			if (nfit_struct->region_index == 0) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRegionIndex",
> +					"NFIT NVDIMM Control Region Structure Index must not be zero");
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_FLUSH_ADDRESS) {
> +			fwts_acpi_table_nfit_flush_addr *nfit_struct = (fwts_acpi_table_nfit_flush_addr *) entry;
> +			uint64_t reserved;
> +			uint16_t i;
> +
> +			reserved = (uint64_t) nfit_struct->reserved[0] + ((uint64_t) nfit_struct->reserved[1] << 8) +
> +				   ((uint64_t) nfit_struct->reserved[2] << 16) + ((uint64_t) nfit_struct->reserved[3] << 24) +
> +				   ((uint64_t) nfit_struct->reserved[4] << 32) + ((uint64_t) nfit_struct->reserved[5] << 40);
> +
> +			fwts_log_info_verbatim(fw, "    NFIT Device Handle:                     0x%8.8" PRIx32, nfit_struct->device_handle);
> +			fwts_log_info_verbatim(fw, "    Number of Flush Hint Addresses:         0x%4.4" PRIx16, nfit_struct->hint_count);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%16.16" PRIx64, reserved);
> +			for (i = 0; i < nfit_struct->hint_count; i++)
> +				fwts_log_info_verbatim(fw, "    Flush Hint Address:                     0x%16.16" PRIx64, nfit_struct->hint_address[i]);
> +
> +			if (reserved != 0)
> +				reserved_passed = reserved;
> +
> +		} else {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"NFITBadSubType",
> +				"NFIT Structure supports type 0..6, got "
> +				"0x%4.4" PRIx16 " instead", entry->type);
> +		}
> +
> +		if (reserved_passed != 0) {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_LOW,
> +				"NFITReservedNonZero",
> +				"NFIT reserved field must be zero, got "
> +				"0x%16.16" PRIx64 " instead", reserved_passed);
> +		}
> +
> +		fwts_log_nl(fw);
> +		offset += entry->length;
> +		entry = (fwts_acpi_table_nfit_struct_header *) (table->data + offset);
> +	}
> +
> +
> +	if (passed)
> +		fwts_passed(fw, "No issues found in NFIT table.");
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test nfit_tests[] = {
> +	{ nfit_test1, "NFIT NVDIMM Firmware Interface Table test." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops nfit_ops = {
> +	.description = "NFIT NVDIMM Firmware Interface Table test.",
> +	.init        = nfit_init,
> +	.minor_tests = nfit_tests
> +};
> +
> +FWTS_REGISTER("nfit", &nfit_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_TEST_ACPI)
> +
> +#endif
> diff --git a/src/lib/include/fwts_acpi.h b/src/lib/include/fwts_acpi.h
> index b675f4f..fd5a6b0 100644
> --- a/src/lib/include/fwts_acpi.h
> +++ b/src/lib/include/fwts_acpi.h
> @@ -991,6 +991,116 @@ typedef struct {
>  } __attribute__ ((packed)) fwts_acpi_table_rasf;
>  
>  /*
> + * ACPI NFIT (NVDIMM Firmware Interface), 5.2.25
> + */
> +typedef struct {
> +	fwts_acpi_table_header	header;
> +	uint32_t	reserved;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit;
> +
> +typedef struct {
> +	uint16_t		type;
> +	uint16_t		length;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_struct_header;
> +
> +typedef enum {
> +	FWTS_ACPI_NFIT_TYPE_SYSTEM_ADDRESS       = 0,
> +	FWTS_ACPI_NFIT_TYPE_MEMORY_MAP           = 1,
> +	FWTS_ACPI_NFIT_TYPE_INTERLEAVE           = 2,
> +	FWTS_ACPI_NFIT_TYPE_SMBIOS               = 3,
> +	FWTS_ACPI_NFIT_TYPE_CONTROL_REGION       = 4,
> +	FWTS_ACPI_NFIT_TYPE_DATA_REGION          = 5,
> +	FWTS_ACPI_NFIT_TYPE_FLUSH_ADDRESS        = 6,
> +	FWTS_ACPI_NFIT_TYPE_RESERVED             = 7     /* >= 7 are reserved */
> +} fwts_acpi_nfit_type;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	range_index;
> +	uint16_t	flags;
> +	uint32_t	reserved;
> +	uint32_t	proximity_domain;
> +	uint8_t		range_guid[16];
> +	uint64_t	address;
> +	uint64_t	length;
> +	uint64_t	memory_mapping;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_system_memory;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint32_t	device_handle;
> +	uint16_t	physical_id;
> +	uint16_t	region_id;
> +	uint16_t	range_index;
> +	uint16_t	region_index;
> +	uint64_t	region_size;
> +	uint64_t	region_offset;
> +	uint64_t	address;
> +	uint16_t	interleave_index;
> +	uint16_t	interleave_ways;
> +	uint16_t	flags;
> +	uint16_t	reserved;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_memory_map;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	interleave_index;
> +	uint16_t	reserved;
> +	uint32_t	line_count;
> +	uint32_t	line_size;
> +	uint32_t   line_offset[];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_interleave;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint32_t	reserved;
> +	uint8_t		smbios[];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_smbios;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	region_index;
> +	uint16_t	vendor_id;
> +	uint16_t	device_id;
> +	uint16_t	revision_id;
> +	uint16_t	subsystem_vendor_id;
> +	uint16_t	subsystem_device_id;
> +	uint16_t	subsystem_revision_id;
> +	uint8_t		valid_fields;
> +	uint8_t		manufacturing_location;
> +	uint16_t	manufacturing_date;
> +	uint16_t	reserved;
> +	uint32_t	serial_number;
> +	uint16_t	interface_code;
> +	uint16_t	windows_num;
> +	uint64_t	window_size;
> +	uint64_t	command_offset;
> +	uint64_t	command_size;
> +	uint64_t	status_offset;
> +	uint64_t	status_size;
> +	uint16_t	flags;
> +	uint8_t		reserved1[6];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_control_range;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	region_index;
> +	uint16_t	window_num;
> +	uint64_t	window_offset;
> +	uint64_t	window_size;
> +	uint64_t	capacity;
> +	uint64_t	start_address;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_data_range;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint32_t	device_handle;
> +	uint16_t	hint_count;
> +	uint8_t		reserved[6];
> +	uint64_t	hint_address[];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_flush_addr;
> +
> +/*
>   * ACPI PCCT (Platform Communications Channel Table), 14.1
>   */
>  typedef struct {
> 
Acked-by: Colin Ian King <colin.king@canonical.com>
Ivan Hu Aug. 3, 2016, 3:57 a.m. UTC | #2
On 2016年07月27日 18:48, Alex Hung wrote:
> Signed-off-by: Alex Hung <alex.hung@canonical.com>
> ---
>  src/Makefile.am             |   1 +
>  src/acpi/nfit/nfit.c        | 327 ++++++++++++++++++++++++++++++++++++++++++++
>  src/lib/include/fwts_acpi.h | 110 +++++++++++++++
>  3 files changed, 438 insertions(+)
>  create mode 100644 src/acpi/nfit/nfit.c
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 11c65b2..bc23e8d 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -73,6 +73,7 @@ fwts_SOURCES = main.c 				\
>  	acpi/msct/msct.c 			\
>  	acpi/msdm/msdm.c 			\
>  	acpi/method/method.c 			\
> +	acpi/nfit/nfit.c 			\
>  	acpi/osilinux/osilinux.c 		\
>  	acpi/pcc/pcc.c 				\
>  	acpi/pcct/pcct.c			\
> diff --git a/src/acpi/nfit/nfit.c b/src/acpi/nfit/nfit.c
> new file mode 100644
> index 0000000..3738a50
> --- /dev/null
> +++ b/src/acpi/nfit/nfit.c
> @@ -0,0 +1,327 @@
> +/*
> + * Copyright (C) 2016 Canonical
> + *
> + * Portions of this code original from the Linux-ready Firmware Developer Kit

minor: just wondering which part is from Linux-ready Firmware Developer Kit?

> + *
> + * 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 "fwts.h"
> +
> +#if defined(FWTS_HAS_ACPI)
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <inttypes.h>
> +#include <string.h>
> +#include <ctype.h>
> +
> +static fwts_acpi_table_info *table;
> +
> +static int nfit_init(fwts_framework *fw)
> +{
> +	if (fwts_acpi_find_table(fw, "NFIT", 0, &table) != FWTS_OK) {
> +		fwts_log_error(fw, "Cannot read ACPI tables.");
> +		return FWTS_ERROR;
> +	}
> +	if (table == NULL || (table && table->length == 0)) {
> +		fwts_log_error(fw, "ACPI NFIT table does not exist, skipping test");
> +		return FWTS_SKIP;
> +	}
> +	return FWTS_OK;
> +}
> +
> +/*
> + *  NFIT NVDIMM Firmware Interface Table
> + */
> +static int nfit_test1(fwts_framework *fw)
> +{
> +	fwts_acpi_table_nfit *nfit = (fwts_acpi_table_nfit*) table->data;
> +	fwts_acpi_table_nfit_struct_header *entry;
> +	size_t offset;
> +	bool passed = true;
> +
> +	fwts_log_info_verbatim(fw, "NFIT NVDIMM Firmware Interface Table:");
> +	fwts_log_info_verbatim(fw, "  Reserved:                 0x%8.8" PRIx32, nfit->reserved);
> +	fwts_log_nl(fw);
> +
> +	if (nfit->reserved != 0) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_LOW,
> +			"NFITReservedNonZero",
> +			"NFIT reserved field must be zero, got "
> +			"0x%8.8" PRIx32 " instead", nfit->reserved);
> +	}
> +
> +	offset = sizeof(fwts_acpi_table_nfit);
> +	entry = (fwts_acpi_table_nfit_struct_header *) (table->data + offset);
> +
> +	while (offset < table->length) {
> +		uint64_t reserved_passed = 0;
> +
> +		fwts_log_info_verbatim(fw, "  NFIT Subtable:");
> +		fwts_log_info_verbatim(fw, "    Type:                                   0x%4.4" PRIx16, entry->type);
> +		fwts_log_info_verbatim(fw, "    Length:                                 0x%4.4" PRIx16, entry->length);
> +
> +		if (entry->type == FWTS_ACPI_NFIT_TYPE_SYSTEM_ADDRESS) {
> +			fwts_acpi_table_nfit_system_memory *nfit_struct = (fwts_acpi_table_nfit_system_memory *) entry;
> +			char guid_str[37];
> +
> +			fwts_guid_buf_to_str(nfit_struct->range_guid, guid_str, sizeof(guid_str));
> +
> +			fwts_log_info_verbatim(fw, "    SPA Range Structure Index:              0x%4.4" PRIx16, nfit_struct->range_index);
> +			fwts_log_info_verbatim(fw, "    Flags:                                  0x%4.4" PRIx16, nfit_struct->flags);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%8.8" PRIx32, nfit_struct->reserved);
> +			fwts_log_info_verbatim(fw, "    Proximity Domain:                       0x%8.8" PRIx32, nfit_struct->proximity_domain);
> +			fwts_log_info_verbatim(fw, "    Address Range Type GUID:                %s", guid_str);
> +			fwts_log_info_verbatim(fw, "    System Physical Address Range Base:     0x%16.16" PRIx64, nfit_struct->address);
> +			fwts_log_info_verbatim(fw, "    System Physical Address Range Length:   0x%16.16" PRIx64, nfit_struct->length);
> +			fwts_log_info_verbatim(fw, "    Address Range Memory Mapping Attribute: 0x%16.16" PRIx64, nfit_struct->memory_mapping);
> +
> +			if (nfit_struct->range_index == 0) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRangeIndexZero",
> +					"NFIT SPA Range Structure Index must not be zero");
> +			}
> +
> +			if (nfit_struct->flags & ~0x03) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadFlags",
> +					"NFIT Flags's Bits[15..2] must be zero, got "
> +					"0x%8.8" PRIx32 " instead", nfit_struct->flags);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +			/* TODO check Proximity Domain with SRAT table */
> +
> +			if (nfit_struct->memory_mapping & ~0x01F01F) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadMemoryMappingAttribute",
> +					"NFIT Memory Mapping Attribute must meet UEFI Spec, got "
> +					"0x%16.16" PRIx64 " instead", nfit_struct->memory_mapping);
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_MEMORY_MAP) {
> +			fwts_acpi_table_nfit_memory_map *nfit_struct = (fwts_acpi_table_nfit_memory_map *) entry;
> +
> +			fwts_log_info_verbatim(fw, "    NFIT Device Handle:                     0x%8.8" PRIx32, nfit_struct->device_handle);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Physical ID:                     0x%4.4" PRIx16, nfit_struct->physical_id);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Region ID:                       0x%4.4" PRIx16, nfit_struct->region_id);
> +			fwts_log_info_verbatim(fw, "    SPA Range Structure Index:              0x%4.4" PRIx16, nfit_struct->range_index);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Region Size:                     0x%16.16" PRIx64, nfit_struct->region_size);
> +			fwts_log_info_verbatim(fw, "    Region Offset:                          0x%16.16" PRIx64, nfit_struct->region_offset);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Physical Address Region Base:    0x%16.16" PRIx64, nfit_struct->address);
> +			fwts_log_info_verbatim(fw, "    Interleave Structure Index:             0x%4.4" PRIx16, nfit_struct->interleave_index);
> +			fwts_log_info_verbatim(fw, "    Interleave Ways:                        0x%4.4" PRIx16, nfit_struct->interleave_ways);
> +			fwts_log_info_verbatim(fw, "    NVDIMM State Flags:                     0x%4.4" PRIx16, nfit_struct->flags);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
> +
> +			if (nfit_struct->flags & ~0x7F) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadFlags",
> +					"NFIT Flags's Bits[15..7] must be zero, got "
> +					"0x%8.8" PRIx32 " instead", nfit_struct->flags);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_INTERLEAVE) {
> +			fwts_acpi_table_nfit_interleave *nfit_struct = (fwts_acpi_table_nfit_interleave *) entry;
> +			uint32_t i;
> +
> +			fwts_log_info_verbatim(fw, "    Interleave Structure Index:             0x%4.4" PRIx16, nfit_struct->interleave_index);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
> +			fwts_log_info_verbatim(fw, "    Number of Lines Described:              0x%8.8" PRIx32, nfit_struct->line_count);
> +			fwts_log_info_verbatim(fw, "    Line Size:                              0x%8.8" PRIx32, nfit_struct->line_size);
> +
> +			for (i = 0; i < nfit_struct->line_count; i++) {
> +				fwts_log_info_verbatim(fw, "    Line Offset:                            0x%8.8"  PRIx32, nfit_struct->line_offset[i]);
> +
> +				if (nfit_struct->line_offset[i] % nfit_struct->line_size)
> +					fwts_failed(fw, LOG_LEVEL_HIGH,
> +						"NFITBadLineOffsetAlignment",
> +						"NFIT Line Offset must be aligned nfit_struct->line_size, got "
> +						"0x%8.8" PRIx32 " instead", nfit_struct->line_offset[i]);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +			if (nfit_struct->line_count == 0) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadLineCount",
> +					"NFIT Number of Lines must not be zero");
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_SMBIOS) {
> +			fwts_acpi_table_nfit_smbios *nfit_struct = (fwts_acpi_table_nfit_smbios *) entry;
> +
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%8.8" PRIx32, nfit_struct->reserved);
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_CONTROL_REGION) {
> +			fwts_acpi_table_nfit_control_range *nfit_struct = (fwts_acpi_table_nfit_control_range *) entry;
> +			uint64_t reserved1;
> +
> +			reserved1 = (uint64_t) nfit_struct->reserved1[0] + ((uint64_t) nfit_struct->reserved1[1] << 8) +
> +				   ((uint64_t) nfit_struct->reserved1[2] << 16) + ((uint64_t) nfit_struct->reserved1[3] << 24) +
> +				   ((uint64_t) nfit_struct->reserved1[4] << 32) + ((uint64_t) nfit_struct->reserved1[5] << 40);
> +
> +			if (reserved1 != 0)
> +				reserved_passed = reserved1;
> +
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
> +			fwts_log_info_verbatim(fw, "    Vendor ID:                              0x%4.4" PRIx16, nfit_struct->vendor_id);
> +			fwts_log_info_verbatim(fw, "    Device ID:                              0x%4.4" PRIx16, nfit_struct->device_id);
> +			fwts_log_info_verbatim(fw, "    Revision ID:                            0x%4.4" PRIx16, nfit_struct->revision_id);
> +			fwts_log_info_verbatim(fw, "    Subsystem Vendor ID:                    0x%4.4" PRIx16, nfit_struct->subsystem_vendor_id);
> +			fwts_log_info_verbatim(fw, "    Subsystem Device ID:                    0x%4.4" PRIx16, nfit_struct->subsystem_device_id);
> +			fwts_log_info_verbatim(fw, "    Subsystem Revision ID:                  0x%4.4" PRIx16, nfit_struct->subsystem_revision_id);
> +			fwts_log_info_verbatim(fw, "    Valid Fields:                           0x%2.2" PRIx8, nfit_struct->valid_fields);
> +			fwts_log_info_verbatim(fw, "    Manufacturing Location:                 0x%2.2" PRIx8, nfit_struct->manufacturing_location);
> +			fwts_log_info_verbatim(fw, "    Manufacturing Date:                     0x%4.4" PRIx16, nfit_struct->manufacturing_date);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
> +			fwts_log_info_verbatim(fw, "    Serial Number:                          0x%8.8" PRIx32, nfit_struct->serial_number);
> +			fwts_log_info_verbatim(fw, "    Region Format Interface Code:           0x%4.4" PRIx16, nfit_struct->interface_code);
> +			fwts_log_info_verbatim(fw, "    Number of Block Control Windows:        0x%4.4" PRIx16, nfit_struct->windows_num);
> +			fwts_log_info_verbatim(fw, "    Size of Block Control Window:           0x%16.16" PRIx64, nfit_struct->window_size);
> +			fwts_log_info_verbatim(fw, "    Command Register Offset:                0x%16.16" PRIx64, nfit_struct->command_offset);
> +			fwts_log_info_verbatim(fw, "    Size of Command Register:               0x%16.16" PRIx64, nfit_struct->command_size);
> +			fwts_log_info_verbatim(fw, "    Status RegisterOffset:                  0x%16.16" PRIx64, nfit_struct->status_offset);
> +			fwts_log_info_verbatim(fw, "    Size of Status Register:                0x%16.16" PRIx64, nfit_struct->status_size);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Flag:             0x%4.4" PRIx16, nfit_struct->flags);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%16.16" PRIx64, reserved1);
> +
> +			if (nfit_struct->revision_id & 0xFF00) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRevisionId",
> +					"NFIT Revision ID's BYTE 1 must be zero, got "
> +					"0x%4.4" PRIx16 " instead", nfit_struct->revision_id);
> +			}
> +
> +			if (nfit_struct->subsystem_revision_id & 0xFF00) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRevisionId",
> +					"NFIT Subsystem Revision ID's BYTE 1 must be zero, got "
> +					"0x%4.4" PRIx16 " instead", nfit_struct->subsystem_revision_id);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +			if (nfit_struct->valid_fields & ~0x01) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadValidField",
> +					"NFIT Valid Field's Bits[7..1] must be zero, got "
> +					"0x%2.2" PRIx8 " instead", nfit_struct->valid_fields);
> +			}
> +
> +			if (nfit_struct->flags & ~0x01) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadFlags",
> +					"NFIT Flags's Bits[15..1] must be zero, got "
> +					"0x%4.4" PRIx16 " instead", nfit_struct->flags);
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_DATA_REGION) {
> +			fwts_acpi_table_nfit_data_range *nfit_struct = (fwts_acpi_table_nfit_data_range *) entry;
> +
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
> +			fwts_log_info_verbatim(fw, "    Number of Block Data Windows:           0x%4.4" PRIx16, nfit_struct->window_num);
> +			fwts_log_info_verbatim(fw, "    Block Data Window Start Offset:         0x%16.16" PRIx64, nfit_struct->window_offset);
> +			fwts_log_info_verbatim(fw, "    Size of Block Data Window:              0x%16.16" PRIx64, nfit_struct->window_size);
> +			fwts_log_info_verbatim(fw, "    NBlock Accessible Memory Capacity:      0x%16.16" PRIx64, nfit_struct->capacity);
> +			fwts_log_info_verbatim(fw, "    Beginning address of First Block:       0x%16.16" PRIx64, nfit_struct->start_address);
> +
> +			if (nfit_struct->region_index == 0) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRegionIndex",
> +					"NFIT NVDIMM Control Region Structure Index must not be zero");
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_FLUSH_ADDRESS) {
> +			fwts_acpi_table_nfit_flush_addr *nfit_struct = (fwts_acpi_table_nfit_flush_addr *) entry;
> +			uint64_t reserved;
> +			uint16_t i;
> +
> +			reserved = (uint64_t) nfit_struct->reserved[0] + ((uint64_t) nfit_struct->reserved[1] << 8) +
> +				   ((uint64_t) nfit_struct->reserved[2] << 16) + ((uint64_t) nfit_struct->reserved[3] << 24) +
> +				   ((uint64_t) nfit_struct->reserved[4] << 32) + ((uint64_t) nfit_struct->reserved[5] << 40);
> +
> +			fwts_log_info_verbatim(fw, "    NFIT Device Handle:                     0x%8.8" PRIx32, nfit_struct->device_handle);
> +			fwts_log_info_verbatim(fw, "    Number of Flush Hint Addresses:         0x%4.4" PRIx16, nfit_struct->hint_count);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%16.16" PRIx64, reserved);
> +			for (i = 0; i < nfit_struct->hint_count; i++)
> +				fwts_log_info_verbatim(fw, "    Flush Hint Address:                     0x%16.16" PRIx64, nfit_struct->hint_address[i]);
> +
> +			if (reserved != 0)
> +				reserved_passed = reserved;
> +
> +		} else {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"NFITBadSubType",
> +				"NFIT Structure supports type 0..6, got "
> +				"0x%4.4" PRIx16 " instead", entry->type);
> +		}
> +
> +		if (reserved_passed != 0) {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_LOW,
> +				"NFITReservedNonZero",
> +				"NFIT reserved field must be zero, got "
> +				"0x%16.16" PRIx64 " instead", reserved_passed);
> +		}
> +
> +		fwts_log_nl(fw);
> +		offset += entry->length;
> +		entry = (fwts_acpi_table_nfit_struct_header *) (table->data + offset);
> +	}
> +
> +
> +	if (passed)
> +		fwts_passed(fw, "No issues found in NFIT table.");
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test nfit_tests[] = {
> +	{ nfit_test1, "NFIT NVDIMM Firmware Interface Table test." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops nfit_ops = {
> +	.description = "NFIT NVDIMM Firmware Interface Table test.",
> +	.init        = nfit_init,
> +	.minor_tests = nfit_tests
> +};
> +
> +FWTS_REGISTER("nfit", &nfit_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_TEST_ACPI)
> +
> +#endif
> diff --git a/src/lib/include/fwts_acpi.h b/src/lib/include/fwts_acpi.h
> index b675f4f..fd5a6b0 100644
> --- a/src/lib/include/fwts_acpi.h
> +++ b/src/lib/include/fwts_acpi.h
> @@ -991,6 +991,116 @@ typedef struct {
>  } __attribute__ ((packed)) fwts_acpi_table_rasf;
>
>  /*
> + * ACPI NFIT (NVDIMM Firmware Interface), 5.2.25
> + */
> +typedef struct {
> +	fwts_acpi_table_header	header;
> +	uint32_t	reserved;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit;
> +
> +typedef struct {
> +	uint16_t		type;
> +	uint16_t		length;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_struct_header;
> +
> +typedef enum {
> +	FWTS_ACPI_NFIT_TYPE_SYSTEM_ADDRESS       = 0,
> +	FWTS_ACPI_NFIT_TYPE_MEMORY_MAP           = 1,
> +	FWTS_ACPI_NFIT_TYPE_INTERLEAVE           = 2,
> +	FWTS_ACPI_NFIT_TYPE_SMBIOS               = 3,
> +	FWTS_ACPI_NFIT_TYPE_CONTROL_REGION       = 4,
> +	FWTS_ACPI_NFIT_TYPE_DATA_REGION          = 5,
> +	FWTS_ACPI_NFIT_TYPE_FLUSH_ADDRESS        = 6,
> +	FWTS_ACPI_NFIT_TYPE_RESERVED             = 7     /* >= 7 are reserved */
> +} fwts_acpi_nfit_type;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	range_index;
> +	uint16_t	flags;
> +	uint32_t	reserved;
> +	uint32_t	proximity_domain;
> +	uint8_t		range_guid[16];
> +	uint64_t	address;
> +	uint64_t	length;
> +	uint64_t	memory_mapping;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_system_memory;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint32_t	device_handle;
> +	uint16_t	physical_id;
> +	uint16_t	region_id;
> +	uint16_t	range_index;
> +	uint16_t	region_index;
> +	uint64_t	region_size;
> +	uint64_t	region_offset;
> +	uint64_t	address;
> +	uint16_t	interleave_index;
> +	uint16_t	interleave_ways;
> +	uint16_t	flags;
> +	uint16_t	reserved;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_memory_map;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	interleave_index;
> +	uint16_t	reserved;
> +	uint32_t	line_count;
> +	uint32_t	line_size;
> +	uint32_t   line_offset[];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_interleave;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint32_t	reserved;
> +	uint8_t		smbios[];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_smbios;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	region_index;
> +	uint16_t	vendor_id;
> +	uint16_t	device_id;
> +	uint16_t	revision_id;
> +	uint16_t	subsystem_vendor_id;
> +	uint16_t	subsystem_device_id;
> +	uint16_t	subsystem_revision_id;
> +	uint8_t		valid_fields;
> +	uint8_t		manufacturing_location;
> +	uint16_t	manufacturing_date;
> +	uint16_t	reserved;
> +	uint32_t	serial_number;
> +	uint16_t	interface_code;
> +	uint16_t	windows_num;
> +	uint64_t	window_size;
> +	uint64_t	command_offset;
> +	uint64_t	command_size;
> +	uint64_t	status_offset;
> +	uint64_t	status_size;
> +	uint16_t	flags;
> +	uint8_t		reserved1[6];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_control_range;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	region_index;
> +	uint16_t	window_num;
> +	uint64_t	window_offset;
> +	uint64_t	window_size;
> +	uint64_t	capacity;
> +	uint64_t	start_address;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_data_range;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint32_t	device_handle;
> +	uint16_t	hint_count;
> +	uint8_t		reserved[6];
> +	uint64_t	hint_address[];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_flush_addr;
> +
> +/*
>   * ACPI PCCT (Platform Communications Channel Table), 14.1
>   */
>  typedef struct {
>
Ivan Hu Aug. 3, 2016, 6:16 a.m. UTC | #3
On 2016年07月27日 18:48, Alex Hung wrote:
> Signed-off-by: Alex Hung <alex.hung@canonical.com>
> ---
>  src/Makefile.am             |   1 +
>  src/acpi/nfit/nfit.c        | 327 ++++++++++++++++++++++++++++++++++++++++++++
>  src/lib/include/fwts_acpi.h | 110 +++++++++++++++
>  3 files changed, 438 insertions(+)
>  create mode 100644 src/acpi/nfit/nfit.c
>
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 11c65b2..bc23e8d 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -73,6 +73,7 @@ fwts_SOURCES = main.c 				\
>  	acpi/msct/msct.c 			\
>  	acpi/msdm/msdm.c 			\
>  	acpi/method/method.c 			\
> +	acpi/nfit/nfit.c 			\
>  	acpi/osilinux/osilinux.c 		\
>  	acpi/pcc/pcc.c 				\
>  	acpi/pcct/pcct.c			\
> diff --git a/src/acpi/nfit/nfit.c b/src/acpi/nfit/nfit.c
> new file mode 100644
> index 0000000..3738a50
> --- /dev/null
> +++ b/src/acpi/nfit/nfit.c
> @@ -0,0 +1,327 @@
> +/*
> + * Copyright (C) 2016 Canonical
> + *
> + * Portions of this code original from the Linux-ready Firmware Developer Kit
> + *
> + * 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 "fwts.h"
> +
> +#if defined(FWTS_HAS_ACPI)
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <inttypes.h>
> +#include <string.h>
> +#include <ctype.h>
> +
> +static fwts_acpi_table_info *table;
> +
> +static int nfit_init(fwts_framework *fw)
> +{
> +	if (fwts_acpi_find_table(fw, "NFIT", 0, &table) != FWTS_OK) {
> +		fwts_log_error(fw, "Cannot read ACPI tables.");
> +		return FWTS_ERROR;
> +	}
> +	if (table == NULL || (table && table->length == 0)) {
> +		fwts_log_error(fw, "ACPI NFIT table does not exist, skipping test");
> +		return FWTS_SKIP;
> +	}
> +	return FWTS_OK;
> +}
> +
> +/*
> + *  NFIT NVDIMM Firmware Interface Table
> + */
> +static int nfit_test1(fwts_framework *fw)
> +{
> +	fwts_acpi_table_nfit *nfit = (fwts_acpi_table_nfit*) table->data;
> +	fwts_acpi_table_nfit_struct_header *entry;
> +	size_t offset;
> +	bool passed = true;
> +
> +	fwts_log_info_verbatim(fw, "NFIT NVDIMM Firmware Interface Table:");
> +	fwts_log_info_verbatim(fw, "  Reserved:                 0x%8.8" PRIx32, nfit->reserved);
> +	fwts_log_nl(fw);
> +
> +	if (nfit->reserved != 0) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_LOW,
> +			"NFITReservedNonZero",
> +			"NFIT reserved field must be zero, got "
> +			"0x%8.8" PRIx32 " instead", nfit->reserved);
> +	}
> +
> +	offset = sizeof(fwts_acpi_table_nfit);
> +	entry = (fwts_acpi_table_nfit_struct_header *) (table->data + offset);
> +
> +	while (offset < table->length) {
> +		uint64_t reserved_passed = 0;
> +
> +		fwts_log_info_verbatim(fw, "  NFIT Subtable:");
> +		fwts_log_info_verbatim(fw, "    Type:                                   0x%4.4" PRIx16, entry->type);
> +		fwts_log_info_verbatim(fw, "    Length:                                 0x%4.4" PRIx16, entry->length);
> +
> +		if (entry->type == FWTS_ACPI_NFIT_TYPE_SYSTEM_ADDRESS) {
> +			fwts_acpi_table_nfit_system_memory *nfit_struct = (fwts_acpi_table_nfit_system_memory *) entry;
> +			char guid_str[37];
> +
> +			fwts_guid_buf_to_str(nfit_struct->range_guid, guid_str, sizeof(guid_str));
> +
> +			fwts_log_info_verbatim(fw, "    SPA Range Structure Index:              0x%4.4" PRIx16, nfit_struct->range_index);
> +			fwts_log_info_verbatim(fw, "    Flags:                                  0x%4.4" PRIx16, nfit_struct->flags);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%8.8" PRIx32, nfit_struct->reserved);
> +			fwts_log_info_verbatim(fw, "    Proximity Domain:                       0x%8.8" PRIx32, nfit_struct->proximity_domain);
> +			fwts_log_info_verbatim(fw, "    Address Range Type GUID:                %s", guid_str);
> +			fwts_log_info_verbatim(fw, "    System Physical Address Range Base:     0x%16.16" PRIx64, nfit_struct->address);
> +			fwts_log_info_verbatim(fw, "    System Physical Address Range Length:   0x%16.16" PRIx64, nfit_struct->length);
> +			fwts_log_info_verbatim(fw, "    Address Range Memory Mapping Attribute: 0x%16.16" PRIx64, nfit_struct->memory_mapping);
> +
> +			if (nfit_struct->range_index == 0) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRangeIndexZero",
> +					"NFIT SPA Range Structure Index must not be zero");
> +			}
> +
> +			if (nfit_struct->flags & ~0x03) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadFlags",
> +					"NFIT Flags's Bits[15..2] must be zero, got "
> +					"0x%8.8" PRIx32 " instead", nfit_struct->flags);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +			/* TODO check Proximity Domain with SRAT table */
> +
> +			if (nfit_struct->memory_mapping & ~0x01F01F) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadMemoryMappingAttribute",
> +					"NFIT Memory Mapping Attribute must meet UEFI Spec, got "
> +					"0x%16.16" PRIx64 " instead", nfit_struct->memory_mapping);
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_MEMORY_MAP) {
> +			fwts_acpi_table_nfit_memory_map *nfit_struct = (fwts_acpi_table_nfit_memory_map *) entry;
> +
> +			fwts_log_info_verbatim(fw, "    NFIT Device Handle:                     0x%8.8" PRIx32, nfit_struct->device_handle);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Physical ID:                     0x%4.4" PRIx16, nfit_struct->physical_id);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Region ID:                       0x%4.4" PRIx16, nfit_struct->region_id);
> +			fwts_log_info_verbatim(fw, "    SPA Range Structure Index:              0x%4.4" PRIx16, nfit_struct->range_index);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Region Size:                     0x%16.16" PRIx64, nfit_struct->region_size);
> +			fwts_log_info_verbatim(fw, "    Region Offset:                          0x%16.16" PRIx64, nfit_struct->region_offset);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Physical Address Region Base:    0x%16.16" PRIx64, nfit_struct->address);
> +			fwts_log_info_verbatim(fw, "    Interleave Structure Index:             0x%4.4" PRIx16, nfit_struct->interleave_index);
> +			fwts_log_info_verbatim(fw, "    Interleave Ways:                        0x%4.4" PRIx16, nfit_struct->interleave_ways);
> +			fwts_log_info_verbatim(fw, "    NVDIMM State Flags:                     0x%4.4" PRIx16, nfit_struct->flags);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
> +
> +			if (nfit_struct->flags & ~0x7F) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadFlags",
> +					"NFIT Flags's Bits[15..7] must be zero, got "
> +					"0x%8.8" PRIx32 " instead", nfit_struct->flags);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_INTERLEAVE) {
> +			fwts_acpi_table_nfit_interleave *nfit_struct = (fwts_acpi_table_nfit_interleave *) entry;
> +			uint32_t i;
> +
> +			fwts_log_info_verbatim(fw, "    Interleave Structure Index:             0x%4.4" PRIx16, nfit_struct->interleave_index);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
> +			fwts_log_info_verbatim(fw, "    Number of Lines Described:              0x%8.8" PRIx32, nfit_struct->line_count);
> +			fwts_log_info_verbatim(fw, "    Line Size:                              0x%8.8" PRIx32, nfit_struct->line_size);
> +
> +			for (i = 0; i < nfit_struct->line_count; i++) {
> +				fwts_log_info_verbatim(fw, "    Line Offset:                            0x%8.8"  PRIx32, nfit_struct->line_offset[i]);
> +
> +				if (nfit_struct->line_offset[i] % nfit_struct->line_size)
> +					fwts_failed(fw, LOG_LEVEL_HIGH,
> +						"NFITBadLineOffsetAlignment",
> +						"NFIT Line Offset must be aligned nfit_struct->line_size, got "
> +						"0x%8.8" PRIx32 " instead", nfit_struct->line_offset[i]);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +			if (nfit_struct->line_count == 0) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadLineCount",
> +					"NFIT Number of Lines must not be zero");
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_SMBIOS) {
> +			fwts_acpi_table_nfit_smbios *nfit_struct = (fwts_acpi_table_nfit_smbios *) entry;
> +
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%8.8" PRIx32, nfit_struct->reserved);
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_CONTROL_REGION) {
> +			fwts_acpi_table_nfit_control_range *nfit_struct = (fwts_acpi_table_nfit_control_range *) entry;
> +			uint64_t reserved1;
> +
> +			reserved1 = (uint64_t) nfit_struct->reserved1[0] + ((uint64_t) nfit_struct->reserved1[1] << 8) +
> +				   ((uint64_t) nfit_struct->reserved1[2] << 16) + ((uint64_t) nfit_struct->reserved1[3] << 24) +
> +				   ((uint64_t) nfit_struct->reserved1[4] << 32) + ((uint64_t) nfit_struct->reserved1[5] << 40);
> +
> +			if (reserved1 != 0)
> +				reserved_passed = reserved1;
> +
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
> +			fwts_log_info_verbatim(fw, "    Vendor ID:                              0x%4.4" PRIx16, nfit_struct->vendor_id);
> +			fwts_log_info_verbatim(fw, "    Device ID:                              0x%4.4" PRIx16, nfit_struct->device_id);
> +			fwts_log_info_verbatim(fw, "    Revision ID:                            0x%4.4" PRIx16, nfit_struct->revision_id);
> +			fwts_log_info_verbatim(fw, "    Subsystem Vendor ID:                    0x%4.4" PRIx16, nfit_struct->subsystem_vendor_id);
> +			fwts_log_info_verbatim(fw, "    Subsystem Device ID:                    0x%4.4" PRIx16, nfit_struct->subsystem_device_id);
> +			fwts_log_info_verbatim(fw, "    Subsystem Revision ID:                  0x%4.4" PRIx16, nfit_struct->subsystem_revision_id);
> +			fwts_log_info_verbatim(fw, "    Valid Fields:                           0x%2.2" PRIx8, nfit_struct->valid_fields);
> +			fwts_log_info_verbatim(fw, "    Manufacturing Location:                 0x%2.2" PRIx8, nfit_struct->manufacturing_location);
> +			fwts_log_info_verbatim(fw, "    Manufacturing Date:                     0x%4.4" PRIx16, nfit_struct->manufacturing_date);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
> +			fwts_log_info_verbatim(fw, "    Serial Number:                          0x%8.8" PRIx32, nfit_struct->serial_number);
> +			fwts_log_info_verbatim(fw, "    Region Format Interface Code:           0x%4.4" PRIx16, nfit_struct->interface_code);
> +			fwts_log_info_verbatim(fw, "    Number of Block Control Windows:        0x%4.4" PRIx16, nfit_struct->windows_num);
> +			fwts_log_info_verbatim(fw, "    Size of Block Control Window:           0x%16.16" PRIx64, nfit_struct->window_size);
> +			fwts_log_info_verbatim(fw, "    Command Register Offset:                0x%16.16" PRIx64, nfit_struct->command_offset);
> +			fwts_log_info_verbatim(fw, "    Size of Command Register:               0x%16.16" PRIx64, nfit_struct->command_size);
> +			fwts_log_info_verbatim(fw, "    Status RegisterOffset:                  0x%16.16" PRIx64, nfit_struct->status_offset);
> +			fwts_log_info_verbatim(fw, "    Size of Status Register:                0x%16.16" PRIx64, nfit_struct->status_size);
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Flag:             0x%4.4" PRIx16, nfit_struct->flags);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%16.16" PRIx64, reserved1);
> +
> +			if (nfit_struct->revision_id & 0xFF00) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRevisionId",
> +					"NFIT Revision ID's BYTE 1 must be zero, got "
> +					"0x%4.4" PRIx16 " instead", nfit_struct->revision_id);
> +			}
> +
> +			if (nfit_struct->subsystem_revision_id & 0xFF00) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRevisionId",
> +					"NFIT Subsystem Revision ID's BYTE 1 must be zero, got "
> +					"0x%4.4" PRIx16 " instead", nfit_struct->subsystem_revision_id);
> +			}
> +
> +			if (nfit_struct->reserved != 0)
> +				reserved_passed = nfit_struct->reserved;
> +
> +			if (nfit_struct->valid_fields & ~0x01) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadValidField",
> +					"NFIT Valid Field's Bits[7..1] must be zero, got "
> +					"0x%2.2" PRIx8 " instead", nfit_struct->valid_fields);
> +			}
> +
> +			if (nfit_struct->flags & ~0x01) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadFlags",
> +					"NFIT Flags's Bits[15..1] must be zero, got "
> +					"0x%4.4" PRIx16 " instead", nfit_struct->flags);
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_DATA_REGION) {
> +			fwts_acpi_table_nfit_data_range *nfit_struct = (fwts_acpi_table_nfit_data_range *) entry;
> +
> +			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
> +			fwts_log_info_verbatim(fw, "    Number of Block Data Windows:           0x%4.4" PRIx16, nfit_struct->window_num);
> +			fwts_log_info_verbatim(fw, "    Block Data Window Start Offset:         0x%16.16" PRIx64, nfit_struct->window_offset);
> +			fwts_log_info_verbatim(fw, "    Size of Block Data Window:              0x%16.16" PRIx64, nfit_struct->window_size);
> +			fwts_log_info_verbatim(fw, "    NBlock Accessible Memory Capacity:      0x%16.16" PRIx64, nfit_struct->capacity);
> +			fwts_log_info_verbatim(fw, "    Beginning address of First Block:       0x%16.16" PRIx64, nfit_struct->start_address);
> +
> +			if (nfit_struct->region_index == 0) {
> +				passed = false;
> +				fwts_failed(fw, LOG_LEVEL_HIGH,
> +					"NFITBadRegionIndex",
> +					"NFIT NVDIMM Control Region Structure Index must not be zero");
> +			}
> +
> +		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_FLUSH_ADDRESS) {
> +			fwts_acpi_table_nfit_flush_addr *nfit_struct = (fwts_acpi_table_nfit_flush_addr *) entry;
> +			uint64_t reserved;
> +			uint16_t i;
> +
> +			reserved = (uint64_t) nfit_struct->reserved[0] + ((uint64_t) nfit_struct->reserved[1] << 8) +
> +				   ((uint64_t) nfit_struct->reserved[2] << 16) + ((uint64_t) nfit_struct->reserved[3] << 24) +
> +				   ((uint64_t) nfit_struct->reserved[4] << 32) + ((uint64_t) nfit_struct->reserved[5] << 40);
> +
> +			fwts_log_info_verbatim(fw, "    NFIT Device Handle:                     0x%8.8" PRIx32, nfit_struct->device_handle);
> +			fwts_log_info_verbatim(fw, "    Number of Flush Hint Addresses:         0x%4.4" PRIx16, nfit_struct->hint_count);
> +			fwts_log_info_verbatim(fw, "    Reserved:                               0x%16.16" PRIx64, reserved);
> +			for (i = 0; i < nfit_struct->hint_count; i++)
> +				fwts_log_info_verbatim(fw, "    Flush Hint Address:                     0x%16.16" PRIx64, nfit_struct->hint_address[i]);
> +
> +			if (reserved != 0)
> +				reserved_passed = reserved;
> +
> +		} else {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"NFITBadSubType",
> +				"NFIT Structure supports type 0..6, got "
> +				"0x%4.4" PRIx16 " instead", entry->type);
> +		}
> +
> +		if (reserved_passed != 0) {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_LOW,
> +				"NFITReservedNonZero",
> +				"NFIT reserved field must be zero, got "
> +				"0x%16.16" PRIx64 " instead", reserved_passed);
> +		}
> +
> +		fwts_log_nl(fw);
> +		offset += entry->length;
> +		entry = (fwts_acpi_table_nfit_struct_header *) (table->data + offset);
> +	}
> +
> +
> +	if (passed)
> +		fwts_passed(fw, "No issues found in NFIT table.");
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test nfit_tests[] = {
> +	{ nfit_test1, "NFIT NVDIMM Firmware Interface Table test." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops nfit_ops = {
> +	.description = "NFIT NVDIMM Firmware Interface Table test.",
> +	.init        = nfit_init,
> +	.minor_tests = nfit_tests
> +};
> +
> +FWTS_REGISTER("nfit", &nfit_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_TEST_ACPI)
> +
> +#endif
> diff --git a/src/lib/include/fwts_acpi.h b/src/lib/include/fwts_acpi.h
> index b675f4f..fd5a6b0 100644
> --- a/src/lib/include/fwts_acpi.h
> +++ b/src/lib/include/fwts_acpi.h
> @@ -991,6 +991,116 @@ typedef struct {
>  } __attribute__ ((packed)) fwts_acpi_table_rasf;
>
>  /*
> + * ACPI NFIT (NVDIMM Firmware Interface), 5.2.25
> + */
> +typedef struct {
> +	fwts_acpi_table_header	header;
> +	uint32_t	reserved;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit;
> +
> +typedef struct {
> +	uint16_t		type;
> +	uint16_t		length;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_struct_header;
> +
> +typedef enum {
> +	FWTS_ACPI_NFIT_TYPE_SYSTEM_ADDRESS       = 0,
> +	FWTS_ACPI_NFIT_TYPE_MEMORY_MAP           = 1,
> +	FWTS_ACPI_NFIT_TYPE_INTERLEAVE           = 2,
> +	FWTS_ACPI_NFIT_TYPE_SMBIOS               = 3,
> +	FWTS_ACPI_NFIT_TYPE_CONTROL_REGION       = 4,
> +	FWTS_ACPI_NFIT_TYPE_DATA_REGION          = 5,
> +	FWTS_ACPI_NFIT_TYPE_FLUSH_ADDRESS        = 6,
> +	FWTS_ACPI_NFIT_TYPE_RESERVED             = 7     /* >= 7 are reserved */
> +} fwts_acpi_nfit_type;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	range_index;
> +	uint16_t	flags;
> +	uint32_t	reserved;
> +	uint32_t	proximity_domain;
> +	uint8_t		range_guid[16];
> +	uint64_t	address;
> +	uint64_t	length;
> +	uint64_t	memory_mapping;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_system_memory;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint32_t	device_handle;
> +	uint16_t	physical_id;
> +	uint16_t	region_id;
> +	uint16_t	range_index;
> +	uint16_t	region_index;
> +	uint64_t	region_size;
> +	uint64_t	region_offset;
> +	uint64_t	address;
> +	uint16_t	interleave_index;
> +	uint16_t	interleave_ways;
> +	uint16_t	flags;
> +	uint16_t	reserved;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_memory_map;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	interleave_index;
> +	uint16_t	reserved;
> +	uint32_t	line_count;
> +	uint32_t	line_size;
> +	uint32_t   line_offset[];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_interleave;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint32_t	reserved;
> +	uint8_t		smbios[];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_smbios;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	region_index;
> +	uint16_t	vendor_id;
> +	uint16_t	device_id;
> +	uint16_t	revision_id;
> +	uint16_t	subsystem_vendor_id;
> +	uint16_t	subsystem_device_id;
> +	uint16_t	subsystem_revision_id;
> +	uint8_t		valid_fields;
> +	uint8_t		manufacturing_location;
> +	uint16_t	manufacturing_date;
> +	uint16_t	reserved;
> +	uint32_t	serial_number;
> +	uint16_t	interface_code;
> +	uint16_t	windows_num;
> +	uint64_t	window_size;
> +	uint64_t	command_offset;
> +	uint64_t	command_size;
> +	uint64_t	status_offset;
> +	uint64_t	status_size;
> +	uint16_t	flags;
> +	uint8_t		reserved1[6];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_control_range;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint16_t	region_index;
> +	uint16_t	window_num;
> +	uint64_t	window_offset;
> +	uint64_t	window_size;
> +	uint64_t	capacity;
> +	uint64_t	start_address;
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_data_range;
> +
> +typedef struct {
> +	fwts_acpi_table_nfit_struct_header	header;
> +	uint32_t	device_handle;
> +	uint16_t	hint_count;
> +	uint8_t		reserved[6];
> +	uint64_t	hint_address[];
> +} __attribute__ ((packed)) fwts_acpi_table_nfit_flush_addr;
> +
> +/*
>   * ACPI PCCT (Platform Communications Channel Table), 14.1
>   */
>  typedef struct {
>

All look good to me execpt "Portions of this code original from the 
Linux-ready Firmware Developer Kit"

Alex, maybe you can delete the line about "Linux-ready Firmware 
Developer Kit" when you commit the patch.

Acked-by: Ivan Hu <ivan.hu@canonical.com>
diff mbox

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index 11c65b2..bc23e8d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -73,6 +73,7 @@  fwts_SOURCES = main.c 				\
 	acpi/msct/msct.c 			\
 	acpi/msdm/msdm.c 			\
 	acpi/method/method.c 			\
+	acpi/nfit/nfit.c 			\
 	acpi/osilinux/osilinux.c 		\
 	acpi/pcc/pcc.c 				\
 	acpi/pcct/pcct.c			\
diff --git a/src/acpi/nfit/nfit.c b/src/acpi/nfit/nfit.c
new file mode 100644
index 0000000..3738a50
--- /dev/null
+++ b/src/acpi/nfit/nfit.c
@@ -0,0 +1,327 @@ 
+/*
+ * Copyright (C) 2016 Canonical
+ *
+ * Portions of this code original from the Linux-ready Firmware Developer Kit
+ *
+ * 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 "fwts.h"
+
+#if defined(FWTS_HAS_ACPI)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+#include <ctype.h>
+
+static fwts_acpi_table_info *table;
+
+static int nfit_init(fwts_framework *fw)
+{
+	if (fwts_acpi_find_table(fw, "NFIT", 0, &table) != FWTS_OK) {
+		fwts_log_error(fw, "Cannot read ACPI tables.");
+		return FWTS_ERROR;
+	}
+	if (table == NULL || (table && table->length == 0)) {
+		fwts_log_error(fw, "ACPI NFIT table does not exist, skipping test");
+		return FWTS_SKIP;
+	}
+	return FWTS_OK;
+}
+
+/*
+ *  NFIT NVDIMM Firmware Interface Table
+ */
+static int nfit_test1(fwts_framework *fw)
+{
+	fwts_acpi_table_nfit *nfit = (fwts_acpi_table_nfit*) table->data;
+	fwts_acpi_table_nfit_struct_header *entry;
+	size_t offset;
+	bool passed = true;
+
+	fwts_log_info_verbatim(fw, "NFIT NVDIMM Firmware Interface Table:");
+	fwts_log_info_verbatim(fw, "  Reserved:                 0x%8.8" PRIx32, nfit->reserved);
+	fwts_log_nl(fw);
+
+	if (nfit->reserved != 0) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_LOW,
+			"NFITReservedNonZero",
+			"NFIT reserved field must be zero, got "
+			"0x%8.8" PRIx32 " instead", nfit->reserved);
+	}
+
+	offset = sizeof(fwts_acpi_table_nfit);
+	entry = (fwts_acpi_table_nfit_struct_header *) (table->data + offset);
+
+	while (offset < table->length) {
+		uint64_t reserved_passed = 0;
+
+		fwts_log_info_verbatim(fw, "  NFIT Subtable:");
+		fwts_log_info_verbatim(fw, "    Type:                                   0x%4.4" PRIx16, entry->type);
+		fwts_log_info_verbatim(fw, "    Length:                                 0x%4.4" PRIx16, entry->length);
+
+		if (entry->type == FWTS_ACPI_NFIT_TYPE_SYSTEM_ADDRESS) {
+			fwts_acpi_table_nfit_system_memory *nfit_struct = (fwts_acpi_table_nfit_system_memory *) entry;
+			char guid_str[37];
+
+			fwts_guid_buf_to_str(nfit_struct->range_guid, guid_str, sizeof(guid_str));
+
+			fwts_log_info_verbatim(fw, "    SPA Range Structure Index:              0x%4.4" PRIx16, nfit_struct->range_index);
+			fwts_log_info_verbatim(fw, "    Flags:                                  0x%4.4" PRIx16, nfit_struct->flags);
+			fwts_log_info_verbatim(fw, "    Reserved:                               0x%8.8" PRIx32, nfit_struct->reserved);
+			fwts_log_info_verbatim(fw, "    Proximity Domain:                       0x%8.8" PRIx32, nfit_struct->proximity_domain);
+			fwts_log_info_verbatim(fw, "    Address Range Type GUID:                %s", guid_str);
+			fwts_log_info_verbatim(fw, "    System Physical Address Range Base:     0x%16.16" PRIx64, nfit_struct->address);
+			fwts_log_info_verbatim(fw, "    System Physical Address Range Length:   0x%16.16" PRIx64, nfit_struct->length);
+			fwts_log_info_verbatim(fw, "    Address Range Memory Mapping Attribute: 0x%16.16" PRIx64, nfit_struct->memory_mapping);
+
+			if (nfit_struct->range_index == 0) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadRangeIndexZero",
+					"NFIT SPA Range Structure Index must not be zero");
+			}
+
+			if (nfit_struct->flags & ~0x03) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadFlags",
+					"NFIT Flags's Bits[15..2] must be zero, got "
+					"0x%8.8" PRIx32 " instead", nfit_struct->flags);
+			}
+
+			if (nfit_struct->reserved != 0)
+				reserved_passed = nfit_struct->reserved;
+
+			/* TODO check Proximity Domain with SRAT table */
+
+			if (nfit_struct->memory_mapping & ~0x01F01F) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadMemoryMappingAttribute",
+					"NFIT Memory Mapping Attribute must meet UEFI Spec, got "
+					"0x%16.16" PRIx64 " instead", nfit_struct->memory_mapping);
+			}
+
+		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_MEMORY_MAP) {
+			fwts_acpi_table_nfit_memory_map *nfit_struct = (fwts_acpi_table_nfit_memory_map *) entry;
+
+			fwts_log_info_verbatim(fw, "    NFIT Device Handle:                     0x%8.8" PRIx32, nfit_struct->device_handle);
+			fwts_log_info_verbatim(fw, "    NVDIMM Physical ID:                     0x%4.4" PRIx16, nfit_struct->physical_id);
+			fwts_log_info_verbatim(fw, "    NVDIMM Region ID:                       0x%4.4" PRIx16, nfit_struct->region_id);
+			fwts_log_info_verbatim(fw, "    SPA Range Structure Index:              0x%4.4" PRIx16, nfit_struct->range_index);
+			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
+			fwts_log_info_verbatim(fw, "    NVDIMM Region Size:                     0x%16.16" PRIx64, nfit_struct->region_size);
+			fwts_log_info_verbatim(fw, "    Region Offset:                          0x%16.16" PRIx64, nfit_struct->region_offset);
+			fwts_log_info_verbatim(fw, "    NVDIMM Physical Address Region Base:    0x%16.16" PRIx64, nfit_struct->address);
+			fwts_log_info_verbatim(fw, "    Interleave Structure Index:             0x%4.4" PRIx16, nfit_struct->interleave_index);
+			fwts_log_info_verbatim(fw, "    Interleave Ways:                        0x%4.4" PRIx16, nfit_struct->interleave_ways);
+			fwts_log_info_verbatim(fw, "    NVDIMM State Flags:                     0x%4.4" PRIx16, nfit_struct->flags);
+			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
+
+			if (nfit_struct->flags & ~0x7F) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadFlags",
+					"NFIT Flags's Bits[15..7] must be zero, got "
+					"0x%8.8" PRIx32 " instead", nfit_struct->flags);
+			}
+
+			if (nfit_struct->reserved != 0)
+				reserved_passed = nfit_struct->reserved;
+
+		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_INTERLEAVE) {
+			fwts_acpi_table_nfit_interleave *nfit_struct = (fwts_acpi_table_nfit_interleave *) entry;
+			uint32_t i;
+
+			fwts_log_info_verbatim(fw, "    Interleave Structure Index:             0x%4.4" PRIx16, nfit_struct->interleave_index);
+			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
+			fwts_log_info_verbatim(fw, "    Number of Lines Described:              0x%8.8" PRIx32, nfit_struct->line_count);
+			fwts_log_info_verbatim(fw, "    Line Size:                              0x%8.8" PRIx32, nfit_struct->line_size);
+
+			for (i = 0; i < nfit_struct->line_count; i++) {
+				fwts_log_info_verbatim(fw, "    Line Offset:                            0x%8.8"  PRIx32, nfit_struct->line_offset[i]);
+
+				if (nfit_struct->line_offset[i] % nfit_struct->line_size)
+					fwts_failed(fw, LOG_LEVEL_HIGH,
+						"NFITBadLineOffsetAlignment",
+						"NFIT Line Offset must be aligned nfit_struct->line_size, got "
+						"0x%8.8" PRIx32 " instead", nfit_struct->line_offset[i]);
+			}
+
+			if (nfit_struct->reserved != 0)
+				reserved_passed = nfit_struct->reserved;
+
+			if (nfit_struct->line_count == 0) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadLineCount",
+					"NFIT Number of Lines must not be zero");
+			}
+
+		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_SMBIOS) {
+			fwts_acpi_table_nfit_smbios *nfit_struct = (fwts_acpi_table_nfit_smbios *) entry;
+
+			fwts_log_info_verbatim(fw, "    Reserved:                               0x%8.8" PRIx32, nfit_struct->reserved);
+
+		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_CONTROL_REGION) {
+			fwts_acpi_table_nfit_control_range *nfit_struct = (fwts_acpi_table_nfit_control_range *) entry;
+			uint64_t reserved1;
+
+			reserved1 = (uint64_t) nfit_struct->reserved1[0] + ((uint64_t) nfit_struct->reserved1[1] << 8) +
+				   ((uint64_t) nfit_struct->reserved1[2] << 16) + ((uint64_t) nfit_struct->reserved1[3] << 24) +
+				   ((uint64_t) nfit_struct->reserved1[4] << 32) + ((uint64_t) nfit_struct->reserved1[5] << 40);
+
+			if (reserved1 != 0)
+				reserved_passed = reserved1;
+
+			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
+			fwts_log_info_verbatim(fw, "    Vendor ID:                              0x%4.4" PRIx16, nfit_struct->vendor_id);
+			fwts_log_info_verbatim(fw, "    Device ID:                              0x%4.4" PRIx16, nfit_struct->device_id);
+			fwts_log_info_verbatim(fw, "    Revision ID:                            0x%4.4" PRIx16, nfit_struct->revision_id);
+			fwts_log_info_verbatim(fw, "    Subsystem Vendor ID:                    0x%4.4" PRIx16, nfit_struct->subsystem_vendor_id);
+			fwts_log_info_verbatim(fw, "    Subsystem Device ID:                    0x%4.4" PRIx16, nfit_struct->subsystem_device_id);
+			fwts_log_info_verbatim(fw, "    Subsystem Revision ID:                  0x%4.4" PRIx16, nfit_struct->subsystem_revision_id);
+			fwts_log_info_verbatim(fw, "    Valid Fields:                           0x%2.2" PRIx8, nfit_struct->valid_fields);
+			fwts_log_info_verbatim(fw, "    Manufacturing Location:                 0x%2.2" PRIx8, nfit_struct->manufacturing_location);
+			fwts_log_info_verbatim(fw, "    Manufacturing Date:                     0x%4.4" PRIx16, nfit_struct->manufacturing_date);
+			fwts_log_info_verbatim(fw, "    Reserved:                               0x%4.4" PRIx16, nfit_struct->reserved);
+			fwts_log_info_verbatim(fw, "    Serial Number:                          0x%8.8" PRIx32, nfit_struct->serial_number);
+			fwts_log_info_verbatim(fw, "    Region Format Interface Code:           0x%4.4" PRIx16, nfit_struct->interface_code);
+			fwts_log_info_verbatim(fw, "    Number of Block Control Windows:        0x%4.4" PRIx16, nfit_struct->windows_num);
+			fwts_log_info_verbatim(fw, "    Size of Block Control Window:           0x%16.16" PRIx64, nfit_struct->window_size);
+			fwts_log_info_verbatim(fw, "    Command Register Offset:                0x%16.16" PRIx64, nfit_struct->command_offset);
+			fwts_log_info_verbatim(fw, "    Size of Command Register:               0x%16.16" PRIx64, nfit_struct->command_size);
+			fwts_log_info_verbatim(fw, "    Status RegisterOffset:                  0x%16.16" PRIx64, nfit_struct->status_offset);
+			fwts_log_info_verbatim(fw, "    Size of Status Register:                0x%16.16" PRIx64, nfit_struct->status_size);
+			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Flag:             0x%4.4" PRIx16, nfit_struct->flags);
+			fwts_log_info_verbatim(fw, "    Reserved:                               0x%16.16" PRIx64, reserved1);
+
+			if (nfit_struct->revision_id & 0xFF00) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadRevisionId",
+					"NFIT Revision ID's BYTE 1 must be zero, got "
+					"0x%4.4" PRIx16 " instead", nfit_struct->revision_id);
+			}
+
+			if (nfit_struct->subsystem_revision_id & 0xFF00) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadRevisionId",
+					"NFIT Subsystem Revision ID's BYTE 1 must be zero, got "
+					"0x%4.4" PRIx16 " instead", nfit_struct->subsystem_revision_id);
+			}
+
+			if (nfit_struct->reserved != 0)
+				reserved_passed = nfit_struct->reserved;
+
+			if (nfit_struct->valid_fields & ~0x01) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadValidField",
+					"NFIT Valid Field's Bits[7..1] must be zero, got "
+					"0x%2.2" PRIx8 " instead", nfit_struct->valid_fields);
+			}
+
+			if (nfit_struct->flags & ~0x01) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadFlags",
+					"NFIT Flags's Bits[15..1] must be zero, got "
+					"0x%4.4" PRIx16 " instead", nfit_struct->flags);
+			}
+
+		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_DATA_REGION) {
+			fwts_acpi_table_nfit_data_range *nfit_struct = (fwts_acpi_table_nfit_data_range *) entry;
+
+			fwts_log_info_verbatim(fw, "    NVDIMM Control Region Structure Index:  0x%4.4" PRIx16, nfit_struct->region_index);
+			fwts_log_info_verbatim(fw, "    Number of Block Data Windows:           0x%4.4" PRIx16, nfit_struct->window_num);
+			fwts_log_info_verbatim(fw, "    Block Data Window Start Offset:         0x%16.16" PRIx64, nfit_struct->window_offset);
+			fwts_log_info_verbatim(fw, "    Size of Block Data Window:              0x%16.16" PRIx64, nfit_struct->window_size);
+			fwts_log_info_verbatim(fw, "    NBlock Accessible Memory Capacity:      0x%16.16" PRIx64, nfit_struct->capacity);
+			fwts_log_info_verbatim(fw, "    Beginning address of First Block:       0x%16.16" PRIx64, nfit_struct->start_address);
+
+			if (nfit_struct->region_index == 0) {
+				passed = false;
+				fwts_failed(fw, LOG_LEVEL_HIGH,
+					"NFITBadRegionIndex",
+					"NFIT NVDIMM Control Region Structure Index must not be zero");
+			}
+
+		} else if (entry->type == FWTS_ACPI_NFIT_TYPE_FLUSH_ADDRESS) {
+			fwts_acpi_table_nfit_flush_addr *nfit_struct = (fwts_acpi_table_nfit_flush_addr *) entry;
+			uint64_t reserved;
+			uint16_t i;
+
+			reserved = (uint64_t) nfit_struct->reserved[0] + ((uint64_t) nfit_struct->reserved[1] << 8) +
+				   ((uint64_t) nfit_struct->reserved[2] << 16) + ((uint64_t) nfit_struct->reserved[3] << 24) +
+				   ((uint64_t) nfit_struct->reserved[4] << 32) + ((uint64_t) nfit_struct->reserved[5] << 40);
+
+			fwts_log_info_verbatim(fw, "    NFIT Device Handle:                     0x%8.8" PRIx32, nfit_struct->device_handle);
+			fwts_log_info_verbatim(fw, "    Number of Flush Hint Addresses:         0x%4.4" PRIx16, nfit_struct->hint_count);
+			fwts_log_info_verbatim(fw, "    Reserved:                               0x%16.16" PRIx64, reserved);
+			for (i = 0; i < nfit_struct->hint_count; i++)
+				fwts_log_info_verbatim(fw, "    Flush Hint Address:                     0x%16.16" PRIx64, nfit_struct->hint_address[i]);
+
+			if (reserved != 0)
+				reserved_passed = reserved;
+
+		} else {
+			passed = false;
+			fwts_failed(fw, LOG_LEVEL_HIGH,
+				"NFITBadSubType",
+				"NFIT Structure supports type 0..6, got "
+				"0x%4.4" PRIx16 " instead", entry->type);
+		}
+
+		if (reserved_passed != 0) {
+			passed = false;
+			fwts_failed(fw, LOG_LEVEL_LOW,
+				"NFITReservedNonZero",
+				"NFIT reserved field must be zero, got "
+				"0x%16.16" PRIx64 " instead", reserved_passed);
+		}
+
+		fwts_log_nl(fw);
+		offset += entry->length;
+		entry = (fwts_acpi_table_nfit_struct_header *) (table->data + offset);
+	}
+
+
+	if (passed)
+		fwts_passed(fw, "No issues found in NFIT table.");
+
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test nfit_tests[] = {
+	{ nfit_test1, "NFIT NVDIMM Firmware Interface Table test." },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops nfit_ops = {
+	.description = "NFIT NVDIMM Firmware Interface Table test.",
+	.init        = nfit_init,
+	.minor_tests = nfit_tests
+};
+
+FWTS_REGISTER("nfit", &nfit_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_TEST_ACPI)
+
+#endif
diff --git a/src/lib/include/fwts_acpi.h b/src/lib/include/fwts_acpi.h
index b675f4f..fd5a6b0 100644
--- a/src/lib/include/fwts_acpi.h
+++ b/src/lib/include/fwts_acpi.h
@@ -991,6 +991,116 @@  typedef struct {
 } __attribute__ ((packed)) fwts_acpi_table_rasf;
 
 /*
+ * ACPI NFIT (NVDIMM Firmware Interface), 5.2.25
+ */
+typedef struct {
+	fwts_acpi_table_header	header;
+	uint32_t	reserved;
+} __attribute__ ((packed)) fwts_acpi_table_nfit;
+
+typedef struct {
+	uint16_t		type;
+	uint16_t		length;
+} __attribute__ ((packed)) fwts_acpi_table_nfit_struct_header;
+
+typedef enum {
+	FWTS_ACPI_NFIT_TYPE_SYSTEM_ADDRESS       = 0,
+	FWTS_ACPI_NFIT_TYPE_MEMORY_MAP           = 1,
+	FWTS_ACPI_NFIT_TYPE_INTERLEAVE           = 2,
+	FWTS_ACPI_NFIT_TYPE_SMBIOS               = 3,
+	FWTS_ACPI_NFIT_TYPE_CONTROL_REGION       = 4,
+	FWTS_ACPI_NFIT_TYPE_DATA_REGION          = 5,
+	FWTS_ACPI_NFIT_TYPE_FLUSH_ADDRESS        = 6,
+	FWTS_ACPI_NFIT_TYPE_RESERVED             = 7     /* >= 7 are reserved */
+} fwts_acpi_nfit_type;
+
+typedef struct {
+	fwts_acpi_table_nfit_struct_header	header;
+	uint16_t	range_index;
+	uint16_t	flags;
+	uint32_t	reserved;
+	uint32_t	proximity_domain;
+	uint8_t		range_guid[16];
+	uint64_t	address;
+	uint64_t	length;
+	uint64_t	memory_mapping;
+} __attribute__ ((packed)) fwts_acpi_table_nfit_system_memory;
+
+typedef struct {
+	fwts_acpi_table_nfit_struct_header	header;
+	uint32_t	device_handle;
+	uint16_t	physical_id;
+	uint16_t	region_id;
+	uint16_t	range_index;
+	uint16_t	region_index;
+	uint64_t	region_size;
+	uint64_t	region_offset;
+	uint64_t	address;
+	uint16_t	interleave_index;
+	uint16_t	interleave_ways;
+	uint16_t	flags;
+	uint16_t	reserved;
+} __attribute__ ((packed)) fwts_acpi_table_nfit_memory_map;
+
+typedef struct {
+	fwts_acpi_table_nfit_struct_header	header;
+	uint16_t	interleave_index;
+	uint16_t	reserved;
+	uint32_t	line_count;
+	uint32_t	line_size;
+	uint32_t   line_offset[];
+} __attribute__ ((packed)) fwts_acpi_table_nfit_interleave;
+
+typedef struct {
+	fwts_acpi_table_nfit_struct_header	header;
+	uint32_t	reserved;
+	uint8_t		smbios[];
+} __attribute__ ((packed)) fwts_acpi_table_nfit_smbios;
+
+typedef struct {
+	fwts_acpi_table_nfit_struct_header	header;
+	uint16_t	region_index;
+	uint16_t	vendor_id;
+	uint16_t	device_id;
+	uint16_t	revision_id;
+	uint16_t	subsystem_vendor_id;
+	uint16_t	subsystem_device_id;
+	uint16_t	subsystem_revision_id;
+	uint8_t		valid_fields;
+	uint8_t		manufacturing_location;
+	uint16_t	manufacturing_date;
+	uint16_t	reserved;
+	uint32_t	serial_number;
+	uint16_t	interface_code;
+	uint16_t	windows_num;
+	uint64_t	window_size;
+	uint64_t	command_offset;
+	uint64_t	command_size;
+	uint64_t	status_offset;
+	uint64_t	status_size;
+	uint16_t	flags;
+	uint8_t		reserved1[6];
+} __attribute__ ((packed)) fwts_acpi_table_nfit_control_range;
+
+typedef struct {
+	fwts_acpi_table_nfit_struct_header	header;
+	uint16_t	region_index;
+	uint16_t	window_num;
+	uint64_t	window_offset;
+	uint64_t	window_size;
+	uint64_t	capacity;
+	uint64_t	start_address;
+} __attribute__ ((packed)) fwts_acpi_table_nfit_data_range;
+
+typedef struct {
+	fwts_acpi_table_nfit_struct_header	header;
+	uint32_t	device_handle;
+	uint16_t	hint_count;
+	uint8_t		reserved[6];
+	uint64_t	hint_address[];
+} __attribute__ ((packed)) fwts_acpi_table_nfit_flush_addr;
+
+/*
  * ACPI PCCT (Platform Communications Channel Table), 14.1
  */
 typedef struct {