Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/acpi/acpica/dscontrol.c
26285 views
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 - 2025, 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
374