/*++

Copyright (c) 2014 Minoca Corp. All Rights Reserved

Module Name:

    fvsect.c

Abstract:

    This module implements section extraction support for UEFI firmware
    volumes.

Author:

    Evan Green 11-Mar-2014

Environment:

    Firmware

--*/

//
// ------------------------------------------------------------------- Includes
//

#include "ueficore.h"
#include "fwvolp.h"

//
// ---------------------------------------------------------------- Definitions
//

#define EFI_SECTION_STREAM_NODE_MAGIC 0x6D727453 // 'mrtS'
#define EFI_SECTION_STREAM_CHILD_MAGIC 0x72745343 // 'rtSC'

#define NULL_STREAM_HANDLE 0

//
// ------------------------------------------------------ Data Type Definitions
//

/*++

Structure Description:

    This structure defines EFI firmware volume section stream data.

Members:

    Magic - Stores the magic value EFI_SECTION_STREAM_CHILD_MAGIC.

    ListEntry - Stores pointers to the next and previous child nodes in the
        stream.

    Type - Stores the type of child section.

    Size - Stores the size of the child section.

    OffsetInStream - Stores the offset from the beginning of the stream base to
        the section header in the stream.

    EncapsulatedStreamHandle - Stores 0 if the section is not an
        encapsulating section. Otherwise, it contains the stream handle of the
        encapsulated stream. This handle is always produced any time an
        encapsulating child is encountered, irrespective of whether or not the
        encapsulated stream is processed further.

    EncapsulationGuid - Stores the GUID of the encapsulation protocol.

    Event - Stores the event used to register for notification of the
        GUIDed extraction protocol arrival.

--*/

typedef struct _EFI_SECTION_CHILD_NODE {
    ULONG Magic;
    LIST_ENTRY ListEntry;
    UINT32 Type;
    UINT32 Size;
    UINT32 OffsetInStream;
    UINTN EncapsulatedStreamHandle;
    EFI_GUID *EncapsulationGuid;
    EFI_EVENT Event;
} EFI_SECTION_CHILD_NODE, *PEFI_SECTION_CHILD_NODE;

/*++

Structure Description:

    This structure defines EFI firmware volume section stream data.

Members:

    Magic - Stores the magic value EFI_SECTION_STREAM_NODE_MAGIC.

    ListEntry - Stores pointers to the next and previous stream nodes in the
        global list.

    StreamHandle - Stores the stream handle value.

    StreamBuffer - Stores a pointer to the stream data.

    StreamLength - Stores the size of the stream data in bytes.

    ChildList - Stores the list of child sections.

    AuthenticationStatus - Stores the authentication status for GUIDed
        extractions.

--*/

typedef struct _EFI_SECTION_STREAM_NODE {
    ULONG Magic;
    LIST_ENTRY ListEntry;
    UINTN StreamHandle;
    UINT8 *StreamBuffer;
    UINTN StreamLength;
    LIST_ENTRY ChildList;
    UINT32 AuthenticationStatus;
} EFI_SECTION_STREAM_NODE, *PEFI_SECTION_STREAM_NODE;

//
// ----------------------------------------------- Internal Function Prototypes
//

EFIAPI
EFI_STATUS
EfipFvOpenSectionStream (
    UINTN SectionStreamLength,
    VOID *SectionStream,
    BOOLEAN AllocateBuffer,
    UINT32 AuthenticationStatus,
    UINTN *SectionStreamHandle
    );

EFI_STATUS
EfipFvCreateChildNode (
    PEFI_SECTION_STREAM_NODE Stream,
    UINT32 ChildOffset,
    PEFI_SECTION_CHILD_NODE *ChildNode
    );

EFI_STATUS
EfipFvFindChildNode (
    PEFI_SECTION_STREAM_NODE SourceStream,
    EFI_SECTION_TYPE SearchType,
    UINTN *SectionInstance,
    EFI_GUID *SectionDefinitionGuid,
    PEFI_SECTION_CHILD_NODE *FoundChild,
    PEFI_SECTION_STREAM_NODE *FoundStream,
    UINT32 *AuthenticationStatus
    );

BOOLEAN
EfipFvIsValidSectionStream (
    VOID *SectionStream,
    UINTN SectionStreamLength
    );

EFI_STATUS
EfipFvFindStreamNode (
    UINTN SearchHandle,
    PEFI_SECTION_STREAM_NODE *FoundStream
    );

BOOLEAN
EfipFvChildIsType (
    PEFI_SECTION_STREAM_NODE Stream,
    PEFI_SECTION_CHILD_NODE Child,
    EFI_SECTION_TYPE SearchType,
    EFI_GUID *SectionDefinitionGuid
    );

VOID
EfipFvFreeChildNode (
    PEFI_SECTION_CHILD_NODE ChildNode
    );

//
// -------------------------------------------------------------------- Globals
//

LIST_ENTRY EfiStreamRoot;

//
// ------------------------------------------------------------------ Functions
//

EFIAPI
EFI_STATUS
EfiFvInitializeSectionExtraction (
    EFI_HANDLE ImageHandle,
    EFI_SYSTEM_TABLE *SystemTable
    )

/*++

Routine Description:

    This routine initializes the section extraction support for firmware
    volumes.

Arguments:

    ImageHandle - Supplies a pointer to the image handle.

    SystemTable - Supplies a pointer to the EFI system table.

Return Value:

    EFI status code.

--*/

{

    INITIALIZE_LIST_HEAD(&EfiStreamRoot);
    return EFI_SUCCESS;
}

EFIAPI
EFI_STATUS
EfiFvOpenSectionStream (
    UINTN SectionStreamLength,
    VOID *SectionStream,
    UINTN *SectionStreamHandle
    )

/*++

Routine Description:

    This routine creates and returns a new section stream handle to represent
    a new section stream.

Arguments:

    SectionStreamLength - Supplies the size in bytes of the section stream.

    SectionStream - Supplies the section stream.

    SectionStreamHandle - Supplies a pointer where a handle to the stream will
        be returned.

Return Value:

    EFI_SUCCESS on success.

    EFI_OUT_OF_RESOURCES if memory allocation failed.

    EFI_INVALID_PARAMETER if the section stream does not end noincidentally to
    the end of the previous section.

--*/

{

    EFI_STATUS Status;

    if (EfipFvIsValidSectionStream(SectionStream, SectionStreamLength) ==
        FALSE) {

        return EFI_INVALID_PARAMETER;
    }

    Status = EfipFvOpenSectionStream(SectionStreamLength,
                                     SectionStream,
                                     TRUE,
                                     0,
                                     SectionStreamHandle);

    return Status;
}

EFIAPI
EFI_STATUS
EfiFvCloseSectionStream (
    UINTN StreamHandle
    )

/*++

Routine Description:

    This routine closes an open section stream handle.

Arguments:

    StreamHandle - Supplies the stream handle previously returned.

Return Value:

    EFI status code.

--*/

{

    PEFI_SECTION_CHILD_NODE ChildNode;
    EFI_TPL OldTpl;
    EFI_STATUS Status;
    PEFI_SECTION_STREAM_NODE StreamNode;

    OldTpl = EfiCoreRaiseTpl(TPL_NOTIFY);
    Status = EfipFvFindStreamNode(StreamHandle, &StreamNode);
    if (!EFI_ERROR(Status)) {
        LIST_REMOVE(&(StreamNode->ListEntry));
        while (LIST_EMPTY(&(StreamNode->ChildList)) == FALSE) {
            ChildNode = LIST_VALUE(StreamNode->ChildList.Next,
                                   EFI_SECTION_CHILD_NODE,
                                   ListEntry);

            EfipFvFreeChildNode(ChildNode);
        }

        EfiCoreFreePool(StreamNode->StreamBuffer);
        EfiCoreFreePool(StreamNode);
        Status = EFI_SUCCESS;

    } else {
        Status = EFI_INVALID_PARAMETER;
    }

    EfiCoreRestoreTpl(OldTpl);
    return Status;
}

EFIAPI
EFI_STATUS
EfiFvGetSection (
    UINTN SectionStreamHandle,
    EFI_SECTION_TYPE *SectionType,
    EFI_GUID *SectionDefinitionGuid,
    UINTN SectionInstance,
    VOID **Buffer,
    UINTN *BufferSize,
    UINT32 *AuthenticationStatus,
    BOOLEAN IsFfs3Fv
    )

