Commit 9f3119b7 authored by Zhao Yakui's avatar Zhao Yakui Committed by Len Brown

ACPI: Validate XSDT, use RSDT if XSDT fails

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=8630Signed-off-by: default avatarZhao Yakui  <yakui.zhao@intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent b377fd39
...@@ -51,6 +51,65 @@ ACPI_MODULE_NAME("tbutils") ...@@ -51,6 +51,65 @@ ACPI_MODULE_NAME("tbutils")
static acpi_physical_address static acpi_physical_address
acpi_tb_get_root_table_entry(u8 * table_entry, acpi_tb_get_root_table_entry(u8 * table_entry,
acpi_native_uint table_entry_size); 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_address rsdp_address, u8 flags) ...@@ -341,6 +400,7 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address, u8 flags)
u32 table_count; u32 table_count;
struct acpi_table_header *table; struct acpi_table_header *table;
acpi_physical_address address; acpi_physical_address address;
acpi_physical_address rsdt_address;
u32 length; u32 length;
u8 *table_entry; u8 *table_entry;
acpi_status status; acpi_status status;
...@@ -369,6 +429,8 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address, u8 flags) ...@@ -369,6 +429,8 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address, u8 flags)
*/ */
address = (acpi_physical_address) rsdp->xsdt_physical_address; address = (acpi_physical_address) rsdp->xsdt_physical_address;
table_entry_size = sizeof(u64); table_entry_size = sizeof(u64);
rsdt_address = (acpi_physical_address)
rsdp->rsdt_physical_address;
} else { } else {
/* Root table is an RSDT (32-bit physical addresses) */ /* Root table is an RSDT (32-bit physical addresses) */
...@@ -382,6 +444,15 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address, u8 flags) ...@@ -382,6 +444,15 @@ acpi_tb_parse_root_table(acpi_physical_address rsdp_address, u8 flags)
*/ */
acpi_os_unmap_memory(rsdp, sizeof(struct acpi_table_rsdp)); 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 */ /* Map the RSDT/XSDT table header to get the full table length */
table = acpi_os_map_memory(address, sizeof(struct acpi_table_header)); table = acpi_os_map_memory(address, sizeof(struct acpi_table_header));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment