xref: /dflybsd-src/sys/contrib/dev/acpica/source/components/disassembler/dmcstyle.c (revision 5943f66ca1b24657281aeb1be2396c522bee7e07)
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