/*++

Routine Description:

    This routine reads a section from a given section stream.

Arguments:

    SectionStreamHandle - Supplies the stream handle of the stream to get the
        section from.

    SectionType - Supplies a pointer that on input contains the type of section
        to search for. On output, this will return the type of the section
        found.

    SectionDefinitionGuid - Supplies a pointer to the GUID of the section to
        search for if the section type indicates EFI_SECTION_GUID_DEFINED.

    SectionInstance - Supplies the instance of the requested section to
        return.

    Buffer - Supplies a pointer to a buffer value. If the value of the buffer
        is NULL, then the buffer is callee-allocated. If it is not NULL, then
        the supplied buffer is used.

    BufferSize - Supplies a pointer that on input contains the size of the
        buffer, if supplied. On output, the size of the section will be
        returned.

    AuthenticationStatus - Supplies a pointer where the authentication status
        will be returned.

    IsFfs3Fv - Supplies a boolean indicating if the firmware file system is
        version 3 (TRUE) or version 2 (FALSE).

Return Value:

    EFI_SUCCESS on success.

    EFI_PROTOCOL_ERROR if a GUIDed section was encountered but no extraction
    protocol was found.

    EFI_NOT_FOUND if an error occurred while parsing the section stream, or the
    requested section does not exist.

    EFI_OUT_OF_RESOURCES on allocation failure.

    EFI_INVALID_PARAMETER if the given section stream handle does not exist.

    EFI_WARN_TOO_SMALL if a buffer value was supplied but it was not big enough
    to hold the requested section.

--*/

{

    PEFI_SECTION_CHILD_NODE ChildNode;
    PEFI_SECTION_STREAM_NODE ChildStreamNode;
    UINT8 *CopyBuffer;
    UINTN CopySize;
    UINT32 ExtractedAuthenticationStatus;
    UINTN Instance;
    EFI_TPL OldTpl;
    EFI_COMMON_SECTION_HEADER *Section;
    UINTN SectionSize;
    EFI_STATUS Status;
    PEFI_SECTION_STREAM_NODE StreamNode;

    OldTpl = EfiCoreRaiseTpl(TPL_NOTIFY);
    Instance = SectionInstance + 1;
    Status = EfipFvFindStreamNode(SectionStreamHandle, &StreamNode);
    if (EFI_ERROR(Status)) {
        Status = EFI_INVALID_PARAMETER;
        goto FvGetSectionEnd;
    }

    //
    // Locate and return the appropriate section. If the section type is NULL,
    // return the whole stream.
    //

    if (SectionType == NULL) {
        CopySize = StreamNode->StreamLength;
        CopyBuffer = StreamNode->StreamBuffer;
        *AuthenticationStatus = StreamNode->AuthenticationStatus;

    } else {
        Status = EfipFvFindChildNode(StreamNode,
                                     *SectionType,
                                     &Instance,
                                     SectionDefinitionGuid,
                                     &ChildNode,
                                     &ChildStreamNode,
                                     &ExtractedAuthenticationStatus);

        if (EFI_ERROR(Status)) {
            goto FvGetSectionEnd;
        }

        Section = (EFI_COMMON_SECTION_HEADER *)(ChildStreamNode->StreamBuffer +
                                                ChildNode->OffsetInStream);

        if (EFI_IS_SECTION2(Section)) {

            ASSERT(EFI_SECTION2_SIZE(Section) > 0x00FFFFFF);

            if (IsFfs3Fv == FALSE) {
                RtlDebugPrint("Error: FFS3 section in FFS2 volume.\n");
                Status = EFI_NOT_FOUND;
                goto FvGetSectionEnd;
            }

            CopySize = EFI_SECTION2_SIZE(Section) -
                       sizeof(EFI_COMMON_SECTION_HEADER2);

            CopyBuffer = (UINT8 *)Section + sizeof(EFI_COMMON_SECTION_HEADER2);

        } else {
            CopySize = EFI_SECTION_SIZE(Section) -
                       sizeof(EFI_COMMON_SECTION_HEADER);

            CopyBuffer = (UINT8 *)Section + sizeof(EFI_COMMON_SECTION_HEADER);
        }

        *AuthenticationStatus = ExtractedAuthenticationStatus;
    }

    SectionSize = CopySize;
    if (*Buffer != NULL) {
        if (*BufferSize < CopySize) {
            Status = EFI_WARN_BUFFER_TOO_SMALL;
            CopySize = *BufferSize;
        }

    } else {
        *Buffer = EfiCoreAllocateBootPool(CopySize);
        if (*Buffer == NULL) {
            Status = EFI_OUT_OF_RESOURCES;
            goto FvGetSectionEnd;
        }
    }

    EfiCoreCopyMemory(*Buffer, CopyBuffer, CopySize);
    *BufferSize = SectionSize;

FvGetSectionEnd:
    EfiCoreRestoreTpl(OldTpl);
    return Status;
}

