[PATCH 1/2] Add ACPI IORT test (LP: #1450440)
Colin King
colin.king at canonical.com
Thu Jul 16 19:02:23 UTC 2015
From: Colin Ian King <colin.king at canonical.com>
ARM has a new IORT table, so add support for this.
Signed-off-by: Colin Ian King <colin.king at canonical.com>
---
src/Makefile.am | 1 +
src/acpi/iort/iort.c | 707 ++++++++++++++++++++++++++++++++++++++++++++
src/lib/include/fwts_acpi.h | 96 ++++++
3 files changed, 804 insertions(+)
create mode 100644 src/acpi/iort/iort.c
diff --git a/src/Makefile.am b/src/Makefile.am
index ba626f0..3e7fc3c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -54,6 +54,7 @@ fwts_SOURCES = main.c \
acpi/gtdt/gtdt.c \
acpi/hest/hest.c \
acpi/hpet/hpet.c \
+ acpi/iort/iort.c \
acpi/lid/lid.c \
acpi/lpit/lpit.c \
acpi/madt/madt.c \
diff --git a/src/acpi/iort/iort.c b/src/acpi/iort/iort.c
new file mode 100644
index 0000000..874b7b3
--- /dev/null
+++ b/src/acpi/iort/iort.c
@@ -0,0 +1,707 @@
+/*
+ * Copyright (C) 2015 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"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+
+static fwts_acpi_table_info *table;
+
+static int iort_init(fwts_framework *fw)
+{
+
+ if (fwts_acpi_find_table(fw, "IORT", 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 IORT table does not exist, skipping test");
+ return FWTS_SKIP;
+ }
+
+ return FWTS_OK;
+}
+
+/*
+ * iort_node_dump()
+ * dump IORT Node header, common to all IORT node types
+ */
+static void iort_node_dump(
+ fwts_framework *fw,
+ const char *node_name,
+ fwts_acpi_table_iort_node *node)
+{
+ fwts_log_info_verbatum(fw, "%s:", node_name);
+ fwts_log_info_verbatum(fw, " Type: 0x%2.2" PRIx8, node->type);
+ fwts_log_info_verbatum(fw, " Length: 0x%4.4" PRIx16, node->length);
+ fwts_log_info_verbatum(fw, " Revision: 0x%2.2" PRIx8, node->revision);
+ fwts_log_info_verbatum(fw, " Reserved: 0x%8.8" PRIx32, node->reserved);
+ fwts_log_info_verbatum(fw, " Number of ID mappings: 0x%8.8" PRIx32, node->id_mappings_count);
+ fwts_log_info_verbatum(fw, " Reference to ID Array: 0x%8.8" PRIx32, node->id_array_offset);
+}
+
+/*
+ * iort_node_check()
+ * sanity check IORT Node header
+ */
+static void iort_node_check(
+ fwts_framework *fw,
+ uint8_t *data,
+ bool no_id_mappings,
+ bool no_id_array,
+ bool *passed)
+{
+ fwts_acpi_table_iort_node *node = (fwts_acpi_table_iort_node *)data;
+
+ if (node->revision != 0) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "IORTNodeRevisionNonZero",
+ "IORT Node Revision field is 0x%2.2" PRIx8
+ " and should be zero.",
+ node->revision);
+ }
+ if (node->reserved != 0) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "IORTNodeReservedNonZero",
+ "IORT Node Reserved field is 0x0%8.8" PRIx32
+ " and should be zero.",
+ node->reserved);
+ }
+ if (no_id_mappings && node->id_mappings_count) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "IORTNodeMappingsCountNonZero",
+ "IORT Node Number of ID Mappings field is 0x0%8.8" PRIx32
+ " and should be zero for this node type.",
+ node->id_mappings_count);
+ }
+ if (no_id_array && node->id_array_offset) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_LOW,
+ "IORTNodeRefToIdArrayNonZero",
+ "IORT Node Referenced to ID Array field is 0x0%8.8" PRIx32
+ " and should be zero for this node type.",
+ node->id_array_offset);
+ }
+}
+
+
+/*
+ * IORT ID mappings
+ */
+
+/*
+ * iort_id_mapping_dump()
+ * dump IORT ID mapping
+ */
+static void iort_id_mapping_dump(
+ fwts_framework *fw,
+ uint32_t i,
+ fwts_acpi_table_iort_id_mapping *id_mapping)
+{
+ fwts_log_info_verbatum(fw, "ID Mapping %" PRIu32, i);
+ fwts_log_info_verbatum(fw, " Input Base: 0x%8.8" PRIx32, id_mapping->input_base);
+ fwts_log_info_verbatum(fw, " ID Count: 0x%8.8" PRIx32, id_mapping->id_count);
+ fwts_log_info_verbatum(fw, " Output Base: 0x%8.8" PRIx32, id_mapping->output_base);
+ fwts_log_info_verbatum(fw, " Output Reference: 0x%8.8" PRIx32, id_mapping->output_reference);
+ fwts_log_info_verbatum(fw, " Flags: 0x%8.8" PRIx32, id_mapping->flags);
+}
+
+/*
+ * iort_id_mappings_dump()
+ * dump array of ID mappings
+ */
+static void iort_id_mappings_dump(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *data_end)
+{
+ uint32_t i;
+ fwts_acpi_table_iort_node *node = (fwts_acpi_table_iort_node *)data;
+ fwts_acpi_table_iort_id_mapping *id_mapping;
+
+ id_mapping = (fwts_acpi_table_iort_id_mapping *)(data + node->id_array_offset);
+
+ for (i = 0; i < node->id_mappings_count; i++, id_mapping++) {
+ if (sizeof(*id_mapping) + (uint8_t *)id_mapping > data_end)
+ break;
+ iort_id_mapping_dump(fw, i, id_mapping);
+ }
+}
+
+/*
+ * iort_id_mapping_check()
+ * sanity check ID mapping
+ */
+static void iort_id_mapping_check(
+ fwts_framework *fw,
+ uint32_t i,
+ fwts_acpi_table_iort_id_mapping *id_mapping,
+ bool *passed)
+{
+ if (id_mapping->flags & ~1) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "IORTIdMappingReservedNonZero",
+ "IORT ID Mapping %" PRIu32 " Reserved Flags field reserved "
+ "bits [31:1] should be all zero, got 0x%" PRIx32
+ " instead",
+ i, id_mapping->flags);
+ }
+ /* Should also check output_reference is out of range or not */
+}
+
+/*
+ * iort_id_mappings_check()
+ * snaity check array of ID mappings
+ */
+static void iort_id_mappings_check(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *data_end,
+ bool *passed)
+{
+ uint32_t i;
+ fwts_acpi_table_iort_node *node = (fwts_acpi_table_iort_node *)data;
+ fwts_acpi_table_iort_id_mapping *id_mapping;
+
+ id_mapping = (fwts_acpi_table_iort_id_mapping *)(data + node->id_array_offset);
+
+ for (i = 0; i < node->id_mappings_count; i++, id_mapping++) {
+ if (sizeof(*id_mapping) + (uint8_t *)id_mapping > data_end) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTIdMappingOutsideTable",
+ "IORT ID Mapping %" PRIu32 " is outside the "
+ "IORT ACPI table. Either the offset is incorrect "
+ "or the IORT table size or the node is too small.", i);
+ break;
+ }
+ iort_id_mapping_check(fw, i, id_mapping, passed);
+ }
+}
+
+/*
+ * IORT SMMU interrupts
+ */
+
+/*
+ * iort_smmu_interrupt_dump()
+ * dump array of SMMU interrupts
+ */
+static void iort_smmu_interrupt_dump(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *data_end,
+ uint32_t offset,
+ uint32_t count)
+{
+ uint32_t i;
+
+ fwts_acpi_table_iort_smmu_interrupt *intr =
+ (fwts_acpi_table_iort_smmu_interrupt *)(data + offset);
+
+ for (i = 0; i < count; i++, intr++) {
+ if (sizeof(*intr) + (uint8_t *)intr > data_end)
+ break;
+ fwts_log_info_verbatum(fw, " GSIV: 0x%8.8" PRIx32, intr->gsiv);
+ fwts_log_info_verbatum(fw, " Interrupt Flags: 0x%8.8" PRIx32, intr->flags);
+
+ }
+}
+
+/*
+ * iort_smmu_interrupt_flags_check()
+ * sanity check SMMU interrupt flag bits
+ */
+static void iort_smmu_interrupt_flags_check(
+ fwts_framework *fw,
+ char *name,
+ uint32_t flags,
+ bool *passed)
+{
+ if (flags & ~1) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "IORTSMMUIntrFlagsReservedNonZero",
+ "IORT %s Flags field reserved "
+ "bits [31:1] should be all zero, got 0x%" PRIx32,
+ name, flags);
+ }
+}
+
+/*
+ * iort_smmu_interrupt_check()
+ * sanity check array of SMMU interrupts
+ */
+static void iort_smmu_interrupt_check(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *data_end,
+ uint32_t offset,
+ uint32_t count,
+ bool *passed)
+{
+ uint32_t i;
+
+ fwts_acpi_table_iort_smmu_interrupt *intr =
+ (fwts_acpi_table_iort_smmu_interrupt *)(data + offset);
+
+ for (i = 0; i < count; i++, intr++) {
+ if (sizeof(*intr) + (uint8_t *)intr > data_end) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTSmmuInterruptsOutsideTable",
+ "IORT SMMU Interrupt %" PRIu32 " is outside the "
+ "IORT ACPI table. Either the offset is incorrect "
+ "or the IORT table size is too small.", i);
+ break;
+ }
+ iort_smmu_interrupt_flags_check(fw, "SMMU_GSV",
+ intr->flags, passed);
+ }
+}
+
+/*
+ * IORT Global Interrupts
+ */
+
+/*
+ * iort_smmu_global_interrupt_dump()
+ * dump SMMU global interrupts
+ */
+static void iort_smmu_global_interrupt_dump(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *data_end,
+ uint32_t offset)
+{
+ fwts_acpi_table_iort_smmu_global_interrupt_array *intr =
+ (fwts_acpi_table_iort_smmu_global_interrupt_array *)(data + offset);
+
+ if (sizeof(*intr) + (uint8_t*)intr <= data_end) {
+ fwts_log_info_verbatum(fw, " SMMU_NSgIrpt: 0x%8.8" PRIx32, intr->smmu_nsgirpt);
+ fwts_log_info_verbatum(fw, " SMMU_NSgIrpt Flags: 0x%8.8" PRIx32, intr->smmu_nsgirpt_flags);
+ fwts_log_info_verbatum(fw, " SMMU_NSgCfgIrpt: 0x%8.8" PRIx32, intr->smmu_nsgcfgirpt);
+ fwts_log_info_verbatum(fw, " SMMU_NSgCfgIrpt Flags: 0x%8.8" PRIx32, intr->smmu_nsgcfgirpt_flags);
+ }
+}
+
+/*
+ * iort_smmu_global_interrupt_check()
+ * sanity check SMMU global interrupts
+ */
+static void iort_smmu_global_interrupt_check(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *data_end,
+ uint32_t offset,
+ bool *passed)
+{
+ fwts_acpi_table_iort_smmu_global_interrupt_array *intr =
+ (fwts_acpi_table_iort_smmu_global_interrupt_array *)(data + offset);
+ uint8_t *intr_end = data + offset +
+ sizeof(fwts_acpi_table_iort_smmu_global_interrupt_array);
+
+ if (sizeof(*intr) + (uint8_t *)intr_end > data_end) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTSmmuGlobalInterruptsOutsideTable",
+ "IORT SMMU Global Interrupt is outside the "
+ "IORT ACPI table. The offset is incorrect "
+ "or the IORT table size is too small.");
+ return;
+ }
+ iort_smmu_interrupt_flags_check(fw, "SMMU_NsgIrpt",
+ intr->smmu_nsgirpt_flags, passed);
+ iort_smmu_interrupt_flags_check(fw, "SMMU_NsgCfgIrpt",
+ intr->smmu_nsgcfgirpt_flags, passed);
+}
+
+/*
+ * IORT Node checks
+ */
+
+/*
+ * Check IORT ITS Group Node
+ */
+static void iort_check_its_group(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *node_end,
+ bool *passed)
+{
+ fwts_acpi_table_iort_its_group_node *node =
+ (fwts_acpi_table_iort_its_group_node *)data;
+ uint32_t *its_id = node->identifier_array;
+ size_t its_id_array_size = node->its_count * sizeof(*its_id);
+
+ iort_node_dump(fw, "IORT ITS Group Node", (fwts_acpi_table_iort_node *)data);
+ fwts_log_info_verbatum(fw, " Number of ITSs: 0x%8.8" PRIx32, node->its_count);
+
+
+ /* Array too big? */
+ if (its_id_array_size + (uint8_t*)its_id > node_end) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTItsIdArrayOutsideTable",
+ "IORT ITS Identifier Array end is outside the "
+ "IORT ACPI table. Either the Number of ID mappings is "
+ "too large or the IORT table size or ITS Group Node is too small.");
+ } else {
+ uint32_t i;
+
+ for (i = 0; i < node->its_count; i++, its_id++) {
+ fwts_log_info_verbatum(fw, " GIC ITS Identifier: 0x%8.8" PRIx32, *its_id);
+ }
+ }
+ iort_node_check(fw, data, true, true, passed);
+ fwts_log_nl(fw);
+}
+
+/*
+ * iort_memory_access_properties_check()
+ * sanity check memory access properties
+ */
+static void iort_memory_access_properties_check(
+ fwts_framework *fw,
+ const char *name,
+ fwts_acpi_table_iort_properties *properties,
+ bool *passed)
+{
+ uint8_t cca, cpm, dacs;
+
+ if (properties->cache_coherent > 1) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTCacheCoherentAttrInvalid",
+ "IORT %s Coherent Attribute "
+ "is 0x%" PRIx32 " and should be either "
+ "0 (device is fully coherent) or 1 (device is "
+ "not coherent).",
+ name, properties->cache_coherent);
+ }
+ if (properties->allocation_hints & ~0xf) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTAllocHintsReservedNonZero",
+ "IORT %s Allocation Hints reserved bits "
+ "[7:4] are reserved and should be zero.",
+ name);
+ }
+ if (properties->reserved) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_MEDIUM,
+ "IORTNodeReservedNonZero",
+ "IORT %s Node Reserved is 0x%" PRIx32
+ " and is reserved and should be zero.",
+ name, properties->reserved);
+ }
+ if (properties->memory_access_flags & ~0x3) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTMemoryAccessFlagsReservedNonZero",
+ "IORT %s Memory Access Flags is 0x%" PRIx8
+ " and all the reserved bits [7:2] should be zero but some are not.",
+ name, properties->memory_access_flags);
+ }
+ cca = properties->cache_coherent & 1;
+ cpm = properties->memory_access_flags & 1;
+ dacs = (properties->memory_access_flags >> 1) & 1;
+
+ if ((cca == 1) && (cpm == 0)) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTMemAttrInvalid",
+ "IORT %s Memory Attributes are illegal, "
+ "CCA cannot be 1 if CPM is 0.", name);
+ }
+ if ((cca == 0) && (cpm == 1) && (dacs == 1)) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTMemAttrInvalid",
+ "IORT %s Memory Attributes are illegal, "
+ "CCA cannot be 0 if CPM and DACS are 1. If the device "
+ "has a coherent path to memory and natively outputs "
+ "IWB-OWB-ISH then CCA must be 1.", name);
+ }
+}
+
+/*
+ * Check IORT Named Component Node
+ */
+static void iort_check_named_component(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *node_end,
+ bool *passed)
+{
+ fwts_acpi_table_iort_named_component_node *node =
+ (fwts_acpi_table_iort_named_component_node *)data;
+ uint8_t *obj_name;
+
+ iort_node_dump(fw, "IORT Named Component Node", (fwts_acpi_table_iort_node *)data);
+ fwts_log_info_verbatum(fw, " Node Flags: 0x%8.8" PRIx32, node->flags);
+ fwts_log_info_verbatum(fw, " Cache Coherent Attribute: 0x%8.8" PRIx32, node->properties.cache_coherent);
+ fwts_log_info_verbatum(fw, " Allocation Hints: 0x%2.2" PRIx8, node->properties.allocation_hints);
+ fwts_log_info_verbatum(fw, " Reserved: 0x%4.4" PRIx16, node->properties.reserved);
+ fwts_log_info_verbatum(fw, " Memory Access Flags 0x%2.2" PRIx8, node->properties.memory_access_flags);
+ fwts_log_info_verbatum(fw, " Device Memory Addr. Size: 0x%2.2" PRIx8, node->device_memory_address_size);
+
+ /* Is object name sane, zero terminated and inside the table? */
+ for (obj_name = node->device_object_name; *obj_name; obj_name++) {
+ if (obj_name > node_end) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTBadNamedComponentDeviceObjectName",
+ "IORT Named Component Device Object Name string "
+ "does not contain a null byte string terminator "
+ "inside the IORT ACPI table. Either the null "
+ "terminator is missing, or the IORT ACPI table "
+ "is truncated.");
+ /* Need to skip dumping it out as it is probably garbage */
+ goto next;
+ }
+ }
+ fwts_log_info_verbatum(fw, " Device Object Name: %s", (char *)node->device_object_name);
+
+next:
+ iort_id_mappings_dump(fw, data, node_end);
+ iort_node_check(fw, data, false, false, passed);
+
+ if (node->flags) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTNamedComponentNodeFlagsNonZero",
+ "IORT Named Component Node Flags is 0x%" PRIx32 " and is "
+ "reserved and should be zero.", node->flags);
+ }
+
+ iort_memory_access_properties_check(fw, "Named Component Node", &node->properties, passed);
+ iort_id_mappings_check(fw, data, node_end, passed);
+ fwts_log_nl(fw);
+}
+
+/*
+ * Check IORT PCI Root Complex Node
+ */
+static void iort_check_pci_root_complex(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *node_end,
+ bool *passed)
+{
+ fwts_acpi_table_iort_pci_root_complex_node *node =
+ (fwts_acpi_table_iort_pci_root_complex_node *)data;
+
+ iort_node_dump(fw, "IORT PCI Root Complex Node", (fwts_acpi_table_iort_node *)data);
+ fwts_log_info_verbatum(fw, " Cache Coherent Attribute: 0x%8.8" PRIx32, node->properties.cache_coherent);
+ fwts_log_info_verbatum(fw, " Allocation Hints: 0x%2.2" PRIx8, node->properties.allocation_hints);
+ fwts_log_info_verbatum(fw, " Reserved: 0x%4.4" PRIx16, node->properties.reserved);
+ fwts_log_info_verbatum(fw, " Memory Access Flags 0x%4.4" PRIx16, node->properties.memory_access_flags);
+ fwts_log_info_verbatum(fw, " ATS Attribute: 0x%8.8" PRIx32, node->ats_attribute);
+ fwts_log_info_verbatum(fw, " PCI Segment Number: 0x%8.8" PRIx32, node->pci_segment_number);
+
+ iort_id_mappings_dump(fw, data, node_end);
+ iort_node_check(fw, data, false, false, passed);
+
+ if (node->ats_attribute > 1) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTCPCIRootComplexAtsAttrInvalid",
+ "IORT PCI Root Complex Node ATS Attribute is 0x%" PRIx32
+ " and was expecting either 0 (root complex supports ATS) "
+ "or 1 (root complex does not support ATS).",
+ node->ats_attribute);
+ }
+
+ iort_memory_access_properties_check(fw, "PCI Root Complex Node", &node->properties, passed);
+ iort_id_mappings_check(fw, data, node_end, passed);
+ fwts_log_nl(fw);
+}
+
+/*
+ * Check IORT SMMUv1, SMMUv2 node
+ */
+static void iort_check_smmu(
+ fwts_framework *fw,
+ uint8_t *data,
+ uint8_t *node_end,
+ bool *passed)
+{
+ fwts_acpi_table_iort_smmu_node *node =
+ (fwts_acpi_table_iort_smmu_node *)data;
+
+ iort_node_dump(fw, "IORT SMMU node", (fwts_acpi_table_iort_node *)data);
+ fwts_log_info_verbatum(fw, " Base Address: 0x%16.16" PRIx64, node->base_address);
+ fwts_log_info_verbatum(fw, " Span: 0x%16.16" PRIx64, node->span);
+ fwts_log_info_verbatum(fw, " Model: 0x%8.8" PRIx32, node->model);
+ fwts_log_info_verbatum(fw, " Flags: 0x%8.8" PRIx32, node->flags);
+ fwts_log_info_verbatum(fw, " Global Intr. Offset: 0x%8.8" PRIx32, node->global_interrupt_array_offset);
+ fwts_log_info_verbatum(fw, " Number of Context Intr.: 0x%8.8" PRIx32, node->context_interrupt_count);
+ fwts_log_info_verbatum(fw, " Context Intr. Offset: 0x%8.8" PRIx32, node->context_interrupt_array_offset);
+ fwts_log_info_verbatum(fw, " Number of PMU Intr.: 0x%8.8" PRIx32, node->pmu_interrupt_count);
+ fwts_log_info_verbatum(fw, " PMU Intr. Offset: 0x%8.8" PRIx32, node->pmu_interrupt_array_offset);
+
+ fwts_log_info_verbatum(fw, "Global Interrupt Array:");
+ iort_smmu_global_interrupt_dump(fw, data, node_end,
+ node->global_interrupt_array_offset);
+ fwts_log_info_verbatum(fw, "Context Interrupt Array:");
+ iort_smmu_interrupt_dump(fw, data, node_end,
+ node->context_interrupt_array_offset, node->context_interrupt_count);
+ fwts_log_info_verbatum(fw, "PMU Interrupt Array:");
+ iort_smmu_interrupt_dump(fw, data, node_end,
+ node->pmu_interrupt_array_offset, node->pmu_interrupt_count);
+ iort_id_mappings_dump(fw, data, node_end);
+
+ iort_node_check(fw, data, false, false, passed);
+ iort_smmu_global_interrupt_check(fw, data, node_end,
+ node->global_interrupt_array_offset, passed);
+ iort_smmu_interrupt_check(fw, data, node_end,
+ node->context_interrupt_array_offset, node->context_interrupt_count, passed);
+ iort_smmu_interrupt_check(fw, data, node_end,
+ node->pmu_interrupt_array_offset, node->pmu_interrupt_count, passed);
+ iort_id_mappings_check(fw, data, node_end, passed);
+
+ if (node->model > 3) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTSmmuInvalidModel",
+ "IORT SMMU Model is 0x%" PRIx32 " and was expecting "
+ "a model value 0 to 3.", node->model);
+ }
+ if (node->flags & ~3) {
+ *passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTSmmuReservedFlags",
+ "IORT SMMU Reserved Flags is 0x%" PRIx32 " and has "
+ "some reserved bits [31:2] set when they should be zero.",
+ node->flags);
+ }
+ fwts_log_nl(fw);
+}
+
+/*
+ * IORT Remapping Table
+ * http://infocenter.arm.com/help/topic/com.arm.doc.den0049a/DEN0049A_IO_Remapping_Table.pdf
+ */
+static int iort_test1(fwts_framework *fw)
+{
+ bool passed = true;
+ fwts_acpi_table_iort *iort = (fwts_acpi_table_iort *)table->data;
+ uint8_t *data = (uint8_t *)table->data;
+ uint8_t *data_end;
+ uint32_t count;
+
+ if (table->length < (ssize_t)iort->header.length) {
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTTooShort",
+ "IORT table incorrectly sized, IORT "
+ "header reports it is %" PRIu32 " bytes, "
+ "instead got %zu bytes",
+ iort->header.length, table->length);
+ goto done;
+ }
+
+ if (table->length < sizeof(fwts_acpi_table_iort)) {
+ passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTTooShort",
+ "IORT table too short, expecting %zu bytes, "
+ "instead got %zu bytes",
+ sizeof(fwts_acpi_table_iort), table->length);
+ goto done;
+ }
+
+ data_end = data + table->length;
+
+ fwts_log_info_verbatum(fw, "IORT IO Remapping Table test");
+ fwts_log_info_verbatum(fw, " Number of IORT Nodes: 0x%4.4" PRIx8, iort->io_rt_nodes_count);
+ fwts_log_info_verbatum(fw, " IORT Node Array Offset: 0x%4.4" PRIx8, iort->io_rt_offset);
+ fwts_log_info_verbatum(fw, " Reserved: 0x%4.4" PRIx8, iort->reserved);
+ fwts_log_nl(fw);
+
+ data = (uint8_t *)table->data + iort->io_rt_offset;
+
+ for (count = 0; count < iort->io_rt_nodes_count; count++) {
+ fwts_acpi_table_iort_node *node = (fwts_acpi_table_iort_node *)data;
+ uint8_t *node_end;
+
+ if (data + node->length > data_end) {
+ passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTTooShort",
+ "IORT table too short, parsing IORT node %" PRIu32
+ " and the end of this node falls outside "
+ "the IORT table.", count);
+ break;
+ }
+ node_end = data + node->length;
+
+ switch (node->type) {
+ case 0x00:
+ iort_check_its_group(fw, data, node_end, &passed);
+ break;
+ case 0x01:
+ iort_check_named_component(fw, data, node_end, &passed);
+ break;
+ case 0x02:
+ iort_check_pci_root_complex(fw, data, node_end, &passed);
+ break;
+ case 0x03:
+ iort_check_smmu(fw, data, node_end, &passed);
+ break;
+ default:
+ /* reserved */
+ passed = false;
+ fwts_failed(fw, LOG_LEVEL_HIGH,
+ "IORTInvalidType",
+ "IORT Type 0x%2.2" PRIx8 " is invalid, aborting check",
+ node->type);
+ data = data_end; /* Forces termination */
+ break;
+ }
+ data += node->length;
+ }
+ if (passed)
+ fwts_passed(fw, "No issues found in IORT.");
+
+done:
+ return FWTS_OK;
+}
+
+static fwts_framework_minor_test iort_tests[] = {
+ { iort_test1, "IORT IO Remapping Table test." },
+ { NULL, NULL }
+};
+
+static fwts_framework_ops iort_ops = {
+ .description = "IORT IO Remapping Table test.",
+ .init = iort_init,
+ .minor_tests = iort_tests
+};
+
+FWTS_REGISTER("iort", &iort_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_TEST_ACPI)
diff --git a/src/lib/include/fwts_acpi.h b/src/lib/include/fwts_acpi.h
index 112af72..3da5ad3 100644
--- a/src/lib/include/fwts_acpi.h
+++ b/src/lib/include/fwts_acpi.h
@@ -1172,4 +1172,100 @@ typedef struct {
uint8_t data[0]; /* Proprietary data */
} __attribute__ ((packed)) fwts_acpi_table_msdm;
+/*
+ * IORT IO Remapping Table
+ * http://infocenter.arm.com/help/topic/com.arm.doc.den0049a/DEN0049A_IO_Remapping_Table.pdf
+ */
+typedef struct {
+ fwts_acpi_table_header header;
+ uint32_t io_rt_nodes_count;
+ uint32_t io_rt_offset;
+ uint32_t reserved;
+} __attribute__ ((packed)) fwts_acpi_table_iort;
+
+typedef struct {
+ uint8_t type;
+ uint16_t length;
+ uint8_t revision;
+ uint32_t reserved;
+ uint32_t id_mappings_count;
+ uint32_t id_array_offset;
+} __attribute__ ((packed)) fwts_acpi_table_iort_node;
+
+typedef struct {
+ uint32_t input_base;
+ uint32_t id_count;
+ uint32_t output_base;
+ uint32_t output_reference;
+ uint32_t flags;
+} __attribute__ ((packed)) fwts_acpi_table_iort_id_mapping;
+
+typedef struct {
+ uint32_t gsiv;
+ uint32_t flags;
+} __attribute__ ((packed)) fwts_acpi_table_iort_smmu_interrupt;
+
+typedef struct {
+ /* Global Interrupt Array Section */
+ uint32_t smmu_nsgirpt;
+ uint32_t smmu_nsgirpt_flags;
+ uint32_t smmu_nsgcfgirpt;
+ uint32_t smmu_nsgcfgirpt_flags;
+} __attribute__ ((packed)) fwts_acpi_table_iort_smmu_global_interrupt_array;
+
+typedef struct {
+ fwts_acpi_table_iort_node iort_node;
+ uint64_t base_address;
+ uint64_t span;
+ uint32_t model;
+ uint32_t flags;
+ uint32_t global_interrupt_array_offset;
+ uint32_t context_interrupt_count;
+ uint32_t context_interrupt_array_offset;
+ uint32_t pmu_interrupt_count;
+ uint32_t pmu_interrupt_array_offset;
+
+ /*
+ * Global Interrupt array, Context Interrupt Array
+ * and PMU Interrupt arrays follow at offsets into
+ * the table
+ */
+} __attribute__ ((packed)) fwts_acpi_table_iort_smmu_node;
+
+typedef struct {
+ fwts_acpi_table_iort_node iort_node;
+ uint32_t its_count;
+ uint32_t identifier_array[0];
+} __attribute__ ((packed)) fwts_acpi_table_iort_its_group_node;
+
+typedef struct {
+ uint32_t cache_coherent;
+ uint8_t allocation_hints;
+ uint16_t reserved;
+ uint8_t memory_access_flags;
+} __attribute__ ((packed)) fwts_acpi_table_iort_properties;
+
+typedef struct {
+ fwts_acpi_table_iort_node iort_node;
+ uint32_t flags;
+ fwts_acpi_table_iort_properties properties;
+ uint8_t device_memory_address_size;
+ uint8_t device_object_name[0];
+ /*
+ followed by padding and array of ids_mappings at some offset
+ uint32_t identifier_array[0];
+ */
+} __attribute__ ((packed)) fwts_acpi_table_iort_named_component_node;
+
+typedef struct {
+ fwts_acpi_table_iort_node iort_node;
+ fwts_acpi_table_iort_properties properties;
+ uint32_t ats_attribute;
+ uint32_t pci_segment_number;
+ /*
+ followed by array of ids_mappings at some offset
+ uint32_t identifier_array[0];
+ */
+} __attribute__ ((packed)) fwts_acpi_table_iort_pci_root_complex_node;
+
#endif
--
2.1.4
More information about the fwts-devel
mailing list