aboutsummaryrefslogtreecommitdiffstats
path: root/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree')
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c205
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c673
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h212
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c566
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c1047
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h127
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c98
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c353
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h220
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c548
-rw-r--r--roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h138
11 files changed, 4187 insertions, 0 deletions
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c
new file mode 100644
index 000000000..e09372b03
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlClone.c
@@ -0,0 +1,205 @@
+/** @file
+ AML Clone.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+
+/** Clone a node.
+
+ This function does not clone the children nodes.
+ The cloned node returned is not attached to any tree.
+
+ @param [in] Node Pointer to a node.
+ @param [out] ClonedNode Pointer holding the cloned node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCloneNode (
+ IN AML_NODE_HEADER * Node,
+ OUT AML_NODE_HEADER ** ClonedNode
+ )
+{
+ EFI_STATUS Status;
+
+ AML_OBJECT_NODE * ObjectNode;
+ AML_DATA_NODE * DataNode;
+ AML_ROOT_NODE * RootNode;
+
+ if (!IS_AML_NODE_VALID (Node) ||
+ (ClonedNode == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *ClonedNode = NULL;
+
+ if (IS_AML_DATA_NODE (Node)) {
+ DataNode = (AML_DATA_NODE*)Node;
+ Status = AmlCreateDataNode (
+ DataNode->DataType,
+ DataNode->Buffer,
+ DataNode->Size,
+ (AML_DATA_NODE**)ClonedNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+ } else if (IS_AML_OBJECT_NODE (Node)) {
+ ObjectNode = (AML_OBJECT_NODE*)Node;
+
+ Status = AmlCreateObjectNode (
+ ObjectNode->AmlByteEncoding,
+ ObjectNode->PkgLen,
+ (AML_OBJECT_NODE**)ClonedNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+ } else if (IS_AML_ROOT_NODE (Node)) {
+ RootNode = (AML_ROOT_NODE*)Node;
+
+ Status = AmlCreateRootNode (
+ RootNode->SdtHeader,
+ (AML_ROOT_NODE**)ClonedNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+ } else {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ return Status;
+}
+
+/** Clone a node and its children (clone a tree branch).
+
+ The cloned branch returned is not attached to any tree.
+
+ @param [in] Node Pointer to a node.
+ Node is the head of the branch to clone.
+ @param [out] ClonedNode Pointer holding the head of the created cloned
+ branch.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCloneTree (
+ IN AML_NODE_HEADER * Node,
+ OUT AML_NODE_HEADER ** ClonedNode
+ )
+{
+ EFI_STATUS Status;
+
+ AML_NODE_HEADER * HeadNode;
+ AML_NODE_HEADER * ClonedChildNode;
+ AML_NODE_HEADER * FixedArgNode;
+
+ EAML_PARSE_INDEX Index;
+ EAML_PARSE_INDEX MaxIndex;
+
+ LIST_ENTRY * StartLink;
+ LIST_ENTRY * CurrentLink;
+
+ if (!IS_AML_NODE_VALID (Node) ||
+ (ClonedNode == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = AmlCloneNode (Node, &HeadNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Clone the fixed arguments and bind them to their parent.
+ MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
+ (AML_OBJECT_NODE*)Node
+ );
+ for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) {
+ FixedArgNode = AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index);
+ if (FixedArgNode == NULL) {
+ Status = EFI_INVALID_PARAMETER;
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ // Clone child.
+ Status = AmlCloneTree (
+ FixedArgNode,
+ &ClonedChildNode
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ // Bind child.
+ Status = AmlSetFixedArgument (
+ (AML_OBJECT_NODE*)HeadNode,
+ Index,
+ ClonedChildNode
+ );
+ if (EFI_ERROR (Status)) {
+ AmlDeleteTree (ClonedChildNode);
+ ASSERT (0);
+ goto error_handler;
+ }
+ } // for
+
+ // Clone the variable arguments and bind them to their parent.
+ StartLink = AmlNodeGetVariableArgList (Node);
+ if (StartLink != NULL) {
+ CurrentLink = StartLink->ForwardLink;
+ while (CurrentLink != StartLink) {
+ // Clone child.
+ Status = AmlCloneTree ((AML_NODE_HEADER*)CurrentLink, &ClonedChildNode);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ // Bind child.
+ Status = AmlVarListAddTailInternal (
+ HeadNode,
+ ClonedChildNode
+ );
+ if (EFI_ERROR (Status)) {
+ AmlDeleteTree (ClonedChildNode);
+ ASSERT (0);
+ goto error_handler;
+ }
+
+ CurrentLink = CurrentLink->ForwardLink;
+ } // while
+ }
+
+ *ClonedNode = HeadNode;
+ return Status;
+
+error_handler:
+ *ClonedNode = NULL;
+
+ if (HeadNode != NULL) {
+ AmlDeleteTree (HeadNode);
+ }
+
+ return Status;
+}
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c
new file mode 100644
index 000000000..2c80440a4
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.c
@@ -0,0 +1,673 @@
+/** @file
+ AML Node.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Tree/AmlNode.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlTree.h>
+
+/** Initialize an AML_NODE_HEADER structure.
+
+ @param [in] Node Pointer to a node header.
+ @param [in] NodeType NodeType to initialize the Node with.
+ Must be an EAML_NODE_TYPE.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlInitializeNodeHeader (
+ IN AML_NODE_HEADER * Node,
+ IN EAML_NODE_TYPE NodeType
+ )
+{
+ if (Node == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ InitializeListHead (&Node->Link);
+
+ Node->Parent = NULL;
+ Node->NodeType = NodeType;
+
+ return EFI_SUCCESS;
+}
+
+/** Delete a root node and its ACPI DSDT/SSDT header.
+
+ It is the caller's responsibility to check the RootNode has been removed
+ from the tree and is not referencing any other node in the tree.
+
+ @param [in] RootNode Pointer to a root node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlDeleteRootNode (
+ IN AML_ROOT_NODE * RootNode
+ )
+{
+ if (!IS_AML_ROOT_NODE (RootNode)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((RootNode->SdtHeader != NULL)) {
+ FreePool (RootNode->SdtHeader);
+ } else {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (RootNode);
+ return EFI_SUCCESS;
+}
+
+/** Create an AML_ROOT_NODE.
+ This node will be the root of the tree.
+
+ @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT header to copy
+ the data from.
+ @param [out] NewRootNodePtr The created AML_ROOT_NODE.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCreateRootNode (
+ IN CONST EFI_ACPI_DESCRIPTION_HEADER * SdtHeader,
+ OUT AML_ROOT_NODE ** NewRootNodePtr
+ )
+{
+ EFI_STATUS Status;
+ AML_ROOT_NODE * RootNode;
+
+ if ((SdtHeader == NULL) ||
+ (NewRootNodePtr == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ RootNode = AllocateZeroPool (sizeof (AML_ROOT_NODE));
+ if (RootNode == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = AmlInitializeNodeHeader (&RootNode->NodeHeader, EAmlNodeRoot);
+ if (EFI_ERROR (Status)) {
+ FreePool (RootNode);
+ ASSERT (0);
+ return Status;
+ }
+
+ InitializeListHead (&RootNode->VariableArgs);
+
+ RootNode->SdtHeader = AllocateCopyPool (
+ sizeof (EFI_ACPI_DESCRIPTION_HEADER),
+ SdtHeader
+ );
+ if (RootNode->SdtHeader == NULL) {
+ ASSERT (0);
+ AmlDeleteRootNode (RootNode);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ *NewRootNodePtr = RootNode;
+
+ return EFI_SUCCESS;
+}
+
+/** Delete an object node.
+
+ It is the caller's responsibility to check the ObjectNode has been removed
+ from the tree and is not referencing any other node in the tree.
+
+ @param [in] ObjectNode Pointer to an object node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlDeleteObjectNode (
+ IN AML_OBJECT_NODE * ObjectNode
+ )
+{
+ if (!IS_AML_OBJECT_NODE (ObjectNode)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (ObjectNode);
+ return EFI_SUCCESS;
+}
+
+/** Create an AML_OBJECT_NODE.
+
+ @param [in] AmlByteEncoding Byte encoding entry.
+ @param [in] PkgLength PkgLength of the node if the AmlByteEncoding
+ has the PkgLen attribute.
+ 0 otherwise.
+ @param [out] NewObjectNodePtr The created AML_OBJECT_NODE.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCreateObjectNode (
+ IN CONST AML_BYTE_ENCODING * AmlByteEncoding,
+ IN UINT32 PkgLength,
+ OUT AML_OBJECT_NODE ** NewObjectNodePtr
+ )
+{
+ EFI_STATUS Status;
+ AML_OBJECT_NODE * ObjectNode;
+
+ if ((AmlByteEncoding == NULL) ||
+ (NewObjectNodePtr == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ObjectNode = AllocateZeroPool (sizeof (AML_OBJECT_NODE));
+ if (ObjectNode == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = AmlInitializeNodeHeader (&ObjectNode->NodeHeader, EAmlNodeObject);
+ if (EFI_ERROR (Status)) {
+ FreePool (ObjectNode);
+ ASSERT (0);
+ return Status;
+ }
+
+ InitializeListHead (&ObjectNode->VariableArgs);
+
+ // ObjectNode->FixedArgs[...] is already initialised to NULL as the
+ // ObjectNode is Zero allocated.
+ ObjectNode->AmlByteEncoding = AmlByteEncoding;
+ ObjectNode->PkgLen = PkgLength;
+
+ *NewObjectNodePtr = ObjectNode;
+
+ return EFI_SUCCESS;
+}
+
+/** Delete a data node and its buffer.
+
+ It is the caller's responsibility to check the DataNode has been removed
+ from the tree and is not referencing any other node in the tree.
+
+ @param [in] DataNode Pointer to a data node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlDeleteDataNode (
+ IN AML_DATA_NODE * DataNode
+ )
+{
+ if (!IS_AML_DATA_NODE (DataNode)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (DataNode->Buffer != NULL) {
+ FreePool (DataNode->Buffer);
+ } else {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (DataNode);
+ return EFI_SUCCESS;
+}
+
+/** Create an AML_DATA_NODE.
+
+ @param [in] DataType DataType of the node.
+ @param [in] Data Pointer to the AML bytecode corresponding to
+ this node. Data is copied from there.
+ @param [in] DataSize Number of bytes to consider at the address
+ pointed by Data.
+ @param [out] NewDataNodePtr The created AML_DATA_NODE.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCreateDataNode (
+ IN EAML_NODE_DATA_TYPE DataType,
+ IN CONST UINT8 * Data,
+ IN UINT32 DataSize,
+ OUT AML_DATA_NODE ** NewDataNodePtr
+ )
+{
+ EFI_STATUS Status;
+ AML_DATA_NODE * DataNode;
+
+ // A data node must not be created for certain data types.
+ if ((DataType == EAmlNodeDataTypeNone) ||
+ (DataType == EAmlNodeDataTypeReserved1) ||
+ (DataType == EAmlNodeDataTypeReserved2) ||
+ (DataType == EAmlNodeDataTypeReserved3) ||
+ (DataType == EAmlNodeDataTypeReserved4) ||
+ (DataType == EAmlNodeDataTypeReserved5) ||
+ (Data == NULL) ||
+ (DataSize == 0) ||
+ (NewDataNodePtr == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ DataNode = AllocateZeroPool (sizeof (AML_DATA_NODE));
+ if (DataNode == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Status = AmlInitializeNodeHeader (&DataNode->NodeHeader, EAmlNodeData);
+ if (EFI_ERROR (Status)) {
+ FreePool (DataNode);
+ ASSERT (0);
+ return Status;
+ }
+
+ DataNode->Buffer = AllocateCopyPool (DataSize, Data);
+ if (DataNode->Buffer == NULL) {
+ AmlDeleteDataNode (DataNode);
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ DataNode->DataType = DataType;
+ DataNode->Size = DataSize;
+
+ *NewDataNodePtr = DataNode;
+
+ return EFI_SUCCESS;
+}
+
+/** Delete a Node.
+
+ @param [in] Node Pointer to a Node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteNode (
+ IN AML_NODE_HEADER * Node
+ )
+{
+ EFI_STATUS Status;
+ EAML_PARSE_INDEX Index;
+
+ // Check that the node being deleted is unlinked.
+ // When removing the node, its parent and list are reset
+ // with InitializeListHead. Thus it must be empty.
+ if (!IS_AML_NODE_VALID (Node) ||
+ !AML_NODE_IS_DETACHED (Node)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ switch (Node->NodeType) {
+ case EAmlNodeRoot:
+ {
+ // Check the variable list of arguments has been cleaned.
+ if (!IsListEmpty (AmlNodeGetVariableArgList (Node))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = AmlDeleteRootNode ((AML_ROOT_NODE*)Node);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+ break;
+ }
+
+ case EAmlNodeObject:
+ {
+ // Check the variable list of arguments has been cleaned.
+ if (!IsListEmpty (AmlNodeGetVariableArgList (Node))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Check the fixed argument list has been cleaned.
+ for (Index = EAmlParseIndexTerm0; Index < EAmlParseIndexMax; Index++) {
+ if (((AML_OBJECT_NODE*)Node)->FixedArgs[Index] != NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+ }
+
+ Status = AmlDeleteObjectNode ((AML_OBJECT_NODE*)Node);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+ break;
+ }
+
+ case EAmlNodeData:
+ {
+ Status = AmlDeleteDataNode ((AML_DATA_NODE*)Node);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+ break;
+ }
+
+ default:
+ {
+ ASSERT (0);
+ Status = EFI_INVALID_PARAMETER;
+ break;
+ }
+ } // switch
+
+ return Status;
+}
+
+/** Check whether ObjectNode has the input attribute.
+ This function can be used to check ObjectNode is an object node
+ at the same time.
+
+ @param [in] ObjectNode Pointer to an object node.
+ @param [in] Attribute Attribute to check for.
+
+ @retval TRUE The node is an AML object and the attribute is present.
+ @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlNodeHasAttribute (
+ IN CONST AML_OBJECT_NODE * ObjectNode,
+ IN AML_OP_ATTRIBUTE Attribute
+ )
+{
+ if (!IS_AML_OBJECT_NODE (ObjectNode) ||
+ (ObjectNode->AmlByteEncoding == NULL)) {
+ return FALSE;
+ }
+
+ return ((ObjectNode->AmlByteEncoding->Attribute &
+ Attribute) == 0 ? FALSE : TRUE);
+}
+
+/** Check whether ObjectNode has the input OpCode/SubOpcode couple.
+
+ @param [in] ObjectNode Pointer to an object node.
+ @param [in] OpCode OpCode to check
+ @param [in] SubOpCode SubOpCode to check
+
+ @retval TRUE The node is an AML object and
+ the Opcode and the SubOpCode match.
+ @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlNodeCompareOpCode (
+ IN CONST AML_OBJECT_NODE * ObjectNode,
+ IN UINT8 OpCode,
+ IN UINT8 SubOpCode
+ )
+{
+ if (!IS_AML_OBJECT_NODE (ObjectNode) ||
+ (ObjectNode->AmlByteEncoding == NULL)) {
+ return FALSE;
+ }
+
+ ASSERT (AmlIsOpCodeValid (OpCode, SubOpCode));
+
+ return ((ObjectNode->AmlByteEncoding->OpCode == OpCode) &&
+ (ObjectNode->AmlByteEncoding->SubOpCode == SubOpCode)) ?
+ TRUE : FALSE;
+}
+
+/** Check whether a Node is an integer node.
+
+ By integer node we mean an object node having one of the following opcode:
+ - AML_BYTE_PREFIX;
+ - AML_WORD_PREFIX;
+ - AML_DWORD_PREFIX;
+ - AML_QWORD_PREFIX.
+
+ @param [in] Node The node to check.
+
+ @retval TRUE The Node is an integer node.
+ @retval FALSE Otherwise.
+*/
+BOOLEAN
+EFIAPI
+IsIntegerNode (
+ IN AML_OBJECT_NODE * Node
+ )
+{
+ UINT8 OpCode;
+
+ if (!IS_AML_OBJECT_NODE (Node) ||
+ (Node->AmlByteEncoding == NULL)) {
+ return FALSE;
+ }
+
+ // Check Node is an integer node.
+ OpCode = Node->AmlByteEncoding->OpCode;
+ if ((OpCode != AML_BYTE_PREFIX) &&
+ (OpCode != AML_WORD_PREFIX) &&
+ (OpCode != AML_DWORD_PREFIX) &&
+ (OpCode != AML_QWORD_PREFIX)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** Check whether a Node is a ZeroOp, a OneOp or a OnesOp.
+
+ These two objects don't have a data node holding
+ a value. This require special handling.
+
+ @param [in] Node The node to check.
+
+ @retval TRUE The Node is a ZeroOp or OneOp.
+ @retval FALSE Otherwise.
+*/
+BOOLEAN
+EFIAPI
+IsSpecialIntegerNode (
+ IN AML_OBJECT_NODE * Node
+ )
+{
+ UINT8 OpCode;
+
+ if (!IS_AML_OBJECT_NODE (Node) ||
+ (Node->AmlByteEncoding == NULL)) {
+ return FALSE;
+ }
+
+ OpCode = Node->AmlByteEncoding->OpCode;
+
+ if ((OpCode != AML_ZERO_OP) &&
+ (OpCode != AML_ONE_OP) &&
+ (OpCode != AML_ONES_OP)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/** Check whether Node corresponds to a method definition.
+
+ A method definition can be introduced:
+ - By a method object, having an AML_METHOD_OP OpCode;
+ - By an external definition of a method, having an AML_EXTERNAL_OP OpCode
+ and an ObjectType byte set to the MethodObj.
+
+ Note:
+ An alias node, having an AML_ALIAS_OP, can be resolved to a method
+ definition. This function doesn't handle this case.
+
+ @param [in] Node Node to check whether it is a method definition.
+
+ @retval TRUE The Node is a method definition.
+ @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlIsMethodDefinitionNode (
+ IN CONST AML_OBJECT_NODE * Node
+ )
+{
+ AML_DATA_NODE * ObjectType;
+
+ // Node is checked to be an object node aswell.
+ if (AmlNodeCompareOpCode (Node, AML_METHOD_OP, 0)) {
+ return TRUE;
+ } else if (AmlNodeCompareOpCode (Node, AML_EXTERNAL_OP, 0)) {
+ // If the node is an external definition, check this is a method.
+ // DefExternal := ExternalOp NameString ObjectType ArgumentCount
+ // ExternalOp := 0x15
+ // ObjectType := ByteData
+ // ArgumentCount := ByteData (0 – 7)
+ ObjectType = (AML_DATA_NODE*)AmlGetFixedArgument (
+ (AML_OBJECT_NODE*)Node,
+ EAmlParseIndexTerm1
+ );
+ if (IS_AML_DATA_NODE (ObjectType) &&
+ (ObjectType->DataType == EAmlNodeDataTypeUInt) &&
+ ((ObjectType->Size == 1))) {
+ if (*((UINT8*)ObjectType->Buffer) == (UINT8)EAmlObjTypeMethodObj) {
+ // The external definition is a method.
+ return TRUE;
+ } else {
+ // The external definition is not a method.
+ return FALSE;
+ }
+ } else {
+ // The tree is inconsistent.
+ ASSERT (0);
+ return FALSE;
+ }
+ }
+
+ // This is not a method definition.
+ return FALSE;
+}
+
+/** Get the index at which the name of the node is stored.
+
+ @param [in] ObjectNode Pointer to an object node.
+ Must have the AML_IN_NAMESPACE attribute.
+ @param [out] Index Index of the name in the fixed list of arguments.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+AmlNodeGetNameIndex (
+ IN CONST AML_OBJECT_NODE * ObjectNode,
+ OUT EAML_PARSE_INDEX * Index
+ )
+{
+ EAML_PARSE_INDEX NameIndex;
+
+ if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE) ||
+ (ObjectNode->AmlByteEncoding == NULL) ||
+ (Index == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ NameIndex = ObjectNode->AmlByteEncoding->NameIndex;
+
+ if ((NameIndex > ObjectNode->AmlByteEncoding->MaxIndex) ||
+ (ObjectNode->AmlByteEncoding->Format[NameIndex] != EAmlName)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *Index = NameIndex;
+
+ return EFI_SUCCESS;
+}
+
+/** Get the name of the Node.
+
+ Node must be part of the namespace.
+
+ @param [in] ObjectNode Pointer to an object node,
+ which is part of the namespace.
+
+ @return A pointer to the name.
+ NULL otherwise.
+ Return NULL for the root node.
+**/
+CHAR8 *
+EFIAPI
+AmlNodeGetName (
+ IN CONST AML_OBJECT_NODE * ObjectNode
+ )
+{
+ EFI_STATUS Status;
+ EAML_PARSE_INDEX NameIndex;
+ AML_DATA_NODE * DataNode;
+
+ if (!AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE)) {
+ ASSERT (0);
+ return NULL;
+ }
+
+ // Get the index at which the name is stored in the fixed arguments list.
+ Status = AmlNodeGetNameIndex (ObjectNode, &NameIndex);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return NULL;
+ }
+
+ // The name is stored in a Data node.
+ DataNode = (AML_DATA_NODE*)ObjectNode->FixedArgs[NameIndex];
+ if (IS_AML_DATA_NODE (DataNode) &&
+ (DataNode->DataType == EAmlNodeDataTypeNameString)) {
+ return (CHAR8*)DataNode->Buffer;
+ }
+
+ /* Return NULL if no name is found.
+ This can occur if the name of a node is defined as a further
+ fixed argument.
+ E.g.: CreateField (BD03, 0x28, Add (ID03 + 0x08), BF33)
+ ^
+ The parser is here.
+ The parent of the Add statement is the CreateField statement. This
+ statement defines a name in the AML namespace. This name defined as
+ the fourth fixed argument. It hasn't been parsed yet.
+ */
+ return NULL;
+}
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h
new file mode 100644
index 000000000..3584b572b
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNode.h
@@ -0,0 +1,212 @@
+/** @file
+ AML Node.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_NODE_H_
+#define AML_NODE_H_
+
+#include <AmlNodeDefines.h>
+#include <IndustryStandard/Acpi.h>
+
+/** Create an AML_ROOT_NODE.
+ This node will be the root of the tree.
+
+ @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT header to copy
+ the data from.
+ @param [out] NewRootNodePtr The created AML_ROOT_NODE.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCreateRootNode (
+ IN CONST EFI_ACPI_DESCRIPTION_HEADER * SdtHeader,
+ OUT AML_ROOT_NODE ** NewRootNodePtr
+ );
+
+/** Create an AML_OBJECT_NODE.
+
+ @param [in] AmlByteEncoding Byte encoding entry.
+ @param [in] PkgLength PkgLength of the node if the AmlByteEncoding
+ has the PkgLen attribute.
+ 0 otherwise.
+ @param [out] NewObjectNodePtr The created AML_OBJECT_NODE.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCreateObjectNode (
+ IN CONST AML_BYTE_ENCODING * AmlByteEncoding,
+ IN UINT32 PkgLength,
+ OUT AML_OBJECT_NODE ** NewObjectNodePtr
+ );
+
+/** Create an AML_DATA_NODE.
+
+ @param [in] DataType DataType of the node.
+ @param [in] Data Pointer to the AML bytecode corresponding to
+ this node. Data is copied from there.
+ @param [in] DataSize Number of bytes to consider at the address
+ pointed by Data.
+ @param [out] NewDataNodePtr The created AML_DATA_NODE.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlCreateDataNode (
+ IN EAML_NODE_DATA_TYPE DataType,
+ IN CONST UINT8 * Data,
+ IN UINT32 DataSize,
+ OUT AML_DATA_NODE ** NewDataNodePtr
+ );
+
+/** Delete a Node.
+
+ @param [in] Node Pointer to a Node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteNode (
+ IN AML_NODE_HEADER * Node
+ );
+
+/** Check whether ObjectNode has the input attribute.
+ This function can be used to check ObjectNode is an object node
+ at the same time.
+
+ @param [in] ObjectNode Pointer to an object node.
+ @param [in] Attribute Attribute to check for.
+
+ @retval TRUE The node is an AML object and the attribute is present.
+ @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlNodeHasAttribute (
+ IN CONST AML_OBJECT_NODE * ObjectNode,
+ IN AML_OP_ATTRIBUTE Attribute
+ );
+
+/** Check whether ObjectNode has the input OpCode/SubOpcode couple.
+
+ @param [in] ObjectNode Pointer to an object node.
+ @param [in] OpCode OpCode to check
+ @param [in] SubOpCode SubOpCode to check
+
+ @retval TRUE The node is an AML object and
+ the Opcode and the SubOpCode match.
+ @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlNodeCompareOpCode (
+ IN CONST AML_OBJECT_NODE * ObjectNode,
+ IN UINT8 OpCode,
+ IN UINT8 SubOpCode
+ );
+
+/** Check whether a Node is an integer node.
+
+ By integer node we mean an object node having one of the following opcode:
+ - AML_BYTE_PREFIX;
+ - AML_WORD_PREFIX;
+ - AML_DWORD_PREFIX;
+ - AML_QWORD_PREFIX.
+
+ @param [in] Node The node to check.
+
+ @retval TRUE The Node is an integer node.
+ @retval FALSE Otherwise.
+*/
+BOOLEAN
+EFIAPI
+IsIntegerNode (
+ IN AML_OBJECT_NODE * Node
+ );
+
+/** Check whether a Node is a ZeroOp, a OneOp or a OnesOp.
+
+ These two objects don't have a data node holding
+ a value. This require special handling.
+
+ @param [in] Node The node to check.
+
+ @retval TRUE The Node is a ZeroOp or OneOp.
+ @retval FALSE Otherwise.
+*/
+BOOLEAN
+EFIAPI
+IsSpecialIntegerNode (
+ IN AML_OBJECT_NODE * Node
+ );
+
+/** Check whether Node corresponds to a method definition.
+
+ A method definition can be introduced:
+ - By a method object, having an AML_METHOD_OP OpCode;
+ - By an external definition of a method, having an AML_EXTERNAL_OP OpCode
+ and an ObjectType byte set to the MethodObj.
+
+ Note:
+ An alias node, having an AML_ALIAS_OP, can be resolved to a method
+ definition. This function doesn't handle this case.
+
+ @param [in] Node Node to check whether it is a method definition.
+
+ @retval TRUE The Node is a method definition.
+ @retval FALSE Otherwise.
+**/
+BOOLEAN
+EFIAPI
+AmlIsMethodDefinitionNode (
+ IN CONST AML_OBJECT_NODE * Node
+ );
+
+/** Get the index at which the name of the node is stored.
+
+ @param [in] ObjectNode Pointer to an object node.
+ Must have the AML_IN_NAMESPACE attribute.
+ @param [out] Index Index of the name in the fixed list of arguments.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+AmlNodeGetNameIndex (
+ IN CONST AML_OBJECT_NODE * ObjectNode,
+ OUT EAML_PARSE_INDEX * Index
+ );
+
+/** Get the name of the Node.
+
+ Node must be part of the namespace.
+
+ @param [in] ObjectNode Pointer to an object node,
+ which is part of the namespace.
+
+ @return A pointer to the name.
+ NULL otherwise.
+ Return NULL for the root node.
+**/
+CHAR8 *
+EFIAPI
+AmlNodeGetName (
+ IN CONST AML_OBJECT_NODE * ObjectNode
+ );
+
+#endif // AML_NODE_H_
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c
new file mode 100644
index 000000000..870346c40
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlNodeInterface.c
@@ -0,0 +1,566 @@
+/** @file
+ AML Node Interface.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+
+#include <AmlCoreInterface.h>
+#include <ResourceData/AmlResourceData.h>
+#include <String/AmlString.h>
+#include <Tree/AmlNode.h>
+#include <Tree/AmlTree.h>
+#include <Utils/AmlUtility.h>
+
+/** Returns the tree node type (Root/Object/Data).
+
+ @param [in] Node Pointer to a Node.
+
+ @return The node type.
+ EAmlNodeUnknown if invalid parameter.
+**/
+EAML_NODE_TYPE
+EFIAPI
+AmlGetNodeType (
+ IN AML_NODE_HEADER * Node
+ )
+{
+ if (!IS_AML_NODE_VALID (Node)) {
+ ASSERT (0);
+ return EAmlNodeUnknown;
+ }
+
+ return Node->NodeType;
+}
+
+/** Get the RootNode information.
+ The Node must be a root node.
+
+ @param [in] RootNode Pointer to a root node.
+ @param [out] SdtHeaderBuffer Buffer to copy the ACPI DSDT/SSDT header to.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetRootNodeInfo (
+ IN AML_ROOT_NODE * RootNode,
+ OUT EFI_ACPI_DESCRIPTION_HEADER * SdtHeaderBuffer
+ )
+{
+ if (!IS_AML_ROOT_NODE (RootNode) ||
+ (SdtHeaderBuffer == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (
+ SdtHeaderBuffer,
+ RootNode->SdtHeader,
+ sizeof (EFI_ACPI_DESCRIPTION_HEADER)
+ );
+
+ return EFI_SUCCESS;
+}
+
+/** Get the ObjectNode information.
+ The Node must be an object node.
+
+ @ingroup NodeInterfaceApi
+
+ @param [in] ObjectNode Pointer to an object node.
+ @param [out] OpCode Pointer holding the OpCode.
+ Optional, can be NULL.
+ @param [out] SubOpCode Pointer holding the SubOpCode.
+ Optional, can be NULL.
+ @param [out] PkgLen Pointer holding the PkgLen.
+ The PkgLen is 0 for nodes
+ not having the Pkglen attribute.
+ Optional, can be NULL.
+ @param [out] IsNameSpaceNode Pointer holding TRUE if the node is defining
+ or changing the NameSpace scope.
+ E.g.: The "Name ()" and "Scope ()" ASL
+ statements add/modify the NameSpace scope.
+ Their corresponding node are NameSpace nodes.
+ Optional, can be NULL.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetObjectNodeInfo (
+ IN AML_OBJECT_NODE * ObjectNode,
+ OUT UINT8 * OpCode, OPTIONAL
+ OUT UINT8 * SubOpCode, OPTIONAL
+ OUT UINT32 * PkgLen, OPTIONAL
+ OUT BOOLEAN * IsNameSpaceNode OPTIONAL
+ )
+{
+ if (!IS_AML_OBJECT_NODE (ObjectNode)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (OpCode != NULL) {
+ *OpCode = ObjectNode->AmlByteEncoding->OpCode;
+ }
+ if (SubOpCode != NULL) {
+ *SubOpCode = ObjectNode->AmlByteEncoding->SubOpCode;
+ }
+ if (PkgLen != NULL) {
+ *PkgLen = ObjectNode->PkgLen;
+ }
+ if (IsNameSpaceNode != NULL) {
+ *IsNameSpaceNode = AmlNodeHasAttribute (ObjectNode, AML_IN_NAMESPACE);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/** Returns the count of the fixed arguments for the input Node.
+
+ @param [in] Node Pointer to an object node.
+
+ @return Number of fixed arguments of the object node.
+ Return 0 if the node is not an object node.
+**/
+UINT8
+AmlGetFixedArgumentCount (
+ IN AML_OBJECT_NODE * Node
+ )
+{
+ if (IS_AML_OBJECT_NODE (Node) &&
+ (Node->AmlByteEncoding != NULL)) {
+ return (UINT8)Node->AmlByteEncoding->MaxIndex;
+ }
+
+ return 0;
+}
+
+/** Get the data type of the DataNode.
+ The Node must be a data node.
+
+ @param [in] DataNode Pointer to a data node.
+ @param [out] DataType Pointer holding the data type of the data buffer.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetNodeDataType (
+ IN AML_DATA_NODE * DataNode,
+ OUT EAML_NODE_DATA_TYPE * DataType
+ )
+{
+ if (!IS_AML_DATA_NODE (DataNode) ||
+ (DataType == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *DataType = DataNode->DataType;
+
+ return EFI_SUCCESS;
+}
+
+/** Get the descriptor Id of the resource data element
+ contained in the DataNode.
+
+ The Node must be a data node.
+ The Node must have the resource data type, i.e. have the
+ EAmlNodeDataTypeResourceData data type.
+
+ @param [in] DataNode Pointer to a data node containing a
+ resource data element.
+ @param [out] ResourceDataType Pointer holding the descriptor Id of
+ the resource data.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetResourceDataType (
+ IN AML_DATA_NODE * DataNode,
+ OUT AML_RD_HEADER * ResourceDataType
+ )
+{
+ if (!IS_AML_DATA_NODE (DataNode) ||
+ (ResourceDataType == NULL) ||
+ (DataNode->DataType != EAmlNodeDataTypeResourceData)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *ResourceDataType = AmlRdGetDescId (DataNode->Buffer);
+
+ return EFI_SUCCESS;
+}
+
+/** Get the data buffer and size of the DataNode.
+ The Node must be a data node.
+
+ BufferSize is always updated to the size of buffer of the DataNode.
+
+ If:
+ - the content of BufferSize is >= to the DataNode's buffer size;
+ - Buffer is not NULL;
+ then copy the content of the DataNode's buffer in Buffer.
+
+ @param [in] DataNode Pointer to a data node.
+ @param [out] Buffer Buffer to write the data to.
+ Optional, if NULL, only update BufferSize.
+ @param [in, out] BufferSize Pointer holding:
+ - At entry, the size of the Buffer;
+ - At exit, the size of the DataNode's
+ buffer size.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlGetDataNodeBuffer (
+ IN AML_DATA_NODE * DataNode,
+ OUT UINT8 * Buffer, OPTIONAL
+ IN OUT UINT32 * BufferSize
+ )
+{
+ if (!IS_AML_DATA_NODE (DataNode) ||
+ (BufferSize == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((*BufferSize >= DataNode->Size) &&
+ (Buffer != NULL)) {
+ CopyMem (Buffer, DataNode->Buffer, DataNode->Size);
+ }
+
+ *BufferSize = DataNode->Size;
+
+ return EFI_SUCCESS;
+}
+
+/** Update the ACPI DSDT/SSDT table header.
+
+ The input SdtHeader information is copied to the tree RootNode.
+ The table Length field is automatically updated.
+ The checksum field is only updated when serializing the tree.
+
+ @param [in] RootNode Pointer to a root node.
+ @param [in] SdtHeader Pointer to an ACPI DSDT/SSDT table header.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpdateRootNode (
+ IN AML_ROOT_NODE * RootNode,
+ IN CONST EFI_ACPI_DESCRIPTION_HEADER * SdtHeader
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Length;
+
+ if (!IS_AML_ROOT_NODE (RootNode) ||
+ (SdtHeader == NULL) ||
+ ((SdtHeader->Signature !=
+ EFI_ACPI_6_3_SECONDARY_SYSTEM_DESCRIPTION_TABLE_SIGNATURE) &&
+ (SdtHeader->Signature !=
+ EFI_ACPI_6_3_DIFFERENTIATED_SYSTEM_DESCRIPTION_TABLE_SIGNATURE))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CopyMem (
+ RootNode->SdtHeader,
+ SdtHeader,
+ sizeof (EFI_ACPI_DESCRIPTION_HEADER)
+ );
+
+ // Update the Length field.
+ Status = AmlComputeSize ((AML_NODE_HEADER*)RootNode, &Length);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ RootNode->SdtHeader->Length = Length +
+ (UINT32)sizeof (EFI_ACPI_DESCRIPTION_HEADER);
+
+ return Status;
+}
+
+/** Update an object node representing an integer with a new value.
+
+ The object node must have one of the following OpCodes:
+ - AML_BYTE_PREFIX
+ - AML_WORD_PREFIX
+ - AML_DWORD_PREFIX
+ - AML_QWORD_PREFIX
+ - AML_ZERO_OP
+ - AML_ONE_OP
+
+ The following OpCode is not supported:
+ - AML_ONES_OP
+
+ @param [in] IntegerOpNode Pointer an object node containing an integer.
+ Must not be an object node with an AML_ONES_OP
+ OpCode.
+ @param [in] NewInteger New integer value to set.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpdateInteger (
+ IN AML_OBJECT_NODE * IntegerOpNode,
+ IN UINT64 NewInteger
+ )
+{
+ EFI_STATUS Status;
+
+ INT8 ValueWidthDiff;
+
+ if (!IS_AML_OBJECT_NODE (IntegerOpNode) ||
+ (!IsIntegerNode (IntegerOpNode) &&
+ !IsSpecialIntegerNode (IntegerOpNode)) ||
+ AmlNodeCompareOpCode (IntegerOpNode, AML_ONES_OP, 0)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = AmlNodeSetIntegerValue (IntegerOpNode, NewInteger, &ValueWidthDiff);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // If the new size is different from the old size, propagate the new size.
+ if (ValueWidthDiff != 0) {
+ // Propagate the information.
+ Status = AmlPropagateInformation (
+ (AML_NODE_HEADER*)IntegerOpNode,
+ (ValueWidthDiff > 0) ? TRUE : FALSE,
+ ABS (ValueWidthDiff),
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+ }
+
+ return Status;
+}
+
+/** Update the buffer of a data node.
+
+ Note: The data type of the buffer's content must match the data type of the
+ DataNode. This is a hard restriction to prevent undesired behaviour.
+
+ @param [in] DataNode Pointer to a data node.
+ @param [in] DataType Data type of the Buffer's content.
+ @param [in] Buffer Buffer containing the new data. The content of
+ the Buffer is copied.
+ @param [in] Size Size of the Buffer.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_UNSUPPORTED Operation not supporter.
+**/
+EFI_STATUS
+EFIAPI
+AmlUpdateDataNode (
+ IN AML_DATA_NODE * DataNode,
+ IN EAML_NODE_DATA_TYPE DataType,
+ IN UINT8 * Buffer,
+ IN UINT32 Size
+ )
+{
+ EFI_STATUS Status;
+
+ UINT32 ExpectedSize;
+ AML_OBJECT_NODE * ParentNode;
+ EAML_NODE_DATA_TYPE ExpectedArgType;
+ EAML_PARSE_INDEX Index;
+
+ if (!IS_AML_DATA_NODE (DataNode) ||
+ (DataType > EAmlNodeDataTypeMax) ||
+ (Buffer == NULL) ||
+ (Size == 0)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ParentNode = (AML_OBJECT_NODE*)AmlGetParent ((AML_NODE_HEADER*)DataNode);
+ if (!IS_AML_OBJECT_NODE (ParentNode)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // The NewNode and OldNode must have the same type.
+ // We do not allow to change the argument type of a data node.
+ // If required, the initial ASL template should be modified
+ // accordingly.
+ // It is however possible to interchange a raw buffer and a
+ // resource data element, since raw data can be misinterpreted
+ // as a resource data element.
+ ExpectedArgType = DataNode->DataType;
+ if ((ExpectedArgType != DataType) &&
+ (((ExpectedArgType != EAmlNodeDataTypeRaw) &&
+ (ExpectedArgType != EAmlNodeDataTypeResourceData)) ||
+ ((DataType != EAmlNodeDataTypeRaw) &&
+ (DataType != EAmlNodeDataTypeResourceData)))) {
+ ASSERT (0);
+ return EFI_UNSUPPORTED;
+ }
+
+ // Perform some compatibility checks.
+ switch (DataType) {
+ case EAmlNodeDataTypeNameString:
+ {
+ // Check the name contained in the Buffer is an AML name
+ // with the right size.
+ Status = AmlGetNameStringSize ((CONST CHAR8*)Buffer, &ExpectedSize);
+ if (EFI_ERROR (Status) ||
+ (Size != ExpectedSize)) {
+ ASSERT (0);
+ return Status;
+ }
+ break;
+ }
+ case EAmlNodeDataTypeString:
+ {
+ ExpectedSize = 0;
+ while (ExpectedSize < Size) {
+ // Cf ACPI 6.3 specification 20.2.3 Data Objects Encoding.
+ // AsciiCharList := Nothing | <AsciiChar AsciiCharList>
+ // AsciiChar := 0x01 - 0x7F
+ // NullChar := 0x00
+ if (Buffer[ExpectedSize] > 0x7F) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+ ExpectedSize++;
+ }
+
+ if (ExpectedSize != Size) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+ }
+ case EAmlNodeDataTypeUInt:
+ {
+ if (AmlIsNodeFixedArgument ((CONST AML_NODE_HEADER*)DataNode, &Index)) {
+ if ((ParentNode->AmlByteEncoding == NULL) ||
+ (ParentNode->AmlByteEncoding->Format == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // It is not possible to change the size of a fixed length UintX.
+ // E.g. for PackageOp the first fixed argument is of type EAmlUInt8
+ // and represents the count of elements. This type cannot be changed.
+ if ((ParentNode->AmlByteEncoding->Format[Index] != EAmlObject) &&
+ (DataNode->Size != Size)) {
+ ASSERT (0);
+ return EFI_UNSUPPORTED;
+ }
+ }
+ break;
+ }
+ case EAmlNodeDataTypeRaw:
+ {
+ // Check if the parent node has the byte list flag set.
+ if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+ }
+ case EAmlNodeDataTypeResourceData:
+ {
+ // The resource data can be either small or large resource data.
+ // Small resource data must be at least 1 byte.
+ // Large resource data must be at least as long as the header
+ // of a large resource data.
+ if (AML_RD_IS_LARGE (Buffer) &&
+ (Size < sizeof (ACPI_LARGE_RESOURCE_HEADER))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Check if the parent node has the byte list flag set.
+ if (!AmlNodeHasAttribute (ParentNode, AML_HAS_BYTE_LIST)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Check the size of the buffer is equal to the resource data size
+ // encoded in the input buffer.
+ ExpectedSize = AmlRdGetSize (Buffer);
+ if (ExpectedSize != Size) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+ }
+ case EAmlNodeDataTypeFieldPkgLen:
+ {
+ // Check the parent is a FieldNamed field element.
+ if (!AmlNodeCompareOpCode (ParentNode, AML_FIELD_NAMED_OP, 0)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+ break;
+ }
+ // None and reserved types.
+ default:
+ {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ break;
+ }
+ } // switch
+
+ // If the new size is different from the old size, propagate the new size.
+ if (DataNode->Size != Size) {
+ // Propagate the information.
+ Status = AmlPropagateInformation (
+ DataNode->NodeHeader.Parent,
+ (Size > DataNode->Size) ? TRUE : FALSE,
+ (Size > DataNode->Size) ?
+ (Size - DataNode->Size) :
+ (DataNode->Size - Size),
+ 0
+ );
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // Free the old DataNode buffer and allocate a new buffer to store the
+ // new data.
+ FreePool (DataNode->Buffer);
+ DataNode->Buffer = AllocateZeroPool (Size);
+ if (DataNode->Buffer == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ DataNode->Size = Size;
+ }
+
+ CopyMem (DataNode->Buffer, Buffer, Size);
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c
new file mode 100644
index 000000000..65dad95da
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.c
@@ -0,0 +1,1047 @@
+/** @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 * CurrRdNode;
+ AML_RD_HEADER RdDataType;
+
+ if (!AmlNodeCompareOpCode (BufferOpNode, AML_BUFFER_OP, 0) ||
+ !IS_AML_DATA_NODE (NewRdNode) ||
+ (NewRdNode->DataType != EAmlNodeDataTypeResourceData)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Get the first Resource data node in the variable list of
+ // argument of the BufferOp node.
+ CurrRdNode = (AML_DATA_NODE*)AmlGetNextVariableArgument (
+ (AML_NODE_HEADER*)BufferOpNode,
+ NULL
+ );
+ if ((CurrRdNode == NULL) ||
+ !IS_AML_DATA_NODE (CurrRdNode) ||
+ (CurrRdNode->DataType != EAmlNodeDataTypeResourceData)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Iterate through the Resource Data nodes to find the End Tag.
+ while (TRUE) {
+ Status = AmlGetResourceDataType (CurrRdNode, &RdDataType);
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ return Status;
+ }
+
+ // If the Resource Data is an End Tag,
+ // add the new node before and return.
+ if (AmlRdCompareDescId (
+ &RdDataType,
+ AML_RD_BUILD_SMALL_DESC_ID (ACPI_SMALL_END_TAG_DESCRIPTOR_NAME))) {
+ Status = AmlVarListAddBefore (
+ (AML_NODE_HEADER*)CurrRdNode,
+ (AML_NODE_HEADER*)NewRdNode)
+ ;
+ if (EFI_ERROR (Status)) {
+ ASSERT (0);
+ }
+ return Status;
+ }
+
+ // Get the next Resource Data node.
+ // If this was the last node and no End Tag was found, return error.
+ // It is possible to have only one Resource Data in a BufferOp,
+ // but it should not be possible to add a new Resource Data in the list
+ // in this case.
+ CurrRdNode = (AML_DATA_NODE*)AmlGetSiblingVariableArgument (
+ (AML_NODE_HEADER*)CurrRdNode
+ );
+ if (!IS_AML_DATA_NODE (CurrRdNode) ||
+ (CurrRdNode->DataType != EAmlNodeDataTypeResourceData)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+ } // while
+}
+
+/** 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;
+}
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h
new file mode 100644
index 000000000..0b3803c47
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTree.h
@@ -0,0 +1,127 @@
+/** @file
+ AML Tree.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_TREE_H_
+#define AML_TREE_H_
+
+#include <AmlNodeDefines.h>
+
+/** 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
+ );
+
+/** 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
+ );
+
+/** 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 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
+ );
+
+/** 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
+ );
+
+/** 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
+ );
+
+#endif // AML_TREE_H_
+
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c
new file mode 100644
index 000000000..3eeb7253b
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeEnumerator.c
@@ -0,0 +1,98 @@
+/** @file
+ AML Tree Enumerator.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlTree.h>
+
+/** Enumerate all nodes of the subtree under the input Node in the AML
+ bytestream order (i.e. in a depth first order), and call the CallBack
+ function with the input Context.
+ The prototype of the Callback function is EDKII_AML_TREE_ENUM_CALLBACK.
+
+ @param [in] Node Enumerate nodes of the subtree under this Node.
+ Must be a valid node.
+ @param [in] CallBack Callback function to call on each node.
+ @param [in, out] Context Void pointer used to pass some information
+ to the Callback function.
+ Optional, can be NULL.
+ @param [out] Status Optional parameter that can be used to get
+ the status of the Callback function.
+ If used, need to be init to EFI_SUCCESS.
+
+ @retval TRUE if the enumeration can continue or has finished without
+ interruption.
+ @retval FALSE if the enumeration needs to stopped or has stopped.
+**/
+BOOLEAN
+EFIAPI
+AmlEnumTree (
+ IN AML_NODE_HEADER * Node,
+ IN EDKII_AML_TREE_ENUM_CALLBACK CallBack,
+ IN OUT VOID * Context, OPTIONAL
+ OUT EFI_STATUS * Status OPTIONAL
+ )
+{
+ BOOLEAN ContinueEnum;
+
+ EAML_PARSE_INDEX Index;
+ EAML_PARSE_INDEX MaxIndex;
+
+ LIST_ENTRY * StartLink;
+ LIST_ENTRY * CurrentLink;
+
+ if (!IS_AML_NODE_VALID (Node) || (CallBack == NULL)) {
+ ASSERT (0);
+ if (Status != NULL) {
+ *Status = EFI_INVALID_PARAMETER;
+ }
+ return FALSE;
+ }
+
+ ContinueEnum = (*CallBack)(Node, Context, Status);
+ if (ContinueEnum == FALSE) {
+ return ContinueEnum;
+ }
+
+ // Iterate through the fixed list of arguments.
+ MaxIndex = (EAML_PARSE_INDEX)AmlGetFixedArgumentCount (
+ (AML_OBJECT_NODE*)Node
+ );
+ for (Index = EAmlParseIndexTerm0; Index < MaxIndex; Index++) {
+ ContinueEnum = AmlEnumTree (
+ AmlGetFixedArgument ((AML_OBJECT_NODE*)Node, Index),
+ CallBack,
+ Context,
+ Status
+ );
+ if (ContinueEnum == FALSE) {
+ return ContinueEnum;
+ }
+ }
+
+ // Iterate through the variable list of arguments.
+ StartLink = AmlNodeGetVariableArgList (Node);
+ if (StartLink != NULL) {
+ CurrentLink = StartLink->ForwardLink;
+ while (CurrentLink != StartLink) {
+ ContinueEnum = AmlEnumTree (
+ (AML_NODE_HEADER*)CurrentLink,
+ CallBack,
+ Context,
+ Status
+ );
+ if (ContinueEnum == FALSE) {
+ return ContinueEnum;
+ }
+ CurrentLink = CurrentLink->ForwardLink;
+ } // while
+ }
+
+ return ContinueEnum;
+}
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c
new file mode 100644
index 000000000..e99c23499
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.c
@@ -0,0 +1,353 @@
+/** @file
+ AML Tree Iterator.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <AmlNodeDefines.h>
+#include <Tree/AmlTreeIterator.h>
+
+#include <AmlCoreInterface.h>
+#include <Tree/AmlTreeTraversal.h>
+
+/** Iterator to traverse the tree.
+
+ This is an internal structure.
+*/
+typedef struct AmlTreeInternalIterator {
+ /// External iterator structure, containing the external APIs.
+ /// Must be the first field.
+ AML_TREE_ITERATOR Iterator;
+
+ // Note: The following members of this structure are opaque to the users
+ // of the Tree iterator APIs.
+
+ /// Pointer to the node on which the iterator has been initialized.
+ CONST AML_NODE_HEADER * InitialNode;
+
+ /// Pointer to the current node.
+ CONST AML_NODE_HEADER * CurrentNode;
+
+ /// Iteration mode.
+ /// Allow to choose how to traverse the tree/choose which node is next.
+ EAML_ITERATOR_MODE Mode;
+} AML_TREE_ITERATOR_INTERNAL;
+
+/** Get the current node of an iterator.
+
+ @param [in] Iterator Pointer to an iterator.
+ @param [out] OutNode Pointer holding the current node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetNode (
+ IN AML_TREE_ITERATOR * Iterator,
+ OUT AML_NODE_HEADER ** OutNode
+ )
+{
+ AML_TREE_ITERATOR_INTERNAL * InternalIterator;
+
+ InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+ // CurrentNode can be NULL, but InitialNode cannot.
+ if ((OutNode == NULL) ||
+ (InternalIterator == NULL) ||
+ (InternalIterator->Mode <= EAmlIteratorUnknown) ||
+ (InternalIterator->Mode >= EAmlIteratorModeMax) ||
+ !IS_AML_NODE_VALID (InternalIterator->InitialNode) ||
+ ((InternalIterator->CurrentNode != NULL) &&
+ !IS_AML_NODE_VALID (InternalIterator->CurrentNode))) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *OutNode = (AML_NODE_HEADER*)InternalIterator->CurrentNode;
+
+ return EFI_SUCCESS;
+}
+
+/** Move the current node of the iterator to the next node,
+ according to the iteration mode selected.
+
+ If NextNode is not NULL, return the next node.
+
+ @param [in] Iterator Pointer to an iterator.
+ @param [out] NextNode If not NULL, updated to the next node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetNextLinear (
+ IN AML_TREE_ITERATOR * Iterator,
+ OUT AML_NODE_HEADER ** NextNode
+ )
+{
+ AML_TREE_ITERATOR_INTERNAL * InternalIterator;
+
+ InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+ // CurrentNode can be NULL, but InitialNode cannot.
+ if ((InternalIterator == NULL) ||
+ (InternalIterator->Mode != EAmlIteratorLinear) ||
+ !IS_AML_NODE_VALID (InternalIterator->InitialNode) ||
+ !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Get the next node according to the iteration mode.
+ InternalIterator->CurrentNode = AmlGetNextNode (
+ InternalIterator->CurrentNode
+ );
+
+ if (NextNode != NULL) {
+ *NextNode = (AML_NODE_HEADER*)InternalIterator->CurrentNode;
+ }
+ return EFI_SUCCESS;
+}
+
+/** Move the current node of the iterator to the previous node,
+ according to the iteration mode selected.
+
+ If PrevNode is not NULL, return the previous node.
+
+ @param [in] Iterator Pointer to an iterator.
+ @param [out] PrevNode If not NULL, updated to the previous node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetPreviousLinear (
+ IN AML_TREE_ITERATOR * Iterator,
+ OUT AML_NODE_HEADER ** PrevNode
+ )
+{
+ AML_TREE_ITERATOR_INTERNAL * InternalIterator;
+
+ InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+ // CurrentNode can be NULL, but InitialNode cannot.
+ if ((InternalIterator == NULL) ||
+ (InternalIterator->Mode != EAmlIteratorLinear) ||
+ !IS_AML_NODE_VALID (InternalIterator->InitialNode) ||
+ !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ // Get the previous node according to the iteration mode.
+ InternalIterator->CurrentNode = AmlGetPreviousNode (
+ InternalIterator->CurrentNode
+ );
+ if (PrevNode != NULL) {
+ *PrevNode = (AML_NODE_HEADER*)InternalIterator->CurrentNode;
+ }
+ return EFI_SUCCESS;
+}
+
+/** Move the current node of the iterator to the next node,
+ according to the iteration mode selected.
+
+ If NextNode is not NULL, return the next node.
+
+ @param [in] Iterator Pointer to an iterator.
+ @param [out] NextNode If not NULL, updated to the next node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetNextBranch (
+ IN AML_TREE_ITERATOR * Iterator,
+ OUT AML_NODE_HEADER ** NextNode
+ )
+{
+ AML_TREE_ITERATOR_INTERNAL * InternalIterator;
+ AML_NODE_HEADER * Node;
+
+ InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+ // CurrentNode can be NULL, but InitialNode cannot.
+ if ((InternalIterator == NULL) ||
+ (InternalIterator->Mode != EAmlIteratorBranch) ||
+ !IS_AML_NODE_VALID (InternalIterator->InitialNode) ||
+ !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Node = AmlGetNextNode (InternalIterator->CurrentNode);
+ // Check whether NextNode is a sibling of InitialNode.
+ if (AmlGetParent (Node) ==
+ AmlGetParent ((AML_NODE_HEADER*)InternalIterator->InitialNode)) {
+ Node = NULL;
+ }
+
+ InternalIterator->CurrentNode = Node;
+
+ if (NextNode != NULL) {
+ *NextNode = Node;
+ }
+ return EFI_SUCCESS;
+}
+
+/** Move the current node of the iterator to the previous node,
+ according to the iteration mode selected.
+
+ If PrevNode is not NULL, return the previous node.
+
+ @param [in] Iterator Pointer to an iterator.
+ @param [out] PrevNode If not NULL, updated to the previous node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+AmlIteratorGetPreviousBranch (
+ IN AML_TREE_ITERATOR * Iterator,
+ OUT AML_NODE_HEADER ** PrevNode
+ )
+{
+ AML_TREE_ITERATOR_INTERNAL * InternalIterator;
+ AML_NODE_HEADER * Node;
+
+ InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)Iterator;
+
+ // CurrentNode can be NULL, but InitialNode cannot.
+ if ((InternalIterator == NULL) ||
+ (InternalIterator->Mode != EAmlIteratorBranch) ||
+ !IS_AML_NODE_VALID (InternalIterator->InitialNode) ||
+ !IS_AML_NODE_VALID (InternalIterator->CurrentNode)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Node = AmlGetPreviousNode (InternalIterator->CurrentNode);
+ // Check whether PreviousNode is a sibling of InitialNode.
+ if (AmlGetParent (Node) ==
+ AmlGetParent ((AML_NODE_HEADER*)InternalIterator->InitialNode)) {
+ Node = NULL;
+ }
+
+ InternalIterator->CurrentNode = Node;
+
+ if (PrevNode != NULL) {
+ *PrevNode = Node;
+ }
+ return EFI_SUCCESS;
+}
+
+/** Initialize an iterator.
+
+ Note: The caller must call AmlDeleteIterator () to free the memory
+ allocated for the iterator.
+
+ @param [in] Node Pointer to the node.
+ @param [in] IteratorMode Selected mode to traverse the tree.
+ @param [out] IteratorPtr Pointer holding the created iterator.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlInitializeIterator (
+ IN AML_NODE_HEADER * Node,
+ IN EAML_ITERATOR_MODE IteratorMode,
+ OUT AML_TREE_ITERATOR ** IteratorPtr
+ )
+{
+ AML_TREE_ITERATOR_INTERNAL * InternalIterator;
+
+ if (!IS_AML_NODE_VALID (Node) ||
+ (IteratorMode <= EAmlIteratorUnknown) ||
+ (IteratorMode >= EAmlIteratorModeMax) ||
+ (IteratorPtr == NULL)) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *IteratorPtr = NULL;
+ InternalIterator = (AML_TREE_ITERATOR_INTERNAL*)AllocateZeroPool (
+ sizeof (
+ AML_TREE_ITERATOR_INTERNAL
+ )
+ );
+ if (InternalIterator == NULL) {
+ ASSERT (0);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ InternalIterator->InitialNode = Node;
+ InternalIterator->CurrentNode = Node;
+ InternalIterator->Mode = IteratorMode;
+ InternalIterator->Iterator.GetNode = AmlIteratorGetNode;
+
+ switch (InternalIterator->Mode) {
+ case EAmlIteratorLinear:
+ {
+ InternalIterator->Iterator.GetNext = AmlIteratorGetNextLinear;
+ InternalIterator->Iterator.GetPrevious = AmlIteratorGetPreviousLinear;
+ break;
+ }
+ case EAmlIteratorBranch:
+ {
+ InternalIterator->Iterator.GetNext = AmlIteratorGetNextBranch;
+ InternalIterator->Iterator.GetPrevious = AmlIteratorGetPreviousBranch;
+ break;
+ }
+ default:
+ {
+ ASSERT (0);
+ FreePool (InternalIterator);
+ return EFI_INVALID_PARAMETER;
+ }
+ } // switch
+
+ *IteratorPtr = &InternalIterator->Iterator;
+
+ return EFI_SUCCESS;
+}
+
+/** Delete an iterator.
+
+ Note: The caller must have first initialized the iterator with the
+ AmlInitializeIterator () function.
+
+ @param [in] Iterator Pointer to an iterator.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteIterator (
+ IN AML_TREE_ITERATOR * Iterator
+ )
+{
+ if (Iterator == NULL) {
+ ASSERT (0);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FreePool (Iterator);
+
+ return EFI_SUCCESS;
+}
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h
new file mode 100644
index 000000000..d96eecab9
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeIterator.h
@@ -0,0 +1,220 @@
+/** @file
+ AML Iterator.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_ITERATOR_H_
+#define AML_ITERATOR_H_
+
+/* This header file does not include internal Node definition,
+ i.e. AML_ROOT_NODE, AML_OBJECT_NODE, etc. The node definitions
+ must be included by the caller file. The function prototypes must
+ only expose AML_NODE_HANDLE, AML_ROOT_NODE_HANDLE, etc. node
+ definitions.
+ This allows to keep the functions defined here both internal and
+ potentially external. If necessary, any function of this file can
+ be exposed externally.
+ The Api folder is internal to the AmlLib, but should only use these
+ functions. They provide a "safe" way to interact with the AmlLib.
+*/
+
+/**
+ @defgroup IteratorLibrary Iterator library
+ @ingroup NavigationApis
+ @{
+ The iterator library allow to navigate in the AML tree using an iterator.
+ It is possible to initialize/delete an iterator.
+
+ This iterator can progress in the tree by different orders:
+ - Linear progression: Iterate following the AML bytestream order
+ (depth first).
+ - Branch progression: Iterate following the AML bytestream order
+ (depth first), but stop iterating at the
+ end of the branch.
+
+ An iterator has the following features:
+ - GetNode: Get the current node pointed by the iterator.
+ - GetNext: Move the current node of the iterator to the next
+ node, according to the iteration mode selected.
+ - GetPrevious: Move the current node of the iterator to the previous
+ node, according to the iteration mode selected.
+ @}
+*/
+
+/**
+ @defgroup IteratorApis Iterator APIs
+ @ingroup IteratorLibrary
+ @{
+ Iterator APIs defines the action that can be done on an iterator:
+ - Initialization;
+ - Deletion;
+ - Getting the node currently pointed by the iterator;
+ - Moving to the next node;
+ - Moving to the previous node.
+ @}
+*/
+
+/**
+ @defgroup IteratorStructures Iterator structures
+ @ingroup IteratorLibrary
+ @{
+ Iterator structures define the enum/define values and structures related
+ to iterators.
+ @}
+*/
+
+/** Iterator mode.
+
+ Modes to choose how the iterator is progressing in the tree.
+ A
+ \-B <- Iterator initialized with this node.
+ | \-C
+ | | \-D
+ | \-E
+ | \-F
+ | \-G
+ \-H
+ \-I
+
+ @ingroup IteratorStructures
+*/
+typedef enum EAmlIteratorMode {
+ EAmlIteratorUnknown, ///< Unknown/Invalid AML IteratorMode
+ EAmlIteratorLinear, ///< Iterate following the AML bytestream order
+ /// (depth first).
+ /// The order followed by the iterator would be:
+ /// B, C, D, E, F, G, H, I, NULL.
+ EAmlIteratorBranch, ///< Iterate through the node of a branch.
+ /// The iteration follows the AML bytestream
+ /// order but within the branch B.
+ /// The order followed by the iterator would be:
+ /// B, C, D, E, F, G, NULL.
+ EAmlIteratorModeMax ///< Max enum.
+} EAML_ITERATOR_MODE;
+
+/** Iterator.
+
+ Allows to traverse the tree in different orders.
+
+ @ingroup IteratorStructures
+*/
+typedef struct AmlTreeIterator AML_TREE_ITERATOR;
+
+/** Function pointer to a get the current node of the iterator.
+
+ @ingroup IteratorApis
+
+ @param [in] Iterator Pointer to an iterator.
+ @param [out] OutNode Pointer holding the current node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+*/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_AML_TREE_ITERATOR_GET_NODE) (
+ IN AML_TREE_ITERATOR * Iterator,
+ OUT AML_NODE_HANDLE * OutNode
+ );
+
+/** Function pointer to move the current node of the iterator to the
+ next node, according to the iteration mode selected.
+
+ If NextNode is not NULL, return the next node.
+
+ @ingroup IteratorApis
+
+ @param [in] Iterator Pointer to an iterator.
+ @param [out] NextNode If not NULL, updated to the next node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+*/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_AML_TREE_ITERATOR_GET_NEXT) (
+ IN AML_TREE_ITERATOR * Iterator,
+ OUT AML_NODE_HANDLE * NextNode
+ );
+
+/** Function pointer to move the current node of the iterator to the
+ previous node, according to the iteration mode selected.
+
+ If PrevNode is not NULL, return the previous node.
+
+ @ingroup IteratorApis
+
+ @param [in] Iterator Pointer to an iterator.
+ @param [out] PrevNode If not NULL, updated to the previous node.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+*/
+typedef
+EFI_STATUS
+(EFIAPI * EDKII_AML_TREE_ITERATOR_GET_PREVIOUS) (
+ IN AML_TREE_ITERATOR * Iterator,
+ OUT AML_NODE_HANDLE * PrevNode
+ );
+
+/** Iterator structure to traverse the tree.
+
+ @ingroup IteratorStructures
+*/
+typedef struct AmlTreeIterator {
+ /// Get the current node of the iterator.
+ EDKII_AML_TREE_ITERATOR_GET_NODE GetNode;
+
+ /// Update the current node of the iterator with the next node.
+ EDKII_AML_TREE_ITERATOR_GET_NEXT GetNext;
+
+ /// Update the current node of the iterator with the previous node.
+ EDKII_AML_TREE_ITERATOR_GET_PREVIOUS GetPrevious;
+} AML_TREE_ITERATOR;
+
+
+/** Initialize an iterator.
+
+ Note: The caller must call AmlDeleteIterator () to free the memory
+ allocated for the iterator.
+
+ @ingroup IteratorApis
+
+ @param [in] Node Pointer to the node.
+ @param [in] IteratorMode Selected mode to traverse the tree.
+ @param [out] IteratorPtr Pointer holding the created iterator.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+ @retval EFI_OUT_OF_RESOURCES Could not allocate memory.
+**/
+EFI_STATUS
+EFIAPI
+AmlInitializeIterator (
+ IN AML_NODE_HANDLE Node,
+ IN EAML_ITERATOR_MODE IteratorMode,
+ OUT AML_TREE_ITERATOR ** IteratorPtr
+ );
+
+/** Delete an iterator.
+
+ Note: The caller must have first initialized the iterator with the
+ AmlInitializeIterator () function.
+
+ @ingroup IteratorApis
+
+ @param [in] Iterator Pointer to an iterator.
+
+ @retval EFI_SUCCESS The function completed successfully.
+ @retval EFI_INVALID_PARAMETER Invalid parameter.
+**/
+EFI_STATUS
+EFIAPI
+AmlDeleteIterator (
+ IN AML_TREE_ITERATOR * Iterator
+ );
+
+#endif // AML_ITERATOR_H_
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c
new file mode 100644
index 000000000..9d0c794db
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.c
@@ -0,0 +1,548 @@
+/** @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
+}
diff --git a/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h
new file mode 100644
index 000000000..a4980b716
--- /dev/null
+++ b/roms/edk2/DynamicTablesPkg/Library/Common/AmlLib/Tree/AmlTreeTraversal.h
@@ -0,0 +1,138 @@
+/** @file
+ AML Tree Traversal.
+
+ Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef AML_TREE_TRAVERSAL_H_
+#define AML_TREE_TRAVERSAL_H_
+
+#include <AmlNodeDefines.h>
+
+/** 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
+ );
+
+/** 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
+ );
+
+/** 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
+ );
+
+/** 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
+ );
+
+#endif // AML_TREE_TRAVERSAL_H_
+