| /** @file | |
| AML Tree Traversal. | |
| Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Tree/AmlTreeTraversal.h> | |
| #include <AmlCoreInterface.h> | |
| #include <Tree/AmlTree.h> | |
| /** Get the sibling node among the nodes being in | |
| the same variable argument list. | |
| (ParentNode) /-i # Child of fixed argument b | |
| \ / | |
| |- [a][b][c][d] # Fixed Arguments | |
| |- {(VarArgNode)->(f)->(g)} # Variable Arguments | |
| \ | |
| \-h # Child of variable argument e | |
| Node must be in a variable list of arguments. | |
| Traversal Order: VarArgNode, f, g, NULL | |
| @ingroup CoreNavigationApis | |
| @param [in] VarArgNode Pointer to a node. | |
| Must be in a variable list of arguments. | |
| @return The next node after VarArgNode in the variable list of arguments. | |
| Return NULL if | |
| - VarArgNode is the last node of the list, or | |
| - VarArgNode is not part of a variable list of arguments. | |
| **/ | |
| AML_NODE_HEADER * | |
| EFIAPI | |
| AmlGetSiblingVariableArgument ( | |
| IN AML_NODE_HEADER *VarArgNode | |
| ) | |
| { | |
| EAML_PARSE_INDEX Index; | |
| AML_NODE_HEADER *ParentNode; | |
| // VarArgNode must be an object node or a data node, | |
| // and be in a variable list of arguments. | |
| if ((!IS_AML_OBJECT_NODE (VarArgNode) && | |
| !IS_AML_DATA_NODE (VarArgNode)) || | |
| AmlIsNodeFixedArgument (VarArgNode, &Index)) | |
| { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| ParentNode = AmlGetParent (VarArgNode); | |
| if (!IS_AML_NODE_VALID (ParentNode)) { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| return AmlGetNextVariableArgument (ParentNode, VarArgNode); | |
| } | |
| /** Get the next variable argument. | |
| (Node) /-i # Child of fixed argument b | |
| \ / | |
| |- [a][b][c][d] # Fixed Arguments | |
| |- {(e)->(f)->(g)} # Variable Arguments | |
| \ | |
| \-h # Child of variable argument e | |
| Traversal Order: e, f, g, NULL | |
| @param [in] Node Pointer to a Root node or Object Node. | |
| @param [in] CurrVarArg Pointer to the Current Variable Argument. | |
| @return The node after the CurrVarArg in the variable list of arguments. | |
| If CurrVarArg is NULL, return the first node of the | |
| variable argument list. | |
| Return NULL if | |
| - CurrVarArg is the last node of the list, or | |
| - Node does not have a variable list of arguments. | |
| **/ | |
| AML_NODE_HEADER * | |
| EFIAPI | |
| AmlGetNextVariableArgument ( | |
| IN AML_NODE_HEADER *Node, | |
| IN AML_NODE_HEADER *CurrVarArg | |
| ) | |
| { | |
| CONST LIST_ENTRY *StartLink; | |
| CONST LIST_ENTRY *NextLink; | |
| // Node must be a RootNode or an Object Node | |
| // and the CurrVarArg must not be a Root Node. | |
| if ((!IS_AML_ROOT_NODE (Node) && | |
| !IS_AML_OBJECT_NODE (Node)) || | |
| ((CurrVarArg != NULL) && | |
| (!IS_AML_OBJECT_NODE (CurrVarArg) && | |
| !IS_AML_DATA_NODE (CurrVarArg)))) | |
| { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| StartLink = AmlNodeGetVariableArgList (Node); | |
| if (StartLink == NULL) { | |
| return NULL; | |
| } | |
| // Get the first child of the variable list of arguments. | |
| if (CurrVarArg == NULL) { | |
| NextLink = StartLink->ForwardLink; | |
| if (NextLink != StartLink) { | |
| return (AML_NODE_HEADER *)NextLink; | |
| } | |
| // List is empty. | |
| return NULL; | |
| } | |
| // Check if CurrVarArg is in the VariableArgument List. | |
| if (!IsNodeInList (StartLink, &CurrVarArg->Link)) { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| // Get the node following the CurrVarArg. | |
| NextLink = CurrVarArg->Link.ForwardLink; | |
| if (NextLink != StartLink) { | |
| return (AML_NODE_HEADER *)NextLink; | |
| } | |
| // End of the list has been reached. | |
| return NULL; | |
| } | |
| /** Get the previous variable argument. | |
| (Node) /-i # Child of fixed argument b | |
| \ / | |
| |- [a][b][c][d] # Fixed Arguments | |
| |- {(e)->(f)->(g)} # Variable Arguments | |
| \ | |
| \-h # Child of variable argument e | |
| Traversal Order: g, f, e, NULL | |
| @param [in] Node Pointer to a root node or an object node. | |
| @param [in] CurrVarArg Pointer to the Current Variable Argument. | |
| @return The node before the CurrVarArg in the variable list of | |
| arguments. | |
| If CurrVarArg is NULL, return the last node of the | |
| variable list of arguments. | |
| Return NULL if: | |
| - CurrVarArg is the first node of the list, or | |
| - Node doesn't have a variable list of arguments. | |
| **/ | |
| AML_NODE_HEADER * | |
| EFIAPI | |
| AmlGetPreviousVariableArgument ( | |
| IN AML_NODE_HEADER *Node, | |
| IN AML_NODE_HEADER *CurrVarArg | |
| ) | |
| { | |
| CONST LIST_ENTRY *StartLink; | |
| CONST LIST_ENTRY *PreviousLink; | |
| // Node must be a RootNode or an Object Node | |
| // and the CurrVarArg must not be a Root Node. | |
| if ((!IS_AML_ROOT_NODE (Node) && | |
| !IS_AML_OBJECT_NODE (Node)) || | |
| ((CurrVarArg != NULL) && | |
| (!IS_AML_OBJECT_NODE (CurrVarArg) && | |
| !IS_AML_DATA_NODE (CurrVarArg)))) | |
| { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| StartLink = AmlNodeGetVariableArgList (Node); | |
| if (StartLink == NULL) { | |
| return NULL; | |
| } | |
| // Get the last child of the variable list of arguments. | |
| if (CurrVarArg == NULL) { | |
| PreviousLink = StartLink->BackLink; | |
| if (PreviousLink != StartLink) { | |
| return (AML_NODE_HEADER *)PreviousLink; | |
| } | |
| // List is empty. | |
| return NULL; | |
| } | |
| // Check if CurrVarArg is in the VariableArgument List. | |
| if (!IsNodeInList (StartLink, &CurrVarArg->Link)) { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| // Get the node before the CurrVarArg. | |
| PreviousLink = CurrVarArg->Link.BackLink; | |
| if (PreviousLink != StartLink) { | |
| return (AML_NODE_HEADER *)PreviousLink; | |
| } | |
| // We have reached the beginning of the list. | |
| return NULL; | |
| } | |
| /** Get the next sibling node among the children of the input Node. | |
| This function traverses the FixedArguments followed by the | |
| VariableArguments at the same level in the hierarchy. | |
| Fixed arguments are before variable arguments. | |
| (Node) /-i # Child of fixed argument b | |
| \ / | |
| |- [a][b][c][d] # Fixed Arguments | |
| |- {(e)->(f)->(g)} # Variable Arguments | |
| \ | |
| \-h # Child of variable argument e | |
| Traversal Order: a, b, c, d, e, f, g, NULL | |
| @param [in] Node Pointer to a root node or an object node. | |
| @param [in] ChildNode Get the node after the ChildNode. | |
| @return The node after the ChildNode among the children of the input Node. | |
| - If ChildNode is NULL, return the first available node among | |
| the fixed argument list then variable list of arguments; | |
| - If ChildNode is the last node of the fixed argument list, | |
| return the first argument of the variable list of arguments; | |
| - If ChildNode is the last node of the variable list of arguments, | |
| return NULL. | |
| **/ | |
| AML_NODE_HEADER * | |
| EFIAPI | |
| AmlGetNextSibling ( | |
| IN CONST AML_NODE_HEADER *Node, | |
| IN CONST AML_NODE_HEADER *ChildNode | |
| ) | |
| { | |
| EAML_PARSE_INDEX Index; | |
| AML_NODE_HEADER *CandidateNode; | |
| // Node must be a RootNode or an Object Node | |
| // and the CurrVarArg must not be a Root Node. | |
| if ((!IS_AML_ROOT_NODE (Node) && | |
| !IS_AML_OBJECT_NODE (Node)) || | |
| ((ChildNode != NULL) && | |
| (!IS_AML_OBJECT_NODE (ChildNode) && | |
| !IS_AML_DATA_NODE (ChildNode)))) | |
| { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| if (IS_AML_OBJECT_NODE (Node)) { | |
| if (ChildNode == NULL) { | |
| // Get the fixed argument at index 0 of the ChildNode. | |
| CandidateNode = AmlGetFixedArgument ( | |
| (AML_OBJECT_NODE *)Node, | |
| EAmlParseIndexTerm0 | |
| ); | |
| if (CandidateNode != NULL) { | |
| return CandidateNode; | |
| } | |
| } else { | |
| // (ChildNode != NULL) | |
| if (AmlIsNodeFixedArgument (ChildNode, &Index)) { | |
| // Increment index to point to the next fixed argument. | |
| Index++; | |
| // The node is part of the list of fixed arguments. | |
| if (Index == (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( | |
| (AML_OBJECT_NODE *)Node | |
| ) | |
| ) | |
| { | |
| // It is at the last argument of the fixed argument list. | |
| // Get the first argument of the variable list of arguments. | |
| ChildNode = NULL; | |
| } else { | |
| // Else return the next node in the list of fixed arguments. | |
| return AmlGetFixedArgument ((AML_OBJECT_NODE *)Node, Index); | |
| } | |
| } | |
| } | |
| } // IS_AML_OBJECT_NODE (Node) | |
| // Else, get the next node in the variable list of arguments. | |
| return AmlGetNextVariableArgument ( | |
| (AML_NODE_HEADER *)Node, | |
| (AML_NODE_HEADER *)ChildNode | |
| ); | |
| } | |
| /** Get the previous sibling node among the children of the input Node. | |
| This function traverses the FixedArguments followed by the | |
| VariableArguments at the same level in the hierarchy. | |
| Fixed arguments are before variable arguments. | |
| (Node) /-i # Child of fixed argument b | |
| \ / | |
| |- [a][b][c][d] # Fixed Arguments | |
| |- {(e)->(f)->(g)} # Variable Arguments | |
| \ | |
| \-h # Child of variable argument e | |
| Traversal Order: g, f, e, d, c, b, a, NULL | |
| @param [in] Node The node to get the fixed argument from. | |
| @param [in] ChildNode Get the node before the ChildNode. | |
| @return The node before the ChildNode among the children of the input Node. | |
| - If ChildNode is NULL, return the last available node among | |
| the variable list of arguments then fixed argument list; | |
| - If ChildNode is the first node of the variable list of arguments, | |
| return the last argument of the fixed argument list; | |
| - If ChildNode is the first node of the fixed argument list, | |
| return NULL. | |
| **/ | |
| AML_NODE_HEADER * | |
| EFIAPI | |
| AmlGetPreviousSibling ( | |
| IN CONST AML_NODE_HEADER *Node, | |
| IN CONST AML_NODE_HEADER *ChildNode | |
| ) | |
| { | |
| EAML_PARSE_INDEX Index; | |
| EAML_PARSE_INDEX MaxIndex; | |
| AML_NODE_HEADER *CandidateNode; | |
| // Node must be a Root Node or an Object Node | |
| // and the ChildNode must not be a Root Node. | |
| if ((!IS_AML_ROOT_NODE (Node) && | |
| !IS_AML_OBJECT_NODE (Node)) || | |
| ((ChildNode != NULL) && | |
| (!IS_AML_OBJECT_NODE (ChildNode) && | |
| !IS_AML_DATA_NODE (ChildNode)))) | |
| { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount ( | |
| (AML_OBJECT_NODE *)Node | |
| ); | |
| // Get the last variable argument if no ChildNode. | |
| // Otherwise the fixed argument list is checked first. | |
| if ((ChildNode != NULL) && | |
| IS_AML_OBJECT_NODE (Node) && | |
| (MaxIndex != EAmlParseIndexTerm0)) | |
| { | |
| if (AmlIsNodeFixedArgument (ChildNode, &Index)) { | |
| // The node is part of the list of fixed arguments. | |
| if (Index == EAmlParseIndexTerm0) { | |
| // The node is the first fixed argument, return NULL. | |
| return NULL; | |
| } else { | |
| // Return the previous node in the fixed argument list. | |
| return AmlGetFixedArgument ( | |
| (AML_OBJECT_NODE *)Node, | |
| (EAML_PARSE_INDEX)(Index - 1) | |
| ); | |
| } | |
| } | |
| } | |
| // ChildNode is in the variable list of arguments. | |
| CandidateNode = AmlGetPreviousVariableArgument ( | |
| (AML_NODE_HEADER *)Node, | |
| (AML_NODE_HEADER *)ChildNode | |
| ); | |
| if (CandidateNode != NULL) { | |
| if (!IS_AML_NODE_VALID (CandidateNode)) { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| // A Node has been found | |
| return CandidateNode; | |
| } else if (MaxIndex != EAmlParseIndexTerm0) { | |
| // ChildNode was the first node of the variable list of arguments. | |
| return AmlGetFixedArgument ( | |
| (AML_OBJECT_NODE *)Node, | |
| (EAML_PARSE_INDEX)(MaxIndex - 1) | |
| ); | |
| } else { | |
| // No fixed arguments or variable arguments. | |
| return NULL; | |
| } | |
| } | |
| /** Iterate through the nodes in the same order as the AML bytestream. | |
| The iteration is similar to a depth-first path. | |
| (Node) /-i # Child of fixed argument b | |
| \ / | |
| |- [a][b][c][d] # Fixed Arguments | |
| |- {(e)->(f)->(g)} # Variable Arguments | |
| \ | |
| \-h # Child of variable argument e | |
| Traversal Order: a, b, i, c, d, e, h, f, g, NULL | |
| Note: The branch i and h will be traversed if it has any children. | |
| @param [in] Node Pointer to a node. | |
| @return The next node in the AML bytestream order. | |
| Return NULL if Node is the Node corresponding to the last | |
| bytecode of the tree. | |
| **/ | |
| AML_NODE_HEADER * | |
| EFIAPI | |
| AmlGetNextNode ( | |
| IN CONST AML_NODE_HEADER *Node | |
| ) | |
| { | |
| AML_NODE_HEADER *ParentNode; | |
| AML_NODE_HEADER *CandidateNode; | |
| if (!IS_AML_NODE_VALID (Node)) { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| if (IS_AML_ROOT_NODE (Node) || IS_AML_OBJECT_NODE (Node)) { | |
| // The node has children. Get the first child. | |
| CandidateNode = AmlGetNextSibling (Node, NULL); | |
| if (CandidateNode != NULL) { | |
| if (!IS_AML_NODE_VALID (CandidateNode)) { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| // A Node has been found | |
| return CandidateNode; | |
| } else if (IS_AML_ROOT_NODE (Node)) { | |
| // The node is the root node and it doesn't have children. | |
| return NULL; | |
| } | |
| } | |
| // We have traversed the current branch, go to the parent node | |
| // and start traversing the next branch. | |
| // Keep going up the tree until you reach the root node. | |
| while (1) { | |
| if (IS_AML_ROOT_NODE (Node)) { | |
| // This is the last node of the tree. | |
| return NULL; | |
| } | |
| ParentNode = AmlGetParent ((AML_NODE_HEADER *)Node); | |
| if (!IS_AML_NODE_VALID (ParentNode)) { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| CandidateNode = AmlGetNextSibling (ParentNode, Node); | |
| if (CandidateNode != NULL) { | |
| if (!IS_AML_NODE_VALID (CandidateNode)) { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| // A Node has been found | |
| return CandidateNode; | |
| } | |
| Node = ParentNode; | |
| } // while | |
| return NULL; | |
| } | |
| /** Iterate through the nodes in the reverse order of the AML bytestream. | |
| The iteration is similar to a depth-first path, | |
| but done in a reverse order. | |
| (Node) /-i # Child of fixed argument b | |
| \ / | |
| |- [a][b][c][d] # Fixed Arguments | |
| |- {(e)->(f)->(g)} # Variable Arguments | |
| \ | |
| \-h # Child of variable argument e | |
| Traversal Order: g, f, h, e, d, c, i, b, a, NULL | |
| Note: The branch i and h will be traversed if it has any children. | |
| @param [in] Node Pointer to a node. | |
| @return The previous node in the AML bytestream order. | |
| Return NULL if Node is the Node corresponding to the last | |
| bytecode of the tree. | |
| **/ | |
| AML_NODE_HEADER * | |
| EFIAPI | |
| AmlGetPreviousNode ( | |
| IN CONST AML_NODE_HEADER *Node | |
| ) | |
| { | |
| AML_NODE_HEADER *ParentNode; | |
| AML_NODE_HEADER *CandidateNode; | |
| AML_NODE_HEADER *PreviousNode; | |
| if (!IS_AML_NODE_VALID (Node)) { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| while (1) { | |
| if (IS_AML_ROOT_NODE (Node)) { | |
| // This is the root node. | |
| return NULL; | |
| } | |
| ParentNode = AmlGetParent ((AML_NODE_HEADER *)Node); | |
| CandidateNode = AmlGetPreviousSibling (ParentNode, Node); | |
| if (CandidateNode == NULL) { | |
| // Node is the first child of its parent. | |
| return ParentNode; | |
| } else if (IS_AML_DATA_NODE (CandidateNode)) { | |
| // CandidateNode is a data node, thus it has no children. | |
| return CandidateNode; | |
| } else if (IS_AML_OBJECT_NODE (CandidateNode)) { | |
| // Get the previous node in the list of children of ParentNode, | |
| // then get the last child of this node. | |
| // If this node has children, get its last child, etc. | |
| while (1) { | |
| PreviousNode = CandidateNode; | |
| CandidateNode = AmlGetPreviousSibling (PreviousNode, NULL); | |
| if (CandidateNode == NULL) { | |
| return PreviousNode; | |
| } else if (IS_AML_DATA_NODE (CandidateNode)) { | |
| return CandidateNode; | |
| } | |
| } // while | |
| } else { | |
| ASSERT (0); | |
| return NULL; | |
| } | |
| } // while | |
| } |