Merge commit 'origin/master' into next
[pandora-kernel.git] / drivers / acpi / acpica / exconfig.c
index 3deb20a..277fd60 100644 (file)
@@ -47,6 +47,7 @@
 #include "acnamesp.h"
 #include "actables.h"
 #include "acdispat.h"
+#include "acevents.h"
 
 #define _COMPONENT          ACPI_EXECUTER
 ACPI_MODULE_NAME("exconfig")
@@ -57,6 +58,10 @@ acpi_ex_add_table(u32 table_index,
                  struct acpi_namespace_node *parent_node,
                  union acpi_operand_object **ddb_handle);
 
+static acpi_status
+acpi_ex_region_read(union acpi_operand_object *obj_desc,
+                   u32 length, u8 *buffer);
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ex_add_table
@@ -91,6 +96,7 @@ acpi_ex_add_table(u32 table_index,
 
        /* Init the table handle */
 
+       obj_desc->common.flags |= AOPOBJ_DATA_VALID;
        obj_desc->reference.class = ACPI_REFCLASS_TABLE;
        *ddb_handle = obj_desc;
 
@@ -229,6 +235,8 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
                                       walk_state);
                if (ACPI_FAILURE(status)) {
                        (void)acpi_ex_unload_table(ddb_handle);
+
+                       acpi_ut_remove_reference(ddb_handle);
                        return_ACPI_STATUS(status);
                }
        }
@@ -252,6 +260,47 @@ acpi_ex_load_table_op(struct acpi_walk_state *walk_state,
        return_ACPI_STATUS(status);
 }
 
+/*******************************************************************************
+ *
+ * FUNCTION:    acpi_ex_region_read
+ *
+ * PARAMETERS:  obj_desc        - Region descriptor
+ *              Length          - Number of bytes to read
+ *              Buffer          - Pointer to where to put the data
+ *
+ * RETURN:      Status
+ *
+ * DESCRIPTION: Read data from an operation region. The read starts from the
+ *              beginning of the region.
+ *
+ ******************************************************************************/
+
+static acpi_status
+acpi_ex_region_read(union acpi_operand_object *obj_desc, u32 length, u8 *buffer)
+{
+       acpi_status status;
+       acpi_integer value;
+       u32 region_offset = 0;
+       u32 i;
+
+       /* Bytewise reads */
+
+       for (i = 0; i < length; i++) {
+               status = acpi_ev_address_space_dispatch(obj_desc, ACPI_READ,
+                                                       region_offset, 8,
+                                                       &value);
+               if (ACPI_FAILURE(status)) {
+                       return status;
+               }
+
+               *buffer = (u8)value;
+               buffer++;
+               region_offset++;
+       }
+
+       return AE_OK;
+}
+
 /*******************************************************************************
  *
  * FUNCTION:    acpi_ex_load_op
@@ -314,18 +363,23 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
                        }
                }
 
-               /*
-                * Map the table header and get the actual table length. The region
-                * length is not guaranteed to be the same as the table length.
-                */
-               table = acpi_os_map_memory(obj_desc->region.address,
-                                          sizeof(struct acpi_table_header));
+               /* Get the table header first so we can get the table length */
+
+               table = ACPI_ALLOCATE(sizeof(struct acpi_table_header));
                if (!table) {
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }
 
+               status =
+                   acpi_ex_region_read(obj_desc,
+                                       sizeof(struct acpi_table_header),
+                                       ACPI_CAST_PTR(u8, table));
                length = table->length;
-               acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
+               ACPI_FREE(table);
+
+               if (ACPI_FAILURE(status)) {
+                       return_ACPI_STATUS(status);
+               }
 
                /* Must have at least an ACPI table header */
 
