[acpi] Add support for locating tables via the XSDT The ACPI XSDT is a 64-bit version of the RSDT, providing pointers to ACPI tables that may lie outside of the 32-bit address space. The only difference between the two tables is the size of the entries in the table pointer array. Add support for parsing the located root system description table as either a 32-bit RSDT or a 64-bit XSDT, with the 64-bit code paths optimised out via dead code elimination in 32-bit builds. Signed-off-by: Michael Brown <mcb30@ipxe.org>
diff --git a/src/core/acpi.c b/src/core/acpi.c index 99ff33b..623e0e8 100644 --- a/src/core/acpi.c +++ b/src/core/acpi.c
@@ -97,7 +97,7 @@ } /** - * Locate ACPI table via RSDT + * Locate ACPI table via RSDT/XSDT * * @v signature Requested table signature * @v index Requested index of table with this signature @@ -107,20 +107,29 @@ unsigned int index ) { const struct acpi_rsdt *rsdt; const struct acpi_header *table; + physaddr_t entry; size_t len; + int ext; unsigned int count; unsigned int i; - /* Locate RSDT */ + /* Locate RSDT/XSDT */ rsdt = acpi_find_rsdt(); if ( ! rsdt ) { - DBG ( "RSDT not found\n" ); + DBG ( "RSDT/XSDT not found\n" ); return NULL; } - /* Read RSDT header */ - if ( rsdt->acpi.signature != cpu_to_le32 ( RSDT_SIGNATURE ) ) { - DBGC ( colour, "RSDT %#08lx has invalid signature:\n", + /* Read RSDT/XSDT header */ + if ( ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) && + ( rsdt->acpi.signature == cpu_to_le32 ( XSDT_SIGNATURE ) ) ) { + /* XSDT with 64-bit entries */ + ext = 1; + } else if ( rsdt->acpi.signature == cpu_to_le32 ( RSDT_SIGNATURE ) ) { + /* RSDT with 32-bit entries */ + ext = 0; + } else { + DBGC ( colour, "RSDT/XSDT %#08lx has invalid signature:\n", virt_to_phys ( rsdt ) ); DBGC_HDA ( colour, virt_to_phys ( rsdt ), &rsdt->acpi, sizeof ( rsdt->acpi ) ); @@ -128,8 +137,8 @@ } len = le32_to_cpu ( rsdt->acpi.length ); if ( len < sizeof ( rsdt->acpi ) ) { - DBGC ( colour, "RSDT %#08lx has invalid length:\n", - virt_to_phys ( rsdt ) ); + DBGC ( colour, "%cSDT %#08lx has invalid length:\n", + ( ext ? 'X' : 'R' ), virt_to_phys ( rsdt ) ); DBGC_HDA ( colour, virt_to_phys ( rsdt ), &rsdt->acpi, sizeof ( rsdt->acpi ) ); return NULL; @@ -137,20 +146,22 @@ /* Validate checksum */ if ( acpi_checksum ( &rsdt->acpi ) != 0 ) { - DBGC ( colour, "RSDT %#08lx has bad checksum\n", - virt_to_phys ( rsdt ) ); + DBGC ( colour, "%cSDT %#08lx has bad checksum\n", + ( ext ? 'X' : 'R' ), virt_to_phys ( rsdt ) ); return NULL; } /* Calculate number of entries */ - count = ( ( len - sizeof ( rsdt->acpi ) ) / - sizeof ( rsdt->entry[0] ) ); + len -= sizeof ( rsdt->acpi ); + count = ( ext ? ( len / sizeof ( rsdt->entry64[0] ) ) : + ( len / sizeof ( rsdt->entry32[0] ) ) ); /* Search through entries */ for ( i = 0 ; i < count ; i++ ) { /* Read table header */ - table = phys_to_virt ( rsdt->entry[i] ); + entry = ( ext ? rsdt->entry64[i] : rsdt->entry32[i] ); + table = phys_to_virt ( entry ); /* Check table signature */ if ( table->signature != cpu_to_le32 ( signature ) ) @@ -162,20 +173,20 @@ /* Check table integrity */ if ( acpi_checksum ( table ) != 0 ) { - DBGC ( colour, "RSDT %#08lx found %s with bad " - "checksum at %#08lx\n", virt_to_phys ( rsdt ), - acpi_name ( signature ), - virt_to_phys ( table ) ); + DBGC ( colour, "%cSDT %#08lx found %s with bad " + "checksum at %#08lx\n", ( ext ? 'X' : 'R' ), + virt_to_phys ( rsdt ), acpi_name ( signature ), + entry ); break; } - DBGC ( colour, "RSDT %#08lx found %s at %#08lx\n", - virt_to_phys ( rsdt ), acpi_name ( signature ), - virt_to_phys ( table ) ); + DBGC ( colour, "%cSDT %#08lx found %s at %#08lx\n", + ( ext ? 'X' : 'R' ), virt_to_phys ( rsdt ), + acpi_name ( signature ), entry ); return table; } - DBGC ( colour, "RSDT %#08lx could not find %s\n", + DBGC ( colour, "%cSDT %#08lx could not find %s\n", ( ext ? 'X' : 'R' ), virt_to_phys ( rsdt ), acpi_name ( signature ) ); return NULL; }
diff --git a/src/include/ipxe/acpi.h b/src/include/ipxe/acpi.h index c423aa5..52237a8 100644 --- a/src/include/ipxe/acpi.h +++ b/src/include/ipxe/acpi.h
@@ -246,12 +246,20 @@ /** Root System Description Table (RSDT) signature */ #define RSDT_SIGNATURE ACPI_SIGNATURE ( 'R', 'S', 'D', 'T' ) +/** Extended System Description Table (XSDT) signature */ +#define XSDT_SIGNATURE ACPI_SIGNATURE ( 'X', 'S', 'D', 'T' ) + /** ACPI Root System Description Table (RSDT) */ struct acpi_rsdt { /** ACPI header */ struct acpi_header acpi; /** ACPI table entries */ - uint32_t entry[0]; + union { + /** 32-bit RSDT entries */ + uint32_t entry32[0]; + /** 64-bit XSDT entries */ + uint64_t entry64[0]; + }; } __attribute__ (( packed )); /** Fixed ACPI Description Table (FADT) signature */