ACPICA: Hardware: Enable 64-bit firmware waking vector for selected FACS
authorLv Zheng <lv.zheng@intel.com>
Wed, 1 Jul 2015 06:43:04 +0000 (14:43 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 1 Jul 2015 21:17:52 +0000 (23:17 +0200)
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 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/7aa598d7
Reported-and-tested-by: Oswald Buddenhagen <ossi@kde.org>
Signed-off-by: Lv Zheng <lv.zheng@intel.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/acpi/acpica/hwxfsleep.c
include/acpi/acpixf.h

index 82e310b94ae4681ca2923336436f0edea3bafec3..25cd9783b640aa3dd3ba5f90f9d761946d9539dd 100644 (file)
@@ -72,6 +72,7 @@ static struct acpi_sleep_functions acpi_sleep_dispatch[] = {
 
 /*
  * These functions are removed for the ACPI_REDUCED_HARDWARE case:
+ *      acpi_set_firmware_waking_vectors
  *      acpi_set_firmware_waking_vector
  *      acpi_set_firmware_waking_vector64
  *      acpi_enter_sleep_state_s4bios
@@ -80,20 +81,24 @@ static struct acpi_sleep_functions acpi_sleep_dispatch[] = {
 #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
  *                                    entry point.
+ *              physical_address64  - 64-bit physical address of ACPI protected
+ *                                    mode 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_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)
 
        /* 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;
+                       /* Set the 64-bit vector */
+
+                       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);
 }
 
+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)
 
 #if ACPI_MACHINE_WIDTH == 64
@@ -136,19 +175,15 @@ ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector)
  ******************************************************************************/
 acpi_status acpi_set_firmware_waking_vector64(u64 physical_address)
 {
-       ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64);
-
-       /* Determine if the 64-bit vector actually exists */
+       acpi_status status;
 
-       if ((acpi_gbl_FACS->length <= 32) || (acpi_gbl_FACS->version < 1)) {
-               return_ACPI_STATUS(AE_NOT_EXIST);
-       }
+       ACPI_FUNCTION_TRACE(acpi_set_firmware_waking_vector64);
 
-       /* 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;
-       acpi_gbl_FACS->xfirmware_waking_vector = physical_address;
-       return_ACPI_STATUS(AE_OK);
+       return_ACPI_STATUS(status);
 }
 
 ACPI_EXPORT_SYMBOL(acpi_set_firmware_waking_vector64)
index d68f1cd39c495f732b66c048d64cbe384162cd6d..821095ce5af76a23426c280ad5c556b25e0e5c20 100644 (file)
@@ -814,8 +814,12 @@ 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))
+                               acpi_set_firmware_waking_vectors
+                               (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
 ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status
                                acpi_set_firmware_waking_vector64(u64