@@ -334,10 +388,19 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
                }
 
                /*
-                * The memory region is not guaranteed to remain stable and we must
-                * copy the table to a local buffer. For example, the memory region
-                * is corrupted after suspend on some machines. Dynamically loaded
-                * tables are usually small, so this overhead is minimal.
+                * The original implementation simply mapped the table, with no copy.
+                * However, the memory region is not guaranteed to remain stable and
+                * we must copy the table to a local buffer. For example, the memory
+                * region is corrupted after suspend on some machines. Dynamically
+                * loaded tables are usually small, so this overhead is minimal.
+                *
+                * The latest implementation (5/2009) does not use a mapping at all.
+                * We use the low-level operation region interface to read the table
+                * instead of the obvious optimization of using a direct mapping.
+                * This maintains a consistent use of operation regions across the
+                * entire subsystem. This is important if additional processing must
+                * be performed in the (possibly user-installed) operation region
+                * handler. For example, acpi_exec and ASLTS depend on this.
                 */
 
                /* Allocate a buffer for the table */
@@ -347,17 +410,16 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
                        return_ACPI_STATUS(AE_NO_MEMORY);
                }
 
-               /* Map the entire table and copy it */
+               /* Read the entire table */
 
-               table = acpi_os_map_memory(obj_desc->region.address, length);
-               if (!table) {
+               status = acpi_ex_region_read(obj_desc, length,
+                                            ACPI_CAST_PTR(u8,
+                                                          table_desc.pointer));
+               if (ACPI_FAILURE(status)) {
                        ACPI_FREE(table_desc.pointer);
-                       return_ACPI_STATUS(AE_NO_MEMORY);
+                       return_ACPI_STATUS(status);
                }
 
-               ACPI_MEMCPY(table_desc.pointer, table, length);
-               acpi_os_unmap_memory(table, length);
-
                table_desc.address = obj_desc->region.address;
                break;
 
@@ -454,6 +516,10 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
                return_ACPI_STATUS(status);
        }
 
+       /* Remove the reference by added by acpi_ex_store above */
+
+       acpi_ut_remove_reference(ddb_handle);
+
        /* Invoke table handler if present */
 
        if (acpi_gbl_table_handler) {
@@ -495,13 +561,18 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
 
        /*
         * Validate the handle
-        * Although the handle is partially validated in acpi_ex_reconfiguration(),
+        * Although the handle is partially validated in acpi_ex_reconfiguration()
         * when it calls acpi_ex_resolve_operands(), the handle is more completely
         * validated here.
+        *
+        * Handle must be a valid operand object of type reference. Also, the
+        * ddb_handle must still be marked valid (table has not been previously
+        * unloaded)
         */
        if ((!ddb_handle) ||
            (ACPI_GET_DESCRIPTOR_TYPE(ddb_handle) != ACPI_DESC_TYPE_OPERAND) ||
-           (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE)) {
+           (ddb_handle->common.type != ACPI_TYPE_LOCAL_REFERENCE) ||
+           (!(ddb_handle->common.flags & AOPOBJ_DATA_VALID))) {
                return_ACPI_STATUS(AE_BAD_PARAMETER);
        }
 
@@ -509,6 +580,12 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
 
        table_index = table_desc->reference.value;
 
+       /* Ensure the table is still loaded */
+
+       if (!acpi_tb_is_table_loaded(table_index)) {
+               return_ACPI_STATUS(AE_NOT_EXIST);
+       }
+
        /* Invoke table handler if present */
 
        if (acpi_gbl_table_handler) {
@@ -530,8 +607,10 @@ acpi_status acpi_ex_unload_table(union acpi_operand_object *ddb_handle)
        (void)acpi_tb_release_owner_id(table_index);
        acpi_tb_set_table_loaded_flag(table_index, FALSE);
 
-       /* Table unloaded, remove a reference to the ddb_handle object */
-
-       acpi_ut_remove_reference(ddb_handle);
+       /*
+        * Invalidate the handle. We do this because the handle may be stored
+        * in a named object and may not be actually deleted until much later.
+        */
+       ddb_handle->common.flags &= ~AOPOBJ_DATA_VALID;
        return_ACPI_STATUS(AE_OK);
 }