xref: /openbmc/linux/drivers/acpi/acpica/dscontrol.c (revision fa0dadde)
1 // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2 /******************************************************************************
3  *
4  * Module Name: dscontrol - Support for execution control opcodes -
5  *                          if/else/while/return
6  *
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_DISPATCHER
19 ACPI_MODULE_NAME("dscontrol")
20 
21 /*******************************************************************************
22  *
23  * FUNCTION:    acpi_ds_exec_begin_control_op
24  *
25  * PARAMETERS:  walk_list       - The list that owns the walk stack
26  *              op              - The control Op
27  *
28  * RETURN:      Status
29  *
30  * DESCRIPTION: Handles all control ops encountered during control method
31  *              execution.
32  *
33  ******************************************************************************/
34 acpi_status
35 acpi_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 these
70 		 * constructs. We need to manage these as a stack, in order
71 		 * 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 executions
80 		 * of a loop
81 		 */
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_op
122  *
123  * PARAMETERS:  walk_list       - The list that owns the walk stack
124  *              op              - The control Op
125  *
126  * RETURN:      Status
127  *
128  * DESCRIPTION: Handles all control ops encountered during control method
129  *              execution.
130  *
131  ******************************************************************************/
132 
133 acpi_status
134 acpi_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 an
149 		 * ELSE to come
150 		 */
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 start
156 		 * of the IF and free it
157 		 */
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 interpreter
178 			 * to escape possibly infinite loops. This can occur in poorly
179 			 * written AML when the hardware does not respond within a while
180 			 * 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 loop
191 			 * another time
192 			 */
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 value
219 		 * It can be either an immediate operand or a result that
220 		 * has been bubbled up the tree
221 		 */
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 as
239 			 * an arg or local), resolve it now because it may
240 			 * 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 result
251 			 * value. This is the only place where walk_state->return_desc
252 			 * 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 as
265 			 * an arg or local), resolve it now because it may
266 			 * cease to exist at the end of the method.
267 			 *
268 			 * Allow references created by the Index operator to return
269 			 * unchanged.
270 			 */
271 			if ((ACPI_GET_DESCRIPTOR_TYPE
272 			     (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