//
// --------------------------------------------------------- Internal Functions
//

EFIAPI
EFI_STATUS
EfipFvOpenSectionStream (
    UINTN SectionStreamLength,
    VOID *SectionStream,
    BOOLEAN AllocateBuffer,
    UINT32 AuthenticationStatus,
    UINTN *SectionStreamHandle
    )

/*++

Routine Description:

    This routine creates and returns a new section stream handle to represent
    a new section stream.

Arguments:

    SectionStreamLength - Supplies the size in bytes of the section stream.

    SectionStream - Supplies the section stream.

    AllocateBuffer - Supplies a boolean indicating whether to copy the stream
        buffer (TRUE) or use the buffer in-place (FALSE).

    AuthenticationStatus - Supplies the authentication status.

    SectionStreamHandle - Supplies a pointer where a handle to the stream will
        be returned.

Return Value:

    EFI_SUCCESS on success.

    EFI_OUT_OF_RESOURCES if memory allocation failed.

    EFI_INVALID_PARAMETER if the section stream does not end noincidentally to
    the end of the previous section.

--*/

{

    PEFI_SECTION_STREAM_NODE NewStream;
    EFI_TPL OldTpl;

    NewStream = EfiCoreAllocateBootPool(sizeof(EFI_SECTION_STREAM_NODE));
    if (NewStream == NULL) {
        return EFI_OUT_OF_RESOURCES;
    }

    EfiCoreSetMemory(NewStream, sizeof(EFI_SECTION_STREAM_NODE), 0);
    if (AllocateBuffer != FALSE) {
        if (SectionStreamLength > 0) {
            NewStream->StreamBuffer =
                                  EfiCoreAllocateBootPool(SectionStreamLength);

            if (NewStream->StreamBuffer == NULL) {
                EfiCoreFreePool(NewStream);
                return EFI_OUT_OF_RESOURCES;
            }

            EfiCoreCopyMemory(NewStream->StreamBuffer,
                              SectionStream,
                              SectionStreamLength);

        }

    //
    // The caller supplied the buffer, use it directly.
    //

    } else {
        NewStream->StreamBuffer = SectionStream;
    }

    NewStream->Magic = EFI_SECTION_STREAM_NODE_MAGIC;
    NewStream->StreamHandle = (UINTN)NewStream;
    NewStream->StreamLength = SectionStreamLength;
    INITIALIZE_LIST_HEAD(&(NewStream->ChildList));
    NewStream->AuthenticationStatus = AuthenticationStatus;

    //
    // Add this shiny new stream to the list.
    //

    OldTpl = EfiCoreRaiseTpl(TPL_NOTIFY);
    INSERT_BEFORE(&(NewStream->ListEntry), &EfiStreamRoot);
    EfiCoreRestoreTpl(OldTpl);
    *SectionStreamHandle = NewStream->StreamHandle;
    return EFI_SUCCESS;
}

EFI_STATUS
EfipFvCreateChildNode (
    PEFI_SECTION_STREAM_NODE Stream,
    UINT32 ChildOffset,
    PEFI_SECTION_CHILD_NODE *ChildNode
    )

/*++

Routine Description:

    This routine parses and creates a new child node.

Arguments:

    Stream - Supplies a pointer to the stream to parse.

    ChildOffset - Supplies the offset within the stream to parse from.

    ChildNode - Supplies a pointer where a pointer to the newly created child
        will be returned.

Return Value:

    EFI_SUCCESS on success.

    EFI_PROTOCOL_ERROR if the GUIDed extraction protocol needed does not
    exist.

--*/

