1 /****************************************************************************** 2 * 3 * Module Name: aslanalyze.c - Support functions for parse tree walks 4 * 5 *****************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2021, 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 MERCHANTABILITY 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 #include "aslcompiler.h" 45 #include "aslcompiler.y.h" 46 #include "acnamesp.h" 47 #include <string.h> 48 49 50 #define _COMPONENT ACPI_COMPILER 51 ACPI_MODULE_NAME ("aslanalyze") 52 53 54 /* Local Prototypes */ 55 56 static ACPI_STATUS 57 ApDeviceSubtreeWalk ( 58 ACPI_PARSE_OBJECT *Op, 59 UINT32 Level, 60 void *Context); 61 62 63 /******************************************************************************* 64 * 65 * FUNCTION: AnIsInternalMethod 66 * 67 * PARAMETERS: Op - Current op 68 * 69 * RETURN: Boolean 70 * 71 * DESCRIPTION: Check for an internal control method. 72 * 73 ******************************************************************************/ 74 75 BOOLEAN 76 AnIsInternalMethod ( 77 ACPI_PARSE_OBJECT *Op) 78 { 79 80 if ((!strcmp (Op->Asl.ExternalName, "\\_OSI")) || 81 (!strcmp (Op->Asl.ExternalName, "_OSI"))) 82 { 83 return (TRUE); 84 } 85 86 return (FALSE); 87 } 88 89 90 /******************************************************************************* 91 * 92 * FUNCTION: AnGetInternalMethodReturnType 93 * 94 * PARAMETERS: Op - Current op 95 * 96 * RETURN: Btype 97 * 98 * DESCRIPTION: Get the return type of an internal method 99 * 100 ******************************************************************************/ 101 102 UINT32 103 AnGetInternalMethodReturnType ( 104 ACPI_PARSE_OBJECT *Op) 105 { 106 107 if ((!strcmp (Op->Asl.ExternalName, "\\_OSI")) || 108 (!strcmp (Op->Asl.ExternalName, "_OSI"))) 109 { 110 return (ACPI_BTYPE_STRING); 111 } 112 113 return (0); 114 } 115 116 117 /******************************************************************************* 118 * 119 * FUNCTION: AnCheckId 120 * 121 * PARAMETERS: Op - Current parse op 122 * Type - HID or CID 123 * 124 * RETURN: None 125 * 126 * DESCRIPTION: Perform various checks on _HID and _CID strings. Only limited 127 * checks can be performed on _CID strings. 128 * 129 ******************************************************************************/ 130 131 void 132 AnCheckId ( 133 ACPI_PARSE_OBJECT *Op, 134 ACPI_NAME Type) 135 { 136 UINT32 i; 137 ACPI_SIZE Length; 138 139 140 /* Only care about string versions of _HID/_CID (integers are legal) */ 141 142 if (Op->Asl.ParseOpcode != PARSEOP_STRING_LITERAL) 143 { 144 return; 145 } 146 147 /* For both _HID and _CID, the string must be non-null */ 148 149 Length = strlen (Op->Asl.Value.String); 150 if (!Length) 151 { 152 AslError (ASL_ERROR, ASL_MSG_NULL_STRING, Op, NULL); 153 return; 154 } 155 156 /* 157 * One of the things we want to catch here is the use of a leading 158 * asterisk in the string -- an odd construct that certain platform 159 * manufacturers are fond of. Technically, a leading asterisk is OK 160 * for _CID, but a valid use of this has not been seen. 161 */ 162 if (*Op->Asl.Value.String == '*') 163 { 164 AslError (ASL_ERROR, ASL_MSG_LEADING_ASTERISK, 165 Op, Op->Asl.Value.String); 166 return; 167 } 168 169 /* _CID strings are bus-specific, no more checks can be performed */ 170 171 if (Type == ASL_TYPE_CID) 172 { 173 return; 174 } 175 176 /* For _HID, all characters must be alphanumeric */ 177 178 for (i = 0; Op->Asl.Value.String[i]; i++) 179 { 180 if (!isalnum ((int) Op->Asl.Value.String[i])) 181 { 182 AslError (ASL_ERROR, ASL_MSG_ALPHANUMERIC_STRING, 183 Op, Op->Asl.Value.String); 184 return; 185 } 186 } 187 188 /* 189 * _HID String must be one of these forms: 190 * 191 * "AAA####" A is an uppercase letter and # is a hex digit 192 * "ACPI####" # is a hex digit 193 * "NNNN####" N is an uppercase letter or decimal digit (0-9) 194 * # is a hex digit (ACPI 5.0) 195 */ 196 if ((Length < 7) || (Length > 8)) 197 { 198 AslError (ASL_ERROR, ASL_MSG_HID_LENGTH, 199 Op, Op->Asl.Value.String); 200 return; 201 } 202 203 /* _HID Length is valid (7 or 8), now check prefix (first 3 or 4 chars) */ 204 205 if (Length == 7) 206 { 207 /* AAA####: Ensure the alphabetic prefix is all uppercase */ 208 209 for (i = 0; i < 3; i++) 210 { 211 if (!isupper ((int) Op->Asl.Value.String[i])) 212 { 213 AslError (ASL_ERROR, ASL_MSG_UPPER_CASE, 214 Op, &Op->Asl.Value.String[i]); 215 return; 216 } 217 } 218 } 219 else /* Length == 8 */ 220 { 221 /* 222 * ACPI#### or NNNN####: 223 * Ensure the prefix contains only uppercase alpha or decimal digits 224 */ 225 for (i = 0; i < 4; i++) 226 { 227 if (!isupper ((int) Op->Asl.Value.String[i]) && 228 !isdigit ((int) Op->Asl.Value.String[i])) 229 { 230 AslError (ASL_ERROR, ASL_MSG_HID_PREFIX, 231 Op, &Op->Asl.Value.String[i]); 232 return; 233 } 234 } 235 } 236 237 /* Remaining characters (suffix) must be hex digits */ 238 239 for (; i < Length; i++) 240 { 241 if (!isxdigit ((int) Op->Asl.Value.String[i])) 242 { 243 AslError (ASL_ERROR, ASL_MSG_HID_SUFFIX, 244 Op, &Op->Asl.Value.String[i]); 245 break; 246 } 247 } 248 } 249 250 251 /******************************************************************************* 252 * 253 * FUNCTION: AnLastStatementIsReturn 254 * 255 * PARAMETERS: Op - A method parse node 256 * 257 * RETURN: TRUE if last statement is an ASL RETURN. False otherwise 258 * 259 * DESCRIPTION: Walk down the list of top level statements within a method 260 * to find the last one. Check if that last statement is in 261 * fact a RETURN statement. 262 * 263 ******************************************************************************/ 264 265 BOOLEAN 266 AnLastStatementIsReturn ( 267 ACPI_PARSE_OBJECT *Op) 268 { 269 ACPI_PARSE_OBJECT *Next; 270 271 272 /* Check if last statement is a return */ 273 274 Next = ASL_GET_CHILD_NODE (Op); 275 while (Next) 276 { 277 if ((!Next->Asl.Next) && 278 (Next->Asl.ParseOpcode == PARSEOP_RETURN)) 279 { 280 return (TRUE); 281 } 282 283 Next = ASL_GET_PEER_NODE (Next); 284 } 285 286 return (FALSE); 287 } 288 289 290 /******************************************************************************* 291 * 292 * FUNCTION: AnCheckMethodReturnValue 293 * 294 * PARAMETERS: Op - Parent 295 * OpInfo - Parent info 296 * ArgOp - Method invocation op 297 * RequiredBtypes - What caller requires 298 * ThisNodeBtype - What this node returns (if anything) 299 * 300 * RETURN: None 301 * 302 * DESCRIPTION: Check a method invocation for 1) A return value and if it does 303 * in fact return a value, 2) check the type of the return value. 304 * 305 ******************************************************************************/ 306 307 void 308 AnCheckMethodReturnValue ( 309 ACPI_PARSE_OBJECT *Op, 310 const ACPI_OPCODE_INFO *OpInfo, 311 ACPI_PARSE_OBJECT *ArgOp, 312 UINT32 RequiredBtypes, 313 UINT32 ThisNodeBtype) 314 { 315 ACPI_PARSE_OBJECT *OwningOp; 316 ACPI_NAMESPACE_NODE *Node; 317 char *ExternalPath; 318 319 320 Node = ArgOp->Asl.Node; 321 322 if (!Node) 323 { 324 /* No error message, this can happen and is OK */ 325 326 return; 327 } 328 329 /* Examine the parent op of this method */ 330 331 OwningOp = Node->Op; 332 ExternalPath = AcpiNsGetNormalizedPathname (Node, TRUE); 333 334 if (OwningOp->Asl.CompileFlags & OP_METHOD_NO_RETVAL) 335 { 336 /* Method NEVER returns a value */ 337 338 AslError (ASL_ERROR, ASL_MSG_NO_RETVAL, Op, ExternalPath); 339 } 340 else if (OwningOp->Asl.CompileFlags & OP_METHOD_SOME_NO_RETVAL) 341 { 342 /* Method SOMETIMES returns a value, SOMETIMES not */ 343 344 AslError (ASL_WARNING, ASL_MSG_SOME_NO_RETVAL, Op, ExternalPath); 345 } 346 else if (!(ThisNodeBtype & RequiredBtypes)) 347 { 348 /* Method returns a value, but the type is wrong */ 349 350 AnFormatBtype (AslGbl_StringBuffer, ThisNodeBtype); 351 AnFormatBtype (AslGbl_StringBuffer2, RequiredBtypes); 352 353 /* 354 * The case where the method does not return any value at all 355 * was already handled in the namespace cross reference 356 * -- Only issue an error if the method in fact returns a value, 357 * but it is of the wrong type 358 */ 359 if (ThisNodeBtype != 0) 360 { 361 snprintf (AslGbl_MsgBuffer, sizeof(AslGbl_MsgBuffer), 362 "Method returns [%s], %s operator requires [%s]", 363 AslGbl_StringBuffer, OpInfo->Name, AslGbl_StringBuffer2); 364 365 AslError (ASL_WARNING, ASL_MSG_INVALID_TYPE, ArgOp, AslGbl_MsgBuffer); 366 } 367 } 368 369 if (ExternalPath) 370 { 371 ACPI_FREE (ExternalPath); 372 } 373 } 374 375 376 /******************************************************************************* 377 * 378 * FUNCTION: AnIsResultUsed 379 * 380 * PARAMETERS: Op - Parent op for the operator 381 * 382 * RETURN: TRUE if result from this operation is actually consumed 383 * 384 * DESCRIPTION: Determine if the function result value from an operator is 385 * used. 386 * 387 ******************************************************************************/ 388 389 BOOLEAN 390 AnIsResultUsed ( 391 ACPI_PARSE_OBJECT *Op) 392 { 393 ACPI_PARSE_OBJECT *Parent; 394 395 396 switch (Op->Asl.ParseOpcode) 397 { 398 case PARSEOP_INCREMENT: 399 case PARSEOP_DECREMENT: 400 401 /* These are standalone operators, no return value */ 402 403 return (TRUE); 404 405 default: 406 407 break; 408 } 409 410 /* Examine parent to determine if the return value is used */ 411 412 Parent = Op->Asl.Parent; 413 switch (Parent->Asl.ParseOpcode) 414 { 415 /* If/While - check if the operator is the predicate */ 416 417 case PARSEOP_IF: 418 case PARSEOP_WHILE: 419 420 /* First child is the predicate */ 421 422 if (Parent->Asl.Child == Op) 423 { 424 return (TRUE); 425 } 426 427 return (FALSE); 428 429 /* Not used if one of these is the parent */ 430 431 case PARSEOP_METHOD: 432 case PARSEOP_DEFINITION_BLOCK: 433 case PARSEOP_ELSE: 434 435 return (FALSE); 436 437 default: 438 439 /* Any other type of parent means that the result is used */ 440 441 return (TRUE); 442 } 443 } 444 445 446 /******************************************************************************* 447 * 448 * FUNCTION: ApCheckForGpeNameConflict 449 * 450 * PARAMETERS: Op - Current parse op 451 * 452 * RETURN: None 453 * 454 * DESCRIPTION: Check for a conflict between GPE names within this scope. 455 * Conflict means two GPE names with the same GPE number, but 456 * different types -- such as _L1C and _E1C. 457 * 458 ******************************************************************************/ 459 460 void 461 ApCheckForGpeNameConflict ( 462 ACPI_PARSE_OBJECT *Op) 463 { 464 ACPI_PARSE_OBJECT *NextOp; 465 UINT32 GpeNumber; 466 char Name[ACPI_NAMESEG_SIZE + 1]; 467 char Target[ACPI_NAMESEG_SIZE]; 468 469 470 /* Need a null-terminated string version of NameSeg */ 471 472 ACPI_MOVE_32_TO_32 (Name, Op->Asl.NameSeg); 473 Name[ACPI_NAMESEG_SIZE] = 0; 474 475 /* 476 * For a GPE method: 477 * 1st char must be underscore 478 * 2nd char must be L or E 479 * 3rd/4th chars must be a hex number 480 */ 481 if ((Name[0] != '_') || 482 ((Name[1] != 'L') && (Name[1] != 'E'))) 483 { 484 return; 485 } 486 487 /* Verify 3rd/4th chars are a valid hex value */ 488 489 GpeNumber = strtoul (&Name[2], NULL, 16); 490 if (GpeNumber == ACPI_UINT32_MAX) 491 { 492 return; 493 } 494 495 /* 496 * We are now sure we have an _Lxx or _Exx. 497 * Create the target name that would cause collision (Flip E/L) 498 */ 499 ACPI_MOVE_32_TO_32 (Target, Name); 500 501 /* Inject opposite letter ("L" versus "E") */ 502 503 if (Name[1] == 'L') 504 { 505 Target[1] = 'E'; 506 } 507 else /* Name[1] == 'E' */ 508 { 509 Target[1] = 'L'; 510 } 511 512 /* Search all peers (objects within this scope) for target match */ 513 514 NextOp = Op->Asl.Next; 515 while (NextOp) 516 { 517 /* 518 * We mostly care about methods, but check Name() constructs also, 519 * even though they will get another error for not being a method. 520 * All GPE names must be defined as control methods. 521 */ 522 if ((NextOp->Asl.ParseOpcode == PARSEOP_METHOD) || 523 (NextOp->Asl.ParseOpcode == PARSEOP_NAME)) 524 { 525 if (ACPI_COMPARE_NAMESEG (Target, NextOp->Asl.NameSeg)) 526 { 527 /* Found both _Exy and _Lxy in the same scope, error */ 528 529 AslError (ASL_ERROR, ASL_MSG_GPE_NAME_CONFLICT, NextOp, 530 Name); 531 return; 532 } 533 } 534 535 NextOp = NextOp->Asl.Next; 536 } 537 538 /* OK, no conflict found */ 539 540 return; 541 } 542 543 544 /******************************************************************************* 545 * 546 * FUNCTION: ApCheckRegMethod 547 * 548 * PARAMETERS: Op - Current parse op 549 * 550 * RETURN: None 551 * 552 * DESCRIPTION: Ensure that a _REG method has a corresponding Operation 553 * Region declaration within the same scope. Note: _REG is defined 554 * to have two arguments and must therefore be defined as a 555 * control method. 556 * 557 ******************************************************************************/ 558 559 void 560 ApCheckRegMethod ( 561 ACPI_PARSE_OBJECT *Op) 562 { 563 ACPI_PARSE_OBJECT *Next; 564 ACPI_PARSE_OBJECT *Parent; 565 566 567 /* We are only interested in _REG methods */ 568 569 if (!ACPI_COMPARE_NAMESEG (METHOD_NAME__REG, &Op->Asl.NameSeg)) 570 { 571 return; 572 } 573 574 /* Get the start of the current scope */ 575 576 Parent = Op->Asl.Parent; 577 Next = Parent->Asl.Child; 578 579 /* Search entire scope for an operation region declaration */ 580 581 while (Next) 582 { 583 if (Next->Asl.ParseOpcode == PARSEOP_OPERATIONREGION) 584 { 585 return; /* Found region, OK */ 586 } 587 588 Next = Next->Asl.Next; 589 } 590 591 /* No region found, issue warning */ 592 593 AslError (ASL_WARNING, ASL_MSG_NO_REGION, Op, NULL); 594 } 595 596 597 /******************************************************************************* 598 * 599 * FUNCTION: ApFindNameInDeviceTree 600 * 601 * PARAMETERS: Name - Name to search for 602 * Op - Current parse op 603 * 604 * RETURN: TRUE if name found in the same scope as Op. 605 * 606 * DESCRIPTION: Determine if a name appears in the same scope as Op, as either 607 * a Method() or a Name(). "Same scope" can mean under an If or 608 * Else statement. 609 * 610 * NOTE: Detects _HID/_ADR in this type of construct (legal in ACPI 6.1+) 611 * 612 * Scope (\_SB.PCI0) 613 * { 614 * Device (I2C0) 615 * { 616 * If (SMD0 != 4) { 617 * Name (_HID, "INT3442") 618 * } Else { 619 * Name (_ADR, 0x400) 620 * } 621 * } 622 * } 623 ******************************************************************************/ 624 625 BOOLEAN 626 ApFindNameInDeviceTree ( 627 char *Name, 628 ACPI_PARSE_OBJECT *Op) 629 { 630 ACPI_STATUS Status; 631 632 633 Status = TrWalkParseTree (Op, ASL_WALK_VISIT_DOWNWARD, 634 ApDeviceSubtreeWalk, NULL, Name); 635 636 if (Status == AE_CTRL_TRUE) 637 { 638 return (TRUE); /* Found a match */ 639 } 640 641 return (FALSE); 642 } 643 644 645 /* Callback function for interface above */ 646 647 static ACPI_STATUS 648 ApDeviceSubtreeWalk ( 649 ACPI_PARSE_OBJECT *Op, 650 UINT32 Level, 651 void *Context) 652 { 653 char *Name = ACPI_CAST_PTR (char, Context); 654 655 656 switch (Op->Asl.ParseOpcode) 657 { 658 case PARSEOP_DEVICE: 659 660 /* Level 0 is the starting device, ignore it */ 661 662 if (Level > 0) 663 { 664 /* Ignore sub-devices */ 665 666 return (AE_CTRL_DEPTH); 667 } 668 break; 669 670 case PARSEOP_NAME: 671 case PARSEOP_METHOD: 672 673 /* These are what we are looking for */ 674 675 if (ACPI_COMPARE_NAMESEG (Name, Op->Asl.NameSeg)) 676 { 677 return (AE_CTRL_TRUE); 678 } 679 return (AE_CTRL_DEPTH); 680 681 case PARSEOP_SCOPE: 682 case PARSEOP_FIELD: 683 case PARSEOP_OPERATIONREGION: 684 685 /* 686 * We want to ignore these, because either they can be large 687 * subtrees or open a scope to somewhere else. 688 */ 689 return (AE_CTRL_DEPTH); 690 691 default: 692 break; 693 } 694 695 return (AE_OK); 696 } 697 698 699 /******************************************************************************* 700 * 701 * FUNCTION: ApFindNameInScope 702 * 703 * PARAMETERS: Name - Name to search for 704 * Op - Current parse op 705 * 706 * RETURN: TRUE if name found in the same scope as Op. 707 * 708 * DESCRIPTION: Determine if a name appears in the same scope as Op, as either 709 * a Method() or a Name(). 710 * 711 ******************************************************************************/ 712 713 BOOLEAN 714 ApFindNameInScope ( 715 char *Name, 716 ACPI_PARSE_OBJECT *Op) 717 { 718 ACPI_PARSE_OBJECT *Next; 719 ACPI_PARSE_OBJECT *Parent; 720 721 722 /* Get the start of the current scope */ 723 724 Parent = Op->Asl.Parent; 725 Next = Parent->Asl.Child; 726 727 /* Search entire scope for a match to the name */ 728 729 while (Next) 730 { 731 if ((Next->Asl.ParseOpcode == PARSEOP_METHOD) || 732 (Next->Asl.ParseOpcode == PARSEOP_NAME)) 733 { 734 if (ACPI_COMPARE_NAMESEG (Name, Next->Asl.NameSeg)) 735 { 736 return (TRUE); 737 } 738 } 739 740 Next = Next->Asl.Next; 741 } 742 743 return (FALSE); 744 } 745