diff options
author | Dennis Noordsij <dennis.noordsij@helsinki.fi> | 2008-08-14 21:37:58 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2008-10-22 23:14:34 -0400 |
commit | f0e0da8a6cca44396c7a711e308d58084e881617 (patch) | |
tree | 015422fb82f6a86ac06f328ead51d6bcd6ca1d12 | |
parent | 3fa8749e584b55f1180411ab1b51117190bac1e5 (diff) |
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=10734
Signed-off-by: Dennis Noordsij <dennis.noordsij@helsinki.fi>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
-rw-r--r-- | drivers/acpi/executer/exconfig.c | 113 |
1 files changed, 82 insertions, 31 deletions
diff --git a/drivers/acpi/executer/exconfig.c b/drivers/acpi/executer/exconfig.c index 8892b9824fae..331a114f42c9 100644 --- a/drivers/acpi/executer/exconfig.c +++ b/drivers/acpi/executer/exconfig.c | |||
@@ -280,6 +280,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, | |||
280 | struct acpi_walk_state *walk_state) | 280 | struct acpi_walk_state *walk_state) |
281 | { | 281 | { |
282 | union acpi_operand_object *ddb_handle; | 282 | union acpi_operand_object *ddb_handle; |
283 | struct acpi_table_header *table; | ||
283 | struct acpi_table_desc table_desc; | 284 | struct acpi_table_desc table_desc; |
284 | u32 table_index; | 285 | u32 table_index; |
285 | acpi_status status; | 286 | acpi_status status; |
@@ -294,9 +295,8 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, | |||
294 | switch (ACPI_GET_OBJECT_TYPE(obj_desc)) { | 295 | switch (ACPI_GET_OBJECT_TYPE(obj_desc)) { |
295 | case ACPI_TYPE_REGION: | 296 | case ACPI_TYPE_REGION: |
296 | 297 | ||
297 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, "Load from Region %p %s\n", | 298 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, |
298 | obj_desc, | 299 | "Load table from Region %p\n", obj_desc)); |
299 | acpi_ut_get_object_type_name(obj_desc))); | ||
300 | 300 | ||
301 | /* Region must be system_memory (from ACPI spec) */ | 301 | /* Region must be system_memory (from ACPI spec) */ |
302 | 302 | ||
@@ -316,61 +316,112 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, | |||
316 | } | 316 | } |
317 | 317 | ||
318 | /* | 318 | /* |
319 | * We will simply map the memory region for the table. However, the | 319 | * Map the table header and get the actual table length. The region |
320 | * memory region is technically not guaranteed to remain stable and | 320 | * length is not guaranteed to be the same as the table length. |
321 | * we may eventually have to copy the table to a local buffer. | 321 | */ |
322 | table = acpi_os_map_memory(obj_desc->region.address, | ||
323 | sizeof(struct acpi_table_header)); | ||
324 | if (!table) { | ||
325 | return_ACPI_STATUS(AE_NO_MEMORY); | ||
326 | } | ||
327 | |||
328 | length = table->length; | ||
329 | acpi_os_unmap_memory(table, sizeof(struct acpi_table_header)); | ||
330 | |||
331 | /* Must have at least an ACPI table header */ | ||
332 | |||
333 | if (length < sizeof(struct acpi_table_header)) { | ||
334 | return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); | ||
335 | } | ||
336 | |||
337 | /* | ||
338 | * The memory region is not guaranteed to remain stable and we must | ||
339 | * copy the table to a local buffer. For example, the memory region | ||
340 | * is corrupted after suspend on some machines. Dynamically loaded | ||
341 | * tables are usually small, so this overhead is minimal. | ||
322 | */ | 342 | */ |
343 | |||
344 | /* Allocate a buffer for the table */ | ||
345 | |||
346 | table_desc.pointer = ACPI_ALLOCATE(length); | ||
347 | if (!table_desc.pointer) { | ||
348 | return_ACPI_STATUS(AE_NO_MEMORY); | ||
349 | } | ||
350 | |||
351 | /* Map the entire table and copy it */ | ||
352 | |||
353 | table = acpi_os_map_memory(obj_desc->region.address, length); | ||
354 | if (!table) { | ||
355 | ACPI_FREE(table_desc.pointer); | ||
356 | return_ACPI_STATUS(AE_NO_MEMORY); | ||
357 | } | ||
358 | |||
359 | ACPI_MEMCPY(table_desc.pointer, table, length); | ||
360 | acpi_os_unmap_memory(table, length); | ||
361 | |||
323 | table_desc.address = obj_desc->region.address; | 362 | table_desc.address = obj_desc->region.address; |
324 | table_desc.length = obj_desc->region.length; | ||
325 | table_desc.flags = ACPI_TABLE_ORIGIN_MAPPED; | ||
326 | break; | 363 | break; |
327 | 364 | ||
328 | case ACPI_TYPE_BUFFER: /* Buffer or resolved region_field */ | 365 | case ACPI_TYPE_BUFFER: /* Buffer or resolved region_field */ |
329 | 366 | ||
330 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, | 367 | ACPI_DEBUG_PRINT((ACPI_DB_EXEC, |
331 | "Load from Buffer or Field %p %s\n", obj_desc, | 368 | "Load table from Buffer or Field %p\n", |
332 | acpi_ut_get_object_type_name(obj_desc))); | 369 | obj_desc)); |
333 | |||
334 | length = obj_desc->buffer.length; | ||
335 | 370 | ||
336 | /* Must have at least an ACPI table header */ | 371 | /* Must have at least an ACPI table header */ |
337 | 372 | ||
338 | if (length < sizeof(struct acpi_table_header)) { | 373 | if (obj_desc->buffer.length < sizeof(struct acpi_table_header)) { |
339 | return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); | 374 | return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); |
340 | } | 375 | } |
341 | 376 | ||
342 | /* Validate checksum here. It won't get validated in tb_add_table */ | 377 | /* Get the actual table length from the table header */ |
343 | 378 | ||
344 | status = | 379 | table = |
345 | acpi_tb_verify_checksum(ACPI_CAST_PTR | 380 | ACPI_CAST_PTR(struct acpi_table_header, |
346 | (struct acpi_table_header, | 381 | obj_desc->buffer.pointer); |
347 | obj_desc->buffer.pointer), length); | 382 | length = table->length; |
348 | if (ACPI_FAILURE(status)) { | 383 | |
349 | return_ACPI_STATUS(status); | 384 | /* Table cannot extend beyond the buffer */ |
385 | |||
386 | if (length > obj_desc->buffer.length) { | ||
387 | return_ACPI_STATUS(AE_AML_BUFFER_LIMIT); | ||
388 | } | ||
389 | if (length < sizeof(struct acpi_table_header)) { | ||
390 | return_ACPI_STATUS(AE_INVALID_TABLE_LENGTH); | ||
350 | } | 391 | } |
351 | 392 | ||
352 | /* | 393 | /* |
353 | * We need to copy the buffer since the original buffer could be | 394 | * Copy the table from the buffer because the buffer could be modified |
354 | * changed or deleted in the future | 395 | * or even deleted in the future |
355 | */ | 396 | */ |
356 | table_desc.pointer = ACPI_ALLOCATE(length); | 397 | table_desc.pointer = ACPI_ALLOCATE(length); |
357 | if (!table_desc.pointer) { | 398 | if (!table_desc.pointer) { |
358 | return_ACPI_STATUS(AE_NO_MEMORY); | 399 | return_ACPI_STATUS(AE_NO_MEMORY); |
359 | } | 400 | } |
360 | 401 | ||
361 | ACPI_MEMCPY(table_desc.pointer, obj_desc->buffer.pointer, | 402 | ACPI_MEMCPY(table_desc.pointer, table, length); |
362 | length); | 403 | table_desc.address = ACPI_TO_INTEGER(table_desc.pointer); |
363 | table_desc.length = length; | ||
364 | table_desc.flags = ACPI_TABLE_ORIGIN_ALLOCATED; | ||
365 | break; | 404 | break; |
366 | 405 | ||
367 | default: | 406 | default: |
368 | return_ACPI_STATUS(AE_AML_OPERAND_TYPE); | 407 | return_ACPI_STATUS(AE_AML_OPERAND_TYPE); |
369 | } | 408 | } |
370 | 409 | ||
371 | /* | 410 | /* Validate table checksum (will not get validated in tb_add_table) */ |
372 | * Install the new table into the local data structures | 411 | |
373 | */ | 412 | status = acpi_tb_verify_checksum(table_desc.pointer, length); |
413 | if (ACPI_FAILURE(status)) { | ||
414 | ACPI_FREE(table_desc.pointer); | ||
415 | return_ACPI_STATUS(status); | ||
416 | } | ||
417 | |||
418 | /* Complete the table descriptor */ | ||
419 | |||
420 | table_desc.length = length; | ||
421 | table_desc.flags = ACPI_TABLE_ORIGIN_ALLOCATED; | ||
422 | |||
423 | /* Install the new table into the local data structures */ | ||
424 | |||
374 | status = acpi_tb_add_table(&table_desc, &table_index); | 425 | status = acpi_tb_add_table(&table_desc, &table_index); |
375 | if (ACPI_FAILURE(status)) { | 426 | if (ACPI_FAILURE(status)) { |
376 | goto cleanup; | 427 | goto cleanup; |
@@ -379,7 +430,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, | |||
379 | /* | 430 | /* |
380 | * Add the table to the namespace. | 431 | * Add the table to the namespace. |
381 | * | 432 | * |
382 | * Note: We load the table objects relative to the root of the namespace. | 433 | * Note: Load the table objects relative to the root of the namespace. |
383 | * This appears to go against the ACPI specification, but we do it for | 434 | * This appears to go against the ACPI specification, but we do it for |
384 | * compatibility with other ACPI implementations. | 435 | * compatibility with other ACPI implementations. |
385 | */ | 436 | */ |
@@ -415,7 +466,7 @@ acpi_ex_load_op(union acpi_operand_object *obj_desc, | |||
415 | cleanup: | 466 | cleanup: |
416 | if (ACPI_FAILURE(status)) { | 467 | if (ACPI_FAILURE(status)) { |
417 | 468 | ||
418 | /* Delete allocated buffer or mapping */ | 469 | /* Delete allocated table buffer */ |
419 | 470 | ||
420 | acpi_tb_delete_table(&table_desc); | 471 | acpi_tb_delete_table(&table_desc); |
421 | } | 472 | } |