{

    PEFI_SECTION_CHILD_NODE Node;
    EFI_COMMON_SECTION_HEADER *SectionHeader;
    EFI_STATUS Status;

    SectionHeader = (EFI_COMMON_SECTION_HEADER *)(Stream->StreamBuffer +
                                                  ChildOffset);

    *ChildNode = EfiCoreAllocateBootPool(sizeof(EFI_SECTION_CHILD_NODE));
    if (*ChildNode == NULL) {
        return EFI_OUT_OF_RESOURCES;
    }

    Node = *ChildNode;
    EfiCoreSetMemory(Node, sizeof(EFI_SECTION_CHILD_NODE), 0);
    Node->Magic = EFI_SECTION_STREAM_CHILD_MAGIC;
    Node->Type = SectionHeader->Elements.Type;
    if (EFI_IS_SECTION2(SectionHeader)) {
        Node->Size = EFI_SECTION2_SIZE(SectionHeader);

    } else {
        Node->Size = EFI_SECTION_SIZE(SectionHeader);
    }

    Node->OffsetInStream = ChildOffset;
    Node->EncapsulatedStreamHandle = NULL_STREAM_HANDLE;
    Node->EncapsulationGuid = NULL;
    switch (Node->Type) {

    //
    // Handle compressed encapsulation sections.
    //

    case EFI_SECTION_COMPRESSION:
        Status = EFI_SUCCESS;
        break;

    //
    // Handle GUIDed encapsulation sections.
    //

    case EFI_SECTION_GUID_DEFINED:
        Status = EFI_SUCCESS;
        break;

    //
    // No processing is needed on leaf nodes.
    //

    default:
        Status = EFI_SUCCESS;
        break;
    }

    INSERT_BEFORE(&(Node->ListEntry), &(Stream->ChildList));
    return Status;
}

EFI_STATUS
EfipFvFindChildNode (
    PEFI_SECTION_STREAM_NODE SourceStream,
    EFI_SECTION_TYPE SearchType,
    UINTN *SectionInstance,
    EFI_GUID *SectionDefinitionGuid,
    PEFI_SECTION_CHILD_NODE *FoundChild,
    PEFI_SECTION_STREAM_NODE *FoundStream,
    UINT32 *AuthenticationStatus
    )

/*++

Routine Description:

    This routine recursively searches for and builds the section stream
    database looking for the requested section.

Arguments:

    SourceStream - Supplies a pointer to the stream to search.

    SearchType - Supplies the type of section to search for.

    SectionInstance - Supplies a pointer to the section instance to find. This
        is an in/out parameter to handle recursion.

    SectionDefinitionGuid - Supplies a pointer where the GUID of the section
        will be returned.

    FoundChild - Supplies a pointer where the child node will be returned on
        success.

    FoundStream - Supplies a pointer where the stream corresponding with the
        child will be returned on success.

    AuthenticationStatus - Supplies a pointer to the authentication status.

Return Value:

    EFI_SUCCESS on success.

    EFI_NOT_FOUND if the requested child node does not exist.

    EFI_PROTOCOL_ERROR if a required GUIDed section extraction protocol does
    not exist.

--*/

