[03/12] sbbr/dbg2: Add test cases to dbg2 acpi table as per SBBR.

Submitted by Supreeth Venkatesh on March 2, 2017, 10:26 p.m.

Details

Message ID 1488493596-3437-4-git-send-email-supreeth.venkatesh@arm.com
State New
Headers show

Commit Message

Supreeth Venkatesh March 2, 2017, 10:26 p.m.
Server Base Boot Requirements (SBBR) specification is intended for SBSA-
compliant 64-bit ARMv8 servers.
It defines the base firmware requirements for out-of-box support of any
ARM SBSA-compatible Operating System or hypervisor.
The requirements in this specification are expected to be minimal yet
complete for booting a multi-core ARMv8 server platform, while leaving
plenty of room for OEM or ODM innovations and design details.
For more information, download the SBBR specification here:
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044b/index.html

This change introduces test cases as per SBBR specification to dbg2
table. These test cases may be subset/superset of acpi tests already
existing. However, to preserve "sbbr" classification, new file is
created, even when most of the code will be re-used from acpi.

Signed-off-by: Supreeth Venkatesh <supreeth.venkatesh@arm.com>
---
 src/sbbr/dbg2/dbg2.c | 388 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 388 insertions(+)
 create mode 100644 src/sbbr/dbg2/dbg2.c

Comments

