Commit aca2a5d3 authored by Lv Zheng's avatar Lv Zheng Committed by Rafael J. Wysocki

ACPICA: Hardware: Enable 64-bit firmware waking vector for selected FACS

ACPICA commit 7aa598d711644ab0de5f70ad88f1e2de253115e4

The following commit is reported to have broken s2ram on some platforms:
 Commit: 0249ed24
 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 new
acpi_set_firmware_waking_vectors() API 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. Lv Zheng.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=74021
Link: https://github.com/acpica/acpica/commit/7aa598d7Reported-and-tested-by: default avatarOswald Buddenhagen <ossi@kde.org>
Signed-off-by: default avatarLv Zheng <lv.zheng@intel.com>
Signed-off-by: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent 7011bf4e
...@@ -72,6 +72,7 @@ static struct acpi_sleep_functions acpi_sleep_dispatch[] = { ...@@ -72,6 +72,7 @@ static struct acpi_sleep_functions acpi_sleep_dispatch[] = {
/* /*
* These functions are removed for the ACPI_REDUCED_HARDWARE case: * These functions are removed for the ACPI_REDUCED_HARDWARE case:
* acpi_set_firmware_waking_vectors
* acpi_set_firmware_waking_vector * acpi_set_firmware_waking_vector
* acpi_set_firmware_waking_vector64 * acpi_set_firmware_waking_vector64
* acpi_enter_sleep_state_s4bios * acpi_enter_sleep_state_s4bios
...@@ -80,20 +81,24 @@ static struct acpi_sleep_functions acpi_sleep_dispatch[] = { ...@@ -80,20 +81,24 @@ static struct acpi_sleep_functions acpi_sleep_dispatch[] = {
#if (!ACPI_REDUCED_HARDWARE) #if (!ACPI_REDUCED_HARDWARE)
/******************************************************************************* /*******************************************************************************
* *
* FUNCTION: acpi_set_firmware_waking_vector * FUNCTION: acpi_set_firmware_waking_vectors
* *
* PARAMETERS: physical_address - 32-bit physical address of ACPI real mode * PARAMETERS: physical_address - 32-bit physical address of ACPI real mode
* entry point. * entry point.
* physical_address64 - 64-bit physical address of ACPI protected
* mode entry point.
* *
* RETURN: Status * 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_vectors(acpi_physical_address physical_address,
acpi_physical_address physical_address64)
{ {
ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector); ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vectors);
/* /*
...@@ -106,17 +111,51 @@ acpi_status acpi_set_firmware_waking_vector(u32 physical_address) ...@@ -106,17 +111,51 @@ acpi_status acpi_set_firmware_waking_vector(u32 physical_address)
/* Set the 32-bit vector */ /* 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)) { /* Set the 64-bit vector */
acpi_gbl_FACS->xfirmware_waking_vector = 0;
acpi_gbl_FACS->xfirmware_waking_vector =
physical_address64;
} else {
/* Clear the 64-bit vector if it exists */
acpi_gbl_FACS->xfirmware_waking_vector = 0;
}
} }
return_ACPI_STATUS(AE_OK); return_ACPI_STATUS(AE_OK);
} }
ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vectors)
/*******************************************************************************
*
* FUNCTION: acpi_set_firmware_waking_vector
*
* PARAMETERS: physical_address - 32-bit physical address of ACPI real mode
* entry point.
*
* RETURN: Status
*
* DESCRIPTION: Sets the 32-bit firmware_waking_vector field of the FACS
*
******************************************************************************/
acpi_status acpi_set_firmware_waking_vector(u32 physical_address)
{
acpi_status status;
ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector);
status = acpi_set_firmware_waking_vectors((acpi_physical_address)
physical_address, 0);
return_ACPI_STATUS(status);
}
ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector) ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
#if ACPI_MACHINE_WIDTH == 64 #if ACPI_MACHINE_WIDTH == 64
...@@ -136,19 +175,15 @@ ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector) ...@@ -136,19 +175,15 @@ ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
******************************************************************************/ ******************************************************************************/
acpi_status acpi_set_firmware_waking_vector64(u64 physical_address) acpi_status acpi_set_firmware_waking_vector64(u64 physical_address)
{ {
ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64); acpi_status status;
/* Determine if the 64-bit vector actually exists */
if ((acpi_gbl_FACS->length <= 32) || (acpi_gbl_FACS->version < 1)) { ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64);
return_ACPI_STATUS(AE_NOT_EXIST);
}
/* Clear 32-bit vector, set the 64-bit X_ vector */ status = acpi_set_firmware_waking_vectors(0,
(acpi_physical_address)
physical_address);
acpi_gbl_FACS->firmware_waking_vector = 0; return_ACPI_STATUS(status);
acpi_gbl_FACS->xfirmware_waking_vector = physical_address;
return_ACPI_STATUS(AE_OK);
} }
ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64) ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64)
......
...@@ -814,8 +814,12 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status ...@@ -814,8 +814,12 @@ ACPI_EXTERNAL_RETURN_STATUS(acpi_status
ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_leave_sleep_state(u8 sleep_state)) ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_leave_sleep_state(u8 sleep_state))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
acpi_set_firmware_waking_vector(u32 acpi_set_firmware_waking_vectors
physical_address)) (acpi_physical_address physical_address,
acpi_physical_address physical_address64))
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
acpi_set_firmware_waking_vector(u32
physical_address))
#if ACPI_MACHINE_WIDTH == 64 #if ACPI_MACHINE_WIDTH == 64
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
acpi_set_firmware_waking_vector64(u64 acpi_set_firmware_waking_vector64(u64
......
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