@@ -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 */
};
/******************************************************************************
@@ -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 */
@@ -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) {
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(-)