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