diff mbox series

[1/2] acpi: aest: add tests for ACPI AEST table

Message ID 20221228082430.51622-1-ivan.hu@canonical.com
State Accepted
Headers show
Series [1/2] acpi: aest: add tests for ACPI AEST table | expand

Commit Message

Ivan Hu Dec. 28, 2022, 8:24 a.m. UTC
BugLink: https://bugs.launchpad.net/fwts/+bug/2000387

Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
---
 src/Makefile.am             |   1 +
 src/acpi/aest/aest.c        | 248 ++++++++++++++++++++++++++++++++++++
 src/lib/include/fwts_acpi.h | 116 +++++++++++++++++
 3 files changed, 365 insertions(+)
 create mode 100644 src/acpi/aest/aest.c
diff mbox series

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index 60fdd49d..9f348ad1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -58,6 +58,7 @@  fwts_SOURCES = main.c 				\
 	acpi/acpiinfo/acpiinfo.c 		\
 	acpi/acpitables/acpitables.c 		\
 	sbbr/acpitables/acpitables.c 		\
+	acpi/aest/aest.c				\
 	acpi/apicinstance/apicinstance.c 	\
 	acpi/asf/asf.c				\
 	acpi/aspt/aspt.c			\
diff --git a/src/acpi/aest/aest.c b/src/acpi/aest/aest.c
new file mode 100644
index 00000000..cf7b9e58
--- /dev/null
+++ b/src/acpi/aest/aest.c
@@ -0,0 +1,248 @@ 
+/*
+ * Copyright (C) 2022 Canonical
+ *
+ * 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.
+ *
+ */
+#include "fwts.h"
+
+#if defined(FWTS_HAS_ACPI)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+static fwts_acpi_table_info *table;
+acpi_table_init(AEST, &table)
+
+static void aest_processor_test(
+	fwts_framework *fw,
+	fwts_acpi_table_aest_processor *entry,
+	uint32_t *offset,
+	bool *passed)
+{
+	fwts_log_info_verbatim(fw, "    Node-specific data (Processor):");
+	fwts_log_info_simp_int(fw, "    ACPI Processor ID:              ", entry->acpi_processor_id);
+	fwts_log_info_simp_int(fw, "    Resource Type:                  ", entry->resource_type);
+	fwts_log_info_simp_int(fw, "    Reserved:                       ", entry->reserved);
+	fwts_log_info_simp_int(fw, "    Flags:                          ", entry->flags);
+	fwts_log_info_simp_int(fw, "    Revision:                       ", entry->revision);
+	fwts_log_info_simp_int(fw, "    Processor affinity lvl ind:     ", entry->processor_affinity_level_indicator);
+
+	fwts_acpi_reserved_zero("AEST", "Reserved", entry->reserved, passed);
+	fwts_acpi_reserved_bits("AEST", "Flags", entry->flags, 2, 7, passed);
+
+	*offset += sizeof(fwts_acpi_table_aest_processor);
+	if (entry->resource_type == FWTS_AEST_RESOURCE_CACHE) {
+		fwts_acpi_table_aest_cache_resource_substructure *cache =
+			(fwts_acpi_table_aest_cache_resource_substructure *)
+			((uint8_t *)entry + sizeof(fwts_acpi_table_aest_processor));
+		fwts_log_info_simp_int(fw, "    Cache Reference:                ", cache->cache_reference);
+		fwts_log_info_simp_int(fw, "    Reserved:                       ", cache->reserved);
+		fwts_acpi_reserved_zero("AEST", "Reserved", cache->reserved, passed);
+		*offset += sizeof(fwts_acpi_table_aest_cache_resource_substructure);
+	} else if (entry->resource_type == FWTS_AEST_RESOURCE_TLB) {
+		fwts_acpi_table_aest_tlb_resource_substructure *tlb =
+			(fwts_acpi_table_aest_tlb_resource_substructure *)
+			((uint8_t *)entry + sizeof(fwts_acpi_table_aest_processor));
+		fwts_log_info_simp_int(fw, "    TLB Reference:                  ", tlb->tlb_reference);
+		fwts_log_info_simp_int(fw, "    Reserved:                       ", tlb->reserved);
+		fwts_acpi_reserved_zero("AEST", "Reserved", tlb->reserved, passed);
+		*offset += sizeof(fwts_acpi_table_aest_tlb_resource_substructure);
+	} else if (entry->resource_type == FWTS_AEST_RESOURCE_GENERIC) {
+		fwts_acpi_table_aest_generic_resource_substructure *generic =
+			(fwts_acpi_table_aest_generic_resource_substructure *)
+			((uint8_t *)entry + sizeof(fwts_acpi_table_aest_processor));
+		fwts_log_info_simp_int(fw, "    Data:                               ", generic->data);
+		*offset += sizeof(fwts_acpi_table_aest_generic_resource_substructure);
+	} else
+		fwts_acpi_reserved_type(fw, "AEST", entry->resource_type, 0, FWTS_AEST_RESOURCE_RESERVED, passed);
+
+}
+
+static void aest_interface_test(
+	fwts_framework *fw,
+	fwts_acpi_table_aest_interface *entry,
+	bool *passed)
+{
+	uint32_t reserved = entry->reserved[0] + (entry->reserved[1] << 8) + (entry->reserved[2] << 16);
+
+	fwts_log_info_verbatim(fw, "    Interface:");
+	fwts_log_info_simp_int(fw, "    Interface Type:                 ", entry->interface_type);
+	fwts_log_info_simp_int(fw, "    Reserved:                       ", reserved);
+	fwts_log_info_simp_int(fw, "    Flags:                          ", entry->flags);
+	fwts_log_info_simp_int(fw, "    Base Address:                   ", entry->base_address);
+	fwts_log_info_simp_int(fw, "    Start Error Record Index:       ", entry->start_error_record_index);
+	fwts_log_info_simp_int(fw, "    Number Of Error Records:        ", entry->number_of_error_records);
+	fwts_log_info_simp_int(fw, "    Error Record Implemented:       ", entry->error_record_implemented);
+	fwts_log_info_simp_int(fw, "    Status Reporting Supported:     ", entry->error_record_based_status_reporting_supported);
+	fwts_log_info_simp_int(fw, "    Addressing Mode:                ", entry->addressing_mode);
+
+	fwts_acpi_reserved_zero("AEST", "Reserved", reserved, passed);
+	fwts_acpi_reserved_bits("AEST", "Flags", entry->flags, 2, 31, passed);
+	fwts_acpi_reserved_type(fw, "AEST", entry->interface_type, 0, 2, passed);
+}
+
+static void aest_interrupt_test(
+	fwts_framework *fw,
+	fwts_acpi_table_aest_interrupt *entry,
+	bool *passed)
+{
+	uint32_t reserved1 = entry->reserved1[0] + (entry->reserved1[1] << 8)+ (entry->reserved1[2] << 16);
+
+	fwts_log_info_simp_int(fw, "    Interrupt Type:                 ", entry->interrupt_type);
+	fwts_log_info_simp_int(fw, "    Reserved:                       ", entry->reserved);
+	fwts_log_info_simp_int(fw, "    Interrupt Flags:                ", entry->interrupt_flags);
+	fwts_log_info_simp_int(fw, "    Interrupt GSIV:                 ", entry->interrupt_gsiv);
+	fwts_log_info_simp_int(fw, "    ID:                             ", entry->id);
+	fwts_log_info_simp_int(fw, "    Reserved1:                      ", reserved1);
+
+	fwts_acpi_reserved_zero("AEST", "Reserved", entry->reserved, passed);
+	fwts_acpi_reserved_bits("AEST", "Interrupt Type", entry->interrupt_type, 2, 7, passed);
+	fwts_acpi_reserved_bits("AEST", "Interrupt Flags", entry->interrupt_flags, 1, 31, passed);
+	fwts_acpi_reserved_zero("AEST", "Reserved1", reserved1, passed);
+	fwts_acpi_reserved_type(fw, "AEST", entry->interrupt_type, 0, 2, passed);
+}
+
+static int aest_test1(fwts_framework *fw)
+{
+	fwts_acpi_table_aest_node *node;
+	bool passed = true;
+	uint32_t offset;
+
+
+	fwts_log_info_verbatim(fw, "AEST Arm Error Source Table:");
+
+	node = (fwts_acpi_table_aest_node *)(table->data + sizeof(fwts_acpi_table_aest));
+	offset = sizeof(fwts_acpi_table_aest);
+	while (offset < table->length) {
+		char buffer[128];
+		fwts_acpi_table_aest_processor *processor = NULL;
+		fwts_acpi_table_aest_memory_controller *mem_controller = NULL;
+		fwts_acpi_table_aest_smmu *smmu = NULL;
+		fwts_acpi_table_aest_vendor_defined *vendor_defined = NULL;
+		fwts_acpi_table_aest_gic *gic = NULL;
+
+		if (fwts_acpi_structure_length_zero(fw, "AEST", node->length, offset)) {
+			passed = false;
+			break;
+		}
+
+		fwts_log_info_verbatim(fw, "  AEST node structure:");
+		fwts_log_info_simp_int(fw, "    Type:                           ", node->type);
+		fwts_log_info_simp_int(fw, "    Length:                         ", node->length);
+		fwts_log_info_simp_int(fw, "    Reserved:                       ", node->reserved);
+		fwts_log_info_simp_int(fw, "    Offset to Node-specific data:   ", node->offset_node_specific_data);
+		fwts_log_info_simp_int(fw, "    Offset to Node Interface:       ", node->offset_node_interface);
+		fwts_log_info_simp_int(fw, "    Offset to Node Interrupt Array: ", node->offset_node_interrupt_array);
+		fwts_log_info_simp_int(fw, "    Node Interrupt Array Size:      ", node->node_interrupt_size);
+		fwts_log_info_simp_int(fw, "    Timestamp Rate:                 ", node->timestamp_rate);
+		fwts_log_info_simp_int(fw, "    Reserved1:                      ", node->reserved1);
+		fwts_log_info_simp_int(fw, "    Error Injection Countdown Rate: ", node->error_injection_countdown_rate);
+
+		fwts_acpi_reserved_zero("AEST", "Reserved", node->reserved, &passed);
+		fwts_acpi_reserved_zero("AEST", "Reserved1", node->reserved1, &passed);
+
+		offset += sizeof(fwts_acpi_table_aest_node);
+
+		switch(node->type) {
+			case FWTS_AEST_PROCESSOR:
+				processor = (fwts_acpi_table_aest_processor *)(table->data + offset);
+				aest_processor_test(fw, processor, &offset, &passed);
+				break;
+			case FWTS_AEST_MEMORY:
+				mem_controller = (fwts_acpi_table_aest_memory_controller *)(table->data + offset);
+				fwts_log_info_verbatim(fw, "    Node-specific data (Memory Controller):");
+				fwts_log_info_simp_int(fw, "    Proximity Domain:               ",
+					mem_controller->proximity_domain);
+				offset += sizeof(fwts_acpi_table_aest_memory_controller);
+				break;
+			case FWTS_AEST_SMMU:
+				smmu = (fwts_acpi_table_aest_smmu *)(table->data + offset);
+				fwts_log_info_verbatim(fw, "    Node-specific data (SMMU):");
+				fwts_log_info_simp_int(fw, "    IORT Node Reference:            ",
+					smmu->iort_node_reference);
+				fwts_log_info_simp_int(fw, "    SMMU-specific Data Subcomponent Reference: ",
+					smmu->smmu_specific_data_subcomponent_reference);
+				offset += sizeof(fwts_acpi_table_aest_smmu);
+				break;
+			case FWTS_AEST_VENDOR_DEFINED:
+				vendor_defined = (fwts_acpi_table_aest_vendor_defined *)(table->data + offset);
+				fwts_log_info_verbatim(fw, "    Node-specific data (Vendor-defined):");
+				fwts_log_info_simp_int(fw, "    Hardware ID:                        ",
+					vendor_defined->hardware_id);
+				fwts_log_info_simp_int(fw, "    SMMU-specific Data Subcomponent Reference: ",
+					vendor_defined->unique_id);
+				fwts_log_info_verbatim(fw, "    Vendor-specific data:");
+				fwts_dump_raw_data(buffer, sizeof(buffer),
+					vendor_defined->vendor_specific_data,
+					0,
+					sizeof(vendor_defined->vendor_specific_data));
+				fwts_log_info_verbatim(fw, "%s", buffer);
+				offset += sizeof(fwts_acpi_table_aest_vendor_defined);
+				break;
+			case FWTS_AEST_GIC:
+				gic = (fwts_acpi_table_aest_gic *)(table->data + offset);
+				fwts_log_info_verbatim(fw, "    Node-specific data (GIC):");
+				fwts_log_info_simp_int(fw, "    Interface Type:                 ",
+					gic->interface_type);
+				fwts_log_info_simp_int(fw, "    Instance Identifier:            ",
+					gic->instance_identifier);
+				fwts_acpi_reserved_type(fw, "AEST",
+					gic->interface_type,
+					0,
+					FWTS_AEST_GIC_RESERVED, &passed);
+				offset += sizeof(fwts_acpi_table_aest_gic);
+				break;
+			default:
+				fwts_acpi_reserved_type(fw, "AEST", node->type, 0, FWTS_AEST_RESERVED, &passed);
+				break;
+		}
+
+		if (node->offset_node_interface >= sizeof(fwts_acpi_table_aest_interface)) {
+			aest_interface_test(fw, (fwts_acpi_table_aest_interface *)(table->data + offset), &passed);
+			offset += sizeof(fwts_acpi_table_aest_interface);
+		}
+
+		uint32_t i;
+
+		for (i = 0; i < node->node_interrupt_size; i++) {
+			fwts_log_info_verbatim(fw, "    Interrupt Array");
+			aest_interrupt_test(fw, (fwts_acpi_table_aest_interrupt *)(table->data + offset), &passed);
+			offset += sizeof(fwts_acpi_table_aest_interrupt);
+		}
+
+		node = (fwts_acpi_table_aest_node *)(table->data + offset);
+		fwts_log_nl(fw);
+	}
+
+	if (passed)
+		fwts_passed(fw, "No issues found in AEST table.");
+
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test aest_tests[] = {
+	{ aest_test1, "Validate AEST table." },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops aest_ops = {
+	.description = "AEST Arm Error Source Table test.",
+	.init        = AEST_init,
+	.minor_tests = aest_tests
+};
+
+FWTS_REGISTER("aest", &aest_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_ACPI)
+
+#endif
diff --git a/src/lib/include/fwts_acpi.h b/src/lib/include/fwts_acpi.h
index ebeb6363..60e6e48a 100644
--- a/src/lib/include/fwts_acpi.h
+++ b/src/lib/include/fwts_acpi.h
@@ -2359,4 +2359,120 @@  typedef struct {
 	uint64_t	base_addr;
 } __attribute__ ((packed)) fwts_acpi_table_cedt_rdpas;
 
+/*
+ * ACPI AEST (Arm Error Source Table)
+ * https://developer.arm.com/documentation/den0085/latest/
+ */
+typedef struct {
+	fwts_acpi_table_header	header;
+} __attribute__ ((packed)) fwts_acpi_table_aest;
+
+typedef enum {
+	FWTS_AEST_PROCESSOR		= 0,
+	FWTS_AEST_MEMORY		= 1,
+	FWTS_AEST_SMMU			= 2,
+	FWTS_AEST_VENDOR_DEFINED	= 3,
+	FWTS_AEST_GIC			= 4,
+	FWTS_AEST_RESERVED
+} fwts_acpi_aest_type;
+
+typedef struct {
+	uint8_t		type;
+	uint16_t	length;
+	uint8_t		reserved;
+	uint32_t	offset_node_specific_data;
+	uint32_t	offset_node_interface;
+	uint32_t	offset_node_interrupt_array;
+	uint32_t	node_interrupt_size;
+	uint64_t	timestamp_rate;
+	uint64_t	reserved1;
+	uint64_t	error_injection_countdown_rate;
+	/*
+	 * followed by Node-specific data, Node interface
+	 * and Node Interrupt array
+	 */
+} __attribute__ ((packed)) fwts_acpi_table_aest_node;
+
+typedef enum {
+	FWTS_AEST_RESOURCE_CACHE	= 0,
+	FWTS_AEST_RESOURCE_TLB		= 1,
+	FWTS_AEST_RESOURCE_GENERIC	= 2,
+	FWTS_AEST_RESOURCE_RESERVED
+} fwts_acpi_aest_resource_type;
+
+typedef struct {
+	uint32_t	acpi_processor_id;
+	uint8_t		resource_type;
+	uint8_t		reserved;
+	uint8_t		flags;
+	uint8_t		revision;
+	uint64_t	processor_affinity_level_indicator;
+	/*
+	 * followed by Resource substructure
+	 */
+} __attribute__ ((packed)) fwts_acpi_table_aest_processor;
+
+typedef struct {
+	uint32_t	cache_reference;
+	uint32_t	reserved;
+} __attribute__ ((packed)) fwts_acpi_table_aest_cache_resource_substructure;
+
+typedef struct {
+	uint32_t	tlb_reference;
+	uint32_t	reserved;
+} __attribute__ ((packed)) fwts_acpi_table_aest_tlb_resource_substructure;
+
+typedef struct {
+	uint32_t	data;
+} __attribute__ ((packed)) fwts_acpi_table_aest_generic_resource_substructure;
+
+typedef struct {
+	uint32_t	proximity_domain;
+} __attribute__ ((packed)) fwts_acpi_table_aest_memory_controller;
+
+typedef struct {
+	uint32_t	iort_node_reference;
+	uint32_t	smmu_specific_data_subcomponent_reference;
+} __attribute__ ((packed)) fwts_acpi_table_aest_smmu;
+
+typedef struct {
+	uint32_t	hardware_id;
+	uint32_t	unique_id;
+	uint8_t		vendor_specific_data[16];
+} __attribute__ ((packed)) fwts_acpi_table_aest_vendor_defined;
+
+typedef enum {
+	FWTS_AEST_GICC	= 0,
+	FWTS_AEST_GICD	= 1,
+	FWTS_AEST_GICR	= 2,
+	FWTS_AEST_GOCS	= 3,
+	FWTS_AEST_GIC_RESERVED
+} fwts_acpi_aest_gic_type;
+
+typedef struct {
+	uint32_t	interface_type;
+	uint32_t	instance_identifier;
+} __attribute__ ((packed)) fwts_acpi_table_aest_gic;
+
+typedef struct {
+	uint8_t		interface_type;
+	uint8_t		reserved[3];
+	uint32_t	flags;
+	uint64_t	base_address;
+	uint32_t	start_error_record_index;
+	uint32_t	number_of_error_records;
+	uint64_t	error_record_implemented;
+	uint64_t	error_record_based_status_reporting_supported;
+	uint64_t	addressing_mode;
+} __attribute__ ((packed)) fwts_acpi_table_aest_interface;
+
+typedef struct {
+	uint8_t		interrupt_type;
+	uint16_t	reserved;
+	uint8_t		interrupt_flags;
+	uint32_t	interrupt_gsiv;
+	uint8_t		id;
+	uint8_t		reserved1[3];
+} __attribute__ ((packed)) fwts_acpi_table_aest_interrupt;
+
 #endif