ACPICA: Add support for module-level executable AML code

Add limited support for executable AML code that exists outside
of any control method. This type of code has been illegal since
ACPI 2.0.  The code must exist in an If/Else/While block. All AML
tables are supported, including tables that are dynamically loaded.
ACPICA BZ 762.

http://acpica.org/bugzilla/show_bug.cgi?id=762

Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
This commit is contained in:
Lin Ming 2009-08-13 14:03:15 +08:00 committed by Len Brown
parent 999e08f998
commit 7f0c826a43
13 changed files with 306 additions and 52 deletions

View File

@ -295,6 +295,7 @@ extern char const *acpi_gbl_exception_names_ctrl[];
ACPI_EXTERN struct acpi_namespace_node acpi_gbl_root_node_struct;
ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_root_node;
ACPI_EXTERN struct acpi_namespace_node *acpi_gbl_fadt_gpe_device;
ACPI_EXTERN union acpi_operand_object *acpi_gbl_module_code_list;
extern const u8 acpi_gbl_ns_properties[ACPI_NUM_NS_TYPES];
extern const struct acpi_predefined_names

View File

@ -196,6 +196,8 @@ acpi_ns_dump_objects(acpi_object_type type,
*/
acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info);
void acpi_ns_exec_module_code_list(void);
/*
* nspredef - Support for predefined/reserved names
*/

View File

@ -98,6 +98,7 @@
#define AOPOBJ_SETUP_COMPLETE 0x10
#define AOPOBJ_SINGLE_DATUM 0x20
#define AOPOBJ_INVALID 0x40 /* Used if host OS won't allow an op_region address */
#define AOPOBJ_MODULE_LEVEL 0x80
/******************************************************************************
*

View File

@ -62,6 +62,8 @@
#define ACPI_PARSE_DEFERRED_OP 0x0100
#define ACPI_PARSE_DISASSEMBLE 0x0200
#define ACPI_PARSE_MODULE_LEVEL 0x0400
/******************************************************************************
*
* Parser interfaces

View File

@ -123,9 +123,12 @@ acpi_ds_create_buffer_field(union acpi_parse_object *op,
flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE |
ACPI_NS_ERROR_IF_FOUND;
/* Mark node temporary if we are executing a method */
if (walk_state->method_node) {
/*
* Mark node temporary if we are executing a normal control
* method. (Don't mark if this is a module-level code method)
*/
if (walk_state->method_node &&
!(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) {
flags |= ACPI_NS_TEMPORARY;
}
@ -456,9 +459,12 @@ acpi_ds_init_field_objects(union acpi_parse_object *op,
flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE |
ACPI_NS_ERROR_IF_FOUND;
/* Mark node(s) temporary if we are executing a method */
if (walk_state->method_node) {
/*
* Mark node(s) temporary if we are executing a normal control
* method. (Don't mark if this is a module-level code method)
*/
if (walk_state->method_node &&
!(walk_state->parse_flags & ACPI_PARSE_MODULE_LEVEL)) {
flags |= ACPI_NS_TEMPORARY;
}

View File

@ -578,10 +578,15 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
}
/*
* Delete any namespace objects created anywhere within
* the namespace by the execution of this method
* Delete any namespace objects created anywhere within the
* namespace by the execution of this method. Unless this method
* is a module-level executable code method, in which case we
* want make the objects permanent.
*/
acpi_ns_delete_namespace_by_owner(method_desc->method.owner_id);
if (!(method_desc->method.flags & AOPOBJ_MODULE_LEVEL)) {
acpi_ns_delete_namespace_by_owner(method_desc->method.
owner_id);
}
}
/* Decrement the thread count on the method */
@ -622,7 +627,9 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc,
/* No more threads, we can free the owner_id */
acpi_ut_release_owner_id(&method_desc->method.owner_id);
if (!(method_desc->method.flags & AOPOBJ_MODULE_LEVEL)) {
acpi_ut_release_owner_id(&method_desc->method.owner_id);
}
}
return_VOID;

View File

@ -581,21 +581,6 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
if ((!(walk_state->op_info->flags & AML_NSOPCODE) &&
(walk_state->opcode != AML_INT_NAMEPATH_OP)) ||
(!(walk_state->op_info->flags & AML_NAMED))) {
#ifdef ACPI_ENABLE_MODULE_LEVEL_CODE
if ((walk_state->op_info->class == AML_CLASS_EXECUTE) ||
(walk_state->op_info->class == AML_CLASS_CONTROL)) {
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
"Begin/EXEC: %s (fl %8.8X)\n",
walk_state->op_info->name,
walk_state->op_info->flags));
/* Executing a type1 or type2 opcode outside of a method */
status =
acpi_ds_exec_begin_op(walk_state, out_op);
return_ACPI_STATUS(status);
}
#endif
return_ACPI_STATUS(AE_OK);
}
@ -768,7 +753,13 @@ acpi_ds_load2_begin_op(struct acpi_walk_state *walk_state,
/* Execution mode, node cannot already exist, node is temporary */
flags |= (ACPI_NS_ERROR_IF_FOUND | ACPI_NS_TEMPORARY);
flags |= ACPI_NS_ERROR_IF_FOUND;
if (!
(walk_state->
parse_flags & ACPI_PARSE_MODULE_LEVEL)) {
flags |= ACPI_NS_TEMPORARY;
}
}
/* Add new entry or lookup existing entry */
@ -851,24 +842,6 @@ acpi_status acpi_ds_load2_end_op(struct acpi_walk_state *walk_state)
/* Check if opcode had an associated namespace object */
if (!(walk_state->op_info->flags & AML_NSOBJECT)) {
#ifndef ACPI_NO_METHOD_EXECUTION
#ifdef ACPI_ENABLE_MODULE_LEVEL_CODE
/* No namespace object. Executable opcode? */
if ((walk_state->op_info->class == AML_CLASS_EXECUTE) ||
(walk_state->op_info->class == AML_CLASS_CONTROL)) {
ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,
"End/EXEC: %s (fl %8.8X)\n",
walk_state->op_info->name,
walk_state->op_info->flags));
/* Executing a type1 or type2 opcode outside of a method */
status = acpi_ds_exec_end_op(walk_state);
return_ACPI_STATUS(status);
}
#endif
#endif
return_ACPI_STATUS(AE_OK);
}

