dmcstyle.c revision 1.1 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