blob: 975174f13acdd452a74092529f3988b3b81e60b6 [file] [log] [blame]
/** @file
AML Tree.
Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Tree/AmlTree.h>
#include <AmlCoreInterface.h>
#include <Tree/AmlNode.h>
#include <Tree/AmlTreeTraversal.h>
#include <Utils/AmlUtility.h>
/** Get the parent node of the input Node.
@param [in] Node Pointer to a node.
@return The parent node of the input Node.
NULL otherwise.
**/
AML_NODE_HEADER *
EFIAPI
AmlGetParent (
IN AML_NODE_HEADER *Node
)
{
if (IS_AML_DATA_NODE (Node) ||
IS_AML_OBJECT_NODE (Node))
{
return Node->Parent;
}
return NULL;
}
/** Get the root node from any node of the tree.
This is done by climbing up the tree until the root node is reached.
@param [in] Node Pointer to a node.
@return The root node of the tree.
NULL if error.
**/
AML_ROOT_NODE *
EFIAPI
AmlGetRootNode (
IN CONST AML_NODE_HEADER *Node
)
{
if (!IS_AML_NODE_VALID (Node)) {
ASSERT (0);
return NULL;
}
while (!IS_AML_ROOT_NODE (Node)) {
Node = Node->Parent;
if (!IS_AML_NODE_VALID (Node)) {
ASSERT (0);
return NULL;
}
}
return (AML_ROOT_NODE *)Node;
}
/** Get the node at the input Index in the fixed argument list of the input
ObjectNode.
@param [in] ObjectNode Pointer to an object node.
@param [in] Index The Index of the fixed argument to get.
@return The node at the input Index in the fixed argument list
of the input ObjectNode.
NULL otherwise, e.g. if the node is not an object node, or no
node is available at this Index.
**/
AML_NODE_HEADER *
EFIAPI
AmlGetFixedArgument (
IN AML_OBJECT_NODE *ObjectNode,
IN EAML_PARSE_INDEX Index
)
{
if (IS_AML_OBJECT_NODE (ObjectNode)) {
if (Index < (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (ObjectNode)) {
return ObjectNode->FixedArgs[Index];
}
}
return NULL;
}
/** Check whether the input Node is in the fixed argument list of its parent
node.
If so, IndexPtr contains this Index.
@param [in] Node Pointer to a Node.
@param [out] IndexPtr Pointer holding the Index of the Node in
its parent's fixed argument list.
@retval TRUE The node is a fixed argument and the index
in IndexPtr is valid.
@retval FALSE The node is not a fixed argument.
**/
BOOLEAN
EFIAPI
AmlIsNodeFixedArgument (
IN CONST AML_NODE_HEADER *Node,
OUT EAML_PARSE_INDEX *IndexPtr
)
{
AML_NODE_HEADER *ParentNode;
EAML_PARSE_INDEX Index;
EAML_PARSE_INDEX MaxIndex;
if ((IndexPtr == NULL) ||
(!IS_AML_DATA_NODE (Node) &&
!IS_AML_OBJECT_NODE (Node)))
{
ASSERT (0);
return FALSE;
}
ParentNode = AmlGetParent ((AML_NODE_HEADER *)Node);
if (IS_AML_ROOT_NODE (ParentNode)) {
return FALSE;
} else if (IS_AML_DATA_NODE (ParentNode)) {
// Tree is inconsistent.
ASSERT (0);
return FALSE;
}
// Check whether the Node is in the fixed argument list.
MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
(AML_OBJECT_NODE *)ParentNode
);
for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) {
if (AmlGetFixedArgument ((AML_OBJECT_NODE *)ParentNode, Index) == Node) {
*IndexPtr = Index;
return TRUE;
}
}
return FALSE;
}
/** Set the fixed argument of the ObjectNode at the Index to the NewNode.
It is the caller's responsibility to save the old node, if desired,
otherwise the reference to the old node will be lost.
If NewNode is not NULL, set its parent to ObjectNode.
@param [in] ObjectNode Pointer to an object node.
@param [in] Index Index in the fixed argument list of
the ObjectNode to set.
@param [in] NewNode Pointer to the NewNode.
Can be NULL, a data node or an object node.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlSetFixedArgument (
IN AML_OBJECT_NODE *ObjectNode,
IN EAML_PARSE_INDEX Index,
IN AML_NODE_HEADER *NewNode
)
{
if (IS_AML_OBJECT_NODE (ObjectNode) &&
(Index <= (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (ObjectNode)) &&
((NewNode == NULL) ||
IS_AML_OBJECT_NODE (NewNode) ||
IS_AML_DATA_NODE (NewNode)))
{
ObjectNode->FixedArgs[Index] = NewNode;
// If NewNode is a data node or an object node, set its parent.
if (NewNode != NULL) {
NewNode->Parent = (AML_NODE_HEADER *)ObjectNode;
}
return EFI_SUCCESS;
}
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
/** If the given AML_NODE_HEADER has a variable list of arguments,
return a pointer to this list.
Return NULL otherwise.
@param [in] Node Pointer to the AML_NODE_HEADER to check.
@return The list of variable arguments if there is one.
NULL otherwise.
**/
LIST_ENTRY *
EFIAPI
AmlNodeGetVariableArgList (
IN CONST AML_NODE_HEADER *Node
)
{
if (IS_AML_ROOT_NODE (Node)) {
return &(((AML_ROOT_NODE *)Node)->VariableArgs);
} else if (IS_AML_OBJECT_NODE (Node)) {
return &(((AML_OBJECT_NODE *)Node)->VariableArgs);
}
return NULL;
}
/** Remove the Node from its parent's variable list of arguments.
The function will fail if the Node is in its parent's fixed
argument list.
The Node is not deleted. The deletion is done separately
from the removal.
@param [in] Node Pointer to a Node.
Must be a data node or an object node.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlRemoveNodeFromVarArgList (
IN AML_NODE_HEADER *Node
)
{
EFI_STATUS Status;
AML_NODE_HEADER *ParentNode;
UINT32 Size;
if ((!IS_AML_DATA_NODE (Node) &&
!IS_AML_OBJECT_NODE (Node)))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
ParentNode = AmlGetParent (Node);
if (!IS_AML_ROOT_NODE (ParentNode) &&
!IS_AML_OBJECT_NODE (ParentNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Check the node is in its parent variable list of arguments.
if (!IsNodeInList (
AmlNodeGetVariableArgList (ParentNode),
&Node->Link
))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Unlink Node from the tree.
RemoveEntryList (&Node->Link);
InitializeListHead (&Node->Link);
Node->Parent = NULL;
// Get the size of the node removed.
Status = AmlComputeSize (Node, &Size);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Propagate the information.
Status = AmlPropagateInformation (ParentNode, FALSE, Size, 1);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Detach the Node from the tree.
The function will fail if the Node is in its parent's fixed
argument list.
The Node is not deleted. The deletion is done separately
from the removal.
@param [in] Node Pointer to a Node.
Must be a data node or an object node.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlDetachNode (
IN AML_NODE_HEADER *Node
)
{
return AmlRemoveNodeFromVarArgList (Node);
}
/** Add the NewNode to the head of the variable list of arguments
of the ParentNode.
@param [in] ParentNode Pointer to the parent node.
Must be a root or an object node.
@param [in] NewNode Pointer to the node to add.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlVarListAddHead (
IN AML_NODE_HEADER *ParentNode,
IN AML_NODE_HEADER *NewNode
)
{
EFI_STATUS Status;
UINT32 NewSize;
LIST_ENTRY *ChildrenList;
// Check arguments and that NewNode is not already attached to a tree.
// ParentNode != Data Node AND NewNode != Root Node AND NewNode != attached.
if ((!IS_AML_ROOT_NODE (ParentNode) &&
!IS_AML_OBJECT_NODE (ParentNode)) ||
(!IS_AML_DATA_NODE (NewNode) &&
!IS_AML_OBJECT_NODE (NewNode)) ||
!AML_NODE_IS_DETACHED (NewNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Insert it at the head of the list.
ChildrenList = AmlNodeGetVariableArgList (ParentNode);
if (ChildrenList == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
InsertHeadList (ChildrenList, &NewNode->Link);
NewNode->Parent = ParentNode;
// Get the size of the NewNode.
Status = AmlComputeSize (NewNode, &NewSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Propagate the new information.
Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Add the NewNode to the tail of the variable list of arguments
of the ParentNode.
NOTE: This is an internal function which does not propagate the size
when a new node is added.
@param [in] ParentNode Pointer to the parent node.
Must be a root or an object node.
@param [in] NewNode Pointer to the node to add.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlVarListAddTailInternal (
IN AML_NODE_HEADER *ParentNode,
IN AML_NODE_HEADER *NewNode
)
{
LIST_ENTRY *ChildrenList;
// Check arguments and that NewNode is not already attached to a tree.
// ParentNode != Data Node AND NewNode != Root Node AND NewNode != attached.
if ((!IS_AML_ROOT_NODE (ParentNode) &&
!IS_AML_OBJECT_NODE (ParentNode)) ||
(!IS_AML_DATA_NODE (NewNode) &&
!IS_AML_OBJECT_NODE (NewNode)) ||
!AML_NODE_IS_DETACHED (NewNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Insert it at the tail of the list.
ChildrenList = AmlNodeGetVariableArgList (ParentNode);
if (ChildrenList == NULL) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
InsertTailList (ChildrenList, &NewNode->Link);
NewNode->Parent = ParentNode;
return EFI_SUCCESS;
}
/** Add the NewNode to the tail of the variable list of arguments
of the ParentNode.
@param [in] ParentNode Pointer to the parent node.
Must be a root or an object node.
@param [in] NewNode Pointer to the node to add.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlVarListAddTail (
IN AML_NODE_HEADER *ParentNode,
IN AML_NODE_HEADER *NewNode
)
{
EFI_STATUS Status;
UINT32 NewSize;
// Add the NewNode and check arguments.
Status = AmlVarListAddTailInternal (ParentNode, NewNode);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the size of the NewNode.
Status = AmlComputeSize (NewNode, &NewSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Propagate the new information.
Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Add the NewNode before the Node in the list of variable
arguments of the Node's parent.
@param [in] Node Pointer to a node.
Must be a root or an object node.
@param [in] NewNode Pointer to the node to add.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlVarListAddBefore (
IN AML_NODE_HEADER *Node,
IN AML_NODE_HEADER *NewNode
)
{
EFI_STATUS Status;
AML_NODE_HEADER *ParentNode;
UINT32 NewSize;
// Check arguments and that NewNode is not already attached to a tree.
if ((!IS_AML_DATA_NODE (NewNode) &&
!IS_AML_OBJECT_NODE (NewNode)) ||
!AML_NODE_IS_DETACHED (NewNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
ParentNode = AmlGetParent (Node);
if (!IS_AML_ROOT_NODE (ParentNode) &&
!IS_AML_OBJECT_NODE (ParentNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Insert it before the input Node.
InsertTailList (&Node->Link, &NewNode->Link);
NewNode->Parent = ParentNode;
// Get the size of the NewNode.
Status = AmlComputeSize (NewNode, &NewSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Propagate the new information.
Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Add the NewNode after the Node in the variable list of arguments
of the Node's parent.
@param [in] Node Pointer to a node.
Must be a root or an object node.
@param [in] NewNode Pointer to the node to add.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlVarListAddAfter (
IN AML_NODE_HEADER *Node,
IN AML_NODE_HEADER *NewNode
)
{
EFI_STATUS Status;
AML_NODE_HEADER *ParentNode;
UINT32 NewSize;
// Check arguments and that NewNode is not already attached to a tree.
if ((!IS_AML_DATA_NODE (NewNode) &&
!IS_AML_OBJECT_NODE (NewNode)) ||
!AML_NODE_IS_DETACHED (NewNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
ParentNode = AmlGetParent (Node);
if (!IS_AML_ROOT_NODE (ParentNode) &&
!IS_AML_OBJECT_NODE (ParentNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Insert the new node after the input Node.
InsertHeadList (&Node->Link, &NewNode->Link);
NewNode->Parent = ParentNode;
// Get the size of the NewNode.
Status = AmlComputeSize (NewNode, &NewSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Propagate the new information.
Status = AmlPropagateInformation (ParentNode, TRUE, NewSize, 1);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Append a Resource Data node to the BufferOpNode.
The Resource Data node is added at the end of the variable
list of arguments of the BufferOpNode, but before the End Tag.
If no End Tag is found, the function returns an error.
@param [in] BufferOpNode Buffer node containing resource data elements.
@param [in] NewRdNode The new Resource Data node to add.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlAppendRdNode (
IN AML_OBJECT_NODE *BufferOpNode,
IN AML_DATA_NODE *NewRdNode
)
{
EFI_STATUS Status;
AML_DATA_NODE *LastRdNode;
if (!AmlNodeCompareOpCode (BufferOpNode, AML_BUFFER_OP, 0) ||
!IS_AML_DATA_NODE (NewRdNode) ||
(NewRdNode->DataType != EAmlNodeDataTypeResourceData))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// To avoid re-computing checksums, if a new resource data elements is
// added/removed/modified in a list of resource data elements, the AmlLib
// resets the checksum to 0.
// It is possible to have only one Resource Data in a BufferOp with
// no EndTag, but it should not be possible to add a new Resource Data
// in the list in this case.
Status = AmlSetRdListCheckSum (BufferOpNode, 0);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the last Resource data node in the variable list of argument of the
// BufferOp node. This must be an EndTag, otherwise setting the checksum
// would have failed.
LastRdNode = (AML_DATA_NODE *)AmlGetPreviousVariableArgument (
(AML_NODE_HEADER *)BufferOpNode,
NULL
);
if ((LastRdNode == NULL) ||
!IS_AML_DATA_NODE (LastRdNode) ||
(LastRdNode->DataType != EAmlNodeDataTypeResourceData))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Add NewRdNode before the EndTag.
Status = AmlVarListAddBefore (
(AML_NODE_HEADER *)LastRdNode,
(AML_NODE_HEADER *)NewRdNode
)
;
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Replace the fixed argument at the Index of the ParentNode with the NewNode.
Note: This function unlinks the OldNode from the tree. It is the callers
responsibility to delete the OldNode if needed.
@param [in] ParentNode Pointer to the parent node.
Must be an object node.
@param [in] Index Index of the fixed argument to replace.
@param [in] NewNode The new node to insert.
Must be an object node or a data node.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
STATIC
EFI_STATUS
EFIAPI
AmlReplaceFixedArgument (
IN AML_OBJECT_NODE *ParentNode,
IN EAML_PARSE_INDEX Index,
IN AML_NODE_HEADER *NewNode
)
{
EFI_STATUS Status;
AML_NODE_HEADER *OldNode;
UINT32 NewSize;
UINT32 OldSize;
AML_PARSE_FORMAT FixedArgType;
// Check arguments and that NewNode is not already attached to a tree.
if (!IS_AML_OBJECT_NODE (ParentNode) ||
(!IS_AML_DATA_NODE (NewNode) &&
!IS_AML_OBJECT_NODE (NewNode)) ||
!AML_NODE_IS_DETACHED (NewNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Perform some compatibility checks between NewNode and OldNode.
FixedArgType = ParentNode->AmlByteEncoding->Format[Index];
switch (FixedArgType) {
case EAmlFieldPkgLen:
{
// A FieldPkgLen can only have a parent node with the
// AML_IS_FIELD_ELEMENT flag.
if (!AmlNodeHasAttribute (
(AML_OBJECT_NODE *)ParentNode,
AML_HAS_FIELD_LIST
))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Fall through.
}
case EAmlUInt8:
case EAmlUInt16:
case EAmlUInt32:
case EAmlUInt64:
case EAmlName:
case EAmlString:
{
// A uint, a name, a string and a FieldPkgLen can only be replaced by a
// data node of the same type.
// Note: This condition might be too strict, but safer.
if (!IS_AML_DATA_NODE (NewNode) ||
(((AML_DATA_NODE *)NewNode)->DataType !=
AmlTypeToNodeDataType (FixedArgType)))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
break;
}
case EAmlObject:
{
// If it's an object node, the grammar is too complex to do any check.
break;
}
case EAmlNone:
default:
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
break;
}
} // switch
// Replace the OldNode with the NewNode.
OldNode = AmlGetFixedArgument (ParentNode, Index);
if (!IS_AML_NODE_VALID (OldNode)) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Unlink the old node.
// Note: This function unlinks the OldNode from the tree. It is the callers
// responsibility to delete the OldNode if needed.
OldNode->Parent = NULL;
Status = AmlSetFixedArgument (ParentNode, Index, NewNode);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the size of the OldNode.
Status = AmlComputeSize (OldNode, &OldSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the size of the NewNode.
Status = AmlComputeSize (NewNode, &NewSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Propagate the new information.
Status = AmlPropagateInformation (
(AML_NODE_HEADER *)ParentNode,
(NewSize > OldSize) ? TRUE : FALSE,
(NewSize > OldSize) ? (NewSize - OldSize) : (OldSize - NewSize),
0
);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Replace the OldNode, which is in a variable list of arguments,
with the NewNode.
Note: This function unlinks the OldNode from the tree. It is the callers
responsibility to delete the OldNode if needed.
@param [in] OldNode Pointer to the node to replace.
Must be a data node or an object node.
@param [in] NewNode The new node to insert.
Must be a data node or an object node.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlReplaceVariableArgument (
IN AML_NODE_HEADER *OldNode,
IN AML_NODE_HEADER *NewNode
)
{
EFI_STATUS Status;
UINT32 NewSize;
UINT32 OldSize;
EAML_PARSE_INDEX Index;
AML_DATA_NODE *NewDataNode;
AML_NODE_HEADER *ParentNode;
LIST_ENTRY *NextLink;
// Check arguments, that NewNode is not already attached to a tree,
// and that OldNode is attached and not in a fixed list of arguments.
if ((!IS_AML_DATA_NODE (OldNode) &&
!IS_AML_OBJECT_NODE (OldNode)) ||
(!IS_AML_DATA_NODE (NewNode) &&
!IS_AML_OBJECT_NODE (NewNode)) ||
!AML_NODE_IS_DETACHED (NewNode) ||
AML_NODE_IS_DETACHED (OldNode) ||
AmlIsNodeFixedArgument (OldNode, &Index))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
ParentNode = AmlGetParent (OldNode);
if (!IS_AML_ROOT_NODE (ParentNode) &&
!IS_AML_OBJECT_NODE (ParentNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
NewDataNode = (AML_DATA_NODE *)NewNode;
// Check attributes if the parent node is an object node.
if (IS_AML_OBJECT_NODE (ParentNode)) {
// A child node of a node with the HAS_CHILD flag must be either a
// data node or an object node. This has already been checked. So,
// check for other cases.
if (AmlNodeHasAttribute ((AML_OBJECT_NODE *)ParentNode, AML_HAS_BYTE_LIST)) {
if (!IS_AML_DATA_NODE (NewNode) ||
((NewDataNode->DataType != EAmlNodeDataTypeRaw) &&
(NewDataNode->DataType != EAmlNodeDataTypeResourceData)))
{
// A child node of a node with the BYTE_LIST flag must be a data node,
// containing raw data or a resource data.
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
} else if (AmlNodeHasAttribute (
(AML_OBJECT_NODE *)ParentNode,
AML_HAS_FIELD_LIST
))
{
if (!AmlNodeHasAttribute (
(CONST AML_OBJECT_NODE *)NewNode,
AML_IS_FIELD_ELEMENT
))
{
// A child node of a node with the FIELD_LIST flag must be an object
// node with AML_IS_FIELD_ELEMENT flag.
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
}
} else {
// Parent node is a root node.
// A root node cannot have a data node as its child.
if (!IS_AML_DATA_NODE (NewNode)) {
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
}
// Unlink OldNode from the tree.
NextLink = RemoveEntryList (&OldNode->Link);
InitializeListHead (&OldNode->Link);
OldNode->Parent = NULL;
// Add the NewNode.
InsertHeadList (NextLink, &NewNode->Link);
NewNode->Parent = ParentNode;
// Get the size of the OldNode.
Status = AmlComputeSize (OldNode, &OldSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Get the size of the NewNode.
Status = AmlComputeSize (NewNode, &NewSize);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
// Propagate the new information.
Status = AmlPropagateInformation (
ParentNode,
(NewSize > OldSize) ? TRUE : FALSE,
(NewSize > OldSize) ? (NewSize - OldSize) : (OldSize - NewSize),
0
);
ASSERT_EFI_ERROR (Status);
return Status;
}
/** Replace the OldNode by the NewNode.
Note: This function unlinks the OldNode from the tree. It is the callers
responsibility to delete the OldNode if needed.
@param [in] OldNode Pointer to the node to replace.
Must be a data node or an object node.
@param [in] NewNode The new node to insert.
Must be a data node or an object node.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlReplaceArgument (
IN AML_NODE_HEADER *OldNode,
IN AML_NODE_HEADER *NewNode
)
{
EFI_STATUS Status;
AML_NODE_HEADER *ParentNode;
EAML_PARSE_INDEX Index;
// Check arguments and that NewNode is not already attached to a tree.
if ((!IS_AML_DATA_NODE (OldNode) &&
!IS_AML_OBJECT_NODE (OldNode)) ||
(!IS_AML_DATA_NODE (NewNode) &&
!IS_AML_OBJECT_NODE (NewNode)) ||
!AML_NODE_IS_DETACHED (NewNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// ParentNode can be a root node or an object node.
ParentNode = AmlGetParent (OldNode);
if (!IS_AML_ROOT_NODE (ParentNode) &&
!IS_AML_OBJECT_NODE (ParentNode))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
if (AmlIsNodeFixedArgument (OldNode, &Index)) {
// OldNode is in its parent's fixed argument list at the Index.
Status = AmlReplaceFixedArgument (
(AML_OBJECT_NODE *)ParentNode,
Index,
NewNode
);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
} else {
// OldNode is not in its parent's fixed argument list.
// It must be in its variable list of arguments.
Status = AmlReplaceVariableArgument (OldNode, NewNode);
ASSERT_EFI_ERROR (Status);
}
return Status;
}
/** Delete a Node and its children.
The Node must be removed from the tree first,
or must be the root node.
@param [in] Node Pointer to the node to delete.
@retval EFI_SUCCESS The function completed successfully.
@retval EFI_INVALID_PARAMETER Invalid parameter.
**/
EFI_STATUS
EFIAPI
AmlDeleteTree (
IN AML_NODE_HEADER *Node
)
{
EFI_STATUS Status;
EAML_PARSE_INDEX Index;
EAML_PARSE_INDEX MaxIndex;
AML_NODE_HEADER *Arg;
LIST_ENTRY *StartLink;
LIST_ENTRY *CurrentLink;
LIST_ENTRY *NextLink;
// Check that the node being deleted is unlinked.
// When removing the node, its parent pointer and
// its lists data structure are reset with
// InitializeListHead. Thus it must be detached
// from the tree to avoid memory leaks.
if (!IS_AML_NODE_VALID (Node) ||
!AML_NODE_IS_DETACHED (Node))
{
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// 1. Recursively detach and delete the fixed arguments.
// Iterate through the fixed list of arguments.
if (IS_AML_OBJECT_NODE (Node)) {
MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
(AML_OBJECT_NODE *)Node
);
for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) {
Arg = AmlGetFixedArgument ((AML_OBJECT_NODE *)Node, Index);
if (Arg == NULL) {
// A fixed argument is missing. The tree is inconsistent.
// Note: During CodeGeneration, the fixed arguments should be set
// with an incrementing index, and then the variable arguments
// should be added. This allows to free as many nodes as
// possible if a crash occurs.
ASSERT (0);
return EFI_INVALID_PARAMETER;
}
// Remove the node from the fixed argument list.
Arg->Parent = NULL;
Status = AmlSetFixedArgument ((AML_OBJECT_NODE *)Node, Index, NULL);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
Status = AmlDeleteTree (Arg);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
}
}
// 2. Recursively detach and delete the variable arguments.
// Iterate through the variable list of arguments.
StartLink = AmlNodeGetVariableArgList (Node);
if (StartLink != NULL) {
NextLink = StartLink->ForwardLink;
while (NextLink != StartLink) {
CurrentLink = NextLink;
// Unlink the node from the tree.
NextLink = RemoveEntryList (CurrentLink);
InitializeListHead (CurrentLink);
((AML_NODE_HEADER *)CurrentLink)->Parent = NULL;
Status = AmlDeleteTree ((AML_NODE_HEADER *)CurrentLink);
if (EFI_ERROR (Status)) {
ASSERT (0);
return Status;
}
} // while
}
// 3. Delete the node.
Status = AmlDeleteNode (Node);
ASSERT_EFI_ERROR (Status);
return Status;
}