Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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: 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
Loading
Loading
Loading
Loading
+82 −31
Original line number Original line Diff line number Diff line
@@ -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,
	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,
		}
		}


		/*
		/*
		 * 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,
	/*
	/*
	 * 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,
      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);
	}
	}