1 /****************************************************************************** 2 * 3 * Module Name: aslmethod.c - Control method analysis walk 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2014, Intel Corp. 9 * All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions, and the following disclaimer, 16 * without modification. 17 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 18 * substantially similar to the "NO WARRANTY" disclaimer below 19 * ("Disclaimer") and any redistribution must be conditioned upon 20 * including a substantially similar Disclaimer requirement for further 21 * binary redistribution. 22 * 3. Neither the names of the above-listed copyright holders nor the names 23 * of any contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * Alternatively, this software may be distributed under the terms of the 27 * GNU General Public License ("GPL") version 2 as published by the Free 28 * Software Foundation. 29 * 30 * NO WARRANTY 31 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR 34 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 35 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 40 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 41 * POSSIBILITY OF SUCH DAMAGES. 42 */ 43 44 45 #include "aslcompiler.h" 46 #include "aslcompiler.y.h" 47 #include "acparser.h" 48 #include "amlcode.h" 49 50 51 #define _COMPONENT ACPI_COMPILER 52 ACPI_MODULE_NAME ("aslmethod") 53 54 55 /* Local prototypes */ 56 57 void 58 MtCheckNamedObjectInMethod ( 59 ACPI_PARSE_OBJECT *Op, 60 ASL_METHOD_INFO *MethodInfo); 61 62 63 /******************************************************************************* 64 * 65 * FUNCTION: MtMethodAnalysisWalkBegin 66 * 67 * PARAMETERS: ASL_WALK_CALLBACK 68 * 69 * RETURN: Status 70 * 71 * DESCRIPTION: Descending callback for the analysis walk. Check methods for: 72 * 1) Initialized local variables 73 * 2) Valid arguments 74 * 3) Return types 75 * 76 ******************************************************************************/ 77 78 ACPI_STATUS 79 MtMethodAnalysisWalkBegin ( 80 ACPI_PARSE_OBJECT *Op, 81 UINT32 Level, 82 void *Context) 83 { 84 ASL_ANALYSIS_WALK_INFO *WalkInfo = (ASL_ANALYSIS_WALK_INFO *) Context; 85 ASL_METHOD_INFO *MethodInfo = WalkInfo->MethodStack; 86 ACPI_PARSE_OBJECT *Next; 87 UINT32 RegisterNumber; 88 UINT32 i; 89 char LocalName[] = "Local0"; 90 char ArgName[] = "Arg0"; 91 ACPI_PARSE_OBJECT *ArgNode; 92 ACPI_PARSE_OBJECT *NextType; 93 ACPI_PARSE_OBJECT *NextParamType; 94 UINT8 ActualArgs = 0; 95 96 97 switch (Op->Asl.ParseOpcode) 98 { 99 case PARSEOP_METHOD: 100 101 TotalMethods++; 102 103 /* Create and init method info */ 104 105 MethodInfo = UtLocalCalloc (sizeof (ASL_METHOD_INFO)); 106 MethodInfo->Next = WalkInfo->MethodStack; 107 MethodInfo->Op = Op; 108 109 WalkInfo->MethodStack = MethodInfo; 110 111 /* Special handling for _PRP, must have a _HID also */ 112 113 if (!ACPI_STRCMP (METHOD_NAME__PRP, Op->Asl.NameSeg)) 114 { 115 if (!ApFindNameInScope (METHOD_NAME__HID, Op)) 116 { 117 AslError (ASL_WARNING, ASL_MSG_MISSING_DEPENDENCY, Op, 118 "_PRP requires _HID in same scope"); 119 } 120 } 121 122 /* Get the name node */ 123 124 Next = Op->Asl.Child; 125 126 /* Get the NumArguments node */ 127 128 Next = Next->Asl.Next; 129 MethodInfo->NumArguments = (UINT8) 130 (((UINT8) Next->Asl.Value.Integer) & 0x07); 131 132 /* Get the SerializeRule and SyncLevel nodes, ignored here */ 133 134 Next = Next->Asl.Next; 135 MethodInfo->ShouldBeSerialized = (UINT8) Next->Asl.Value.Integer; 136 137 Next = Next->Asl.Next; 138 ArgNode = Next; 139 140 /* Get the ReturnType node */ 141 142 Next = Next->Asl.Next; 143 144 NextType = Next->Asl.Child; 145 while (NextType) 146 { 147 /* Get and map each of the ReturnTypes */ 148 149 MethodInfo->ValidReturnTypes |= AnMapObjTypeToBtype (NextType); 150 NextType->Asl.ParseOpcode = PARSEOP_DEFAULT_ARG; 151 NextType = NextType->Asl.Next; 152 } 153 154 /* Get the ParameterType node */ 155 156 Next = Next->Asl.Next; 157 158 NextType = Next->Asl.Child; 159 while (NextType) 160 { 161 if (NextType->Asl.ParseOpcode == PARSEOP_DEFAULT_ARG) 162 { 163 NextParamType = NextType->Asl.Child; 164 while (NextParamType) 165 { 166 MethodInfo->ValidArgTypes[ActualArgs] |= AnMapObjTypeToBtype (NextParamType); 167 NextParamType->Asl.ParseOpcode = PARSEOP_DEFAULT_ARG; 168 NextParamType = NextParamType->Asl.Next; 169 } 170 } 171 else 172 { 173 MethodInfo->ValidArgTypes[ActualArgs] = 174 AnMapObjTypeToBtype (NextType); 175 NextType->Asl.ParseOpcode = PARSEOP_DEFAULT_ARG; 176 ActualArgs++; 177 } 178 179 NextType = NextType->Asl.Next; 180 } 181 182 if ((MethodInfo->NumArguments) && 183 (MethodInfo->NumArguments != ActualArgs)) 184 { 185 /* error: Param list did not match number of args */ 186 } 187 188 /* Allow numarguments == 0 for Function() */ 189 190 if ((!MethodInfo->NumArguments) && (ActualArgs)) 191 { 192 MethodInfo->NumArguments = ActualArgs; 193 ArgNode->Asl.Value.Integer |= ActualArgs; 194 } 195 196 /* 197 * Actual arguments are initialized at method entry. 198 * All other ArgX "registers" can be used as locals, so we 199 * track their initialization. 200 */ 201 for (i = 0; i < MethodInfo->NumArguments; i++) 202 { 203 MethodInfo->ArgInitialized[i] = TRUE; 204 } 205 break; 206 207 case PARSEOP_METHODCALL: 208 209 if (MethodInfo && 210 (Op->Asl.Node == MethodInfo->Op->Asl.Node)) 211 { 212 AslError (ASL_REMARK, ASL_MSG_RECURSION, Op, Op->Asl.ExternalName); 213 } 214 break; 215 216 case PARSEOP_LOCAL0: 217 case PARSEOP_LOCAL1: 218 case PARSEOP_LOCAL2: 219 case PARSEOP_LOCAL3: 220 case PARSEOP_LOCAL4: 221 case PARSEOP_LOCAL5: 222 case PARSEOP_LOCAL6: 223 case PARSEOP_LOCAL7: 224 225 if (!MethodInfo) 226 { 227 /* 228 * Local was used outside a control method, or there was an error 229 * in the method declaration. 230 */ 231 AslError (ASL_REMARK, ASL_MSG_LOCAL_OUTSIDE_METHOD, Op, Op->Asl.ExternalName); 232 return (AE_ERROR); 233 } 234 235 RegisterNumber = (Op->Asl.AmlOpcode & 0x000F); 236 237 /* 238 * If the local is being used as a target, mark the local 239 * initialized 240 */ 241 if (Op->Asl.CompileFlags & NODE_IS_TARGET) 242 { 243 MethodInfo->LocalInitialized[RegisterNumber] = TRUE; 244 } 245 246 /* 247 * Otherwise, this is a reference, check if the local 248 * has been previously initialized. 249 * 250 * The only operator that accepts an uninitialized value is ObjectType() 251 */ 252 else if ((!MethodInfo->LocalInitialized[RegisterNumber]) && 253 (Op->Asl.Parent->Asl.ParseOpcode != PARSEOP_OBJECTTYPE)) 254 { 255 LocalName[strlen (LocalName) -1] = (char) (RegisterNumber + 0x30); 256 AslError (ASL_ERROR, ASL_MSG_LOCAL_INIT, Op, LocalName); 257 } 258 break; 259 260 case PARSEOP_ARG0: 261 case PARSEOP_ARG1: 262 case PARSEOP_ARG2: 263 case PARSEOP_ARG3: 264 case PARSEOP_ARG4: 265 case PARSEOP_ARG5: 266 case PARSEOP_ARG6: 267 268 if (!MethodInfo) 269 { 270 /* 271 * Arg was used outside a control method, or there was an error 272 * in the method declaration. 273 */ 274 AslError (ASL_REMARK, ASL_MSG_LOCAL_OUTSIDE_METHOD, Op, Op->Asl.ExternalName); 275 return (AE_ERROR); 276 } 277 278 RegisterNumber = (Op->Asl.AmlOpcode & 0x000F) - 8; 279 ArgName[strlen (ArgName) -1] = (char) (RegisterNumber + 0x30); 280 281 /* 282 * If the Arg is being used as a target, mark the local 283 * initialized 284 */ 285 if (Op->Asl.CompileFlags & NODE_IS_TARGET) 286 { 287 MethodInfo->ArgInitialized[RegisterNumber] = TRUE; 288 } 289 290 /* 291 * Otherwise, this is a reference, check if the Arg 292 * has been previously initialized. 293 * 294 * The only operator that accepts an uninitialized value is ObjectType() 295 */ 296 else if ((!MethodInfo->ArgInitialized[RegisterNumber]) && 297 (Op->Asl.Parent->Asl.ParseOpcode != PARSEOP_OBJECTTYPE)) 298 { 299 AslError (ASL_ERROR, ASL_MSG_ARG_INIT, Op, ArgName); 300 } 301 302 /* Flag this arg if it is not a "real" argument to the method */ 303 304 if (RegisterNumber >= MethodInfo->NumArguments) 305 { 306 AslError (ASL_REMARK, ASL_MSG_NOT_PARAMETER, Op, ArgName); 307 } 308 break; 309 310 case PARSEOP_RETURN: 311 312 if (!MethodInfo) 313 { 314 /* 315 * Probably was an error in the method declaration, 316 * no additional error here 317 */ 318 ACPI_WARNING ((AE_INFO, "%p, No parent method", Op)); 319 return (AE_ERROR); 320 } 321 322 /* 323 * A child indicates a possible return value. A simple Return or 324 * Return() is marked with NODE_IS_NULL_RETURN by the parser so 325 * that it is not counted as a "real" return-with-value, although 326 * the AML code that is actually emitted is Return(0). The AML 327 * definition of Return has a required parameter, so we are 328 * forced to convert a null return to Return(0). 329 */ 330 if ((Op->Asl.Child) && 331 (Op->Asl.Child->Asl.ParseOpcode != PARSEOP_DEFAULT_ARG) && 332 (!(Op->Asl.Child->Asl.CompileFlags & NODE_IS_NULL_RETURN))) 333 { 334 MethodInfo->NumReturnWithValue++; 335 } 336 else 337 { 338 MethodInfo->NumReturnNoValue++; 339 } 340 break; 341 342 case PARSEOP_BREAK: 343 case PARSEOP_CONTINUE: 344 345 Next = Op->Asl.Parent; 346 while (Next) 347 { 348 if (Next->Asl.ParseOpcode == PARSEOP_WHILE) 349 { 350 break; 351 } 352 Next = Next->Asl.Parent; 353 } 354 355 if (!Next) 356 { 357 AslError (ASL_ERROR, ASL_MSG_NO_WHILE, Op, NULL); 358 } 359 break; 360 361 case PARSEOP_STALL: 362 363 /* We can range check if the argument is an integer */ 364 365 if ((Op->Asl.Child->Asl.ParseOpcode == PARSEOP_INTEGER) && 366 (Op->Asl.Child->Asl.Value.Integer > ACPI_UINT8_MAX)) 367 { 368 AslError (ASL_ERROR, ASL_MSG_INVALID_TIME, Op, NULL); 369 } 370 break; 371 372 case PARSEOP_DEVICE: 373 case PARSEOP_EVENT: 374 case PARSEOP_MUTEX: 375 case PARSEOP_OPERATIONREGION: 376 case PARSEOP_POWERRESOURCE: 377 case PARSEOP_PROCESSOR: 378 case PARSEOP_THERMALZONE: 379 380 /* 381 * The first operand is a name to be created in the namespace. 382 * Check against the reserved list. 383 */ 384 i = ApCheckForPredefinedName (Op, Op->Asl.NameSeg); 385 if (i < ACPI_VALID_RESERVED_NAME_MAX) 386 { 387 AslError (ASL_ERROR, ASL_MSG_RESERVED_USE, Op, Op->Asl.ExternalName); 388 } 389 break; 390 391 case PARSEOP_NAME: 392 393 /* Typecheck any predefined names statically defined with Name() */ 394 395 ApCheckForPredefinedObject (Op, Op->Asl.NameSeg); 396 397 /* Special typechecking for _HID */ 398 399 if (!ACPI_STRCMP (METHOD_NAME__HID, Op->Asl.NameSeg)) 400 { 401 Next = Op->Asl.Child->Asl.Next; 402 AnCheckId (Next, ASL_TYPE_HID); 403 } 404 405 /* Special typechecking for _CID */ 406 407 else if (!ACPI_STRCMP (METHOD_NAME__CID, Op->Asl.NameSeg)) 408 { 409 Next = Op->Asl.Child->Asl.Next; 410 411 if ((Next->Asl.ParseOpcode == PARSEOP_PACKAGE) || 412 (Next->Asl.ParseOpcode == PARSEOP_VAR_PACKAGE)) 413 { 414 Next = Next->Asl.Child; 415 while (Next) 416 { 417 AnCheckId (Next, ASL_TYPE_CID); 418 Next = Next->Asl.Next; 419 } 420 } 421 else 422 { 423 AnCheckId (Next, ASL_TYPE_CID); 424 } 425 } 426 427 else if (!ACPI_STRCMP (METHOD_NAME__PRP, Op->Asl.NameSeg)) 428 { 429 if (!ApFindNameInScope (METHOD_NAME__HID, Op)) 430 { 431 AslError (ASL_WARNING, ASL_MSG_MISSING_DEPENDENCY, Op, 432 "_PRP requires _HID in same scope"); 433 } 434 } 435 436 break; 437 438 default: 439 440 break; 441 } 442 443 /* Check for named object creation within a non-serialized method */ 444 445 MtCheckNamedObjectInMethod (Op, MethodInfo); 446 return (AE_OK); 447 } 448 449 450 /******************************************************************************* 451 * 452 * FUNCTION: MtCheckNamedObjectInMethod 453 * 454 * PARAMETERS: Op - Current parser op 455 * MethodInfo - Info for method being parsed 456 * 457 * RETURN: None 458 * 459 * DESCRIPTION: Detect if a non-serialized method is creating a named object, 460 * which could possibly cause problems if two threads execute 461 * the method concurrently. Emit a remark in this case. 462 * 463 ******************************************************************************/ 464 465 void 466 MtCheckNamedObjectInMethod ( 467 ACPI_PARSE_OBJECT *Op, 468 ASL_METHOD_INFO *MethodInfo) 469 { 470 const ACPI_OPCODE_INFO *OpInfo; 471 472 473 /* We don't care about actual method declarations */ 474 475 if (Op->Asl.AmlOpcode == AML_METHOD_OP) 476 { 477 return; 478 } 479 480 /* Determine if we are creating a named object */ 481 482 OpInfo = AcpiPsGetOpcodeInfo (Op->Asl.AmlOpcode); 483 if (OpInfo->Class == AML_CLASS_NAMED_OBJECT) 484 { 485 /* 486 * If we have a named object created within a non-serialized method, 487 * emit a remark that the method should be serialized. 488 * 489 * Reason: If a thread blocks within the method for any reason, and 490 * another thread enters the method, the method will fail because an 491 * attempt will be made to create the same object twice. 492 */ 493 if (MethodInfo && !MethodInfo->ShouldBeSerialized) 494 { 495 AslError (ASL_REMARK, ASL_MSG_SERIALIZED_REQUIRED, MethodInfo->Op, 496 "due to creation of named objects within"); 497 498 /* Emit message only ONCE per method */ 499 500 MethodInfo->ShouldBeSerialized = TRUE; 501 } 502 } 503 } 504 505 506 /******************************************************************************* 507 * 508 * FUNCTION: MtMethodAnalysisWalkEnd 509 * 510 * PARAMETERS: ASL_WALK_CALLBACK 511 * 512 * RETURN: Status 513 * 514 * DESCRIPTION: Ascending callback for analysis walk. Complete method 515 * return analysis. 516 * 517 ******************************************************************************/ 518 519 ACPI_STATUS 520 MtMethodAnalysisWalkEnd ( 521 ACPI_PARSE_OBJECT *Op, 522 UINT32 Level, 523 void *Context) 524 { 525 ASL_ANALYSIS_WALK_INFO *WalkInfo = (ASL_ANALYSIS_WALK_INFO *) Context; 526 ASL_METHOD_INFO *MethodInfo = WalkInfo->MethodStack; 527 528 529 switch (Op->Asl.ParseOpcode) 530 { 531 case PARSEOP_METHOD: 532 case PARSEOP_RETURN: 533 534 if (!MethodInfo) 535 { 536 printf ("No method info for method! [%s]\n", Op->Asl.Namepath); 537 AslError (ASL_ERROR, ASL_MSG_COMPILER_INTERNAL, Op, 538 "No method info for this method"); 539 540 CmCleanupAndExit (); 541 return (AE_AML_INTERNAL); 542 } 543 break; 544 545 default: 546 547 break; 548 } 549 550 switch (Op->Asl.ParseOpcode) 551 { 552 case PARSEOP_METHOD: 553 554 WalkInfo->MethodStack = MethodInfo->Next; 555 556 /* 557 * Check if there is no return statement at the end of the 558 * method AND we can actually get there -- i.e., the execution 559 * of the method can possibly terminate without a return statement. 560 */ 561 if ((!AnLastStatementIsReturn (Op)) && 562 (!(Op->Asl.CompileFlags & NODE_HAS_NO_EXIT))) 563 { 564 /* 565 * No return statement, and execution can possibly exit 566 * via this path. This is equivalent to Return () 567 */ 568 MethodInfo->NumReturnNoValue++; 569 } 570 571 /* 572 * Check for case where some return statements have a return value 573 * and some do not. Exit without a return statement is a return with 574 * no value 575 */ 576 if (MethodInfo->NumReturnNoValue && 577 MethodInfo->NumReturnWithValue) 578 { 579 AslError (ASL_WARNING, ASL_MSG_RETURN_TYPES, Op, 580 Op->Asl.ExternalName); 581 } 582 583 /* 584 * If there are any RETURN() statements with no value, or there is a 585 * control path that allows the method to exit without a return value, 586 * we mark the method as a method that does not return a value. This 587 * knowledge can be used to check method invocations that expect a 588 * returned value. 589 */ 590 if (MethodInfo->NumReturnNoValue) 591 { 592 if (MethodInfo->NumReturnWithValue) 593 { 594 Op->Asl.CompileFlags |= NODE_METHOD_SOME_NO_RETVAL; 595 } 596 else 597 { 598 Op->Asl.CompileFlags |= NODE_METHOD_NO_RETVAL; 599 } 600 } 601 602 /* 603 * Check predefined method names for correct return behavior 604 * and correct number of arguments. Also, some special checks 605 * For GPE and _REG methods. 606 */ 607 if (ApCheckForPredefinedMethod (Op, MethodInfo)) 608 { 609 /* Special check for two names like _L01 and _E01 in same scope */ 610 611 ApCheckForGpeNameConflict (Op); 612 613 /* 614 * Special check for _REG: Must have an operation region definition 615 * within the same scope! 616 */ 617 ApCheckRegMethod (Op); 618 } 619 620 ACPI_FREE (MethodInfo); 621 break; 622 623 case PARSEOP_NAME: 624 625 /* Special check for two names like _L01 and _E01 in same scope */ 626 627 ApCheckForGpeNameConflict (Op); 628 break; 629 630 case PARSEOP_RETURN: 631 632 /* 633 * If the parent is a predefined method name, attempt to typecheck 634 * the return value. Only static types can be validated. 635 */ 636 ApCheckPredefinedReturnValue (Op, MethodInfo); 637 638 /* 639 * The parent block does not "exit" and continue execution -- the 640 * method is terminated here with the Return() statement. 641 */ 642 Op->Asl.Parent->Asl.CompileFlags |= NODE_HAS_NO_EXIT; 643 644 /* Used in the "typing" pass later */ 645 646 Op->Asl.ParentMethod = MethodInfo->Op; 647 648 /* 649 * If there is a peer node after the return statement, then this 650 * node is unreachable code -- i.e., it won't be executed because of 651 * the preceding Return() statement. 652 */ 653 if (Op->Asl.Next) 654 { 655 AslError (ASL_WARNING, ASL_MSG_UNREACHABLE_CODE, Op->Asl.Next, NULL); 656 } 657 break; 658 659 case PARSEOP_IF: 660 661 if ((Op->Asl.CompileFlags & NODE_HAS_NO_EXIT) && 662 (Op->Asl.Next) && 663 (Op->Asl.Next->Asl.ParseOpcode == PARSEOP_ELSE)) 664 { 665 /* 666 * This IF has a corresponding ELSE. The IF block has no exit, 667 * (it contains an unconditional Return) 668 * mark the ELSE block to remember this fact. 669 */ 670 Op->Asl.Next->Asl.CompileFlags |= NODE_IF_HAS_NO_EXIT; 671 } 672 break; 673 674 case PARSEOP_ELSE: 675 676 if ((Op->Asl.CompileFlags & NODE_HAS_NO_EXIT) && 677 (Op->Asl.CompileFlags & NODE_IF_HAS_NO_EXIT)) 678 { 679 /* 680 * This ELSE block has no exit and the corresponding IF block 681 * has no exit either. Therefore, the parent node has no exit. 682 */ 683 Op->Asl.Parent->Asl.CompileFlags |= NODE_HAS_NO_EXIT; 684 } 685 break; 686 687 688 default: 689 690 if ((Op->Asl.CompileFlags & NODE_HAS_NO_EXIT) && 691 (Op->Asl.Parent)) 692 { 693 /* If this node has no exit, then the parent has no exit either */ 694 695 Op->Asl.Parent->Asl.CompileFlags |= NODE_HAS_NO_EXIT; 696 } 697 break; 698 } 699 700 return (AE_OK); 701 } 702