2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-26 22:24:09 +08:00
linux-next/drivers/acpi/acpica/nsparse.c
Erik Schmauss aa342261bd ACPICA: Remove legacy module-level code support
ACPICA commit 47f5607c204719d9239a12b889df725225098c8f

Module-level code refers to executable ASL code that runs during
table load. This is typically used in ASL to declare named objects
based on a condition evaluated during table load like so:

definition_block(...)
{
  opreation_region (OPR1, system_memory, ...)
  Field (OPR1)
  {
    FLD1, 8 /* Assume that FLD1's value is 0x1 */
  }

  /* The if statement below is referred to as module-level code */

  If (FLD1)
  {
    /* Declare DEV1 conditionally */
    Device (DEV1) {...}
  }

  Device (DEV2)
  {
    ...
  }
}

In legacy module-level code, the execution of the If statement
was deferred after other modules were loaded. The order of
code execution for the table above is the following:

1.) Load OPR1 to the ACPI Namespace
2.) Load FLD1 to the ACPI Namespace (not intended for drivers)
3.) Load DEV2 to the ACPI Namespace
4.) Execute If (FLD1) and load DEV1 if the condition is true

This legacy approach can be problematic for tables that look like the
following:

definition_block(...)
{
  opreation_region (OPR1, system_memory, ...)
  Field (OPR1)
  {
    FLD1, 8 /* Assume that FLD1's value is 0x1 */
  }

  /* The if statement below is referred to as module-level code */

  If (FLD1)
  {
    /* Declare DEV1 conditionally */
    Device (DEV1) {...}
  }

  Scope (DEV1)
  {
    /* Add objects DEV1's scope */
    Name (OBJ1, 0x1234)
  }
}

When loading this in the legacy approach, Scope DEV1 gets evaluated
before the If statement. The following is the order of execution:

1.) Load OPR1 to the ACPI Namespace
2.) Load FLD1 to the ACPI Namespace (not intended for drivers)
3.) Add OBJ1 under DEV1's scope -- ERROR. DEV1 does not exist
4.) Execute If (FLD1) and load DEV1 if the condition is true

The legacy approach can never succeed for tables like this due to the
deferral of the module-level code. Due to this limitation, a new
module-level code was developed. This new approach exeutes if
statements in the order that they appear in the definition block.
With this approach, the order of execution for the above defintion
block is as follows:

1.) Load OPR1 to the ACPI Namespace
2.) Load FLD1 to the ACPI Namespace (not intended for drivers)
3.) Execute If (FLD1) and load DEV1 because the condition is true
4.) Add OBJ1 under DEV1's scope.

Since DEV1 is loaded in the namespace in step 3, step 4 executes
successfully.

This change removes support for the legacy module-level code
execution. From this point onward, the new module-level code
execution will be the official approach.

Link: https://github.com/acpica/acpica/commit/47f5607c
Signed-off-by: Erik Schmauss <erik.schmauss@intel.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2019-02-18 11:21:10 +01:00

