RSDP points to XSDT which in turn points to other tables. In the case of RSDP things are straightforward as we can point to XSDT just by adding the binary blob base address plus the size of RSDP.
For XSDT we traverse the table array and point every other table by calculating their size (exception being FACS and DSDT which are pointed from FADT).
Finally implement a common header and checksum helper functions for usage in all generated ACPI tables.
Signed-off-by: Alexander Spyridakis a.spyridakis@virtualopensystems.com Signed-off-by: Alvise Rigo a.rigo@virtualopensystems.com --- hw/arm/virt-acpi.c | 93 +++++++++++++++++++++++++++++++++++++++++++-- include/hw/acpi/acpi-defs.h | 9 +++++ include/hw/arm/virt-acpi.h | 6 +++ 3 files changed, 104 insertions(+), 4 deletions(-)
diff --git a/hw/arm/virt-acpi.c b/hw/arm/virt-acpi.c index 5c8df45..0d7bb99 100644 --- a/hw/arm/virt-acpi.c +++ b/hw/arm/virt-acpi.c @@ -21,16 +21,101 @@ static void *acpi_table[NUM_ACPI_TABLES]; static int acpi_size[NUM_ACPI_TABLES];
+/* + * Many of the ACPI tables use the same header structure + * This is a common function to fill such information. + */ +static void acpi_fill_common_header_data(void *table, const char *signature, + uint8_t revision, int length) +{ + AcpiTableHeader *h = (AcpiTableHeader *)table; + + memcpy(&h->signature, signature, sizeof(h->signature)); + memcpy(h->oem_id, ACPI_VIRT_QEMU_STR_4, sizeof(h->oem_id)); + memcpy(h->oem_table_id, ACPI_VIRT_MACH_STR_8, sizeof(h->oem_table_id)); + h->revision = revision; + h->length = cpu_to_le32(length); +} + +/* + * Called at the very end of an ACPI table. + * Adding all the data bytes, plus the checksum should equal to zero. + */ +static void acpi_do_checksum(void *table, uint32_t length, uint8_t *checksum) +{ + uint8_t sum, *ptr; + + sum = 0; + ptr = table; + + *checksum = 0; + while (length--) { + sum = (uint8_t)(sum + (*ptr++)); + } + + ptr = table; + *checksum = (uint8_t) (0xff - sum + 1); +} + static void acpi_create_rsdp(void) { - acpi_table[RSDP] = NULL; - acpi_size[RSDP] = 0; + AcpiRsdpDescriptor *rsdp; + + rsdp = g_malloc0(sizeof(*rsdp)); + + /* Set table header information */ + memcpy(&rsdp->signature, "RSD PTR ", sizeof(rsdp->signature)); + memcpy(rsdp->oem_id, ACPI_VIRT_QEMU_STR_6, sizeof(rsdp->oem_id)); + rsdp->length = cpu_to_le32(sizeof(*rsdp)); + rsdp->revision = 0x02; + + /* Point to XSDT */ + rsdp->xsdt_physical_address = cpu_to_le64(ACPI_BASE_ADDRESS + rsdp->length); + + /* Calculate normal and extended checksum */ + acpi_do_checksum(rsdp, + offsetof(AcpiRsdpDescriptor, length), &rsdp->checksum); + acpi_do_checksum(rsdp, rsdp->length, &rsdp->extended_checksum); + + acpi_table[RSDP] = (void *)rsdp; + acpi_size[RSDP] = rsdp->length; }
static void acpi_create_xsdt(void) { - acpi_table[XSDT] = NULL; - acpi_size[XSDT] = 0; + AcpiXsdtDescriptor *xsdt; + int i, xsdt_size; + + /* + * The final size of XSDT is the table size plus the number + * of pointed tables multiplied by the table_offset_entry size. + * + * The initial xsdt table size already includes one pointed table and + * assuming that FADT is the last pointed table we can calculate the + * total based on that. + */ + xsdt_size = sizeof(*xsdt) + + ((FADT - (XSDT + 1)) * sizeof(xsdt->table_offset_entry)); + xsdt = g_malloc0(xsdt_size); + + /* Set table header information */ + acpi_fill_common_header_data(xsdt, "XSDT", 1, xsdt_size); + + /* Point first table included by default in the xsdt structure */ + xsdt->table_offset_entry[0] = cpu_to_le64( + ACPI_BASE_ADDRESS + acpi_size[RSDP] + xsdt->length); + + /* Point all other tables (excluding FACS and DSDT) */ + for (i = XSDT; i < FADT - 1; i++) { + xsdt->table_offset_entry[i] = + xsdt->table_offset_entry[i - 1] + acpi_size[i + 1]; + } + + /* Calculate checksum */ + acpi_do_checksum(xsdt, xsdt->length, &xsdt->checksum); + + acpi_table[XSDT] = (void *)xsdt; + acpi_size[XSDT] = xsdt->length; }
static void acpi_create_madt(uint32_t smp_cpus, diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index c4468f8..779f872 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -88,6 +88,15 @@ struct AcpiTableHeader /* ACPI common table header */ typedef struct AcpiTableHeader AcpiTableHeader;
/* + * Extended System Description Table (XSDT) + */ +struct AcpiXsdtDescriptor { + ACPI_TABLE_HEADER_DEF + uint64_t table_offset_entry[1]; /* Array of pointers to ACPI tables */ +} QEMU_PACKED; +typedef struct AcpiXsdtDescriptor AcpiXsdtDescriptor; + +/* * ACPI 1.0 Fixed ACPI Description Table (FADT) */ struct AcpiFadtDescriptorRev1 diff --git a/include/hw/arm/virt-acpi.h b/include/hw/arm/virt-acpi.h index 5098118..66a73eb 100644 --- a/include/hw/arm/virt-acpi.h +++ b/include/hw/arm/virt-acpi.h @@ -23,6 +23,12 @@
#define NUM_ACPI_TABLES 7
+#define ACPI_VIRT_QEMU_STR_4 "QEMU" +#define ACPI_VIRT_QEMU_STR_6 "QEMU " +#define ACPI_VIRT_MACH_STR_8 "MACHVIRT" + +#define ACPI_BASE_ADDRESS 0x47000000 + enum { RSDP, XSDT,