mirror of https://github.com/acidanthera/audk.git
549 lines
17 KiB
C
549 lines
17 KiB
C
/** @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
|
|
}
|