View File

@ -110,8 +110,15 @@ acpi_ex_add_table(u32 table_index,
if (ACPI_FAILURE(status)) {
acpi_ut_remove_reference(obj_desc);
*ddb_handle = NULL;
return_ACPI_STATUS(status);
}
/* Execute any module-level code that was found in the table */
acpi_ex_exit_interpreter();
acpi_ns_exec_module_code_list();
acpi_ex_enter_interpreter();
return_ACPI_STATUS(status);
}

View File

@ -50,6 +50,11 @@
#define _COMPONENT ACPI_NAMESPACE
ACPI_MODULE_NAME("nseval")
/* Local prototypes */
static void
acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
struct acpi_evaluate_info *info);
/*******************************************************************************
*
* FUNCTION: acpi_ns_evaluate
@ -76,6 +81,7 @@ ACPI_MODULE_NAME("nseval")
* MUTEX: Locks interpreter
*
******************************************************************************/
acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info)
{
acpi_status status;
@ -276,3 +282,134 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info * info)
*/
return_ACPI_STATUS(status);
}
/*******************************************************************************
*
* FUNCTION: acpi_ns_exec_module_code_list
*
* PARAMETERS: None
*
* RETURN: None. Exceptions during method execution are ignored, since
* we cannot abort a table load.
*
* DESCRIPTION: Execute all elements of the global module-level code list.
* Each element is executed as a single control method.
*
******************************************************************************/
void acpi_ns_exec_module_code_list(void)
{
union acpi_operand_object *prev;
union acpi_operand_object *next;
struct acpi_evaluate_info *info;
u32 method_count = 0;
ACPI_FUNCTION_TRACE(ns_exec_module_code_list);
/* Exit now if the list is empty */
next = acpi_gbl_module_code_list;
if (!next) {
return_VOID;
}
/* Allocate the evaluation information block */
info = ACPI_ALLOCATE(sizeof(struct acpi_evaluate_info));
if (!info) {
return_VOID;
}
/* Walk the list, executing each "method" */
while (next) {
prev = next;
next = next->method.mutex;
/* Clear the link field and execute the method */
prev->method.mutex = NULL;
acpi_ns_exec_module_code(prev, info);
method_count++;
/* Delete the (temporary) method object */
acpi_ut_remove_reference(prev);
}
ACPI_INFO((AE_INFO,
"Executed %u blocks of module-level executable AML code",
method_count));
ACPI_FREE(info);
acpi_gbl_module_code_list = NULL;
return_VOID;
}
/*******************************************************************************
*
* FUNCTION: acpi_ns_exec_module_code
*
* PARAMETERS: method_obj - Object container for the module-level code
* Info - Info block for method evaluation
*
* RETURN: None. Exceptions during method execution are ignored, since
* we cannot abort a table load.
*
* DESCRIPTION: Execute a control method containing a block of module-level
* executable AML code. The control method is temporarily
* installed to the root node, then evaluated.
*
******************************************************************************/
static void
acpi_ns_exec_module_code(union acpi_operand_object *method_obj,
struct acpi_evaluate_info *info)
{
union acpi_operand_object *root_obj;
acpi_status status;
ACPI_FUNCTION_TRACE(ns_exec_module_code);
/* Initialize the evaluation information block */
ACPI_MEMSET(info, 0, sizeof(struct acpi_evaluate_info));
info->prefix_node = acpi_gbl_root_node;
/*
* Get the currently attached root object. Add a reference, because the
* ref count will be decreased when the method object is installed to
* the root node.
*/
root_obj = acpi_ns_get_attached_object(acpi_gbl_root_node);
acpi_ut_add_reference(root_obj);
/* Install the method (module-level code) in the root node */
status = acpi_ns_attach_object(acpi_gbl_root_node, method_obj,
ACPI_TYPE_METHOD);
if (ACPI_FAILURE(status)) {
goto exit;
}
/* Execute the root node as a control method */
status = acpi_ns_evaluate(info);
ACPI_DEBUG_PRINT((ACPI_DB_INIT, "Executed module-level code at %p\n",
method_obj->method.aml_start));
/* Detach the temporary method object */
acpi_ns_detach_object(acpi_gbl_root_node);
/* Restore the original root object */
status =
acpi_ns_attach_object(acpi_gbl_root_node, root_obj,
ACPI_TYPE_DEVICE);
exit:
acpi_ut_remove_reference(root_obj);
return_VOID;
}

