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