dmcstyle.c revision 281075
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 <contrib/dev/acpica/include/acpi.h>
45#include <contrib/dev/acpica/include/accommon.h>
46#include <contrib/dev/acpica/include/acparser.h>
47#include <contrib/dev/acpica/include/amlcode.h>
48#include <contrib/dev/acpica/include/acdisasm.h>
49#include <contrib/dev/acpica/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
59static char *
60AcpiDmGetCompoundSymbol (
61   UINT16                   AslOpcode);
62
63static void
64AcpiDmPromoteTarget (
65    ACPI_PARSE_OBJECT       *Op,
66    ACPI_PARSE_OBJECT       *Target);
67
68static BOOLEAN
69AcpiDmIsValidTarget (
70    ACPI_PARSE_OBJECT       *Op);
71
72static BOOLEAN
73AcpiDmIsTargetAnOperand (
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
94BOOLEAN
95AcpiDmCheckForSymbolicOpcode (
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
484void
485AcpiDmCloseOperator (
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
568static char *
569AcpiDmGetCompoundSymbol (
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
641static void
642AcpiDmPromoteTarget (
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
681static BOOLEAN
682AcpiDmIsValidTarget (
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
712static BOOLEAN
713AcpiDmIsTargetAnOperand (
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