1218585Sjkim/******************************************************************************
2218585Sjkim *
3218585Sjkim * Module Name: dscontrol - Support for execution control opcodes -
4218585Sjkim *                          if/else/while/return
5218585Sjkim *
6218585Sjkim *****************************************************************************/
7218585Sjkim
8218585Sjkim/*
9306536Sjkim * Copyright (C) 2000 - 2016, Intel Corp.
10218585Sjkim * All rights reserved.
11218585Sjkim *
12218585Sjkim * Redistribution and use in source and binary forms, with or without
13218585Sjkim * modification, are permitted provided that the following conditions
14218585Sjkim * are met:
15218585Sjkim * 1. Redistributions of source code must retain the above copyright
16218585Sjkim *    notice, this list of conditions, and the following disclaimer,
17218585Sjkim *    without modification.
18218585Sjkim * 2. Redistributions in binary form must reproduce at minimum a disclaimer
19218585Sjkim *    substantially similar to the "NO WARRANTY" disclaimer below
20218585Sjkim *    ("Disclaimer") and any redistribution must be conditioned upon
21218585Sjkim *    including a substantially similar Disclaimer requirement for further
22218585Sjkim *    binary redistribution.
23218585Sjkim * 3. Neither the names of the above-listed copyright holders nor the names
24218585Sjkim *    of any contributors may be used to endorse or promote products derived
25218585Sjkim *    from this software without specific prior written permission.
26218585Sjkim *
27218585Sjkim * Alternatively, this software may be distributed under the terms of the
28218585Sjkim * GNU General Public License ("GPL") version 2 as published by the Free
29218585Sjkim * Software Foundation.
30218585Sjkim *
31218585Sjkim * NO WARRANTY
32218585Sjkim * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
33218585Sjkim * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
34218585Sjkim * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
35218585Sjkim * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
36218585Sjkim * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37218585Sjkim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38218585Sjkim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39218585Sjkim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
40218585Sjkim * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
41218585Sjkim * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42218585Sjkim * POSSIBILITY OF SUCH DAMAGES.
43218585Sjkim */
44218585Sjkim
45218590Sjkim#include <contrib/dev/acpica/include/acpi.h>
46218590Sjkim#include <contrib/dev/acpica/include/accommon.h>
47218590Sjkim#include <contrib/dev/acpica/include/amlcode.h>
48218590Sjkim#include <contrib/dev/acpica/include/acdispat.h>
49218590Sjkim#include <contrib/dev/acpica/include/acinterp.h>
50306536Sjkim#include <contrib/dev/acpica/include/acdebug.h>
51218585Sjkim
52218585Sjkim#define _COMPONENT          ACPI_DISPATCHER
53218585Sjkim        ACPI_MODULE_NAME    ("dscontrol")
54218585Sjkim
55218585Sjkim
56218585Sjkim/*******************************************************************************
57218585Sjkim *
58218585Sjkim * FUNCTION:    AcpiDsExecBeginControlOp
59218585Sjkim *
60218585Sjkim * PARAMETERS:  WalkList        - The list that owns the walk stack
61218585Sjkim *              Op              - The control Op
62218585Sjkim *
63218585Sjkim * RETURN:      Status
64218585Sjkim *
65218585Sjkim * DESCRIPTION: Handles all control ops encountered during control method
66218585Sjkim *              execution.
67218585Sjkim *
68218585Sjkim ******************************************************************************/
69218585Sjkim
70218585SjkimACPI_STATUS
71218585SjkimAcpiDsExecBeginControlOp (
72218585Sjkim    ACPI_WALK_STATE         *WalkState,
73218585Sjkim    ACPI_PARSE_OBJECT       *Op)
74218585Sjkim{
75218585Sjkim    ACPI_STATUS             Status = AE_OK;
76218585Sjkim    ACPI_GENERIC_STATE      *ControlState;
77218585Sjkim
78218585Sjkim
79218585Sjkim    ACPI_FUNCTION_NAME (DsExecBeginControlOp);
80218585Sjkim
81218585Sjkim
82218585Sjkim    ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Op=%p Opcode=%2.2X State=%p\n",
83218585Sjkim        Op, Op->Common.AmlOpcode, WalkState));
84218585Sjkim
85218585Sjkim    switch (Op->Common.AmlOpcode)
86218585Sjkim    {
87218585Sjkim    case AML_WHILE_OP:
88218585Sjkim        /*
89218585Sjkim         * If this is an additional iteration of a while loop, continue.
90218585Sjkim         * There is no need to allocate a new control state.
91218585Sjkim         */
92218585Sjkim        if (WalkState->ControlState)
93218585Sjkim        {
94218585Sjkim            if (WalkState->ControlState->Control.AmlPredicateStart ==
95218585Sjkim                (WalkState->ParserState.Aml - 1))
96218585Sjkim            {
97218585Sjkim                /* Reset the state to start-of-loop */
98218585Sjkim
99218585Sjkim                WalkState->ControlState->Common.State =
100218585Sjkim                    ACPI_CONTROL_CONDITIONAL_EXECUTING;
101218585Sjkim                break;
102218585Sjkim            }
103218585Sjkim        }
104218585Sjkim
105218585Sjkim        /*lint -fallthrough */
106218585Sjkim
107218585Sjkim    case AML_IF_OP:
108218585Sjkim        /*
109218585Sjkim         * IF/WHILE: Create a new control state to manage these
110218585Sjkim         * constructs. We need to manage these as a stack, in order
111218585Sjkim         * to handle nesting.
112218585Sjkim         */
113218585Sjkim        ControlState = AcpiUtCreateControlState ();
114218585Sjkim        if (!ControlState)
115218585Sjkim        {
116218585Sjkim            Status = AE_NO_MEMORY;
117218585Sjkim            break;
118218585Sjkim        }
119218585Sjkim        /*
120218585Sjkim         * Save a pointer to the predicate for multiple executions
121218585Sjkim         * of a loop
122218585Sjkim         */
123306536Sjkim        ControlState->Control.AmlPredicateStart =
124306536Sjkim            WalkState->ParserState.Aml - 1;
125306536Sjkim        ControlState->Control.PackageEnd =
126306536Sjkim            WalkState->ParserState.PkgEnd;
127306536Sjkim        ControlState->Control.Opcode =
128306536Sjkim            Op->Common.AmlOpcode;
129218585Sjkim
130218585Sjkim
131218585Sjkim        /* Push the control state on this walk's control stack */
132218585Sjkim
133218585Sjkim        AcpiUtPushGenericState (&WalkState->ControlState, ControlState);
134218585Sjkim        break;
135218585Sjkim
136218585Sjkim    case AML_ELSE_OP:
137218585Sjkim
138218585Sjkim        /* Predicate is in the state object */
139218585Sjkim        /* If predicate is true, the IF was executed, ignore ELSE part */
140218585Sjkim
141218585Sjkim        if (WalkState->LastPredicate)
142218585Sjkim        {
143218585Sjkim            Status = AE_CTRL_TRUE;
144218585Sjkim        }
145218585Sjkim
146218585Sjkim        break;
147218585Sjkim
148218585Sjkim    case AML_RETURN_OP:
149218585Sjkim
150218585Sjkim        break;
151218585Sjkim
152218585Sjkim    default:
153250838Sjkim
154218585Sjkim        break;
155218585Sjkim    }
156218585Sjkim
157218585Sjkim    return (Status);
158218585Sjkim}
159218585Sjkim
160218585Sjkim
161218585Sjkim/*******************************************************************************
162218585Sjkim *
163218585Sjkim * FUNCTION:    AcpiDsExecEndControlOp
164218585Sjkim *
165218585Sjkim * PARAMETERS:  WalkList        - The list that owns the walk stack
166218585Sjkim *              Op              - The control Op
167218585Sjkim *
168218585Sjkim * RETURN:      Status
169218585Sjkim *
170218585Sjkim * DESCRIPTION: Handles all control ops encountered during control method
171218585Sjkim *              execution.
172218585Sjkim *
173218585Sjkim ******************************************************************************/
174218585Sjkim
175218585SjkimACPI_STATUS
176218585SjkimAcpiDsExecEndControlOp (
177218585Sjkim    ACPI_WALK_STATE         *WalkState,
178218585Sjkim    ACPI_PARSE_OBJECT       *Op)
179218585Sjkim{
180218585Sjkim    ACPI_STATUS             Status = AE_OK;
181218585Sjkim    ACPI_GENERIC_STATE      *ControlState;
182218585Sjkim
183218585Sjkim
184218585Sjkim    ACPI_FUNCTION_NAME (DsExecEndControlOp);
185218585Sjkim
186218585Sjkim
187218585Sjkim    switch (Op->Common.AmlOpcode)
188218585Sjkim    {
189218585Sjkim    case AML_IF_OP:
190218585Sjkim
191218585Sjkim        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[IF_OP] Op=%p\n", Op));
192218585Sjkim
193218585Sjkim        /*
194218585Sjkim         * Save the result of the predicate in case there is an
195218585Sjkim         * ELSE to come
196218585Sjkim         */
197218585Sjkim        WalkState->LastPredicate =
198218585Sjkim            (BOOLEAN) WalkState->ControlState->Common.Value;
199218585Sjkim
200218585Sjkim        /*
201218585Sjkim         * Pop the control state that was created at the start
202218585Sjkim         * of the IF and free it
203218585Sjkim         */
204218585Sjkim        ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
205218585Sjkim        AcpiUtDeleteGenericState (ControlState);
206218585Sjkim        break;
207218585Sjkim
208218585Sjkim    case AML_ELSE_OP:
209218585Sjkim
210218585Sjkim        break;
211218585Sjkim
212218585Sjkim    case AML_WHILE_OP:
213218585Sjkim
214218585Sjkim        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", Op));
215218585Sjkim
216218585Sjkim        ControlState = WalkState->ControlState;
217218585Sjkim        if (ControlState->Common.Value)
218218585Sjkim        {
219218585Sjkim            /* Predicate was true, the body of the loop was just executed */
220218585Sjkim
221218585Sjkim            /*
222218585Sjkim             * This loop counter mechanism allows the interpreter to escape
223218585Sjkim             * possibly infinite loops. This can occur in poorly written AML
224218585Sjkim             * when the hardware does not respond within a while loop and the
225218585Sjkim             * loop does not implement a timeout.
226218585Sjkim             */
227218585Sjkim            ControlState->Control.LoopCount++;
228306536Sjkim            if (ControlState->Control.LoopCount > AcpiGbl_MaxLoopIterations)
229218585Sjkim            {
230218585Sjkim                Status = AE_AML_INFINITE_LOOP;
231218585Sjkim                break;
232218585Sjkim            }
233218585Sjkim
234218585Sjkim            /*
235218585Sjkim             * Go back and evaluate the predicate and maybe execute the loop
236218585Sjkim             * another time
237218585Sjkim             */
238218585Sjkim            Status = AE_CTRL_PENDING;
239306536Sjkim            WalkState->AmlLastWhile =
240306536Sjkim                ControlState->Control.AmlPredicateStart;
241218585Sjkim            break;
242218585Sjkim        }
243218585Sjkim
244218585Sjkim        /* Predicate was false, terminate this while loop */
245218585Sjkim
246218585Sjkim        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
247218585Sjkim            "[WHILE_OP] termination! Op=%p\n",Op));
248218585Sjkim
249218585Sjkim        /* Pop this control state and free it */
250218585Sjkim
251218585Sjkim        ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
252218585Sjkim        AcpiUtDeleteGenericState (ControlState);
253218585Sjkim        break;
254218585Sjkim
255218585Sjkim    case AML_RETURN_OP:
256218585Sjkim
257218585Sjkim        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
258218585Sjkim            "[RETURN_OP] Op=%p Arg=%p\n",Op, Op->Common.Value.Arg));
259218585Sjkim
260218585Sjkim        /*
261218585Sjkim         * One optional operand -- the return value
262218585Sjkim         * It can be either an immediate operand or a result that
263218585Sjkim         * has been bubbled up the tree
264218585Sjkim         */
265218585Sjkim        if (Op->Common.Value.Arg)
266218585Sjkim        {
267218585Sjkim            /* Since we have a real Return(), delete any implicit return */
268218585Sjkim
269218585Sjkim            AcpiDsClearImplicitReturn (WalkState);
270218585Sjkim
271218585Sjkim            /* Return statement has an immediate operand */
272218585Sjkim
273218585Sjkim            Status = AcpiDsCreateOperands (WalkState, Op->Common.Value.Arg);
274218585Sjkim            if (ACPI_FAILURE (Status))
275218585Sjkim            {
276218585Sjkim                return (Status);
277218585Sjkim            }
278218585Sjkim
279218585Sjkim            /*
280218585Sjkim             * If value being returned is a Reference (such as
281218585Sjkim             * an arg or local), resolve it now because it may
282218585Sjkim             * cease to exist at the end of the method.
283218585Sjkim             */
284306536Sjkim            Status = AcpiExResolveToValue (
285306536Sjkim                &WalkState->Operands [0], WalkState);
286218585Sjkim            if (ACPI_FAILURE (Status))
287218585Sjkim            {
288218585Sjkim                return (Status);
289218585Sjkim            }
290218585Sjkim
291218585Sjkim            /*
292218585Sjkim             * Get the return value and save as the last result
293241973Sjkim             * value. This is the only place where WalkState->ReturnDesc
294218585Sjkim             * is set to anything other than zero!
295218585Sjkim             */
296218585Sjkim            WalkState->ReturnDesc = WalkState->Operands[0];
297218585Sjkim        }
298218585Sjkim        else if (WalkState->ResultCount)
299218585Sjkim        {
300218585Sjkim            /* Since we have a real Return(), delete any implicit return */
301218585Sjkim
302218585Sjkim            AcpiDsClearImplicitReturn (WalkState);
303218585Sjkim
304218585Sjkim            /*
305218585Sjkim             * The return value has come from a previous calculation.
306218585Sjkim             *
307218585Sjkim             * If value being returned is a Reference (such as
308218585Sjkim             * an arg or local), resolve it now because it may
309218585Sjkim             * cease to exist at the end of the method.
310218585Sjkim             *
311218585Sjkim             * Allow references created by the Index operator to return
312218585Sjkim             * unchanged.
313218585Sjkim             */
314306536Sjkim            if ((ACPI_GET_DESCRIPTOR_TYPE (WalkState->Results->Results.ObjDesc[0]) ==
315306536Sjkim                    ACPI_DESC_TYPE_OPERAND) &&
316306536Sjkim                ((WalkState->Results->Results.ObjDesc [0])->Common.Type ==
317306536Sjkim                    ACPI_TYPE_LOCAL_REFERENCE) &&
318306536Sjkim                ((WalkState->Results->Results.ObjDesc [0])->Reference.Class !=
319306536Sjkim                    ACPI_REFCLASS_INDEX))
320218585Sjkim            {
321306536Sjkim                Status = AcpiExResolveToValue (
322306536Sjkim                    &WalkState->Results->Results.ObjDesc [0], WalkState);
323218585Sjkim                if (ACPI_FAILURE (Status))
324218585Sjkim                {
325218585Sjkim                    return (Status);
326218585Sjkim                }
327218585Sjkim            }
328218585Sjkim
329218585Sjkim            WalkState->ReturnDesc = WalkState->Results->Results.ObjDesc [0];
330218585Sjkim        }
331218585Sjkim        else
332218585Sjkim        {
333218585Sjkim            /* No return operand */
334218585Sjkim
335218585Sjkim            if (WalkState->NumOperands)
336218585Sjkim            {
337218585Sjkim                AcpiUtRemoveReference (WalkState->Operands [0]);
338218585Sjkim            }
339218585Sjkim
340306536Sjkim            WalkState->Operands[0] = NULL;
341306536Sjkim            WalkState->NumOperands = 0;
342306536Sjkim            WalkState->ReturnDesc = NULL;
343218585Sjkim        }
344218585Sjkim
345218585Sjkim
346218585Sjkim        ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
347218585Sjkim            "Completed RETURN_OP State=%p, RetVal=%p\n",
348218585Sjkim            WalkState, WalkState->ReturnDesc));
349218585Sjkim
350218585Sjkim        /* End the control method execution right now */
351218585Sjkim
352218585Sjkim        Status = AE_CTRL_TERMINATE;
353218585Sjkim        break;
354218585Sjkim
355218585Sjkim    case AML_NOOP_OP:
356218585Sjkim
357218585Sjkim        /* Just do nothing! */
358250838Sjkim
359218585Sjkim        break;
360218585Sjkim
361218585Sjkim    case AML_BREAK_POINT_OP:
362218585Sjkim
363306536Sjkim        AcpiDbSignalBreakPoint (WalkState);
364218585Sjkim
365218585Sjkim        /* Call to the OSL in case OS wants a piece of the action */
366218585Sjkim
367218585Sjkim        Status = AcpiOsSignal (ACPI_SIGNAL_BREAKPOINT,
368306536Sjkim            "Executed AML Breakpoint opcode");
369218585Sjkim        break;
370218585Sjkim
371218585Sjkim    case AML_BREAK_OP:
372218585Sjkim    case AML_CONTINUE_OP: /* ACPI 2.0 */
373218585Sjkim
374218585Sjkim        /* Pop and delete control states until we find a while */
375218585Sjkim
376218585Sjkim        while (WalkState->ControlState &&
377218585Sjkim                (WalkState->ControlState->Control.Opcode != AML_WHILE_OP))
378218585Sjkim        {
379218585Sjkim            ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
380218585Sjkim            AcpiUtDeleteGenericState (ControlState);
381218585Sjkim        }
382218585Sjkim
383218585Sjkim        /* No while found? */
384218585Sjkim
385218585Sjkim        if (!WalkState->ControlState)
386218585Sjkim        {
387218585Sjkim            return (AE_AML_NO_WHILE);
388218585Sjkim        }
389218585Sjkim
390218585Sjkim        /* Was: WalkState->AmlLastWhile = WalkState->ControlState->Control.AmlPredicateStart; */
391218585Sjkim
392306536Sjkim        WalkState->AmlLastWhile =
393306536Sjkim            WalkState->ControlState->Control.PackageEnd;
394218585Sjkim
395218585Sjkim        /* Return status depending on opcode */
396218585Sjkim
397218585Sjkim        if (Op->Common.AmlOpcode == AML_BREAK_OP)
398218585Sjkim        {
399218585Sjkim            Status = AE_CTRL_BREAK;
400218585Sjkim        }
401218585Sjkim        else
402218585Sjkim        {
403218585Sjkim            Status = AE_CTRL_CONTINUE;
404218585Sjkim        }
405218585Sjkim        break;
406218585Sjkim
407218585Sjkim    default:
408218585Sjkim
409218585Sjkim        ACPI_ERROR ((AE_INFO, "Unknown control opcode=0x%X Op=%p",
410218585Sjkim            Op->Common.AmlOpcode, Op));
411218585Sjkim
412218585Sjkim        Status = AE_AML_BAD_OPCODE;
413218585Sjkim        break;
414218585Sjkim    }
415218585Sjkim
416218585Sjkim    return (Status);
417218585Sjkim}
418