mirror of https://github.com/acidanthera/audk.git
1457 lines
54 KiB
C
1457 lines
54 KiB
C
/** @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;
|
|
}
|