1 /******************************************************************************
   2  *
   3  * Module Name: dscontrol - Support for execution control opcodes -
   4  *                          if/else/while/return
   5  *
   6  *****************************************************************************/
   7 
   8 /*
   9  * Copyright (C) 2000 - 2014, 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 #define __DSCONTROL_C__
  46 
  47 #include "acpi.h"
  48 #include "accommon.h"
  49 #include "amlcode.h"
  50 #include "acdispat.h"
  51 #include "acinterp.h"
  52 
  53 #define _COMPONENT          ACPI_DISPATCHER
  54         ACPI_MODULE_NAME    ("dscontrol")
  55 
  56 
  57 /*******************************************************************************
  58  *
  59  * FUNCTION:    AcpiDsExecBeginControlOp
  60  *
  61  * PARAMETERS:  WalkList        - The list that owns the walk stack
  62  *              Op              - The control Op
  63  *
  64  * RETURN:      Status
  65  *
  66  * DESCRIPTION: Handles all control ops encountered during control method
  67  *              execution.
  68  *
  69  ******************************************************************************/
  70 
  71 ACPI_STATUS
  72 AcpiDsExecBeginControlOp (
  73     ACPI_WALK_STATE         *WalkState,
  74     ACPI_PARSE_OBJECT       *Op)
  75 {
  76     ACPI_STATUS             Status = AE_OK;
  77     ACPI_GENERIC_STATE      *ControlState;
  78 
  79 
  80     ACPI_FUNCTION_NAME (DsExecBeginControlOp);
  81 
  82 
  83     ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Op=%p Opcode=%2.2X State=%p\n",
  84         Op, Op->Common.AmlOpcode, WalkState));
  85 
  86     switch (Op->Common.AmlOpcode)
  87     {
  88     case AML_WHILE_OP:
  89         /*
  90          * If this is an additional iteration of a while loop, continue.
  91          * There is no need to allocate a new control state.
  92          */
  93         if (WalkState->ControlState)
  94         {
  95             if (WalkState->ControlState->Control.AmlPredicateStart ==
  96                 (WalkState->ParserState.Aml - 1))
  97             {
  98                 /* Reset the state to start-of-loop */
  99 
 100                 WalkState->ControlState->Common.State =
 101                     ACPI_CONTROL_CONDITIONAL_EXECUTING;
 102                 break;
 103             }
 104         }
 105 
 106         /*lint -fallthrough */
 107 
 108     case AML_IF_OP:
 109         /*
 110          * IF/WHILE: Create a new control state to manage these
 111          * constructs. We need to manage these as a stack, in order
 112          * to handle nesting.
 113          */
 114         ControlState = AcpiUtCreateControlState ();
 115         if (!ControlState)
 116         {
 117             Status = AE_NO_MEMORY;
 118             break;
 119         }
 120         /*
 121          * Save a pointer to the predicate for multiple executions
 122          * of a loop
 123          */
 124         ControlState->Control.AmlPredicateStart = WalkState->ParserState.Aml - 1;
 125         ControlState->Control.PackageEnd = WalkState->ParserState.PkgEnd;
 126         ControlState->Control.Opcode = Op->Common.AmlOpcode;
 127 
 128 
 129         /* Push the control state on this walk's control stack */
 130 
 131         AcpiUtPushGenericState (&WalkState->ControlState, ControlState);
 132         break;
 133 
 134     case AML_ELSE_OP:
 135 
 136         /* Predicate is in the state object */
 137         /* If predicate is true, the IF was executed, ignore ELSE part */
 138 
 139         if (WalkState->LastPredicate)
 140         {
 141             Status = AE_CTRL_TRUE;
 142         }
 143 
 144         break;
 145 
 146     case AML_RETURN_OP:
 147 
 148         break;
 149 
 150     default:
 151 
 152         break;
 153     }
 154 
 155     return (Status);
 156 }
 157 
 158 
 159 /*******************************************************************************
 160  *
 161  * FUNCTION:    AcpiDsExecEndControlOp
 162  *
 163  * PARAMETERS:  WalkList        - The list that owns the walk stack
 164  *              Op              - The control Op
 165  *
 166  * RETURN:      Status
 167  *
 168  * DESCRIPTION: Handles all control ops encountered during control method
 169  *              execution.
 170  *
 171  ******************************************************************************/
 172 
 173 ACPI_STATUS
 174 AcpiDsExecEndControlOp (
 175     ACPI_WALK_STATE         *WalkState,
 176     ACPI_PARSE_OBJECT       *Op)
 177 {
 178     ACPI_STATUS             Status = AE_OK;
 179     ACPI_GENERIC_STATE      *ControlState;
 180 
 181 
 182     ACPI_FUNCTION_NAME (DsExecEndControlOp);
 183 
 184 
 185     switch (Op->Common.AmlOpcode)
 186     {
 187     case AML_IF_OP:
 188 
 189         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[IF_OP] Op=%p\n", Op));
 190 
 191         /*
 192          * Save the result of the predicate in case there is an
 193          * ELSE to come
 194          */
 195         WalkState->LastPredicate =
 196             (BOOLEAN) WalkState->ControlState->Common.Value;
 197 
 198         /*
 199          * Pop the control state that was created at the start
 200          * of the IF and free it
 201          */
 202         ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
 203         AcpiUtDeleteGenericState (ControlState);
 204         break;
 205 
 206     case AML_ELSE_OP:
 207 
 208         break;
 209 
 210     case AML_WHILE_OP:
 211 
 212         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", Op));
 213 
 214         ControlState = WalkState->ControlState;
 215         if (ControlState->Common.Value)
 216         {
 217             /* Predicate was true, the body of the loop was just executed */
 218 
 219             /*
 220              * This loop counter mechanism allows the interpreter to escape
 221              * possibly infinite loops. This can occur in poorly written AML
 222              * when the hardware does not respond within a while loop and the
 223              * loop does not implement a timeout.
 224              */
 225             ControlState->Control.LoopCount++;
 226             if (ControlState->Control.LoopCount > ACPI_MAX_LOOP_ITERATIONS)
 227             {
 228                 Status = AE_AML_INFINITE_LOOP;
 229                 break;
 230             }
 231 
 232             /*
 233              * Go back and evaluate the predicate and maybe execute the loop
 234              * another time
 235              */
 236             Status = AE_CTRL_PENDING;
 237             WalkState->AmlLastWhile = ControlState->Control.AmlPredicateStart;
 238             break;
 239         }
 240 
 241         /* Predicate was false, terminate this while loop */
 242 
 243         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
 244             "[WHILE_OP] termination! Op=%p\n",Op));
 245 
 246         /* Pop this control state and free it */
 247 
 248         ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
 249         AcpiUtDeleteGenericState (ControlState);
 250         break;
 251 
 252     case AML_RETURN_OP:
 253 
 254         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
 255             "[RETURN_OP] Op=%p Arg=%p\n",Op, Op->Common.Value.Arg));
 256 
 257         /*
 258          * One optional operand -- the return value
 259          * It can be either an immediate operand or a result that
 260          * has been bubbled up the tree
 261          */
 262         if (Op->Common.Value.Arg)
 263         {
 264             /* Since we have a real Return(), delete any implicit return */
 265 
 266             AcpiDsClearImplicitReturn (WalkState);
 267 
 268             /* Return statement has an immediate operand */
 269 
 270             Status = AcpiDsCreateOperands (WalkState, Op->Common.Value.Arg);
 271             if (ACPI_FAILURE (Status))
 272             {
 273                 return (Status);
 274             }
 275 
 276             /*
 277              * If value being returned is a Reference (such as
 278              * an arg or local), resolve it now because it may
 279              * cease to exist at the end of the method.
 280              */
 281             Status = AcpiExResolveToValue (&WalkState->Operands [0], WalkState);
 282             if (ACPI_FAILURE (Status))
 283             {
 284                 return (Status);
 285             }
 286 
 287             /*
 288              * Get the return value and save as the last result
 289              * value. This is the only place where WalkState->ReturnDesc
 290              * is set to anything other than zero!
 291              */
 292             WalkState->ReturnDesc = WalkState->Operands[0];
 293         }
 294         else if (WalkState->ResultCount)
 295         {
 296             /* Since we have a real Return(), delete any implicit return */
 297 
 298             AcpiDsClearImplicitReturn (WalkState);
 299 
 300             /*
 301              * The return value has come from a previous calculation.
 302              *
 303              * If value being returned is a Reference (such as
 304              * an arg or local), resolve it now because it may
 305              * cease to exist at the end of the method.
 306              *
 307              * Allow references created by the Index operator to return
 308              * unchanged.
 309              */
 310             if ((ACPI_GET_DESCRIPTOR_TYPE (WalkState->Results->Results.ObjDesc[0]) == ACPI_DESC_TYPE_OPERAND) &&
 311                 ((WalkState->Results->Results.ObjDesc [0])->Common.Type == ACPI_TYPE_LOCAL_REFERENCE) &&
 312                 ((WalkState->Results->Results.ObjDesc [0])->Reference.Class != ACPI_REFCLASS_INDEX))
 313             {
 314                 Status = AcpiExResolveToValue (&WalkState->Results->Results.ObjDesc [0], WalkState);
 315                 if (ACPI_FAILURE (Status))
 316                 {
 317                     return (Status);
 318                 }
 319             }
 320 
 321             WalkState->ReturnDesc = WalkState->Results->Results.ObjDesc [0];
 322         }
 323         else
 324         {
 325             /* No return operand */
 326 
 327             if (WalkState->NumOperands)
 328             {
 329                 AcpiUtRemoveReference (WalkState->Operands [0]);
 330             }
 331 
 332             WalkState->Operands [0]     = NULL;
 333             WalkState->NumOperands      = 0;
 334             WalkState->ReturnDesc       = NULL;
 335         }
 336 
 337 
 338         ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH,
 339             "Completed RETURN_OP State=%p, RetVal=%p\n",
 340             WalkState, WalkState->ReturnDesc));
 341 
 342         /* End the control method execution right now */
 343 
 344         Status = AE_CTRL_TERMINATE;
 345         break;
 346 
 347     case AML_NOOP_OP:
 348 
 349         /* Just do nothing! */
 350 
 351         break;
 352 
 353     case AML_BREAK_POINT_OP:
 354 
 355         /*
 356          * Set the single-step flag. This will cause the debugger (if present)
 357          * to break to the console within the AML debugger at the start of the
 358          * next AML instruction.
 359          */
 360         ACPI_DEBUGGER_EXEC (
 361             AcpiGbl_CmSingleStep = TRUE);
 362         ACPI_DEBUGGER_EXEC (
 363             AcpiOsPrintf ("**break** Executed AML BreakPoint opcode\n"));
 364 
 365         /* Call to the OSL in case OS wants a piece of the action */
 366 
 367         Status = AcpiOsSignal (ACPI_SIGNAL_BREAKPOINT,
 368                     "Executed AML Breakpoint opcode");
 369         break;
 370 
 371     case AML_BREAK_OP:
 372     case AML_CONTINUE_OP: /* ACPI 2.0 */
 373 
 374         /* Pop and delete control states until we find a while */
 375 
 376         while (WalkState->ControlState &&
 377                 (WalkState->ControlState->Control.Opcode != AML_WHILE_OP))
 378         {
 379             ControlState = AcpiUtPopGenericState (&WalkState->ControlState);
 380             AcpiUtDeleteGenericState (ControlState);
 381         }
 382 
 383         /* No while found? */
 384 
 385         if (!WalkState->ControlState)
 386         {
 387             return (AE_AML_NO_WHILE);
 388         }
 389 
 390         /* Was: WalkState->AmlLastWhile = WalkState->ControlState->Control.AmlPredicateStart; */
 391 
 392         WalkState->AmlLastWhile = WalkState->ControlState->Control.PackageEnd;
 393 
 394         /* Return status depending on opcode */
 395 
 396         if (Op->Common.AmlOpcode == AML_BREAK_OP)
 397         {
 398             Status = AE_CTRL_BREAK;
 399         }
 400         else
 401         {
 402             Status = AE_CTRL_CONTINUE;
 403         }
 404         break;
 405 
 406     default:
 407 
 408         ACPI_ERROR ((AE_INFO, "Unknown control opcode=0x%X Op=%p",
 409             Op->Common.AmlOpcode, Op));
 410 
 411         Status = AE_AML_BAD_OPCODE;
 412         break;
 413     }
 414 
 415     return (Status);
 416 }