1 /******************************************************************************
2  *
3  * Module Name: dscontrol - Support for execution control opcodes -
4  *                          if/else/while/return
5  *
6  *****************************************************************************/
7 
8 /*
9  * Copyright (C) 2000 - 2015, Intel Corp.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions, and the following disclaimer,
17  *    without modification.
18  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19  *    substantially similar to the "NO WARRANTY" disclaimer below
20  *    ("Disclaimer") and any redistribution must be conditioned upon
21  *    including a substantially similar Disclaimer requirement for further
22  *    binary redistribution.
23  * 3. Neither the names of the above-listed copyright holders nor the names
24  *    of any contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * Alternatively, this software may be distributed under the terms of the
28  * GNU General Public License ("GPL") version 2 as published by the Free
29  * Software Foundation.
30  *
31  * NO WARRANTY
32  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42  * POSSIBILITY OF SUCH DAMAGES.
43  */
44 
45 #include "acpi.h"
46 #include "accommon.h"
47 #include "amlcode.h"
48 #include "acdispat.h"
49 #include "acinterp.h"
50 
51 #define _COMPONENT          ACPI_DISPATCHER
52         ACPI_MODULE_NAME    ("dscontrol")
53 
54 
55 /*******************************************************************************
56  *
57  * FUNCTION:    AcpiDsExecBeginControlOp
58  *
59  * PARAMETERS:  WalkList        - The list that owns the walk stack
60  *              Op              - The control Op
61  *
62  * RETURN:      Status
63  *
64  * DESCRIPTION: Handles all control ops encountered during control method
65  *              execution.
66  *
67  ******************************************************************************/
68 
69 ACPI_STATUS
70 AcpiDsExecBeginControlOp (
71     ACPI_WALK_STATE         *WalkState,
72     ACPI_PARSE_OBJECT       *Op)
73 {
74     ACPI_STATUS             Status = AE_OK;
75     ACPI_GENERIC_STATE      *ControlState;
76 
77 
78     ACPI_FUNCTION_NAME (DsExecBeginControlOp);
79 
80 
81     ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Op=%p Opcode=%2.2X State=%p\n",
82         Op, Op->Common.AmlOpcode, WalkState));
83 
84     switch (Op->Common.AmlOpcode)
85     {
86     case AML_WHILE_OP:
87         /*
88          * If this is an additional iteration of a while loop, continue.
89          * There is no need to allocate a new control state.
90          */
91         if (WalkState->ControlState)
92         {
93             if (WalkState->ControlState->Control.AmlPredicateStart ==
94                 (WalkState->ParserState.Aml - 1))
95             {
96                 /* Reset the state to start-of-loop */
97 
98                 WalkState->ControlState->Common.State =
99                     ACPI_CONTROL_CONDITIONAL_EXECUTING;
100                 break;
101             }
102         }
103 
104         /*lint -fallthrough */
105 
106     case AML_IF_OP:
107         /*
108          * IF/WHILE: Create a new control state to manage these
109          * constructs. We need to manage these as a stack, in order
110          * to handle nesting.
111          */
112         ControlState = AcpiUtCreateControlState ();
113         if (!ControlState)
114         {
115             Status = AE_NO_MEMORY;
116             break;
117         }
118         /*
119          * Save a pointer to the predicate for multiple executions
120          * of a loop
121          */
122         ControlState->Control.AmlPredicateStart = WalkState->ParserState.Aml - 1;
123         ControlState->Control.PackageEnd = WalkState->ParserState.PkgEnd;
124         ControlState->Control.Opcode = Op->Common.AmlOpcode;
125 
126 
127         /* Push the control state on this walk's control stack */
128 
129         AcpiUtPushGenericState (&WalkState->ControlState, ControlState);
130         break;
131 
132     case AML_ELSE_OP:
133 
134         /* Predicate is in the state object */
135         /* If predicate is true, the IF was executed, ignore ELSE part */
136 
137         if (WalkState->LastPredicate)
138         {
139             Status = AE_CTRL_TRUE;
140         }
141 
142         break;
143 
144     case AML_RETURN_OP:
145 
146         break;
147 
148     default:
149 
150         break;
151     }
152 
153     return (Status);
154 }
155 
156 
157 /*******************************************************************************
158  *
159  * FUNCTION:    AcpiDsExecEndControlOp
160  *
161  * PARAMETERS:  WalkList        - The list that owns the walk stack
162  *              Op              - The control Op
163  *
164  * RETURN:      Status
165  *
166  * DESCRIPTION: Handles all control ops encountered during control method
167  *              execution.
168  *
169  ******************************************************************************/
170 
171 ACPI_STATUS
172 AcpiDsExecEndControlOp (
173     ACPI_WALK_STATE         *WalkState,
174     ACPI_PARSE_OBJECT       *Op)
175 {
176     ACPI_STATUS             Status = AE_OK;
177     ACPI_GENERIC_STATE      *ControlState;
178 
179 
180     ACPI_FUNCTION_NAME (DsExecEndControlOp);
181 
182 
183     switch (Op->Common.AmlOpcode)
184     {
185     case AML_IF_OP:
186 
187         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[IF_OP] Op=%p\n", Op));
188 
189         /*
190          * Save the result of the predicate in case there is an
191          * ELSE to come
192          */
193         WalkState->LastPredicate =
194             (BOOLEAN) WalkState->ControlState->Common.Value;
195 
196         /*
197          * Pop the control state that was created at the start
198          * of the IF and free it
199          */
200         ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
201         AcpiUtDeleteGenericState (ControlState);
202         break;
203 
204     case AML_ELSE_OP:
205 
206         break;
207 
208     case AML_WHILE_OP:
209 
210         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", Op));
211 
212         ControlState = WalkState->ControlState;
213         if (ControlState->Common.Value)
214         {
215             /* Predicate was true, the body of the loop was just executed */
216 
217             /*
218              * This loop counter mechanism allows the interpreter to escape
219              * possibly infinite loops. This can occur in poorly written AML
220              * when the hardware does not respond within a while loop and the
221              * loop does not implement a timeout.
222              */
223             ControlState->Control.LoopCount++;
224             if (ControlState->Control.LoopCount > AcpiGbl_MaxLoopIterations)
225             {
226                 Status = AE_AML_INFINITE_LOOP;
227                 break;
228             }
229 
230             /*
231              * Go back and evaluate the predicate and maybe execute the loop
232              * another time
233              */
234             Status = AE_CTRL_PENDING;
235             WalkState->AmlLastWhile = ControlState->Control.AmlPredicateStart;
236             break;
237         }
238 
239         /* Predicate was false, terminate this while loop */
240 
241         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
242             "[WHILE_OP] termination! Op=%p\n",Op));
243 
244         /* Pop this control state and free it */
245 
246         ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
247         AcpiUtDeleteGenericState (ControlState);
248         break;
249 
250     case AML_RETURN_OP:
251 
252         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
253             "[RETURN_OP] Op=%p Arg=%p\n",Op, Op->Common.Value.Arg));
254 
255         /*
256          * One optional operand -- the return value
257          * It can be either an immediate operand or a result that
258          * has been bubbled up the tree
259          */
260         if (Op->Common.Value.Arg)
261         {
262             /* Since we have a real Return(), delete any implicit return */
263 
264             AcpiDsClearImplicitReturn (WalkState);
265 
266             /* Return statement has an immediate operand */
267 
268             Status = AcpiDsCreateOperands (WalkState, Op->Common.Value.Arg);
269             if (ACPI_FAILURE (Status))
270             {
271                 return (Status);
272             }
273 
274             /*
275              * If value being returned is a Reference (such as
276              * an arg or local), resolve it now because it may
277              * cease to exist at the end of the method.
278              */
279             Status = AcpiExResolveToValue (&WalkState->Operands [0], WalkState);
280             if (ACPI_FAILURE (Status))
281             {
282                 return (Status);
283             }
284 
285             /*
286              * Get the return value and save as the last result
287              * value. This is the only place where WalkState->ReturnDesc
288              * is set to anything other than zero!
289              */
290             WalkState->ReturnDesc = WalkState->Operands[0];
291         }
292         else if (WalkState->ResultCount)
293         {
294             /* Since we have a real Return(), delete any implicit return */
295 
296             AcpiDsClearImplicitReturn (WalkState);
297 
298             /*
299              * The return value has come from a previous calculation.
300              *
301              * If value being returned is a Reference (such as
302              * an arg or local), resolve it now because it may
303              * cease to exist at the end of the method.
304              *
305              * Allow references created by the Index operator to return
306              * unchanged.
307              */
308             if ((ACPI_GET_DESCRIPTOR_TYPE (WalkState->Results->Results.ObjDesc[0]) == ACPI_DESC_TYPE_OPERAND) &&
309                 ((WalkState->Results->Results.ObjDesc [0])->Common.Type == ACPI_TYPE_LOCAL_REFERENCE) &&
310                 ((WalkState->Results->Results.ObjDesc [0])->Reference.Class != ACPI_REFCLASS_INDEX))
311             {
312                 Status = AcpiExResolveToValue (&WalkState->Results->Results.ObjDesc [0], WalkState);
313                 if (ACPI_FAILURE (Status))
314                 {
315                     return (Status);
316                 }
317             }
318 
319             WalkState->ReturnDesc = WalkState->Results->Results.ObjDesc [0];
320         }
321         else
322         {
323             /* No return operand */
324 
325             if (WalkState->NumOperands)
326             {
327                 AcpiUtRemoveReference (WalkState->Operands [0]);
328             }
329 
330             WalkState->Operands [0]     = NULL;
331             WalkState->NumOperands      = 0;
332             WalkState->ReturnDesc       = NULL;
333         }
334 
335 
336         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
337             "Completed RETURN_OP State=%p, RetVal=%p\n",
338             WalkState, WalkState->ReturnDesc));
339 
340         /* End the control method execution right now */
341 
342         Status = AE_CTRL_TERMINATE;
343         break;
344 
345     case AML_NOOP_OP:
346 
347         /* Just do nothing! */
348 
349         break;
350 
351     case AML_BREAK_POINT_OP:
352 
353         /*
354          * Set the single-step flag. This will cause the debugger (if present)
355          * to break to the console within the AML debugger at the start of the
356          * next AML instruction.
357          */
358         ACPI_DEBUGGER_EXEC (
359             AcpiGbl_CmSingleStep = TRUE);
360         ACPI_DEBUGGER_EXEC (
361             AcpiOsPrintf ("**break** Executed AML BreakPoint opcode\n"));
362 
363         /* Call to the OSL in case OS wants a piece of the action */
364 
365         Status = AcpiOsSignal (ACPI_SIGNAL_BREAKPOINT,
366                     "Executed AML Breakpoint opcode");
367         break;
368 
369     case AML_BREAK_OP:
370     case AML_CONTINUE_OP: /* ACPI 2.0 */
371 
372         /* Pop and delete control states until we find a while */
373 
374         while (WalkState->ControlState &&
375                 (WalkState->ControlState->Control.Opcode != AML_WHILE_OP))
376         {
377             ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
378             AcpiUtDeleteGenericState (ControlState);
379         }
380 
381         /* No while found? */
382 
383         if (!WalkState->ControlState)
384         {
385             return (AE_AML_NO_WHILE);
386         }
387 
388         /* Was: WalkState->AmlLastWhile = WalkState->ControlState->Control.AmlPredicateStart; */
389 
390         WalkState->AmlLastWhile = WalkState->ControlState->Control.PackageEnd;
391 
392         /* Return status depending on opcode */
393 
394         if (Op->Common.AmlOpcode == AML_BREAK_OP)
395         {
396             Status = AE_CTRL_BREAK;
397         }
398         else
399         {
400             Status = AE_CTRL_CONTINUE;
401         }
402         break;
403 
404     default:
405 
406         ACPI_ERROR ((AE_INFO, "Unknown control opcode=0x%X Op=%p",
407             Op->Common.AmlOpcode, Op));
408 
409         Status = AE_AML_BAD_OPCODE;
410         break;
411     }
412 
413     return (Status);
414 }
415