diff mbox

[1/2] tcpa: Add ACPI TCPA test

Message ID 1435569852-26328-2-git-send-email-alex.hung@canonical.com
State Rejected
Headers show

Commit Message

Alex Hung June 29, 2015, 9:24 a.m. UTC
Signed-off-by: Alex Hung <alex.hung@canonical.com>
---
 src/Makefile.am      |   1 +
 src/acpi/tcpa/tcpa.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 225 insertions(+)
 create mode 100644 src/acpi/tcpa/tcpa.c

Comments

Colin Ian King June 29, 2015, 10:04 a.m. UTC | #1
On 29/06/15 10:24, Alex Hung wrote:
> Signed-off-by: Alex Hung <alex.hung@canonical.com>
> ---
>  src/Makefile.am      |   1 +
>  src/acpi/tcpa/tcpa.c | 224 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 225 insertions(+)
>  create mode 100644 src/acpi/tcpa/tcpa.c
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 0a85265..4a1794a 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -70,6 +70,7 @@ fwts_SOURCES = main.c 				\
>  	acpi/slit/slit.c 			\
>  	acpi/spcr/spcr.c 			\
>  	acpi/spmi/spmi.c 			\
> +	acpi/tcpa/tcpa.c 			\
>  	acpi/srat/srat.c 			\
>  	acpi/syntaxcheck/syntaxcheck.c 		\
>  	acpi/wakealarm/wakealarm.c 		\
> diff --git a/src/acpi/tcpa/tcpa.c b/src/acpi/tcpa/tcpa.c
> new file mode 100644
> index 0000000..4a45581
> --- /dev/null
> +++ b/src/acpi/tcpa/tcpa.c
> @@ -0,0 +1,224 @@
> +/*
> + * Copyright (C) 2010-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.
> + *
> + */
> +#include "fwts.h"
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <inttypes.h>
> +#include <stdbool.h>
> +
> +static fwts_acpi_table_info *table;
> +
> +static int tcpa_init(fwts_framework *fw)
> +{
> +	if (fwts_acpi_find_table(fw, "TCPA", 0, &table) != FWTS_OK) {
> +		fwts_log_error(fw, "Cannot load ACPI table");
> +		return FWTS_ERROR;
> +	}
> +	if (table == NULL) {
> +		fwts_log_error(fw, "ACPI TCPA table does not exist, skipping test");
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +

A comment with a reference to the specification would be helpful here

> +static int tcpa_client_test(fwts_framework *fw, fwts_acpi_table_tcpa *tcpa)
> +{
> +	bool passed = true;
> +
> +	if (tcpa->header.length != 50) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"TCPABadSize",
> +			"TCPA size is incorrect, expecting 0x32 bytes, "
> +			"instead got 0x%8.8" PRIx32 " bytes",
> +			tcpa->header.length);
> +	}
> +
> +	if (tcpa->header.revision != 2) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"TCPABadRevision",
> +			"TCPA revision is incorrect, expecting 0x02, "
> +			"instead got 0x%2.2" PRIx8,
> +			tcpa->header.revision);
> +	}
> +
> +	fwts_log_info_verbatum(fw, "TCPA Table:");
> +	fwts_log_info_verbatum(fw, "  Platform Class:                  0x%4.4"   PRIx16, tcpa->platform_class);
> +	fwts_log_info_verbatum(fw, "  Log Area Minimum Length:         0x%8.8"   PRIx32, tcpa->client.log_zone_length);
> +	fwts_log_info_verbatum(fw, "  Log Area Start Address:          0x%16.16" PRIx64, tcpa->client.log_zone_addr);
> +
> +	return passed;
> +}
> +
> +static int tcpa_server_test(fwts_framework *fw, fwts_acpi_table_tcpa *tcpa)
> +{
> +	bool passed = true;
> +	uint32_t reserved2;
> +
> +	if (tcpa->header.length != 100) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"TCPABadSize",
> +			"TCPA size is incorrect, expecting 0x64 bytes, "
> +			"instead got 0x%8.8" PRIx32 " bytes",
> +			tcpa->header.length);
> +	}
> +
> +	if (tcpa->header.revision != 2) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"TCPABadRevision",
> +			"TCPA revision is incorrect, expecting 0x02, "
> +			"instead got 0x%2.2" PRIx8,
> +			tcpa->header.revision);
> +	}
> +
> +	reserved2 = tcpa->server.reserved2[0] + (tcpa->server.reserved2[1] << 4) + (tcpa->server.reserved2[2] << 8);
> +
> +	fwts_log_info_verbatum(fw, "TCPA Table:");
> +	fwts_log_info_verbatum(fw, "  Platform Class:                  0x%4.4"   PRIx16, tcpa->platform_class);
> +	fwts_log_info_verbatum(fw, "  Reserved:	                       0x%4.4"   PRIx16, tcpa->server.reserved);
> +	fwts_log_info_verbatum(fw, "  Log Area Minimum Length:         0x%16.16" PRIx64, tcpa->server.log_zone_length);
> +	fwts_log_info_verbatum(fw, "  Log Area Start Address:          0x%16.16" PRIx64, tcpa->server.log_zone_addr);
> +	fwts_log_info_verbatum(fw, "  Specification Revision:          0x%4.4"   PRIx16, tcpa->server.spec_revision);
> +	fwts_log_info_verbatum(fw, "  Device Flags:                    0x%2.2"   PRIx16, tcpa->server.device_flag);
> +	fwts_log_info_verbatum(fw, "  Interrupt Flags:                 0x%2.2"   PRIx16, tcpa->server.interrupt_flag);
> +	fwts_log_info_verbatum(fw, "  GPE:                             0x%2.2"   PRIx16, tcpa->server.gpe);
> +	fwts_log_info_verbatum(fw, "  Reserved:                        0x%8.8"   PRIx32, reserved2);
> +	fwts_log_info_verbatum(fw, "  Global System Interrupt:         0x%8.8"   PRIx32, tcpa->server.global_sys_interrupt);
> +	fwts_log_info_verbatum(fw, "  Base Address:");
> +	fwts_log_info_verbatum(fw, "    Address Space ID:              0x%2.2"   PRIx8, tcpa->server.base_addr.address_space_id);
> +	fwts_log_info_verbatum(fw, "    Register Bit Width             0x%2.2"   PRIx8, tcpa->server.base_addr.register_bit_width);
> +	fwts_log_info_verbatum(fw, "    Register Bit Offset            0x%2.2"   PRIx8, tcpa->server.base_addr.register_bit_offset);
> +	fwts_log_info_verbatum(fw, "    Access Size                    0x%2.2"   PRIx8, tcpa->server.base_addr.access_width);
> +	fwts_log_info_verbatum(fw, "    Address                        0x%16.16" PRIx64, tcpa->server.base_addr.address);
> +	fwts_log_info_verbatum(fw, "  Reserved:                        0x%8.8"   PRIx32, tcpa->server.reserved3);
> +	fwts_log_info_verbatum(fw, "  Configuration Address:");
> +	fwts_log_info_verbatum(fw, "    Address Space ID:              0x%2.2"   PRIx8, tcpa->server.config_addr.address_space_id);
> +	fwts_log_info_verbatum(fw, "    Register Bit Width             0x%2.2"   PRIx8, tcpa->server.config_addr.register_bit_width);
> +	fwts_log_info_verbatum(fw, "    Register Bit Offset            0x%2.2"   PRIx8, tcpa->server.config_addr.register_bit_offset);
> +	fwts_log_info_verbatum(fw, "    Access Size                    0x%2.2"   PRIx8, tcpa->server.config_addr.access_width);
> +	fwts_log_info_verbatum(fw, "    Address                        0x%16.16" PRIx64, tcpa->server.config_addr.address);
> +	fwts_log_info_verbatum(fw, "  PCI Segment Group:               0x%2.2"   PRIx8, tcpa->server.pci_seg_number);
> +	fwts_log_info_verbatum(fw, "  PCI Bus:                         0x%2.2"   PRIx8, tcpa->server.pci_bus_number);
> +	fwts_log_info_verbatum(fw, "  PCI Device:                      0x%2.2"   PRIx8, tcpa->server.pci_dev_number);
> +	fwts_log_info_verbatum(fw, "  PCI Function:                    0x%2.2"   PRIx8, tcpa->server.pci_func_number);
> +
> +	if (tcpa->server.reserved != 0) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_LOW,
> +			"TCPAReservedNonZero",
> +			"TCPA first reserved field must be zero, got "
> +			"0x%2.2" PRIx8 " instead", tcpa->server.reserved);
> +	}
> +
> +	if (reserved2 != 0) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_LOW,
> +			"TCPAReservedNonZero",
> +			"TCPA second reserved field must be zero, got "
> +			"0x%2.2" PRIx8 " instead", reserved2);
> +	}
> +
> +	if (tcpa->server.reserved3 != 0) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_LOW,
> +			"TCPAReservedNonZero",
> +			"TCPA third reserved field must be zero, got "
> +			"0x%2.2" PRIx8 " instead", tcpa->server.reserved3);
> +	}
> +
> +	if (tcpa->server.device_flag & 1) {
> +		if (!(tcpa->server.interrupt_flag & 2)) {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"TCPABadInterruptPolarity",
> +				"TCPA interrupt Polarity should be one, got zero");
> +		}
> +
> +		if (tcpa->server.interrupt_flag & 1) {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"TCPABadInterruptMode",
> +				"TCPA interrupt mode should be zero, got one");
> +		}
> +	}
> +
> +	if (tcpa->server.base_addr.address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_MEMORY &&
> +	    tcpa->server.base_addr.address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_IO) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"TCPABadAddressID",
> +			"TCPA base address ID must be 1 or zero, got "
> +			"0x%2.2" PRIx8 " instead", tcpa->server.base_addr.address_space_id);
> +	}
> +
> +	if (tcpa->server.config_addr.address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_MEMORY &&
> +	    tcpa->server.config_addr.address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_IO) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"TCPABadAddressID",
> +			"TCPA configuration address ID must be 1 or zero, got "
> +			"0x%2.2" PRIx8 " instead", tcpa->server.config_addr.address_space_id);
> +	}
> +
> +	return passed;
> +}
> +
> +static int tcpa_test1(fwts_framework *fw)
> +{
> +	fwts_acpi_table_tcpa *tcpa = (fwts_acpi_table_tcpa*)table->data;
> +	bool passed = true;
> +
> +	switch (tcpa->platform_class) {
> +	case 0:
> +		passed = tcpa_client_test(fw, tcpa);
> +		break;
> +	case 1:
> +		passed = tcpa_server_test(fw, tcpa);
> +		break;
> +	default:
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"TCPABadPlatformClass",
> +			"TCPA's platform class must be zero or one, got 0x%" PRIx16,
> +			tcpa->platform_class);
> +		break;
> +	}
> +
> +	if (passed)
> +		fwts_passed(fw, "No issues found in TCPA table.");
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test tcpa_tests[] = {
> +	{ tcpa_test1, "Validate TCPA table." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops tcpa_ops = {
> +	.description = "Trusted Computing Platform Alliance Capabilities Table test.",
> +	.init        = tcpa_init,
> +	.minor_tests = tcpa_tests
> +};
> +
> +FWTS_REGISTER("tcpa", &tcpa_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_ROOT_PRIV | FWTS_FLAG_TEST_ACPI)
> 

The ROOT PRIV is not required for several reasons:
1. The acpi table loading does the too check implicitly for tables that
are loaded from memory
2. When loading tables from file one may not want to run with root priv

When I ran the fwts-check as a normal user I got the following
difference between the desired results and the actual results:

< tcpa            Must be run as root or sudo to be able to read system
< tcpa            information.
< tcpa            Aborted test, insufficient privilege.

..which is because of the FWTS_FLAG_ROOT_PRIV check.

Apart from this, looks OK
diff mbox

Patch

diff --git a/src/Makefile.am b/src/Makefile.am
index 0a85265..4a1794a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -70,6 +70,7 @@  fwts_SOURCES = main.c 				\
 	acpi/slit/slit.c 			\
 	acpi/spcr/spcr.c 			\
 	acpi/spmi/spmi.c 			\
+	acpi/tcpa/tcpa.c 			\
 	acpi/srat/srat.c 			\
 	acpi/syntaxcheck/syntaxcheck.c 		\
 	acpi/wakealarm/wakealarm.c 		\
diff --git a/src/acpi/tcpa/tcpa.c b/src/acpi/tcpa/tcpa.c
new file mode 100644
index 0000000..4a45581
--- /dev/null
+++ b/src/acpi/tcpa/tcpa.c
@@ -0,0 +1,224 @@ 
+/*
+ * Copyright (C) 2010-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.
+ *
+ */
+#include "fwts.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <stdbool.h>
+
+static fwts_acpi_table_info *table;
+
+static int tcpa_init(fwts_framework *fw)
+{
+	if (fwts_acpi_find_table(fw, "TCPA", 0, &table) != FWTS_OK) {
+		fwts_log_error(fw, "Cannot load ACPI table");
+		return FWTS_ERROR;
+	}
+	if (table == NULL) {
+		fwts_log_error(fw, "ACPI TCPA table does not exist, skipping test");
+		return FWTS_ERROR;
+	}
+
+	return FWTS_OK;
+}
+
+static int tcpa_client_test(fwts_framework *fw, fwts_acpi_table_tcpa *tcpa)
+{
+	bool passed = true;
+
+	if (tcpa->header.length != 50) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"TCPABadSize",
+			"TCPA size is incorrect, expecting 0x32 bytes, "
+			"instead got 0x%8.8" PRIx32 " bytes",
+			tcpa->header.length);
+	}
+
+	if (tcpa->header.revision != 2) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"TCPABadRevision",
+			"TCPA revision is incorrect, expecting 0x02, "
+			"instead got 0x%2.2" PRIx8,
+			tcpa->header.revision);
+	}
+
+	fwts_log_info_verbatum(fw, "TCPA Table:");
+	fwts_log_info_verbatum(fw, "  Platform Class:                  0x%4.4"   PRIx16, tcpa->platform_class);
+	fwts_log_info_verbatum(fw, "  Log Area Minimum Length:         0x%8.8"   PRIx32, tcpa->client.log_zone_length);
+	fwts_log_info_verbatum(fw, "  Log Area Start Address:          0x%16.16" PRIx64, tcpa->client.log_zone_addr);
+
+	return passed;
+}
+
+static int tcpa_server_test(fwts_framework *fw, fwts_acpi_table_tcpa *tcpa)
+{
+	bool passed = true;
+	uint32_t reserved2;
+
+	if (tcpa->header.length != 100) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"TCPABadSize",
+			"TCPA size is incorrect, expecting 0x64 bytes, "
+			"instead got 0x%8.8" PRIx32 " bytes",
+			tcpa->header.length);
+	}
+
+	if (tcpa->header.revision != 2) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"TCPABadRevision",
+			"TCPA revision is incorrect, expecting 0x02, "
+			"instead got 0x%2.2" PRIx8,
+			tcpa->header.revision);
+	}
+
+	reserved2 = tcpa->server.reserved2[0] + (tcpa->server.reserved2[1] << 4) + (tcpa->server.reserved2[2] << 8);
+
+	fwts_log_info_verbatum(fw, "TCPA Table:");
+	fwts_log_info_verbatum(fw, "  Platform Class:                  0x%4.4"   PRIx16, tcpa->platform_class);
+	fwts_log_info_verbatum(fw, "  Reserved:	                       0x%4.4"   PRIx16, tcpa->server.reserved);
+	fwts_log_info_verbatum(fw, "  Log Area Minimum Length:         0x%16.16" PRIx64, tcpa->server.log_zone_length);
+	fwts_log_info_verbatum(fw, "  Log Area Start Address:          0x%16.16" PRIx64, tcpa->server.log_zone_addr);
+	fwts_log_info_verbatum(fw, "  Specification Revision:          0x%4.4"   PRIx16, tcpa->server.spec_revision);
+	fwts_log_info_verbatum(fw, "  Device Flags:                    0x%2.2"   PRIx16, tcpa->server.device_flag);
+	fwts_log_info_verbatum(fw, "  Interrupt Flags:                 0x%2.2"   PRIx16, tcpa->server.interrupt_flag);
+	fwts_log_info_verbatum(fw, "  GPE:                             0x%2.2"   PRIx16, tcpa->server.gpe);
+	fwts_log_info_verbatum(fw, "  Reserved:                        0x%8.8"   PRIx32, reserved2);
+	fwts_log_info_verbatum(fw, "  Global System Interrupt:         0x%8.8"   PRIx32, tcpa->server.global_sys_interrupt);
+	fwts_log_info_verbatum(fw, "  Base Address:");
+	fwts_log_info_verbatum(fw, "    Address Space ID:              0x%2.2"   PRIx8, tcpa->server.base_addr.address_space_id);
+	fwts_log_info_verbatum(fw, "    Register Bit Width             0x%2.2"   PRIx8, tcpa->server.base_addr.register_bit_width);
+	fwts_log_info_verbatum(fw, "    Register Bit Offset            0x%2.2"   PRIx8, tcpa->server.base_addr.register_bit_offset);
+	fwts_log_info_verbatum(fw, "    Access Size                    0x%2.2"   PRIx8, tcpa->server.base_addr.access_width);
+	fwts_log_info_verbatum(fw, "    Address                        0x%16.16" PRIx64, tcpa->server.base_addr.address);
+	fwts_log_info_verbatum(fw, "  Reserved:                        0x%8.8"   PRIx32, tcpa->server.reserved3);
+	fwts_log_info_verbatum(fw, "  Configuration Address:");
+	fwts_log_info_verbatum(fw, "    Address Space ID:              0x%2.2"   PRIx8, tcpa->server.config_addr.address_space_id);
+	fwts_log_info_verbatum(fw, "    Register Bit Width             0x%2.2"   PRIx8, tcpa->server.config_addr.register_bit_width);
+	fwts_log_info_verbatum(fw, "    Register Bit Offset            0x%2.2"   PRIx8, tcpa->server.config_addr.register_bit_offset);
+	fwts_log_info_verbatum(fw, "    Access Size                    0x%2.2"   PRIx8, tcpa->server.config_addr.access_width);
+	fwts_log_info_verbatum(fw, "    Address                        0x%16.16" PRIx64, tcpa->server.config_addr.address);
+	fwts_log_info_verbatum(fw, "  PCI Segment Group:               0x%2.2"   PRIx8, tcpa->server.pci_seg_number);
+	fwts_log_info_verbatum(fw, "  PCI Bus:                         0x%2.2"   PRIx8, tcpa->server.pci_bus_number);
+	fwts_log_info_verbatum(fw, "  PCI Device:                      0x%2.2"   PRIx8, tcpa->server.pci_dev_number);
+	fwts_log_info_verbatum(fw, "  PCI Function:                    0x%2.2"   PRIx8, tcpa->server.pci_func_number);
+
+	if (tcpa->server.reserved != 0) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_LOW,
+			"TCPAReservedNonZero",
+			"TCPA first reserved field must be zero, got "
+			"0x%2.2" PRIx8 " instead", tcpa->server.reserved);
+	}
+
+	if (reserved2 != 0) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_LOW,
+			"TCPAReservedNonZero",
+			"TCPA second reserved field must be zero, got "
+			"0x%2.2" PRIx8 " instead", reserved2);
+	}
+
+	if (tcpa->server.reserved3 != 0) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_LOW,
+			"TCPAReservedNonZero",
+			"TCPA third reserved field must be zero, got "
+			"0x%2.2" PRIx8 " instead", tcpa->server.reserved3);
+	}
+
+	if (tcpa->server.device_flag & 1) {
+		if (!(tcpa->server.interrupt_flag & 2)) {
+			passed = false;
+			fwts_failed(fw, LOG_LEVEL_HIGH,
+				"TCPABadInterruptPolarity",
+				"TCPA interrupt Polarity should be one, got zero");
+		}
+
+		if (tcpa->server.interrupt_flag & 1) {
+			passed = false;
+			fwts_failed(fw, LOG_LEVEL_HIGH,
+				"TCPABadInterruptMode",
+				"TCPA interrupt mode should be zero, got one");
+		}
+	}
+
+	if (tcpa->server.base_addr.address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_MEMORY &&
+	    tcpa->server.base_addr.address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_IO) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"TCPABadAddressID",
+			"TCPA base address ID must be 1 or zero, got "
+			"0x%2.2" PRIx8 " instead", tcpa->server.base_addr.address_space_id);
+	}
+
+	if (tcpa->server.config_addr.address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_MEMORY &&
+	    tcpa->server.config_addr.address_space_id != FWTS_GAS_ADDR_SPACE_ID_SYSTEM_IO) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"TCPABadAddressID",
+			"TCPA configuration address ID must be 1 or zero, got "
+			"0x%2.2" PRIx8 " instead", tcpa->server.config_addr.address_space_id);
+	}
+
+	return passed;
+}
+
+static int tcpa_test1(fwts_framework *fw)
+{
+	fwts_acpi_table_tcpa *tcpa = (fwts_acpi_table_tcpa*)table->data;
+	bool passed = true;
+
+	switch (tcpa->platform_class) {
+	case 0:
+		passed = tcpa_client_test(fw, tcpa);
+		break;
+	case 1:
+		passed = tcpa_server_test(fw, tcpa);
+		break;
+	default:
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"TCPABadPlatformClass",
+			"TCPA's platform class must be zero or one, got 0x%" PRIx16,
+			tcpa->platform_class);
+		break;
+	}
+
+	if (passed)
+		fwts_passed(fw, "No issues found in TCPA table.");
+
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test tcpa_tests[] = {
+	{ tcpa_test1, "Validate TCPA table." },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops tcpa_ops = {
+	.description = "Trusted Computing Platform Alliance Capabilities Table test.",
+	.init        = tcpa_init,
+	.minor_tests = tcpa_tests
+};
+
+FWTS_REGISTER("tcpa", &tcpa_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_BATCH | FWTS_FLAG_ROOT_PRIV | FWTS_FLAG_TEST_ACPI)