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