1 /*******************************************************************************
3 * Module Name: dmwalk - AML disassembly tree walk
5 ******************************************************************************/
8 * Copyright (C) 2000 - 2015, Intel Corp.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
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.
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>
52 #ifdef ACPI_DISASSEMBLER
54 #define _COMPONENT ACPI_CA_DEBUGGER
55 ACPI_MODULE_NAME ("dmwalk")
58 #define DB_FULL_OP_INFO "[%4.4s] @%5.5X #%4.4X: "
60 /* Stub for non-compiler code */
62 #ifndef ACPI_ASL_COMPILER
71 /* Local prototypes */
75 ACPI_PARSE_OBJECT *Op,
81 ACPI_PARSE_OBJECT *Op,
87 ACPI_PARSE_OBJECT *Op);
90 /*******************************************************************************
92 * FUNCTION: AcpiDmDisassemble
94 * PARAMETERS: WalkState - Current state
95 * Origin - Starting object
96 * NumOpcodes - Max number of opcodes to be displayed
100 * DESCRIPTION: Disassemble parser object and its children. This is the
101 * main entry point of the disassembler.
103 ******************************************************************************/
107 ACPI_WALK_STATE *WalkState,
108 ACPI_PARSE_OBJECT *Origin,
111 ACPI_PARSE_OBJECT *Op = Origin;
112 ACPI_OP_WALK_INFO Info;
123 Info.WalkState = WalkState;
124 AcpiDmWalkParseTree (Op, AcpiDmDescendingOp, AcpiDmAscendingOp, &Info);
129 /*******************************************************************************
131 * FUNCTION: AcpiDmWalkParseTree
133 * PARAMETERS: Op - Root Op object
134 * DescendingCallback - Called during tree descent
135 * AscendingCallback - Called during tree ascent
136 * Context - To be passed to the callbacks
138 * RETURN: Status from callback(s)
140 * DESCRIPTION: Walk the entire parse tree.
142 ******************************************************************************/
145 AcpiDmWalkParseTree (
146 ACPI_PARSE_OBJECT *Op,
147 ASL_WALK_CALLBACK DescendingCallback,
148 ASL_WALK_CALLBACK AscendingCallback,
151 BOOLEAN NodePreviouslyVisited;
152 ACPI_PARSE_OBJECT *StartOp = Op;
154 ACPI_PARSE_OBJECT *Next;
155 ACPI_OP_WALK_INFO *Info = Context;
159 NodePreviouslyVisited = FALSE;
163 if (NodePreviouslyVisited)
165 if (AscendingCallback)
167 Status = AscendingCallback (Op, Info->Level, Context);
168 if (ACPI_FAILURE (Status))
176 /* Let the callback process the node */
178 Status = DescendingCallback (Op, Info->Level, Context);
179 if (ACPI_SUCCESS (Status))
181 /* Visit children first, once */
183 Next = AcpiPsGetArg (Op, 0);
191 else if (Status != AE_CTRL_DEPTH)
193 /* Exit immediately on any error */
199 /* Terminate walk at start op */
206 /* No more children, re-visit this node */
208 if (!NodePreviouslyVisited)
210 NodePreviouslyVisited = TRUE;
214 /* No more children, visit peers */
218 Op = Op->Common.Next;
219 NodePreviouslyVisited = FALSE;
223 /* No peers, re-visit parent */
225 if (Info->Level != 0 )
230 Op = Op->Common.Parent;
231 NodePreviouslyVisited = TRUE;
235 /* If we get here, the walk completed with no errors */
241 /*******************************************************************************
243 * FUNCTION: AcpiDmBlockType
245 * PARAMETERS: Op - Object to be examined
247 * RETURN: BlockType - not a block, parens, braces, or even both.
249 * DESCRIPTION: Type of block for this op (parens or braces)
251 ******************************************************************************/
255 ACPI_PARSE_OBJECT *Op)
257 const ACPI_OPCODE_INFO *OpInfo;
265 switch (Op->Common.AmlOpcode)
269 return (BLOCK_BRACE);
274 case AML_PROCESSOR_OP:
275 case AML_POWER_RES_OP:
276 case AML_THERMAL_ZONE_OP:
280 case AML_INDEX_FIELD_OP:
281 case AML_BANK_FIELD_OP:
283 return (BLOCK_PAREN | BLOCK_BRACE);
287 if ((Op->Common.DisasmOpcode == ACPI_DASM_UNICODE) ||
288 (Op->Common.DisasmOpcode == ACPI_DASM_UUID) ||
289 (Op->Common.DisasmOpcode == ACPI_DASM_PLD_METHOD))
294 /*lint -fallthrough */
297 case AML_VAR_PACKAGE_OP:
299 return (BLOCK_PAREN | BLOCK_BRACE);
303 return (BLOCK_PAREN);
305 case AML_INT_METHODCALL_OP:
307 if (Op->Common.Parent &&
308 ((Op->Common.Parent->Common.AmlOpcode == AML_PACKAGE_OP) ||
309 (Op->Common.Parent->Common.AmlOpcode == AML_VAR_PACKAGE_OP)))
311 /* This is a reference to a method, not an invocation */
316 /*lint -fallthrough */
320 OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
321 if (OpInfo->Flags & AML_HAS_ARGS)
323 return (BLOCK_PAREN);
331 /*******************************************************************************
333 * FUNCTION: AcpiDmListType
335 * PARAMETERS: Op - Object to be examined
337 * RETURN: ListType - has commas or not.
339 * DESCRIPTION: Type of block for this op (parens or braces)
341 ******************************************************************************/
345 ACPI_PARSE_OBJECT *Op)
347 const ACPI_OPCODE_INFO *OpInfo;
355 switch (Op->Common.AmlOpcode)
362 case AML_POWER_RES_OP:
363 case AML_PROCESSOR_OP:
364 case AML_THERMAL_ZONE_OP:
368 case AML_INDEX_FIELD_OP:
369 case AML_BANK_FIELD_OP:
375 case AML_VAR_PACKAGE_OP:
377 return (BLOCK_COMMA_LIST);
381 OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
382 if (OpInfo->Flags & AML_HAS_ARGS)
384 return (BLOCK_COMMA_LIST);
392 /*******************************************************************************
394 * FUNCTION: AcpiDmDescendingOp
396 * PARAMETERS: ASL_WALK_CALLBACK
400 * DESCRIPTION: First visitation of a parse object during tree descent.
401 * Decode opcode name and begin parameter list(s), if any.
403 ******************************************************************************/
407 ACPI_PARSE_OBJECT *Op,
411 ACPI_OP_WALK_INFO *Info = Context;
412 const ACPI_OPCODE_INFO *OpInfo;
414 ACPI_PARSE_OBJECT *NextOp;
418 if (AcpiGbl_DbOpt_Verbose && AcpiGbl_PreviousOp)
420 /* Dump the entire statement in AML byte code */
422 if (Op->Common.Aml > AcpiGbl_PreviousOp->Common.Aml)
425 AcpiUtDumpBuffer (AcpiGbl_PreviousOp->Common.Aml,
426 (Op->Common.Aml - AcpiGbl_PreviousOp->Common.Aml),
428 AcpiDmIndent (Level);
431 AcpiGbl_PreviousOp = Op;
433 if (Op->Common.DisasmFlags & ACPI_PARSEOP_IGNORE)
435 /* Ignore this op -- it was handled elsewhere */
437 return (AE_CTRL_DEPTH);
440 /* Level 0 is at the Definition Block level */
444 /* In verbose mode, print the AML offset, opcode and depth count */
448 AmlOffset = (UINT32) ACPI_PTR_DIFF (Op->Common.Aml,
449 Info->WalkState->ParserState.AmlStart);
450 VERBOSE_PRINT ((DB_FULL_OP_INFO,
451 (Info->WalkState->MethodNode ?
452 Info->WalkState->MethodNode->Name.Ascii : " "),
453 AmlOffset, (UINT32) Op->Common.AmlOpcode));
456 if (Op->Common.AmlOpcode == AML_SCOPE_OP)
458 /* This is the beginning of the Definition Block */
460 AcpiOsPrintf ("{\n");
462 /* Emit all External() declarations here */
464 AcpiDmEmitExternals ();
468 else if ((AcpiDmBlockType (Op->Common.Parent) & BLOCK_BRACE) &&
469 (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST)) &&
470 (Op->Common.AmlOpcode != AML_INT_BYTELIST_OP))
473 * This is a first-level element of a term list,
476 switch (Op->Common.AmlOpcode)
480 * Optionally just ignore this opcode. Some tables use
481 * NoOp opcodes for "padding" out packages that the BIOS
482 * changes dynamically. This can leave hundreds or
483 * thousands of NoOp opcodes that if disassembled,
484 * cannot be compiled because they are syntactically
487 if (AcpiGbl_IgnoreNoopOperator)
489 Op->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
497 AcpiDmIndent (Level);
501 Info->LastLevel = Level;
506 * This is an inexpensive mechanism to try and keep lines from getting
507 * too long. When the limit is hit, start a new line at the previous
508 * indent plus one. A better but more expensive mechanism would be to
509 * keep track of the current column.
512 if (Info->Count /* +Info->LastLevel */ > 12)
516 AcpiDmIndent (Info->LastLevel + 1);
519 /* If ASL+ is enabled, check for a C-style operator */
521 if (AcpiDmCheckForSymbolicOpcode (Op, Info))
526 /* Print the opcode name */
528 AcpiDmDisassembleOneOp (NULL, Info, Op);
530 if ((Op->Common.DisasmOpcode == ACPI_DASM_LNOT_PREFIX) ||
531 (Op->Common.AmlOpcode == AML_INT_CONNECTION_OP))
536 if ((Op->Common.AmlOpcode == AML_NAME_OP) ||
537 (Op->Common.AmlOpcode == AML_RETURN_OP))
542 /* Start the opcode argument list if necessary */
544 OpInfo = AcpiPsGetOpcodeInfo (Op->Common.AmlOpcode);
546 if ((OpInfo->Flags & AML_HAS_ARGS) ||
547 (Op->Common.AmlOpcode == AML_EVENT_OP))
549 /* This opcode has an argument list */
551 if (AcpiDmBlockType (Op) & BLOCK_PAREN)
556 /* If this is a named opcode, print the associated name value */
558 if (OpInfo->Flags & AML_NAMED)
560 switch (Op->Common.AmlOpcode)
564 NextOp = AcpiPsGetDepthNext (NULL, Op);
565 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
566 AcpiDmNamestring (NextOp->Common.Value.Name);
569 /*lint -fallthrough */
573 Name = AcpiPsGetName (Op);
576 AcpiDmNamestring ((char *) Op->Named.Path);
580 AcpiDmDumpName (Name);
583 if (Op->Common.AmlOpcode != AML_INT_NAMEDFIELD_OP)
585 if (AcpiGbl_DbOpt_Verbose)
587 (void) AcpiPsDisplayObjectPathname (NULL, Op);
593 switch (Op->Common.AmlOpcode)
597 AcpiDmMethodFlags (Op);
600 /* Emit description comment for Method() with a predefined ACPI name */
602 AcpiDmPredefinedDescription (Op);
607 /* Check for _HID and related EISAID() */
609 AcpiDmCheckForHardwareId (Op);
615 AcpiDmRegionFlags (Op);
618 case AML_POWER_RES_OP:
620 /* Mark the next two Ops as part of the parameter list */
623 NextOp = AcpiPsGetDepthNext (NULL, Op);
624 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
626 NextOp = NextOp->Common.Next;
627 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
630 case AML_PROCESSOR_OP:
632 /* Mark the next three Ops as part of the parameter list */
635 NextOp = AcpiPsGetDepthNext (NULL, Op);
636 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
638 NextOp = NextOp->Common.Next;
639 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
641 NextOp = NextOp->Common.Next;
642 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
646 case AML_DATA_REGION_OP:
658 case AML_THERMAL_ZONE_OP:
665 AcpiOsPrintf ("*** Unhandled named opcode %X\n",
666 Op->Common.AmlOpcode);
671 else switch (Op->Common.AmlOpcode)
674 case AML_BANK_FIELD_OP:
675 case AML_INDEX_FIELD_OP:
679 /* Name of the parent OperationRegion */
681 NextOp = AcpiPsGetDepthNext (NULL, Op);
682 AcpiDmNamestring (NextOp->Common.Value.Name);
684 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
686 switch (Op->Common.AmlOpcode)
688 case AML_BANK_FIELD_OP:
690 /* Namestring - Bank Name */
692 NextOp = AcpiPsGetDepthNext (NULL, NextOp);
693 AcpiDmNamestring (NextOp->Common.Value.Name);
694 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
698 * Bank Value. This is a TermArg in the middle of the parameter
699 * list, must handle it here.
701 * Disassemble the TermArg parse tree. ACPI_PARSEOP_PARAMLIST
702 * eliminates newline in the output.
704 NextOp = NextOp->Common.Next;
706 Info->Flags = ACPI_PARSEOP_PARAMLIST;
707 AcpiDmWalkParseTree (NextOp, AcpiDmDescendingOp,
708 AcpiDmAscendingOp, Info);
712 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
716 case AML_INDEX_FIELD_OP:
718 /* Namestring - Data Name */
720 NextOp = AcpiPsGetDepthNext (NULL, NextOp);
721 AcpiDmNamestring (NextOp->Common.Value.Name);
723 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
731 AcpiDmFieldFlags (NextOp);
736 /* The next op is the size parameter */
738 NextOp = AcpiPsGetDepthNext (NULL, Op);
741 /* Single-step support */
746 if (Op->Common.DisasmOpcode == ACPI_DASM_RESOURCE)
749 * We have a resource list. Don't need to output
750 * the buffer size Op. Open up a new block
752 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_IGNORE;
753 NextOp = NextOp->Common.Next;
756 /* Emit description comment for Name() with a predefined ACPI name */
758 AcpiDmPredefinedDescription (Op->Asl.Parent);
761 AcpiDmIndent (Info->Level);
762 AcpiOsPrintf ("{\n");
766 /* Normal Buffer, mark size as in the parameter list */
768 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
771 case AML_VAR_PACKAGE_OP:
775 /* The next op is the size or predicate parameter */
777 NextOp = AcpiPsGetDepthNext (NULL, Op);
780 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
786 /* The next op is the size parameter */
788 NextOp = AcpiPsGetDepthNext (NULL, Op);
791 NextOp->Common.DisasmFlags |= ACPI_PARSEOP_PARAMLIST;
805 if (AcpiDmBlockType (Op) & BLOCK_BRACE)
808 AcpiDmIndent (Level);
809 AcpiOsPrintf ("{\n");
817 /*******************************************************************************
819 * FUNCTION: AcpiDmAscendingOp
821 * PARAMETERS: ASL_WALK_CALLBACK
825 * DESCRIPTION: Second visitation of a parse object, during ascent of parse
826 * tree. Close out any parameter lists and complete the opcode.
828 ******************************************************************************/
832 ACPI_PARSE_OBJECT *Op,
836 ACPI_OP_WALK_INFO *Info = Context;
837 ACPI_PARSE_OBJECT *ParentOp;
840 if (Op->Common.DisasmFlags & ACPI_PARSEOP_IGNORE)
842 /* Ignore this op -- it was handled elsewhere */
847 if ((Level == 0) && (Op->Common.AmlOpcode == AML_SCOPE_OP))
849 /* Indicates the end of the current descriptor block (table) */
851 AcpiOsPrintf ("}\n\n");
855 switch (AcpiDmBlockType (Op))
859 /* Completed an op that has arguments, add closing paren if needed */
861 AcpiDmCloseOperator (Op);
863 if (Op->Common.AmlOpcode == AML_NAME_OP)
865 /* Emit description comment for Name() with a predefined ACPI name */
867 AcpiDmPredefinedDescription (Op);
871 /* For Create* operators, attempt to emit resource tag description */
873 AcpiDmFieldPredefinedDescription (Op);
876 /* Decode Notify() values */
878 if (Op->Common.AmlOpcode == AML_NOTIFY_OP)
880 AcpiDmNotifyDescription (Op);
883 AcpiDmDisplayTargetPathname (Op);
885 /* Could be a nested operator, check if comma required */
887 if (!AcpiDmCommaIfListMember (Op))
889 if ((AcpiDmBlockType (Op->Common.Parent) & BLOCK_BRACE) &&
890 (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST)) &&
891 (Op->Common.AmlOpcode != AML_INT_BYTELIST_OP))
894 * This is a first-level element of a term list
897 if (!(Info->Flags & ACPI_PARSEOP_PARAMLIST))
906 case (BLOCK_BRACE | BLOCK_PAREN):
908 /* Completed an op that has a term list, add closing brace */
910 if (Op->Common.DisasmFlags & ACPI_PARSEOP_EMPTY_TERMLIST)
916 AcpiDmIndent (Level);
920 AcpiDmCommaIfListMember (Op);
922 if (AcpiDmBlockType (Op->Common.Parent) != BLOCK_PAREN)
925 if (!(Op->Common.DisasmFlags & ACPI_PARSEOP_EMPTY_TERMLIST))
927 if ((Op->Common.AmlOpcode == AML_IF_OP) &&
929 (Op->Common.Next->Common.AmlOpcode == AML_ELSE_OP))
934 if ((AcpiDmBlockType (Op->Common.Parent) & BLOCK_BRACE) &&
947 /* Could be a nested operator, check if comma required */
949 if (!AcpiDmCommaIfListMember (Op))
951 if ((AcpiDmBlockType (Op->Common.Parent) & BLOCK_BRACE) &&
952 (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST)) &&
953 (Op->Common.AmlOpcode != AML_INT_BYTELIST_OP))
956 * This is a first-level element of a term list
962 else if (Op->Common.Parent)
964 switch (Op->Common.Parent->Common.AmlOpcode)
967 case AML_VAR_PACKAGE_OP:
969 if (!(Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST))
983 if (Op->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST)
985 if ((Op->Common.Next) &&
986 (Op->Common.Next->Common.DisasmFlags & ACPI_PARSEOP_PARAMLIST))
992 * The parent Op is guaranteed to be valid because of the flag
993 * ACPI_PARSEOP_PARAMLIST -- which means that this op is part of
994 * a parameter list and thus has a valid parent.
996 ParentOp = Op->Common.Parent;
999 * Just completed a parameter node for something like "Buffer (param)".
1000 * Close the paren and open up the term list block with a brace
1002 if (Op->Common.Next)
1007 * Emit a description comment for a Name() operator that is a
1008 * predefined ACPI name. Must check the grandparent.
1010 ParentOp = ParentOp->Common.Parent;
1012 (ParentOp->Asl.AmlOpcode == AML_NAME_OP))
1014 AcpiDmPredefinedDescription (ParentOp);
1017 AcpiOsPrintf ("\n");
1018 AcpiDmIndent (Level - 1);
1019 AcpiOsPrintf ("{\n");
1023 ParentOp->Common.DisasmFlags |= ACPI_PARSEOP_EMPTY_TERMLIST;
1024 AcpiOsPrintf (") {");
1028 if ((Op->Common.AmlOpcode == AML_NAME_OP) ||
1029 (Op->Common.AmlOpcode == AML_RETURN_OP))
1035 * For ASL+, check for and emit a C-style symbol. If valid, the
1036 * symbol string has been deferred until after the first operand
1038 if (AcpiGbl_CstyleDisassembly)
1040 if (Op->Asl.OperatorSymbol)
1042 AcpiOsPrintf ("%s", Op->Asl.OperatorSymbol);
1043 Op->Asl.OperatorSymbol = NULL;
1050 #endif /* ACPI_DISASSEMBLER */