Colin King March 2, 2017, 11:19 p.m.
On 02/03/17 22:26, Supreeth Venkatesh wrote:
> Server Base Boot Requirements (SBBR) specification is intended for SBSA-
> compliant 64-bit ARMv8 servers.
> It defines the base firmware requirements for out-of-box support of any
> ARM SBSA-compatible Operating System or hypervisor.
> The requirements in this specification are expected to be minimal yet
> complete for booting a multi-core ARMv8 server platform, while leaving
> plenty of room for OEM or ODM innovations and design details.
> For more information, download the SBBR specification here:
> http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0044b/index.html
> 
> This change introduces test cases as per SBBR specification to dbg2
> table. These test cases may be subset/superset of acpi tests already
> existing. However, to preserve "sbbr" classification, new file is
> created, even when most of the code will be re-used from acpi.
> 
> Signed-off-by: Supreeth Venkatesh <supreeth.venkatesh@arm.com>
> ---
>  src/sbbr/dbg2/dbg2.c | 388 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 388 insertions(+)
>  create mode 100644 src/sbbr/dbg2/dbg2.c
> 
> diff --git a/src/sbbr/dbg2/dbg2.c b/src/sbbr/dbg2/dbg2.c
> new file mode 100644
> index 0000000..3056791
> --- /dev/null
> +++ b/src/sbbr/dbg2/dbg2.c
> @@ -0,0 +1,388 @@
> +/*
> + * Copyright (C) 2015-2017 Canonical
> + * Copyright (C) 2017 ARM Ltd
> + *
> + * 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_SBBR)
> +
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <unistd.h>
> +#include <inttypes.h>
> +#include <string.h>
> +
> +#include "fwts_acpi_object_eval.h"
> +
> +static fwts_acpi_table_info *table;
> +
> +static int sbbr_dbg2_init(fwts_framework *fw)
> +{
> +	if (fwts_acpi_find_table(fw, "DBG2", 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 DBG2 table does not exist");
> +		return FWTS_ERROR;
> +	}
> +
> +	return FWTS_OK;
> +}
> +
> +static int sbbr_dbg2_sbsa_uart_test(fwts_framework *fw)
> +{
> +	uint32_t i;
> +
> +	fwts_acpi_table_dbg2 *dbg2 = (fwts_acpi_table_dbg2 *)table->data;
> +	fwts_acpi_table_dbg2_info *info;
> +
> +	const int SBBR_DBG2_PORT_SERIAL = 0x8000;
> +	const int SBBR_DBG2_ARM_SBSA_UART = 0x000E;
> +
> +	info = (fwts_acpi_table_dbg2_info *)(table->data + dbg2->info_offset);
> +
> +	for (i = 0; i < dbg2->info_count; i++) {
> +		if( (info->port_type    == SBBR_DBG2_PORT_SERIAL  ) &&
> +			(info->port_subtype == SBBR_DBG2_ARM_SBSA_UART)    )

There's quite a bit of white spacing that needs fixing up, I'd prefer:

		if ((info->port_type == SBBR_DBG2_PORT_SERIAL) &&
		    (info->port_subtype == SBBR_DBG2_ARM_SBSA_UART)) {


> +			fwts_passed(fw, "DBG2 provides a standard serial debug port and describes ARM SBSA Generic UART");
> +			return FWTS_OK;
> +		}
> +
> +		/* ..and onto the next info structure .. */
> +		info = (fwts_acpi_table_dbg2_info *)((uint8_t *)info + info->length);
> +	}
> +
> +	fwts_failed(fw, LOG_LEVEL_CRITICAL, "dbg2_sbsa_uart:", "DBG2 provides a non standard debug port");
> +	return FWTS_OK;
> +}
> +
> +
> +/*
> + *  dbg2_check_offset()
> + *	check if offset does not fall off end of table
> + */
> +static void dbg2_check_offset(
> +	fwts_framework *fw,
> +	const size_t table_length,
> +	const size_t offset,
> +	const char *msg,
> +	bool *passed)
> +{
> +	if (table_length < offset) {
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"DBG2TooShort",
> +			"DBG2 table too short, expecting %zu bytes, "
> +			"instead got %zu bytes for a DBG2 table and %s",
> +			offset, table->length, msg);
> +		*passed = false;
> +	}
> +}
> +
> +/*
> + *  dbg2_check_namespace_string()
> + *	check namespace string is '\0' termimated
> + */
> +static void dbg2_check_namespace_string(
> +	fwts_framework *fw,
> +	const char *str,
> +	size_t length,
> +	bool *passed)
> +{
> +	size_t i;
> +
> +	for (i = 0; i < length; i++) {
> +		if (*(str + i) == '\0')
> +			return;
> +	}
> +	fwts_failed(fw, LOG_LEVEL_HIGH,
> +		"DBG2NameSpaceStringNull",
> +		"DBG2 Name Space String was not null terminated");
> +	*passed = false;
> +}
> +
> +
> +/*
> + *  DBG2 Table
> + *   see https://msdn.microsoft.com/en-us/library/windows/hardware/dn639131%28v=vs.85%29.aspx
> + */
> +static int sbbr_dbg2_generic_test(fwts_framework *fw)
> +{
> +	bool passed = true;
> +	fwts_acpi_table_dbg2 *dbg2 = (fwts_acpi_table_dbg2 *)table->data;
> +	fwts_acpi_table_dbg2_info *info;
> +	uint32_t i;
> +	size_t total_size;
> +
> +	/* Enough length for the initial dbg2 header? */
> +	if (table->length < sizeof(fwts_acpi_table_dbg2)) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"DBG2TooShort",
> +			"DBG2 table too short, expecting %zu bytes, "
> +			"instead got %zu bytes",
> +			sizeof(fwts_acpi_table_dbg2), table->length);
> +		goto done;
> +	}
> +
> +	fwts_log_info_verbatim(fw, "DBG2 Table:");
> +	fwts_log_info_verbatim(fw, "  Info Offset:              0x%8.8" PRIx32, dbg2->info_offset);
> +	fwts_log_info_verbatim(fw, "  Info Count:               0x%8.8" PRIx32, dbg2->info_count);
> +	fwts_log_nl(fw);
> +
> +	total_size = dbg2->info_offset +
> +		(dbg2->info_count * sizeof(fwts_acpi_table_dbg2_info));
> +	if (table->length < total_size) {
> +		passed = false;
> +		fwts_failed(fw, LOG_LEVEL_HIGH,
> +			"DBG2TooShort",
> +			"DBG2 table too short, expecting %zu bytes, "
> +			"instead got %zu bytes for a DBG2 table "
> +			"containing %" PRIu32 " debug device "
> +			"information structures",
> +			sizeof(fwts_acpi_table_dbg2), table->length,
> +			dbg2->info_count);
> +		goto done;
> +	}
> +
> +	info = (fwts_acpi_table_dbg2_info *)(table->data + dbg2->info_offset);
> +
> +	/*
> +	 *  Sanity check structures
> +	 */
> +	for (i = 0; i < dbg2->info_count; i++) {
> +		uint32_t offset = (uint8_t *)info - (uint8_t *)table->data;
> +		bool ok, length_ok;
> +		char *port = NULL, *subport = NULL;
> +
> +		switch (info->port_type) {
> +		case 0x8000:
> +			port = "Serial";
> +			switch (info->port_subtype) {
> +			case 0x0000:
> +				subport = "Fully 16550-compatible";
> +				break;
> +			case 0x0001:
> +				subport = "16550 subset compatible";
> +				break;
> +			case 0x0003:
> +				subport = "ARMPL011 UART";
> +				break;
> +			case 0x000d:
> +				subport = "ARM SBSA Generic UART (deprecated)";
> +				break;
> +			case 0x000e:
> +				subport = "ARM SBSA Generic UART";
> +				break;
> +			case 0x000f:
> +				subport = "ARM DCC";
> +				break;
> +			case 0x0010:
> +				subport = "BCM2835";
> +				break;
> +			default:
> +				break;
> +			}
> +			break;
> +		case 0x8001:
> +			port = "1394";
> +			switch (info->port_subtype) {
> +			case 0x0000:
> +				subport = "IEEE1394";
> +				break;
> +			default:
> +				break;
> +			}
> +			break;
> +		case 0x8002:
> +			port = "USB";
> +			switch (info->port_subtype) {
> +			case 0x0000:
> +				subport = "XHCI controller";
> +				break;
> +			case 0x0001:
> +				subport = "EHCI controller";
> +				break;
> +			default:
> +				break;
> +			}
> +			break;
> +		case 0x8003:
> +			port = "Net";
> +			subport = "PCI";
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		fwts_log_info_verbatim(fw, "DBG2 Info Structure %" PRIu32 ":", i);
> +		fwts_log_info_verbatim(fw, "  Revision:                 0x%2.2" PRIx8, info->revision);
> +		fwts_log_info_verbatim(fw, "  Length:                   0x%4.4" PRIx16, info->length);
> +		fwts_log_info_verbatim(fw, "  Number of Registers       0x%2.2" PRIx8, info->number_of_regs);
> +		fwts_log_info_verbatim(fw, "  Namespace String Length:  0x%4.4" PRIx16, info->namespace_length);
> +		fwts_log_info_verbatim(fw, "  Namespace String Offset:  0x%4.4" PRIx16, info->namespace_offset);
> +		fwts_log_info_verbatim(fw, "  OEM Data Length:          0x%4.4" PRIx16, info->oem_data_length);
> +		fwts_log_info_verbatim(fw, "  OEM Data Offset:          0x%4.4" PRIx16, info->oem_data_offset);
> +		fwts_log_info_verbatim(fw, "  Port Type:                0x%4.4" PRIx16 " (%s)", info->port_type,
> +			port ? port : "(Reserved)");
> +		fwts_log_info_verbatim(fw, "  Port Subtype:             0x%4.4" PRIx16 " (%s)", info->port_subtype,
> +			subport ? subport : "(Reserved)");
> +		fwts_log_info_verbatim(fw, "  Reserved:                 0x%4.4" PRIx16, info->reserved);
> +		fwts_log_info_verbatim(fw, "  Base Address Offset:      0x%4.4" PRIx16, info->base_address_offset);
> +		fwts_log_info_verbatim(fw, "  Address Size Offset:      0x%4.4" PRIx16, info->address_size_offset);
> +		fwts_log_nl(fw);
> +
> +		if (info->revision != 0) {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"DBG2NonZeroRevision",
> +				"DBG2 Info Structure Revision is 0x%2.2" PRIx8
> +				" and was expecting 0x00",
> +				info->revision);
> +		}
> +		if (port == NULL) {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"DBG2PortTypeReserved",
> +				"DBG2 Info Structure Port Type is 0x%4.4" PRIx16
> +				" which is a reserved type.",
> +				info->port_type);
> +		}
> +		if (subport == NULL) {
> +			passed = false;
> +			fwts_failed(fw, LOG_LEVEL_HIGH,
> +				"DBG2PortSubTypeReserved",
> +				"DBG2 Info Structure Port Subtype is 0x%4.4" PRIx16
> +				" which is a reserved type.",
> +				info->port_subtype);
> +		}
> +
> +		length_ok = true;
> +		dbg2_check_offset(fw, table->length, offset + info->length,
> +			"DBG2 Info Structure Namespace Length", &length_ok);
> +		passed &= length_ok;
> +
> +		ok = true;
> +		dbg2_check_offset(fw, table->length, offset + info->namespace_offset,
> +			"DBG2 Info Structure Namespace String Offset", &ok);
> +		dbg2_check_offset(fw, table->length, offset + info->namespace_offset + info->namespace_length,
> +			"DBG2 Info Structure Namespace String End", &ok);
> +		if (ok) {
> +			char *str = (char *)table->data + offset + info->namespace_offset;
> +			dbg2_check_namespace_string(fw, str, info->namespace_length, &passed);
> +			fwts_log_info_verbatim(fw, "  Namespace String:         '%s'", str);
> +			if (strcmp(str, ".") != 0) {
> +				bool found = fwts_acpi_obj_find(fw, str);
> +				if (!found) {
> +					passed = false;
> +					fwts_failed(fw, LOG_LEVEL_HIGH,
> +							"DBG2DeviceNotFound",
> +							"DBG2 Device '%s' not found in ACPI object name space.",
> +							str);
> +				}
> +			}
> +		}
> +
> +		dbg2_check_offset(fw, table->length, offset + info->oem_data_offset,
> +			"DBG2 Info Structure OEM Data Offset", &passed);
> +		dbg2_check_offset(fw, table->length, offset + info->oem_data_offset + info->oem_data_length,
> +			"DBG2 Info Structure OEM Data End", &passed);
> +
> +		ok = true;
> +		total_size = info->number_of_regs * sizeof(fwts_acpi_gas);
> +		dbg2_check_offset(fw, table->length, offset + info->base_address_offset,
> +			"DBG2 Info Structure Base Address Offset", &ok);
> +		dbg2_check_offset(fw, table->length, offset + info->base_address_offset + total_size,
> +			"DBG2 Info Structure Base Address End", &ok);
> +		total_size = info->number_of_regs * sizeof(uint32_t);
> +		dbg2_check_offset(fw, table->length, offset + info->address_size_offset,
> +			"DBG2 Info Structure Address Size Offset", &ok);
> +		dbg2_check_offset(fw, table->length, offset + info->address_size_offset + total_size,
> +			"DBG2 Info Structure Address Size End", &ok);
> +		passed &= ok;
> +
> +		/* Sanity check the GAS array */
> +		if (ok) {
> +			uint8_t j;
> +			fwts_acpi_gas *gas = (fwts_acpi_gas *)(table->data + offset + info->base_address_offset);
> +			uint32_t *addrsize = (uint32_t *)(table->data + offset + info->address_size_offset);
> +
> +			for (j = 0; j < info->number_of_regs; j++, gas++, addrsize++) {
> +				fwts_log_info_verbatim(fw, "    Address Space ID:       0x%2.2" PRIx8, gas->address_space_id);
> +				fwts_log_info_verbatim(fw, "    Register Bit Width      0x%2.2" PRIx8, gas->register_bit_width);
> +				fwts_log_info_verbatim(fw, "    Register Bit Offset     0x%2.2" PRIx8, gas->register_bit_offset);
> +				fwts_log_info_verbatim(fw, "    Access Size             0x%2.2" PRIx8, gas->access_width);
> +				fwts_log_info_verbatim(fw, "    Address                 0x%16.16" PRIx64, gas->address);
> +				fwts_log_nl(fw);
> +
> +				if (*addrsize == 0) {
> +					passed = false;
> +					fwts_failed(fw, LOG_LEVEL_HIGH,
> +						"DBG2InvalidAddressSize",
> +						"DBG2 Address Size is 0");
> +				}
> +
> +				if (gas->register_bit_width == 0) {
> +					passed = false;
> +					fwts_failed(fw, LOG_LEVEL_HIGH,
> +						"DBG2InvalidGasWidth",
> +						"DBG2 Generic Address Structure has "
> +						"zero register bit width which is probably incorrect");
> +				}
> +				if (gas->address == 0) {
> +					passed = false;
> +					fwts_failed(fw, LOG_LEVEL_HIGH,
> +						"DBG2InvalidGasAddress",
> +						"DBG2 Generic Address Structure has "
> +						"a NULL Address which is incorrect");
> +				}
> +			}
> +		}
> +		if (!length_ok)
> +			break;
> +
> +		/* ..and onto the next info structure .. */
> +		info = (fwts_acpi_table_dbg2_info *)
> +			((uint8_t *)info + info->length);
> +	}
> +
> +done:
> +	if (passed)
> +		fwts_passed(fw, "No issues found in DBG2 table.");
> +
> +	return FWTS_OK;
> +}
> +
> +static fwts_framework_minor_test sbbr_dbg2_tests[] = {
> +	{ sbbr_dbg2_sbsa_uart_test, "DBG2 ARM SBSA Generic UART test," },
> +	{ sbbr_dbg2_generic_test, "DBG2 (Debug Port Table 2) test." },
> +	{ NULL, NULL }
> +};
> +
> +static fwts_framework_ops sbbr_dbg2_ops = {
> +	.description = "DBG2 (Debug Port Table 2) test.",
> +	.init        = sbbr_dbg2_init,
> +	.minor_tests = sbbr_dbg2_tests
> +};
> +
> +FWTS_REGISTER("sbbr_dbg2", &sbbr_dbg2_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_TEST_SBBR)
> +
> +#endif
> 

