1 /******************************************************************************* 2 * 3 * Module Name: dmcstyle - Support for C-style operator disassembly 4 * 5 ******************************************************************************/ 6 7 /* 8 * Copyright (C) 2000 - 2015, 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 #include "acpi.h" 45 #include "accommon.h" 46 #include "acparser.h" 47 #include "amlcode.h" 48 #include "acdisasm.h" 49 #include "acdebug.h" 50 51 #ifdef ACPI_DISASSEMBLER 52 53 #define _COMPONENT ACPI_CA_DEBUGGER 54 ACPI_MODULE_NAME ("dmcstyle") 55 56 57 /* Local prototypes */ 58 59 static char * 60 AcpiDmGetCompoundSymbol ( 61 UINT16 AslOpcode); 62 63 static void 64 AcpiDmPromoteTarget ( 65 ACPI_PARSE_OBJECT *Op, 66 ACPI_PARSE_OBJECT *Target); 67 68 static BOOLEAN 69 AcpiDmIsValidTarget ( 70 ACPI_PARSE_OBJECT *Op); 71 72 static BOOLEAN 73 AcpiDmIsTargetAnOperand ( 74 ACPI_PARSE_OBJECT *Target, 75 ACPI_PARSE_OBJECT *Operand, 76 BOOLEAN TopLevel); 77 78 79 /******************************************************************************* 80 * 81 * FUNCTION: AcpiDmCheckForSymbolicOpcode 82 * 83 * PARAMETERS: Op - Current parse object 84 * Walk - Current parse tree walk info 85 * 86 * RETURN: TRUE if opcode can be converted to symbolic, FALSE otherwise 87 * 88 * DESCRIPTION: This is the main code that implements disassembly of AML code 89 * to C-style operators. Called during descending phase of the 90 * parse tree walk. 91 * 92 ******************************************************************************/ 93 94 BOOLEAN 95 AcpiDmCheckForSymbolicOpcode ( 96 ACPI_PARSE_OBJECT *Op, 97 ACPI_OP_WALK_INFO *Info) 98 { 99 char *OperatorSymbol = NULL; 100 ACPI_PARSE_OBJECT *Child1; 101 ACPI_PARSE_OBJECT *Child2; 102 ACPI_PARSE_OBJECT *Target; 103 104 105 /* Exit immediately if ASL+ not enabled */ 106 107 if (!AcpiGbl_CstyleDisassembly) 108 { 109 return (FALSE); 110 } 111 112 /* Get the first operand */ 113 114 Child1 = AcpiPsGetArg (Op, 0); 115 if (!Child1) 116 { 117 /* Parse tree may be confused or corrupted */ 118 119 return (FALSE); 120 } 121 122 /* Get the second operand */ 123 124 Child2 = Child1->Common.Next; 125 if (!Child2) 126 { 127 /* Parse tree may be confused or corrupted */ 128 129 return (FALSE); 130 } 131 132 /* Setup the operator string for this opcode */ 133 134 switch (Op->Common.AmlOpcode) 135 { 136 case AML_ADD_OP: 137 OperatorSymbol = " + "; 138 break; 139 140 case AML_SUBTRACT_OP: 141 OperatorSymbol = " - "; 142 break; 143 144 case AML_MULTIPLY_OP: 145 OperatorSymbol = " * "; 146 break; 147 148 case AML_DIVIDE_OP: 149 OperatorSymbol = " / "; 150 break; 151 152 case AML_MOD_OP: 153 OperatorSymbol = " % "; 154 break; 155 156 case AML_SHIFT_LEFT_OP: 157 OperatorSymbol = " << "; 158 break; 159 160 case AML_SHIFT_RIGHT_OP: 161 OperatorSymbol = " >> "; 162 break; 163 164 case AML_BIT_AND_OP: 165 OperatorSymbol = " & "; 166 break; 167 168 case AML_BIT_OR_OP: 169 OperatorSymbol = " | "; 170 break; 171 172 case AML_BIT_XOR_OP: 173 OperatorSymbol = " ^ "; 174 break; 175 176 /* Logical operators, no target */ 177 178 case AML_LAND_OP: 179 OperatorSymbol = " && "; 180 break; 181 182 case AML_LEQUAL_OP: 183 OperatorSymbol = " == "; 184 break; 185 186 case AML_LGREATER_OP: 187 OperatorSymbol = " > "; 188 break; 189 190 case AML_LLESS_OP: 191 OperatorSymbol = " < "; 192 break; 193 194 case AML_LOR_OP: 195 OperatorSymbol = " || "; 196 break; 197 198 case AML_LNOT_OP: 199 /* 200 * Check for the LNOT sub-opcodes. These correspond to 201 * LNotEqual, LLessEqual, and LGreaterEqual. There are 202 * no actual AML opcodes for these operators. 203 */ 204 switch (Child1->Common.AmlOpcode) 205 { 206 case AML_LEQUAL_OP: 207 OperatorSymbol = " != "; 208 break; 209 210 case AML_LGREATER_OP: 211 OperatorSymbol = " <= "; 212 break; 213 214 case AML_LLESS_OP: 215 OperatorSymbol = " >= "; 216 break; 217 218 default: 219 220 /* Unary LNOT case, emit "!" immediately */ 221 222 AcpiOsPrintf ("!"); 223 return (TRUE); 224 } 225 226 Child1->Common.DisasmOpcode = ACPI_DASM_LNOT_SUFFIX; 227 Op->Common.DisasmOpcode = ACPI_DASM_LNOT_PREFIX; 228 229 /* Save symbol string in the next child (not peer) */ 230 231 Child2 = AcpiPsGetArg (Child1, 0); 232 if (!Child2) 233 { 234 return (FALSE); 235 } 236 237 Child2->Common.OperatorSymbol = OperatorSymbol; 238 return (TRUE); 239 240 #ifdef INDEX_SUPPORT 241 case AML_INDEX_OP: 242 Child1->Common.OperatorSymbol = " ["; 243 Child2->Common.OperatorSymbol = "]"; 244 break; 245 #endif 246 247 /* Unary operators */ 248 249 case AML_DECREMENT_OP: 250 OperatorSymbol = "--"; 251 break; 252 253 case AML_INCREMENT_OP: 254 OperatorSymbol = "++"; 255 break; 256 257 case AML_BIT_NOT_OP: 258 case AML_STORE_OP: 259 OperatorSymbol = NULL; 260 break; 261 262 default: 263 return (FALSE); 264 } 265 266 if (Child1->Common.DisasmOpcode == ACPI_DASM_LNOT_SUFFIX) 267 { 268 return (TRUE); 269 } 270 271 /* 272 * This is the key to how the disassembly of the C-style operators 273 * works. We save the operator symbol in the first child, thus 274 * deferring symbol output until after the first operand has been 275 * emitted. 276 */ 277 if (!Child1->Common.OperatorSymbol) 278 { 279 Child1->Common.OperatorSymbol = OperatorSymbol; 280 } 281 282 /* 283 * Check for a valid target as the 3rd (or sometimes 2nd) operand 284 * 285 * Compound assignment operator support: 286 * Attempt to optimize constructs of the form: 287 * Add (Local1, 0xFF, Local1) 288 * to: 289 * Local1 += 0xFF 290 * 291 * Only the math operators and Store() have a target. 292 * Logicals have no target. 293 */ 294 switch (Op->Common.AmlOpcode) 295 { 296 case AML_ADD_OP: 297 case AML_SUBTRACT_OP: 298 case AML_MULTIPLY_OP: 299 case AML_DIVIDE_OP: 300 case AML_MOD_OP: 301 case AML_SHIFT_LEFT_OP: 302 case AML_SHIFT_RIGHT_OP: 303 case AML_BIT_AND_OP: 304 case AML_BIT_OR_OP: 305 case AML_BIT_XOR_OP: 306 307 /* Target is 3rd operand */ 308 309 Target = Child2->Common.Next; 310 311 if (Op->Common.AmlOpcode == AML_DIVIDE_OP) 312 { 313 if (!Target) 314 { 315 /* Parse tree may be confused or corrupted */ 316 317 return (FALSE); 318 } 319 320 /* 321 * Divide has an extra target operand (Remainder). 322 * If this extra target is specified, it cannot be converted 323 * to a C-style operator 324 */ 325 if (AcpiDmIsValidTarget (Target)) 326 { 327 Child1->Common.OperatorSymbol = NULL; 328 return (FALSE); 329 } 330 331 Target->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE; 332 Target = Target->Common.Next; 333 } 334 335 /* Parser should ensure there is at least a placeholder target */ 336 337 if (!Target) 338 { 339 return (FALSE); 340 } 341 342 if (!AcpiDmIsValidTarget (Target)) 343 { 344 /* Not a valid target (placeholder only, from parser) */ 345 break; 346 } 347 348 /* 349 * Promote the target up to the first child in the parse 350 * tree. This is done because the target will be output 351 * first, in the form: 352 * <Target> = Operands... 353 */ 354 AcpiDmPromoteTarget (Op, Target); 355 356 /* 357 * Check for possible conversion to a "Compound Assignment". 358 * 359 * Determine if either operand is the same as the target 360 * and display compound assignment operator and other operand. 361 */ 362 if ((AcpiDmIsTargetAnOperand (Target, Child1, TRUE)) || 363 (AcpiDmIsTargetAnOperand (Target, Child2, TRUE))) 364 { 365 Target->Common.OperatorSymbol = 366 AcpiDmGetCompoundSymbol (Op->Common.AmlOpcode); 367 368 /* Convert operator to compound assignment */ 369 370 Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND; 371 Child1->Common.OperatorSymbol = NULL; 372 return (TRUE); 373 } 374 375 /* 376 * If we are within a C-style expression, emit an extra open 377 * paren. Implemented by examining the parent op. 378 */ 379 switch (Op->Common.Parent->Common.AmlOpcode) 380 { 381 case AML_ADD_OP: 382 case AML_SUBTRACT_OP: 383 case AML_MULTIPLY_OP: 384 case AML_DIVIDE_OP: 385 case AML_MOD_OP: 386 case AML_SHIFT_LEFT_OP: 387 case AML_SHIFT_RIGHT_OP: 388 case AML_BIT_AND_OP: 389 case AML_BIT_OR_OP: 390 case AML_BIT_XOR_OP: 391 case AML_LAND_OP: 392 case AML_LEQUAL_OP: 393 case AML_LGREATER_OP: 394 case AML_LLESS_OP: 395 case AML_LOR_OP: 396 397 Op->Common.DisasmFlags |= ACPI_PARSEOP_ASSIGNMENT; 398 AcpiOsPrintf ("("); 399 break; 400 401 default: 402 break; 403 } 404 405 /* Normal output for ASL/AML operators with a target operand */ 406 407 Target->Common.OperatorSymbol = " = ("; 408 return (TRUE); 409 410 /* Binary operators, no parens */ 411 412 case AML_DECREMENT_OP: 413 case AML_INCREMENT_OP: 414 return (TRUE); 415 416 #ifdef INDEX_SUPPORT 417 case AML_INDEX_OP: 418 419 /* Target is optional, 3rd operand */ 420 421 Target = Child2->Common.Next; 422 if (!Target) 423 { 424 /* Parse tree may be confused or corrupted */ 425 426 return (FALSE); 427 } 428 429 if (AcpiDmIsValidTarget (Target)) 430 { 431 AcpiDmPromoteTarget (Op, Target); 432 433 if (!Target->Common.OperatorSymbol) 434 { 435 Target->Common.OperatorSymbol = " = "; 436 } 437 } 438 return (TRUE); 439 #endif 440 441 case AML_STORE_OP: 442 /* 443 * Target is the 2nd operand. 444 * We know the target is valid, it is not optional. 445 * In the parse tree, simply swap the target with the 446 * source so that the target is processed first. 447 */ 448 Target = Child1->Common.Next; 449 if (!Target) 450 { 451 /* Parse tree may be confused or corrupted */ 452 453 return (FALSE); 454 } 455 456 AcpiDmPromoteTarget (Op, Target); 457 458 if (!Target->Common.OperatorSymbol) 459 { 460 Target->Common.OperatorSymbol = " = "; 461 } 462 return (TRUE); 463 464 case AML_BIT_NOT_OP: 465 466 /* Target is optional, 2nd operand */ 467 468 Target = Child1->Common.Next; 469 if (!Target) 470 { 471 return (FALSE); 472 } 473 474 if (AcpiDmIsValidTarget (Target)) 475 { 476 /* Valid target, not a placeholder */ 477 478 AcpiDmPromoteTarget (Op, Target); 479 Target->Common.OperatorSymbol = " = ~"; 480 } 481 else 482 { 483 /* No target. Emit this prefix operator immediately */ 484 485 AcpiOsPrintf ("~"); 486 } 487 return (TRUE); 488 489 default: 490 break; 491 } 492 493 /* All other operators, emit an open paren */ 494 495 AcpiOsPrintf ("("); 496 return (TRUE); 497 } 498 499 500 /******************************************************************************* 501 * 502 * FUNCTION: AcpiDmCloseOperator 503 * 504 * PARAMETERS: Op - Current parse object 505 * 506 * RETURN: None 507 * 508 * DESCRIPTION: Closes an operator by adding a closing parentheses if and 509 * when necessary. Called during ascending phase of the 510 * parse tree walk. 511 * 512 ******************************************************************************/ 513 514 void 515 AcpiDmCloseOperator ( 516 ACPI_PARSE_OBJECT *Op) 517 { 518 519 /* Always emit paren if ASL+ disassembly disabled */ 520 521 if (!AcpiGbl_CstyleDisassembly) 522 { 523 AcpiOsPrintf (")"); 524 return; 525 } 526 527 /* Check if we need to add an additional closing paren */ 528 529 switch (Op->Common.AmlOpcode) 530 { 531 case AML_ADD_OP: 532 case AML_SUBTRACT_OP: 533 case AML_MULTIPLY_OP: 534 case AML_DIVIDE_OP: 535 case AML_MOD_OP: 536 case AML_SHIFT_LEFT_OP: 537 case AML_SHIFT_RIGHT_OP: 538 case AML_BIT_AND_OP: 539 case AML_BIT_OR_OP: 540 case AML_BIT_XOR_OP: 541 case AML_LAND_OP: 542 case AML_LEQUAL_OP: 543 case AML_LGREATER_OP: 544 case AML_LLESS_OP: 545 case AML_LOR_OP: 546 547 /* Emit paren only if this is not a compound assignment */ 548 549 if (Op->Common.DisasmFlags & ACPI_PARSEOP_COMPOUND) 550 { 551 return; 552 } 553 554 /* Emit extra close paren for assignment within an expression */ 555 556 if (Op->Common.DisasmFlags & ACPI_PARSEOP_ASSIGNMENT) 557 { 558 AcpiOsPrintf (")"); 559 } 560 break; 561 562 563 /* No need for parens for these */ 564 565 #ifdef INDEX_SUPPORT 566 case AML_INDEX_OP: 567 #endif 568 case AML_DECREMENT_OP: 569 case AML_INCREMENT_OP: 570 case AML_LNOT_OP: 571 case AML_BIT_NOT_OP: 572 case AML_STORE_OP: 573 return; 574 575 default: 576 577 /* Always emit paren for non-ASL+ operators */ 578 break; 579 } 580 581 AcpiOsPrintf (")"); 582 } 583 584 585 /******************************************************************************* 586 * 587 * FUNCTION: AcpiDmGetCompoundSymbol 588 * 589 * PARAMETERS: AslOpcode 590 * 591 * RETURN: String containing the compound assignment symbol 592 * 593 * DESCRIPTION: Detect opcodes that can be converted to compound assignment, 594 * return the appropriate operator string. 595 * 596 ******************************************************************************/ 597 598 static char * 599 AcpiDmGetCompoundSymbol ( 600 UINT16 AmlOpcode) 601 { 602 char *Symbol; 603 604 605 switch (AmlOpcode) 606 { 607 case AML_ADD_OP: 608 Symbol = " += "; 609 break; 610 611 case AML_SUBTRACT_OP: 612 Symbol = " -= "; 613 break; 614 615 case AML_MULTIPLY_OP: 616 Symbol = " *= "; 617 break; 618 619 case AML_DIVIDE_OP: 620 Symbol = " /= "; 621 break; 622 623 case AML_MOD_OP: 624 Symbol = " %= "; 625 break; 626 627 case AML_SHIFT_LEFT_OP: 628 Symbol = " <<= "; 629 break; 630 631 case AML_SHIFT_RIGHT_OP: 632 Symbol = " >>= "; 633 break; 634 635 case AML_BIT_AND_OP: 636 Symbol = " &= "; 637 break; 638 639 case AML_BIT_OR_OP: 640 Symbol = " |= "; 641 break; 642 643 case AML_BIT_XOR_OP: 644 Symbol = " ^= "; 645 break; 646 647 default: 648 649 /* No operator string for all other opcodes */ 650 return (NULL); 651 } 652 653 return (Symbol); 654 } 655 656 657 /******************************************************************************* 658 * 659 * FUNCTION: AcpiDmPromoteTarget 660 * 661 * PARAMETERS: Op - Operator parse object 662 * Target - Target associate with the Op 663 * 664 * RETURN: None 665 * 666 * DESCRIPTION: Transform the parse tree by moving the target up to the first 667 * child of the Op. 668 * 669 ******************************************************************************/ 670 671 static void 672 AcpiDmPromoteTarget ( 673 ACPI_PARSE_OBJECT *Op, 674 ACPI_PARSE_OBJECT *Target) 675 { 676 ACPI_PARSE_OBJECT *Child; 677 678 679 /* Link target directly to the Op as first child */ 680 681 Child = Op->Common.Value.Arg; 682 Op->Common.Value.Arg = Target; 683 Target->Common.Next = Child; 684 685 /* Find the last peer, it is linked to the target. Unlink it. */ 686 687 while (Child->Common.Next != Target) 688 { 689 Child = Child->Common.Next; 690 } 691 692 Child->Common.Next = NULL; 693 } 694 695 696 /******************************************************************************* 697 * 698 * FUNCTION: AcpiDmIsValidTarget 699 * 700 * PARAMETERS: Target - Target Op from the parse tree 701 * 702 * RETURN: TRUE if the Target is real. FALSE if it is just a placeholder 703 * Op that was inserted by the parser. 704 * 705 * DESCRIPTION: Determine if a Target Op is a placeholder Op or a real Target. 706 * In other words, determine if the optional target is used or 707 * not. 708 * 709 ******************************************************************************/ 710 711 static BOOLEAN 712 AcpiDmIsValidTarget ( 713 ACPI_PARSE_OBJECT *Target) 714 { 715 716 if ((Target->Common.AmlOpcode == AML_INT_NAMEPATH_OP) && 717 (Target->Common.Value.Arg == NULL)) 718 { 719 return (FALSE); 720 } 721 722 return (TRUE); 723 } 724 725 726 /******************************************************************************* 727 * 728 * FUNCTION: AcpiDmIsTargetAnOperand 729 * 730 * PARAMETERS: Target - Target associated with the expression 731 * Operand - An operand associated with expression 732 * 733 * RETURN: TRUE if expression can be converted to a compound assignment. 734 * FALSE otherwise. 735 * 736 * DESCRIPTION: Determine if the Target duplicates the operand, in order to 737 * detect if the expression can be converted to a compound 738 * assigment. (+=, *=, etc.) 739 * 740 ******************************************************************************/ 741 742 static BOOLEAN 743 AcpiDmIsTargetAnOperand ( 744 ACPI_PARSE_OBJECT *Target, 745 ACPI_PARSE_OBJECT *Operand, 746 BOOLEAN TopLevel) 747 { 748 const ACPI_OPCODE_INFO *OpInfo; 749 BOOLEAN Same; 750 751 752 /* 753 * Opcodes must match. Note: ignoring the difference between nameseg 754 * and namepath for now. May be needed later. 755 */ 756 if (Target->Common.AmlOpcode != Operand->Common.AmlOpcode) 757 { 758 return (FALSE); 759 } 760 761 /* Nodes should match, even if they are NULL */ 762 763 if (Target->Common.Node != Operand->Common.Node) 764 { 765 return (FALSE); 766 } 767 768 /* Determine if a child exists */ 769 770 OpInfo = AcpiPsGetOpcodeInfo (Operand->Common.AmlOpcode); 771 if (OpInfo->Flags & AML_HAS_ARGS) 772 { 773 Same = AcpiDmIsTargetAnOperand (Target->Common.Value.Arg, 774 Operand->Common.Value.Arg, FALSE); 775 if (!Same) 776 { 777 return (FALSE); 778 } 779 } 780 781 /* Check the next peer, as long as we are not at the top level */ 782 783 if ((!TopLevel) && 784 Target->Common.Next) 785 { 786 Same = AcpiDmIsTargetAnOperand (Target->Common.Next, 787 Operand->Common.Next, FALSE); 788 if (!Same) 789 { 790 return (FALSE); 791 } 792 } 793 794 /* Supress the duplicate operand at the top-level */ 795 796 if (TopLevel) 797 { 798 Operand->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE; 799 } 800 return (TRUE); 801 } 802 803 #endif 804