DynamicTablesPkg: AML tree/node cloning

It is often desirable to clone an AML branch/tree
or an AML node. An example of could be to clone
an AML template before fixup so that the original
AML template remains unmodified. Another example
would be replicating a device branch in the AML
tree and fixing up the device information.

To facilitate such scenarios the AmlLib library
provides functions that can be used to clone an
AML branch/tree or an AML node.

Signed-off-by: Pierre Gondois <pierre.gondois@arm.com>
Signed-off-by: Sami Mujawar <sami.mujawar@arm.com>
Reviewed-by: Alexei Fedorov <Alexei.Fedorov@arm.com>
This commit is contained in:
Pierre Gondois 2020-08-03 19:40:54 +01:00 committed by mergify[bot]
parent e2c1104c50
commit 0414377c02
1 changed files with 205 additions and 0 deletions

View File

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