|
@@ -280,6 +280,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
|
|
|
struct acpi_walk_state *walk_state)
|
|
|
{
|
|
|
union acpi_operand_object *ddb_handle;
|
|
|
+ struct acpi_table_header *table;
|
|
|
struct acpi_table_desc table_desc;
|
|
|
u32 table_index;
|
|
|
acpi_status status;
|
|
@@ -294,9 +295,8 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
|
|
|
switch (ACPI_GET_OBJECT_TYPE(obj_desc)) {
|
|
|
case ACPI_TYPE_REGION:
|
|
|
|
|
|
- ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Load from Region %p %s\n",
|
|
|
- obj_desc,
|
|
|
- acpi_ut_get_object_type_name(obj_desc)));
|
|
|
+ ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
|
|
|
+ "Load table from Region %p\n", obj_desc));
|
|
|
|
|
|
/* Region must be system_memory (from ACPI spec) */
|
|
|
|
|
@@ -316,61 +316,112 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * We will simply map the memory region for the table. However, the
|
|
|
- * memory region is technically not guaranteed to remain stable and
|
|
|
- * we may eventually have to copy the table to a local buffer.
|
|
|
+ * 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));
|
|
|
+ if (!table) {
|
|
|
+ return_ACPI_STATUS(AE_NO_MEMORY);
|
|
|
+ }
|
|
|
+
|
|
|
+ length = table->length;
|
|
|
+ acpi_os_unmap_memory(table, sizeof(struct acpi_table_header));
|
|
|
+
|
|
|
+ /* Must have at least an ACPI table header */
|
|
|
+
|
|
|
+ if (length < sizeof(struct acpi_table_header)) {
|
|
|
+ return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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.
|
|
|
*/
|
|
|
+
|
|
|
+ /* Allocate a buffer for the table */
|
|
|
+
|
|
|
+ table_desc.pointer = ACPI_ALLOCATE(length);
|
|
|
+ if (!table_desc.pointer) {
|
|
|
+ return_ACPI_STATUS(AE_NO_MEMORY);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Map the entire table and copy it */
|
|
|
+
|
|
|
+ table = acpi_os_map_memory(obj_desc->region.address, length);
|
|
|
+ if (!table) {
|
|
|
+ ACPI_FREE(table_desc.pointer);
|
|
|
+ return_ACPI_STATUS(AE_NO_MEMORY);
|
|
|
+ }
|
|
|
+
|
|
|
+ ACPI_MEMCPY(table_desc.pointer, table, length);
|
|
|
+ acpi_os_unmap_memory(table, length);
|
|
|
+
|
|
|
table_desc.address = obj_desc->region.address;
|
|
|
- table_desc.length = obj_desc->region.length;
|
|
|
- table_desc.flags = ACPI_TABLE_ORIGIN_MAPPED;
|
|
|
break;
|
|
|
|
|
|
case ACPI_TYPE_BUFFER: /* Buffer or resolved region_field */
|
|
|
|
|
|
ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
|
|
|
- "Load from Buffer or Field %p %s\n", obj_desc,
|
|
|
- acpi_ut_get_object_type_name(obj_desc)));
|
|
|
-
|
|
|
- length = obj_desc->buffer.length;
|
|
|
+ "Load table from Buffer or Field %p\n",
|
|
|
+ obj_desc));
|
|
|
|
|
|
/* Must have at least an ACPI table header */
|
|
|
|
|
|
- if (length < sizeof(struct acpi_table_header)) {
|
|
|
+ if (obj_desc->buffer.length < sizeof(struct acpi_table_header)) {
|
|
|
return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
|
|
|
}
|
|
|
|
|
|
- /* Validate checksum here. It won't get validated in tb_add_table */
|
|
|
+ /* Get the actual table length from the table header */
|
|
|
|
|
|
- status =
|
|
|
- acpi_tb_verify_checksum(ACPI_CAST_PTR
|
|
|
- (struct acpi_table_header,
|
|
|
- obj_desc->buffer.pointer), length);
|
|
|
- if (ACPI_FAILURE(status)) {
|
|
|
- return_ACPI_STATUS(status);
|
|
|
+ table =
|
|
|
+ ACPI_CAST_PTR(struct acpi_table_header,
|
|
|
+ obj_desc->buffer.pointer);
|
|
|
+ length = table->length;
|
|
|
+
|
|
|
+ /* Table cannot extend beyond the buffer */
|
|
|
+
|
|
|
+ if (length > obj_desc->buffer.length) {
|
|
|
+ return_ACPI_STATUS(AE_AML_BUFFER_LIMIT);
|
|
|
+ }
|
|
|
+ if (length < sizeof(struct acpi_table_header)) {
|
|
|
+ return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
- * We need to copy the buffer since the original buffer could be
|
|
|
- * changed or deleted in the future
|
|
|
+ * Copy the table from the buffer because the buffer could be modified
|
|
|
+ * or even deleted in the future
|
|
|
*/
|
|
|
table_desc.pointer = ACPI_ALLOCATE(length);
|
|
|
if (!table_desc.pointer) {
|
|
|
return_ACPI_STATUS(AE_NO_MEMORY);
|
|
|
}
|
|
|
|
|
|
- ACPI_MEMCPY(table_desc.pointer, obj_desc->buffer.pointer,
|
|
|
- length);
|
|
|
- table_desc.length = length;
|
|
|
- table_desc.flags = ACPI_TABLE_ORIGIN_ALLOCATED;
|
|
|
+ ACPI_MEMCPY(table_desc.pointer, table, length);
|
|
|
+ table_desc.address = ACPI_TO_INTEGER(table_desc.pointer);
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * Install the new table into the local data structures
|
|
|
- */
|
|
|
+ /* Validate table checksum (will not get validated in tb_add_table) */
|
|
|
+
|
|
|
+ status = acpi_tb_verify_checksum(table_desc.pointer, length);
|
|
|
+ if (ACPI_FAILURE(status)) {
|
|
|
+ ACPI_FREE(table_desc.pointer);
|
|
|
+ return_ACPI_STATUS(status);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Complete the table descriptor */
|
|
|
+
|
|
|
+ table_desc.length = length;
|
|
|
+ table_desc.flags = ACPI_TABLE_ORIGIN_ALLOCATED;
|
|
|
+
|
|
|
+ /* Install the new table into the local data structures */
|
|
|
+
|
|
|
status = acpi_tb_add_table(&table_desc, &table_index);
|
|
|
if (ACPI_FAILURE(status)) {
|
|
|
goto cleanup;
|
|
@@ -379,7 +430,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
|
|
|
/*
|
|
|
* Add the table to the namespace.
|
|
|
*
|
|
|
- * Note: We load the table objects relative to the root of the namespace.
|
|
|
+ * Note: Load the table objects relative to the root of the namespace.
|
|
|
* This appears to go against the ACPI specification, but we do it for
|
|
|
* compatibility with other ACPI implementations.
|
|
|
*/
|
|
@@ -415,7 +466,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
|
|
|
cleanup:
|
|
|
if (ACPI_FAILURE(status)) {
|
|
|
|
|
|
- /* Delete allocated buffer or mapping */
|
|
|
+ /* Delete allocated table buffer */
|
|
|
|
|
|
acpi_tb_delete_table(&table_desc);
|
|
|
}
|