/** @file
AML Tree Iterator.
Copyright (c) 2019 - 2020, Arm Limited. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include
#include
#include
#include
/** 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;
}