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