diff mbox series

ACPICA: Dispatcher: always generate buffer objects for ASL create_field() operator

Message ID 20191206134544.963913-1-luzmaximilian@gmail.com (mailing list archive)
State Deferred, archived
Headers show
Series ACPICA: Dispatcher: always generate buffer objects for ASL create_field() operator | expand

Commit Message

Maximilian Luz Dec. 6, 2019, 1:45 p.m. UTC
The Microsoft AML interpreter always creates Buffer objects via the
create_field() operator. Linux currently does this only when the field
size is larger than the maximum integer width. This causes problems with
the DSDT of Microsoft Surface devices.

These devices determine the success of a function call by the type
returned from it. On success, a Buffer object is expected, on failure an
Integer containing an error code. This buffer object is created with a
dynamic size via the create_field() operator. Due to the difference in
behavior, Buffer values of small size are however converted to Integers
and thus interpreted by the DSDT as the method having failed, whereas in
reality it succeeded.

This is actually not a bug in the kernel, but rather Microsoft not
conforming with the ACPI specification.

Signed-off-by: Maximilian Luz <luzmaximilian@gmail.com>
---
Description to be expanded.

This is a re-submit with changes as requested in
    PROBLEM: Calling ObjectType on buffer field reports type integer
---
 drivers/acpi/acpica/acobject.h |  3 ++-
 drivers/acpi/acpica/dsopcode.c |  2 ++
 drivers/acpi/acpica/exfield.c  | 15 ++++++++++++---
 3 files changed, 16 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/drivers/acpi/acpica/acobject.h b/drivers/acpi/acpica/acobject.h
index 8def0e3d690f..10154a8157ec 100644
--- a/drivers/acpi/acpica/acobject.h
+++ b/drivers/acpi/acpica/acobject.h
@@ -260,7 +260,8 @@  struct acpi_object_index_field {
 /* The buffer_field is different in that it is part of a Buffer, not an op_region */
 
 struct acpi_object_buffer_field {
-	ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO union acpi_operand_object *buffer_obj;	/* Containing Buffer object */
+	ACPI_OBJECT_COMMON_HEADER ACPI_COMMON_FIELD_INFO u8 is_create_field;	/* Special case for objects created by create_field() */
+	union acpi_operand_object *buffer_obj;	/* Containing Buffer object */
 };
 
 /******************************************************************************
diff --git a/drivers/acpi/acpica/dsopcode.c b/drivers/acpi/acpica/dsopcode.c
index 10f32b62608e..4632055121cd 100644
--- a/drivers/acpi/acpica/dsopcode.c
+++ b/drivers/acpi/acpica/dsopcode.c
@@ -217,6 +217,8 @@  acpi_ds_init_buffer_field(u16 aml_opcode,
 	}
 
 	obj_desc->buffer_field.buffer_obj = buffer_desc;
+	obj_desc->buffer_field.is_create_field =
+		aml_opcode == AML_CREATE_FIELD_OP;
 
 	/* Reference count for buffer_desc inherits obj_desc count */
 
diff --git a/drivers/acpi/acpica/exfield.c b/drivers/acpi/acpica/exfield.c
index d3d2dbfba680..7a4521f5ab3e 100644
--- a/drivers/acpi/acpica/exfield.c
+++ b/drivers/acpi/acpica/exfield.c
@@ -96,7 +96,8 @@  acpi_ex_get_protocol_buffer_length(u32 protocol_id, u32 *return_length)
  * RETURN:      Status
  *
  * DESCRIPTION: Read from a named field. Returns either an Integer or a
- *              Buffer, depending on the size of the field.
+ *              Buffer, depending on the size of the field and whether if a
+ *              field is created by the create_field() operator.
  *
  ******************************************************************************/
 
@@ -154,14 +155,22 @@  acpi_ex_read_data_from_field(struct acpi_walk_state *walk_state,
 	 * the use of arithmetic operators on the returned value if the
 	 * field size is equal or smaller than an Integer.
 	 *
+	 * However, all buffer fields created by create_field operator needs to
+	 * remain as a buffer to match other AML interpreter implementations.
+	 *
 	 * Note: Field.length is in bits.
 	 */
 	buffer_length =
 	    (acpi_size)ACPI_ROUND_BITS_UP_TO_BYTES(obj_desc->field.bit_length);
 
-	if (buffer_length > acpi_gbl_integer_byte_width) {
+	if (buffer_length > acpi_gbl_integer_byte_width ||
+	    (obj_desc->common.type == ACPI_TYPE_BUFFER_FIELD &&
+	     obj_desc->buffer_field.is_create_field)) {
 
-		/* Field is too large for an Integer, create a Buffer instead */
+		/*
+		 * Field is either too large for an Integer or created by the
+		 * create_field() operator, so we need to create a Buffer.
+		 */
 
 		buffer_desc = acpi_ut_create_buffer_object(buffer_length);
 		if (!buffer_desc) {