[PATCH v3 25/57] x86: acpi: Support generation of the DBG2 table

Simon Glass
2020-09-06T21:43:33Z

Add an implementation of the DBG2 (Debug Port Table 2) ACPI table.
Adjust one of the header includes to be in the correct order, before
adding more.

Note that the DBG2 table is generic but the PCI UART is x86-specific at
present since it assumes an ns16550 UART. It can be generalised later
if necessary.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

(no changes since v1)

Changes in v1:
- Move ASL_REVISION define into this patch
- Move acpi_create_dbg2() into generic code
- Update commit message

arch/x86/include/asm/acpi_table.h | 11 ++++++
arch/x86/lib/acpi_table.c | 41 ++++++++++++++++++++
include/acpi/acpi_table.h | 40 +++++++++++++++++++
lib/acpi/acpi_table.c | 64 +++++++++++++++++++++++++++++++
4 files changed, 156 insertions(+)

diff --git a/arch/x86/include/asm/acpi_table.h b/arch/x86/include/asm/acpi_table.h
index 7047ee6c772..1b7ff509516 100644
--- a/arch/x86/include/asm/acpi_table.h
+++ b/arch/x86/include/asm/acpi_table.h
@@ -46,6 +46,17 @@ u32 acpi_fill_csrt(u32 current);
*/
int acpi_write_hpet(struct acpi_ctx *ctx);

