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