dmcstyle.c revision 1.3 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 const 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 const 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 /* Check operands for conversion to a "Compound Assignment" */
341
342 switch (Op->Common.AmlOpcode)
343 {
344 /* Commutative operators */
345
346 case AML_ADD_OP:
347 case AML_MULTIPLY_OP:
348 case AML_BIT_AND_OP:
349 case AML_BIT_OR_OP:
350 case AML_BIT_XOR_OP:
351 /*
352 * For the commutative operators, we can convert to a
353 * compound statement only if at least one (either) operand
354 * is the same as the target.
355 *
356 * Add (A, B, A) --> A += B
357 * Add (B, A, A) --> A += B
358 * Add (B, C, A) --> A = (B + C)
359 */
360 if ((AcpiDmIsTargetAnOperand (Target, Child1, TRUE)) ||
361 (AcpiDmIsTargetAnOperand (Target, Child2, TRUE)))
362 {
363 Target->Common.OperatorSymbol =
364 AcpiDmGetCompoundSymbol (Op->Common.AmlOpcode);
365
366 /* Convert operator to compound assignment */
367
368 Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND;
369 Child1->Common.OperatorSymbol = NULL;
370 return (TRUE);
371 }
372 break;
373
374 /* Non-commutative operators */
375
376 case AML_SUBTRACT_OP:
377 case AML_DIVIDE_OP:
378 case AML_MOD_OP:
379 case AML_SHIFT_LEFT_OP:
380 case AML_SHIFT_RIGHT_OP:
381 /*
382 * For the non-commutative operators, we can convert to a
383 * compound statement only if the target is the same as the
384 * first operand.
385 *
386 * Subtract (A, B, A) --> A -= B
387 * Subtract (B, A, A) --> A = (B - A)
388 */
389 if ((AcpiDmIsTargetAnOperand (Target, Child1, TRUE)))
390 {
391 Target->Common.OperatorSymbol =
392 AcpiDmGetCompoundSymbol (Op->Common.AmlOpcode);
393
394 /* Convert operator to compound assignment */
395
396 Op->Common.DisasmFlags |= ACPI_PARSEOP_COMPOUND;
397 Child1->Common.OperatorSymbol = NULL;
398 return (TRUE);
399 }
400 break;
401
402 default:
403 break;
404 }
405
406 /*
407 * If we are within a C-style expression, emit an extra open
408 * paren. Implemented by examining the parent op.
409 */
410 switch (Op->Common.Parent->Common.AmlOpcode)
411 {
412 case AML_ADD_OP:
413 case AML_SUBTRACT_OP:
414 case AML_MULTIPLY_OP:
415 case AML_DIVIDE_OP:
416 case AML_MOD_OP:
417 case AML_SHIFT_LEFT_OP:
418 case AML_SHIFT_RIGHT_OP:
419 case AML_BIT_AND_OP:
420 case AML_BIT_OR_OP:
421 case AML_BIT_XOR_OP:
422 case AML_LAND_OP:
423 case AML_LEQUAL_OP:
424 case AML_LGREATER_OP:
425 case AML_LLESS_OP:
426 case AML_LOR_OP:
427
428 Op->Common.DisasmFlags |= ACPI_PARSEOP_ASSIGNMENT;
429 AcpiOsPrintf ("(");
430 break;
431
432 default:
433 break;
434 }
435
436 /* Normal output for ASL/AML operators with a target operand */
437
438 Target->Common.OperatorSymbol = " = (";
439 return (TRUE);
440
441 /* Binary operators, no parens */
442
443 case AML_DECREMENT_OP:
444 case AML_INCREMENT_OP:
445 return (TRUE);
446
447 #ifdef INDEX_SUPPORT
448 case AML_INDEX_OP:
449
450 /* Target is optional, 3rd operand */
451
452 Target = Child2->Common.Next;
453 if (AcpiDmIsValidTarget (Target))
454 {
455 AcpiDmPromoteTarget (Op, Target);
456
457 if (!Target->Common.OperatorSymbol)
458 {
459 Target->Common.OperatorSymbol = " = ";
460 }
461 }
462 return (TRUE);
463 #endif
464
465 case AML_STORE_OP:
466 /*
467 * Target is the 2nd operand.
468 * We know the target is valid, it is not optional.
469 * In the parse tree, simply swap the target with the
470 * source so that the target is processed first.
471 */
472 Target = Child1->Common.Next;
473 if (!Target)
474 {
475 return (FALSE);
476 }
477
478 AcpiDmPromoteTarget (Op, Target);
479 if (!Target->Common.OperatorSymbol)
480 {
481 Target->Common.OperatorSymbol = " = ";
482 }
483 return (TRUE);
484
485 case AML_BIT_NOT_OP:
486
487 /* Target is optional, 2nd operand */
488
489 Target = Child1->Common.Next;
490 if (!Target)
491 {
492 return (FALSE);
493 }
494
495 if (AcpiDmIsValidTarget (Target))
496 {
497 /* Valid target, not a placeholder */
498
499 AcpiDmPromoteTarget (Op, Target);
500 Target->Common.OperatorSymbol = " = ~";
501 }
502 else
503 {
504 /* No target. Emit this prefix operator immediately */
505
506 AcpiOsPrintf ("~");
507 }
508 return (TRUE);
509
510 default:
511 break;
512 }
513
514 /* All other operators, emit an open paren */
515
516 AcpiOsPrintf ("(");
517 return (TRUE);
518 }
519
520
521 /*******************************************************************************
522 *
523 * FUNCTION: AcpiDmCloseOperator
524 *
525 * PARAMETERS: Op - Current parse object
526 *
527 * RETURN: None
528 *
529 * DESCRIPTION: Closes an operator by adding a closing parentheses if and
530 * when necessary. Called during ascending phase of the
531 * parse tree walk.
532 *
533 ******************************************************************************/
534
535 void
536 AcpiDmCloseOperator (
537 ACPI_PARSE_OBJECT *Op)
538 {
539
540 /* Always emit paren if ASL+ disassembly disabled */
541
542 if (!AcpiGbl_CstyleDisassembly)
543 {
544 AcpiOsPrintf (")");
545 return;
546 }
547
548 /* Check if we need to add an additional closing paren */
549
550 switch (Op->Common.AmlOpcode)
551 {
552 case AML_ADD_OP:
553 case AML_SUBTRACT_OP:
554 case AML_MULTIPLY_OP:
555 case AML_DIVIDE_OP:
556 case AML_MOD_OP:
557 case AML_SHIFT_LEFT_OP:
558 case AML_SHIFT_RIGHT_OP:
559 case AML_BIT_AND_OP:
560 case AML_BIT_OR_OP:
561 case AML_BIT_XOR_OP:
562 case AML_LAND_OP:
563 case AML_LEQUAL_OP:
564 case AML_LGREATER_OP:
565 case AML_LLESS_OP:
566 case AML_LOR_OP:
567
568 /* Emit paren only if this is not a compound assignment */
569
570 if (Op->Common.DisasmFlags & ACPI_PARSEOP_COMPOUND)
571 {
572 return;
573 }
574
575 /* Emit extra close paren for assignment within an expression */
576
577 if (Op->Common.DisasmFlags & ACPI_PARSEOP_ASSIGNMENT)
578 {
579 AcpiOsPrintf (")");
580 }
581 break;
582
583
584 /* No need for parens for these */
585
586 #ifdef INDEX_SUPPORT
587 case AML_INDEX_OP:
588 #endif
589 case AML_DECREMENT_OP:
590 case AML_INCREMENT_OP:
591 case AML_LNOT_OP:
592 case AML_BIT_NOT_OP:
593 case AML_STORE_OP:
594 return;
595
596 default:
597
598 /* Always emit paren for non-ASL+ operators */
599 break;
600 }
601
602 AcpiOsPrintf (")");
603 }
604
605
606 /*******************************************************************************
607 *
608 * FUNCTION: AcpiDmGetCompoundSymbol
609 *
610 * PARAMETERS: AslOpcode
611 *
612 * RETURN: String containing the compound assignment symbol
613 *
614 * DESCRIPTION: Detect opcodes that can be converted to compound assignment,
615 * return the appropriate operator string.
616 *
617 ******************************************************************************/
618
619 static const char *
620 AcpiDmGetCompoundSymbol (
621 UINT16 AmlOpcode)
622 {
623 const char *Symbol;
624
625
626 switch (AmlOpcode)
627 {
628 case AML_ADD_OP:
629 Symbol = " += ";
630 break;
631
632 case AML_SUBTRACT_OP:
633 Symbol = " -= ";
634 break;
635
636 case AML_MULTIPLY_OP:
637 Symbol = " *= ";
638 break;
639
640 case AML_DIVIDE_OP:
641 Symbol = " /= ";
642 break;
643
644 case AML_MOD_OP:
645 Symbol = " %= ";
646 break;
647
648 case AML_SHIFT_LEFT_OP:
649 Symbol = " <<= ";
650 break;
651
652 case AML_SHIFT_RIGHT_OP:
653 Symbol = " >>= ";
654 break;
655
656 case AML_BIT_AND_OP:
657 Symbol = " &= ";
658 break;
659
660 case AML_BIT_OR_OP:
661 Symbol = " |= ";
662 break;
663
664 case AML_BIT_XOR_OP:
665 Symbol = " ^= ";
666 break;
667
668 default:
669
670 /* No operator string for all other opcodes */
671 return (NULL);
672 }
673
674 return (Symbol);
675 }
676
677
678 /*******************************************************************************
679 *
680 * FUNCTION: AcpiDmPromoteTarget
681 *
682 * PARAMETERS: Op - Operator parse object
683 * Target - Target associate with the Op
684 *
685 * RETURN: None
686 *
687 * DESCRIPTION: Transform the parse tree by moving the target up to the first
688 * child of the Op.
689 *
690 ******************************************************************************/
691
692 static void
693 AcpiDmPromoteTarget (
694 ACPI_PARSE_OBJECT *Op,
695 ACPI_PARSE_OBJECT *Target)
696 {
697 ACPI_PARSE_OBJECT *Child;
698
699
700 /* Link target directly to the Op as first child */
701
702 Child = Op->Common.Value.Arg;
703 Op->Common.Value.Arg = Target;
704 Target->Common.Next = Child;
705
706 /* Find the last peer, it is linked to the target. Unlink it. */
707
708 while (Child->Common.Next != Target)
709 {
710 Child = Child->Common.Next;
711 }
712
713 Child->Common.Next = NULL;
714 }
715
716
717 /*******************************************************************************
718 *
719 * FUNCTION: AcpiDmIsValidTarget
720 *
721 * PARAMETERS: Target - Target Op from the parse tree
722 *
723 * RETURN: TRUE if the Target is real. FALSE if it is just a placeholder
724 * Op that was inserted by the parser.
725 *
726 * DESCRIPTION: Determine if a Target Op is a placeholder Op or a real Target.
727 * In other words, determine if the optional target is used or
728 * not. Note: If Target is NULL, something is seriously wrong,
729 * probably with the parse tree.
730 *
731 ******************************************************************************/
732
733 static BOOLEAN
734 AcpiDmIsValidTarget (
735 ACPI_PARSE_OBJECT *Target)
736 {
737
738 if (!Target)
739 {
740 return (FALSE);
741 }
742
743 if ((Target->Common.AmlOpcode == AML_INT_NAMEPATH_OP) &&
744 (Target->Common.Value.Arg == NULL))
745 {
746 return (FALSE);
747 }
748
749 return (TRUE);
750 }
751
752
753 /*******************************************************************************
754 *
755 * FUNCTION: AcpiDmIsTargetAnOperand
756 *
757 * PARAMETERS: Target - Target associated with the expression
758 * Operand - An operand associated with expression
759 *
760 * RETURN: TRUE if expression can be converted to a compound assignment.
761 * FALSE otherwise.
762 *
763 * DESCRIPTION: Determine if the Target duplicates the operand, in order to
764 * detect if the expression can be converted to a compound
765 * assigment. (+=, *=, etc.)
766 *
767 ******************************************************************************/
768
769 static BOOLEAN
770 AcpiDmIsTargetAnOperand (
771 ACPI_PARSE_OBJECT *Target,
772 ACPI_PARSE_OBJECT *Operand,
773 BOOLEAN TopLevel)
774 {
775 const ACPI_OPCODE_INFO *OpInfo;
776 BOOLEAN Same;
777
778
779 /*
780 * Opcodes must match. Note: ignoring the difference between nameseg
781 * and namepath for now. May be needed later.
782 */
783 if (Target->Common.AmlOpcode != Operand->Common.AmlOpcode)
784 {
785 return (FALSE);
786 }
787
788 /* Nodes should match, even if they are NULL */
789
790 if (Target->Common.Node != Operand->Common.Node)
791 {
792 return (FALSE);
793 }
794
795 /* Determine if a child exists */
796
797 OpInfo = AcpiPsGetOpcodeInfo (Operand->Common.AmlOpcode);
798 if (OpInfo->Flags & AML_HAS_ARGS)
799 {
800 Same = AcpiDmIsTargetAnOperand (Target->Common.Value.Arg,
801 Operand->Common.Value.Arg, FALSE);
802 if (!Same)
803 {
804 return (FALSE);
805 }
806 }
807
808 /* Check the next peer, as long as we are not at the top level */
809
810 if ((!TopLevel) &&
811 Target->Common.Next)
812 {
813 Same = AcpiDmIsTargetAnOperand (Target->Common.Next,
814 Operand->Common.Next, FALSE);
815 if (!Same)
816 {
817 return (FALSE);
818 }
819 }
820
821 /* Supress the duplicate operand at the top-level */
822
823 if (TopLevel)
824 {
825 Operand->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
826 }
827 return (TRUE);
828 }
829
830 #endif
831