From 729df0f848daf2f17d02107199fa92efe909d995 Mon Sep 17 00:00:00 2001 From: Lin Ming Date: Thu, 1 Apr 2010 10:47:56 +0800 Subject: [PATCH] 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 Signed-off-by: Bob Moore Signed-off-by: Len Brown --- drivers/acpi/acpica/acglobal.h | 5 +++++ drivers/acpi/acpica/actables.h | 2 ++ drivers/acpi/acpica/psxface.c | 4 ++++ drivers/acpi/acpica/tbutils.c | 38 ++++++++++++++++++++++++++++++++++ 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 2b2e61ed373..a419fe98a5f 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 8ff3b741df2..fc52b6f2d69 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 6064dd4e94c..67e7ad51705 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 f47a70e2006..07bc7437f82 100644 --- a/drivers/acpi/acpica/tbutils.c +++ b/drivers/acpi/acpica/tbutils.c @@ -347,6 +347,44 @@ u8 acpi_tb_checksum(u8 *buffer, u32 length) return sum; } +/******************************************************************************* + * + * 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 diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index 684614d0d32..30565100b94 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); -- 2.39.2