/****************************************************************************** * * Module Name: asltree - Parse tree management * *****************************************************************************/ /* * Copyright (C) 2000 - 2023, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions, and the following disclaimer, * without modification. * 2. Redistributions in binary form must reproduce at minimum a disclaimer * substantially similar to the "NO WARRANTY" disclaimer below * ("Disclaimer") and any redistribution must be conditioned upon * including a substantially similar Disclaimer requirement for further * binary redistribution. * 3. Neither the names of the above-listed copyright holders nor the names * of any contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * Alternatively, this software may be distributed under the terms of the * GNU General Public License ("GPL") version 2 as published by the Free * Software Foundation. * * NO WARRANTY * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ #include "aslcompiler.h" #include "aslcompiler.y.h" #include "acapps.h" #define _COMPONENT ACPI_COMPILER ACPI_MODULE_NAME ("asltree") /******************************************************************************* * * FUNCTION: TrSetOpIntegerValue * * PARAMETERS: ParseOpcode - New opcode to be assigned to the op * Op - An existing parse op * * RETURN: The updated op * * DESCRIPTION: Used to set the integer value of a op, * usually to a specific size (8, 16, 32, or 64 bits) * ******************************************************************************/ ACPI_PARSE_OBJECT * TrSetOpIntegerValue ( UINT32 ParseOpcode, ACPI_PARSE_OBJECT *Op) { if (!Op) { return (NULL); } DbgPrint (ASL_PARSE_OUTPUT, "\nUpdateOp: Old - %s, New - %s\n", UtGetOpName (Op->Asl.ParseOpcode), UtGetOpName (ParseOpcode)); /* Assign new opcode and name */ if (Op->Asl.ParseOpcode == PARSEOP_ONES) { switch (ParseOpcode) { case PARSEOP_BYTECONST: Op->Asl.Value.Integer = ACPI_UINT8_MAX; break; case PARSEOP_WORDCONST: Op->Asl.Value.Integer = ACPI_UINT16_MAX; break; case PARSEOP_DWORDCONST: Op->Asl.Value.Integer = ACPI_UINT32_MAX; break; /* Don't need to do the QWORD case */ default: /* Don't care about others */ break; } } Op->Asl.ParseOpcode = (UINT16) ParseOpcode; UtSetParseOpName (Op); /* * For the BYTE, WORD, and DWORD constants, make sure that the integer * that was passed in will actually fit into the data type */ switch (ParseOpcode) { case PARSEOP_BYTECONST: UtCheckIntegerRange (Op, 0x00, ACPI_UINT8_MAX); Op->Asl.Value.Integer &= ACPI_UINT8_MAX; break; case PARSEOP_WORDCONST: UtCheckIntegerRange (Op, 0x00, ACPI_UINT16_MAX); Op->Asl.Value.Integer &= ACPI_UINT16_MAX; break; case PARSEOP_DWORDCONST: UtCheckIntegerRange (Op, 0x00, ACPI_UINT32_MAX); Op->Asl.Value.Integer &= ACPI_UINT32_MAX; break; default: /* Don't care about others, don't need to check QWORD */ break; } /* Converter: if this is a method invocation, turn off capture comments */ if (AcpiGbl_CaptureComments && (ParseOpcode == PARSEOP_METHODCALL)) { AslGbl_CommentState.CaptureComments = FALSE; } return (Op); } /******************************************************************************* * * FUNCTION: TrSetOpFlags * * PARAMETERS: Op - An existing parse op * Flags - New flags word * * RETURN: The updated parser op * * DESCRIPTION: Set bits in the op flags word. Will not clear bits, only set * ******************************************************************************/ ACPI_PARSE_OBJECT * TrSetOpFlags ( ACPI_PARSE_OBJECT *Op, UINT32 Flags) { if (!Op) { return (NULL); } DbgPrint (ASL_PARSE_OUTPUT, "\nSetOpFlags: %s Op %p, %8.8X", Op->Asl.ParseOpName, Op, Flags); TrPrintOpFlags (Flags, ASL_PARSE_OUTPUT); DbgPrint (ASL_PARSE_OUTPUT, "\n\n"); Op->Asl.CompileFlags |= Flags; return (Op); } /******************************************************************************* * * FUNCTION: TrSetOpAmlLength * * PARAMETERS: Op - An existing parse op * Length - AML Length * * RETURN: The updated parser op * * DESCRIPTION: Set the AML Length in a op. Used by the parser to indicate * the presence of a op that must be reduced to a fixed length * constant. * ******************************************************************************/ ACPI_PARSE_OBJECT * TrSetOpAmlLength ( ACPI_PARSE_OBJECT *Op, UINT32 Length) { DbgPrint (ASL_PARSE_OUTPUT, "\nSetOpAmlLength: Op %p, %8.8X\n", Op, Length); if (!Op) { return (NULL); } Op->Asl.AmlLength = Length; return (Op); } /******************************************************************************* * * FUNCTION: TrSetOpParent * * PARAMETERS: Op - To be set to new parent * ParentOp - The parent * * RETURN: None, sets Op parent directly * * DESCRIPTION: Change the parent of a parse op. * ******************************************************************************/ void TrSetOpParent ( ACPI_PARSE_OBJECT *Op, ACPI_PARSE_OBJECT *ParentOp) { Op->Asl.Parent = ParentOp; } /******************************************************************************* * * FUNCTION: TrSetOpCurrentFilename * * PARAMETERS: Op - An existing parse op * * RETURN: None * * DESCRIPTION: Save the include file filename. Used for debug output only. * ******************************************************************************/ void TrSetOpCurrentFilename ( ACPI_PARSE_OBJECT *Op) { Op->Asl.Filename = AslGbl_PreviousIncludeFilename; } /******************************************************************************* * * FUNCTION: TrSetOpIntegerWidth * * PARAMETERS: Op - An existing parse op * * RETURN: None * * DESCRIPTION: * ******************************************************************************/ void TrSetOpIntegerWidth ( ACPI_PARSE_OBJECT *TableSignatureOp, ACPI_PARSE_OBJECT *RevisionOp) { /* TBD: Check table sig? (DSDT vs. SSDT) */ /* Handle command-line version override */ if (AslGbl_RevisionOverride) { AcpiUtSetIntegerWidth (AslGbl_RevisionOverride); } else { AcpiUtSetIntegerWidth ((UINT8) RevisionOp->Asl.Value.Integer); } } /******************************************************************************* * * FUNCTION: TrSetOpEndLineNumber * * PARAMETERS: Op - An existing parse op * * RETURN: None. * * DESCRIPTION: Set the ending line numbers (file line and logical line) of a * parse op to the current line numbers. * ******************************************************************************/ void TrSetOpEndLineNumber ( ACPI_PARSE_OBJECT *Op) { /* If the end line # is already set, just return */ if (Op->Asl.EndLine) { return; } Op->Asl.EndLine = AslGbl_CurrentLineNumber; Op->Asl.EndLogicalLine = AslGbl_LogicalLineNumber; } /******************************************************************************* * * FUNCTION: TrLinkOpChildren * * PARAMETERS: Op - An existing parse op * NumChildren - Number of children to follow * ... - A list of child ops to link to the new * op. NumChildren long. * * RETURN: The updated (linked) op * * DESCRIPTION: Link a group of ops to an existing parse op * ******************************************************************************/ ACPI_PARSE_OBJECT * TrLinkOpChildren ( ACPI_PARSE_OBJECT *Op, UINT32 NumChildren, ...) { ACPI_PARSE_OBJECT *Child; ACPI_PARSE_OBJECT *PrevChild; ACPI_PARSE_OBJECT *LastSibling; va_list ap; UINT32 i; BOOLEAN FirstChild; ACPI_FUNCTION_NAME (TrLinkOpChildren); va_start (ap, NumChildren); TrSetOpEndLineNumber (Op); DbgPrint (ASL_PARSE_OUTPUT, "\nLinkChildren Line [%u to %u] NewParent %p Child %u Op %s ", Op->Asl.LineNumber, Op->Asl.EndLine, Op, NumChildren, UtGetOpName(Op->Asl.ParseOpcode)); switch (Op->Asl.ParseOpcode) { case PARSEOP_ASL_CODE: if (!AslGbl_ParseTreeRoot) { DbgPrint (ASL_PARSE_OUTPUT, "Creating first Definition Block\n"); AslGbl_ParseTreeRoot = Op; Op->Asl.ParseOpcode = PARSEOP_DEFAULT_ARG; } else { DbgPrint (ASL_PARSE_OUTPUT, "Creating subsequent Definition Block\n"); Op = AslGbl_ParseTreeRoot; } DbgPrint (ASL_PARSE_OUTPUT, "ASLCODE (Tree Completed)->"); break; case PARSEOP_DEFINITION_BLOCK: DbgPrint (ASL_PARSE_OUTPUT, "DEFINITION_BLOCK (Tree Completed)->"); break; case PARSEOP_OPERATIONREGION: DbgPrint (ASL_PARSE_OUTPUT, "OPREGION->"); break; case PARSEOP_OR: DbgPrint (ASL_PARSE_OUTPUT, "OR->"); break; default: /* Nothing to do for other opcodes */ break; } /* The following is for capturing comments */ if (AcpiGbl_CaptureComments) { /* * If there are "regular comments" detected at this point, * then is an endBlk comment. Categorize it as so and distribute * all regular comments to this parse op. */ if (AslGbl_CommentListHead) { Op->Asl.EndBlkComment = AslGbl_CommentListHead; CvDbgPrint ("EndBlk Comment for %s: %s", Op->Asl.ParseOpName, AslGbl_CommentListHead->Comment); AslGbl_CommentListHead = NULL; AslGbl_CommentListTail = NULL; } } /* Link the new op to it's children */ PrevChild = NULL; FirstChild = TRUE; for (i = 0; i < NumChildren; i++) { Child = va_arg (ap, ACPI_PARSE_OBJECT *); if ((Child == PrevChild) && (Child != NULL)) { AslError (ASL_WARNING, ASL_MSG_COMPILER_INTERNAL, Child, "Child op list invalid"); va_end(ap); return (Op); } DbgPrint (ASL_PARSE_OUTPUT, "%p, ", Child); /* * If child is NULL, this means that an optional argument * was omitted. We must create a placeholder with a special * opcode (DEFAULT_ARG) so that the code generator will know * that it must emit the correct default for this argument */ if (!Child) { Child = TrAllocateOp (PARSEOP_DEFAULT_ARG); } /* Link first child to parent */ if (FirstChild) { FirstChild = FALSE; /* * In the case that multiple definition blocks are being compiled, * append the definition block to the end of the child list as the * last sibling. This is done to facilitate namespace cross- * reference between multiple definition blocks. */ if (Op->Asl.Child && (Op->Asl.Child->Asl.ParseOpcode == PARSEOP_DEFINITION_BLOCK)) { LastSibling = Op->Asl.Child; while (LastSibling->Asl.Next) { LastSibling = LastSibling->Asl.Next; } LastSibling->Asl.Next = Child; } else { Op->Asl.Child = Child; } } /* Point all children to parent */ Child->Asl.Parent = Op; /* Link children in a peer list */ if (PrevChild) { PrevChild->Asl.Next = Child; } /* * This child might be a list, point all ops in the list * to the same parent */ while (Child->Asl.Next) { Child = Child->Asl.Next; Child->Asl.Parent = Op; } PrevChild = Child; } va_end(ap); DbgPrint (ASL_PARSE_OUTPUT, "\n\n"); if (AcpiGbl_CaptureComments) { AslGbl_CommentState.LatestParseOp = Op; CvDbgPrint ("%s=====Set latest parse op to this op.\n", ACPI_GET_FUNCTION_NAME); } return (Op); } /******************************************************************************* * * FUNCTION: TrLinkPeerOp * * PARAMETERS: Op1 - First peer * Op2 - Second peer * * RETURN: Op1 or the non-null op. * * DESCRIPTION: Link two ops as peers. Handles cases where one peer is null. * ******************************************************************************/ ACPI_PARSE_OBJECT * TrLinkPeerOp ( ACPI_PARSE_OBJECT *Op1, ACPI_PARSE_OBJECT *Op2) { ACPI_PARSE_OBJECT *Next; DbgPrint (ASL_PARSE_OUTPUT, "\nLinkPeerOp: 1=%p (%s), 2=%p (%s)\n", Op1, Op1 ? UtGetOpName(Op1->Asl.ParseOpcode) : NULL, Op2, Op2 ? UtGetOpName(Op2->Asl.ParseOpcode) : NULL); if ((!Op1) && (!Op2)) { DbgPrint (ASL_PARSE_OUTPUT, "\nTwo Null ops!\n"); return (Op1); } /* If one of the ops is null, just return the non-null op */ if (!Op2) { return (Op1); } if (!Op1) { return (Op2); } if (Op1 == Op2) { DbgPrint (ASL_DEBUG_OUTPUT, "\n************* Internal error, linking op to itself %p\n", Op1); AslError (ASL_WARNING, ASL_MSG_COMPILER_INTERNAL, Op1, "Linking op to itself"); return (Op1); } Op1->Asl.Parent = Op2->Asl.Parent; /* * Op 1 may already have a peer list (such as an IF/ELSE pair), * so we must walk to the end of the list and attach the new * peer at the end */ Next = Op1; while (Next->Asl.Next) { Next = Next->Asl.Next; } Next->Asl.Next = Op2; return (Op1); } /******************************************************************************* * * FUNCTION: TrLinkPeerOps * * PARAMETERS: NumPeers - The number of ops in the list to follow * ... - A list of ops to link together as peers * * RETURN: The first op in the list (head of the peer list) * * DESCRIPTION: Link together an arbitrary number of peer ops. * ******************************************************************************/ ACPI_PARSE_OBJECT * TrLinkPeerOps ( UINT32 NumPeers, ...) { ACPI_PARSE_OBJECT *This; ACPI_PARSE_OBJECT *Next; va_list ap; UINT32 i; ACPI_PARSE_OBJECT *Start; DbgPrint (ASL_PARSE_OUTPUT, "\nLinkPeerOps: (%u) ", NumPeers); va_start (ap, NumPeers); This = va_arg (ap, ACPI_PARSE_OBJECT *); Start = This; /* * Link all peers */ for (i = 0; i < (NumPeers -1); i++) { DbgPrint (ASL_PARSE_OUTPUT, "%u=%p ", (i+1), This); while (This->Asl.Next) { This = This->Asl.Next; } /* Get another peer op */ Next = va_arg (ap, ACPI_PARSE_OBJECT *); if (!Next) { Next = TrAllocateOp (PARSEOP_DEFAULT_ARG); } /* link new op to the current op */ This->Asl.Next = Next; This = Next; } va_end (ap); DbgPrint (ASL_PARSE_OUTPUT,"\n"); return (Start); } /******************************************************************************* * * FUNCTION: TrLinkChildOp * * PARAMETERS: Op1 - Parent op * Op2 - Op to become a child * * RETURN: The parent op * * DESCRIPTION: Link two ops together as a parent and child * ******************************************************************************/ ACPI_PARSE_OBJECT * TrLinkChildOp ( ACPI_PARSE_OBJECT *Op1, ACPI_PARSE_OBJECT *Op2) { ACPI_PARSE_OBJECT *Next; DbgPrint (ASL_PARSE_OUTPUT, "\nLinkChildOp: Parent=%p (%s), Child=%p (%s)\n", Op1, Op1 ? UtGetOpName(Op1->Asl.ParseOpcode): NULL, Op2, Op2 ? UtGetOpName(Op2->Asl.ParseOpcode): NULL); /* * Converter: if TrLinkChildOp is called to link a method call, * turn on capture comments as it signifies that we are done parsing * a method call. */ if (AcpiGbl_CaptureComments && Op1) { if (Op1->Asl.ParseOpcode == PARSEOP_METHODCALL) { AslGbl_CommentState.CaptureComments = TRUE; } AslGbl_CommentState.LatestParseOp = Op1; } if (!Op1 || !Op2) { return (Op1); } Op1->Asl.Child = Op2; /* Set the child and all peers of the child to point to the parent */ Next = Op2; while (Next) { Next->Asl.Parent = Op1; Next = Next->Asl.Next; } return (Op1); } /******************************************************************************* * * FUNCTION: TrWalkParseTree * * PARAMETERS: Op - Walk starting point * Visitation - Type of walk * DescendingCallback - Called during tree descent * AscendingCallback - Called during tree ascent * Context - To be passed to the callbacks * * RETURN: Status from callback(s) * * DESCRIPTION: Walk the entire parse tree. * ******************************************************************************/ ACPI_STATUS TrWalkParseTree ( ACPI_PARSE_OBJECT *Op, UINT32 Visitation, ASL_WALK_CALLBACK DescendingCallback, ASL_WALK_CALLBACK AscendingCallback, void *Context) { UINT32 Level; BOOLEAN OpPreviouslyVisited; ACPI_PARSE_OBJECT *StartOp = Op; ACPI_STATUS Status; ACPI_PARSE_OBJECT *Restore = NULL; BOOLEAN WalkOneDefinitionBlock = Visitation & ASL_WALK_VISIT_DB_SEPARATELY; if (!AslGbl_ParseTreeRoot) { return (AE_OK); } Level = 0; OpPreviouslyVisited = FALSE; if (Op->Asl.ParseOpcode == PARSEOP_DEFINITION_BLOCK && WalkOneDefinitionBlock) { Restore = Op->Asl.Next; Op->Asl.Next = NULL; } switch (Visitation & ~ASL_WALK_VISIT_DB_SEPARATELY) { case ASL_WALK_VISIT_DOWNWARD: while (Op) { if (!OpPreviouslyVisited) { /* Let the callback process the op. */ Status = DescendingCallback (Op, Level, Context); if (ACPI_SUCCESS (Status)) { /* Visit children first, once */ if (Op->Asl.Child) { Level++; Op = Op->Asl.Child; continue; } } else if (Status != AE_CTRL_DEPTH) { /* Exit immediately on any error */ goto ErrorExit; } } /* Terminate walk at start op */ if (Op == StartOp) { break; } /* No more children, visit peers */ if (Op->Asl.Next) { Op = Op->Asl.Next; OpPreviouslyVisited = FALSE; } else { /* No children or peers, re-visit parent */ if (Level != 0 ) { Level--; } Op = Op->Asl.Parent; OpPreviouslyVisited = TRUE; } } break; case ASL_WALK_VISIT_UPWARD: while (Op) { /* Visit leaf op (no children) or parent op on return trip */ if ((!Op->Asl.Child) || (OpPreviouslyVisited)) { /* Let the callback process the op. */ Status = AscendingCallback (Op, Level, Context); if (ACPI_FAILURE (Status)) { goto ErrorExit; } } else { /* Visit children first, once */ Level++; Op = Op->Asl.Child; continue; } /* Terminate walk at start op */ if (Op == StartOp) { break; } /* No more children, visit peers */ if (Op->Asl.Next) { Op = Op->Asl.Next; OpPreviouslyVisited = FALSE; } else { /* No children or peers, re-visit parent */ if (Level != 0 ) { Level--; } Op = Op->Asl.Parent; OpPreviouslyVisited = TRUE; } } break; case ASL_WALK_VISIT_TWICE: while (Op) { if (OpPreviouslyVisited) { Status = AscendingCallback (Op, Level, Context); if (ACPI_FAILURE (Status)) { goto ErrorExit; } } else { /* Let the callback process the op. */ Status = DescendingCallback (Op, Level, Context); if (ACPI_SUCCESS (Status)) { /* Visit children first, once */ if (Op->Asl.Child) { Level++; Op = Op->Asl.Child; continue; } } else if (Status != AE_CTRL_DEPTH) { /* Exit immediately on any error */ goto ErrorExit; } } /* Terminate walk at start op */ if (Op == StartOp) { break; } /* No more children, visit peers */ if (Op->Asl.Next) { Op = Op->Asl.Next; OpPreviouslyVisited = FALSE; } else { /* No children or peers, re-visit parent */ if (Level != 0 ) { Level--; } Op = Op->Asl.Parent; OpPreviouslyVisited = TRUE; } } break; default: /* No other types supported */ break; } /* If we get here, the walk completed with no errors */ if (Op->Asl.ParseOpcode == PARSEOP_DEFINITION_BLOCK && WalkOneDefinitionBlock) { Op->Asl.Next = Restore; } return (AE_OK); ErrorExit: if (Op->Asl.ParseOpcode == PARSEOP_DEFINITION_BLOCK && WalkOneDefinitionBlock) { Op->Asl.Next = Restore; } return (Status); }