dscontrol.c revision 303975
1130803Smarcel/******************************************************************************
2130803Smarcel *
3130803Smarcel * Module Name: dscontrol - Support for execution control opcodes -
4130803Smarcel *                          if/else/while/return
5130803Smarcel *
6130803Smarcel *****************************************************************************/
7130803Smarcel
8130803Smarcel/*
9130803Smarcel * Copyright (C) 2000 - 2016, Intel Corp.
10130803Smarcel * All rights reserved.
11130803Smarcel *
12130803Smarcel * Redistribution and use in source and binary forms, with or without
13130803Smarcel * modification, are permitted provided that the following conditions
14130803Smarcel * are met:
15130803Smarcel * 1. Redistributions of source code must retain the above copyright
16130803Smarcel *    notice, this list of conditions, and the following disclaimer,
17130803Smarcel *    without modification.
18130803Smarcel * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19130803Smarcel *    substantially similar to the "NO WARRANTY" disclaimer below
20130803Smarcel *    ("Disclaimer") and any redistribution must be conditioned upon
21130803Smarcel *    including a substantially similar Disclaimer requirement for further
22130803Smarcel *    binary redistribution.
23130803Smarcel * 3. Neither the names of the above-listed copyright holders nor the names
24130803Smarcel *    of any contributors may be used to endorse or promote products derived
25130803Smarcel *    from this software without specific prior written permission.
26130803Smarcel *
27130803Smarcel * Alternatively, this software may be distributed under the terms of the
28130803Smarcel * GNU General Public License ("GPL") version 2 as published by the Free
29130803Smarcel * Software Foundation.
30130803Smarcel *
31130803Smarcel * NO WARRANTY
32130803Smarcel * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33130803Smarcel * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34130803Smarcel * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35130803Smarcel * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36130803Smarcel * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37130803Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38130803Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39130803Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40130803Smarcel * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41130803Smarcel * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42130803Smarcel * POSSIBILITY OF SUCH DAMAGES.
43130803Smarcel */
44130803Smarcel
45130803Smarcel#include <contrib/dev/acpica/include/acpi.h>
46130803Smarcel#include <contrib/dev/acpica/include/accommon.h>
47130803Smarcel#include <contrib/dev/acpica/include/amlcode.h>
48130803Smarcel#include <contrib/dev/acpica/include/acdispat.h>
49130803Smarcel#include <contrib/dev/acpica/include/acinterp.h>
50130803Smarcel#include <contrib/dev/acpica/include/acdebug.h>
51130803Smarcel
52130803Smarcel#define _COMPONENT          ACPI_DISPATCHER
53130803Smarcel        ACPI_MODULE_NAME    ("dscontrol")
54130803Smarcel
55130803Smarcel
56130803Smarcel/*******************************************************************************
57130803Smarcel *
58130803Smarcel * FUNCTION:    AcpiDsExecBeginControlOp
59130803Smarcel *
60130803Smarcel * PARAMETERS:  WalkList        - The list that owns the walk stack
61130803Smarcel *              Op              - The control Op
62130803Smarcel *
63130803Smarcel * RETURN:      Status
64130803Smarcel *
65130803Smarcel * DESCRIPTION: Handles all control ops encountered during control method
66130803Smarcel *              execution.
67130803Smarcel *
68130803Smarcel ******************************************************************************/
69130803Smarcel
70130803SmarcelACPI_STATUS
71130803SmarcelAcpiDsExecBeginControlOp (
72130803Smarcel    ACPI_WALK_STATE         *WalkState,
73130803Smarcel    ACPI_PARSE_OBJECT       *Op)
74130803Smarcel{
75130803Smarcel    ACPI_STATUS             Status = AE_OK;
76130803Smarcel    ACPI_GENERIC_STATE      *ControlState;
77130803Smarcel
78130803Smarcel
79130803Smarcel    ACPI_FUNCTION_NAME (DsExecBeginControlOp);
80130803Smarcel
81130803Smarcel
82130803Smarcel    ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Op=%p Opcode=%2.2X State=%p\n",
83130803Smarcel        Op, Op->Common.AmlOpcode, WalkState));
84130803Smarcel
85130803Smarcel    switch (Op->Common.AmlOpcode)
86130803Smarcel    {
87130803Smarcel    case AML_WHILE_OP:
88130803Smarcel        /*
89130803Smarcel         * If this is an additional iteration of a while loop, continue.
90130803Smarcel         * There is no need to allocate a new control state.
91130803Smarcel         */
92130803Smarcel        if (WalkState->ControlState)
93130803Smarcel        {
94130803Smarcel            if (WalkState->ControlState->Control.AmlPredicateStart ==
95130803Smarcel                (WalkState->ParserState.Aml - 1))
96130803Smarcel            {
97130803Smarcel                /* Reset the state to start-of-loop */
98130803Smarcel
99130803Smarcel                WalkState->ControlState->Common.State =
100130803Smarcel                    ACPI_CONTROL_CONDITIONAL_EXECUTING;
101130803Smarcel                break;
102130803Smarcel            }
103130803Smarcel        }
104130803Smarcel
105130803Smarcel        /*lint -fallthrough */
106130803Smarcel
107130803Smarcel    case AML_IF_OP:
108130803Smarcel        /*
109130803Smarcel         * IF/WHILE: Create a new control state to manage these
110130803Smarcel         * constructs. We need to manage these as a stack, in order
111130803Smarcel         * to handle nesting.
112130803Smarcel         */
113130803Smarcel        ControlState = AcpiUtCreateControlState ();
114130803Smarcel        if (!ControlState)
115130803Smarcel        {
116130803Smarcel            Status = AE_NO_MEMORY;
117130803Smarcel            break;
118130803Smarcel        }
119130803Smarcel        /*
120130803Smarcel         * Save a pointer to the predicate for multiple executions
121130803Smarcel         * of a loop
122130803Smarcel         */
123130803Smarcel        ControlState->Control.AmlPredicateStart =
124130803Smarcel            WalkState->ParserState.Aml - 1;
125130803Smarcel        ControlState->Control.PackageEnd =
126130803Smarcel            WalkState->ParserState.PkgEnd;
127130803Smarcel        ControlState->Control.Opcode =
128130803Smarcel            Op->Common.AmlOpcode;
129130803Smarcel
130130803Smarcel
131130803Smarcel        /* Push the control state on this walk's control stack */
132130803Smarcel
133130803Smarcel        AcpiUtPushGenericState (&WalkState->ControlState, ControlState);
134130803Smarcel        break;
135130803Smarcel
136130803Smarcel    case AML_ELSE_OP:
137130803Smarcel
138130803Smarcel        /* Predicate is in the state object */
139130803Smarcel        /* If predicate is true, the IF was executed, ignore ELSE part */
140130803Smarcel
141130803Smarcel        if (WalkState->LastPredicate)
142130803Smarcel        {
143130803Smarcel            Status = AE_CTRL_TRUE;
144130803Smarcel        }
145130803Smarcel
146130803Smarcel        break;
147130803Smarcel
148130803Smarcel    case AML_RETURN_OP:
149130803Smarcel
150130803Smarcel        break;
151130803Smarcel
152130803Smarcel    default:
153130803Smarcel
154130803Smarcel        break;
155130803Smarcel    }
156130803Smarcel
157130803Smarcel    return (Status);
158130803Smarcel}
159130803Smarcel
160130803Smarcel
161130803Smarcel/*******************************************************************************
162130803Smarcel *
163130803Smarcel * FUNCTION:    AcpiDsExecEndControlOp
164130803Smarcel *
165130803Smarcel * PARAMETERS:  WalkList        - The list that owns the walk stack
166130803Smarcel *              Op              - The control Op
167130803Smarcel *
168130803Smarcel * RETURN:      Status
169130803Smarcel *
170130803Smarcel * DESCRIPTION: Handles all control ops encountered during control method
171130803Smarcel *              execution.
172130803Smarcel *
173130803Smarcel ******************************************************************************/
174130803Smarcel
175130803SmarcelACPI_STATUS
176130803SmarcelAcpiDsExecEndControlOp (
177130803Smarcel    ACPI_WALK_STATE         *WalkState,
178130803Smarcel    ACPI_PARSE_OBJECT       *Op)
179130803Smarcel{
180130803Smarcel    ACPI_STATUS             Status = AE_OK;
181130803Smarcel    ACPI_GENERIC_STATE      *ControlState;
182130803Smarcel
183130803Smarcel
184130803Smarcel    ACPI_FUNCTION_NAME (DsExecEndControlOp);
185130803Smarcel
186130803Smarcel
187130803Smarcel    switch (Op->Common.AmlOpcode)
188130803Smarcel    {
189130803Smarcel    case AML_IF_OP:
190130803Smarcel
191130803Smarcel        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[IF_OP] Op=%p\n", Op));
192130803Smarcel
193130803Smarcel        /*
194130803Smarcel         * Save the result of the predicate in case there is an
195130803Smarcel         * ELSE to come
196130803Smarcel         */
197130803Smarcel        WalkState->LastPredicate =
198130803Smarcel            (BOOLEAN) WalkState->ControlState->Common.Value;
199130803Smarcel
200130803Smarcel        /*
201130803Smarcel         * Pop the control state that was created at the start
202130803Smarcel         * of the IF and free it
203130803Smarcel         */
204130803Smarcel        ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
205130803Smarcel        AcpiUtDeleteGenericState (ControlState);
206130803Smarcel        break;
207130803Smarcel
208130803Smarcel    case AML_ELSE_OP:
209130803Smarcel
210130803Smarcel        break;
211130803Smarcel
212130803Smarcel    case AML_WHILE_OP:
213130803Smarcel
214130803Smarcel        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", Op));
215130803Smarcel
216130803Smarcel        ControlState = WalkState->ControlState;
217130803Smarcel        if (ControlState->Common.Value)
218130803Smarcel        {
219130803Smarcel            /* Predicate was true, the body of the loop was just executed */
220130803Smarcel
221130803Smarcel            /*
222130803Smarcel             * This loop counter mechanism allows the interpreter to escape
223130803Smarcel             * possibly infinite loops. This can occur in poorly written AML
224130803Smarcel             * when the hardware does not respond within a while loop and the
225130803Smarcel             * loop does not implement a timeout.
226130803Smarcel             */
227130803Smarcel            ControlState->Control.LoopCount++;
228130803Smarcel            if (ControlState->Control.LoopCount > AcpiGbl_MaxLoopIterations)
229130803Smarcel            {
230130803Smarcel                Status = AE_AML_INFINITE_LOOP;
231130803Smarcel                break;
232130803Smarcel            }
233130803Smarcel
234130803Smarcel            /*
235130803Smarcel             * Go back and evaluate the predicate and maybe execute the loop
236130803Smarcel             * another time
237130803Smarcel             */
238130803Smarcel            Status = AE_CTRL_PENDING;
239130803Smarcel            WalkState->AmlLastWhile =
240130803Smarcel                ControlState->Control.AmlPredicateStart;
241130803Smarcel            break;
242130803Smarcel        }
243130803Smarcel
244130803Smarcel        /* Predicate was false, terminate this while loop */
245130803Smarcel
246130803Smarcel        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
247130803Smarcel            "[WHILE_OP] termination! Op=%p\n",Op));
248130803Smarcel
249130803Smarcel        /* Pop this control state and free it */
250130803Smarcel
251130803Smarcel        ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
252130803Smarcel        AcpiUtDeleteGenericState (ControlState);
253130803Smarcel        break;
254130803Smarcel
255130803Smarcel    case AML_RETURN_OP:
256130803Smarcel
257130803Smarcel        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
258130803Smarcel            "[RETURN_OP] Op=%p Arg=%p\n",Op, Op->Common.Value.Arg));
259130803Smarcel
260130803Smarcel        /*
261130803Smarcel         * One optional operand -- the return value
262130803Smarcel         * It can be either an immediate operand or a result that
263130803Smarcel         * has been bubbled up the tree
264130803Smarcel         */
265130803Smarcel        if (Op->Common.Value.Arg)
266130803Smarcel        {
267130803Smarcel            /* Since we have a real Return(), delete any implicit return */
268130803Smarcel
269130803Smarcel            AcpiDsClearImplicitReturn (WalkState);
270130803Smarcel
271130803Smarcel            /* Return statement has an immediate operand */
272130803Smarcel
273130803Smarcel            Status = AcpiDsCreateOperands (WalkState, Op->Common.Value.Arg);
274130803Smarcel            if (ACPI_FAILURE (Status))
275130803Smarcel            {
276130803Smarcel                return (Status);
277130803Smarcel            }
278130803Smarcel
279130803Smarcel            /*
280130803Smarcel             * If value being returned is a Reference (such as
281130803Smarcel             * an arg or local), resolve it now because it may
282130803Smarcel             * cease to exist at the end of the method.
283130803Smarcel             */
284130803Smarcel            Status = AcpiExResolveToValue (
285130803Smarcel                &WalkState->Operands [0], WalkState);
286130803Smarcel            if (ACPI_FAILURE (Status))
287130803Smarcel            {
288130803Smarcel                return (Status);
289130803Smarcel            }
290130803Smarcel
291130803Smarcel            /*
292130803Smarcel             * Get the return value and save as the last result
293130803Smarcel             * value. This is the only place where WalkState->ReturnDesc
294130803Smarcel             * is set to anything other than zero!
295130803Smarcel             */
296130803Smarcel            WalkState->ReturnDesc = WalkState->Operands[0];
297130803Smarcel        }
298130803Smarcel        else if (WalkState->ResultCount)
299130803Smarcel        {
300130803Smarcel            /* Since we have a real Return(), delete any implicit return */
301130803Smarcel
302130803Smarcel            AcpiDsClearImplicitReturn (WalkState);
303130803Smarcel
304130803Smarcel            /*
305130803Smarcel             * The return value has come from a previous calculation.
306130803Smarcel             *
307130803Smarcel             * If value being returned is a Reference (such as
308130803Smarcel             * an arg or local), resolve it now because it may
309130803Smarcel             * cease to exist at the end of the method.
310130803Smarcel             *
311130803Smarcel             * Allow references created by the Index operator to return
312130803Smarcel             * unchanged.
313130803Smarcel             */
314130803Smarcel            if ((ACPI_GET_DESCRIPTOR_TYPE (WalkState->Results->Results.ObjDesc[0]) ==
315130803Smarcel                    ACPI_DESC_TYPE_OPERAND) &&
316130803Smarcel                ((WalkState->Results->Results.ObjDesc [0])->Common.Type ==
317130803Smarcel                    ACPI_TYPE_LOCAL_REFERENCE) &&
318130803Smarcel                ((WalkState->Results->Results.ObjDesc [0])->Reference.Class !=
319130803Smarcel                    ACPI_REFCLASS_INDEX))
320130803Smarcel            {
321130803Smarcel                Status = AcpiExResolveToValue (
322130803Smarcel                    &WalkState->Results->Results.ObjDesc [0], WalkState);
323130803Smarcel                if (ACPI_FAILURE (Status))
324130803Smarcel                {
325130803Smarcel                    return (Status);
326130803Smarcel                }
327130803Smarcel            }
328130803Smarcel
329130803Smarcel            WalkState->ReturnDesc = WalkState->Results->Results.ObjDesc [0];
330130803Smarcel        }
331130803Smarcel        else
332130803Smarcel        {
333130803Smarcel            /* No return operand */
334130803Smarcel
335130803Smarcel            if (WalkState->NumOperands)
336130803Smarcel            {
337130803Smarcel                AcpiUtRemoveReference (WalkState->Operands [0]);
338130803Smarcel            }
339130803Smarcel
340130803Smarcel            WalkState->Operands[0] = NULL;
341130803Smarcel            WalkState->NumOperands = 0;
342130803Smarcel            WalkState->ReturnDesc = NULL;
343130803Smarcel        }
344130803Smarcel
345130803Smarcel
346130803Smarcel        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
347130803Smarcel            "Completed RETURN_OP State=%p, RetVal=%p\n",
348130803Smarcel            WalkState, WalkState->ReturnDesc));
349130803Smarcel
350130803Smarcel        /* End the control method execution right now */
351130803Smarcel
352130803Smarcel        Status = AE_CTRL_TERMINATE;
353130803Smarcel        break;
354130803Smarcel
355130803Smarcel    case AML_NOOP_OP:
356130803Smarcel
357130803Smarcel        /* Just do nothing! */
358130803Smarcel
359130803Smarcel        break;
360130803Smarcel
361130803Smarcel    case AML_BREAK_POINT_OP:
362130803Smarcel
363130803Smarcel        AcpiDbSignalBreakPoint (WalkState);
364130803Smarcel
365130803Smarcel        /* Call to the OSL in case OS wants a piece of the action */
366130803Smarcel
367130803Smarcel        Status = AcpiOsSignal (ACPI_SIGNAL_BREAKPOINT,
368130803Smarcel            "Executed AML Breakpoint opcode");
369130803Smarcel        break;
370130803Smarcel
371130803Smarcel    case AML_BREAK_OP:
372130803Smarcel    case AML_CONTINUE_OP: /* ACPI 2.0 */
373130803Smarcel
374130803Smarcel        /* Pop and delete control states until we find a while */
375130803Smarcel
376130803Smarcel        while (WalkState->ControlState &&
377130803Smarcel                (WalkState->ControlState->Control.Opcode != AML_WHILE_OP))
378130803Smarcel        {
379130803Smarcel            ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
380130803Smarcel            AcpiUtDeleteGenericState (ControlState);
381130803Smarcel        }
382130803Smarcel
383130803Smarcel        /* No while found? */
384130803Smarcel
385130803Smarcel        if (!WalkState->ControlState)
386130803Smarcel        {
387130803Smarcel            return (AE_AML_NO_WHILE);
388130803Smarcel        }
389130803Smarcel
390130803Smarcel        /* Was: WalkState->AmlLastWhile = WalkState->ControlState->Control.AmlPredicateStart; */
391130803Smarcel
392130803Smarcel        WalkState->AmlLastWhile =
393130803Smarcel            WalkState->ControlState->Control.PackageEnd;
394130803Smarcel
395130803Smarcel        /* Return status depending on opcode */
396130803Smarcel
397130803Smarcel        if (Op->Common.AmlOpcode == AML_BREAK_OP)
398130803Smarcel        {
399130803Smarcel            Status = AE_CTRL_BREAK;
400130803Smarcel        }
401130803Smarcel        else
402130803Smarcel        {
403130803Smarcel            Status = AE_CTRL_CONTINUE;
404130803Smarcel        }
405130803Smarcel        break;
406130803Smarcel
407130803Smarcel    default:
408130803Smarcel
409130803Smarcel        ACPI_ERROR ((AE_INFO, "Unknown control opcode=0x%X Op=%p",
410130803Smarcel            Op->Common.AmlOpcode, Op));
411130803Smarcel
412130803Smarcel        Status = AE_AML_BAD_OPCODE;
413130803Smarcel        break;
414130803Smarcel    }
415130803Smarcel
416130803Smarcel    return (Status);
417130803Smarcel}
418130803Smarcel