[UPDATE PATCH v2 03/28] ACPICA: Hardware: Enable 64-bit firmware waking vector for selected FACS.

From: Lv Zheng
Date: Wed Jun 24 2015 - 22:35:58 EST


ACPICA commit 7aa598d711644ab0de5f70ad88f1e2de253115e4

The following commit is reported to have broken s2ram on some platforms:
Commit: 0249ed2444d65d65fc3f3f64f398f1ad0b7e54cd
ACPICA: Add option to favor 32-bit FADT addresses.
The platform reports 2 FACS tables (which is not allowed by ACPI
specification) and the new 32-bit address favor rule forces OSPMs to use
the FACS table reported via FADT's X_FIRMWARE_CTRL field.

The root cause of the reported bug might be one of the followings:
1. BIOS may favor the 64-bit firmware waking vector address when the
version of the FACS is greater than 0 and Linux currently only supports
resuming from the real mode, so the 64-bit firmware waking vector has
never been set and might be invalid to BIOS while the commit enables
higher version FACS.
2. BIOS may favor the FACS reported via the "FIRMWARE_CTRL" field in the
FADT while the commit doesn't set the firmware waking vector address of
the FACS reported by "FIRMWARE_CTRL", it only sets the firware waking
vector address of the FACS reported by "X_FIRMWARE_CTRL".

This patch excludes the cases that can trigger the bugs caused by the root
cause 1.

ACPI specification says:
A. 32-bit FACS address (FIRMWARE_CTRL field in FADT):
Physical memory address of the FACS, where OSPM and firmware exchange
control information.
If the X_FIRMWARE_CTRL field contains a non zero value then this field
must be zero.
A zero value indicates that no FACS is specified by this field.
B. 64-bit FACS address (X_FIRMWARE_CTRL field in FADT):
64bit physical memory address of the FACS.
This field is used when the physical address of the FACS is above 4GB.
If the FIRMWARE_CTRL field contains a non zero value then this field
must be zero.
A zero value indicates that no FACS is specified by this field.
Thus the 32bit and 64bit firmware waking vector should indicate completely
different resuming environment - real mode (1MB addressable) and non real
mode (4GB+ addressable) and currently Linux only supports resuming from
real mode.

This patch enables 64-bit firmware waking vector for selected FACS via
acpi_set_firmware_waking_vector() so that it's up to OSPMs to determine which
resuming mode should be used by BIOS and ACPICA changes won't trigger the
bugs caused by the root cause 1. For example, Linux can pass
physical_address64=0 as the parameter of acpi_set_firmware_waking_vector() to
indicate no 64bit waking vector support. Lv Zheng.

This patch also updates acpi_set_firmware_waking_vector() invocations in
order to keep 32-bit firmware waking vector favor for Linux. 64-bit
firmware waking vector has never been enabled by Linux. The
(acpi_physical_address)0 for 64-bit address can be used to force ACPICA to
set only 32-bit firmware waking vector for Linux.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=74021
Link: https://github.com/acpica/acpica/commit/7aa598d7
Reported-and-tested-by: Oswald Buddenhagen <ossi@xxxxxxx>
Signed-off-by: Lv Zheng <lv.zheng@xxxxxxxxx>
Signed-off-by: Bob Moore <robert.moore@xxxxxxxxx>
---
arch/ia64/include/asm/acpi.h | 3 +-
arch/ia64/kernel/acpi.c | 2 --
arch/x86/include/asm/acpi.h | 3 +-
drivers/acpi/acpica/hwxfsleep.c | 61 ++++++++++++---------------------------
drivers/acpi/sleep.c | 8 +++--
include/acpi/acpixf.h | 11 +++----
6 files changed, 33 insertions(+), 55 deletions(-)

Index: linux-acpica/drivers/acpi/acpica/hwxfsleep.c
===================================================================
--- linux-acpica.orig/drivers/acpi/acpica/hwxfsleep.c
+++ linux-acpica/drivers/acpi/acpica/hwxfsleep.c
@@ -73,7 +73,6 @@ static struct acpi_sleep_functions acpi_
/*
* These functions are removed for the ACPI_REDUCED_HARDWARE case:
* acpi_set_firmware_waking_vector
- * acpi_set_firmware_waking_vector64
* acpi_enter_sleep_state_s4bios
*/

@@ -83,15 +82,19 @@ static struct acpi_sleep_functions acpi_
* FUNCTION: acpi_set_firmware_waking_vector
*
* PARAMETERS: physical_address - 32-bit physical address of ACPI real mode
- * entry point.
+ * entry point
+ * physical_address64 - 64-bit physical address of ACPI protected
+ * entry point
*
* RETURN: Status
*
- * DESCRIPTION: Sets the 32-bit firmware_waking_vector field of the FACS
+ * DESCRIPTION: Sets the firmware_waking_vector fields of the FACS
*
******************************************************************************/

