| /** @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 - 2014, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include <FrameworkDxe.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/DebugLib.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/UefiLib.h> | |
| #include <Protocol/Decompress.h> | |
| #include <Protocol/GuidedSectionExtraction.h> | |
| #include <Protocol/SectionExtraction.h> | |
| // | |
| // Local defines and typedefs | |
| // | |
| #define FRAMEWORK_SECTION_CHILD_SIGNATURE SIGNATURE_32('S','X','F','S') | |
| #define CHILD_SECTION_NODE_FROM_LINK(Node) \ | |
| CR (Node, FRAMEWORK_SECTION_CHILD_NODE, Link, FRAMEWORK_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; | |
| } FRAMEWORK_SECTION_CHILD_NODE; | |
| #define FRAMEWORK_SECTION_STREAM_SIGNATURE SIGNATURE_32('S','X','S','S') | |
| #define STREAM_NODE_FROM_LINK(Node) \ | |
| CR (Node, FRAMEWORK_SECTION_STREAM_NODE, Link, FRAMEWORK_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; | |
| } FRAMEWORK_SECTION_STREAM_NODE; | |
| #define NULL_STREAM_HANDLE 0 | |
| typedef struct { | |
| FRAMEWORK_SECTION_CHILD_NODE *ChildNode; | |
| FRAMEWORK_SECTION_STREAM_NODE *ParentStream; | |
| VOID *Registration; | |
| } RPN_EVENT_CONTEXT; | |
| /** | |
| SEP member function. This function creates and returns a new section stream | |
| handle to represent the new section stream. | |
| @param This Indicates the calling context. | |
| @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 Section wase opened 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 EFI_SECTION_EXTRACTION_PROTOCOL *This, | |
| IN UINTN SectionStreamLength, | |
| IN VOID *SectionStream, | |
| OUT UINTN *SectionStreamHandle | |
| ) | |
| ; | |
| /** | |
| SEP member function. Retrieves requested section from section stream. | |
| @param This Pointer to SEP instance. | |
| @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 requried 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 Indicates the authentication status of the retrieved | |
| section. | |
| @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 contents are section contents | |
| are truncated. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GetSection ( | |
| IN EFI_SECTION_EXTRACTION_PROTOCOL *This, | |
| 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 | |
| ) | |
| ; | |
| /** | |
| SEP member function. Deletes an existing section stream | |
| @param This Indicates the calling context. | |
| @param StreamHandleToClose Indicates the stream to close | |
| @retval EFI_SUCCESS Section stream was closed 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 | |
| CloseSectionStream ( | |
| IN EFI_SECTION_EXTRACTION_PROTOCOL *This, | |
| IN UINTN StreamHandleToClose | |
| ) | |
| ; | |
| // | |
| // Module globals | |
| // | |
| LIST_ENTRY mStreamRoot = INITIALIZE_LIST_HEAD_VARIABLE (mStreamRoot); | |
| EFI_HANDLE mSectionExtractionHandle = NULL; | |
| EFI_SECTION_EXTRACTION_PROTOCOL mSectionExtraction = { | |
| OpenSectionStream, | |
| GetSection, | |
| CloseSectionStream | |
| }; | |
| /** | |
| 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 | |
| SectionExtractionEntryPoint ( | |
| IN EFI_HANDLE ImageHandle, | |
| IN EFI_SYSTEM_TABLE *SystemTable | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| // | |
| // Install SEP to a new handle | |
| // | |
| Status = gBS->InstallProtocolInterface ( | |
| &mSectionExtractionHandle, | |
| &gEfiSectionExtractionProtocolGuid, | |
| EFI_NATIVE_INTERFACE, | |
| &mSectionExtraction | |
| ); | |
| 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. | |
| @param AuthenticationStatus Indicates the default authentication status for the | |
| new stream. | |
| @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 | |
| ) | |
| { | |
| FRAMEWORK_SECTION_STREAM_NODE *NewStream; | |
| EFI_TPL OldTpl; | |
| // | |
| // Allocate a new stream | |
| // | |
| NewStream = AllocatePool (sizeof (FRAMEWORK_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) { | |
| FreePool (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 = FRAMEWORK_SECTION_STREAM_SIGNATURE; | |
| NewStream->StreamHandle = (UINTN) NewStream; | |
| NewStream->StreamLength = SectionStreamLength; | |
| InitializeListHead (&NewStream->Children); | |
| NewStream->AuthenticationStatus = AuthenticationStatus; | |
| // | |
| // Add new stream to stream list | |
| // | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| InsertTailList (&mStreamRoot, &NewStream->Link); | |
| gBS->RestoreTPL (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 This Indicates the calling context. | |
| @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 Section wase opened 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 EFI_SECTION_EXTRACTION_PROTOCOL *This, | |
| 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, | |
| TRUE, | |
| 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 FRAMEWORK_SECTION_STREAM_NODE *Stream, | |
| IN FRAMEWORK_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); | |
| } | |
| } | |
| /** | |
| Create a protocol notification event and return it. | |
| @param ProtocolGuid Protocol to register notification event on. | |
| @param NotifyTpl Maximum TPL to signal the NotifyFunction. | |
| @param NotifyFunction EFI notification routine. | |
| @param NotifyContext Context passed into Event when it is created. | |
| @param Registration Registration key returned from RegisterProtocolNotify(). | |
| @param SignalFlag Boolean value to decide whether kick the event after register or not. | |
| @return The EFI_EVENT that has been registered to be signaled when a ProtocolGuid | |
| is added to the system. | |
| **/ | |
| EFI_EVENT | |
| CreateProtocolNotifyEvent ( | |
| IN EFI_GUID *ProtocolGuid, | |
| IN EFI_TPL NotifyTpl, | |
| IN EFI_EVENT_NOTIFY NotifyFunction, | |
| IN VOID *NotifyContext, | |
| OUT VOID **Registration, | |
| IN BOOLEAN SignalFlag | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_EVENT Event; | |
| // | |
| // Create the event | |
| // | |
| Status = gBS->CreateEvent ( | |
| EVT_NOTIFY_SIGNAL, | |
| NotifyTpl, | |
| NotifyFunction, | |
| NotifyContext, | |
| &Event | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| // | |
| // Register for protocol notifactions on this event | |
| // | |
| Status = gBS->RegisterProtocolNotify ( | |
| ProtocolGuid, | |
| Event, | |
| Registration | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if (SignalFlag) { | |
| // | |
| // Kick the event so we will perform an initial pass of | |
| // current installed drivers | |
| // | |
| gBS->SignalEvent (Event); | |
| } | |
| return Event; | |
| } | |
| /** | |
| 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; | |
| // | |
| // 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 = gBS->LocateProtocol (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. | |
| 1. Initialize the section stream when the GUIDED_SECTION_EXTRACTION_PROTOCOL is installed. | |
| 2. Removes a stale section stream and re-initializes it with an updated AuthenticationStatus. | |
| @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; | |
| Status = EFI_SUCCESS; | |
| if (Context->ChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) { | |
| Status = CloseSectionStream (&mSectionExtraction, Context->ChildNode->EncapsulatedStreamHandle); | |
| } | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // The stream is not initialized, open it. | |
| // Or the stream closed successfully, so re-open the stream with correct AuthenticationStatus. | |
| // | |
| 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); | |
| // | |
| // OR in the parent stream's aggregate status. | |
| // | |
| AuthenticationStatus |= Context->ParentStream->AuthenticationStatus & EFI_AGGREGATE_AUTH_STATUS_ALL; | |
| Status = OpenSectionStreamEx ( | |
| NewStreamBufferSize, | |
| NewStreamBuffer, | |
| FALSE, | |
| AuthenticationStatus, | |
| &Context->ChildNode->EncapsulatedStreamHandle | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| // | |
| // If above, the stream did not close successfully, it indicates it's | |
| // already been closed by someone, so just destroy the event and be done with | |
| // it. | |
| // | |
| gBS->CloseEvent (Event); | |
| Context->ChildNode->Event = NULL; | |
| FreePool (Context); | |
| } | |
| /** | |
| Worker function. Constructor for RPN event if needed to keep AuthenticationStatus | |
| cache correct 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 FRAMEWORK_SECTION_STREAM_NODE *ParentStream, | |
| IN FRAMEWORK_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 = CreateProtocolNotifyEvent ( | |
| Context->ChildNode->EncapsulationGuid, | |
| TPL_NOTIFY, | |
| NotifyGuidedExtraction, | |
| Context, | |
| &Context->Registration, | |
| FALSE | |
| ); | |
| } | |
| /** | |
| 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. | |
| @retval 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 FRAMEWORK_SECTION_STREAM_NODE *Stream, | |
| IN UINT32 ChildOffset, | |
| OUT FRAMEWORK_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; | |
| FRAMEWORK_SECTION_CHILD_NODE *Node; | |
| SectionHeader = (EFI_COMMON_SECTION_HEADER *) (Stream->StreamBuffer + ChildOffset); | |
| // | |
| // Allocate a new node | |
| // | |
| *ChildNode = AllocateZeroPool (sizeof (FRAMEWORK_SECTION_CHILD_NODE)); | |
| Node = *ChildNode; | |
| if (Node == NULL) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| // | |
| // Now initialize it | |
| // | |
| Node->Signature = FRAMEWORK_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)) { | |
| FreePool (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) { | |
| FreePool (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 = gBS->LocateProtocol (&gEfiDecompressProtocolGuid, NULL, (VOID **)&Decompress); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = Decompress->GetInfo ( | |
| Decompress, | |
| CompressionSource, | |
| CompressionSourceSize, | |
| (UINT32 *)&NewStreamBufferSize, | |
| &ScratchSize | |
| ); | |
| if (EFI_ERROR (Status) || (NewStreamBufferSize != UncompressedLength)) { | |
| FreePool (Node); | |
| FreePool (NewStreamBuffer); | |
| if (!EFI_ERROR (Status)) { | |
| Status = EFI_BAD_BUFFER_SIZE; | |
| } | |
| return Status; | |
| } | |
| ScratchBuffer = AllocatePool (ScratchSize); | |
| if (ScratchBuffer == NULL) { | |
| FreePool (Node); | |
| FreePool (NewStreamBuffer); | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| Status = Decompress->Decompress ( | |
| Decompress, | |
| CompressionSource, | |
| CompressionSourceSize, | |
| NewStreamBuffer, | |
| (UINT32)NewStreamBufferSize, | |
| ScratchBuffer, | |
| ScratchSize | |
| ); | |
| FreePool (ScratchBuffer); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Node); | |
| FreePool (NewStreamBuffer); | |
| return Status; | |
| } | |
| } | |
| } else { | |
| NewStreamBuffer = NULL; | |
| NewStreamBufferSize = 0; | |
| } | |
| Status = OpenSectionStreamEx ( | |
| NewStreamBufferSize, | |
| NewStreamBuffer, | |
| FALSE, | |
| Stream->AuthenticationStatus, | |
| &Node->EncapsulatedStreamHandle | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (Node); | |
| FreePool (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)) { | |
| FreePool (*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) == EFI_GUIDED_SECTION_AUTH_STATUS_VALID) { | |
| // | |
| // OR in the parent stream's aggregate status. | |
| // | |
| AuthenticationStatus |= Stream->AuthenticationStatus & EFI_AGGREGATE_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)) { | |
| FreePool (*ChildNode); | |
| FreePool (NewStreamBuffer); | |
| return Status; | |
| } | |
| } else { | |
| // | |
| // There's no GUIDed section extraction protocol available. | |
| // | |
| if ((GuidedSectionAttributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == EFI_GUIDED_SECTION_PROCESSING_REQUIRED) { | |
| // | |
| // If the section REQUIRES an extraction protocol, register for RPN | |
| // when the required GUIDed extraction protocol becomes available. | |
| // | |
| AuthenticationStatus = 0; | |
| 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) { | |
| // | |
| // The local status of the new stream is contained in | |
| // AuthenticaionStatus. This value needs to be ORed into the | |
| // Aggregate bits also... | |
| // | |
| // | |
| // Clear out and initialize the local status | |
| // | |
| AuthenticationStatus &= ~EFI_LOCAL_AUTH_STATUS_ALL; | |
| AuthenticationStatus |= EFI_LOCAL_AUTH_STATUS_IMAGE_SIGNED | EFI_LOCAL_AUTH_STATUS_NOT_TESTED; | |
| // | |
| // OR local status into aggregate status | |
| // | |
| AuthenticationStatus |= AuthenticationStatus >> 16; | |
| } | |
| 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)) { | |
| FreePool (Node); | |
| return Status; | |
| } | |
| } | |
| } | |
| if ((AuthenticationStatus & EFI_LOCAL_AUTH_STATUS_ALL) == | |
| (EFI_LOCAL_AUTH_STATUS_IMAGE_SIGNED | EFI_LOCAL_AUTH_STATUS_NOT_TESTED)) { | |
| // | |
| // Need to register for RPN for when the required GUIDed extraction | |
| // protocol becomes available. This will enable us to refresh the | |
| // AuthenticationStatus cached in the Stream if it's ever requested | |
| // again. | |
| // | |
| CreateGuidedExtractionRpnEvent (Stream, Node); | |
| } | |
| 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 to deal with recursions. | |
| @param SectionDefinitionGuid Guid of section definition | |
| @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. | |
| @retval 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 | |
| **/ | |
| EFI_STATUS | |
| FindChildNode ( | |
| IN FRAMEWORK_SECTION_STREAM_NODE *SourceStream, | |
| IN EFI_SECTION_TYPE SearchType, | |
| IN OUT UINTN *SectionInstance, | |
| IN EFI_GUID *SectionDefinitionGuid, | |
| OUT FRAMEWORK_SECTION_CHILD_NODE **FoundChild, | |
| OUT FRAMEWORK_SECTION_STREAM_NODE **FoundStream, | |
| OUT UINT32 *AuthenticationStatus | |
| ) | |
| { | |
| FRAMEWORK_SECTION_CHILD_NODE *CurrentChildNode; | |
| FRAMEWORK_SECTION_CHILD_NODE *RecursedChildNode; | |
| FRAMEWORK_SECTION_STREAM_NODE *RecursedFoundStream; | |
| UINT32 NextChildOffset; | |
| EFI_STATUS ErrorStatus; | |
| EFI_STATUS Status; | |
| 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; | |
| } | |
| } | |
| if (CurrentChildNode->EncapsulatedStreamHandle != NULL_STREAM_HANDLE) { | |
| // | |
| // If the current node is an encapsulating node, recurse into it... | |
| // | |
| Status = FindChildNode ( | |
| (FRAMEWORK_SECTION_STREAM_NODE *)CurrentChildNode->EncapsulatedStreamHandle, | |
| SearchType, | |
| SectionInstance, | |
| SectionDefinitionGuid, | |
| &RecursedChildNode, | |
| &RecursedFoundStream, | |
| AuthenticationStatus | |
| ); | |
| // | |
| // If the status is not EFI_SUCCESS, just save the error code and continue | |
| // to find the request child node in the rest stream. | |
| // | |
| if (*SectionInstance == 0) { | |
| ASSERT_EFI_ERROR (Status); | |
| *FoundChild = RecursedChildNode; | |
| *FoundStream = RecursedFoundStream; | |
| return EFI_SUCCESS; | |
| } else { | |
| 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 FRAMEWORK_SECTION_STREAM_NODE **FoundStream | |
| ) | |
| { | |
| FRAMEWORK_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 This Pointer to SEP instance. | |
| @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 requried 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 Indicates the authentication status of the retrieved | |
| section. | |
| @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 contents are section contents | |
| are truncated. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| GetSection ( | |
| IN EFI_SECTION_EXTRACTION_PROTOCOL *This, | |
| 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 | |
| ) | |
| { | |
| FRAMEWORK_SECTION_STREAM_NODE *StreamNode; | |
| EFI_TPL OldTpl; | |
| EFI_STATUS Status; | |
| FRAMEWORK_SECTION_CHILD_NODE *ChildNode; | |
| FRAMEWORK_SECTION_STREAM_NODE *ChildStreamNode; | |
| UINTN CopySize; | |
| UINT32 ExtractedAuthenticationStatus; | |
| UINTN Instance; | |
| UINT8 *CopyBuffer; | |
| UINTN SectionSize; | |
| EFI_COMMON_SECTION_HEADER *Section; | |
| OldTpl = gBS->RaiseTPL (TPL_NOTIFY); | |
| Instance = SectionInstance + 1; | |
| ChildStreamNode = NULL; | |
| // | |
| // 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, | |
| &ChildNode, | |
| &ChildStreamNode, | |
| &ExtractedAuthenticationStatus | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| goto GetSection_Done; | |
| } | |
| ASSERT (ChildNode != NULL); | |
| ASSERT (ChildStreamNode != NULL); | |
| Section = (EFI_COMMON_SECTION_HEADER *) (ChildStreamNode->StreamBuffer + ChildNode->OffsetInStream); | |
| if (IS_SECTION2 (Section)) { | |
| 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: | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } | |
| /** | |
| Worker function. Destructor for child nodes. | |
| @param ChildNode Indicates the node to destroy | |
| **/ | |
| VOID | |
| FreeChildNode ( | |
| IN FRAMEWORK_SECTION_CHILD_NODE *ChildNode | |
| ) | |
| { | |
| ASSERT (ChildNode->Signature == FRAMEWORK_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 (&mSectionExtraction, ChildNode->EncapsulatedStreamHandle); | |
| } | |
| if (ChildNode->Event != NULL) { | |
| gBS->CloseEvent (ChildNode->Event); | |
| } | |
| // | |
| // Last, free the child node itself | |
| // | |
| FreePool (ChildNode); | |
| } | |
| /** | |
| SEP member function. Deletes an existing section stream | |
| @param This Indicates the calling context. | |
| @param StreamHandleToClose Indicates the stream to close | |
| @retval EFI_SUCCESS Section stream was closed 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 | |
| CloseSectionStream ( | |
| IN EFI_SECTION_EXTRACTION_PROTOCOL *This, | |
| IN UINTN StreamHandleToClose | |
| ) | |
| { | |
| FRAMEWORK_SECTION_STREAM_NODE *StreamNode; | |
| EFI_TPL OldTpl; | |
| EFI_STATUS Status; | |
| LIST_ENTRY *Link; | |
| FRAMEWORK_SECTION_CHILD_NODE *ChildNode; | |
| OldTpl = gBS->RaiseTPL (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); | |
| } | |
| FreePool (StreamNode->StreamBuffer); | |
| FreePool (StreamNode); | |
| Status = EFI_SUCCESS; | |
| } else { | |
| Status = EFI_INVALID_PARAMETER; | |
| } | |
| gBS->RestoreTPL (OldTpl); | |
| return Status; | |
| } |