{

    PEFI_SECTION_CHILD_NODE CurrentChildNode;
    VOID *EncapsulatedStream;
    EFI_STATUS ErrorStatus;
    BOOLEAN Match;
    UINT32 NextChildOffset;
    PEFI_SECTION_CHILD_NODE RecursedChildNode;
    PEFI_SECTION_STREAM_NODE RecursedFoundStream;
    EFI_STATUS Status;

    CurrentChildNode = NULL;
    ErrorStatus = EFI_NOT_FOUND;
    if (SourceStream->StreamLength == 0) {
        return EFI_NOT_FOUND;
    }

    //
    // If the stream exists but not child nodes have been parsed out yet, then
    // extract the first child.
    //

    if ((LIST_EMPTY(&(SourceStream->ChildList)) != FALSE) &&
        (SourceStream->StreamLength >= sizeof(EFI_COMMON_SECTION_HEADER))) {

        Status = EfipFvCreateChildNode(SourceStream, 0, &CurrentChildNode);
        if (EFI_ERROR(Status)) {
            return Status;
        }
    }

    //
    // At least one child has been parsed out of the section stream.  So walk
    // through the sections that have already been parsed out looking for the
    // requested section. If necessary, continue parsing section stream and
    // adding children until either the requested section is found, or the
    // stream ends.
    //

    CurrentChildNode = LIST_VALUE(SourceStream->ChildList.Next,
                                  EFI_SECTION_CHILD_NODE,
                                  ListEntry);

    while (TRUE) {

        ASSERT((CurrentChildNode != NULL) &&
               (CurrentChildNode->Magic == EFI_SECTION_STREAM_CHILD_MAGIC));

        Match = EfipFvChildIsType(SourceStream,
                                  CurrentChildNode,
                                  SearchType,
                                  SectionDefinitionGuid);

        if (Match != FALSE) {
            *SectionInstance -= 1;
            if (*SectionInstance == 0) {
                *FoundChild = CurrentChildNode;
                *FoundStream = SourceStream;
                *AuthenticationStatus = SourceStream->AuthenticationStatus;
                return EFI_SUCCESS;
            }
        }

        //
        // If the current node is an encapsulating node, recurse into it.
        //

        if (CurrentChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) {
            EncapsulatedStream =
                          (VOID *)(CurrentChildNode->EncapsulatedStreamHandle);

            Status = EfipFvFindChildNode(
                                  (PEFI_SECTION_STREAM_NODE)EncapsulatedStream,
                                  SearchType,
                                  SectionInstance,
                                  SectionDefinitionGuid,
                                  &RecursedChildNode,
                                  &RecursedFoundStream,
                                  AuthenticationStatus);

            //
            // If the recursion was not successful, save the error code and
            // continue to find the requested child node in the rest of the
            // stream.
            //

            if (*SectionInstance == 0) {

                ASSERT(!EFI_ERROR(Status));

                *FoundChild = RecursedChildNode;
                *FoundStream = RecursedFoundStream;
                return Status;

            } else {
                ErrorStatus = Status;
            }

        //
        // If the node type is GUIDed, but the node has no encapsulating data,
        // node data should not be parsed because a required GUIDed section
        // extraction protocol does not exist.
        //

        } else if ((CurrentChildNode->Type == EFI_SECTION_GUID_DEFINED) &&
                   (SearchType != EFI_SECTION_GUID_DEFINED)) {

            ErrorStatus = EFI_PROTOCOL_ERROR;
        }

        //
        // If there are more parsed nodes, go look through them.
        //

        if (CurrentChildNode->ListEntry.Next != &(SourceStream->ChildList)) {
            CurrentChildNode = LIST_VALUE(CurrentChildNode->ListEntry.Next,
                                          EFI_SECTION_CHILD_NODE,
                                          ListEntry);

        //
        // This is the end of the list of parsed nodes. See if there's any more
        // data and continue parsing out more children if there is.
        //

        } else {
            NextChildOffset = CurrentChildNode->OffsetInStream +
                              CurrentChildNode->Size;

            NextChildOffset = ALIGN_VALUE(NextChildOffset, 4);
            if (NextChildOffset <=
                (SourceStream->StreamLength -
                 sizeof(EFI_COMMON_SECTION_HEADER))) {

                Status = EfipFvCreateChildNode(SourceStream,
                                               NextChildOffset,
                                               &CurrentChildNode);

                if (EFI_ERROR(Status)) {
                    return Status;
                }

            } else {

                ASSERT(EFI_ERROR(ErrorStatus));

                return ErrorStatus;
            }
        }
    }

    //
    // Execution should never actually get here.
    //

    ASSERT(FALSE);

    return EFI_NOT_FOUND;
}

BOOLEAN
EfipFvIsValidSectionStream (
    VOID *SectionStream,
    UINTN SectionStreamLength
    )

/*++

Routine Description:

    This routine determines whether or not a stream is valid.

Arguments:

    SectionStream - Supplies the section stream to check.

    SectionStreamLength - Supplies the size in bytes of the section stream.

Return Value:

    TRUE if the section stream is valid.

    FALSE if the section stream is invalid.

--*/

{

    EFI_COMMON_SECTION_HEADER *NextSectionHeader;
    EFI_COMMON_SECTION_HEADER *SectionHeader;
    UINTN SectionLength;
    UINTN TotalLength;

    TotalLength = 0;
    SectionHeader = (EFI_COMMON_SECTION_HEADER *)SectionStream;
    while (TotalLength < SectionStreamLength) {
        if (EFI_IS_SECTION2(SectionHeader)) {
            SectionLength = EFI_SECTION2_SIZE(SectionHeader);

        } else {
            SectionLength = EFI_SECTION_SIZE(SectionHeader);
        }

        TotalLength += SectionLength;
        if (TotalLength == SectionStreamLength) {
            return TRUE;
        }

        //
        // Move to the next byte following the section, and figure out where
        // the next section begins.
        //

        SectionHeader = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)SectionHeader +
                                                      SectionLength);

        NextSectionHeader = ALIGN_POINTER(SectionHeader, 4);
        TotalLength += (UINTN)NextSectionHeader - (UINTN)SectionHeader;
        SectionHeader = NextSectionHeader;
    }

    ASSERT(FALSE);

    return FALSE;
}