Again, just some minor style issues that need fixing up.

Patch hide | download patch | download mbox

diff --git a/src/sbbr/dbg2/dbg2.c b/src/sbbr/dbg2/dbg2.c
new file mode 100644
index 0000000..3056791
--- /dev/null
+++ b/src/sbbr/dbg2/dbg2.c
@@ -0,0 +1,388 @@ 
+/*
+ * Copyright (C) 2015-2017 Canonical
+ * Copyright (C) 2017 ARM Ltd
+ *
+ * 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_SBBR)
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include "fwts_acpi_object_eval.h"
+
+static fwts_acpi_table_info *table;
+
+static int sbbr_dbg2_init(fwts_framework *fw)
+{
+	if (fwts_acpi_find_table(fw, "DBG2", 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 DBG2 table does not exist");
+		return FWTS_ERROR;
+	}
+
+	return FWTS_OK;
+}
+
+static int sbbr_dbg2_sbsa_uart_test(fwts_framework *fw)
+{
+	uint32_t i;
+
+	fwts_acpi_table_dbg2 *dbg2 = (fwts_acpi_table_dbg2 *)table->data;
+	fwts_acpi_table_dbg2_info *info;
+
+	const int SBBR_DBG2_PORT_SERIAL = 0x8000;
+	const int SBBR_DBG2_ARM_SBSA_UART = 0x000E;
+
+	info = (fwts_acpi_table_dbg2_info *)(table->data + dbg2->info_offset);
+
+	for (i = 0; i < dbg2->info_count; i++) {
+		if( (info->port_type    == SBBR_DBG2_PORT_SERIAL  ) &&
+			(info->port_subtype == SBBR_DBG2_ARM_SBSA_UART)    )
+		{
+			fwts_passed(fw, "DBG2 provides a standard serial debug port and describes ARM SBSA Generic UART");
+			return FWTS_OK;
+		}
+
+		/* ..and onto the next info structure .. */
+		info = (fwts_acpi_table_dbg2_info *)((uint8_t *)info + info->length);
+	}
+
+	fwts_failed(fw, LOG_LEVEL_CRITICAL, "dbg2_sbsa_uart:", "DBG2 provides a non standard debug port");
+	return FWTS_OK;
+}
+
+
+/*
+ *  dbg2_check_offset()
+ *	check if offset does not fall off end of table
+ */
+static void dbg2_check_offset(
+	fwts_framework *fw,
+	const size_t table_length,
+	const size_t offset,
+	const char *msg,
+	bool *passed)
+{
+	if (table_length < offset) {
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"DBG2TooShort",
+			"DBG2 table too short, expecting %zu bytes, "
+			"instead got %zu bytes for a DBG2 table and %s",
+			offset, table->length, msg);
+		*passed = false;
+	}
+}
+
+/*
+ *  dbg2_check_namespace_string()
+ *	check namespace string is '\0' termimated
+ */
+static void dbg2_check_namespace_string(
+	fwts_framework *fw,
+	const char *str,
+	size_t length,
+	bool *passed)
+{
+	size_t i;
+
+	for (i = 0; i < length; i++) {
+		if (*(str + i) == '\0')
+			return;
+	}
+	fwts_failed(fw, LOG_LEVEL_HIGH,
+		"DBG2NameSpaceStringNull",
+		"DBG2 Name Space String was not null terminated");
+	*passed = false;
+}
+
+
+/*
+ *  DBG2 Table
+ *   see https://msdn.microsoft.com/en-us/library/windows/hardware/dn639131%28v=vs.85%29.aspx
+ */
+static int sbbr_dbg2_generic_test(fwts_framework *fw)
+{
+	bool passed = true;
+	fwts_acpi_table_dbg2 *dbg2 = (fwts_acpi_table_dbg2 *)table->data;
+	fwts_acpi_table_dbg2_info *info;
+	uint32_t i;
+	size_t total_size;
+
+	/* Enough length for the initial dbg2 header? */
+	if (table->length < sizeof(fwts_acpi_table_dbg2)) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"DBG2TooShort",
+			"DBG2 table too short, expecting %zu bytes, "
+			"instead got %zu bytes",
+			sizeof(fwts_acpi_table_dbg2), table->length);
+		goto done;
+	}
+
+	fwts_log_info_verbatim(fw, "DBG2 Table:");
+	fwts_log_info_verbatim(fw, "  Info Offset:              0x%8.8" PRIx32, dbg2->info_offset);
+	fwts_log_info_verbatim(fw, "  Info Count:               0x%8.8" PRIx32, dbg2->info_count);
+	fwts_log_nl(fw);
+
+	total_size = dbg2->info_offset +
+		(dbg2->info_count * sizeof(fwts_acpi_table_dbg2_info));
+	if (table->length < total_size) {
+		passed = false;
+		fwts_failed(fw, LOG_LEVEL_HIGH,
+			"DBG2TooShort",
+			"DBG2 table too short, expecting %zu bytes, "
+			"instead got %zu bytes for a DBG2 table "
+			"containing %" PRIu32 " debug device "
+			"information structures",
+			sizeof(fwts_acpi_table_dbg2), table->length,
+			dbg2->info_count);
+		goto done;
+	}
+
+	info = (fwts_acpi_table_dbg2_info *)(table->data + dbg2->info_offset);
+
+	/*
+	 *  Sanity check structures
+	 */
+	for (i = 0; i < dbg2->info_count; i++) {
+		uint32_t offset = (uint8_t *)info - (uint8_t *)table->data;
+		bool ok, length_ok;
+		char *port = NULL, *subport = NULL;
+
+		switch (info->port_type) {
+		case 0x8000:
+			port = "Serial";
+			switch (info->port_subtype) {
+			case 0x0000:
+				subport = "Fully 16550-compatible";
+				break;
+			case 0x0001:
+				subport = "16550 subset compatible";
+				break;
+			case 0x0003:
+				subport = "ARMPL011 UART";
+				break;
+			case 0x000d:
+				subport = "ARM SBSA Generic UART (deprecated)";
+				break;
+			case 0x000e:
+				subport = "ARM SBSA Generic UART";
+				break;
+			case 0x000f:
+				subport = "ARM DCC";
+				break;
+			case 0x0010:
+				subport = "BCM2835";
+				break;
+			default:
+				break;
+			}
+			break;
+		case 0x8001:
+			port = "1394";
+			switch (info->port_subtype) {
+			case 0x0000:
+				subport = "IEEE1394";
+				break;
+			default:
+				break;
+			}
+			break;
+		case 0x8002:
+			port = "USB";
+			switch (info->port_subtype) {
+			case 0x0000:
+				subport = "XHCI controller";
+				break;
+			case 0x0001:
+				subport = "EHCI controller";
+				break;
+			default:
+				break;
+			}
+			break;
+		case 0x8003:
+			port = "Net";
+			subport = "PCI";
+			break;
+		default:
+			break;
+		}
+
+		fwts_log_info_verbatim(fw, "DBG2 Info Structure %" PRIu32 ":", i);
+		fwts_log_info_verbatim(fw, "  Revision:                 0x%2.2" PRIx8, info->revision);
+		fwts_log_info_verbatim(fw, "  Length:                   0x%4.4" PRIx16, info->length);
+		fwts_log_info_verbatim(fw, "  Number of Registers       0x%2.2" PRIx8, info->number_of_regs);
+		fwts_log_info_verbatim(fw, "  Namespace String Length:  0x%4.4" PRIx16, info->namespace_length);
+		fwts_log_info_verbatim(fw, "  Namespace String Offset:  0x%4.4" PRIx16, info->namespace_offset);
+		fwts_log_info_verbatim(fw, "  OEM Data Length:          0x%4.4" PRIx16, info->oem_data_length);
+		fwts_log_info_verbatim(fw, "  OEM Data Offset:          0x%4.4" PRIx16, info->oem_data_offset);
+		fwts_log_info_verbatim(fw, "  Port Type:                0x%4.4" PRIx16 " (%s)", info->port_type,
+			port ? port : "(Reserved)");
+		fwts_log_info_verbatim(fw, "  Port Subtype:             0x%4.4" PRIx16 " (%s)", info->port_subtype,
+			subport ? subport : "(Reserved)");
+		fwts_log_info_verbatim(fw, "  Reserved:                 0x%4.4" PRIx16, info->reserved);
+		fwts_log_info_verbatim(fw, "  Base Address Offset:      0x%4.4" PRIx16, info->base_address_offset);
+		fwts_log_info_verbatim(fw, "  Address Size Offset:      0x%4.4" PRIx16, info->address_size_offset);
+		fwts_log_nl(fw);
+
+		if (info->revision != 0) {
+			passed = false;
+			fwts_failed(fw, LOG_LEVEL_HIGH,
+				"DBG2NonZeroRevision",
+				"DBG2 Info Structure Revision is 0x%2.2" PRIx8
+				" and was expecting 0x00",
+				info->revision);
+		}
+		if (port == NULL) {
+			passed = false;
+			fwts_failed(fw, LOG_LEVEL_HIGH,
+				"DBG2PortTypeReserved",
+				"DBG2 Info Structure Port Type is 0x%4.4" PRIx16
+				" which is a reserved type.",
+				info->port_type);
+		}
+		if (subport == NULL) {
+			passed = false;
+			fwts_failed(fw, LOG_LEVEL_HIGH,
+				"DBG2PortSubTypeReserved",
+				"DBG2 Info Structure Port Subtype is 0x%4.4" PRIx16
+				" which is a reserved type.",
+				info->port_subtype);
+		}
+
+		length_ok = true;
+		dbg2_check_offset(fw, table->length, offset + info->length,
+			"DBG2 Info Structure Namespace Length", &length_ok);
+		passed &= length_ok;
+
+		ok = true;
+		dbg2_check_offset(fw, table->length, offset + info->namespace_offset,
+			"DBG2 Info Structure Namespace String Offset", &ok);
+		dbg2_check_offset(fw, table->length, offset + info->namespace_offset + info->namespace_length,
+			"DBG2 Info Structure Namespace String End", &ok);
+		if (ok) {
+			char *str = (char *)table->data + offset + info->namespace_offset;
+			dbg2_check_namespace_string(fw, str, info->namespace_length, &passed);
+			fwts_log_info_verbatim(fw, "  Namespace String:         '%s'", str);
+			if (strcmp(str, ".") != 0) {
+				bool found = fwts_acpi_obj_find(fw, str);
+				if (!found) {
+					passed = false;
+					fwts_failed(fw, LOG_LEVEL_HIGH,
+							"DBG2DeviceNotFound",
+							"DBG2 Device '%s' not found in ACPI object name space.",
+							str);
+				}
+			}
+		}
+
+		dbg2_check_offset(fw, table->length, offset + info->oem_data_offset,
+			"DBG2 Info Structure OEM Data Offset", &passed);
+		dbg2_check_offset(fw, table->length, offset + info->oem_data_offset + info->oem_data_length,
+			"DBG2 Info Structure OEM Data End", &passed);
+
+		ok = true;
+		total_size = info->number_of_regs * sizeof(fwts_acpi_gas);
+		dbg2_check_offset(fw, table->length, offset + info->base_address_offset,
+			"DBG2 Info Structure Base Address Offset", &ok);
+		dbg2_check_offset(fw, table->length, offset + info->base_address_offset + total_size,
+			"DBG2 Info Structure Base Address End", &ok);
+		total_size = info->number_of_regs * sizeof(uint32_t);
+		dbg2_check_offset(fw, table->length, offset + info->address_size_offset,
+			"DBG2 Info Structure Address Size Offset", &ok);
+		dbg2_check_offset(fw, table->length, offset + info->address_size_offset + total_size,
+			"DBG2 Info Structure Address Size End", &ok);
+		passed &= ok;
+
+		/* Sanity check the GAS array */
+		if (ok) {
+			uint8_t j;
+			fwts_acpi_gas *gas = (fwts_acpi_gas *)(table->data + offset + info->base_address_offset);
+			uint32_t *addrsize = (uint32_t *)(table->data + offset + info->address_size_offset);
+
+			for (j = 0; j < info->number_of_regs; j++, gas++, addrsize++) {
+				fwts_log_info_verbatim(fw, "    Address Space ID:       0x%2.2" PRIx8, gas->address_space_id);
+				fwts_log_info_verbatim(fw, "    Register Bit Width      0x%2.2" PRIx8, gas->register_bit_width);
+				fwts_log_info_verbatim(fw, "    Register Bit Offset     0x%2.2" PRIx8, gas->register_bit_offset);
+				fwts_log_info_verbatim(fw, "    Access Size             0x%2.2" PRIx8, gas->access_width);
+				fwts_log_info_verbatim(fw, "    Address                 0x%16.16" PRIx64, gas->address);
+				fwts_log_nl(fw);
+
+				if (*addrsize == 0) {
+					passed = false;
+					fwts_failed(fw, LOG_LEVEL_HIGH,
+						"DBG2InvalidAddressSize",
+						"DBG2 Address Size is 0");
+				}
+
+				if (gas->register_bit_width == 0) {
+					passed = false;
+					fwts_failed(fw, LOG_LEVEL_HIGH,
+						"DBG2InvalidGasWidth",
+						"DBG2 Generic Address Structure has "
+						"zero register bit width which is probably incorrect");
+				}
+				if (gas->address == 0) {
+					passed = false;
+					fwts_failed(fw, LOG_LEVEL_HIGH,
+						"DBG2InvalidGasAddress",
+						"DBG2 Generic Address Structure has "
+						"a NULL Address which is incorrect");
+				}
+			}
+		}
+		if (!length_ok)
+			break;
+
+		/* ..and onto the next info structure .. */
+		info = (fwts_acpi_table_dbg2_info *)
+			((uint8_t *)info + info->length);
+	}
+
+done:
+	if (passed)
+		fwts_passed(fw, "No issues found in DBG2 table.");
+
+	return FWTS_OK;
+}
+
+static fwts_framework_minor_test sbbr_dbg2_tests[] = {
+	{ sbbr_dbg2_sbsa_uart_test, "DBG2 ARM SBSA Generic UART test," },
+	{ sbbr_dbg2_generic_test, "DBG2 (Debug Port Table 2) test." },
+	{ NULL, NULL }
+};
+
+static fwts_framework_ops sbbr_dbg2_ops = {
+	.description = "DBG2 (Debug Port Table 2) test.",
+	.init        = sbbr_dbg2_init,
+	.minor_tests = sbbr_dbg2_tests
+};
+
+FWTS_REGISTER("sbbr_dbg2", &sbbr_dbg2_ops, FWTS_TEST_ANYTIME, FWTS_FLAG_TEST_SBBR)
+
+#endif