diff options
author | Lin Ming <ming.m.lin@intel.com> | 2010-04-01 10:47:56 +0800 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2010-04-20 10:43:16 -0400 |
commit | 729df0f848daf2f17d02107199fa92efe909d995 (patch) | |
tree | d4ece34a277019368999fd36a0bf15a39eeaa742 | |
parent | c1637e9c649a0eb72c467041d78275aabdd48a41 (diff) |
ACPICA: Add detection of corrupted/replaced DSDT
This change adds support to detect a DSDT that has been corrupted
and/or replaced from outside the OS (by firmware). This is
typically catastrophic for the system, but has been seen on
some machines.
https://bugzilla.kernel.org/show_bug.cgi?id=14679
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | drivers/acpi/acpica/acglobal.h | 5 | ||||
-rw-r--r-- | drivers/acpi/acpica/actables.h | 2 | ||||
-rw-r--r-- | drivers/acpi/acpica/psxface.c | 4 | ||||
-rw-r--r-- | drivers/acpi/acpica/tbutils.c | 38 | ||||
-rw-r--r-- | drivers/acpi/acpica/tbxface.c | 32 |
5 files changed, 61 insertions, 20 deletions
diff --git a/drivers/acpi/acpica/acglobal.h b/drivers/acpi/acpica/acglobal.h index 2b2e61ed373e..a419fe98a5fc 100644 --- a/drivers/acpi/acpica/acglobal.h +++ b/drivers/acpi/acpica/acglobal.h @@ -165,6 +165,11 @@ ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1a_enable; ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1b_status; ACPI_EXTERN struct acpi_generic_address acpi_gbl_xpm1b_enable; +/* DSDT information. Used to check for DSDT corruption */ + +ACPI_EXTERN struct acpi_table_desc *acpi_gbl_DSDT; +ACPI_EXTERN struct acpi_table_header acpi_gbl_original_dsdt_header; + /* * Handle both ACPI 1.0 and ACPI 2.0 Integer widths. The integer width is * determined by the revision of the DSDT: If the DSDT revision is less than diff --git a/drivers/acpi/acpica/actables.h b/drivers/acpi/acpica/actables.h index 8ff3b741df28..fc52b6f2d69c 100644 --- a/drivers/acpi/acpica/actables.h +++ b/drivers/acpi/acpica/actables.h @@ -107,6 +107,8 @@ u8 acpi_tb_checksum(u8 *buffer, u32 length); acpi_status acpi_tb_verify_checksum(struct acpi_table_header *table, u32 length); +void acpi_tb_check_dsdt_header(void); + void acpi_tb_install_table(acpi_physical_address address, char *signature, u32 table_index); diff --git a/drivers/acpi/acpica/psxface.c b/drivers/acpi/acpica/psxface.c index 6064dd4e94c2..67e7ad517051 100644 --- a/drivers/acpi/acpica/psxface.c +++ b/drivers/acpi/acpica/psxface.c @@ -220,6 +220,10 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info) ACPI_FUNCTION_TRACE(ps_execute_method); + /* Quick validation of DSDT header */ + + acpi_tb_check_dsdt_header(); + /* Validate the Info and method Node */ if (!info || !info->resolved_node) { diff --git a/drivers/acpi/acpica/tbutils.c b/drivers/acpi/acpica/tbutils.c index f47a70e20063..07bc7437f82b 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -349,6 +349,44 @@ u8 acpi_tb_checksum(u8 *buffer, u32 length) /******************************************************************************* * + * FUNCTION: acpi_tb_check_dsdt_header + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Quick compare to check validity of the DSDT. This will detect + * if the DSDT has been replaced from outside the OS and/or if + * the DSDT header has been corrupted. + * + ******************************************************************************/ + +void acpi_tb_check_dsdt_header(void) +{ + + /* Compare original length and checksum to current values */ + + if (acpi_gbl_original_dsdt_header.length != + acpi_gbl_DSDT->pointer->length + || acpi_gbl_original_dsdt_header.checksum != + acpi_gbl_DSDT->pointer->checksum) { + ACPI_ERROR((AE_INFO, + "The DSDT has been corrupted or replaced - old, new headers below")); + acpi_tb_print_table_header(0, &acpi_gbl_original_dsdt_header); + acpi_tb_print_table_header(acpi_gbl_DSDT->address, + acpi_gbl_DSDT->pointer); + + /* Disable further error messages */ + + acpi_gbl_original_dsdt_header.length = + acpi_gbl_DSDT->pointer->length; + acpi_gbl_original_dsdt_header.checksum = + acpi_gbl_DSDT->pointer->checksum; + } +} + +/******************************************************************************* + * * FUNCTION: acpi_tb_install_table * * PARAMETERS: Address - Physical address of DSDT or FACS diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index 684614d0d327..30565100b94c 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -518,33 +518,25 @@ static acpi_status acpi_tb_load_namespace(void) (void)acpi_ut_acquire_mutex(ACPI_MTX_TABLES); + acpi_gbl_DSDT = &acpi_gbl_root_table_list.tables[ACPI_TABLE_INDEX_DSDT]; + /* - * Load the namespace. The DSDT is required, but any SSDT and PSDT tables - * are optional. + * Load the namespace. The DSDT is required, but any SSDT and + * PSDT tables are optional. Verify the DSDT. */ if (!acpi_gbl_root_table_list.count || - !ACPI_COMPARE_NAME(& - (acpi_gbl_root_table_list. - tables[ACPI_TABLE_INDEX_DSDT].signature), - ACPI_SIG_DSDT) - || - ACPI_FAILURE(acpi_tb_verify_table - (&acpi_gbl_root_table_list. - tables[ACPI_TABLE_INDEX_DSDT]))) { + !ACPI_COMPARE_NAME(&acpi_gbl_DSDT->signature, ACPI_SIG_DSDT) || + ACPI_FAILURE(acpi_tb_verify_table(acpi_gbl_DSDT))) { status = AE_NO_ACPI_TABLES; goto unlock_and_exit; } - /* A valid DSDT is required */ - - status = - acpi_tb_verify_table(&acpi_gbl_root_table_list. - tables[ACPI_TABLE_INDEX_DSDT]); - if (ACPI_FAILURE(status)) { - - status = AE_NO_ACPI_TABLES; - goto unlock_and_exit; - } + /* + * Save the original DSDT header for detection of table corruption + * and/or replacement of the DSDT from outside the OS. + */ + ACPI_MEMCPY(&acpi_gbl_original_dsdt_header, acpi_gbl_DSDT->pointer, + sizeof(struct acpi_table_header)); (void)acpi_ut_release_mutex(ACPI_MTX_TABLES); |