373 lines · c
1// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.02/******************************************************************************3 *4 * Module Name: dscontrol - Support for execution control opcodes -5 * if/else/while/return6 *7 * Copyright (C) 2000 - 2023, Intel Corp.8 *9 *****************************************************************************/10 11#include <acpi/acpi.h>12#include "accommon.h"13#include "amlcode.h"14#include "acdispat.h"15#include "acinterp.h"16#include "acdebug.h"17 18#define _COMPONENT ACPI_DISPATCHER19ACPI_MODULE_NAME("dscontrol")20 21/*******************************************************************************22 *23 * FUNCTION: acpi_ds_exec_begin_control_op24 *25 * PARAMETERS: walk_list - The list that owns the walk stack26 * op - The control Op27 *28 * RETURN: Status29 *30 * DESCRIPTION: Handles all control ops encountered during control method31 * execution.32 *33 ******************************************************************************/34acpi_status35acpi_ds_exec_begin_control_op(struct acpi_walk_state *walk_state,36 union acpi_parse_object *op)37{38 acpi_status status = AE_OK;39 union acpi_generic_state *control_state;40 41 ACPI_FUNCTION_NAME(ds_exec_begin_control_op);42 43 ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "Op=%p Opcode=%2.2X State=%p\n",44 op, op->common.aml_opcode, walk_state));45 46 switch (op->common.aml_opcode) {47 case AML_WHILE_OP:48 /*49 * If this is an additional iteration of a while loop, continue.50 * There is no need to allocate a new control state.51 */52 if (walk_state->control_state) {53 if (walk_state->control_state->control.54 aml_predicate_start ==55 (walk_state->parser_state.aml - 1)) {56 57 /* Reset the state to start-of-loop */58 59 walk_state->control_state->common.state =60 ACPI_CONTROL_CONDITIONAL_EXECUTING;61 break;62 }63 }64 65 ACPI_FALLTHROUGH;66 67 case AML_IF_OP:68 /*69 * IF/WHILE: Create a new control state to manage these70 * constructs. We need to manage these as a stack, in order71 * to handle nesting.72 */73 control_state = acpi_ut_create_control_state();74 if (!control_state) {75 status = AE_NO_MEMORY;76 break;77 }78 /*79 * Save a pointer to the predicate for multiple executions80 * of a loop81 */82 control_state->control.aml_predicate_start =83 walk_state->parser_state.aml - 1;84 control_state->control.package_end =85 walk_state->parser_state.pkg_end;86 control_state->control.opcode = op->common.aml_opcode;87 control_state->control.loop_timeout = acpi_os_get_timer() +88 ((u64)acpi_gbl_max_loop_iterations * ACPI_100NSEC_PER_SEC);89 90 /* Push the control state on this walk's control stack */91 92 acpi_ut_push_generic_state(&walk_state->control_state,93 control_state);94 break;95 96 case AML_ELSE_OP:97 98 /* Predicate is in the state object */99 /* If predicate is true, the IF was executed, ignore ELSE part */100 101 if (walk_state->last_predicate) {102 status = AE_CTRL_TRUE;103 }104 105 break;106 107 case AML_RETURN_OP:108 109 break;110 111 default:112 113 break;114 }115 116 return (status);117}118 119/*******************************************************************************120 *121 * FUNCTION: acpi_ds_exec_end_control_op122 *123 * PARAMETERS: walk_list - The list that owns the walk stack124 * op - The control Op125 *126 * RETURN: Status127 *128 * DESCRIPTION: Handles all control ops encountered during control method129 * execution.130 *131 ******************************************************************************/132 133acpi_status134acpi_ds_exec_end_control_op(struct acpi_walk_state *walk_state,135 union acpi_parse_object *op)136{137 acpi_status status = AE_OK;138 union acpi_generic_state *control_state;139 140 ACPI_FUNCTION_NAME(ds_exec_end_control_op);141 142 switch (op->common.aml_opcode) {143 case AML_IF_OP:144 145 ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[IF_OP] Op=%p\n", op));146 147 /*148 * Save the result of the predicate in case there is an149 * ELSE to come150 */151 walk_state->last_predicate =152 (u8)walk_state->control_state->common.value;153 154 /*155 * Pop the control state that was created at the start156 * of the IF and free it157 */158 control_state =159 acpi_ut_pop_generic_state(&walk_state->control_state);160 acpi_ut_delete_generic_state(control_state);161 break;162 163 case AML_ELSE_OP:164 165 break;166 167 case AML_WHILE_OP:168 169 ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", op));170 171 control_state = walk_state->control_state;172 if (control_state->common.value) {173 174 /* Predicate was true, the body of the loop was just executed */175 176 /*177 * This infinite loop detection mechanism allows the interpreter178 * to escape possibly infinite loops. This can occur in poorly179 * written AML when the hardware does not respond within a while180 * loop and the loop does not implement a timeout.181 */182 if (ACPI_TIME_AFTER(acpi_os_get_timer(),183 control_state->control.184 loop_timeout)) {185 status = AE_AML_LOOP_TIMEOUT;186 break;187 }188 189 /*190 * Go back and evaluate the predicate and maybe execute the loop191 * another time192 */193 status = AE_CTRL_PENDING;194 walk_state->aml_last_while =195 control_state->control.aml_predicate_start;196 break;197 }198 199 /* Predicate was false, terminate this while loop */200 201 ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,202 "[WHILE_OP] termination! Op=%p\n", op));203 204 /* Pop this control state and free it */205 206 control_state =207 acpi_ut_pop_generic_state(&walk_state->control_state);208 acpi_ut_delete_generic_state(control_state);209 break;210 211 case AML_RETURN_OP:212 213 ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,214 "[RETURN_OP] Op=%p Arg=%p\n", op,215 op->common.value.arg));216 217 /*218 * One optional operand -- the return value219 * It can be either an immediate operand or a result that220 * has been bubbled up the tree221 */222 if (op->common.value.arg) {223 224 /* Since we have a real Return(), delete any implicit return */225 226 acpi_ds_clear_implicit_return(walk_state);227 228 /* Return statement has an immediate operand */229 230 status =231 acpi_ds_create_operands(walk_state,232 op->common.value.arg);233 if (ACPI_FAILURE(status)) {234 return (status);235 }236 237 /*238 * If value being returned is a Reference (such as239 * an arg or local), resolve it now because it may240 * cease to exist at the end of the method.241 */242 status =243 acpi_ex_resolve_to_value(&walk_state->operands[0],244 walk_state);245 if (ACPI_FAILURE(status)) {246 return (status);247 }248 249 /*250 * Get the return value and save as the last result251 * value. This is the only place where walk_state->return_desc252 * is set to anything other than zero!253 */254 walk_state->return_desc = walk_state->operands[0];255 } else if (walk_state->result_count) {256 257 /* Since we have a real Return(), delete any implicit return */258 259 acpi_ds_clear_implicit_return(walk_state);260 261 /*262 * The return value has come from a previous calculation.263 *264 * If value being returned is a Reference (such as265 * an arg or local), resolve it now because it may266 * cease to exist at the end of the method.267 *268 * Allow references created by the Index operator to return269 * unchanged.270 */271 if ((ACPI_GET_DESCRIPTOR_TYPE272 (walk_state->results->results.obj_desc[0]) ==273 ACPI_DESC_TYPE_OPERAND)274 && ((walk_state->results->results.obj_desc[0])->275 common.type == ACPI_TYPE_LOCAL_REFERENCE)276 && ((walk_state->results->results.obj_desc[0])->277 reference.class != ACPI_REFCLASS_INDEX)) {278 status =279 acpi_ex_resolve_to_value(&walk_state->280 results->results.281 obj_desc[0],282 walk_state);283 if (ACPI_FAILURE(status)) {284 return (status);285 }286 }287 288 walk_state->return_desc =289 walk_state->results->results.obj_desc[0];290 } else {291 /* No return operand */292 293 if (walk_state->num_operands) {294 acpi_ut_remove_reference(walk_state->295 operands[0]);296 }297 298 walk_state->operands[0] = NULL;299 walk_state->num_operands = 0;300 walk_state->return_desc = NULL;301 }302 303 ACPI_DEBUG_PRINT((ACPI_DB_DISPATCH,304 "Completed RETURN_OP State=%p, RetVal=%p\n",305 walk_state, walk_state->return_desc));306 307 /* End the control method execution right now */308 309 status = AE_CTRL_TERMINATE;310 break;311 312 case AML_NOOP_OP:313 314 /* Just do nothing! */315 316 break;317 318 case AML_BREAKPOINT_OP:319 320 acpi_db_signal_break_point(walk_state);321 322 /* Call to the OSL in case OS wants a piece of the action */323 324 status = acpi_os_signal(ACPI_SIGNAL_BREAKPOINT,325 "Executed AML Breakpoint opcode");326 break;327 328 case AML_BREAK_OP:329 case AML_CONTINUE_OP: /* ACPI 2.0 */330 331 /* Pop and delete control states until we find a while */332 333 while (walk_state->control_state &&334 (walk_state->control_state->control.opcode !=335 AML_WHILE_OP)) {336 control_state =337 acpi_ut_pop_generic_state(&walk_state->338 control_state);339 acpi_ut_delete_generic_state(control_state);340 }341 342 /* No while found? */343 344 if (!walk_state->control_state) {345 return (AE_AML_NO_WHILE);346 }347 348 /* Was: walk_state->aml_last_while = walk_state->control_state->Control.aml_predicate_start; */349 350 walk_state->aml_last_while =351 walk_state->control_state->control.package_end;352 353 /* Return status depending on opcode */354 355 if (op->common.aml_opcode == AML_BREAK_OP) {356 status = AE_CTRL_BREAK;357 } else {358 status = AE_CTRL_CONTINUE;359 }360 break;361 362 default:363 364 ACPI_ERROR((AE_INFO, "Unknown control opcode=0x%X Op=%p",365 op->common.aml_opcode, op));366 367 status = AE_AML_BAD_OPCODE;368 break;369 }370 371 return (status);372}373