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