diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/acpi/acpica/exoparg1.c | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/acpi/acpica/exoparg1.c')
-rw-r--r-- | drivers/acpi/acpica/exoparg1.c | 1086 |
1 files changed, 1086 insertions, 0 deletions
diff --git a/drivers/acpi/acpica/exoparg1.c b/drivers/acpi/acpica/exoparg1.c new file mode 100644 index 000000000..d108a1a86 --- /dev/null +++ b/drivers/acpi/acpica/exoparg1.c @@ -0,0 +1,1086 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 +/****************************************************************************** + * + * Module Name: exoparg1 - AML execution - opcodes with 1 argument + * + * Copyright (C) 2000 - 2022, Intel Corp. + * + *****************************************************************************/ + +#include <acpi/acpi.h> +#include "accommon.h" +#include "acparser.h" +#include "acdispat.h" +#include "acinterp.h" +#include "amlcode.h" +#include "acnamesp.h" + +#define _COMPONENT ACPI_EXECUTER +ACPI_MODULE_NAME("exoparg1") + +/*! + * Naming convention for AML interpreter execution routines. + * + * The routines that begin execution of AML opcodes are named with a common + * convention based upon the number of arguments, the number of target operands, + * and whether or not a value is returned: + * + * AcpiExOpcode_xA_yT_zR + * + * Where: + * + * xA - ARGUMENTS: The number of arguments (input operands) that are + * required for this opcode type (0 through 6 args). + * yT - TARGETS: The number of targets (output operands) that are required + * for this opcode type (0, 1, or 2 targets). + * zR - RETURN VALUE: Indicates whether this opcode type returns a value + * as the function return (0 or 1). + * + * The AcpiExOpcode* functions are called via the Dispatcher component with + * fully resolved operands. +!*/ +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_0A_0T_1R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute operator with no operands, one return value + * + ******************************************************************************/ +acpi_status acpi_ex_opcode_0A_0T_1R(struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *return_desc = NULL; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_0A_0T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_TIMER_OP: /* Timer () */ + + /* Create a return object of type Integer */ + + return_desc = + acpi_ut_create_integer_object(acpi_os_get_timer()); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + break; + + default: /* Unknown opcode */ + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + break; + } + +cleanup: + + /* Delete return object on error */ + + if ((ACPI_FAILURE(status)) || walk_state->result_obj) { + acpi_ut_remove_reference(return_desc); + walk_state->result_obj = NULL; + } else { + /* Save the return value */ + + walk_state->result_obj = return_desc; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_0T_0R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 1 monadic operator with numeric operand on + * object stack + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_1A_0T_0R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_0T_0R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_RELEASE_OP: /* Release (mutex_object) */ + + status = acpi_ex_release_mutex(operand[0], walk_state); + break; + + case AML_RESET_OP: /* Reset (event_object) */ + + status = acpi_ex_system_reset_event(operand[0]); + break; + + case AML_SIGNAL_OP: /* Signal (event_object) */ + + status = acpi_ex_system_signal_event(operand[0]); + break; + + case AML_SLEEP_OP: /* Sleep (msec_time) */ + + status = acpi_ex_system_do_sleep(operand[0]->integer.value); + break; + + case AML_STALL_OP: /* Stall (usec_time) */ + + status = + acpi_ex_system_do_stall((u32) operand[0]->integer.value); + break; + + case AML_UNLOAD_OP: /* Unload (Handle) */ + + status = acpi_ex_unload_table(operand[0]); + break; + + default: /* Unknown opcode */ + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + break; + } + + return_ACPI_STATUS(status); +} + +#ifdef _OBSOLETE_CODE /* Was originally used for Load() operator */ +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_1T_0R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with one argument, one target, and no + * return value. + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_1A_1T_0R(struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object **operand = &walk_state->operands[0]; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_1T_0R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { +#ifdef _OBSOLETE_CODE + case AML_LOAD_OP: + + status = acpi_ex_load_op(operand[0], operand[1], walk_state); + break; +#endif + + default: /* Unknown opcode */ + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + +cleanup: + + return_ACPI_STATUS(status); +} +#endif + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_1T_1R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with one argument, one target, and a + * return value. + * January 2022: Added Load operator, with new ACPI 6.4 + * semantics. + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_1A_1T_1R(struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + union acpi_operand_object *return_desc2 = NULL; + u32 temp32; + u32 i; + u64 power_of_ten; + u64 digit; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_1T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_BIT_NOT_OP: + case AML_FIND_SET_LEFT_BIT_OP: + case AML_FIND_SET_RIGHT_BIT_OP: + case AML_FROM_BCD_OP: + case AML_LOAD_OP: + case AML_TO_BCD_OP: + case AML_CONDITIONAL_REF_OF_OP: + + /* Create a return object of type Integer for these opcodes */ + + return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + switch (walk_state->opcode) { + case AML_BIT_NOT_OP: /* Not (Operand, Result) */ + + return_desc->integer.value = ~operand[0]->integer.value; + break; + + case AML_FIND_SET_LEFT_BIT_OP: /* find_set_left_bit (Operand, Result) */ + + return_desc->integer.value = operand[0]->integer.value; + + /* + * Acpi specification describes Integer type as a little + * endian unsigned value, so this boundary condition is valid. + */ + for (temp32 = 0; return_desc->integer.value && + temp32 < ACPI_INTEGER_BIT_SIZE; ++temp32) { + return_desc->integer.value >>= 1; + } + + return_desc->integer.value = temp32; + break; + + case AML_FIND_SET_RIGHT_BIT_OP: /* find_set_right_bit (Operand, Result) */ + + return_desc->integer.value = operand[0]->integer.value; + + /* + * The Acpi specification describes Integer type as a little + * endian unsigned value, so this boundary condition is valid. + */ + for (temp32 = 0; return_desc->integer.value && + temp32 < ACPI_INTEGER_BIT_SIZE; ++temp32) { + return_desc->integer.value <<= 1; + } + + /* Since the bit position is one-based, subtract from 33 (65) */ + + return_desc->integer.value = + temp32 == + 0 ? 0 : (ACPI_INTEGER_BIT_SIZE + 1) - temp32; + break; + + case AML_FROM_BCD_OP: /* from_bcd (BCDValue, Result) */ + /* + * The 64-bit ACPI integer can hold 16 4-bit BCD characters + * (if table is 32-bit, integer can hold 8 BCD characters) + * Convert each 4-bit BCD value + */ + power_of_ten = 1; + return_desc->integer.value = 0; + digit = operand[0]->integer.value; + + /* Convert each BCD digit (each is one nybble wide) */ + + for (i = 0; + (i < acpi_gbl_integer_nybble_width) && (digit > 0); + i++) { + + /* Get the least significant 4-bit BCD digit */ + + temp32 = ((u32) digit) & 0xF; + + /* Check the range of the digit */ + + if (temp32 > 9) { + ACPI_ERROR((AE_INFO, + "BCD digit too large (not decimal): 0x%X", + temp32)); + + status = AE_AML_NUMERIC_OVERFLOW; + goto cleanup; + } + + /* Sum the digit into the result with the current power of 10 */ + + return_desc->integer.value += + (((u64) temp32) * power_of_ten); + + /* Shift to next BCD digit */ + + digit >>= 4; + + /* Next power of 10 */ + + power_of_ten *= 10; + } + break; + + case AML_LOAD_OP: /* Result1 = Load (Operand[0], Result1) */ + + return_desc->integer.value = 0; + status = + acpi_ex_load_op(operand[0], return_desc, + walk_state); + if (ACPI_SUCCESS(status)) { + + /* Return -1 (non-zero) indicates success */ + + return_desc->integer.value = 0xFFFFFFFFFFFFFFFF; + } + break; + + case AML_TO_BCD_OP: /* to_bcd (Operand, Result) */ + + return_desc->integer.value = 0; + digit = operand[0]->integer.value; + + /* Each BCD digit is one nybble wide */ + + for (i = 0; + (i < acpi_gbl_integer_nybble_width) && (digit > 0); + i++) { + (void)acpi_ut_short_divide(digit, 10, &digit, + &temp32); + + /* + * Insert the BCD digit that resides in the + * remainder from above + */ + return_desc->integer.value |= + (((u64) temp32) << ACPI_MUL_4(i)); + } + + /* Overflow if there is any data left in Digit */ + + if (digit > 0) { + ACPI_ERROR((AE_INFO, + "Integer too large to convert to BCD: 0x%8.8X%8.8X", + ACPI_FORMAT_UINT64(operand[0]-> + integer.value))); + status = AE_AML_NUMERIC_OVERFLOW; + goto cleanup; + } + break; + + case AML_CONDITIONAL_REF_OF_OP: /* cond_ref_of (source_object, Result) */ + /* + * This op is a little strange because the internal return value is + * different than the return value stored in the result descriptor + * (There are really two return values) + */ + if ((struct acpi_namespace_node *)operand[0] == + acpi_gbl_root_node) { + /* + * This means that the object does not exist in the namespace, + * return FALSE + */ + return_desc->integer.value = 0; + goto cleanup; + } + + /* Get the object reference, store it, and remove our reference */ + + status = acpi_ex_get_object_reference(operand[0], + &return_desc2, + walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + status = + acpi_ex_store(return_desc2, operand[1], walk_state); + acpi_ut_remove_reference(return_desc2); + + /* The object exists in the namespace, return TRUE */ + + return_desc->integer.value = ACPI_UINT64_MAX; + goto cleanup; + + default: + + /* No other opcodes get here */ + + break; + } + break; + + case AML_STORE_OP: /* Store (Source, Target) */ + /* + * A store operand is typically a number, string, buffer or lvalue + * Be careful about deleting the source object, + * since the object itself may have been stored. + */ + status = acpi_ex_store(operand[0], operand[1], walk_state); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + + /* It is possible that the Store already produced a return object */ + + if (!walk_state->result_obj) { + /* + * Normally, we would remove a reference on the Operand[0] + * parameter; But since it is being used as the internal return + * object (meaning we would normally increment it), the two + * cancel out, and we simply don't do anything. + */ + walk_state->result_obj = operand[0]; + walk_state->operands[0] = NULL; /* Prevent deletion */ + } + return_ACPI_STATUS(status); + + /* + * ACPI 2.0 Opcodes + */ + case AML_COPY_OBJECT_OP: /* copy_object (Source, Target) */ + + status = + acpi_ut_copy_iobject_to_iobject(operand[0], &return_desc, + walk_state); + break; + + case AML_TO_DECIMAL_STRING_OP: /* to_decimal_string (Data, Result) */ + + status = + acpi_ex_convert_to_string(operand[0], &return_desc, + ACPI_EXPLICIT_CONVERT_DECIMAL); + if (return_desc == operand[0]) { + + /* No conversion performed, add ref to handle return value */ + + acpi_ut_add_reference(return_desc); + } + break; + + case AML_TO_HEX_STRING_OP: /* to_hex_string (Data, Result) */ + + status = + acpi_ex_convert_to_string(operand[0], &return_desc, + ACPI_EXPLICIT_CONVERT_HEX); + if (return_desc == operand[0]) { + + /* No conversion performed, add ref to handle return value */ + + acpi_ut_add_reference(return_desc); + } + break; + + case AML_TO_BUFFER_OP: /* to_buffer (Data, Result) */ + + status = acpi_ex_convert_to_buffer(operand[0], &return_desc); + if (return_desc == operand[0]) { + + /* No conversion performed, add ref to handle return value */ + + acpi_ut_add_reference(return_desc); + } + break; + + case AML_TO_INTEGER_OP: /* to_integer (Data, Result) */ + + /* Perform "explicit" conversion */ + + status = + acpi_ex_convert_to_integer(operand[0], &return_desc, 0); + if (return_desc == operand[0]) { + + /* No conversion performed, add ref to handle return value */ + + acpi_ut_add_reference(return_desc); + } + break; + + case AML_SHIFT_LEFT_BIT_OP: /* shift_left_bit (Source, bit_num) */ + case AML_SHIFT_RIGHT_BIT_OP: /* shift_right_bit (Source, bit_num) */ + + /* These are two obsolete opcodes */ + + ACPI_ERROR((AE_INFO, + "%s is obsolete and not implemented", + acpi_ps_get_opcode_name(walk_state->opcode))); + status = AE_SUPPORT; + goto cleanup; + + default: /* Unknown opcode */ + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + if (ACPI_SUCCESS(status)) { + + /* Store the return value computed above into the target object */ + + status = acpi_ex_store(return_desc, operand[1], walk_state); + } + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(return_desc); + } + + /* Save return object on success */ + + else if (!walk_state->result_obj) { + walk_state->result_obj = return_desc; + } + + return_ACPI_STATUS(status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_0T_1R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with one argument, no target, and a return value + * + ******************************************************************************/ + +acpi_status acpi_ex_opcode_1A_0T_1R(struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *temp_desc; + union acpi_operand_object *return_desc = NULL; + acpi_status status = AE_OK; + u32 type; + u64 value; + + ACPI_FUNCTION_TRACE_STR(ex_opcode_1A_0T_1R, + acpi_ps_get_opcode_name(walk_state->opcode)); + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_LOGICAL_NOT_OP: /* LNot (Operand) */ + + return_desc = acpi_ut_create_integer_object((u64) 0); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Set result to ONES (TRUE) if Value == 0. Note: + * return_desc->Integer.Value is initially == 0 (FALSE) from above. + */ + if (!operand[0]->integer.value) { + return_desc->integer.value = ACPI_UINT64_MAX; + } + break; + + case AML_DECREMENT_OP: /* Decrement (Operand) */ + case AML_INCREMENT_OP: /* Increment (Operand) */ + /* + * Create a new integer. Can't just get the base integer and + * increment it because it may be an Arg or Field. + */ + return_desc = acpi_ut_create_internal_object(ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Since we are expecting a Reference operand, it can be either a + * NS Node or an internal object. + */ + temp_desc = operand[0]; + if (ACPI_GET_DESCRIPTOR_TYPE(temp_desc) == + ACPI_DESC_TYPE_OPERAND) { + + /* Internal reference object - prevent deletion */ + + acpi_ut_add_reference(temp_desc); + } + + /* + * Convert the Reference operand to an Integer (This removes a + * reference on the Operand[0] object) + * + * NOTE: We use LNOT_OP here in order to force resolution of the + * reference operand to an actual integer. + */ + status = acpi_ex_resolve_operands(AML_LOGICAL_NOT_OP, + &temp_desc, walk_state); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, + "While resolving operands for [%s]", + acpi_ps_get_opcode_name(walk_state-> + opcode))); + + goto cleanup; + } + + /* + * temp_desc is now guaranteed to be an Integer object -- + * Perform the actual increment or decrement + */ + if (walk_state->opcode == AML_INCREMENT_OP) { + return_desc->integer.value = + temp_desc->integer.value + 1; + } else { + return_desc->integer.value = + temp_desc->integer.value - 1; + } + + /* Finished with this Integer object */ + + acpi_ut_remove_reference(temp_desc); + + /* + * Store the result back (indirectly) through the original + * Reference object + */ + status = acpi_ex_store(return_desc, operand[0], walk_state); + break; + + case AML_OBJECT_TYPE_OP: /* object_type (source_object) */ + /* + * Note: The operand is not resolved at this point because we want to + * get the associated object, not its value. For example, we don't + * want to resolve a field_unit to its value, we want the actual + * field_unit object. + */ + + /* Get the type of the base object */ + + status = + acpi_ex_resolve_multiple(walk_state, operand[0], &type, + NULL); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* Allocate a descriptor to hold the type. */ + + return_desc = acpi_ut_create_integer_object((u64) type); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + break; + + case AML_SIZE_OF_OP: /* size_of (source_object) */ + /* + * Note: The operand is not resolved at this point because we want to + * get the associated object, not its value. + */ + + /* Get the base object */ + + status = + acpi_ex_resolve_multiple(walk_state, operand[0], &type, + &temp_desc); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * The type of the base object must be integer, buffer, string, or + * package. All others are not supported. + * + * NOTE: Integer is not specifically supported by the ACPI spec, + * but is supported implicitly via implicit operand conversion. + * rather than bother with conversion, we just use the byte width + * global (4 or 8 bytes). + */ + switch (type) { + case ACPI_TYPE_INTEGER: + + value = acpi_gbl_integer_byte_width; + break; + + case ACPI_TYPE_STRING: + + value = temp_desc->string.length; + break; + + case ACPI_TYPE_BUFFER: + + /* Buffer arguments may not be evaluated at this point */ + + status = acpi_ds_get_buffer_arguments(temp_desc); + value = temp_desc->buffer.length; + break; + + case ACPI_TYPE_PACKAGE: + + /* Package arguments may not be evaluated at this point */ + + status = acpi_ds_get_package_arguments(temp_desc); + value = temp_desc->package.count; + break; + + default: + + ACPI_ERROR((AE_INFO, + "Operand must be Buffer/Integer/String/Package" + " - found type %s", + acpi_ut_get_type_name(type))); + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * Now that we have the size of the object, create a result + * object to hold the value + */ + return_desc = acpi_ut_create_integer_object(value); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + break; + + case AML_REF_OF_OP: /* ref_of (source_object) */ + + status = + acpi_ex_get_object_reference(operand[0], &return_desc, + walk_state); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + break; + + case AML_DEREF_OF_OP: /* deref_of (obj_reference | String) */ + + /* Check for a method local or argument, or standalone String */ + + if (ACPI_GET_DESCRIPTOR_TYPE(operand[0]) == + ACPI_DESC_TYPE_NAMED) { + temp_desc = + acpi_ns_get_attached_object((struct + acpi_namespace_node *) + operand[0]); + if (temp_desc + && ((temp_desc->common.type == ACPI_TYPE_STRING) + || (temp_desc->common.type == + ACPI_TYPE_LOCAL_REFERENCE))) { + operand[0] = temp_desc; + acpi_ut_add_reference(temp_desc); + } else { + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + } else { + switch ((operand[0])->common.type) { + case ACPI_TYPE_LOCAL_REFERENCE: + /* + * This is a deref_of (local_x | arg_x) + * + * Must resolve/dereference the local/arg reference first + */ + switch (operand[0]->reference.class) { + case ACPI_REFCLASS_LOCAL: + case ACPI_REFCLASS_ARG: + + /* Set Operand[0] to the value of the local/arg */ + + status = + acpi_ds_method_data_get_value + (operand[0]->reference.class, + operand[0]->reference.value, + walk_state, &temp_desc); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + /* + * Delete our reference to the input object and + * point to the object just retrieved + */ + acpi_ut_remove_reference(operand[0]); + operand[0] = temp_desc; + break; + + case ACPI_REFCLASS_REFOF: + + /* Get the object to which the reference refers */ + + temp_desc = + operand[0]->reference.object; + acpi_ut_remove_reference(operand[0]); + operand[0] = temp_desc; + break; + + default: + + /* Must be an Index op - handled below */ + break; + } + break; + + case ACPI_TYPE_STRING: + + break; + + default: + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + } + + if (ACPI_GET_DESCRIPTOR_TYPE(operand[0]) != + ACPI_DESC_TYPE_NAMED) { + if ((operand[0])->common.type == ACPI_TYPE_STRING) { + /* + * This is a deref_of (String). The string is a reference + * to a named ACPI object. + * + * 1) Find the owning Node + * 2) Dereference the node to an actual object. Could be a + * Field, so we need to resolve the node to a value. + */ + status = + acpi_ns_get_node_unlocked(walk_state-> + scope_info->scope. + node, + operand[0]-> + string.pointer, + ACPI_NS_SEARCH_PARENT, + ACPI_CAST_INDIRECT_PTR + (struct + acpi_namespace_node, + &return_desc)); + if (ACPI_FAILURE(status)) { + goto cleanup; + } + + status = + acpi_ex_resolve_node_to_value + (ACPI_CAST_INDIRECT_PTR + (struct acpi_namespace_node, &return_desc), + walk_state); + goto cleanup; + } + } + + /* Operand[0] may have changed from the code above */ + + if (ACPI_GET_DESCRIPTOR_TYPE(operand[0]) == + ACPI_DESC_TYPE_NAMED) { + /* + * This is a deref_of (object_reference) + * Get the actual object from the Node (This is the dereference). + * This case may only happen when a local_x or arg_x is + * dereferenced above, or for references to device and + * thermal objects. + */ + switch (((struct acpi_namespace_node *)operand[0])-> + type) { + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_THERMAL: + + /* These types have no node subobject, return the NS node */ + + return_desc = operand[0]; + break; + + default: + /* For most types, get the object attached to the node */ + + return_desc = acpi_ns_get_attached_object((struct acpi_namespace_node *)operand[0]); + acpi_ut_add_reference(return_desc); + break; + } + } else { + /* + * This must be a reference object produced by either the + * Index() or ref_of() operator + */ + switch (operand[0]->reference.class) { + case ACPI_REFCLASS_INDEX: + /* + * The target type for the Index operator must be + * either a Buffer or a Package + */ + switch (operand[0]->reference.target_type) { + case ACPI_TYPE_BUFFER_FIELD: + + temp_desc = + operand[0]->reference.object; + + /* + * Create a new object that contains one element of the + * buffer -- the element pointed to by the index. + * + * NOTE: index into a buffer is NOT a pointer to a + * sub-buffer of the main buffer, it is only a pointer to a + * single element (byte) of the buffer! + * + * Since we are returning the value of the buffer at the + * indexed location, we don't need to add an additional + * reference to the buffer itself. + */ + return_desc = + acpi_ut_create_integer_object((u64) + temp_desc->buffer.pointer[operand[0]->reference.value]); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + break; + + case ACPI_TYPE_PACKAGE: + /* + * Return the referenced element of the package. We must + * add another reference to the referenced object, however. + */ + return_desc = + *(operand[0]->reference.where); + if (!return_desc) { + /* + * Element is NULL, do not allow the dereference. + * This provides compatibility with other ACPI + * implementations. + */ + return_ACPI_STATUS + (AE_AML_UNINITIALIZED_ELEMENT); + } + + acpi_ut_add_reference(return_desc); + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown Index TargetType 0x%X in reference object %p", + operand[0]->reference. + target_type, operand[0])); + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + case ACPI_REFCLASS_REFOF: + + return_desc = operand[0]->reference.object; + + if (ACPI_GET_DESCRIPTOR_TYPE(return_desc) == + ACPI_DESC_TYPE_NAMED) { + return_desc = + acpi_ns_get_attached_object((struct + acpi_namespace_node + *) + return_desc); + if (!return_desc) { + break; + } + + /* + * June 2013: + * buffer_fields/field_units require additional resolution + */ + switch (return_desc->common.type) { + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + status = + acpi_ex_read_data_from_field + (walk_state, return_desc, + &temp_desc); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS + (status); + } + + return_desc = temp_desc; + break; + + default: + + /* Add another reference to the object */ + + acpi_ut_add_reference + (return_desc); + break; + } + } + break; + + default: + + ACPI_ERROR((AE_INFO, + "Unknown class in reference(%p) - 0x%2.2X", + operand[0], + operand[0]->reference.class)); + + status = AE_TYPE; + goto cleanup; + } + } + break; + + default: + + ACPI_ERROR((AE_INFO, "Unknown AML opcode 0x%X", + walk_state->opcode)); + + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE(status)) { + acpi_ut_remove_reference(return_desc); + } + + /* Save return object on success */ + + else { + walk_state->result_obj = return_desc; + } + + return_ACPI_STATUS(status); +} |