EFI_STATUS
EfipFvFindStreamNode (
    UINTN SearchHandle,
    PEFI_SECTION_STREAM_NODE *FoundStream
    )

/*++

Routine Description:

    This routine finds the stream matching the given handle. This routine
    assumes the TPL has already been raised.

Arguments:

    SearchHandle - Supplies the handle to search for.

    FoundStream - Supplies a pointer where a pointer to the stream will be
        returned on success.

Return Value:

    EFI_SUCCESS on success.

    EFI_NOT_FOUND if a stream matching the given handle was not found.

--*/

{

    PLIST_ENTRY CurrentEntry;
    PEFI_SECTION_STREAM_NODE Node;

    CurrentEntry = EfiStreamRoot.Next;
    while (CurrentEntry != &EfiStreamRoot) {
        Node = LIST_VALUE(CurrentEntry, EFI_SECTION_STREAM_NODE, ListEntry);

        ASSERT(Node->Magic == EFI_SECTION_STREAM_NODE_MAGIC);

        if (Node->StreamHandle == SearchHandle) {
            *FoundStream = Node;
            return EFI_SUCCESS;
        }

        CurrentEntry = CurrentEntry->Next;
    }

    *FoundStream = NULL;
    return EFI_NOT_FOUND;
}

BOOLEAN
EfipFvChildIsType (
    PEFI_SECTION_STREAM_NODE Stream,
    PEFI_SECTION_CHILD_NODE Child,
    EFI_SECTION_TYPE SearchType,
    EFI_GUID *SectionDefinitionGuid
    )

/*++

Routine Description:

    This routine determines if the given input stream and child matches the
    input type.

Arguments:

    Stream - Supplies a pointer to the section stream associated with the child.

    Child - Supplies a pointer to the child to check.

    SearchType - Supplies the type of section to check for.

    SectionDefinitionGuid - Supplies the GUID to check against if the search
        type is EFI_SECTION_GUID_DEFINED.

Return Value:

    TRUE if the section matches the parameters.

    FALSE if the section does not match.

--*/

{

    EFI_GUID_DEFINED_SECTION *GuidedSection;
    EFI_GUID_DEFINED_SECTION2 *GuidedSection2;
    EFI_GUID *SectionGuid;

    if (SearchType == EFI_SECTION_ALL) {
        return TRUE;
    }

    if (SearchType != Child->Type) {
        return FALSE;
    }

    if ((SearchType != EFI_SECTION_GUID_DEFINED) ||
        (SectionDefinitionGuid == NULL)) {

        return TRUE;
    }

    GuidedSection = (EFI_GUID_DEFINED_SECTION *)(Stream->StreamBuffer +
                                                 Child->OffsetInStream);

    if (EFI_IS_SECTION2(GuidedSection)) {
        GuidedSection2 = (EFI_GUID_DEFINED_SECTION2 *)GuidedSection;
        SectionGuid = &(GuidedSection2->SectionDefinitionGuid);

    } else {
        SectionGuid = &(GuidedSection->SectionDefinitionGuid);
    }

    if (EfiCoreCompareGuids(SectionGuid, SectionDefinitionGuid) != FALSE) {
        return TRUE;
    }

    return FALSE;
}

VOID
EfipFvFreeChildNode (
    PEFI_SECTION_CHILD_NODE ChildNode
    )

/*++

Routine Description:

    This routine destroys a firmware volume section child node.

Arguments:

    ChildNode - Supplies the child node to destroy.

Return Value:

    None.

--*/

{

    ASSERT(ChildNode->Magic == EFI_SECTION_STREAM_CHILD_MAGIC);

    LIST_REMOVE(&(ChildNode->ListEntry));
    if (ChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) {
        EfiFvCloseSectionStream(ChildNode->EncapsulatedStreamHandle);
    }

    if (ChildNode->Event != NULL) {
        EfiCloseEvent(ChildNode->Event);
    }

    ChildNode->Magic = 0;
    EfiCoreFreePool(ChildNode);
    return;
}

