diff options
Diffstat (limited to 'drivers/acpi/acpica/psloop.c')
| -rw-r--r-- | drivers/acpi/acpica/psloop.c | 329 |
1 files changed, 114 insertions, 215 deletions
diff --git a/drivers/acpi/acpica/psloop.c b/drivers/acpi/acpica/psloop.c index b4224005783c..c989cadf271c 100644 --- a/drivers/acpi/acpica/psloop.c +++ b/drivers/acpi/acpica/psloop.c @@ -1,45 +1,11 @@ +// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 /****************************************************************************** * * Module Name: psloop - Main AML parse loop * - *****************************************************************************/ - -/* - * Copyright (C) 2000 - 2017, Intel Corp. - * All rights reserved. + * Copyright (C) 2000 - 2025, Intel Corp. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions, and the following disclaimer, - * without modification. - * 2. Redistributions in binary form must reproduce at minimum a disclaimer - * substantially similar to the "NO WARRANTY" disclaimer below - * ("Disclaimer") and any redistribution must be conditioned upon - * including a substantially similar Disclaimer requirement for further - * binary redistribution. - * 3. Neither the names of the above-listed copyright holders nor the names - * of any contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * Alternatively, this software may be distributed under the terms of the - * GNU General Public License ("GPL") version 2 as published by the Free - * Software Foundation. - * - * NO WARRANTY - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING - * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGES. - */ + *****************************************************************************/ /* * Parse the AML and build an operation tree as most interpreters, (such as @@ -56,6 +22,7 @@ #include "acdispat.h" #include "amlcode.h" #include "acconvert.h" +#include "acnamesp.h" #define _COMPONENT ACPI_PARSER ACPI_MODULE_NAME("psloop") @@ -65,10 +32,6 @@ static acpi_status acpi_ps_get_arguments(struct acpi_walk_state *walk_state, u8 * aml_op_start, union acpi_parse_object *op); -static void -acpi_ps_link_module_code(union acpi_parse_object *parent_op, - u8 *aml_start, u32 aml_length, acpi_owner_id owner_id); - /******************************************************************************* * * FUNCTION: acpi_ps_get_arguments @@ -89,7 +52,6 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, { acpi_status status = AE_OK; union acpi_parse_object *arg = NULL; - const struct acpi_opcode_info *op_info; ACPI_FUNCTION_TRACE_PTR(ps_get_arguments, walk_state); @@ -164,87 +126,10 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, INCREMENT_ARG_LIST(walk_state->arg_types); } - /* - * Handle executable code at "module-level". This refers to - * executable opcodes that appear outside of any control method. - */ - if ((walk_state->pass_number <= ACPI_IMODE_LOAD_PASS2) && - ((walk_state->parse_flags & ACPI_PARSE_DISASSEMBLE) == 0)) { - /* - * We want to skip If/Else/While constructs during Pass1 because we - * want to actually conditionally execute the code during Pass2. - * - * Except for disassembly, where we always want to walk the - * If/Else/While packages - */ - switch (op->common.aml_opcode) { - case AML_IF_OP: - case AML_ELSE_OP: - case AML_WHILE_OP: - /* - * Currently supported module-level opcodes are: - * IF/ELSE/WHILE. These appear to be the most common, - * and easiest to support since they open an AML - * package. - */ - if (walk_state->pass_number == - ACPI_IMODE_LOAD_PASS1) { - acpi_ps_link_module_code(op->common. - parent, - aml_op_start, - (u32) - (walk_state-> - parser_state. - pkg_end - - aml_op_start), - walk_state-> - owner_id); - } - - ACPI_DEBUG_PRINT((ACPI_DB_PARSE, - "Pass1: Skipping an If/Else/While body\n")); - - /* Skip body of if/else/while in pass 1 */ - - walk_state->parser_state.aml = - walk_state->parser_state.pkg_end; - walk_state->arg_count = 0; - break; - - default: - /* - * Check for an unsupported executable opcode at module - * level. We must be in PASS1, the parent must be a SCOPE, - * The opcode class must be EXECUTE, and the opcode must - * not be an argument to another opcode. - */ - if ((walk_state->pass_number == - ACPI_IMODE_LOAD_PASS1) - && (op->common.parent->common.aml_opcode == - AML_SCOPE_OP)) { - op_info = - acpi_ps_get_opcode_info(op->common. - aml_opcode); - if ((op_info->class == - AML_CLASS_EXECUTE) && (!arg)) { - ACPI_WARNING((AE_INFO, - "Unsupported module-level executable opcode " - "0x%.2X at table offset 0x%.4X", - op->common. - aml_opcode, - (u32) - (ACPI_PTR_DIFF - (aml_op_start, - walk_state-> - parser_state. - aml_start) + - sizeof(struct - acpi_table_header)))); - } - } - break; - } - } + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Final argument count: %8.8X pass %u\n", + walk_state->arg_count, + walk_state->pass_number)); /* Special processing for certain opcodes */ @@ -277,6 +162,11 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, AML_NAME_OP) && (walk_state->pass_number <= ACPI_IMODE_LOAD_PASS2)) { + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Setup Package/Buffer: Pass %u, AML Ptr: %p\n", + walk_state->pass_number, + aml_op_start)); + /* * Skip parsing of Buffers and Packages because we don't have * enough info in the first pass to parse them correctly. @@ -317,94 +207,6 @@ acpi_ps_get_arguments(struct acpi_walk_state *walk_state, /******************************************************************************* * - * FUNCTION: acpi_ps_link_module_code - * - * PARAMETERS: parent_op - Parent parser op - * aml_start - Pointer to the AML - * aml_length - Length of executable AML - * owner_id - owner_id of module level code - * - * RETURN: None. - * - * DESCRIPTION: Wrap the module-level code with a method object and link the - * object to the global list. Note, the mutex field of the method - * object is used to link multiple module-level code objects. - * - ******************************************************************************/ - -static void -acpi_ps_link_module_code(union acpi_parse_object *parent_op, - u8 *aml_start, u32 aml_length, acpi_owner_id owner_id) -{ - union acpi_operand_object *prev; - union acpi_operand_object *next; - union acpi_operand_object *method_obj; - struct acpi_namespace_node *parent_node; - - ACPI_FUNCTION_TRACE(ps_link_module_code); - - /* Get the tail of the list */ - - prev = next = acpi_gbl_module_code_list; - while (next) { - prev = next; - next = next->method.mutex; - } - - /* - * Insert the module level code into the list. Merge it if it is - * adjacent to the previous element. - */ - if (!prev || - ((prev->method.aml_start + prev->method.aml_length) != aml_start)) { - - /* Create, initialize, and link a new temporary method object */ - - method_obj = acpi_ut_create_internal_object(ACPI_TYPE_METHOD); - if (!method_obj) { - return_VOID; - } - - ACPI_DEBUG_PRINT((ACPI_DB_PARSE, - "Create/Link new code block: %p\n", - method_obj)); - - if (parent_op->common.node) { - parent_node = parent_op->common.node; - } else { - parent_node = acpi_gbl_root_node; - } - - method_obj->method.aml_start = aml_start; - method_obj->method.aml_length = aml_length; - method_obj->method.owner_id = owner_id; - method_obj->method.info_flags |= ACPI_METHOD_MODULE_LEVEL; - - /* - * Save the parent node in next_object. This is cheating, but we - * don't want to expand the method object. - */ - method_obj->method.next_object = - ACPI_CAST_PTR(union acpi_operand_object, parent_node); - - if (!prev) { - acpi_gbl_module_code_list = method_obj; - } else { - prev->method.mutex = method_obj; - } - } else { - ACPI_DEBUG_PRINT((ACPI_DB_PARSE, - "Appending to existing code block: %p\n", - prev)); - - prev->method.aml_length += aml_length; - } - - return_VOID; -} - -/******************************************************************************* - * * FUNCTION: acpi_ps_parse_loop * * PARAMETERS: walk_state - Current state @@ -422,6 +224,7 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) union acpi_parse_object *op = NULL; /* current op */ struct acpi_parse_state *parser_state; u8 *aml_op_start = NULL; + u8 opcode_length; ACPI_FUNCTION_TRACE_PTR(ps_parse_loop, walk_state); @@ -432,7 +235,7 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) parser_state = &walk_state->parser_state; walk_state->arg_types = 0; -#if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY)) +#ifndef ACPI_CONSTANT_EVAL_ONLY if (walk_state->walk_type & ACPI_WALK_METHOD_RESTART) { @@ -461,8 +264,7 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) ACPI_TO_POINTER (TRUE)); if (ACPI_FAILURE(status) - && ((status & AE_CODE_MASK) != - AE_CODE_CONTROL)) { + && !ACPI_CNTL_EXCEPTION(status)) { if (status == AE_AML_NO_RETURN_VALUE) { ACPI_EXCEPTION((AE_INFO, status, "Invoked method did not return a value")); @@ -503,6 +305,19 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) status = acpi_ps_create_op(walk_state, aml_op_start, &op); if (ACPI_FAILURE(status)) { + /* + * ACPI_PARSE_MODULE_LEVEL means that we are loading a table by + * executing it as a control method. However, if we encounter + * an error while loading the table, we need to keep trying to + * load the table rather than aborting the table load. Set the + * status to AE_OK to proceed with the table load. + */ + if ((walk_state-> + parse_flags & ACPI_PARSE_MODULE_LEVEL) + && ((status == AE_ALREADY_EXISTS) + || (status == AE_NOT_FOUND))) { + status = AE_OK; + } if (status == AE_CTRL_PARSE_CONTINUE) { continue; } @@ -521,6 +336,34 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } + if (acpi_ns_opens_scope + (acpi_ps_get_opcode_info + (walk_state->opcode)->object_type)) { + /* + * If the scope/device op fails to parse, skip the body of + * the scope op because the parse failure indicates that + * the device may not exist. + */ + ACPI_INFO(("Skipping parse of AML opcode: %s (0x%4.4X)", acpi_ps_get_opcode_name(walk_state->opcode), walk_state->opcode)); + + /* + * Determine the opcode length before skipping the opcode. + * An opcode can be 1 byte or 2 bytes in length. + */ + opcode_length = 1; + if ((walk_state->opcode & 0xFF00) == + AML_EXTENDED_OPCODE) { + opcode_length = 2; + } + walk_state->parser_state.aml = + walk_state->aml + opcode_length; + + walk_state->parser_state.aml = + acpi_ps_get_next_package_end + (&walk_state->parser_state); + walk_state->aml = + walk_state->parser_state.aml; + } continue; } @@ -563,13 +406,50 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } - + if ((walk_state->control_state) && + ((walk_state->control_state->control. + opcode == AML_IF_OP) + || (walk_state->control_state->control. + opcode == AML_WHILE_OP))) { + /* + * If the if/while op fails to parse, we will skip parsing + * the body of the op. + */ + parser_state->aml = + walk_state->control_state->control. + aml_predicate_start + 1; + parser_state->aml = + acpi_ps_get_next_package_end + (parser_state); + walk_state->aml = parser_state->aml; + + ACPI_ERROR((AE_INFO, + "Skipping While/If block")); + if (*walk_state->aml == AML_ELSE_OP) { + ACPI_ERROR((AE_INFO, + "Skipping Else block")); + walk_state->parser_state.aml = + walk_state->aml + 1; + walk_state->parser_state.aml = + acpi_ps_get_next_package_end + (parser_state); + walk_state->aml = + parser_state->aml; + } + ACPI_FREE(acpi_ut_pop_generic_state + (&walk_state->control_state)); + } + op = NULL; continue; } } /* Check for arguments that need to be processed */ + ACPI_DEBUG_PRINT((ACPI_DB_PARSE, + "Parseloop: argument count: %8.8X\n", + walk_state->arg_count)); + if (walk_state->arg_count) { /* * There are arguments (complex ones), push Op and @@ -647,6 +527,25 @@ acpi_status acpi_ps_parse_loop(struct acpi_walk_state *walk_state) acpi_ps_next_parse_state(walk_state, op, status); if (status == AE_CTRL_PENDING) { status = AE_OK; + } else + if ((walk_state-> + parse_flags & ACPI_PARSE_MODULE_LEVEL) + && (ACPI_AML_EXCEPTION(status) + || status == AE_ALREADY_EXISTS + || status == AE_NOT_FOUND)) { + /* + * ACPI_PARSE_MODULE_LEVEL flag means that we + * are currently loading a table by executing + * it as a control method. However, if we + * encounter an error while loading the table, + * we need to keep trying to load the table + * rather than aborting the table load (setting + * the status to AE_OK continues the table + * load). If we get a failure at this point, it + * means that the dispatcher got an error while + * trying to execute the Op. + */ + status = AE_OK; } } |
