ACPICA: Add support for module-level executable AML code
Lin Ming [Thu, 13 Aug 2009 06:03:15 +0000 (14:03 +0800)]
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>

13 files changed:
drivers/acpi/acpica/acglobal.h
drivers/acpi/acpica/acnamesp.h
drivers/acpi/acpica/acobject.h
drivers/acpi/acpica/acparser.h
drivers/acpi/acpica/dsfield.c
drivers/acpi/acpica/dsmethod.c
drivers/acpi/acpica/dswload.c
drivers/acpi/acpica/exconfig.c
drivers/acpi/acpica/nseval.c
drivers/acpi/acpica/psloop.c
drivers/acpi/acpica/psxface.c
drivers/acpi/acpica/utglobal.c
drivers/acpi/acpica/utxface.c

index 6389f7c..29ba66d 100644 (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
index f75a7a0..09a2764 100644 (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
  */
index eb6f038..b39d682 100644 (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
 
 /******************************************************************************
  *
index 23ee0fb..22881e8 100644 (file)
@@ -62,6 +62,8 @@
 #define ACPI_PARSE_DEFERRED_OP          0x0100
 #define ACPI_PARSE_DISASSEMBLE          0x0200
 
+#define ACPI_PARSE_MODULE_LEVEL         0x0400
+
 /******************************************************************************
  *
  * Parser interfaces
index 53e27bc..54a225e 100644 (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;
        }
 
index 14b8b8e..567a489 100644 (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;
index 3023cea..6de3a99 100644 (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);
        }
 
index 277fd60..24afef8 100644 (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);
 }
 
index 8e7dec1..846d113 100644 (file)
 #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;
+}
index c5f6ce1..cd7995b 100644 (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:
@@ -553,6 +596,66 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state,
 
 /*******************************************************************************
  *
+ * 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
  *
  * PARAMETERS:  walk_state          - Current state
index ff06032..dd9731c 100644 (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) {
index 9e33b62..3f2c68f 100644 (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;
index 483edbb..b1f5f68 100644 (file)
@@ -251,6 +251,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:
         * operation_regions, buffer_fields, Buffers, and Packages.