/** @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; | |
} |