View File

@ -86,6 +86,9 @@ static acpi_status
acpi_ps_complete_final_op(struct acpi_walk_state *walk_state,
union acpi_parse_object *op, acpi_status status);
static void
acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id);
/*******************************************************************************
*
* FUNCTION: acpi_ps_get_aml_opcode
@ -390,6 +393,7 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
{
acpi_status status = AE_OK;
union acpi_parse_object *arg = NULL;
const struct acpi_opcode_info *op_info;
ACPI_FUNCTION_TRACE_PTR(ps_get_arguments, walk_state);
@ -449,13 +453,11 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
INCREMENT_ARG_LIST(walk_state->arg_types);
}
/* Special processing for certain opcodes */
/* TBD (remove): Temporary mechanism to disable this code if needed */
#ifdef ACPI_ENABLE_MODULE_LEVEL_CODE
if ((walk_state->pass_number <= ACPI_IMODE_LOAD_PASS1) &&
/*
* Handle executable code at "module-level". This refers to
* executable opcodes that appear outside of any control method.
*/
if ((walk_state->pass_number <= ACPI_IMODE_LOAD_PASS2) &&
((walk_state->parse_flags & ACPI_PARSE_DISASSEMBLE) == 0)) {
/*
* We want to skip If/Else/While constructs during Pass1 because we
@ -469,6 +471,23 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
case AML_ELSE_OP:
case AML_WHILE_OP:
/*
* Currently supported module-level opcodes are:
* IF/ELSE/WHILE. These appear to be the most common,
* and easiest to support since they open an AML
* package.
*/
if (walk_state->pass_number ==
ACPI_IMODE_LOAD_PASS1) {
acpi_ps_link_module_code(aml_op_start,
walk_state->
parser_state.
pkg_end -
aml_op_start,
walk_state->
owner_id);
}
ACPI_DEBUG_PRINT((ACPI_DB_PARSE,
"Pass1: Skipping an If/Else/While body\n"));
@ -480,10 +499,34 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
break;
default:
/*
* Check for an unsupported executable opcode at module
* level. We must be in PASS1, the parent must be a SCOPE,
* The opcode class must be EXECUTE, and the opcode must
* not be an argument to another opcode.
*/
if ((walk_state->pass_number ==
ACPI_IMODE_LOAD_PASS1)
&& (op->common.parent->common.aml_opcode ==
AML_SCOPE_OP)) {
op_info =
acpi_ps_get_opcode_info(op->common.
aml_opcode);
if ((op_info->class ==
AML_CLASS_EXECUTE) && (!arg)) {
ACPI_WARNING((AE_INFO,
"Detected an unsupported executable opcode "
"at module-level: [0x%.4X] at table offset 0x%.4X",
op->common.aml_opcode,
(u32)((aml_op_start - walk_state->parser_state.aml_start)
+ sizeof(struct acpi_table_header))));
}
}
break;
}
}
#endif
/* Special processing for certain opcodes */
switch (op->common.aml_opcode) {
case AML_METHOD_OP:
@ -551,6 +594,66 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
return_ACPI_STATUS(AE_OK);
}
/*******************************************************************************
*
* FUNCTION: acpi_ps_link_module_code
*
* PARAMETERS: aml_start - Pointer to the AML
* aml_length - Length of executable AML
* owner_id - owner_id of module level code
*
* RETURN: None.
*
* DESCRIPTION: Wrap the module-level code with a method object and link the
* object to the global list. Note, the mutex field of the method
* object is used to link multiple module-level code objects.
*
******************************************************************************/
static void
acpi_ps_link_module_code(u8 *aml_start, u32 aml_length, acpi_owner_id owner_id)
{
union acpi_operand_object *prev;
union acpi_operand_object *next;
union acpi_operand_object *method_obj;
/* Get the tail of the list */
prev = next = acpi_gbl_module_code_list;
while (next) {
prev = next;
next = next->method.mutex;
}
/*
* Insert the module level code into the list. Merge it if it is
* adjacent to the previous element.
*/
if (!prev ||
((prev->method.aml_start + prev->method.aml_length) != aml_start)) {
/* Create, initialize, and link a new temporary method object */
method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD);
if (!method_obj) {
return;
}
method_obj->method.aml_start = aml_start;
method_obj->method.aml_length = aml_length;
method_obj->method.owner_id = owner_id;
method_obj->method.flags |= AOPOBJ_MODULE_LEVEL;
if (!prev) {
acpi_gbl_module_code_list = method_obj;
} else {
prev->method.mutex = method_obj;
}
} else {
prev->method.aml_length += aml_length;
}
}
/*******************************************************************************
*
* FUNCTION: acpi_ps_complete_op

View File

@ -280,6 +280,10 @@ acpi_status acpi_ps_execute_method(struct acpi_evaluate_info *info)
goto cleanup;
}
if (info->obj_desc->method.flags & AOPOBJ_MODULE_LEVEL) {
walk_state->parse_flags |= ACPI_PARSE_MODULE_LEVEL;
}
/* Invoke an internal method if necessary */
if (info->obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) {

View File

@ -807,6 +807,7 @@ acpi_status acpi_ut_init_globals(void)
/* Namespace */
acpi_gbl_module_code_list = NULL;
acpi_gbl_root_node = NULL;
acpi_gbl_root_node_struct.name.integer = ACPI_ROOT_NAME;
acpi_gbl_root_node_struct.descriptor_type = ACPI_DESC_TYPE_NAMED;

View File

@ -250,6 +250,16 @@ acpi_status acpi_initialize_objects(u32 flags)
}
}
/*
* Execute any module-level code that was detected during the table load
* phase. Although illegal since ACPI 2.0, there are many machines that
* contain this type of code. Each block of detected executable AML code
* outside of any control method is wrapped with a temporary control
* method object and placed on a global list. The methods on this list
* are executed below.
*/
acpi_ns_exec_module_code_list();
/*
* Initialize the objects that remain uninitialized. This runs the
* executable AML that may be part of the declaration of these objects: