ACK: [PATCH 1/2] Add ACPI IORT test (LP: #1450440)
Alex Hung
alex.hung at canonical.com
Tue Jul 21 06:24:24 UTC 2015
On 07/17/2015 03:02 AM, Colin King wrote:
> 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
>
Acked-by: Alex Hung <alex.hung at canonical.com>
More information about the fwts-devel
mailing list