dscontrol.c revision 281075
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 <contrib/dev/acpica/include/acpi.h>
46#include <contrib/dev/acpica/include/accommon.h>
47#include <contrib/dev/acpica/include/amlcode.h>
48#include <contrib/dev/acpica/include/acdispat.h>
49#include <contrib/dev/acpica/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
69ACPI_STATUS
70AcpiDsExecBeginControlOp (
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
171ACPI_STATUS
172AcpiDsExecEndControlOp (
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 > ACPI_MAX_LOOP_ITERATIONS)
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