Message ID | 1469616485-2810-1-git-send-email-alex.hung@canonical.com |
---|---|
State | Accepted |
Headers | show |
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>
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 { >
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 --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 {
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