diff options
Diffstat (limited to 'MdeModulePkg/Core/Dxe/SectionExtraction')
| -rw-r--r-- | MdeModulePkg/Core/Dxe/SectionExtraction/CoreSectionExtraction.c | 3306 |
1 files changed, 1653 insertions, 1653 deletions
diff --git a/MdeModulePkg/Core/Dxe/SectionExtraction/CoreSectionExtraction.c b/MdeModulePkg/Core/Dxe/SectionExtraction/CoreSectionExtraction.c index 2152833ff6..ce1a3835ba 100644 --- a/MdeModulePkg/Core/Dxe/SectionExtraction/CoreSectionExtraction.c +++ b/MdeModulePkg/Core/Dxe/SectionExtraction/CoreSectionExtraction.c @@ -1,1653 +1,1653 @@ -/** @file
- Section Extraction Protocol implementation.
-
- Stream database is implemented as a linked list of section streams,
- where each stream contains a linked list of children, which may be leaves or
- encapsulations.
-
- Children that are encapsulations generate new stream entries
- when they are created. Streams can also be created by calls to
- SEP->OpenSectionStream().
-
- The database is only created far enough to return the requested data from
- any given stream, or to determine that the requested data is not found.
-
- If a GUIDed encapsulation is encountered, there are three possiblilites.
-
- 1) A support protocol is found, in which the stream is simply processed with
- the support protocol.
-
- 2) A support protocol is not found, but the data is available to be read
- without processing. In this case, the database is built up through the
- recursions to return the data, and a RPN event is set that will enable
- the stream in question to be refreshed if and when the required section
- extraction protocol is published.This insures the AuthenticationStatus
- does not become stale in the cache.
-
- 3) A support protocol is not found, and the data is not available to be read
- without it. This results in EFI_PROTOCOL_ERROR.
-
-Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "DxeMain.h"
-
-//
-// Local defines and typedefs
-//
-#define CORE_SECTION_CHILD_SIGNATURE SIGNATURE_32('S','X','C','S')
-#define CHILD_SECTION_NODE_FROM_LINK(Node) \
- CR (Node, CORE_SECTION_CHILD_NODE, Link, CORE_SECTION_CHILD_SIGNATURE)
-
-typedef struct {
- UINT32 Signature;
- LIST_ENTRY Link;
- UINT32 Type;
- UINT32 Size;
- //
- // StreamBase + OffsetInStream == pointer to section header in stream. The
- // stream base is always known when walking the sections within.
- //
- UINT32 OffsetInStream;
- //
- // Then EncapsulatedStreamHandle below is always 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 the
- // encapsulated stream is processed further.
- //
- UINTN EncapsulatedStreamHandle;
- EFI_GUID *EncapsulationGuid;
- //
- // If the section REQUIRES an extraction protocol, register for RPN
- // when the required GUIDed extraction protocol becomes available.
- //
- EFI_EVENT Event;
-} CORE_SECTION_CHILD_NODE;
-
-#define CORE_SECTION_STREAM_SIGNATURE SIGNATURE_32('S','X','S','S')
-#define STREAM_NODE_FROM_LINK(Node) \
- CR (Node, CORE_SECTION_STREAM_NODE, Link, CORE_SECTION_STREAM_SIGNATURE)
-
-typedef struct {
- UINT32 Signature;
- LIST_ENTRY Link;
- UINTN StreamHandle;
- UINT8 *StreamBuffer;
- UINTN StreamLength;
- LIST_ENTRY Children;
- //
- // Authentication status is from GUIDed encapsulations.
- //
- UINT32 AuthenticationStatus;
-} CORE_SECTION_STREAM_NODE;
-
-#define NULL_STREAM_HANDLE 0
-
-typedef struct {
- CORE_SECTION_CHILD_NODE *ChildNode;
- CORE_SECTION_STREAM_NODE *ParentStream;
- VOID *Registration;
-} RPN_EVENT_CONTEXT;
-
-/**
- The ExtractSection() function processes the input section and
- allocates a buffer from the pool in which it returns the section
- contents. If the section being extracted contains
- authentication information (the section's
- GuidedSectionHeader.Attributes field has the
- EFI_GUIDED_SECTION_AUTH_STATUS_VALID bit set), the values
- returned in AuthenticationStatus must reflect the results of
- the authentication operation. Depending on the algorithm and
- size of the encapsulated data, the time that is required to do
- a full authentication may be prohibitively long for some
- classes of systems. To indicate this, use
- EFI_SECURITY_POLICY_PROTOCOL_GUID, which may be published by
- the security policy driver (see the Platform Initialization
- Driver Execution Environment Core Interface Specification for
- more details and the GUID definition). If the
- EFI_SECURITY_POLICY_PROTOCOL_GUID exists in the handle
- database, then, if possible, full authentication should be
- skipped and the section contents simply returned in the
- OutputBuffer. In this case, the
- EFI_AUTH_STATUS_PLATFORM_OVERRIDE bit AuthenticationStatus
- must be set on return. ExtractSection() is callable only from
- TPL_NOTIFY and below. Behavior of ExtractSection() at any
- EFI_TPL above TPL_NOTIFY is undefined. Type EFI_TPL is
- defined in RaiseTPL() in the UEFI 2.0 specification.
-
-
- @param This Indicates the
- EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL instance.
- @param InputSection Buffer containing the input GUIDed section
- to be processed. OutputBuffer OutputBuffer
- is allocated from boot services pool
- memory and contains the new section
- stream. The caller is responsible for
- freeing this buffer.
- @param OutputBuffer *OutputBuffer is allocated from boot services
- pool memory and contains the new section stream.
- The caller is responsible for freeing this buffer.
- @param OutputSize A pointer to a caller-allocated UINTN in
- which the size of OutputBuffer allocation
- is stored. If the function returns
- anything other than EFI_SUCCESS, the value
- of OutputSize is undefined.
-
- @param AuthenticationStatus A pointer to a caller-allocated
- UINT32 that indicates the
- authentication status of the
- output buffer. If the input
- section's
- GuidedSectionHeader.Attributes
- field has the
- EFI_GUIDED_SECTION_AUTH_STATUS_VAL
- bit as clear, AuthenticationStatus
- must return zero. Both local bits
- (19:16) and aggregate bits (3:0)
- in AuthenticationStatus are
- returned by ExtractSection().
- These bits reflect the status of
- the extraction operation. The bit
- pattern in both regions must be
- the same, as the local and
- aggregate authentication statuses
- have equivalent meaning at this
- level. If the function returns
- anything other than EFI_SUCCESS,
- the value of AuthenticationStatus
- is undefined.
-
-
- @retval EFI_SUCCESS The InputSection was successfully
- processed and the section contents were
- returned.
-
- @retval EFI_OUT_OF_RESOURCES The system has insufficient
- resources to process the
- request.
-
- @retval EFI_INVALID_PARAMETER The GUID in InputSection does
- not match this instance of the
- GUIDed Section Extraction
- Protocol.
-
-**/
-EFI_STATUS
-EFIAPI
-CustomGuidedSectionExtract (
- IN CONST EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *This,
- IN CONST VOID *InputSection,
- OUT VOID **OutputBuffer,
- OUT UINTN *OutputSize,
- OUT UINT32 *AuthenticationStatus
- );
-
-//
-// Module globals
-//
-LIST_ENTRY mStreamRoot = INITIALIZE_LIST_HEAD_VARIABLE (mStreamRoot);
-
-EFI_HANDLE mSectionExtractionHandle = NULL;
-
-EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL mCustomGuidedSectionExtractionProtocol = {
- CustomGuidedSectionExtract
-};
-
-/**
- Entry point of the section extraction code. Initializes an instance of the
- section extraction interface and installs it on a new handle.
-
- @param ImageHandle A handle for the image that is initializing this driver
- @param SystemTable A pointer to the EFI system table
-
- @retval EFI_SUCCESS Driver initialized successfully
- @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
-
-**/
-EFI_STATUS
-EFIAPI
-InitializeSectionExtraction (
- IN EFI_HANDLE ImageHandle,
- IN EFI_SYSTEM_TABLE *SystemTable
- )
-{
- EFI_STATUS Status;
- EFI_GUID *ExtractHandlerGuidTable;
- UINTN ExtractHandlerNumber;
-
- //
- // Get custom extract guided section method guid list
- //
- ExtractHandlerNumber = ExtractGuidedSectionGetGuidList (&ExtractHandlerGuidTable);
-
- Status = EFI_SUCCESS;
- //
- // Install custom guided extraction protocol
- //
- while (ExtractHandlerNumber-- > 0) {
- Status = CoreInstallProtocolInterface (
- &mSectionExtractionHandle,
- &ExtractHandlerGuidTable[ExtractHandlerNumber],
- EFI_NATIVE_INTERFACE,
- &mCustomGuidedSectionExtractionProtocol
- );
- ASSERT_EFI_ERROR (Status);
- }
-
- return Status;
-}
-
-/**
- Check if a stream is valid.
-
- @param SectionStream The section stream to be checked
- @param SectionStreamLength The length of section stream
-
- @return A boolean value indicating the validness of the section stream.
-
-**/
-BOOLEAN
-IsValidSectionStream (
- IN VOID *SectionStream,
- IN UINTN SectionStreamLength
- )
-{
- UINTN TotalLength;
- UINTN SectionLength;
- EFI_COMMON_SECTION_HEADER *SectionHeader;
- EFI_COMMON_SECTION_HEADER *NextSectionHeader;
-
- TotalLength = 0;
- SectionHeader = (EFI_COMMON_SECTION_HEADER *)SectionStream;
-
- while (TotalLength < SectionStreamLength) {
- if (IS_SECTION2 (SectionHeader)) {
- SectionLength = SECTION2_SIZE (SectionHeader);
- } else {
- SectionLength = SECTION_SIZE (SectionHeader);
- }
-
- TotalLength += SectionLength;
-
- if (TotalLength == SectionStreamLength) {
- return TRUE;
- }
-
- //
- // Move to the next byte following the section...
- //
- SectionHeader = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)SectionHeader + SectionLength);
-
- //
- // Figure out where the next section begins
- //
- NextSectionHeader = ALIGN_POINTER (SectionHeader, 4);
- TotalLength += (UINTN)NextSectionHeader - (UINTN)SectionHeader;
- SectionHeader = NextSectionHeader;
- }
-
- ASSERT (FALSE);
- return FALSE;
-}
-
-/**
- Worker function. Constructor for section streams.
-
- @param SectionStreamLength Size in bytes of the section stream.
- @param SectionStream Buffer containing the new section stream.
- @param AllocateBuffer Indicates whether the stream buffer is to be
- copied or the input buffer is to be used in
- place. AuthenticationStatus- Indicates the
- default authentication status for the new
- stream.
- @param AuthenticationStatus A pointer to a caller-allocated UINT32 that
- indicates the authentication status of the
- output buffer. If the input section's
- GuidedSectionHeader.Attributes field
- has the EFI_GUIDED_SECTION_AUTH_STATUS_VALID
- bit as clear, AuthenticationStatus must return
- zero. Both local bits (19:16) and aggregate
- bits (3:0) in AuthenticationStatus are returned
- by ExtractSection(). These bits reflect the
- status of the extraction operation. The bit
- pattern in both regions must be the same, as
- the local and aggregate authentication statuses
- have equivalent meaning at this level. If the
- function returns anything other than
- EFI_SUCCESS, the value of *AuthenticationStatus
- is undefined.
- @param SectionStreamHandle A pointer to a caller allocated section stream
- handle.
-
- @retval EFI_SUCCESS Stream was added to stream database.
- @retval EFI_OUT_OF_RESOURCES memory allocation failed.
-
-**/
-EFI_STATUS
-OpenSectionStreamEx (
- IN UINTN SectionStreamLength,
- IN VOID *SectionStream,
- IN BOOLEAN AllocateBuffer,
- IN UINT32 AuthenticationStatus,
- OUT UINTN *SectionStreamHandle
- )
-{
- CORE_SECTION_STREAM_NODE *NewStream;
- EFI_TPL OldTpl;
-
- //
- // Allocate a new stream
- //
- NewStream = AllocatePool (sizeof (CORE_SECTION_STREAM_NODE));
- if (NewStream == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- if (AllocateBuffer) {
- //
- // if we're here, we're double buffering, allocate the buffer and copy the
- // data in
- //
- if (SectionStreamLength > 0) {
- NewStream->StreamBuffer = AllocatePool (SectionStreamLength);
- if (NewStream->StreamBuffer == NULL) {
- CoreFreePool (NewStream);
- return EFI_OUT_OF_RESOURCES;
- }
-
- //
- // Copy in stream data
- //
- CopyMem (NewStream->StreamBuffer, SectionStream, SectionStreamLength);
- } else {
- //
- // It's possible to have a zero length section stream.
- //
- NewStream->StreamBuffer = NULL;
- }
- } else {
- //
- // If were here, the caller has supplied the buffer (it's an internal call)
- // so just assign the buffer. This happens when we open section streams
- // as a result of expanding an encapsulating section.
- //
- NewStream->StreamBuffer = SectionStream;
- }
-
- //
- // Initialize the rest of the section stream
- //
- NewStream->Signature = CORE_SECTION_STREAM_SIGNATURE;
- NewStream->StreamHandle = (UINTN)NewStream;
- NewStream->StreamLength = SectionStreamLength;
- InitializeListHead (&NewStream->Children);
- NewStream->AuthenticationStatus = AuthenticationStatus;
-
- //
- // Add new stream to stream list
- //
- OldTpl = CoreRaiseTpl (TPL_NOTIFY);
- InsertTailList (&mStreamRoot, &NewStream->Link);
- CoreRestoreTpl (OldTpl);
-
- *SectionStreamHandle = NewStream->StreamHandle;
-
- return EFI_SUCCESS;
-}
-
-/**
- SEP member function. This function creates and returns a new section stream
- handle to represent the new section stream.
-
- @param SectionStreamLength Size in bytes of the section stream.
- @param SectionStream Buffer containing the new section stream.
- @param SectionStreamHandle A pointer to a caller allocated UINTN that on
- output contains the new section stream handle.
-
- @retval EFI_SUCCESS The section stream is created successfully.
- @retval EFI_OUT_OF_RESOURCES memory allocation failed.
- @retval EFI_INVALID_PARAMETER Section stream does not end concident with end
- of last section.
-
-**/
-EFI_STATUS
-EFIAPI
-OpenSectionStream (
- IN UINTN SectionStreamLength,
- IN VOID *SectionStream,
- OUT UINTN *SectionStreamHandle
- )
-{
- //
- // Check to see section stream looks good...
- //
- if (!IsValidSectionStream (SectionStream, SectionStreamLength)) {
- return EFI_INVALID_PARAMETER;
- }
-
- return OpenSectionStreamEx (
- SectionStreamLength,
- SectionStream,
- FALSE,
- 0,
- SectionStreamHandle
- );
-}
-
-/**
- Worker function. Determine if the input stream:child matches the input type.
-
- @param Stream Indicates the section stream associated with the
- child
- @param Child Indicates the child to check
- @param SearchType Indicates the type of section to check against
- for
- @param SectionDefinitionGuid Indicates the GUID to check against if the type
- is EFI_SECTION_GUID_DEFINED
-
- @retval TRUE The child matches
- @retval FALSE The child doesn't match
-
-**/
-BOOLEAN
-ChildIsType (
- IN CORE_SECTION_STREAM_NODE *Stream,
- IN CORE_SECTION_CHILD_NODE *Child,
- IN EFI_SECTION_TYPE SearchType,
- IN EFI_GUID *SectionDefinitionGuid
- )
-{
- EFI_GUID_DEFINED_SECTION *GuidedSection;
-
- if (SearchType == EFI_SECTION_ALL) {
- return TRUE;
- }
-
- if (Child->Type != SearchType) {
- return FALSE;
- }
-
- if ((SearchType != EFI_SECTION_GUID_DEFINED) || (SectionDefinitionGuid == NULL)) {
- return TRUE;
- }
-
- GuidedSection = (EFI_GUID_DEFINED_SECTION *)(Stream->StreamBuffer + Child->OffsetInStream);
- if (IS_SECTION2 (GuidedSection)) {
- return CompareGuid (&(((EFI_GUID_DEFINED_SECTION2 *)GuidedSection)->SectionDefinitionGuid), SectionDefinitionGuid);
- } else {
- return CompareGuid (&GuidedSection->SectionDefinitionGuid, SectionDefinitionGuid);
- }
-}
-
-/**
- Verify the Guided Section GUID by checking if there is the Guided Section GUID configuration table recorded the GUID itself.
-
- @param GuidedSectionGuid The Guided Section GUID.
- @param GuidedSectionExtraction A pointer to the pointer to the supported Guided Section Extraction Protocol
- for the Guided Section.
-
- @return TRUE The GuidedSectionGuid could be identified, and the pointer to
- the Guided Section Extraction Protocol will be returned to *GuidedSectionExtraction.
- @return FALSE The GuidedSectionGuid could not be identified, or
- the Guided Section Extraction Protocol has not been installed yet.
-
-**/
-BOOLEAN
-VerifyGuidedSectionGuid (
- IN EFI_GUID *GuidedSectionGuid,
- OUT EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL **GuidedSectionExtraction
- )
-{
- EFI_GUID *GuidRecorded;
- VOID *Interface;
- EFI_STATUS Status;
-
- Interface = NULL;
-
- //
- // Check if there is the Guided Section GUID configuration table recorded the GUID itself.
- //
- Status = EfiGetSystemConfigurationTable (GuidedSectionGuid, (VOID **)&GuidRecorded);
- if (Status == EFI_SUCCESS) {
- if (CompareGuid (GuidRecorded, GuidedSectionGuid)) {
- //
- // Found the recorded GuidedSectionGuid.
- //
- Status = CoreLocateProtocol (GuidedSectionGuid, NULL, (VOID **)&Interface);
- if (!EFI_ERROR (Status) && (Interface != NULL)) {
- //
- // Found the supported Guided Section Extraction Porotocol for the Guided Section.
- //
- *GuidedSectionExtraction = (EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *)Interface;
- return TRUE;
- }
-
- return FALSE;
- }
- }
-
- return FALSE;
-}
-
-/**
- RPN callback function. Initializes the section stream
- when GUIDED_SECTION_EXTRACTION_PROTOCOL is installed.
-
- @param Event The event that fired
- @param RpnContext A pointer to the context that allows us to identify
- the relevent encapsulation.
-**/
-VOID
-EFIAPI
-NotifyGuidedExtraction (
- IN EFI_EVENT Event,
- IN VOID *RpnContext
- )
-{
- EFI_STATUS Status;
- EFI_GUID_DEFINED_SECTION *GuidedHeader;
- EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *GuidedExtraction;
- VOID *NewStreamBuffer;
- UINTN NewStreamBufferSize;
- UINT32 AuthenticationStatus;
- RPN_EVENT_CONTEXT *Context;
-
- Context = RpnContext;
-
- GuidedHeader = (EFI_GUID_DEFINED_SECTION *)(Context->ParentStream->StreamBuffer + Context->ChildNode->OffsetInStream);
- ASSERT (GuidedHeader->CommonHeader.Type == EFI_SECTION_GUID_DEFINED);
-
- if (!VerifyGuidedSectionGuid (Context->ChildNode->EncapsulationGuid, &GuidedExtraction)) {
- return;
- }
-
- Status = GuidedExtraction->ExtractSection (
- GuidedExtraction,
- GuidedHeader,
- &NewStreamBuffer,
- &NewStreamBufferSize,
- &AuthenticationStatus
- );
- ASSERT_EFI_ERROR (Status);
-
- //
- // Make sure we initialize the new stream with the correct
- // authentication status for both aggregate and local status fields.
- //
- if ((GuidedHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) != 0) {
- //
- // OR in the parent stream's aggregate status.
- //
- AuthenticationStatus |= Context->ParentStream->AuthenticationStatus & EFI_AUTH_STATUS_ALL;
- } else {
- //
- // since there's no authentication data contributed by the section,
- // just inherit the full value from our immediate parent.
- //
- AuthenticationStatus = Context->ParentStream->AuthenticationStatus;
- }
-
- Status = OpenSectionStreamEx (
- NewStreamBufferSize,
- NewStreamBuffer,
- FALSE,
- AuthenticationStatus,
- &Context->ChildNode->EncapsulatedStreamHandle
- );
- ASSERT_EFI_ERROR (Status);
-
- //
- // Close the event when done.
- //
- gBS->CloseEvent (Event);
- Context->ChildNode->Event = NULL;
- FreePool (Context);
-}
-
-/**
- Constructor for RPN event when a missing GUIDED_SECTION_EXTRACTION_PROTOCOL appears...
-
- @param ParentStream Indicates the parent of the ecnapsulation section (child)
- @param ChildNode Indicates the child node that is the encapsulation section.
-
-**/
-VOID
-CreateGuidedExtractionRpnEvent (
- IN CORE_SECTION_STREAM_NODE *ParentStream,
- IN CORE_SECTION_CHILD_NODE *ChildNode
- )
-{
- RPN_EVENT_CONTEXT *Context;
-
- //
- // Allocate new event structure and context
- //
- Context = AllocatePool (sizeof (RPN_EVENT_CONTEXT));
- ASSERT (Context != NULL);
-
- Context->ChildNode = ChildNode;
- Context->ParentStream = ParentStream;
-
- Context->ChildNode->Event = EfiCreateProtocolNotifyEvent (
- Context->ChildNode->EncapsulationGuid,
- TPL_NOTIFY,
- NotifyGuidedExtraction,
- Context,
- &Context->Registration
- );
-}
-
-/**
- Worker function. Constructor for new child nodes.
-
- @param Stream Indicates the section stream in which to add the
- child.
- @param ChildOffset Indicates the offset in Stream that is the
- beginning of the child section.
- @param ChildNode Indicates the Callee allocated and initialized
- child.
-
- @retval EFI_SUCCESS Child node was found and returned.
- EFI_OUT_OF_RESOURCES- Memory allocation failed.
- @retval EFI_PROTOCOL_ERROR Encapsulation sections produce new stream
- handles when the child node is created. If the
- section type is GUID defined, and the extraction
- GUID does not exist, and producing the stream
- requires the GUID, then a protocol error is
- generated and no child is produced. Values
- returned by OpenSectionStreamEx.
-
-**/
-EFI_STATUS
-CreateChildNode (
- IN CORE_SECTION_STREAM_NODE *Stream,
- IN UINT32 ChildOffset,
- OUT CORE_SECTION_CHILD_NODE **ChildNode
- )
-{
- EFI_STATUS Status;
- EFI_COMMON_SECTION_HEADER *SectionHeader;
- EFI_COMPRESSION_SECTION *CompressionHeader;
- EFI_GUID_DEFINED_SECTION *GuidedHeader;
- EFI_DECOMPRESS_PROTOCOL *Decompress;
- EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *GuidedExtraction;
- VOID *NewStreamBuffer;
- VOID *ScratchBuffer;
- UINT32 ScratchSize;
- UINTN NewStreamBufferSize;
- UINT32 AuthenticationStatus;
- VOID *CompressionSource;
- UINT32 CompressionSourceSize;
- UINT32 UncompressedLength;
- UINT8 CompressionType;
- UINT16 GuidedSectionAttributes;
-
- CORE_SECTION_CHILD_NODE *Node;
-
- SectionHeader = (EFI_COMMON_SECTION_HEADER *)(Stream->StreamBuffer + ChildOffset);
-
- //
- // Allocate a new node
- //
- *ChildNode = AllocateZeroPool (sizeof (CORE_SECTION_CHILD_NODE));
- Node = *ChildNode;
- if (Node == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
-
- //
- // Now initialize it
- //
- Node->Signature = CORE_SECTION_CHILD_SIGNATURE;
- Node->Type = SectionHeader->Type;
- if (IS_SECTION2 (SectionHeader)) {
- Node->Size = SECTION2_SIZE (SectionHeader);
- } else {
- Node->Size = SECTION_SIZE (SectionHeader);
- }
-
- Node->OffsetInStream = ChildOffset;
- Node->EncapsulatedStreamHandle = NULL_STREAM_HANDLE;
- Node->EncapsulationGuid = NULL;
-
- //
- // If it's an encapsulating section, then create the new section stream also
- //
- switch (Node->Type) {
- case EFI_SECTION_COMPRESSION:
- //
- // Get the CompressionSectionHeader
- //
- if (Node->Size < sizeof (EFI_COMPRESSION_SECTION)) {
- CoreFreePool (Node);
- return EFI_NOT_FOUND;
- }
-
- CompressionHeader = (EFI_COMPRESSION_SECTION *)SectionHeader;
-
- if (IS_SECTION2 (CompressionHeader)) {
- CompressionSource = (VOID *)((UINT8 *)CompressionHeader + sizeof (EFI_COMPRESSION_SECTION2));
- CompressionSourceSize = (UINT32)(SECTION2_SIZE (CompressionHeader) - sizeof (EFI_COMPRESSION_SECTION2));
- UncompressedLength = ((EFI_COMPRESSION_SECTION2 *)CompressionHeader)->UncompressedLength;
- CompressionType = ((EFI_COMPRESSION_SECTION2 *)CompressionHeader)->CompressionType;
- } else {
- CompressionSource = (VOID *)((UINT8 *)CompressionHeader + sizeof (EFI_COMPRESSION_SECTION));
- CompressionSourceSize = (UINT32)(SECTION_SIZE (CompressionHeader) - sizeof (EFI_COMPRESSION_SECTION));
- UncompressedLength = CompressionHeader->UncompressedLength;
- CompressionType = CompressionHeader->CompressionType;
- }
-
- //
- // Allocate space for the new stream
- //
- if (UncompressedLength > 0) {
- NewStreamBufferSize = UncompressedLength;
- NewStreamBuffer = AllocatePool (NewStreamBufferSize);
- if (NewStreamBuffer == NULL) {
- CoreFreePool (Node);
- return EFI_OUT_OF_RESOURCES;
- }
-
- if (CompressionType == EFI_NOT_COMPRESSED) {
- //
- // stream is not actually compressed, just encapsulated. So just copy it.
- //
- CopyMem (NewStreamBuffer, CompressionSource, NewStreamBufferSize);
- } else if (CompressionType == EFI_STANDARD_COMPRESSION) {
- //
- // Only support the EFI_SATNDARD_COMPRESSION algorithm.
- //
-
- //
- // Decompress the stream
- //
- Status = CoreLocateProtocol (&gEfiDecompressProtocolGuid, NULL, (VOID **)&Decompress);
- ASSERT_EFI_ERROR (Status);
- ASSERT (Decompress != NULL);
-
- Status = Decompress->GetInfo (
- Decompress,
- CompressionSource,
- CompressionSourceSize,
- (UINT32 *)&NewStreamBufferSize,
- &ScratchSize
- );
- if (EFI_ERROR (Status) || (NewStreamBufferSize != UncompressedLength)) {
- CoreFreePool (Node);
- CoreFreePool (NewStreamBuffer);
- if (!EFI_ERROR (Status)) {
- Status = EFI_BAD_BUFFER_SIZE;
- }
-
- return Status;
- }
-
- ScratchBuffer = AllocatePool (ScratchSize);
- if (ScratchBuffer == NULL) {
- CoreFreePool (Node);
- CoreFreePool (NewStreamBuffer);
- return EFI_OUT_OF_RESOURCES;
- }
-
- Status = Decompress->Decompress (
- Decompress,
- CompressionSource,
- CompressionSourceSize,
- NewStreamBuffer,
- (UINT32)NewStreamBufferSize,
- ScratchBuffer,
- ScratchSize
- );
- CoreFreePool (ScratchBuffer);
- if (EFI_ERROR (Status)) {
- CoreFreePool (Node);
- CoreFreePool (NewStreamBuffer);
- return Status;
- }
- }
- } else {
- NewStreamBuffer = NULL;
- NewStreamBufferSize = 0;
- }
-
- Status = OpenSectionStreamEx (
- NewStreamBufferSize,
- NewStreamBuffer,
- FALSE,
- Stream->AuthenticationStatus,
- &Node->EncapsulatedStreamHandle
- );
- if (EFI_ERROR (Status)) {
- CoreFreePool (Node);
- CoreFreePool (NewStreamBuffer);
- return Status;
- }
-
- break;
-
- case EFI_SECTION_GUID_DEFINED:
- GuidedHeader = (EFI_GUID_DEFINED_SECTION *)SectionHeader;
- if (IS_SECTION2 (GuidedHeader)) {
- Node->EncapsulationGuid = &(((EFI_GUID_DEFINED_SECTION2 *)GuidedHeader)->SectionDefinitionGuid);
- GuidedSectionAttributes = ((EFI_GUID_DEFINED_SECTION2 *)GuidedHeader)->Attributes;
- } else {
- Node->EncapsulationGuid = &GuidedHeader->SectionDefinitionGuid;
- GuidedSectionAttributes = GuidedHeader->Attributes;
- }
-
- if (VerifyGuidedSectionGuid (Node->EncapsulationGuid, &GuidedExtraction)) {
- //
- // NewStreamBuffer is always allocated by ExtractSection... No caller
- // allocation here.
- //
- Status = GuidedExtraction->ExtractSection (
- GuidedExtraction,
- GuidedHeader,
- &NewStreamBuffer,
- &NewStreamBufferSize,
- &AuthenticationStatus
- );
- if (EFI_ERROR (Status)) {
- CoreFreePool (*ChildNode);
- return EFI_PROTOCOL_ERROR;
- }
-
- //
- // Make sure we initialize the new stream with the correct
- // authentication status for both aggregate and local status fields.
- //
- if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) != 0) {
- //
- // OR in the parent stream's aggregate status.
- //
- AuthenticationStatus |= Stream->AuthenticationStatus & EFI_AUTH_STATUS_ALL;
- } else {
- //
- // since there's no authentication data contributed by the section,
- // just inherit the full value from our immediate parent.
- //
- AuthenticationStatus = Stream->AuthenticationStatus;
- }
-
- Status = OpenSectionStreamEx (
- NewStreamBufferSize,
- NewStreamBuffer,
- FALSE,
- AuthenticationStatus,
- &Node->EncapsulatedStreamHandle
- );
- if (EFI_ERROR (Status)) {
- CoreFreePool (*ChildNode);
- CoreFreePool (NewStreamBuffer);
- return Status;
- }
- } else {
- //
- // There's no GUIDed section extraction protocol available.
- //
- if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) != 0) {
- //
- // If the section REQUIRES an extraction protocol, register for RPN
- // when the required GUIDed extraction protocol becomes available.
- //
- CreateGuidedExtractionRpnEvent (Stream, Node);
- } else {
- //
- // Figure out the proper authentication status
- //
- AuthenticationStatus = Stream->AuthenticationStatus;
-
- if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) == EFI_GUIDED_SECTION_AUTH_STATUS_VALID) {
- AuthenticationStatus |= EFI_AUTH_STATUS_IMAGE_SIGNED | EFI_AUTH_STATUS_NOT_TESTED;
- }
-
- if (IS_SECTION2 (GuidedHeader)) {
- Status = OpenSectionStreamEx (
- SECTION2_SIZE (GuidedHeader) - ((EFI_GUID_DEFINED_SECTION2 *)GuidedHeader)->DataOffset,
- (UINT8 *)GuidedHeader + ((EFI_GUID_DEFINED_SECTION2 *)GuidedHeader)->DataOffset,
- TRUE,
- AuthenticationStatus,
- &Node->EncapsulatedStreamHandle
- );
- } else {
- Status = OpenSectionStreamEx (
- SECTION_SIZE (GuidedHeader) - ((EFI_GUID_DEFINED_SECTION *)GuidedHeader)->DataOffset,
- (UINT8 *)GuidedHeader + ((EFI_GUID_DEFINED_SECTION *)GuidedHeader)->DataOffset,
- TRUE,
- AuthenticationStatus,
- &Node->EncapsulatedStreamHandle
- );
- }
-
- if (EFI_ERROR (Status)) {
- CoreFreePool (Node);
- return Status;
- }
- }
- }
-
- break;
-
- default:
-
- //
- // Nothing to do if it's a leaf
- //
- break;
- }
-
- //
- // Last, add the new child node to the stream
- //
- InsertTailList (&Stream->Children, &Node->Link);
-
- return EFI_SUCCESS;
-}
-
-/**
- Worker function Recursively searches / builds section stream database
- looking for requested section.
-
- @param SourceStream Indicates the section stream in which to do the
- search.
- @param SearchType Indicates the type of section to search for.
- @param SectionInstance Indicates which instance of section to find.
- This is an in/out parameter and it is 1-based,
- to deal with recursions.
- @param SectionDefinitionGuid Guid of section definition
- @param Depth Nesting depth of encapsulation sections.
- Callers different from FindChildNode() are
- responsible for passing in a zero Depth.
- @param FoundChild Output indicating the child node that is found.
- @param FoundStream Output indicating which section stream the child
- was found in. If this stream was generated as a
- result of an encapsulation section, the
- streamhandle is visible within the SEP driver
- only.
- @param AuthenticationStatus Indicates the authentication status of the found section.
-
- @retval EFI_SUCCESS Child node was found and returned.
- EFI_OUT_OF_RESOURCES- Memory allocation failed.
- @retval EFI_NOT_FOUND Requested child node does not exist.
- @retval EFI_PROTOCOL_ERROR a required GUIDED section extraction protocol
- does not exist
- @retval EFI_ABORTED Recursion aborted because Depth has been
- greater than or equal to
- PcdFwVolDxeMaxEncapsulationDepth.
-
-**/
-EFI_STATUS
-FindChildNode (
- IN CORE_SECTION_STREAM_NODE *SourceStream,
- IN EFI_SECTION_TYPE SearchType,
- IN OUT UINTN *SectionInstance,
- IN EFI_GUID *SectionDefinitionGuid,
- IN UINT32 Depth,
- OUT CORE_SECTION_CHILD_NODE **FoundChild,
- OUT CORE_SECTION_STREAM_NODE **FoundStream,
- OUT UINT32 *AuthenticationStatus
- )
-{
- CORE_SECTION_CHILD_NODE *CurrentChildNode;
- CORE_SECTION_CHILD_NODE *RecursedChildNode;
- CORE_SECTION_STREAM_NODE *RecursedFoundStream;
- UINT32 NextChildOffset;
- EFI_STATUS ErrorStatus;
- EFI_STATUS Status;
-
- ASSERT (*SectionInstance > 0);
-
- if (Depth >= PcdGet32 (PcdFwVolDxeMaxEncapsulationDepth)) {
- return EFI_ABORTED;
- }
-
- CurrentChildNode = NULL;
- ErrorStatus = EFI_NOT_FOUND;
-
- if (SourceStream->StreamLength == 0) {
- return EFI_NOT_FOUND;
- }
-
- if (IsListEmpty (&SourceStream->Children) &&
- (SourceStream->StreamLength >= sizeof (EFI_COMMON_SECTION_HEADER)))
- {
- //
- // This occurs when a section stream exists, but no child sections
- // have been parsed out yet. Therefore, extract the first child and add it
- // to the list of children so we can get started.
- // Section stream may contain an array of zero or more bytes.
- // So, its size should be >= the size of commen section header.
- //
- Status = CreateChildNode (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 we run
- // out of data
- //
- CurrentChildNode = CHILD_SECTION_NODE_FROM_LINK (GetFirstNode (&SourceStream->Children));
-
- for ( ; ;) {
- ASSERT (CurrentChildNode != NULL);
- if (ChildIsType (SourceStream, CurrentChildNode, SearchType, SectionDefinitionGuid)) {
- //
- // The type matches, so check the instance count to see if it's the one we want
- //
- (*SectionInstance)--;
- if (*SectionInstance == 0) {
- //
- // Got it!
- //
- *FoundChild = CurrentChildNode;
- *FoundStream = SourceStream;
- *AuthenticationStatus = SourceStream->AuthenticationStatus;
- return EFI_SUCCESS;
- }
- }
-
- //
- // Type mismatch, or we haven't found the desired instance yet.
- //
- ASSERT (*SectionInstance > 0);
-
- if (CurrentChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) {
- //
- // If the current node is an encapsulating node, recurse into it...
- //
- Status = FindChildNode (
- (CORE_SECTION_STREAM_NODE *)CurrentChildNode->EncapsulatedStreamHandle,
- SearchType,
- SectionInstance,
- SectionDefinitionGuid,
- Depth + 1,
- &RecursedChildNode,
- &RecursedFoundStream,
- AuthenticationStatus
- );
- if (*SectionInstance == 0) {
- //
- // The recursive FindChildNode() call decreased (*SectionInstance) to
- // zero.
- //
- ASSERT_EFI_ERROR (Status);
- *FoundChild = RecursedChildNode;
- *FoundStream = RecursedFoundStream;
- return EFI_SUCCESS;
- } else {
- if (Status == EFI_ABORTED) {
- //
- // If the recursive call was aborted due to nesting depth, stop
- // looking for the requested child node. The skipped subtree could
- // throw off the instance counting.
- //
- return Status;
- }
-
- //
- // Save the error code and continue to find the requested child node in
- // the rest of the stream.
- //
- ErrorStatus = Status;
- }
- } else if ((CurrentChildNode->Type == EFI_SECTION_GUID_DEFINED) && (SearchType != EFI_SECTION_GUID_DEFINED)) {
- //
- // When Node Type is GUIDED section, but Node has no encapsulated data, Node data should not be parsed
- // because a required GUIDED section extraction protocol does not exist.
- // If SearchType is not GUIDED section, EFI_PROTOCOL_ERROR should return.
- //
- ErrorStatus = EFI_PROTOCOL_ERROR;
- }
-
- if (!IsNodeAtEnd (&SourceStream->Children, &CurrentChildNode->Link)) {
- //
- // We haven't found the child node we're interested in yet, but there's
- // still more nodes that have already been parsed so get the next one
- // and continue searching..
- //
- CurrentChildNode = CHILD_SECTION_NODE_FROM_LINK (GetNextNode (&SourceStream->Children, &CurrentChildNode->Link));
- } else {
- //
- // We've exhausted children that have already been parsed, so see if
- // there's any more data and continue parsing out more children if there
- // is.
- //
- NextChildOffset = CurrentChildNode->OffsetInStream + CurrentChildNode->Size;
- //
- // Round up to 4 byte boundary
- //
- NextChildOffset += 3;
- NextChildOffset &= ~(UINTN)3;
- if (NextChildOffset <= SourceStream->StreamLength - sizeof (EFI_COMMON_SECTION_HEADER)) {
- //
- // There's an unparsed child remaining in the stream, so create a new child node
- //
- Status = CreateChildNode (SourceStream, NextChildOffset, &CurrentChildNode);
- if (EFI_ERROR (Status)) {
- return Status;
- }
- } else {
- ASSERT (EFI_ERROR (ErrorStatus));
- return ErrorStatus;
- }
- }
- }
-}
-
-/**
- Worker function. Search stream database for requested stream handle.
-
- @param SearchHandle Indicates which stream to look for.
- @param FoundStream Output pointer to the found stream.
-
- @retval EFI_SUCCESS StreamHandle was found and *FoundStream contains
- the stream node.
- @retval EFI_NOT_FOUND SearchHandle was not found in the stream
- database.
-
-**/
-EFI_STATUS
-FindStreamNode (
- IN UINTN SearchHandle,
- OUT CORE_SECTION_STREAM_NODE **FoundStream
- )
-{
- CORE_SECTION_STREAM_NODE *StreamNode;
-
- if (!IsListEmpty (&mStreamRoot)) {
- StreamNode = STREAM_NODE_FROM_LINK (GetFirstNode (&mStreamRoot));
- for ( ; ;) {
- if (StreamNode->StreamHandle == SearchHandle) {
- *FoundStream = StreamNode;
- return EFI_SUCCESS;
- } else if (IsNodeAtEnd (&mStreamRoot, &StreamNode->Link)) {
- break;
- } else {
- StreamNode = STREAM_NODE_FROM_LINK (GetNextNode (&mStreamRoot, &StreamNode->Link));
- }
- }
- }
-
- return EFI_NOT_FOUND;
-}
-
-/**
- SEP member function. Retrieves requested section from section stream.
-
- @param SectionStreamHandle The section stream from which to extract the
- requested section.
- @param SectionType A pointer to the type of section to search for.
- @param SectionDefinitionGuid If the section type is EFI_SECTION_GUID_DEFINED,
- then SectionDefinitionGuid indicates which of
- these types of sections to search for.
- @param SectionInstance Indicates which instance of the requested
- section to return.
- @param Buffer Double indirection to buffer. If *Buffer is
- non-null on input, then the buffer is caller
- allocated. If Buffer is NULL, then the buffer
- is callee allocated. In either case, the
- required buffer size is returned in *BufferSize.
- @param BufferSize On input, indicates the size of *Buffer if
- *Buffer is non-null on input. On output,
- indicates the required size (allocated size if
- callee allocated) of *Buffer.
- @param AuthenticationStatus A pointer to a caller-allocated UINT32 that
- indicates the authentication status of the
- output buffer. If the input section's
- GuidedSectionHeader.Attributes field
- has the EFI_GUIDED_SECTION_AUTH_STATUS_VALID
- bit as clear, AuthenticationStatus must return
- zero. Both local bits (19:16) and aggregate
- bits (3:0) in AuthenticationStatus are returned
- by ExtractSection(). These bits reflect the
- status of the extraction operation. The bit
- pattern in both regions must be the same, as
- the local and aggregate authentication statuses
- have equivalent meaning at this level. If the
- function returns anything other than
- EFI_SUCCESS, the value of *AuthenticationStatus
- is undefined.
- @param IsFfs3Fv Indicates the FV format.
-
- @retval EFI_SUCCESS Section was retrieved successfully
- @retval EFI_PROTOCOL_ERROR A GUID defined section was encountered in the
- section stream with its
- EFI_GUIDED_SECTION_PROCESSING_REQUIRED bit set,
- but there was no corresponding GUIDed Section
- Extraction Protocol in the handle database.
- *Buffer is unmodified.
- @retval EFI_NOT_FOUND An error was encountered when parsing the
- SectionStream. This indicates the SectionStream
- is not correctly formatted.
- @retval EFI_NOT_FOUND The requested section does not exist.
- @retval EFI_OUT_OF_RESOURCES The system has insufficient resources to process
- the request.
- @retval EFI_INVALID_PARAMETER The SectionStreamHandle does not exist.
- @retval EFI_WARN_TOO_SMALL The size of the caller allocated input buffer is
- insufficient to contain the requested section.
- The input buffer is filled and section contents
- are truncated.
-
-**/
-EFI_STATUS
-EFIAPI
-GetSection (
- IN UINTN SectionStreamHandle,
- IN EFI_SECTION_TYPE *SectionType,
- IN EFI_GUID *SectionDefinitionGuid,
- IN UINTN SectionInstance,
- IN VOID **Buffer,
- IN OUT UINTN *BufferSize,
- OUT UINT32 *AuthenticationStatus,
- IN BOOLEAN IsFfs3Fv
- )
-{
- CORE_SECTION_STREAM_NODE *StreamNode;
- EFI_TPL OldTpl;
- EFI_STATUS Status;
- CORE_SECTION_CHILD_NODE *ChildNode;
- CORE_SECTION_STREAM_NODE *ChildStreamNode;
- UINTN CopySize;
- UINT32 ExtractedAuthenticationStatus;
- UINTN Instance;
- UINT8 *CopyBuffer;
- UINTN SectionSize;
- EFI_COMMON_SECTION_HEADER *Section;
-
- ChildStreamNode = NULL;
- OldTpl = CoreRaiseTpl (TPL_NOTIFY);
- Instance = SectionInstance + 1;
-
- //
- // Locate target stream
- //
- Status = FindStreamNode (SectionStreamHandle, &StreamNode);
- if (EFI_ERROR (Status)) {
- Status = EFI_INVALID_PARAMETER;
- goto GetSection_Done;
- }
-
- //
- // Found the stream, now locate and return the appropriate section
- //
- if (SectionType == NULL) {
- //
- // SectionType == NULL means return the WHOLE section stream...
- //
- CopySize = StreamNode->StreamLength;
- CopyBuffer = StreamNode->StreamBuffer;
- *AuthenticationStatus = StreamNode->AuthenticationStatus;
- } else {
- //
- // There's a requested section type, so go find it and return it...
- //
- Status = FindChildNode (
- StreamNode,
- *SectionType,
- &Instance,
- SectionDefinitionGuid,
- 0, // encapsulation depth
- &ChildNode,
- &ChildStreamNode,
- &ExtractedAuthenticationStatus
- );
- if (EFI_ERROR (Status)) {
- if (Status == EFI_ABORTED) {
- DEBUG ((
- DEBUG_ERROR,
- "%a: recursion aborted due to nesting depth\n",
- __func__
- ));
- //
- // Map "aborted" to "not found".
- //
- Status = EFI_NOT_FOUND;
- }
-
- goto GetSection_Done;
- }
-
- Section = (EFI_COMMON_SECTION_HEADER *)(ChildStreamNode->StreamBuffer + ChildNode->OffsetInStream);
-
- if (IS_SECTION2 (Section)) {
- ASSERT (SECTION2_SIZE (Section) > 0x00FFFFFF);
- if (!IsFfs3Fv) {
- DEBUG ((DEBUG_ERROR, "It is a FFS3 formatted section in a non-FFS3 formatted FV.\n"));
- Status = EFI_NOT_FOUND;
- goto GetSection_Done;
- }
-
- CopySize = SECTION2_SIZE (Section) - sizeof (EFI_COMMON_SECTION_HEADER2);
- CopyBuffer = (UINT8 *)Section + sizeof (EFI_COMMON_SECTION_HEADER2);
- } else {
- CopySize = SECTION_SIZE (Section) - sizeof (EFI_COMMON_SECTION_HEADER);
- CopyBuffer = (UINT8 *)Section + sizeof (EFI_COMMON_SECTION_HEADER);
- }
-
- *AuthenticationStatus = ExtractedAuthenticationStatus;
- }
-
- SectionSize = CopySize;
- if (*Buffer != NULL) {
- //
- // Caller allocated buffer. Fill to size and return required size...
- //
- if (*BufferSize < CopySize) {
- Status = EFI_WARN_BUFFER_TOO_SMALL;
- CopySize = *BufferSize;
- }
- } else {
- //
- // Callee allocated buffer. Allocate buffer and return size.
- //
- *Buffer = AllocatePool (CopySize);
- if (*Buffer == NULL) {
- Status = EFI_OUT_OF_RESOURCES;
- goto GetSection_Done;
- }
- }
-
- CopyMem (*Buffer, CopyBuffer, CopySize);
- *BufferSize = SectionSize;
-
-GetSection_Done:
- CoreRestoreTpl (OldTpl);
-
- return Status;
-}
-
-/**
- Worker function. Destructor for child nodes.
-
- @param ChildNode Indicates the node to destroy
-
-**/
-VOID
-FreeChildNode (
- IN CORE_SECTION_CHILD_NODE *ChildNode
- )
-{
- ASSERT (ChildNode->Signature == CORE_SECTION_CHILD_SIGNATURE);
- //
- // Remove the child from it's list
- //
- RemoveEntryList (&ChildNode->Link);
-
- if (ChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) {
- //
- // If it's an encapsulating section, we close the resulting section stream.
- // CloseSectionStream will free all memory associated with the stream.
- //
- CloseSectionStream (ChildNode->EncapsulatedStreamHandle, TRUE);
- }
-
- if (ChildNode->Event != NULL) {
- gBS->CloseEvent (ChildNode->Event);
- }
-
- //
- // Last, free the child node itself
- //
- CoreFreePool (ChildNode);
-}
-
-/**
- SEP member function. Deletes an existing section stream
-
- @param StreamHandleToClose Indicates the stream to close
- @param FreeStreamBuffer TRUE - Need to free stream buffer;
- FALSE - No need to free stream buffer.
-
- @retval EFI_SUCCESS The section stream is closed sucessfully.
- @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
- @retval EFI_INVALID_PARAMETER Section stream does not end concident with end
- of last section.
-
-**/
-EFI_STATUS
-EFIAPI
-CloseSectionStream (
- IN UINTN StreamHandleToClose,
- IN BOOLEAN FreeStreamBuffer
- )
-{
- CORE_SECTION_STREAM_NODE *StreamNode;
- EFI_TPL OldTpl;
- EFI_STATUS Status;
- LIST_ENTRY *Link;
- CORE_SECTION_CHILD_NODE *ChildNode;
-
- OldTpl = CoreRaiseTpl (TPL_NOTIFY);
-
- //
- // Locate target stream
- //
- Status = FindStreamNode (StreamHandleToClose, &StreamNode);
- if (!EFI_ERROR (Status)) {
- //
- // Found the stream, so close it
- //
- RemoveEntryList (&StreamNode->Link);
- while (!IsListEmpty (&StreamNode->Children)) {
- Link = GetFirstNode (&StreamNode->Children);
- ChildNode = CHILD_SECTION_NODE_FROM_LINK (Link);
- FreeChildNode (ChildNode);
- }
-
- if (FreeStreamBuffer) {
- CoreFreePool (StreamNode->StreamBuffer);
- }
-
- CoreFreePool (StreamNode);
- Status = EFI_SUCCESS;
- } else {
- Status = EFI_INVALID_PARAMETER;
- }
-
- CoreRestoreTpl (OldTpl);
- return Status;
-}
-
-/**
- The ExtractSection() function processes the input section and
- allocates a buffer from the pool in which it returns the section
- contents. If the section being extracted contains
- authentication information (the section's
- GuidedSectionHeader.Attributes field has the
- EFI_GUIDED_SECTION_AUTH_STATUS_VALID bit set), the values
- returned in AuthenticationStatus must reflect the results of
- the authentication operation. Depending on the algorithm and
- size of the encapsulated data, the time that is required to do
- a full authentication may be prohibitively long for some
- classes of systems. To indicate this, use
- EFI_SECURITY_POLICY_PROTOCOL_GUID, which may be published by
- the security policy driver (see the Platform Initialization
- Driver Execution Environment Core Interface Specification for
- more details and the GUID definition). If the
- EFI_SECURITY_POLICY_PROTOCOL_GUID exists in the handle
- database, then, if possible, full authentication should be
- skipped and the section contents simply returned in the
- OutputBuffer. In this case, the
- EFI_AUTH_STATUS_PLATFORM_OVERRIDE bit AuthenticationStatus
- must be set on return. ExtractSection() is callable only from
- TPL_NOTIFY and below. Behavior of ExtractSection() at any
- EFI_TPL above TPL_NOTIFY is undefined. Type EFI_TPL is
- defined in RaiseTPL() in the UEFI 2.0 specification.
-
-
- @param This Indicates the
- EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL instance.
- @param InputSection Buffer containing the input GUIDed section
- to be processed. OutputBuffer OutputBuffer
- is allocated from boot services pool
- memory and contains the new section
- stream. The caller is responsible for
- freeing this buffer.
- @param OutputBuffer *OutputBuffer is allocated from boot services
- pool memory and contains the new section stream.
- The caller is responsible for freeing this buffer.
- @param OutputSize A pointer to a caller-allocated UINTN in
- which the size of OutputBuffer allocation
- is stored. If the function returns
- anything other than EFI_SUCCESS, the value
- of OutputSize is undefined.
-
- @param AuthenticationStatus A pointer to a caller-allocated
- UINT32 that indicates the
- authentication status of the
- output buffer. If the input
- section's
- GuidedSectionHeader.Attributes
- field has the
- EFI_GUIDED_SECTION_AUTH_STATUS_VAL
- bit as clear, AuthenticationStatus
- must return zero. Both local bits
- (19:16) and aggregate bits (3:0)
- in AuthenticationStatus are
- returned by ExtractSection().
- These bits reflect the status of
- the extraction operation. The bit
- pattern in both regions must be
- the same, as the local and
- aggregate authentication statuses
- have equivalent meaning at this
- level. If the function returns
- anything other than EFI_SUCCESS,
- the value of AuthenticationStatus
- is undefined.
-
-
- @retval EFI_SUCCESS The InputSection was successfully
- processed and the section contents were
- returned.
-
- @retval EFI_OUT_OF_RESOURCES The system has insufficient
- resources to process the
- request.
-
- @retval EFI_INVALID_PARAMETER The GUID in InputSection does
- not match this instance of the
- GUIDed Section Extraction
- Protocol.
-
-**/
-EFI_STATUS
-EFIAPI
-CustomGuidedSectionExtract (
- IN CONST EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *This,
- IN CONST VOID *InputSection,
- OUT VOID **OutputBuffer,
- OUT UINTN *OutputSize,
- OUT UINT32 *AuthenticationStatus
- )
-{
- EFI_STATUS Status;
- VOID *ScratchBuffer;
- VOID *AllocatedOutputBuffer;
- UINT32 OutputBufferSize;
- UINT32 ScratchBufferSize;
- UINT16 SectionAttribute;
-
- //
- // Init local variable
- //
- ScratchBuffer = NULL;
- AllocatedOutputBuffer = NULL;
-
- //
- // Call GetInfo to get the size and attribute of input guided section data.
- //
- Status = ExtractGuidedSectionGetInfo (
- InputSection,
- &OutputBufferSize,
- &ScratchBufferSize,
- &SectionAttribute
- );
-
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_ERROR, "GetInfo from guided section Failed - %r\n", Status));
- return Status;
- }
-
- if (ScratchBufferSize > 0) {
- //
- // Allocate scratch buffer
- //
- ScratchBuffer = AllocatePool (ScratchBufferSize);
- if (ScratchBuffer == NULL) {
- return EFI_OUT_OF_RESOURCES;
- }
- }
-
- if (OutputBufferSize > 0) {
- //
- // Allocate output buffer
- //
- AllocatedOutputBuffer = AllocatePool (OutputBufferSize);
- if (AllocatedOutputBuffer == NULL) {
- if (ScratchBuffer != NULL) {
- FreePool (ScratchBuffer);
- }
-
- return EFI_OUT_OF_RESOURCES;
- }
-
- *OutputBuffer = AllocatedOutputBuffer;
- }
-
- //
- // Call decode function to extract raw data from the guided section.
- //
- Status = ExtractGuidedSectionDecode (
- InputSection,
- OutputBuffer,
- ScratchBuffer,
- AuthenticationStatus
- );
- if (EFI_ERROR (Status)) {
- //
- // Decode failed
- //
- if (AllocatedOutputBuffer != NULL) {
- CoreFreePool (AllocatedOutputBuffer);
- }
-
- if (ScratchBuffer != NULL) {
- CoreFreePool (ScratchBuffer);
- }
-
- DEBUG ((DEBUG_ERROR, "Extract guided section Failed - %r\n", Status));
- return Status;
- }
-
- if (*OutputBuffer != AllocatedOutputBuffer) {
- //
- // OutputBuffer was returned as a different value,
- // so copy section contents to the allocated memory buffer.
- //
- CopyMem (AllocatedOutputBuffer, *OutputBuffer, OutputBufferSize);
- *OutputBuffer = AllocatedOutputBuffer;
- }
-
- //
- // Set real size of output buffer.
- //
- *OutputSize = (UINTN)OutputBufferSize;
-
- //
- // Free unused scratch buffer.
- //
- if (ScratchBuffer != NULL) {
- CoreFreePool (ScratchBuffer);
- }
-
- return EFI_SUCCESS;
-}
+/** @file + Section Extraction Protocol implementation. + + Stream database is implemented as a linked list of section streams, + where each stream contains a linked list of children, which may be leaves or + encapsulations. + + Children that are encapsulations generate new stream entries + when they are created. Streams can also be created by calls to + SEP->OpenSectionStream(). + + The database is only created far enough to return the requested data from + any given stream, or to determine that the requested data is not found. + + If a GUIDed encapsulation is encountered, there are three possiblilites. + + 1) A support protocol is found, in which the stream is simply processed with + the support protocol. + + 2) A support protocol is not found, but the data is available to be read + without processing. In this case, the database is built up through the + recursions to return the data, and a RPN event is set that will enable + the stream in question to be refreshed if and when the required section + extraction protocol is published.This insures the AuthenticationStatus + does not become stale in the cache. + + 3) A support protocol is not found, and the data is not available to be read + without it. This results in EFI_PROTOCOL_ERROR. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "DxeMain.h" + +// +// Local defines and typedefs +// +#define CORE_SECTION_CHILD_SIGNATURE SIGNATURE_32('S','X','C','S') +#define CHILD_SECTION_NODE_FROM_LINK(Node) \ + CR (Node, CORE_SECTION_CHILD_NODE, Link, CORE_SECTION_CHILD_SIGNATURE) + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + UINT32 Type; + UINT32 Size; + // + // StreamBase + OffsetInStream == pointer to section header in stream. The + // stream base is always known when walking the sections within. + // + UINT32 OffsetInStream; + // + // Then EncapsulatedStreamHandle below is always 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 the + // encapsulated stream is processed further. + // + UINTN EncapsulatedStreamHandle; + EFI_GUID *EncapsulationGuid; + // + // If the section REQUIRES an extraction protocol, register for RPN + // when the required GUIDed extraction protocol becomes available. + // + EFI_EVENT Event; +} CORE_SECTION_CHILD_NODE; + +#define CORE_SECTION_STREAM_SIGNATURE SIGNATURE_32('S','X','S','S') +#define STREAM_NODE_FROM_LINK(Node) \ + CR (Node, CORE_SECTION_STREAM_NODE, Link, CORE_SECTION_STREAM_SIGNATURE) + +typedef struct { + UINT32 Signature; + LIST_ENTRY Link; + UINTN StreamHandle; + UINT8 *StreamBuffer; + UINTN StreamLength; + LIST_ENTRY Children; + // + // Authentication status is from GUIDed encapsulations. + // + UINT32 AuthenticationStatus; +} CORE_SECTION_STREAM_NODE; + +#define NULL_STREAM_HANDLE 0 + +typedef struct { + CORE_SECTION_CHILD_NODE *ChildNode; + CORE_SECTION_STREAM_NODE *ParentStream; + VOID *Registration; +} RPN_EVENT_CONTEXT; + +/** + The ExtractSection() function processes the input section and + allocates a buffer from the pool in which it returns the section + contents. If the section being extracted contains + authentication information (the section's + GuidedSectionHeader.Attributes field has the + EFI_GUIDED_SECTION_AUTH_STATUS_VALID bit set), the values + returned in AuthenticationStatus must reflect the results of + the authentication operation. Depending on the algorithm and + size of the encapsulated data, the time that is required to do + a full authentication may be prohibitively long for some + classes of systems. To indicate this, use + EFI_SECURITY_POLICY_PROTOCOL_GUID, which may be published by + the security policy driver (see the Platform Initialization + Driver Execution Environment Core Interface Specification for + more details and the GUID definition). If the + EFI_SECURITY_POLICY_PROTOCOL_GUID exists in the handle + database, then, if possible, full authentication should be + skipped and the section contents simply returned in the + OutputBuffer. In this case, the + EFI_AUTH_STATUS_PLATFORM_OVERRIDE bit AuthenticationStatus + must be set on return. ExtractSection() is callable only from + TPL_NOTIFY and below. Behavior of ExtractSection() at any + EFI_TPL above TPL_NOTIFY is undefined. Type EFI_TPL is + defined in RaiseTPL() in the UEFI 2.0 specification. + + + @param This Indicates the + EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL instance. + @param InputSection Buffer containing the input GUIDed section + to be processed. OutputBuffer OutputBuffer + is allocated from boot services pool + memory and contains the new section + stream. The caller is responsible for + freeing this buffer. + @param OutputBuffer *OutputBuffer is allocated from boot services + pool memory and contains the new section stream. + The caller is responsible for freeing this buffer. + @param OutputSize A pointer to a caller-allocated UINTN in + which the size of OutputBuffer allocation + is stored. If the function returns + anything other than EFI_SUCCESS, the value + of OutputSize is undefined. + + @param AuthenticationStatus A pointer to a caller-allocated + UINT32 that indicates the + authentication status of the + output buffer. If the input + section's + GuidedSectionHeader.Attributes + field has the + EFI_GUIDED_SECTION_AUTH_STATUS_VAL + bit as clear, AuthenticationStatus + must return zero. Both local bits + (19:16) and aggregate bits (3:0) + in AuthenticationStatus are + returned by ExtractSection(). + These bits reflect the status of + the extraction operation. The bit + pattern in both regions must be + the same, as the local and + aggregate authentication statuses + have equivalent meaning at this + level. If the function returns + anything other than EFI_SUCCESS, + the value of AuthenticationStatus + is undefined. + + + @retval EFI_SUCCESS The InputSection was successfully + processed and the section contents were + returned. + + @retval EFI_OUT_OF_RESOURCES The system has insufficient + resources to process the + request. + + @retval EFI_INVALID_PARAMETER The GUID in InputSection does + not match this instance of the + GUIDed Section Extraction + Protocol. + +**/ +EFI_STATUS +EFIAPI +CustomGuidedSectionExtract ( + IN CONST EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *This, + IN CONST VOID *InputSection, + OUT VOID **OutputBuffer, + OUT UINTN *OutputSize, + OUT UINT32 *AuthenticationStatus + ); + +// +// Module globals +// +LIST_ENTRY mStreamRoot = INITIALIZE_LIST_HEAD_VARIABLE (mStreamRoot); + +EFI_HANDLE mSectionExtractionHandle = NULL; + +EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL mCustomGuidedSectionExtractionProtocol = { + CustomGuidedSectionExtract +}; + +/** + Entry point of the section extraction code. Initializes an instance of the + section extraction interface and installs it on a new handle. + + @param ImageHandle A handle for the image that is initializing this driver + @param SystemTable A pointer to the EFI system table + + @retval EFI_SUCCESS Driver initialized successfully + @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources + +**/ +EFI_STATUS +EFIAPI +InitializeSectionExtraction ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_GUID *ExtractHandlerGuidTable; + UINTN ExtractHandlerNumber; + + // + // Get custom extract guided section method guid list + // + ExtractHandlerNumber = ExtractGuidedSectionGetGuidList (&ExtractHandlerGuidTable); + + Status = EFI_SUCCESS; + // + // Install custom guided extraction protocol + // + while (ExtractHandlerNumber-- > 0) { + Status = CoreInstallProtocolInterface ( + &mSectionExtractionHandle, + &ExtractHandlerGuidTable[ExtractHandlerNumber], + EFI_NATIVE_INTERFACE, + &mCustomGuidedSectionExtractionProtocol + ); + ASSERT_EFI_ERROR (Status); + } + + return Status; +} + +/** + Check if a stream is valid. + + @param SectionStream The section stream to be checked + @param SectionStreamLength The length of section stream + + @return A boolean value indicating the validness of the section stream. + +**/ +BOOLEAN +IsValidSectionStream ( + IN VOID *SectionStream, + IN UINTN SectionStreamLength + ) +{ + UINTN TotalLength; + UINTN SectionLength; + EFI_COMMON_SECTION_HEADER *SectionHeader; + EFI_COMMON_SECTION_HEADER *NextSectionHeader; + + TotalLength = 0; + SectionHeader = (EFI_COMMON_SECTION_HEADER *)SectionStream; + + while (TotalLength < SectionStreamLength) { + if (IS_SECTION2 (SectionHeader)) { + SectionLength = SECTION2_SIZE (SectionHeader); + } else { + SectionLength = SECTION_SIZE (SectionHeader); + } + + TotalLength += SectionLength; + + if (TotalLength == SectionStreamLength) { + return TRUE; + } + + // + // Move to the next byte following the section... + // + SectionHeader = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)SectionHeader + SectionLength); + + // + // Figure out where the next section begins + // + NextSectionHeader = ALIGN_POINTER (SectionHeader, 4); + TotalLength += (UINTN)NextSectionHeader - (UINTN)SectionHeader; + SectionHeader = NextSectionHeader; + } + + ASSERT (FALSE); + return FALSE; +} + +/** + Worker function. Constructor for section streams. + + @param SectionStreamLength Size in bytes of the section stream. + @param SectionStream Buffer containing the new section stream. + @param AllocateBuffer Indicates whether the stream buffer is to be + copied or the input buffer is to be used in + place. AuthenticationStatus- Indicates the + default authentication status for the new + stream. + @param AuthenticationStatus A pointer to a caller-allocated UINT32 that + indicates the authentication status of the + output buffer. If the input section's + GuidedSectionHeader.Attributes field + has the EFI_GUIDED_SECTION_AUTH_STATUS_VALID + bit as clear, AuthenticationStatus must return + zero. Both local bits (19:16) and aggregate + bits (3:0) in AuthenticationStatus are returned + by ExtractSection(). These bits reflect the + status of the extraction operation. The bit + pattern in both regions must be the same, as + the local and aggregate authentication statuses + have equivalent meaning at this level. If the + function returns anything other than + EFI_SUCCESS, the value of *AuthenticationStatus + is undefined. + @param SectionStreamHandle A pointer to a caller allocated section stream + handle. + + @retval EFI_SUCCESS Stream was added to stream database. + @retval EFI_OUT_OF_RESOURCES memory allocation failed. + +**/ +EFI_STATUS +OpenSectionStreamEx ( + IN UINTN SectionStreamLength, + IN VOID *SectionStream, + IN BOOLEAN AllocateBuffer, + IN UINT32 AuthenticationStatus, + OUT UINTN *SectionStreamHandle + ) +{ + CORE_SECTION_STREAM_NODE *NewStream; + EFI_TPL OldTpl; + + // + // Allocate a new stream + // + NewStream = AllocatePool (sizeof (CORE_SECTION_STREAM_NODE)); + if (NewStream == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + if (AllocateBuffer) { + // + // if we're here, we're double buffering, allocate the buffer and copy the + // data in + // + if (SectionStreamLength > 0) { + NewStream->StreamBuffer = AllocatePool (SectionStreamLength); + if (NewStream->StreamBuffer == NULL) { + CoreFreePool (NewStream); + return EFI_OUT_OF_RESOURCES; + } + + // + // Copy in stream data + // + CopyMem (NewStream->StreamBuffer, SectionStream, SectionStreamLength); + } else { + // + // It's possible to have a zero length section stream. + // + NewStream->StreamBuffer = NULL; + } + } else { + // + // If were here, the caller has supplied the buffer (it's an internal call) + // so just assign the buffer. This happens when we open section streams + // as a result of expanding an encapsulating section. + // + NewStream->StreamBuffer = SectionStream; + } + + // + // Initialize the rest of the section stream + // + NewStream->Signature = CORE_SECTION_STREAM_SIGNATURE; + NewStream->StreamHandle = (UINTN)NewStream; + NewStream->StreamLength = SectionStreamLength; + InitializeListHead (&NewStream->Children); + NewStream->AuthenticationStatus = AuthenticationStatus; + + // + // Add new stream to stream list + // + OldTpl = CoreRaiseTpl (TPL_NOTIFY); + InsertTailList (&mStreamRoot, &NewStream->Link); + CoreRestoreTpl (OldTpl); + + *SectionStreamHandle = NewStream->StreamHandle; + + return EFI_SUCCESS; +} + +/** + SEP member function. This function creates and returns a new section stream + handle to represent the new section stream. + + @param SectionStreamLength Size in bytes of the section stream. + @param SectionStream Buffer containing the new section stream. + @param SectionStreamHandle A pointer to a caller allocated UINTN that on + output contains the new section stream handle. + + @retval EFI_SUCCESS The section stream is created successfully. + @retval EFI_OUT_OF_RESOURCES memory allocation failed. + @retval EFI_INVALID_PARAMETER Section stream does not end concident with end + of last section. + +**/ +EFI_STATUS +EFIAPI +OpenSectionStream ( + IN UINTN SectionStreamLength, + IN VOID *SectionStream, + OUT UINTN *SectionStreamHandle + ) +{ + // + // Check to see section stream looks good... + // + if (!IsValidSectionStream (SectionStream, SectionStreamLength)) { + return EFI_INVALID_PARAMETER; + } + + return OpenSectionStreamEx ( + SectionStreamLength, + SectionStream, + FALSE, + 0, + SectionStreamHandle + ); +} + +/** + Worker function. Determine if the input stream:child matches the input type. + + @param Stream Indicates the section stream associated with the + child + @param Child Indicates the child to check + @param SearchType Indicates the type of section to check against + for + @param SectionDefinitionGuid Indicates the GUID to check against if the type + is EFI_SECTION_GUID_DEFINED + + @retval TRUE The child matches + @retval FALSE The child doesn't match + +**/ +BOOLEAN +ChildIsType ( + IN CORE_SECTION_STREAM_NODE *Stream, + IN CORE_SECTION_CHILD_NODE *Child, + IN EFI_SECTION_TYPE SearchType, + IN EFI_GUID *SectionDefinitionGuid + ) +{ + EFI_GUID_DEFINED_SECTION *GuidedSection; + + if (SearchType == EFI_SECTION_ALL) { + return TRUE; + } + + if (Child->Type != SearchType) { + return FALSE; + } + + if ((SearchType != EFI_SECTION_GUID_DEFINED) || (SectionDefinitionGuid == NULL)) { + return TRUE; + } + + GuidedSection = (EFI_GUID_DEFINED_SECTION *)(Stream->StreamBuffer + Child->OffsetInStream); + if (IS_SECTION2 (GuidedSection)) { + return CompareGuid (&(((EFI_GUID_DEFINED_SECTION2 *)GuidedSection)->SectionDefinitionGuid), SectionDefinitionGuid); + } else { + return CompareGuid (&GuidedSection->SectionDefinitionGuid, SectionDefinitionGuid); + } +} + +/** + Verify the Guided Section GUID by checking if there is the Guided Section GUID configuration table recorded the GUID itself. + + @param GuidedSectionGuid The Guided Section GUID. + @param GuidedSectionExtraction A pointer to the pointer to the supported Guided Section Extraction Protocol + for the Guided Section. + + @return TRUE The GuidedSectionGuid could be identified, and the pointer to + the Guided Section Extraction Protocol will be returned to *GuidedSectionExtraction. + @return FALSE The GuidedSectionGuid could not be identified, or + the Guided Section Extraction Protocol has not been installed yet. + +**/ +BOOLEAN +VerifyGuidedSectionGuid ( + IN EFI_GUID *GuidedSectionGuid, + OUT EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL **GuidedSectionExtraction + ) +{ + EFI_GUID *GuidRecorded; + VOID *Interface; + EFI_STATUS Status; + + Interface = NULL; + + // + // Check if there is the Guided Section GUID configuration table recorded the GUID itself. + // + Status = EfiGetSystemConfigurationTable (GuidedSectionGuid, (VOID **)&GuidRecorded); + if (Status == EFI_SUCCESS) { + if (CompareGuid (GuidRecorded, GuidedSectionGuid)) { + // + // Found the recorded GuidedSectionGuid. + // + Status = CoreLocateProtocol (GuidedSectionGuid, NULL, (VOID **)&Interface); + if (!EFI_ERROR (Status) && (Interface != NULL)) { + // + // Found the supported Guided Section Extraction Porotocol for the Guided Section. + // + *GuidedSectionExtraction = (EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *)Interface; + return TRUE; + } + + return FALSE; + } + } + + return FALSE; +} + +/** + RPN callback function. Initializes the section stream + when GUIDED_SECTION_EXTRACTION_PROTOCOL is installed. + + @param Event The event that fired + @param RpnContext A pointer to the context that allows us to identify + the relevent encapsulation. +**/ +VOID +EFIAPI +NotifyGuidedExtraction ( + IN EFI_EVENT Event, + IN VOID *RpnContext + ) +{ + EFI_STATUS Status; + EFI_GUID_DEFINED_SECTION *GuidedHeader; + EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *GuidedExtraction; + VOID *NewStreamBuffer; + UINTN NewStreamBufferSize; + UINT32 AuthenticationStatus; + RPN_EVENT_CONTEXT *Context; + + Context = RpnContext; + + GuidedHeader = (EFI_GUID_DEFINED_SECTION *)(Context->ParentStream->StreamBuffer + Context->ChildNode->OffsetInStream); + ASSERT (GuidedHeader->CommonHeader.Type == EFI_SECTION_GUID_DEFINED); + + if (!VerifyGuidedSectionGuid (Context->ChildNode->EncapsulationGuid, &GuidedExtraction)) { + return; + } + + Status = GuidedExtraction->ExtractSection ( + GuidedExtraction, + GuidedHeader, + &NewStreamBuffer, + &NewStreamBufferSize, + &AuthenticationStatus + ); + ASSERT_EFI_ERROR (Status); + + // + // Make sure we initialize the new stream with the correct + // authentication status for both aggregate and local status fields. + // + if ((GuidedHeader->Attributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) != 0) { + // + // OR in the parent stream's aggregate status. + // + AuthenticationStatus |= Context->ParentStream->AuthenticationStatus & EFI_AUTH_STATUS_ALL; + } else { + // + // since there's no authentication data contributed by the section, + // just inherit the full value from our immediate parent. + // + AuthenticationStatus = Context->ParentStream->AuthenticationStatus; + } + + Status = OpenSectionStreamEx ( + NewStreamBufferSize, + NewStreamBuffer, + FALSE, + AuthenticationStatus, + &Context->ChildNode->EncapsulatedStreamHandle + ); + ASSERT_EFI_ERROR (Status); + + // + // Close the event when done. + // + gBS->CloseEvent (Event); + Context->ChildNode->Event = NULL; + FreePool (Context); +} + +/** + Constructor for RPN event when a missing GUIDED_SECTION_EXTRACTION_PROTOCOL appears... + + @param ParentStream Indicates the parent of the ecnapsulation section (child) + @param ChildNode Indicates the child node that is the encapsulation section. + +**/ +VOID +CreateGuidedExtractionRpnEvent ( + IN CORE_SECTION_STREAM_NODE *ParentStream, + IN CORE_SECTION_CHILD_NODE *ChildNode + ) +{ + RPN_EVENT_CONTEXT *Context; + + // + // Allocate new event structure and context + // + Context = AllocatePool (sizeof (RPN_EVENT_CONTEXT)); + ASSERT (Context != NULL); + + Context->ChildNode = ChildNode; + Context->ParentStream = ParentStream; + + Context->ChildNode->Event = EfiCreateProtocolNotifyEvent ( + Context->ChildNode->EncapsulationGuid, + TPL_NOTIFY, + NotifyGuidedExtraction, + Context, + &Context->Registration + ); +} + +/** + Worker function. Constructor for new child nodes. + + @param Stream Indicates the section stream in which to add the + child. + @param ChildOffset Indicates the offset in Stream that is the + beginning of the child section. + @param ChildNode Indicates the Callee allocated and initialized + child. + + @retval EFI_SUCCESS Child node was found and returned. + EFI_OUT_OF_RESOURCES- Memory allocation failed. + @retval EFI_PROTOCOL_ERROR Encapsulation sections produce new stream + handles when the child node is created. If the + section type is GUID defined, and the extraction + GUID does not exist, and producing the stream + requires the GUID, then a protocol error is + generated and no child is produced. Values + returned by OpenSectionStreamEx. + +**/ +EFI_STATUS +CreateChildNode ( + IN CORE_SECTION_STREAM_NODE *Stream, + IN UINT32 ChildOffset, + OUT CORE_SECTION_CHILD_NODE **ChildNode + ) +{ + EFI_STATUS Status; + EFI_COMMON_SECTION_HEADER *SectionHeader; + EFI_COMPRESSION_SECTION *CompressionHeader; + EFI_GUID_DEFINED_SECTION *GuidedHeader; + EFI_DECOMPRESS_PROTOCOL *Decompress; + EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *GuidedExtraction; + VOID *NewStreamBuffer; + VOID *ScratchBuffer; + UINT32 ScratchSize; + UINTN NewStreamBufferSize; + UINT32 AuthenticationStatus; + VOID *CompressionSource; + UINT32 CompressionSourceSize; + UINT32 UncompressedLength; + UINT8 CompressionType; + UINT16 GuidedSectionAttributes; + + CORE_SECTION_CHILD_NODE *Node; + + SectionHeader = (EFI_COMMON_SECTION_HEADER *)(Stream->StreamBuffer + ChildOffset); + + // + // Allocate a new node + // + *ChildNode = AllocateZeroPool (sizeof (CORE_SECTION_CHILD_NODE)); + Node = *ChildNode; + if (Node == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Now initialize it + // + Node->Signature = CORE_SECTION_CHILD_SIGNATURE; + Node->Type = SectionHeader->Type; + if (IS_SECTION2 (SectionHeader)) { + Node->Size = SECTION2_SIZE (SectionHeader); + } else { + Node->Size = SECTION_SIZE (SectionHeader); + } + + Node->OffsetInStream = ChildOffset; + Node->EncapsulatedStreamHandle = NULL_STREAM_HANDLE; + Node->EncapsulationGuid = NULL; + + // + // If it's an encapsulating section, then create the new section stream also + // + switch (Node->Type) { + case EFI_SECTION_COMPRESSION: + // + // Get the CompressionSectionHeader + // + if (Node->Size < sizeof (EFI_COMPRESSION_SECTION)) { + CoreFreePool (Node); + return EFI_NOT_FOUND; + } + + CompressionHeader = (EFI_COMPRESSION_SECTION *)SectionHeader; + + if (IS_SECTION2 (CompressionHeader)) { + CompressionSource = (VOID *)((UINT8 *)CompressionHeader + sizeof (EFI_COMPRESSION_SECTION2)); + CompressionSourceSize = (UINT32)(SECTION2_SIZE (CompressionHeader) - sizeof (EFI_COMPRESSION_SECTION2)); + UncompressedLength = ((EFI_COMPRESSION_SECTION2 *)CompressionHeader)->UncompressedLength; + CompressionType = ((EFI_COMPRESSION_SECTION2 *)CompressionHeader)->CompressionType; + } else { + CompressionSource = (VOID *)((UINT8 *)CompressionHeader + sizeof (EFI_COMPRESSION_SECTION)); + CompressionSourceSize = (UINT32)(SECTION_SIZE (CompressionHeader) - sizeof (EFI_COMPRESSION_SECTION)); + UncompressedLength = CompressionHeader->UncompressedLength; + CompressionType = CompressionHeader->CompressionType; + } + + // + // Allocate space for the new stream + // + if (UncompressedLength > 0) { + NewStreamBufferSize = UncompressedLength; + NewStreamBuffer = AllocatePool (NewStreamBufferSize); + if (NewStreamBuffer == NULL) { + CoreFreePool (Node); + return EFI_OUT_OF_RESOURCES; + } + + if (CompressionType == EFI_NOT_COMPRESSED) { + // + // stream is not actually compressed, just encapsulated. So just copy it. + // + CopyMem (NewStreamBuffer, CompressionSource, NewStreamBufferSize); + } else if (CompressionType == EFI_STANDARD_COMPRESSION) { + // + // Only support the EFI_SATNDARD_COMPRESSION algorithm. + // + + // + // Decompress the stream + // + Status = CoreLocateProtocol (&gEfiDecompressProtocolGuid, NULL, (VOID **)&Decompress); + ASSERT_EFI_ERROR (Status); + ASSERT (Decompress != NULL); + + Status = Decompress->GetInfo ( + Decompress, + CompressionSource, + CompressionSourceSize, + (UINT32 *)&NewStreamBufferSize, + &ScratchSize + ); + if (EFI_ERROR (Status) || (NewStreamBufferSize != UncompressedLength)) { + CoreFreePool (Node); + CoreFreePool (NewStreamBuffer); + if (!EFI_ERROR (Status)) { + Status = EFI_BAD_BUFFER_SIZE; + } + + return Status; + } + + ScratchBuffer = AllocatePool (ScratchSize); + if (ScratchBuffer == NULL) { + CoreFreePool (Node); + CoreFreePool (NewStreamBuffer); + return EFI_OUT_OF_RESOURCES; + } + + Status = Decompress->Decompress ( + Decompress, + CompressionSource, + CompressionSourceSize, + NewStreamBuffer, + (UINT32)NewStreamBufferSize, + ScratchBuffer, + ScratchSize + ); + CoreFreePool (ScratchBuffer); + if (EFI_ERROR (Status)) { + CoreFreePool (Node); + CoreFreePool (NewStreamBuffer); + return Status; + } + } + } else { + NewStreamBuffer = NULL; + NewStreamBufferSize = 0; + } + + Status = OpenSectionStreamEx ( + NewStreamBufferSize, + NewStreamBuffer, + FALSE, + Stream->AuthenticationStatus, + &Node->EncapsulatedStreamHandle + ); + if (EFI_ERROR (Status)) { + CoreFreePool (Node); + CoreFreePool (NewStreamBuffer); + return Status; + } + + break; + + case EFI_SECTION_GUID_DEFINED: + GuidedHeader = (EFI_GUID_DEFINED_SECTION *)SectionHeader; + if (IS_SECTION2 (GuidedHeader)) { + Node->EncapsulationGuid = &(((EFI_GUID_DEFINED_SECTION2 *)GuidedHeader)->SectionDefinitionGuid); + GuidedSectionAttributes = ((EFI_GUID_DEFINED_SECTION2 *)GuidedHeader)->Attributes; + } else { + Node->EncapsulationGuid = &GuidedHeader->SectionDefinitionGuid; + GuidedSectionAttributes = GuidedHeader->Attributes; + } + + if (VerifyGuidedSectionGuid (Node->EncapsulationGuid, &GuidedExtraction)) { + // + // NewStreamBuffer is always allocated by ExtractSection... No caller + // allocation here. + // + Status = GuidedExtraction->ExtractSection ( + GuidedExtraction, + GuidedHeader, + &NewStreamBuffer, + &NewStreamBufferSize, + &AuthenticationStatus + ); + if (EFI_ERROR (Status)) { + CoreFreePool (*ChildNode); + return EFI_PROTOCOL_ERROR; + } + + // + // Make sure we initialize the new stream with the correct + // authentication status for both aggregate and local status fields. + // + if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) != 0) { + // + // OR in the parent stream's aggregate status. + // + AuthenticationStatus |= Stream->AuthenticationStatus & EFI_AUTH_STATUS_ALL; + } else { + // + // since there's no authentication data contributed by the section, + // just inherit the full value from our immediate parent. + // + AuthenticationStatus = Stream->AuthenticationStatus; + } + + Status = OpenSectionStreamEx ( + NewStreamBufferSize, + NewStreamBuffer, + FALSE, + AuthenticationStatus, + &Node->EncapsulatedStreamHandle + ); + if (EFI_ERROR (Status)) { + CoreFreePool (*ChildNode); + CoreFreePool (NewStreamBuffer); + return Status; + } + } else { + // + // There's no GUIDed section extraction protocol available. + // + if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) != 0) { + // + // If the section REQUIRES an extraction protocol, register for RPN + // when the required GUIDed extraction protocol becomes available. + // + CreateGuidedExtractionRpnEvent (Stream, Node); + } else { + // + // Figure out the proper authentication status + // + AuthenticationStatus = Stream->AuthenticationStatus; + + if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_AUTH_STATUS_VALID) == EFI_GUIDED_SECTION_AUTH_STATUS_VALID) { + AuthenticationStatus |= EFI_AUTH_STATUS_IMAGE_SIGNED | EFI_AUTH_STATUS_NOT_TESTED; + } + + if (IS_SECTION2 (GuidedHeader)) { + Status = OpenSectionStreamEx ( + SECTION2_SIZE (GuidedHeader) - ((EFI_GUID_DEFINED_SECTION2 *)GuidedHeader)->DataOffset, + (UINT8 *)GuidedHeader + ((EFI_GUID_DEFINED_SECTION2 *)GuidedHeader)->DataOffset, + TRUE, + AuthenticationStatus, + &Node->EncapsulatedStreamHandle + ); + } else { + Status = OpenSectionStreamEx ( + SECTION_SIZE (GuidedHeader) - ((EFI_GUID_DEFINED_SECTION *)GuidedHeader)->DataOffset, + (UINT8 *)GuidedHeader + ((EFI_GUID_DEFINED_SECTION *)GuidedHeader)->DataOffset, + TRUE, + AuthenticationStatus, + &Node->EncapsulatedStreamHandle + ); + } + + if (EFI_ERROR (Status)) { + CoreFreePool (Node); + return Status; + } + } + } + + break; + + default: + + // + // Nothing to do if it's a leaf + // + break; + } + + // + // Last, add the new child node to the stream + // + InsertTailList (&Stream->Children, &Node->Link); + + return EFI_SUCCESS; +} + +/** + Worker function Recursively searches / builds section stream database + looking for requested section. + + @param SourceStream Indicates the section stream in which to do the + search. + @param SearchType Indicates the type of section to search for. + @param SectionInstance Indicates which instance of section to find. + This is an in/out parameter and it is 1-based, + to deal with recursions. + @param SectionDefinitionGuid Guid of section definition + @param Depth Nesting depth of encapsulation sections. + Callers different from FindChildNode() are + responsible for passing in a zero Depth. + @param FoundChild Output indicating the child node that is found. + @param FoundStream Output indicating which section stream the child + was found in. If this stream was generated as a + result of an encapsulation section, the + streamhandle is visible within the SEP driver + only. + @param AuthenticationStatus Indicates the authentication status of the found section. + + @retval EFI_SUCCESS Child node was found and returned. + EFI_OUT_OF_RESOURCES- Memory allocation failed. + @retval EFI_NOT_FOUND Requested child node does not exist. + @retval EFI_PROTOCOL_ERROR a required GUIDED section extraction protocol + does not exist + @retval EFI_ABORTED Recursion aborted because Depth has been + greater than or equal to + PcdFwVolDxeMaxEncapsulationDepth. + +**/ +EFI_STATUS +FindChildNode ( + IN CORE_SECTION_STREAM_NODE *SourceStream, + IN EFI_SECTION_TYPE SearchType, + IN OUT UINTN *SectionInstance, + IN EFI_GUID *SectionDefinitionGuid, + IN UINT32 Depth, + OUT CORE_SECTION_CHILD_NODE **FoundChild, + OUT CORE_SECTION_STREAM_NODE **FoundStream, + OUT UINT32 *AuthenticationStatus + ) +{ + CORE_SECTION_CHILD_NODE *CurrentChildNode; + CORE_SECTION_CHILD_NODE *RecursedChildNode; + CORE_SECTION_STREAM_NODE *RecursedFoundStream; + UINT32 NextChildOffset; + EFI_STATUS ErrorStatus; + EFI_STATUS Status; + + ASSERT (*SectionInstance > 0); + + if (Depth >= PcdGet32 (PcdFwVolDxeMaxEncapsulationDepth)) { + return EFI_ABORTED; + } + + CurrentChildNode = NULL; + ErrorStatus = EFI_NOT_FOUND; + + if (SourceStream->StreamLength == 0) { + return EFI_NOT_FOUND; + } + + if (IsListEmpty (&SourceStream->Children) && + (SourceStream->StreamLength >= sizeof (EFI_COMMON_SECTION_HEADER))) + { + // + // This occurs when a section stream exists, but no child sections + // have been parsed out yet. Therefore, extract the first child and add it + // to the list of children so we can get started. + // Section stream may contain an array of zero or more bytes. + // So, its size should be >= the size of commen section header. + // + Status = CreateChildNode (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 we run + // out of data + // + CurrentChildNode = CHILD_SECTION_NODE_FROM_LINK (GetFirstNode (&SourceStream->Children)); + + for ( ; ;) { + ASSERT (CurrentChildNode != NULL); + if (ChildIsType (SourceStream, CurrentChildNode, SearchType, SectionDefinitionGuid)) { + // + // The type matches, so check the instance count to see if it's the one we want + // + (*SectionInstance)--; + if (*SectionInstance == 0) { + // + // Got it! + // + *FoundChild = CurrentChildNode; + *FoundStream = SourceStream; + *AuthenticationStatus = SourceStream->AuthenticationStatus; + return EFI_SUCCESS; + } + } + + // + // Type mismatch, or we haven't found the desired instance yet. + // + ASSERT (*SectionInstance > 0); + + if (CurrentChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) { + // + // If the current node is an encapsulating node, recurse into it... + // + Status = FindChildNode ( + (CORE_SECTION_STREAM_NODE *)CurrentChildNode->EncapsulatedStreamHandle, + SearchType, + SectionInstance, + SectionDefinitionGuid, + Depth + 1, + &RecursedChildNode, + &RecursedFoundStream, + AuthenticationStatus + ); + if (*SectionInstance == 0) { + // + // The recursive FindChildNode() call decreased (*SectionInstance) to + // zero. + // + ASSERT_EFI_ERROR (Status); + *FoundChild = RecursedChildNode; + *FoundStream = RecursedFoundStream; + return EFI_SUCCESS; + } else { + if (Status == EFI_ABORTED) { + // + // If the recursive call was aborted due to nesting depth, stop + // looking for the requested child node. The skipped subtree could + // throw off the instance counting. + // + return Status; + } + + // + // Save the error code and continue to find the requested child node in + // the rest of the stream. + // + ErrorStatus = Status; + } + } else if ((CurrentChildNode->Type == EFI_SECTION_GUID_DEFINED) && (SearchType != EFI_SECTION_GUID_DEFINED)) { + // + // When Node Type is GUIDED section, but Node has no encapsulated data, Node data should not be parsed + // because a required GUIDED section extraction protocol does not exist. + // If SearchType is not GUIDED section, EFI_PROTOCOL_ERROR should return. + // + ErrorStatus = EFI_PROTOCOL_ERROR; + } + + if (!IsNodeAtEnd (&SourceStream->Children, &CurrentChildNode->Link)) { + // + // We haven't found the child node we're interested in yet, but there's + // still more nodes that have already been parsed so get the next one + // and continue searching.. + // + CurrentChildNode = CHILD_SECTION_NODE_FROM_LINK (GetNextNode (&SourceStream->Children, &CurrentChildNode->Link)); + } else { + // + // We've exhausted children that have already been parsed, so see if + // there's any more data and continue parsing out more children if there + // is. + // + NextChildOffset = CurrentChildNode->OffsetInStream + CurrentChildNode->Size; + // + // Round up to 4 byte boundary + // + NextChildOffset += 3; + NextChildOffset &= ~(UINTN)3; + if (NextChildOffset <= SourceStream->StreamLength - sizeof (EFI_COMMON_SECTION_HEADER)) { + // + // There's an unparsed child remaining in the stream, so create a new child node + // + Status = CreateChildNode (SourceStream, NextChildOffset, &CurrentChildNode); + if (EFI_ERROR (Status)) { + return Status; + } + } else { + ASSERT (EFI_ERROR (ErrorStatus)); + return ErrorStatus; + } + } + } +} + +/** + Worker function. Search stream database for requested stream handle. + + @param SearchHandle Indicates which stream to look for. + @param FoundStream Output pointer to the found stream. + + @retval EFI_SUCCESS StreamHandle was found and *FoundStream contains + the stream node. + @retval EFI_NOT_FOUND SearchHandle was not found in the stream + database. + +**/ +EFI_STATUS +FindStreamNode ( + IN UINTN SearchHandle, + OUT CORE_SECTION_STREAM_NODE **FoundStream + ) +{ + CORE_SECTION_STREAM_NODE *StreamNode; + + if (!IsListEmpty (&mStreamRoot)) { + StreamNode = STREAM_NODE_FROM_LINK (GetFirstNode (&mStreamRoot)); + for ( ; ;) { + if (StreamNode->StreamHandle == SearchHandle) { + *FoundStream = StreamNode; + return EFI_SUCCESS; + } else if (IsNodeAtEnd (&mStreamRoot, &StreamNode->Link)) { + break; + } else { + StreamNode = STREAM_NODE_FROM_LINK (GetNextNode (&mStreamRoot, &StreamNode->Link)); + } + } + } + + return EFI_NOT_FOUND; +} + +/** + SEP member function. Retrieves requested section from section stream. + + @param SectionStreamHandle The section stream from which to extract the + requested section. + @param SectionType A pointer to the type of section to search for. + @param SectionDefinitionGuid If the section type is EFI_SECTION_GUID_DEFINED, + then SectionDefinitionGuid indicates which of + these types of sections to search for. + @param SectionInstance Indicates which instance of the requested + section to return. + @param Buffer Double indirection to buffer. If *Buffer is + non-null on input, then the buffer is caller + allocated. If Buffer is NULL, then the buffer + is callee allocated. In either case, the + required buffer size is returned in *BufferSize. + @param BufferSize On input, indicates the size of *Buffer if + *Buffer is non-null on input. On output, + indicates the required size (allocated size if + callee allocated) of *Buffer. + @param AuthenticationStatus A pointer to a caller-allocated UINT32 that + indicates the authentication status of the + output buffer. If the input section's + GuidedSectionHeader.Attributes field + has the EFI_GUIDED_SECTION_AUTH_STATUS_VALID + bit as clear, AuthenticationStatus must return + zero. Both local bits (19:16) and aggregate + bits (3:0) in AuthenticationStatus are returned + by ExtractSection(). These bits reflect the + status of the extraction operation. The bit + pattern in both regions must be the same, as + the local and aggregate authentication statuses + have equivalent meaning at this level. If the + function returns anything other than + EFI_SUCCESS, the value of *AuthenticationStatus + is undefined. + @param IsFfs3Fv Indicates the FV format. + + @retval EFI_SUCCESS Section was retrieved successfully + @retval EFI_PROTOCOL_ERROR A GUID defined section was encountered in the + section stream with its + EFI_GUIDED_SECTION_PROCESSING_REQUIRED bit set, + but there was no corresponding GUIDed Section + Extraction Protocol in the handle database. + *Buffer is unmodified. + @retval EFI_NOT_FOUND An error was encountered when parsing the + SectionStream. This indicates the SectionStream + is not correctly formatted. + @retval EFI_NOT_FOUND The requested section does not exist. + @retval EFI_OUT_OF_RESOURCES The system has insufficient resources to process + the request. + @retval EFI_INVALID_PARAMETER The SectionStreamHandle does not exist. + @retval EFI_WARN_TOO_SMALL The size of the caller allocated input buffer is + insufficient to contain the requested section. + The input buffer is filled and section contents + are truncated. + +**/ +EFI_STATUS +EFIAPI +GetSection ( + IN UINTN SectionStreamHandle, + IN EFI_SECTION_TYPE *SectionType, + IN EFI_GUID *SectionDefinitionGuid, + IN UINTN SectionInstance, + IN VOID **Buffer, + IN OUT UINTN *BufferSize, + OUT UINT32 *AuthenticationStatus, + IN BOOLEAN IsFfs3Fv + ) +{ + CORE_SECTION_STREAM_NODE *StreamNode; + EFI_TPL OldTpl; + EFI_STATUS Status; + CORE_SECTION_CHILD_NODE *ChildNode; + CORE_SECTION_STREAM_NODE *ChildStreamNode; + UINTN CopySize; + UINT32 ExtractedAuthenticationStatus; + UINTN Instance; + UINT8 *CopyBuffer; + UINTN SectionSize; + EFI_COMMON_SECTION_HEADER *Section; + + ChildStreamNode = NULL; + OldTpl = CoreRaiseTpl (TPL_NOTIFY); + Instance = SectionInstance + 1; + + // + // Locate target stream + // + Status = FindStreamNode (SectionStreamHandle, &StreamNode); + if (EFI_ERROR (Status)) { + Status = EFI_INVALID_PARAMETER; + goto GetSection_Done; + } + + // + // Found the stream, now locate and return the appropriate section + // + if (SectionType == NULL) { + // + // SectionType == NULL means return the WHOLE section stream... + // + CopySize = StreamNode->StreamLength; + CopyBuffer = StreamNode->StreamBuffer; + *AuthenticationStatus = StreamNode->AuthenticationStatus; + } else { + // + // There's a requested section type, so go find it and return it... + // + Status = FindChildNode ( + StreamNode, + *SectionType, + &Instance, + SectionDefinitionGuid, + 0, // encapsulation depth + &ChildNode, + &ChildStreamNode, + &ExtractedAuthenticationStatus + ); + if (EFI_ERROR (Status)) { + if (Status == EFI_ABORTED) { + DEBUG (( + DEBUG_ERROR, + "%a: recursion aborted due to nesting depth\n", + __func__ + )); + // + // Map "aborted" to "not found". + // + Status = EFI_NOT_FOUND; + } + + goto GetSection_Done; + } + + Section = (EFI_COMMON_SECTION_HEADER *)(ChildStreamNode->StreamBuffer + ChildNode->OffsetInStream); + + if (IS_SECTION2 (Section)) { + ASSERT (SECTION2_SIZE (Section) > 0x00FFFFFF); + if (!IsFfs3Fv) { + DEBUG ((DEBUG_ERROR, "It is a FFS3 formatted section in a non-FFS3 formatted FV.\n")); + Status = EFI_NOT_FOUND; + goto GetSection_Done; + } + + CopySize = SECTION2_SIZE (Section) - sizeof (EFI_COMMON_SECTION_HEADER2); + CopyBuffer = (UINT8 *)Section + sizeof (EFI_COMMON_SECTION_HEADER2); + } else { + CopySize = SECTION_SIZE (Section) - sizeof (EFI_COMMON_SECTION_HEADER); + CopyBuffer = (UINT8 *)Section + sizeof (EFI_COMMON_SECTION_HEADER); + } + + *AuthenticationStatus = ExtractedAuthenticationStatus; + } + + SectionSize = CopySize; + if (*Buffer != NULL) { + // + // Caller allocated buffer. Fill to size and return required size... + // + if (*BufferSize < CopySize) { + Status = EFI_WARN_BUFFER_TOO_SMALL; + CopySize = *BufferSize; + } + } else { + // + // Callee allocated buffer. Allocate buffer and return size. + // + *Buffer = AllocatePool (CopySize); + if (*Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto GetSection_Done; + } + } + + CopyMem (*Buffer, CopyBuffer, CopySize); + *BufferSize = SectionSize; + +GetSection_Done: + CoreRestoreTpl (OldTpl); + + return Status; +} + +/** + Worker function. Destructor for child nodes. + + @param ChildNode Indicates the node to destroy + +**/ +VOID +FreeChildNode ( + IN CORE_SECTION_CHILD_NODE *ChildNode + ) +{ + ASSERT (ChildNode->Signature == CORE_SECTION_CHILD_SIGNATURE); + // + // Remove the child from it's list + // + RemoveEntryList (&ChildNode->Link); + + if (ChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) { + // + // If it's an encapsulating section, we close the resulting section stream. + // CloseSectionStream will free all memory associated with the stream. + // + CloseSectionStream (ChildNode->EncapsulatedStreamHandle, TRUE); + } + + if (ChildNode->Event != NULL) { + gBS->CloseEvent (ChildNode->Event); + } + + // + // Last, free the child node itself + // + CoreFreePool (ChildNode); +} + +/** + SEP member function. Deletes an existing section stream + + @param StreamHandleToClose Indicates the stream to close + @param FreeStreamBuffer TRUE - Need to free stream buffer; + FALSE - No need to free stream buffer. + + @retval EFI_SUCCESS The section stream is closed sucessfully. + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. + @retval EFI_INVALID_PARAMETER Section stream does not end concident with end + of last section. + +**/ +EFI_STATUS +EFIAPI +CloseSectionStream ( + IN UINTN StreamHandleToClose, + IN BOOLEAN FreeStreamBuffer + ) +{ + CORE_SECTION_STREAM_NODE *StreamNode; + EFI_TPL OldTpl; + EFI_STATUS Status; + LIST_ENTRY *Link; + CORE_SECTION_CHILD_NODE *ChildNode; + + OldTpl = CoreRaiseTpl (TPL_NOTIFY); + + // + // Locate target stream + // + Status = FindStreamNode (StreamHandleToClose, &StreamNode); + if (!EFI_ERROR (Status)) { + // + // Found the stream, so close it + // + RemoveEntryList (&StreamNode->Link); + while (!IsListEmpty (&StreamNode->Children)) { + Link = GetFirstNode (&StreamNode->Children); + ChildNode = CHILD_SECTION_NODE_FROM_LINK (Link); + FreeChildNode (ChildNode); + } + + if (FreeStreamBuffer) { + CoreFreePool (StreamNode->StreamBuffer); + } + + CoreFreePool (StreamNode); + Status = EFI_SUCCESS; + } else { + Status = EFI_INVALID_PARAMETER; + } + + CoreRestoreTpl (OldTpl); + return Status; +} + +/** + The ExtractSection() function processes the input section and + allocates a buffer from the pool in which it returns the section + contents. If the section being extracted contains + authentication information (the section's + GuidedSectionHeader.Attributes field has the + EFI_GUIDED_SECTION_AUTH_STATUS_VALID bit set), the values + returned in AuthenticationStatus must reflect the results of + the authentication operation. Depending on the algorithm and + size of the encapsulated data, the time that is required to do + a full authentication may be prohibitively long for some + classes of systems. To indicate this, use + EFI_SECURITY_POLICY_PROTOCOL_GUID, which may be published by + the security policy driver (see the Platform Initialization + Driver Execution Environment Core Interface Specification for + more details and the GUID definition). If the + EFI_SECURITY_POLICY_PROTOCOL_GUID exists in the handle + database, then, if possible, full authentication should be + skipped and the section contents simply returned in the + OutputBuffer. In this case, the + EFI_AUTH_STATUS_PLATFORM_OVERRIDE bit AuthenticationStatus + must be set on return. ExtractSection() is callable only from + TPL_NOTIFY and below. Behavior of ExtractSection() at any + EFI_TPL above TPL_NOTIFY is undefined. Type EFI_TPL is + defined in RaiseTPL() in the UEFI 2.0 specification. + + + @param This Indicates the + EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL instance. + @param InputSection Buffer containing the input GUIDed section + to be processed. OutputBuffer OutputBuffer + is allocated from boot services pool + memory and contains the new section + stream. The caller is responsible for + freeing this buffer. + @param OutputBuffer *OutputBuffer is allocated from boot services + pool memory and contains the new section stream. + The caller is responsible for freeing this buffer. + @param OutputSize A pointer to a caller-allocated UINTN in + which the size of OutputBuffer allocation + is stored. If the function returns + anything other than EFI_SUCCESS, the value + of OutputSize is undefined. + + @param AuthenticationStatus A pointer to a caller-allocated + UINT32 that indicates the + authentication status of the + output buffer. If the input + section's + GuidedSectionHeader.Attributes + field has the + EFI_GUIDED_SECTION_AUTH_STATUS_VAL + bit as clear, AuthenticationStatus + must return zero. Both local bits + (19:16) and aggregate bits (3:0) + in AuthenticationStatus are + returned by ExtractSection(). + These bits reflect the status of + the extraction operation. The bit + pattern in both regions must be + the same, as the local and + aggregate authentication statuses + have equivalent meaning at this + level. If the function returns + anything other than EFI_SUCCESS, + the value of AuthenticationStatus + is undefined. + + + @retval EFI_SUCCESS The InputSection was successfully + processed and the section contents were + returned. + + @retval EFI_OUT_OF_RESOURCES The system has insufficient + resources to process the + request. + + @retval EFI_INVALID_PARAMETER The GUID in InputSection does + not match this instance of the + GUIDed Section Extraction + Protocol. + +**/ +EFI_STATUS +EFIAPI +CustomGuidedSectionExtract ( + IN CONST EFI_GUIDED_SECTION_EXTRACTION_PROTOCOL *This, + IN CONST VOID *InputSection, + OUT VOID **OutputBuffer, + OUT UINTN *OutputSize, + OUT UINT32 *AuthenticationStatus + ) +{ + EFI_STATUS Status; + VOID *ScratchBuffer; + VOID *AllocatedOutputBuffer; + UINT32 OutputBufferSize; + UINT32 ScratchBufferSize; + UINT16 SectionAttribute; + + // + // Init local variable + // + ScratchBuffer = NULL; + AllocatedOutputBuffer = NULL; + + // + // Call GetInfo to get the size and attribute of input guided section data. + // + Status = ExtractGuidedSectionGetInfo ( + InputSection, + &OutputBufferSize, + &ScratchBufferSize, + &SectionAttribute + ); + + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "GetInfo from guided section Failed - %r\n", Status)); + return Status; + } + + if (ScratchBufferSize > 0) { + // + // Allocate scratch buffer + // + ScratchBuffer = AllocatePool (ScratchBufferSize); + if (ScratchBuffer == NULL) { + return EFI_OUT_OF_RESOURCES; + } + } + + if (OutputBufferSize > 0) { + // + // Allocate output buffer + // + AllocatedOutputBuffer = AllocatePool (OutputBufferSize); + if (AllocatedOutputBuffer == NULL) { + if (ScratchBuffer != NULL) { + FreePool (ScratchBuffer); + } + + return EFI_OUT_OF_RESOURCES; + } + + *OutputBuffer = AllocatedOutputBuffer; + } + + // + // Call decode function to extract raw data from the guided section. + // + Status = ExtractGuidedSectionDecode ( + InputSection, + OutputBuffer, + ScratchBuffer, + AuthenticationStatus + ); + if (EFI_ERROR (Status)) { + // + // Decode failed + // + if (AllocatedOutputBuffer != NULL) { + CoreFreePool (AllocatedOutputBuffer); + } + + if (ScratchBuffer != NULL) { + CoreFreePool (ScratchBuffer); + } + + DEBUG ((DEBUG_ERROR, "Extract guided section Failed - %r\n", Status)); + return Status; + } + + if (*OutputBuffer != AllocatedOutputBuffer) { + // + // OutputBuffer was returned as a different value, + // so copy section contents to the allocated memory buffer. + // + CopyMem (AllocatedOutputBuffer, *OutputBuffer, OutputBufferSize); + *OutputBuffer = AllocatedOutputBuffer; + } + + // + // Set real size of output buffer. + // + *OutputSize = (UINTN)OutputBufferSize; + + // + // Free unused scratch buffer. + // + if (ScratchBuffer != NULL) { + CoreFreePool (ScratchBuffer); + } + + return EFI_SUCCESS; +} |
