Commit f0e0da8a authored by Dennis Noordsij's avatar Dennis Noordsij Committed by Len Brown

ACPICA: Copy dynamically loaded tables to local buffer

Previously, dynamically loaded tables were simply mapped, but on some machines
this memory is corrupted after suspend. Now copy the table to a local buffer.
For OpRegion case, added checksum verify. Use the table length from the table header,
not the region length. For Buffer case, use the table length also.

http://bugzilla.kernel.org/show_bug.cgi?id=10734Signed-off-by: default avatarDennis Noordsij <dennis.noordsij@helsinki.fi>
Signed-off-by: default avatarBob Moore <robert.moore@intel.com>
Signed-off-by: default avatarLin Ming <ming.m.lin@intel.com>
Signed-off-by: default avatarAndi Kleen <ak@linux.intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
parent 3fa8749e
...@@ -280,6 +280,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, ...@@ -280,6 +280,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
struct acpi_walk_state *walk_state) struct acpi_walk_state *walk_state)
{ {
union acpi_operand_object *ddb_handle; union acpi_operand_object *ddb_handle;
struct acpi_table_header *table;
struct acpi_table_desc table_desc; struct acpi_table_desc table_desc;
u32 table_index; u32 table_index;
acpi_status status; acpi_status status;
...@@ -294,9 +295,8 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, ...@@ -294,9 +295,8 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
switch (ACPI_GET_OBJECT_TYPE(obj_desc)) { switch (ACPI_GET_OBJECT_TYPE(obj_desc)) {
case ACPI_TYPE_REGION: case ACPI_TYPE_REGION:
ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Load from Region %p %s\n", ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
obj_desc, "Load table from Region %p\n", obj_desc));
acpi_ut_get_object_type_name(obj_desc)));
/* Region must be system_memory (from ACPI spec) */ /* Region must be system_memory (from ACPI spec) */
...@@ -316,61 +316,112 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, ...@@ -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 * Map the table header and get the actual table length. The region
* memory region is technically not guaranteed to remain stable and * length is not guaranteed to be the same as the table length.
* we may eventually have to copy the table to a local buffer. */
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.address = obj_desc->region.address;
table_desc.length = obj_desc->region.length;
table_desc.flags = ACPI_TABLE_ORIGIN_MAPPED;
break; break;
case ACPI_TYPE_BUFFER: /* Buffer or resolved region_field */ case ACPI_TYPE_BUFFER: /* Buffer or resolved region_field */
ACPI_DEBUG_PRINT((ACPI_DB_EXEC, ACPI_DEBUG_PRINT((ACPI_DB_EXEC,
"Load from Buffer or Field %p %s\n", obj_desc, "Load table from Buffer or Field %p\n",
acpi_ut_get_object_type_name(obj_desc))); obj_desc));
length = obj_desc->buffer.length;
/* Must have at least an ACPI table header */ /* 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); 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 = table =
acpi_tb_verify_checksum(ACPI_CAST_PTR ACPI_CAST_PTR(struct acpi_table_header,
(struct acpi_table_header, obj_desc->buffer.pointer);
obj_desc->buffer.pointer), length); length = table->length;
if (ACPI_FAILURE(status)) {
return_ACPI_STATUS(status); /* 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 * Copy the table from the buffer because the buffer could be modified
* changed or deleted in the future * or even deleted in the future
*/ */
table_desc.pointer = ACPI_ALLOCATE(length); table_desc.pointer = ACPI_ALLOCATE(length);
if (!table_desc.pointer) { if (!table_desc.pointer) {
return_ACPI_STATUS(AE_NO_MEMORY); return_ACPI_STATUS(AE_NO_MEMORY);
} }
ACPI_MEMCPY(table_desc.pointer, obj_desc->buffer.pointer, ACPI_MEMCPY(table_desc.pointer, table, length);
length); table_desc.address = ACPI_TO_INTEGER(table_desc.pointer);
table_desc.length = length;
table_desc.flags = ACPI_TABLE_ORIGIN_ALLOCATED;
break; break;
default: default:
return_ACPI_STATUS(AE_AML_OPERAND_TYPE); return_ACPI_STATUS(AE_AML_OPERAND_TYPE);
} }
/* /* Validate table checksum (will not get validated in tb_add_table) */
* Install the new table into the local data structures
*/ 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); status = acpi_tb_add_table(&table_desc, &table_index);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
goto cleanup; goto cleanup;
...@@ -379,7 +430,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, ...@@ -379,7 +430,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
/* /*
* Add the table to the namespace. * 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 * This appears to go against the ACPI specification, but we do it for
* compatibility with other ACPI implementations. * compatibility with other ACPI implementations.
*/ */
...@@ -415,7 +466,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, ...@@ -415,7 +466,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc,
cleanup: cleanup:
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status)) {
/* Delete allocated buffer or mapping */ /* Delete allocated table buffer */
acpi_tb_delete_table(&table_desc); acpi_tb_delete_table(&table_desc);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment