ACPI 2.0: Enable TermList interpretion and early opeartion region accesses for table loading
Subsystems (2)
cover ACPI 2.0: Enable TermList interpretion and early opeartion region accesses for table loading
The following AML code is assembled into a static loading SSDT, and used as an instrumentation to pry into the de-facto standard AML interpreter behaviors:
Name (ECOK, Zero)
Scope (\)
{
DBUG ("TermList 1")
If (LEqual (ECOK, Zero))
{
DBUG ("TermList 2")
Device (MDEV)
{
DEBUG (TermList 3")
If (CondRefOf (MDEV))
{
DBUG ("MDEV exists")
}
If (CondRefOf (MDEV._STA))
{
DBUG ("MDEV._STA exists")
}
If (CondRefOf (\_SB.PCI0.EC))
{
DBUG ("\\_SB.PCI0.EC exists")
}
Name (_HID, EisaId ("PNP9999"))
Method (_STA, 0, Serialized)
{
DEBUG ("\\_SB.MDEV._STA")
Return (0x0F)
}
}
DBUG ("TermList 4")
}
Method (_INI, 0, Serialized)
{
DBUG ("\\_SB._INI")
}
}
Scope (_SB.PCI0)
{
Device (EC)
{
...
} }
The DBUG function is a function to write the debugging messages into a SystemIo debug port.
Running Windows with the BIOS providing this SSDT via RSDT, the following messages are obtained from the debug port:
TermList 1
TermList 2
TermList 3
\_SB.MDEV exists
TermList 4
\_SB._INI
...
This test reveals the de-facto table loading behaviors to us:
1. During the table loading, AML opcodes out of a control method (this is called as module level code in ACPICA term) will be executed by the interpreter;
2. Not only the module level AML opcodes wrapped around by If/Else/While (this is the current ACPICA interpreter limitation), but all module level AML opcodes, including the operation region accesses, method invocations, will be executed by the interpreter;
3. Not only the module level AML opcodes put under the root scope (this is another current ACPICA interpreter limitation), but the module level AML opcodes under any scope will be executed by the interpreter;
4. Not only after the table loading (this is another current ACPICA interpreter limitation), but when the table is being loaded, the module level AML opcodes will be executed by the interpreter, please refer to the above CondRefOf validations;
5. For SystemIo, not only after the _REG(1, 1) is evaluated (this is another current ACPICA interpreter limitation), but when the table is being loaded, the SystemIo (the debugging port) is accessible.
In fact, the above behaviors has already been clarified in ACPI 2.0 specification, so the compliance issue is not that Linux is not compliant to the de-facto standard OS, but that Linux is not compliant to ACPI 2.0.
1. Definition tables in fact is defined by the spec as TermList, which has no difference than the control methods:
AMLCode := DefBlockHeader TermList
DefMethod := MethodOp PkgLength NameString MethodFlags TermList
Thus the interpretion of the table should be no difference that the control method evaluation;
2. Spec allows the default operation regions to be accessed before the namespace is ready (which exactly means the table loading), such operation regions include SystemIo, SystemMemory, PciConfig and EmbeddedControl provided by ECDT, note that ECDT is also an ACPI 2.0 feature.
Why ACPICA interpreter is acting so differently from this definition? This is because, there are many software entropies preventing this from being enabled, such entropies need to be cleaned up first in order not to trigger regressions for specific platforms. These entropies include:
1. ECDT support is broken. In fact, the original EC driver was correct, but devlopers start to use the namespace EC instead of ECDT just because several broken ECDT tables were reported on the bugzilla. They trusted the namespace EC settings rather than the ECDT ones, this leads to the evaluation of _REG/_GPE/_CRS and namespace walk before executing the module level AML opcodes. And this in fact disables early EC usages (used during table loading and early device enumeration processes).
2. _REG evaluations are wrong. ACPICA provides APIs for OSPMs to register operation region handlers. But for the early operation region accesses, ACPI spec declares that the evaluations of _REG are not required, but the ACPICA API doesn't avoid running _REG to meet this early requirements. Code to fix this is partially upstreamed during previous ACPICA release cycle.
3. _REG associations are wrong. ACPICA associate _REG control method to all operation region objects before executing the _REG control method. This can happen even when a control method is evaluated and operation regions defined in the method is initialized (acpi_ev_initialize_region). As a part of the ACPICA internal _REG evaluation state machine, it requires the namespace walk, and all namespace walk should be ensured to happen only "AFTER THE NAMESPACE IS INITIALIZED". But when this logic happens during the table loading, it may fail in finding the _REG method since the _REG method may not be created by the interpreter just because _REG is defined after the operation region object's declaration.
4. _REG(CONNECT)/_REG(DISCONNECT) executions are not balanced, this can lead to wrong table loading/unloading results. Since _REG evaluations require the releasing of all interpreter/namespace locks in order to allow another evaluation to happen, and ACPICA operand object destruction code can be invoked from different locking environment, this becomes difficult for the developers to provide one single function to make _REG(CONNECT)/_REG(DISCONNECT) balanced.
5. \_SB._INI is not the first control method evaluated by the interpreter. Many platforms put initialization code in \_SB._INI in order to have named objects initialized very early during the device enumeration process. Without this order is strictly ensured, early operation region access enabling could break these platforms.
6. Linux initialization order is wrong, it is now:
a. load namespace without executing root scope If/Else/While module
level code blocks;
b. probe ECDT and instal EmbeddedControl operation region handler with
_REG evaluated;
c. install SystemMemory, SystemIo, PciConfig operation region handlers
without evaluating _REG;
d. run _REG for SystemMemory, SystemIo, PciConfig operation regions;
e. execute root scope If/Else/While module level code blocks;
f. enable GPE and namespace EC.
While the correct order should be:
a. probe ECDT and instal EmbeddedControl operation region handler with
_REG evaluated;
b. install SystemMemory, SystemIo, PciConfig operation region handlers
without evaluating _REG;
c. load namespace, in the meanshile, execute all module level AML
opcodes; d. run _REG for SystemMemory, SystemIo, PciConfig operation regions; e. enable GPE and namespace EC.
Now it's correct timing to make things back to be spec compliant. In order to achieve this for Linux, we need to:
1. Stop evaluating control methods before namespace is fully loaded, this in fact means no GPEs should be enabled during table loading since GPE indications may lead to _Lxx/_Exx evaluations; no EC event should be enabled during table loading since SCI_EC indications may lead to _Qxx evaluations; no _REG/_CRS should be evaluated since they apparently should happen in the device enumeration process which is performed after loading the namespace, and no namespace walk is allowed.
2. Default operation regions should be initialized and ready for accessing before loading the table.
This patchset fixes all above entropies and ensures the 2 above rules, after that it enables module level AML execution in 2 steps:
1. Improve module level execution support by disabling acpi_gbl_group_module_level_code, this helps to move module level execution to per-table basis.
2. Fix module level execution support by enabling acpi_gbl_parse_table_as_term_list, this helps to execute the entire table as the way similar to a control method evaluation.
The series is tested by running Linux on Dell Latitude 6430u. Since the platform doesn't contain ECDT, the patchset also enables initrd table installation mechanism to fake an ECDT for testing. The patform can be successfully powered on/off, suspended/resumed, device are also correctly enumerated and GPEs/EC events are correctly handled.
PATCH 01-02: The initrd installation testing facility. PATCH 03-07: Entropy reduction stuff in ACPICA.
PATCH 08-10: Entropy reduction stuff in EC driver and ACPI subsystem's
initialization order.
PATCH 12: The first step enabling. PATCH 13: The code to facilitate AML 2.0 table loading. PATCH 14: The second step enabling.
Lv Zheng (14):
ACPI / OSL: Cleanup initrd table override code
ACPI / OSL: Add support to install tables via initrd
ACPI 2.0 / AML: Make default region accessible during the table load ACPI 2.0 / AML: Tune _REG evaluations order in the initialization
steps
ACPI 2.0 / AML: Ensure \_SB._INI executed before any _REG ACPI 2.0 / AML: Rename acpi_gbl_reg_methods_enabled to
acpi_gbl_namespace_initialized
ACPICA: Events: Fix an issue that _REG association can happen before
namespace is initialized
ACPI 2.0 / ECDT: Split EC_FLAGS_HANDLERS_INSTALLED
ACPI 2.0 / ECDT: Remove early namespace reference from EC ACPI 2.0 / ECDT: Enable correct ECDT initialization order ACPI 2.0 / AML: Improve module level execution by moving the
If/Else/While execution to per-table basis
ACPI 2.0 / AML: Add TermList parsing support for table loading ACPI 2.0 / AML: Enable correct ACPI subsystem initialization order
for new table loading mode
ACPI 2.0 / AML: Fix module level execution by correctly parsing table
as TermList
drivers/acpi/acpica/acevents.h | 2 - drivers/acpi/acpica/acglobal.h | 2 +- drivers/acpi/acpica/acnamesp.h | 5 +- drivers/acpi/acpica/acparser.h | 2 + drivers/acpi/acpica/evregion.c | 71 ++++-------- drivers/acpi/acpica/evrgnini.c | 1 - drivers/acpi/acpica/exconfig.c | 7 +- drivers/acpi/acpica/nsinit.c | 135 +++++++++++++--------- drivers/acpi/acpica/nsload.c | 3 +- drivers/acpi/acpica/nsparse.c | 163 +++++++++++++++++++++------ drivers/acpi/acpica/psparse.c | 4 +- drivers/acpi/acpica/psxface.c | 73 ++++++++++++ drivers/acpi/acpica/tbxfload.c | 23 ++++ drivers/acpi/acpica/utxfinit.c | 46 +++----- drivers/acpi/bus.c | 41 ++++--- drivers/acpi/ec.c | 241 ++++++++++++++++++---------------------- drivers/acpi/internal.h | 1 + drivers/acpi/osl.c | 158 ++++++++++++++++---------- drivers/acpi/tables.c | 2 + include/acpi/acpixf.h | 9 +- 20 files changed, 611 insertions(+), 378 deletions(-)
Signature · 1 line
1.7.10
PATCH 1/14 ACPI / OSL: Cleanup initrd table override code
This patch cleans up initrd table override code, merging redundant logics and re-ordering code blocks. No functional changes.
--- drivers/acpi/osl.c | 114 ++++++++++++++++++++++++---------------------------- 1 file changed, 52 insertions(+), 62 deletions(-)
drivers/acpi/osl.c +52 -62
@@ -602,6 +602,14 @@ acpi_os_predefined_override(const struct acpi_predefined_names *init_val,return AE_OK ; } +static void acpi_table_taint (struct acpi_table_header *table) +{ +pr_warn (PREFIX +"Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n" , + table->signature, table->oem_table_id); +add_taint (TAINT_OVERRIDDEN_ACPI_TABLE ,LOCKDEP_NOW_UNRELIABLE ); +} +#ifdef CONFIG_ACPI_INITRD_TABLE_OVERRIDE #include <linux/earlycpio.h> #include <linux/memblock.h>
@@ -746,96 +754,78 @@ void __init acpi_initrd_override(void *data, size_t size) } } } -#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */ - -static void acpi_table_taint (struct acpi_table_header *table) -{ -pr_warn (PREFIX -"Override [%4.4s-%8.8s], this is unsafe: tainting kernel\n" , - table->signature, table->oem_table_id); -add_taint (TAINT_OVERRIDDEN_ACPI_TABLE ,LOCKDEP_NOW_UNRELIABLE ); -} - - -acpi_status -acpi_os_table_override (struct acpi_table_header * existing_table, -struct acpi_table_header ** new_table) -{ -if (!existing_table || !new_table) -return AE_BAD_PARAMETER ; - - *new_table =NULL ; - -#ifdef CONFIG_ACPI_CUSTOM_DSDT -if (strncmp (existing_table->signature,"DSDT" ,4 ) ==0 ) - *new_table = (struct acpi_table_header *)AmlCode; -#endif -if (*new_table !=NULL ) -acpi_table_taint (existing_table); -return AE_OK ; -}acpi_status acpi_os_physical_table_override (struct acpi_table_header *existing_table, -acpi_physical_address *address, -u32 *table_length) +acpi_physical_address *address,u32 *length) { -#ifndef CONFIG_ACPI_INITRD_TABLE_OVERRIDE - *table_length =0 ; - *address =0 ; -return AE_OK ; -#else int table_offset =0 ;struct acpi_table_header *table; +u32 table_length; - *table_length =0 ; + *length =0 ; *address =0 ; -if (!acpi_tables_addr)return AE_OK ; -do { -if (table_offset +ACPI_HEADER_SIZE > all_tables_size) { -WARN_ON (1 ); -return AE_OK ; - } - +while (table_offset +ACPI_HEADER_SIZE <= all_tables_size) { table =acpi_os_map_memory (acpi_tables_addr + table_offset,ACPI_HEADER_SIZE ); -if (table_offset + table->length > all_tables_size) {acpi_os_unmap_memory (table,ACPI_HEADER_SIZE );WARN_ON (1 );return AE_OK ; } - table_offset += table->length; + table_length = table->length; -if (memcmp (existing_table->signature, table->signature,4 )) { -acpi_os_unmap_memory (table, -ACPI_HEADER_SIZE ); -continue ; - } - -/* Only override tables with matching oem id */ -if (memcmp (table->oem_table_id, existing_table->oem_table_id, +/* Only override tables matched */ +if (memcmp (existing_table->signature, table->signature,4 ) || +memcmp (table->oem_table_id, existing_table->oem_table_id,ACPI_OEM_TABLE_ID_SIZE )) { -acpi_os_unmap_memory (table, -ACPI_HEADER_SIZE ); -continue ; +acpi_os_unmap_memory (table,ACPI_HEADER_SIZE ); + goto next_table; } - table_offset -= table->length; - *table_length = table->length; -acpi_os_unmap_memory (table,ACPI_HEADER_SIZE ); + *length = table_length; *address = acpi_tables_addr + table_offset; +acpi_table_taint (existing_table); +acpi_os_unmap_memory (table,ACPI_HEADER_SIZE );break ; - }while (table_offset +ACPI_HEADER_SIZE < all_tables_size); -if (*address !=0 ) -acpi_table_taint (existing_table); +next_table: + table_offset += table_length; + }return AE_OK ; +} +#else +acpi_status +acpi_os_physical_table_override (struct acpi_table_header *existing_table, +acpi_physical_address *address, +u32 *table_length) +{ + *table_length =0 ; + *address =0 ; +return AE_OK ; +} +#endif /* CONFIG_ACPI_INITRD_TABLE_OVERRIDE */ + +acpi_status +acpi_os_table_override (struct acpi_table_header * existing_table, +struct acpi_table_header ** new_table) +{ +if (!existing_table || !new_table) +return AE_BAD_PARAMETER ; + + *new_table =NULL ; + +#ifdef CONFIG_ACPI_CUSTOM_DSDT +if (strncmp (existing_table->signature,"DSDT" ,4 ) ==0 ) + *new_table = (struct acpi_table_header *)AmlCode;#endif +if (*new_table !=NULL ) +acpi_table_taint (existing_table); +return AE_OK ; }static irqreturn_t acpi_irq (int irq,void *dev_id)
Signature · 1 line
1.7.10
PATCH 2/14 ACPI / OSL: Add support to install tables via initrd
In reply to Lv ZhengThis patch adds support to install tables from initrd.
If a table in the initrd wasn't used by the override mechanism, the table would be installed after initializing all RSDT/XSDT tables.
Reference: https://lkml.org/lkml/2014/2/28/368
--- drivers/acpi/internal.h | 1 + drivers/acpi/osl.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++- drivers/acpi/tables.c | 2 ++ 3 files changed, 52 insertions(+), 1 deletion(-)
drivers/acpi/internal.h +1 -0
@@ -20,6 +20,7 @@#define PREFIX "ACPI: " +void acpi_initrd_initialize_tables (void );acpi_status acpi_os_initialize1 (void );void init_acpi_device_notify (void );int acpi_scan_init (void );
drivers/acpi/osl.c +49 -1
87 lines not on this page · raw message
drivers/acpi/tables.c +2 -0
@@ -32,6 +32,7 @@#include <linux/errno.h> #include <linux/acpi.h> #include <linux/bootmem.h> +#include "internal.h" #define ACPI_MAX_TABLES 128
@@ -456,6 +457,7 @@ int __init acpi_table_init(void) status =acpi_initialize_tables (initial_tables,ACPI_MAX_TABLES ,0 );if (ACPI_FAILURE (status))return -EINVAL ; +acpi_initrd_initialize_tables ();check_multiple_madt ();return 0 ;
Signature · 1 line
1.7.10
PATCH 3/14 ACPI 2.0 / AML: Make default region accessible during the table load
In reply to Lv ZhengACPICA commit 016b2a0917cca9cf0d40c38a1541017d9cf569dd
It is proven that the default regions should be accessible during the table loading in order to execute module level AML code.
This patch moves default region handler installation code earlier in order to make this happen.
Note that by putting the code here, we actually allow OSPMs to override default region handlers between acpi_initialize_subsystem() and acpi_load_tables(), without the need to introduce region handler override mechanism in acpi_install_address_space_handler(). OSPMs are also couraged to check acpi_install_address_space_handler() return value to determine if acpi_remove_address_space_handler() should be invoked before installing new address space handler. Lv Zheng.
--- drivers/acpi/acpica/tbxfload.c | 22 ++++++++++++++++++++++ drivers/acpi/acpica/utxfinit.c | 12 +++++++----- 2 files changed, 29 insertions(+), 5 deletions(-)
drivers/acpi/acpica/tbxfload.c +22 -0
@@ -47,6 +47,7 @@#include "accommon.h" #include "acnamesp.h" #include "actables.h" +#include "acevents.h" #define _COMPONENT ACPI_TABLESACPI_MODULE_NAME ("tbxfload" )
@@ -68,6 +69,27 @@ acpi_status __init acpi_load_tables(void)ACPI_FUNCTION_TRACE (acpi_load_tables); +/* +* Install the default operation region handlers. These are the +* handlers that are defined by the ACPI specification to be +* "always accessible" -- namely, system_memory, system_IO, and +* PCI_Config. This also means that no _REG methods need to be +* run for these address spaces. We need to have these handlers +* installed before any AML code can be executed, especially any +* module-level code (11/2015). +* Note that we allow OSPMs to install their own region handlers +* between acpi_initialize_subsystem() and acpi_load_tables() to use +* their customized default region handlers. +*/ +if (!acpi_gbl_group_module_level_code) { + status =acpi_ev_install_region_handlers (); +if (ACPI_FAILURE (status) && status !=AE_ALREADY_EXISTS ) { +ACPI_EXCEPTION ((AE_INFO , status, +"During Region initialization" )); +return_ACPI_STATUS (status); + } + } +/* Load the namespace from the tables */ status =acpi_tb_load_namespace ();
drivers/acpi/acpica/utxfinit.c +7 -5
@@ -163,11 +163,13 @@ acpi_status __init acpi_enable_subsystem(u32 flags)* installed before any AML code can be executed, especially any * module-level code (11/2015). */ - status =acpi_ev_install_region_handlers (); -if (ACPI_FAILURE (status)) { -ACPI_EXCEPTION ((AE_INFO , status, -"During Region initialization" )); -return_ACPI_STATUS (status); +if (acpi_gbl_group_module_level_code) { + status =acpi_ev_install_region_handlers (); +if (ACPI_FAILURE (status)) { +ACPI_EXCEPTION ((AE_INFO , status, +"During Region initialization" )); +return_ACPI_STATUS (status); + } }#if (!ACPI_REDUCED_HARDWARE )
Signature · 1 line
1.7.10
PATCH 4/14 ACPI 2.0 / AML: Tune _REG evaluations order in the initialization steps
In reply to Lv ZhengACPICA commit 77e0c7a482ac30ef857cf3c33d075e5fe5b5e449
This patch tunes _REG evaluations to be later than all table loading facilities:
1. acpi_load_tables(): _REG is currently invoked after this function.
2. acpi_ns_exec_module_code_list(): this executes module level code, the execution should be a part of the table loading while we currently support this in a deferred way.
3. acpi_ns_initialize_objects(): this parses Region/Field/Buffer/Package where pkg_length primitive can be seen in the grammar, the parsing should be a part of the table loading while we currently support this in a deferred way.
Control method evaluation should happen after loading the tables. So this patch changes the order of _REG evaluation when acpi_gbl_group_module_level_code experiment is enabled. Lv Zheng.
--- drivers/acpi/acpica/utxfinit.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-)
drivers/acpi/acpica/utxfinit.c +18 -17
@@ -262,23 +262,6 @@ acpi_status __init acpi_initialize_objects(u32 flags)ACPI_FUNCTION_TRACE (acpi_initialize_objects); -/* -* Run all _REG methods -* -* Note: Any objects accessed by the _REG methods will be automatically -* initialized, even if they contain executable AML (see the call to -* acpi_ns_initialize_objects below). -*/ - acpi_gbl_reg_methods_enabled = TRUE; -if (!(flags &ACPI_NO_ADDRESS_SPACE_INIT )) { -ACPI_DEBUG_PRINT ((ACPI_DB_EXEC , -"[Init] Executing _REG OpRegion methods\n" )); - - status =acpi_ev_initialize_op_regions (); -if (ACPI_FAILURE (status)) { -return_ACPI_STATUS (status); - } - }#ifdef ACPI_EXEC_APP /* * This call implements the "initialization file" option for acpi_exec.
@@ -319,6 +302,24 @@ acpi_status __init acpi_initialize_objects(u32 flags) }/* +* Run all _REG methods +* +* Note: Any objects accessed by the _REG methods will be automatically +* initialized, even if they contain executable AML (see the call to +* acpi_ns_initialize_objects below). +*/ + acpi_gbl_reg_methods_enabled = TRUE; +if (!(flags &ACPI_NO_ADDRESS_SPACE_INIT )) { +ACPI_DEBUG_PRINT ((ACPI_DB_EXEC , +"[Init] Executing _REG OpRegion methods\n" )); + + status =acpi_ev_initialize_op_regions (); +if (ACPI_FAILURE (status)) { +return_ACPI_STATUS (status); + } + } + +/* * Initialize all device objects in the namespace. This runs the device * _STA and _INI methods. */
Signature · 1 line
1.7.10
PATCH 5/14 ACPI 2.0 / AML: Ensure \_SB._INI executed before any _REG
In reply to Lv ZhengACPICA commit 8ae25b8d128b6b8509010be321ff6bf2760f3807 ACPICA commit 19f84c249267fab0bfb138bd14d12510fb4faf24
There is BIOS code relying on the fact that \_SB._INI should get evaluated before any other control methods. This may imply a gap in ACPICA/Linux initialization/enumeration process.
Before revealing Windows true behavior by more validations, this patch only ensures \_SB._INI evaluated before any _REG control methods. This can help to make progress to other initialization order fixes. Lv Zheng.
--- drivers/acpi/acpica/acnamesp.h | 2 +- drivers/acpi/acpica/nsinit.c | 135 ++++++++++++++++++++++++---------------- drivers/acpi/acpica/utxfinit.c | 27 ++------ 3 files changed, 86 insertions(+), 78 deletions(-)
drivers/acpi/acpica/acnamesp.h +1 -1
@@ -88,7 +88,7 @@*/ acpi_status acpi_ns_initialize_objects (void ); -acpi_status acpi_ns_initialize_devices (void ); +acpi_status acpi_ns_initialize_devices (u32 flags);/* * nsload - Namespace loading
drivers/acpi/acpica/nsinit.c +81 -54
169 lines not on this page · raw message
drivers/acpi/acpica/utxfinit.c +4 -23
@@ -301,33 +301,14 @@ acpi_status __init acpi_initialize_objects(u32 flags) } } -/* -* Run all _REG methods -* -* Note: Any objects accessed by the _REG methods will be automatically -* initialized, even if they contain executable AML (see the call to -* acpi_ns_initialize_objects below). -*/ acpi_gbl_reg_methods_enabled = TRUE; -if (!(flags &ACPI_NO_ADDRESS_SPACE_INIT )) { -ACPI_DEBUG_PRINT ((ACPI_DB_EXEC , -"[Init] Executing _REG OpRegion methods\n" )); - - status =acpi_ev_initialize_op_regions (); -if (ACPI_FAILURE (status)) { -return_ACPI_STATUS (status); - } - }/* -* Initialize all device objects in the namespace. This runs the device -* _STA and _INI methods. +* Initialize all device/region objects in the namespace. This runs +* the device _STA and _INI methods and region _REG methods. */ -if (!(flags &ACPI_NO_DEVICE_INIT )) { -ACPI_DEBUG_PRINT ((ACPI_DB_EXEC , -"[Init] Initializing ACPI Devices\n" )); - - status =acpi_ns_initialize_devices (); +if (!(flags & (ACPI_NO_DEVICE_INIT |ACPI_NO_ADDRESS_SPACE_INIT ))) { + status =acpi_ns_initialize_devices (flags);if (ACPI_FAILURE (status)) {return_ACPI_STATUS (status); }
Signature · 1 line
1.7.10
PATCH 6/14 ACPI 2.0 / AML: Rename acpi_gbl_reg_methods_enabled to acpi_gbl_namespace_initialized
In reply to Lv ZhengACPICA commit 4be3b82cf45d324366ea8567102d5108c5ef47cb
The global variable actually means the availability of the namespace, and control methods evaluations should happen after namespace readiness. Thus this patch renames the global variable to reflect this logic. Lv Zheng.
--- drivers/acpi/acpica/acglobal.h | 2 +- drivers/acpi/acpica/evregion.c | 2 +- drivers/acpi/acpica/utxfinit.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
drivers/acpi/acpica/acglobal.h +1 -1
@@ -165,7 +165,7 @@ ACPI_GLOBAL(u8, acpi_gbl_next_owner_id_offset);/* Initialization sequencing */ -ACPI_INIT_GLOBAL (u8, acpi_gbl_reg_methods_enabled, FALSE); +ACPI_INIT_GLOBAL (u8, acpi_gbl_namespace_initialized, FALSE);/* Misc */
drivers/acpi/acpica/evregion.c +1 -1
@@ -600,7 +600,7 @@ acpi_ev_execute_reg_method(union acpi_operand_object *region_obj, u32 function)if (region_obj2->extra.method_REG ==NULL || region_obj->region.handler ==NULL || - !acpi_gbl_reg_methods_enabled) { + !acpi_gbl_namespace_initialized) {return_ACPI_STATUS (AE_OK ); }
drivers/acpi/acpica/utxfinit.c +1 -1
@@ -301,7 +301,7 @@ acpi_status __init acpi_initialize_objects(u32 flags) } } - acpi_gbl_reg_methods_enabled = TRUE; + acpi_gbl_namespace_initialized = TRUE;/* * Initialize all device/region objects in the namespace. This runs
Signature · 1 line
1.7.10
PATCH 7/14 ACPICA: Events: Fix an issue that _REG association can happen before namespace is initialized
In reply to Lv ZhengCurrent code flow cannot ensure _REG association can happen after the namespace is initialized, so we move _REG association to where _REG was about to run to fix this issue.
This issue is detected when acpi_ev_initialize_region() is invoked during the table loading. And this is one of the most important the root cause why ACPICA table loading is split into 2 load passes. Lv Zheng.
--- drivers/acpi/acpica/acevents.h | 2 -- drivers/acpi/acpica/evregion.c | 71 ++++++++++++++-------------------------- drivers/acpi/acpica/evrgnini.c | 1 - 3 files changed, 24 insertions(+), 50 deletions(-)
drivers/acpi/acpica/acevents.h +0 -2
@@ -198,8 +198,6 @@ void acpi_ev_detach_region(union acpi_operand_object *region_obj, u8 acpi_ns_is_locked); -void acpi_ev_associate_reg_method (union acpi_operand_object *region_obj); -void acpi_ev_execute_reg_methods (struct acpi_namespace_node *node,acpi_adr_space_type space_id,u32 function);
drivers/acpi/acpica/evregion.c +24 -47
105 lines not on this page · raw message
drivers/acpi/acpica/evrgnini.c +0 -1
@@ -518,7 +518,6 @@ acpi_ev_initialize_region(union acpi_operand_object *region_obj,return_ACPI_STATUS (AE_OK ); } -acpi_ev_associate_reg_method (region_obj); region_obj->common.flags |=AOPOBJ_OBJECT_INITIALIZED ; node = region_obj->region.node->parent;
Signature · 1 line
1.7.10
PATCH 8/14 ACPI 2.0 / ECDT: Split EC_FLAGS_HANDLERS_INSTALLED
In reply to Lv ZhengThis patch splits EC_FLAGS_HANDLERS_INSTALLED so that address space handler can be installed when it is not possible to install GPE handler during early stage.
This patch also tunes address space handler installation, making it happening earlier than GPE handler installation for the same purpose.
Since acpi_ec_start()/acpi_ec_stop() will be entered multiple times after applying this change, it is also required to protect acpi_enable_gpe()/ acpi_disable_gpe() invocations.
--- drivers/acpi/ec.c | 96 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 41 deletions(-)
drivers/acpi/ec.c +55 -41
@@ -105,8 +105,8 @@ enum ec_command {enum {EC_FLAGS_QUERY_PENDING ,/* Query is pending */ EC_FLAGS_QUERY_GUARDING ,/* Guard for SCI_EVT check */ -EC_FLAGS_HANDLERS_INSTALLED ,/* Handlers for GPE and -* OpReg are installed */ +EC_FLAGS_GPE_HANDLER_INSTALLED ,/* GPE handler installed */ +EC_FLAGS_EC_HANDLER_INSTALLED ,/* OpReg handler installed */ EC_FLAGS_STARTED ,/* Driver is started */ EC_FLAGS_STOPPED ,/* Driver is stopped */ EC_FLAGS_COMMAND_STORM ,/* GPE storms occurred to the
@@ -367,7 +367,8 @@ static inline void acpi_ec_clear_gpe(struct acpi_ec *ec)static void acpi_ec_submit_request (struct acpi_ec *ec) { ec->reference_count++; -if (ec->reference_count ==1 ) +if (test_bit (EC_FLAGS_GPE_HANDLER_INSTALLED , &ec->flags) && + ec->reference_count ==1 )acpi_ec_enable_gpe (ec, true); }
@@ -376,7 +377,8 @@ static void acpi_ec_complete_request(struct acpi_ec *ec)bool flushed = false; ec->reference_count--; -if (ec->reference_count ==0 ) +if (test_bit (EC_FLAGS_GPE_HANDLER_INSTALLED , &ec->flags) && + ec->reference_count ==0 )acpi_ec_disable_gpe (ec, true); flushed =acpi_ec_flushed (ec);if (flushed)
@@ -1287,52 +1289,64 @@ static int ec_install_handlers(struct acpi_ec *ec) {acpi_status status; -if (test_bit (EC_FLAGS_HANDLERS_INSTALLED , &ec->flags)) -return 0 ; - status =acpi_install_gpe_raw_handler (NULL , ec->gpe, -ACPI_GPE_EDGE_TRIGGERED , - &acpi_ec_gpe_handler, ec); -if (ACPI_FAILURE (status)) -return -ENODEV ; -acpi_ec_start (ec, false); - status =acpi_install_address_space_handler (ec->handle, -ACPI_ADR_SPACE_EC , - &acpi_ec_space_handler, -NULL , ec); -if (ACPI_FAILURE (status)) { -if (status ==AE_NOT_FOUND ) { -/* -* Maybe OS fails in evaluating the _REG object. -* The AE_NOT_FOUND error will be ignored and OS -* continue to initialize EC. -*/ -pr_err ("Fail in evaluating the _REG object" -" of EC device. Broken bios is suspected.\n" ); - }else { -acpi_ec_stop (ec, false); -acpi_remove_gpe_handler (NULL , ec->gpe, - &acpi_ec_gpe_handler); -return -ENODEV ; + +if (!test_bit (EC_FLAGS_EC_HANDLER_INSTALLED , &ec->flags)) { + status =acpi_install_address_space_handler (ec->handle, +ACPI_ADR_SPACE_EC , + &acpi_ec_space_handler, +NULL , ec); +if (ACPI_FAILURE (status)) { +if (status ==AE_NOT_FOUND ) { +/* +* Maybe OS fails in evaluating the _REG +* object. The AE_NOT_FOUND error will be +* ignored and OS * continue to initialize +* EC. +*/ +pr_err ("Fail in evaluating the _REG object" +" of EC device. Broken bios is suspected.\n" ); + }else { +acpi_ec_stop (ec, false); +return -ENODEV ; + } + } +set_bit (EC_FLAGS_EC_HANDLER_INSTALLED , &ec->flags); + } + +if (!test_bit (EC_FLAGS_GPE_HANDLER_INSTALLED , &ec->flags)) { + status =acpi_install_gpe_raw_handler (NULL , ec->gpe, +ACPI_GPE_EDGE_TRIGGERED , + &acpi_ec_gpe_handler, ec); +/* This is not fatal as we can poll EC events */ +if (ACPI_SUCCESS (status)) { +set_bit (EC_FLAGS_GPE_HANDLER_INSTALLED , &ec->flags); +if (test_bit (EC_FLAGS_STARTED , &ec->flags) && + ec->reference_count >=1 ) +acpi_ec_enable_gpe (ec, true); } } -set_bit (EC_FLAGS_HANDLERS_INSTALLED , &ec->flags);return 0 ; }static void ec_remove_handlers (struct acpi_ec *ec) { -if (!test_bit (EC_FLAGS_HANDLERS_INSTALLED , &ec->flags)) -return ;acpi_ec_stop (ec, false); -if (ACPI_FAILURE (acpi_remove_address_space_handler (ec->handle, -ACPI_ADR_SPACE_EC , &acpi_ec_space_handler))) -pr_err ("failed to remove space handler\n" ); -if (ACPI_FAILURE (acpi_remove_gpe_handler (NULL , ec->gpe, - &acpi_ec_gpe_handler))) -pr_err ("failed to remove gpe handler\n" ); -clear_bit (EC_FLAGS_HANDLERS_INSTALLED , &ec->flags); + +if (test_bit (EC_FLAGS_EC_HANDLER_INSTALLED , &ec->flags)) { +if (ACPI_FAILURE (acpi_remove_address_space_handler (ec->handle, +ACPI_ADR_SPACE_EC , &acpi_ec_space_handler))) +pr_err ("failed to remove space handler\n" ); +clear_bit (EC_FLAGS_EC_HANDLER_INSTALLED , &ec->flags); + } + +if (test_bit (EC_FLAGS_GPE_HANDLER_INSTALLED , &ec->flags)) { +if (ACPI_FAILURE (acpi_remove_gpe_handler (NULL , ec->gpe, + &acpi_ec_gpe_handler))) +pr_err ("failed to remove gpe handler\n" ); +clear_bit (EC_FLAGS_GPE_HANDLER_INSTALLED , &ec->flags); + } }static int acpi_ec_add (struct acpi_device *device)
@@ -1434,7 +1448,7 @@ ec_parse_io_ports(struct acpi_resource *resource, void *context)int __init acpi_boot_ec_enable (void ) { -if (!boot_ec ||test_bit (EC_FLAGS_HANDLERS_INSTALLED , &boot_ec->flags)) +if (!boot_ec)return 0 ;if (!ec_install_handlers (boot_ec)) { first_ec = boot_ec;
Signature · 1 line
1.7.10
PATCH 9/14 ACPI 2.0 / ECDT: Remove early namespace reference from EC
In reply to Lv ZhengAll operation region accesses are allowed by AML interpreter when AML is executed, so actually BIOSen are responsible to avoid the operation region accesses in AML before OSPM has prepared an operation region driver. This is done via _REG control method. So AML code normally sets a global named object REGC to 1 when _REG(3, 1) is evaluated.
Then what is ECDT? Quoting from ACPI spec 6.0, 5.2.15 Embedded Controller Boot Resources Table (ECDT):
"The presence of this table allows OSPM to provide Embedded Controller
operation region space access before the namespace has been evaluated."
Spec also suggests a compatible mean to indicate the early EC access availability:
Device (EC)
{
Name (REGC, Ones)
Method (_REG, 2)
{
If (LEqual (Arg0, 3))
{
Store (Arg1, REGC)
}
}
Method (ECAV)
{
If (LEqual (REGC, Ones))
{
If (LGreaterEqual (_REV, 2))
{
Return (One)
}
Else
{
Return (Zero)
}
}
Else
{
Return (REGC)
}
} }
In this way, it allows EC accesses to happen before EC._REG(3, 1) is invoked.
But ECAV is not the only way practical BIOSen using to indicate the early EC access availibility, the known variations include:
1. Setting REGC to One in \_SB._INI when _REV >= 2. Since \_SB._INI is the first control method evaluated by OSPM during the enumeration, this allows EC accesses to happen for the entire enumeration process before the namespace EC is enumerated.
2. Initialize REGC to One by default, this even allows EC accesses to happen during the table loading.
Linux is now broken around ECDT support during the long term bug fixing work because it has merged many wrong ECDT bug fixes (see details below). Linux currently uses namespace EC's settings instead of ECDT settings when ECDT is detected. This apparently will result in namespace walk and _CRS/_GPE/_REG evaluations. Such stuffs could only happen after namespace is ready, while ECDT is purposely to be used before namespace is ready.
The wrong bug fixing story is:
1. Link 1:
At Linux ACPI early stages, "no _Lxx/_Exx/_Qxx evaluation can happen before the namespace is ready" are not ensured by ACPICA core and Linux. This is currently ensured by deferred enabling of GPE and defered registering of EC query methods (acpi_ec_register_query_methods).
2. Link 2:
Reporters reported buggy ECDTs, expecting quirks for the platform. Originally, the quirk is simple, only doing things with ECDT. Bug 9399 and 12461 are platforms (Asus L4R, Asus M6R, MSI MS-171F) reported to have wrong ECDT IO port addresses, the port addresses are reversed.
Bug 11880 is a platform (Asus X50GL) reported to have 0 valued port addresses, we can see that all EC accesses are protected by ECAV on this platform, so actually no early EC accesses is required by this platform.
3. Link 3:
But when the bug fixing developer was requested to provide a handy and non-quirk bug fix, he tried to use correct EC settings from namespace and broke the spec purpose. We can even see that the developer was suffered from many regrssions. One interesting one is 14086, where the actual root cause obviously should be: _REG is evaluated too early. But unfortunately, the bug is fixed in a totally wrong way.
So everything goes wrong from these commits:
Commit: c6cb0e878446c79f42e7833d7bb69ed6bfbb381f
Subject: ACPI: EC: Don't trust ECDT tables from ASUS
Commit: a5032bfdd9c80e0231a6324661e123818eb46ecd
Subject: ACPI: EC: Always parse EC device
This patch reverts Linux behavior to simple ECDT quirk support in order to stop early _CRS/_GPE/_REG evaluations.
For Bug 9399, 12461, since it is reported that the platforms require early EC accesses, this patch restores the simple ECDT quirks for them. For Bug 11880, since it is not reported that the platform requires early EC accesses and its ACPI tables contain correct ECAV, we choose an ECDT enumeration failure for this platform.
Link 1: https://bugzilla.kernel.org/show_bug.cgi?id=9916
http://bugzilla.kernel.org/show_bug.cgi?id=10100 https://lkml.org/lkml/2008/2/25/282 Link 2: https://bugzilla.kernel.org/show_bug.cgi?id=9399 https://bugzilla.kernel.org/show_bug.cgi?id=12461 https://bugzilla.kernel.org/show_bug.cgi?id=11880 Link 3: https://bugzilla.kernel.org/show_bug.cgi?id=11884 https://bugzilla.kernel.org/show_bug.cgi?id=14081 https://bugzilla.kernel.org/show_bug.cgi?id=14086 https://bugzilla.kernel.org/show_bug.cgi?id=14446
--- drivers/acpi/ec.c | 145 ++++++++++++++++++++--------------------------------- 1 file changed, 54 insertions(+), 91 deletions(-)
drivers/acpi/ec.c +54 -91
@@ -175,10 +175,9 @@ static void acpi_ec_event_processor(struct work_struct *work);struct acpi_ec *boot_ec, *first_ec;EXPORT_SYMBOL (first_ec); -static int EC_FLAGS_VALIDATE_ECDT ;/* ASUStec ECDTs need to be validated */ -static int EC_FLAGS_SKIP_DSDT_SCAN ;/* Not all BIOS survive early DSDT scan */ static int EC_FLAGS_CLEAR_ON_RESUME ;/* Needs acpi_ec_clear() on boot/resume */ static int EC_FLAGS_QUERY_HANDSHAKE ;/* Needs QR_EC issued when SCI_EVT set */ +static int EC_FLAGS_CORRECT_ECDT ;/* Needs ECDT port address correction */ /* -------------------------------------------------------------------------- * Logging/Debugging
@@ -1358,11 +1357,12 @@ static int acpi_ec_add(struct acpi_device *device)strcpy (acpi_device_class (device),ACPI_EC_CLASS );/* Check for boot EC */ -if (boot_ec && - (boot_ec->handle == device->handle || - boot_ec->handle ==ACPI_ROOT_OBJECT )) { +if (boot_ec) { ec = boot_ec; boot_ec =NULL ; +ec_remove_handlers (ec); +if (first_ec == ec) + first_ec =NULL ; }else { ec =make_acpi_ec ();if (!ec)
@@ -1462,20 +1462,6 @@ static const struct acpi_device_id ec_device_ids[] = { {"", 0}, }; -/* Some BIOS do not survive early DSDT scan, skip it */ -static int ec_skip_dsdt_scan (const struct dmi_system_id *id) -{ -EC_FLAGS_SKIP_DSDT_SCAN =1 ; -return 0 ; -} - -/* ASUStek often supplies us with broken ECDT, validate it */ -static int ec_validate_ecdt (const struct dmi_system_id *id) -{ -EC_FLAGS_VALIDATE_ECDT =1 ; -return 0 ; -} -#if 0 /* * Some EC firmware variations refuses to respond QR_EC when SCI_EVT is not
@@ -1517,30 +1503,29 @@ static int ec_clear_on_resume(const struct dmi_system_id *id)return 0 ; } +static int ec_correct_ecdt (const struct dmi_system_id *id) +{ +pr_debug ("Detected system needing ECDT address correction.\n" ); +EC_FLAGS_CORRECT_ECDT =1 ; +return 0 ; +} +static struct dmi_system_id ec_dmi_table[]__initdata = { { - ec_skip_dsdt_scan,"Compal JFL92" , { -DMI_MATCH (DMI_BIOS_VENDOR ,"COMPAL" ), -DMI_MATCH (DMI_BOARD_NAME ,"JFL92" ) },NULL }, + ec_correct_ecdt,"Asus L4R" , { +DMI_MATCH (DMI_BIOS_VERSION ,"1008.006" ), +DMI_MATCH (DMI_PRODUCT_NAME ,"L4R" ), +DMI_MATCH (DMI_BOARD_NAME ,"L4R" ) },NULL }, + { + ec_correct_ecdt,"Asus M6R" , { +DMI_MATCH (DMI_BIOS_VERSION ,"0207" ), +DMI_MATCH (DMI_PRODUCT_NAME ,"M6R" ), +DMI_MATCH (DMI_BOARD_NAME ,"M6R" ) },NULL }, { - ec_validate_ecdt,"MSI MS-171F" , { + ec_correct_ecdt,"MSI MS-171F" , {DMI_MATCH (DMI_SYS_VENDOR ,"Micro-Star" ),DMI_MATCH (DMI_PRODUCT_NAME ,"MS-171F" ),},NULL }, { - ec_validate_ecdt,"ASUS hardware" , { -DMI_MATCH (DMI_BIOS_VENDOR ,"ASUS" ) },NULL }, - { - ec_validate_ecdt,"ASUS hardware" , { -DMI_MATCH (DMI_BOARD_VENDOR ,"ASUSTeK Computer Inc." ) },NULL }, - { - ec_skip_dsdt_scan,"HP Folio 13" , { -DMI_MATCH (DMI_SYS_VENDOR ,"Hewlett-Packard" ), -DMI_MATCH (DMI_PRODUCT_NAME ,"HP Folio 13" ),},NULL }, - { - ec_validate_ecdt,"ASUS hardware" , { -DMI_MATCH (DMI_SYS_VENDOR ,"ASUSTek Computer Inc." ), -DMI_MATCH (DMI_PRODUCT_NAME ,"L4R" ),},NULL }, - { ec_clear_on_resume,"Samsung hardware" , {DMI_MATCH (DMI_SYS_VENDOR ,"SAMSUNG ELECTRONICS CO., LTD." )},NULL }, {},
@@ -1548,8 +1533,8 @@ static struct dmi_system_id ec_dmi_table[] __initdata = {int __init acpi_ec_ecdt_probe (void ) { +int ret =0 ;acpi_status status; -struct acpi_ec *saved_ec =NULL ;struct acpi_table_ecdt *ecdt_ptr; boot_ec =make_acpi_ec ();
@@ -1561,67 +1546,45 @@ int __init acpi_ec_ecdt_probe(void)dmi_check_system (ec_dmi_table); status =acpi_get_table (ACPI_SIG_ECDT ,1 , (struct acpi_table_header **)&ecdt_ptr); -if (ACPI_SUCCESS (status)) { -pr_info ("EC description table is found, configuring boot EC\n" ); - boot_ec->command_addr = ecdt_ptr->control.address; - boot_ec->data_addr = ecdt_ptr->data.address; - boot_ec->gpe = ecdt_ptr->gpe; - boot_ec->handle =ACPI_ROOT_OBJECT ; -acpi_get_handle (ACPI_ROOT_OBJECT , ecdt_ptr->id, - &boot_ec->handle); -/* Don't trust ECDT, which comes from ASUSTek */ -if (!EC_FLAGS_VALIDATE_ECDT ) - goto install; - saved_ec =kmemdup (boot_ec,sizeof (struct acpi_ec ),GFP_KERNEL ); -if (!saved_ec) -return -ENOMEM ; -/* fall through */ +if (ACPI_FAILURE (status)) { + ret = -ENODEV ; + goto error; } -if (EC_FLAGS_SKIP_DSDT_SCAN ) { -kfree (saved_ec); -return -ENODEV ; +if (!ecdt_ptr->control.address || !ecdt_ptr->data.address) { +/* +* Asus X50GL: +* https://bugzilla.kernel.org/show_bug.cgi?id=11880 +*/ + ret = -ENODEV ; + goto error; } -/* This workaround is needed only on some broken machines, -* which require early EC, but fail to provide ECDT */ -pr_debug ("Look up EC in DSDT\n" ); - status =acpi_get_devices (ec_device_ids[0 ].id, ec_parse_device, - boot_ec,NULL ); -/* Check that acpi_get_devices actually find something */ -if (ACPI_FAILURE (status) || !boot_ec->handle) - goto error; -if (saved_ec) { -/* try to find good ECDT from ASUSTek */ -if (saved_ec->command_addr != boot_ec->command_addr || - saved_ec->data_addr != boot_ec->data_addr || - saved_ec->gpe != boot_ec->gpe || - saved_ec->handle != boot_ec->handle) -pr_info ("ASUSTek keeps feeding us with broken " -"ECDT tables, which are very hard to workaround. " -"Trying to use DSDT EC info instead. Please send " -"output of acpidump to [email protected]\n" ); -kfree (saved_ec); - saved_ec =NULL ; +pr_info ("EC description table is found, configuring boot EC\n" ); +if (EC_FLAGS_CORRECT_ECDT ) { +/* +* Asus L4R, Asus M6R +* https://bugzilla.kernel.org/show_bug.cgi?id=9399 +* MSI MS-171F +* https://bugzilla.kernel.org/show_bug.cgi?id=12461 +*/ + boot_ec->command_addr = ecdt_ptr->data.address; + boot_ec->data_addr = ecdt_ptr->control.address; }else { -/* We really need to limit this workaround, the only ASUS, -* which needs it, has fake EC._INI method, so use it as flag. -* Keep boot_ec struct as it will be needed soon. -*/ -if (!dmi_name_in_vendors ("ASUS" ) || - !acpi_has_method (boot_ec->handle,"_INI" )) -return -ENODEV ; + boot_ec->command_addr = ecdt_ptr->control.address; + boot_ec->data_addr = ecdt_ptr->data.address; } -install: -if (!ec_install_handlers (boot_ec)) { + boot_ec->gpe = ecdt_ptr->gpe; + boot_ec->handle =ACPI_ROOT_OBJECT ; + ret =ec_install_handlers (boot_ec); +if (!ret) first_ec = boot_ec; -return 0 ; - } error: -kfree (boot_ec); -kfree (saved_ec); - boot_ec =NULL ; -return -ENODEV ; +if (ret) { +kfree (boot_ec); + boot_ec =NULL ; + } +return ret; }static int param_set_event_clearing (const char *val,struct kernel_param *kp)
Signature · 1 line
1.7.10
PATCH 10/14 ACPI 2.0 / ECDT: Enable correct ECDT initialization order
In reply to Lv ZhengWith wrong ECDT fixes reversed, it is able to put ECDT probing before acpi_enable_subsystem().
But the ultimate purpose of ECDT re-enabling is to put the ECDT probing before the namespace initialization (acpi_load_tables()). This patch achieves this with protections so that we can enable it later when all necessary corrections are upstreamed.
--- drivers/acpi/bus.c | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-)
drivers/acpi/bus.c +25 -14
@@ -911,11 +911,13 @@ void __init acpi_early_init(void) goto error0; } - status =acpi_load_tables (); -if (ACPI_FAILURE (status)) { - printk(KERN_ERR PREFIX - "Unable to load the System Description Tables\n"); - goto error0; +if (acpi_gbl_group_module_level_code) { + status =acpi_load_tables (); +if (ACPI_FAILURE (status)) { + printk(KERN_ERR PREFIX + "Unable to load the System Description Tables\n"); + goto error0; + } }#ifdef CONFIG_X86
@@ -981,17 +983,10 @@ static int __init acpi_bus_init(void)acpi_os_initialize1 (); - status =acpi_enable_subsystem (ACPI_NO_ACPI_ENABLE ); -if (ACPI_FAILURE (status)) { - printk(KERN_ERR PREFIX - "Unable to start the ACPI Interpreter\n"); - goto error1; - } -/* * ACPI 2.0 requires the EC driver to be loaded and work before -* the EC device is found in the namespace (i.e. before acpi_initialize_objects() -* is called). +* the EC device is found in the namespace (i.e. before +* acpi_load_tables() is called). * * This is accomplished by looking for the ECDT table, and getting * the EC parameters out of that.
@@ -999,6 +994,22 @@ static int __init acpi_bus_init(void) status =acpi_ec_ecdt_probe ();/* Ignore result. Not having an ECDT is not fatal. */ +if (!acpi_gbl_group_module_level_code) { + status =acpi_load_tables (); +if (ACPI_FAILURE (status)) { + printk(KERN_ERR PREFIX + "Unable to load the System Description Tables\n"); + goto error1; + } + } + + status =acpi_enable_subsystem (ACPI_NO_ACPI_ENABLE ); +if (ACPI_FAILURE (status)) { + printk(KERN_ERR PREFIX + "Unable to start the ACPI Interpreter\n"); + goto error1; + } + status =acpi_initialize_objects (ACPI_FULL_INITIALIZATION );if (ACPI_FAILURE (status)) { printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n");
Signature · 1 line
1.7.10
PATCH 11/14 ACPI 2.0 / AML: Improve module level execution by moving the If/Else/While execution to per-table basis
In reply to Lv ZhengThis experiment moves module level If/Else/While executions to per-table basis.
If regressions are found against the enabling of this improvement, this patch is the only one should get bisected. Please report the regressions to the kernel bugzilla for further root causing.
--- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/acpi/acpixf.h +1 -1
@@ -192,7 +192,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);/* * Optionally support group module level code. */ -ACPI_INIT_GLOBAL (u8, acpi_gbl_group_module_level_code, TRUE); +ACPI_INIT_GLOBAL (u8, acpi_gbl_group_module_level_code, FALSE);/* * Optionally use 32-bit FADT addresses if and when there is a conflict
Signature · 1 line
1.7.10
PATCH 12/14 ACPI 2.0 / AML: Add TermList parsing support for table loading
In reply to Lv ZhengIt is proven that not only If/Else/While opcodes, all opcodes can be executed at the module level, including operation region accesses. BIOSen developers are responsible to protect operation region accesses when the operation region drivers are not loaded by the OSPMs (their availabilities are indicated via _REG(CONNECT)).
So in fact, all spec allowed early operation region accesses should be possible at the module level. This includes default operation regions (SystemMemory, SystemIo, PciConfig) and early operation regions (EmbeddedControl reported via ECDT).
And the above facts indeed reflect the spec words around ACPI definition block tables (DSDT/SSDT/...), the table is defined by the AML specification in BNF style as AMLCode:
AMLCode := DefBlockHeader TermList
Where DefBlockHeader is the header of the DSDT/SSDT table. So table loading should be no difference than the control method evaluations as the body of the control method is also defined by the AML specification as TermList:
DefMethod := MethodOp PkgLength NameString MethodFlags TermList
The only difference is: after evaluating control method, created named objects can be freed due to no reference, while named objects created by loading the table should only be freed after the table is unloaded.
So this patch follows the spec and the de-facto standard behavior, enables TermList evaluations when the table is loaded. Lv Zheng.
--- drivers/acpi/acpica/acnamesp.h | 3 + drivers/acpi/acpica/acparser.h | 2 + drivers/acpi/acpica/exconfig.c | 7 +- drivers/acpi/acpica/nsload.c | 3 +- drivers/acpi/acpica/nsparse.c | 163 ++++++++++++++++++++++++++++++++-------- drivers/acpi/acpica/psparse.c | 4 +- drivers/acpi/acpica/psxface.c | 73 ++++++++++++++++++ drivers/acpi/acpica/tbxfload.c | 3 +- drivers/acpi/acpica/utxfinit.c | 6 +- include/acpi/acpixf.h | 7 ++ 10 files changed, 233 insertions(+), 38 deletions(-)
drivers/acpi/acpica/acnamesp.h +3 -0
@@ -130,6 +130,9 @@ acpi_status acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node);acpi_status +acpi_ns_execute_table (u32 table_index,struct acpi_namespace_node *start_node); + +acpi_status acpi_ns_one_complete_parse (u32 pass_number,u32 table_index,struct acpi_namespace_node *start_node);
drivers/acpi/acpica/acparser.h +2 -0
@@ -78,6 +78,8 @@ extern const u8 acpi_gbl_long_op_index[];*/ acpi_status acpi_ps_execute_method (struct acpi_evaluate_info *info); +acpi_status acpi_ps_execute_table (struct acpi_evaluate_info *info); +/* * psargs - Parse AML opcode arguments */
drivers/acpi/acpica/exconfig.c +5 -2
@@ -108,8 +108,10 @@ acpi_ex_add_table(u32 table_index,/* Add the table to the namespace */ +acpi_ex_exit_interpreter (); status =acpi_ns_load_table (table_index, parent_node);if (ACPI_FAILURE (status)) { +acpi_ex_enter_interpreter ();acpi_ut_remove_reference (obj_desc); *ddb_handle =NULL ;return_ACPI_STATUS (status);
@@ -117,8 +119,9 @@ acpi_ex_add_table(u32 table_index,/* Execute any module-level code that was found in the table */ -acpi_ex_exit_interpreter (); -acpi_ns_exec_module_code_list (); +if (acpi_gbl_parse_table_as_term_list) { +acpi_ns_exec_module_code_list (); + }acpi_ex_enter_interpreter ();/*
drivers/acpi/acpica/nsload.c +2 -1
@@ -162,7 +162,8 @@ unlock:* other ACPI implementations. Optionally, the execution can be deferred * until later, see acpi_initialize_objects. */ -if (!acpi_gbl_group_module_level_code) { +if (!acpi_gbl_parse_table_as_term_list + && !acpi_gbl_group_module_level_code) {acpi_ns_exec_module_code_list (); }
drivers/acpi/acpica/nsparse.c +132 -31
188 lines not on this page · raw message
drivers/acpi/acpica/psparse.c +3 -1
@@ -571,7 +571,9 @@ acpi_status acpi_ps_parse_aml(struct acpi_walk_state *walk_state)* cleanup to do */ if (((walk_state->parse_flags &ACPI_PARSE_MODE_MASK ) == -ACPI_PARSE_EXECUTE ) || (ACPI_FAILURE (status))) { +ACPI_PARSE_EXECUTE && + !(walk_state->parse_flags &ACPI_PARSE_MODULE_LEVEL )) || + (ACPI_FAILURE (status))) {acpi_ds_terminate_control_method (walk_state-> method_desc, walk_state);
drivers/acpi/acpica/psxface.c +73 -0
79 lines not on this page · raw message
drivers/acpi/acpica/tbxfload.c +2 -1
@@ -81,7 +81,8 @@ acpi_status __init acpi_load_tables(void)* between acpi_initialize_subsystem() and acpi_load_tables() to use * their customized default region handlers. */ -if (!acpi_gbl_group_module_level_code) { +if (acpi_gbl_parse_table_as_term_list + || !acpi_gbl_group_module_level_code) { status =acpi_ev_install_region_handlers ();if (ACPI_FAILURE (status) && status !=AE_ALREADY_EXISTS ) { ACPI_EXCEPTION((AE_INFO, status,
drivers/acpi/acpica/utxfinit.c +4 -2
@@ -163,7 +163,8 @@ acpi_status __init acpi_enable_subsystem(u32 flags)* installed before any AML code can be executed, especially any * module-level code (11/2015). */ -if (acpi_gbl_group_module_level_code) { +if (!acpi_gbl_parse_table_as_term_list + && acpi_gbl_group_module_level_code) { status =acpi_ev_install_region_handlers ();if (ACPI_FAILURE (status)) { ACPI_EXCEPTION((AE_INFO, status,
@@ -282,7 +283,8 @@ acpi_status __init acpi_initialize_objects(u32 flags)* all of the tables have been loaded. It is a legacy option and is * not compatible with other ACPI implementations. See acpi_ns_load_table. */ -if (acpi_gbl_group_module_level_code) { +if (!acpi_gbl_parse_table_as_term_list + && acpi_gbl_group_module_level_code) {acpi_ns_exec_module_code_list (); }
include/acpi/acpixf.h +7 -0
@@ -195,6 +195,13 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_do_not_use_xsdt, FALSE);ACPI_INIT_GLOBAL (u8, acpi_gbl_group_module_level_code, FALSE);/* +* Optionally support module level code by parsing the entire table as +* a term_list. +* For disassembler, this should be FALSE. +*/ +ACPI_INIT_GLOBAL (u8, acpi_gbl_parse_table_as_term_list, FALSE); + +/* * Optionally use 32-bit FADT addresses if and when there is a conflict * (address mismatch) between the 32-bit and 64-bit versions of the * address. Although ACPICA adheres to the ACPI specification which
Signature · 1 line
1.7.10
PATCH 13/14 ACPI 2.0 / AML: Enable correct ACPI subsystem initialization order for new table loading mode
In reply to Lv ZhengThis patch enables the following initialization order for the new table loading mode (which is enabled by setting acpi_gbl_parse_table_as_term_list to TRUE):
1. Install default region handlers (SystemMemory, SystemIo, PciConfig,
EmbeddedControl via ECDT) without evaluating _REG;
2. Load the table and execute the module level AML opcodes instantly.
--- drivers/acpi/bus.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
drivers/acpi/bus.c +4 -2
@@ -911,7 +911,8 @@ void __init acpi_early_init(void) goto error0; } -if (acpi_gbl_group_module_level_code) { +if (!acpi_gbl_parse_table_as_term_list && + acpi_gbl_group_module_level_code) { status =acpi_load_tables ();if (ACPI_FAILURE (status)) { printk(KERN_ERR PREFIX
@@ -994,7 +995,8 @@ static int __init acpi_bus_init(void) status =acpi_ec_ecdt_probe ();/* Ignore result. Not having an ECDT is not fatal. */ -if (!acpi_gbl_group_module_level_code) { +if (acpi_gbl_parse_table_as_term_list || + !acpi_gbl_group_module_level_code) { status =acpi_load_tables ();if (ACPI_FAILURE (status)) { printk(KERN_ERR PREFIX
Signature · 1 line
1.7.10
PATCH 14/14 ACPI 2.0 / AML: Fix module level execution by correctly parsing table as TermList
In reply to Lv ZhengThis experiment follows de-facto standard behavior, parsing entire table as a single TermList, so that all module level executions are possible during the table loading.
If regressions are found against the enabling of this fix, this patch is the only one should get bisected. Please report the regressions to the kernel bugzilla for further root causing.
--- include/acpi/acpixf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/acpi/acpixf.h +1 -1
@@ -199,7 +199,7 @@ ACPI_INIT_GLOBAL(u8, acpi_gbl_group_module_level_code, FALSE);* a term_list. * For disassembler, this should be FALSE. */ -ACPI_INIT_GLOBAL (u8, acpi_gbl_parse_table_as_term_list, FALSE); +ACPI_INIT_GLOBAL (u8, acpi_gbl_parse_table_as_term_list, TRUE);/* * Optionally use 32-bit FADT addresses if and when there is a conflict
Signature · 1 line
1.7.10
Hi, Rafael
Quoted 4 lines
From: Zheng, Lv
Subject: [PATCH 00/14] ACPI 2.0: Enable TermList interpretion and early opeartion region accesses for table loading
<skip>
Quoted 3 lines
PATCH 01-02: The initrd installation testing facility. PATCH 03-07: Entropy reduction stuff in ACPICA.
PATCH 03-06 were just sent in ACPICA 20160212 release series.
So this patchset need to be re-arranged and sent again after the 20160212 release cycle.
But PATCH 01-02 looks not so closely related to this patchset. Maybe you can help to check to see if they are upstreamable. If they are merged, then I needn't send them again in v2 series. Thanks in advance.
Best regards
-Lv
Quoted 27 lines
PATCH 08-10: Entropy reduction stuff in EC driver and ACPI subsystem's
initialization order.PATCH 12: The first step enabling. PATCH 13: The code to facilitate AML 2.0 table loading. PATCH 14: The second step enabling.Lv Zheng (14):
ACPI / OSL: Cleanup initrd table override code
ACPI / OSL: Add support to install tables via initrd
ACPI 2.0 / AML: Make default region accessible during the table load ACPI 2.0 / AML: Tune_REGevaluations order in the initialization
steps
ACPI 2.0 / AML: Ensure \_SB._INI executed before any_REGACPI 2.0 / AML: Renameacpi_gbl_reg_methods_enabledto
acpi_gbl_namespace_initialized
ACPICA: Events: Fix an issue that_REGassociation can happen before
namespace is initialized
ACPI 2.0 / ECDT: SplitEC_FLAGS_HANDLERS_INSTALLED
ACPI 2.0 / ECDT: Remove early namespace reference from EC ACPI 2.0 / ECDT: Enable correct ECDT initialization order ACPI 2.0 / AML: Improve module level execution by moving the
If/Else/While execution to per-table basis
ACPI 2.0 / AML: Add TermList parsing support for table loading ACPI 2.0 / AML: Enable correct ACPI subsystem initialization order
for new table loading mode
ACPI 2.0 / AML: Fix module level execution by correctly parsing table
as TermList