-acpi_status acpi_set_firmware_waking_vector(u32 physical_address)
+acpi_status
+acpi_set_firmware_waking_vector(acpi_physical_address physical_address,
+ acpi_physical_address physical_address64)
{
ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);

@@ -106,53 +109,27 @@ acpi_status acpi_set_firmware_waking_vec

/* Set the 32-bit vector */

- acpi_gbl_FACS->firmware_waking_vector = physical_address;
+ acpi_gbl_FACS->firmware_waking_vector = (u32)physical_address;

- /* Clear the 64-bit vector if it exists */
+ if (acpi_gbl_FACS->length > 32) {
+ if (acpi_gbl_FACS->version >= 1) {

- if ((acpi_gbl_FACS->length > 32) && (acpi_gbl_FACS->version >= 1)) {
- acpi_gbl_FACS->xfirmware_waking_vector = 0;
- }
-
- return_ACPI_STATUS(AE_OK);
-}
-
-ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
-
-#if ACPI_MACHINE_WIDTH == 64
-/*******************************************************************************
- *
- * FUNCTION: acpi_set_firmware_waking_vector64
- *
- * PARAMETERS: physical_address - 64-bit physical address of ACPI protected
- * mode entry point.
- *
- * RETURN: Status
- *
- * DESCRIPTION: Sets the 64-bit X_firmware_waking_vector field of the FACS, if
- * it exists in the table. This function is intended for use with
- * 64-bit host operating systems.
- *
- ******************************************************************************/
-acpi_status acpi_set_firmware_waking_vector64(u64 physical_address)
-{
- ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64);
+ /* Set the 64-bit vector */

- /* Determine if the 64-bit vector actually exists */
+ acpi_gbl_FACS->xfirmware_waking_vector =
+ physical_address64;
+ } else {
+ /* Clear the 64-bit vector if it exists */

- if ((acpi_gbl_FACS->length <= 32) || (acpi_gbl_FACS->version < 1)) {
- return_ACPI_STATUS(AE_NOT_EXIST);
+ acpi_gbl_FACS->xfirmware_waking_vector = 0;
+ }
}

- /* Clear 32-bit vector, set the 64-bit X_ vector */
-
- acpi_gbl_FACS->firmware_waking_vector = 0;
- acpi_gbl_FACS->xfirmware_waking_vector = physical_address;
return_ACPI_STATUS(AE_OK);
}

-ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64)
-#endif
+ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
+
/*******************************************************************************
*
* FUNCTION: acpi_enter_sleep_state_s4bios
Index: linux-acpica/drivers/acpi/sleep.c
===================================================================
--- linux-acpica.orig/drivers/acpi/sleep.c
+++ linux-acpica/drivers/acpi/sleep.c
@@ -54,6 +54,19 @@ static struct notifier_block tts_notifie
.priority = 0,
};

+static inline void acpi_sleep_set_wakeup_address(void)
+{
+ (void)acpi_set_firmware_waking_vector(
+ (acpi_physical_address)acpi_wakeup_address,
+ (acpi_physical_address)0);
+}
+
+static inline void acpi_sleep_clear_wakeup_address(void)
+{
+ (void)acpi_set_firmware_waking_vector(
+ (acpi_physical_address)0, (acpi_physical_address)0);
+}
+
static int acpi_sleep_prepare(u32 acpi_state)
{
#ifdef CONFIG_ACPI_SLEEP
@@ -61,7 +74,7 @@ static int acpi_sleep_prepare(u32 acpi_s
if (acpi_state == ACPI_STATE_S3) {
if (!acpi_wakeup_address)
return -EFAULT;
- acpi_set_firmware_waking_vector(acpi_wakeup_address);
+ acpi_sleep_set_wakeup_address();

}
ACPI_FLUSH_CPU_CACHE();
@@ -410,7 +423,7 @@ static void acpi_pm_finish(void)
acpi_leave_sleep_state(acpi_state);

/* reset firmware waking vector */
- acpi_set_firmware_waking_vector((acpi_physical_address) 0);
+ acpi_sleep_clear_wakeup_address();

acpi_target_sleep_state = ACPI_STATE_S0;

Index: linux-acpica/include/acpi/acpixf.h
===================================================================
--- linux-acpica.orig/include/acpi/acpixf.h
+++ linux-acpica/include/acpi/acpixf.h
@@ -814,13 +814,10 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_leave_sleep_state(u8 sleep_state))

ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
- acpi_set_firmware_waking_vector(u32
- physical_address))
-#if ACPI_MACHINE_WIDTH == 64
-ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
- acpi_set_firmware_waking_vector64(u64
- physical_address))
-#endif
+ acpi_set_firmware_waking_vector
+ (acpi_physical_address physical_address,
+ acpi_physical_address physical_address64))
+
/*
* ACPI Timer interfaces
*/
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/