272 lines
7.5 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: nsparse - namespace interface to AML parser
*
* Copyright (C) 2000 - 2019, Intel Corp.
*
*****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
#include "acnamesp.h"
#include "acparser.h"
#include "acdispat.h"
#include "actables.h"
#include "acinterp.h"
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nsparse")
/*******************************************************************************
*
* FUNCTION: ns_execute_table
*
* PARAMETERS: table_desc - An ACPI table descriptor for table to parse
* start_node - Where to enter the table into the namespace
*
* RETURN: Status
*
* DESCRIPTION: Load ACPI/AML table by executing the entire table as a single
* large control method.
*
* NOTE: The point of this is to execute any module-level code in-place
* as the table is parsed. Some AML code depends on this behavior.
*
* It is a run-time option at this time, but will eventually become
* the default.
*
* Note: This causes the table to only have a single-pass parse.
* However, this is compatible with other ACPI implementations.
*
******************************************************************************/
acpi_status
acpi_ns_execute_table(u32 table_index, struct acpi_namespace_node *start_node)
{
acpi_status status;
struct acpi_table_header *table;
acpi_owner_id owner_id;
struct acpi_evaluate_info *info = NULL;
u32 aml_length;
u8 *aml_start;
union acpi_operand_object *method_obj = NULL;
ACPI_FUNCTION_TRACE(ns_execute_table);
status = acpi_get_table_by_index(table_index, &table);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Table must consist of at least a complete header */
if (table->length < sizeof(struct acpi_table_header)) {
return_ACPI_STATUS(AE_BAD_HEADER);
}
aml_start = (u8 *)table + sizeof(struct acpi_table_header);
aml_length = table->length - sizeof(struct acpi_table_header);
status = acpi_tb_get_owner_id(table_index, &owner_id);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Create, initialize, and link a new temporary method object */
method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
if (!method_obj) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
/* Allocate the evaluation information block */
info = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_evaluate_info));
if (!info) {
status = AE_NO_MEMORY;
goto cleanup;
}
ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
"%s: Create table pseudo-method for [%4.4s] @%p, method %p\n",
ACPI_GET_FUNCTION_NAME, table->signature, table,
method_obj));
method_obj->method.aml_start = aml_start;
method_obj->method.aml_length = aml_length;
method_obj->method.owner_id = owner_id;
method_obj->method.info_flags |= ACPI_METHOD_MODULE_LEVEL;
info->pass_number = ACPI_IMODE_EXECUTE;
info->node = start_node;
info->obj_desc = method_obj;
info->node_flags = info->node->flags;
info->full_pathname = acpi_ns_get_normalized_pathname(info->node, TRUE);
if (!info->full_pathname) {
status = AE_NO_MEMORY;
goto cleanup;
}
/* Optional object evaluation log */
ACPI_DEBUG_PRINT_RAW((ACPI_DB_EVALUATION,
"%-26s: (Definition Block level)\n",
"Module-level evaluation"));
status = acpi_ps_execute_table(info);
/* Optional object evaluation log */
ACPI_DEBUG_PRINT_RAW((ACPI_DB_EVALUATION,
"%-26s: (Definition Block level)\n",
"Module-level complete"));
cleanup:
if (info) {
ACPI_FREE(info->full_pathname);
info->full_pathname = NULL;
}
ACPI_FREE(info);
acpi_ut_remove_reference(method_obj);
return_ACPI_STATUS(status);
}
/*******************************************************************************
*
* FUNCTION: ns_one_complete_parse
*
* PARAMETERS: pass_number - 1 or 2
* table_desc - The table to be parsed.
*
* RETURN: Status
*
* DESCRIPTION: Perform one complete parse of an ACPI/AML table.
*
******************************************************************************/
acpi_status
acpi_ns_one_complete_parse(u32 pass_number,
u32 table_index,
struct acpi_namespace_node *start_node)
{
union acpi_parse_object *parse_root;
acpi_status status;
u32 aml_length;
u8 *aml_start;
struct acpi_walk_state *walk_state;
struct acpi_table_header *table;
acpi_owner_id owner_id;
ACPI_FUNCTION_TRACE(ns_one_complete_parse);
status = acpi_get_table_by_index(table_index, &table);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Table must consist of at least a complete header */
if (table->length < sizeof(struct acpi_table_header)) {
return_ACPI_STATUS(AE_BAD_HEADER);
}
aml_start = (u8 *)table + sizeof(struct acpi_table_header);
aml_length = table->length - sizeof(struct acpi_table_header);
status = acpi_tb_get_owner_id(table_index, &owner_id);
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status);
}
/* Create and init a Root Node */
parse_root = acpi_ps_create_scope_op(aml_start);
if (!parse_root) {
return_ACPI_STATUS(AE_NO_MEMORY);
}
/* Create and initialize a new walk state */
walk_state = acpi_ds_create_walk_state(owner_id, NULL, NULL, NULL);
if (!walk_state) {
acpi_ps_free_op(parse_root);
return_ACPI_STATUS(AE_NO_MEMORY);
}
status = acpi_ds_init_aml_walk(walk_state, parse_root, NULL,
aml_start, aml_length, NULL,
(u8)pass_number);
if (ACPI_FAILURE(status)) {
acpi_ds_delete_walk_state(walk_state);
goto cleanup;
}
/* Found OSDT table, enable the namespace override feature */
if (ACPI_COMPARE_NAME(table->signature, ACPI_SIG_OSDT) &&
pass_number == ACPI_IMODE_LOAD_PASS1) {
walk_state->namespace_override = TRUE;
}
/* start_node is the default location to load the table */
if (start_node && start_node != acpi_gbl_root_node) {
status =
acpi_ds_scope_stack_push(start_node, ACPI_TYPE_METHOD,
walk_state);
if (ACPI_FAILURE(status)) {
acpi_ds_delete_walk_state(walk_state);
goto cleanup;
}
}
/* Parse the AML */
ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
"*PARSE* pass %u parse\n", pass_number));
acpi_ex_enter_interpreter();
status = acpi_ps_parse_aml(walk_state);
acpi_ex_exit_interpreter();
cleanup:
acpi_ps_delete_parse_tree(parse_root);
return_ACPI_STATUS(status);
}
/*******************************************************************************
*
* FUNCTION: acpi_ns_parse_table
*
* PARAMETERS: table_desc - An ACPI table descriptor for table to parse
* start_node - Where to enter the table into the namespace
*
* RETURN: Status
*
* DESCRIPTION: Parse AML within an ACPI table and return a tree of ops
*
******************************************************************************/
acpi_status
acpi_ns_parse_table(u32 table_index, struct acpi_namespace_node *start_node)
{
acpi_status status;
ACPI_FUNCTION_TRACE(ns_parse_table);
/*
* Executes the AML table as one large control method.
* The point of this is to execute any module-level code in-place
* as the table is parsed. Some AML code depends on this behavior.
*
* Note: This causes the table to only have a single-pass parse.
* However, this is compatible with other ACPI implementations.
*/
ACPI_DEBUG_PRINT_RAW((ACPI_DB_PARSE,
"%s: **** Start table execution pass\n",
ACPI_GET_FUNCTION_NAME));
status = acpi_ns_execute_table(table_index, start_node);
return_ACPI_STATUS(status);
}