From: Zhao Yakui commit 9f3119b70cf189530f1b46a006a052e171a1622f in mainline. ACPI 1.0 used an RSDT with 32-bit physical addresses. ACPI 2.0 adds an XSDT with 32-bit physical addresses. An ACPI 2.0 aware OS is supposed to use the XSDT (when present) instead of the RSDT. However, several systems have failed because the XSDT contains NULL entries -- while it is missing pointers to needed tables, such as SSDTs. When we find an XSDT with NULL entries, discard it and use the ACPI 1.0 RSDT instead. http://bugzilla.kernel.org/show_bug.cgi?id=8630 Signed-off-by: Zhao Yakui Cc: Vincet Fortier Signed-off-by: Len Brown Signed-off-by: Greg Kroah-Hartman --- drivers/acpi/tables/tbutils.c | 71 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) --- a/drivers/acpi/tables/tbutils.c +++ b/drivers/acpi/tables/tbutils.c @@ -51,6 +51,65 @@ ACPI_MODULE_NAME("tbutils") static acpi_physical_address acpi_tb_get_root_table_entry(u8 * table_entry, acpi_native_uint table_entry_size); +/******************************************************************************* + * + * FUNCTION: acpi_tb_check_xsdt + * + * PARAMETERS: address - Pointer to the XSDT + * + * RETURN: status + * AE_OK - XSDT is okay + * AE_NO_MEMORY - can't map XSDT + * AE_INVALID_TABLE_LENGTH - invalid table length + * AE_NULL_ENTRY - XSDT has NULL entry + * + * DESCRIPTION: validate XSDT +******************************************************************************/ + +static acpi_status +acpi_tb_check_xsdt(acpi_physical_address address) +{ + struct acpi_table_header *table; + u32 length; + u64 xsdt_entry_address; + u8 *table_entry; + u32 table_count; + int i; + + table = acpi_os_map_memory(address, sizeof(struct acpi_table_header)); + if (!table) + return AE_NO_MEMORY; + + length = table->length; + acpi_os_unmap_memory(table, sizeof(struct acpi_table_header)); + if (length < sizeof(struct acpi_table_header)) + return AE_INVALID_TABLE_LENGTH; + + table = acpi_os_map_memory(address, length); + if (!table) + return AE_NO_MEMORY; + + /* Calculate the number of tables described in XSDT */ + table_count = + (u32) ((table->length - + sizeof(struct acpi_table_header)) / sizeof(u64)); + table_entry = + ACPI_CAST_PTR(u8, table) + sizeof(struct acpi_table_header); + for (i = 0; i < table_count; i++) { + ACPI_MOVE_64_TO_64(&xsdt_entry_address, table_entry); + if (!xsdt_entry_address) { + /* XSDT has NULL entry */ + break; + } + table_entry += sizeof(u64); + } + acpi_os_unmap_memory(table, length); + + if (i < table_count) + return AE_NULL_ENTRY; + else + return AE_OK; +} /******************************************************************************* * @@ -341,6 +400,7 @@ acpi_tb_parse_root_table(acpi_physical_a u32 table_count; struct acpi_table_header *table; acpi_physical_address address; + acpi_physical_address rsdt_address; u32 length; u8 *table_entry; acpi_status status; @@ -369,6 +429,8 @@ acpi_tb_parse_root_table(acpi_physical_a */ address = (acpi_physical_address) rsdp->xsdt_physical_address; table_entry_size = sizeof(u64); + rsdt_address = (acpi_physical_address) + rsdp->rsdt_physical_address; } else { /* Root table is an RSDT (32-bit physical addresses) */ @@ -382,6 +444,15 @@ acpi_tb_parse_root_table(acpi_physical_a */ acpi_os_unmap_memory(rsdp, sizeof(struct acpi_table_rsdp)); + if (table_entry_size == sizeof(u64)) { + if (acpi_tb_check_xsdt(address) == AE_NULL_ENTRY) { + /* XSDT has NULL entry, RSDT is used */ + address = rsdt_address; + table_entry_size = sizeof(u32); + ACPI_WARNING((AE_INFO, "BIOS XSDT has NULL entry," + "using RSDT")); + } + } /* Map the RSDT/XSDT table header to get the full table length */ table = acpi_os_map_memory(address, sizeof(struct acpi_table_header)); -- - To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/