+/**
+ * acpi_write_dbg2_pci_uart() - Write out a DBG2 table
+ *
+ * @ctx: Current ACPI context
+ * @dev: Debug UART device to describe
+ * @access_size: Access size for UART (e.g. ACPI_ACCESS_SIZE_DWORD_ACCESS)
+ * @return 0 if OK, -ve on error
+ */
+int acpi_write_dbg2_pci_uart(struct acpi_ctx *ctx, struct udevice *dev,
+ uint access_size);
+
/**
* acpi_create_gnvs() - Create a GNVS (Global Non Volatile Storage) table
*
diff --git a/arch/x86/lib/acpi_table.c b/arch/x86/lib/acpi_table.c
index e1900ffe42f..28a27103342 100644
--- a/arch/x86/lib/acpi_table.c
+++ b/arch/x86/lib/acpi_table.c
@@ -15,6 +15,7 @@
#include <serial.h>
#include <version.h>
#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
#include <acpi/acpi_table.h>
#include <asm/acpi/global_nvs.h>
#include <asm/ioapic.h>
@@ -575,3 +576,43 @@ int acpi_write_hpet(struct acpi_ctx *ctx)

return 0;
}
+
+int acpi_write_dbg2_pci_uart(struct acpi_ctx *ctx, struct udevice *dev,
+ uint access_size)
+{
+ struct acpi_dbg2_header *dbg2 = ctx->current;
+ char path[ACPI_PATH_MAX];
+ struct acpi_gen_regaddr address;
+ phys_addr_t addr;
+ int ret;
+
+ if (!device_active(dev)) {
+ log_info("Device not enabled\n");
+ return -EACCES;
+ }
+ /*
+ * PCI devices don't remember their resource allocation information in
+ * U-Boot at present. We assume that MMIO is used for the UART and that
+ * the address space is 32 bytes: ns16550 uses 8 registers of up to
+ * 32-bits each. This is only for debugging so it is not a big deal.
+ */
+ addr = dm_pci_read_bar32(dev, 0);
+ printf("UART addr %lx\n", (ulong)addr);
+
+ memset(&address, '\0', sizeof(address));
+ address.space_id = ACPI_ADDRESS_SPACE_MEMORY;
+ address.addrl = (uint32_t)addr;
+ address.addrh = (uint32_t)((addr >> 32) & 0xffffffff);
+ address.access_size = access_size;
+
+ ret = acpi_device_path(dev, path, sizeof(path));
+ if (ret)
+ return log_msg_ret("path", ret);
+ acpi_create_dbg2(dbg2, ACPI_DBG2_SERIAL_PORT,
+ ACPI_DBG2_16550_COMPATIBLE, &address, 0x1000, path);
+
+ acpi_inc_align(ctx, dbg2->header.length);
+ acpi_add_table(ctx, dbg2);
+
+ return 0;
+}
diff --git a/include/acpi/acpi_table.h b/include/acpi/acpi_table.h
index f8140446a59..c826a797f5b 100644
--- a/include/acpi/acpi_table.h
+++ b/include/acpi/acpi_table.h
@@ -448,6 +448,29 @@ struct __packed acpi_dmar {

#define ACPI_DBG2_UNKNOWN 0x00FF

+/* DBG2: Microsoft Debug Port Table 2 header */
+struct __packed acpi_dbg2_header {
+ struct acpi_table_header header;
+ u32 devices_offset;
+ u32 devices_count;
+};
+
+/* DBG2: Microsoft Debug Port Table 2 device entry */
+struct __packed acpi_dbg2_device {
+ u8 revision;
+ u16 length;
+ u8 address_count;
+ u16 namespace_string_length;
+ u16 namespace_string_offset;
+ u16 oem_data_length;
+ u16 oem_data_offset;
+ u16 port_type;
+ u16 port_subtype;
+ u8 reserved[2];
+ u16 base_address_offset;
+ u16 address_size_offset;
+};
+
/* SPCR (Serial Port Console Redirection table) */
struct __packed acpi_spcr {
struct acpi_table_header header;
@@ -522,6 +545,23 @@ int acpi_get_table_revision(enum acpi_tables table);
*/
int acpi_create_dmar(struct acpi_dmar *dmar, enum dmar_flags flags);

+/**
+ * acpi_create_dbg2() - Create a DBG2 table
+ *
+ * This table describes how to access the debug UART
+ *
+ * @dbg2: Place to put information
+ * @port_type: Serial port type (see ACPI_DBG2_...)
+ * @port_subtype: Serial port sub-type (see ACPI_DBG2_...)
+ * @address: ACPI address of port
+ * @address_size: Size of address space
+ * @device_path: Path of device (created using acpi_device_path())
+ */
+void acpi_create_dbg2(struct acpi_dbg2_header *dbg2,
+ int port_type, int port_subtype,
+ struct acpi_gen_regaddr *address, uint32_t address_size,
+ const char *device_path);
+
/**
* acpi_fill_header() - Set up a new table header
*
diff --git a/lib/acpi/acpi_table.c b/lib/acpi/acpi_table.c
index acc55e7fad6..908d8903893 100644
--- a/lib/acpi/acpi_table.c
+++ b/lib/acpi/acpi_table.c
@@ -264,3 +264,67 @@ void acpi_setup_base_tables(struct acpi_ctx *ctx, void *start)
*/
acpi_align64(ctx);
}
+
+void acpi_create_dbg2(struct acpi_dbg2_header *dbg2,
+ int port_type, int port_subtype,
+ struct acpi_gen_regaddr *address, u32 address_size,
+ const char *device_path)
+{
+ uintptr_t current;
+ struct acpi_dbg2_device *device;
+ u32 *dbg2_addr_size;
+ struct acpi_table_header *header;
+ size_t path_len;
+ const char *path;
+ char *namespace;
+
+ /* Fill out header fields. */
+ current = (uintptr_t)dbg2;
+ memset(dbg2, '\0', sizeof(struct acpi_dbg2_header));
+ header = &dbg2->header;
+
+ header->revision = acpi_get_table_revision(ACPITAB_DBG2);
+ acpi_fill_header(header, "DBG2");
+ header->aslc_revision = ASL_REVISION;
+
+ /* One debug device defined */
+ dbg2->devices_offset = sizeof(struct acpi_dbg2_header);
+ dbg2->devices_count = 1;
+ current += sizeof(struct acpi_dbg2_header);
+
+ /* Device comes after the header */
+ device = (struct acpi_dbg2_device *)current;
+ memset(device, 0, sizeof(struct acpi_dbg2_device));
+ current += sizeof(struct acpi_dbg2_device);
+
+ device->revision = 0;
+ device->address_count = 1;
+ device->port_type = port_type;
+ device->port_subtype = port_subtype;
+
+ /* Base Address comes after device structure */
+ memcpy((void *)current, address, sizeof(struct acpi_gen_regaddr));
+ device->base_address_offset = current - (uintptr_t)device;
+ current += sizeof(struct acpi_gen_regaddr);
+
+ /* Address Size comes after address structure */
+ dbg2_addr_size = (uint32_t *)current;
+ device->address_size_offset = current - (uintptr_t)device;
+ *dbg2_addr_size = address_size;
+ current += sizeof(uint32_t);
+
+ /* Namespace string comes last, use '.' if not provided */
+ path = device_path ? : ".";
+ /* Namespace string length includes NULL terminator */
+ path_len = strlen(path) + 1;
+ namespace = (char *)current;
+ device->namespace_string_length = path_len;
+ device->namespace_string_offset = current - (uintptr_t)device;
+ strncpy(namespace, path, path_len);
+ current += path_len;
+
+ /* Update structure lengths and checksum */
+ device->length = current - (uintptr_t)device;
+ header->length = current - (uintptr_t)dbg2;
+ header->checksum = table_compute_checksum(dbg2, header->length);
+}
--
2.28.0.526.ge36021eeef-goog