2016-10-31 09:31:44 +01:00
|
|
|
/** @file
|
|
|
|
Implement defer image load services for user identification in UEFI2.2.
|
|
|
|
|
|
|
|
Copyright (c) 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 "Defer3rdPartyImageLoad.h"
|
|
|
|
|
|
|
|
//
|
|
|
|
// The structure to save the deferred 3rd party image information.
|
|
|
|
//
|
|
|
|
typedef struct {
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
|
|
|
|
BOOLEAN BootOption;
|
|
|
|
BOOLEAN Loaded;
|
|
|
|
} DEFERRED_3RD_PARTY_IMAGE_INFO;
|
|
|
|
|
|
|
|
//
|
|
|
|
// The table to save the deferred 3rd party image item.
|
|
|
|
//
|
|
|
|
typedef struct {
|
|
|
|
UINTN Count; ///< deferred 3rd party image count
|
|
|
|
DEFERRED_3RD_PARTY_IMAGE_INFO *ImageInfo; ///< deferred 3rd party image item
|
|
|
|
} DEFERRED_3RD_PARTY_IMAGE_TABLE;
|
|
|
|
|
2016-11-01 09:42:21 +01:00
|
|
|
BOOLEAN mImageLoadedAfterEndOfDxe = FALSE;
|
2016-10-31 09:31:44 +01:00
|
|
|
BOOLEAN mEndOfDxe = FALSE;
|
|
|
|
DEFERRED_3RD_PARTY_IMAGE_TABLE mDeferred3rdPartyImage = {
|
|
|
|
0, // Deferred image count
|
|
|
|
NULL // The deferred image info
|
|
|
|
};
|
|
|
|
|
|
|
|
EFI_DEFERRED_IMAGE_LOAD_PROTOCOL mDeferredImageLoad = {
|
|
|
|
GetDefferedImageInfo
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
Return whether the file comes from FV.
|
|
|
|
|
|
|
|
@param[in] File This is a pointer to the device path of the file
|
|
|
|
that is being dispatched.
|
|
|
|
|
|
|
|
@retval TRUE File comes from FV.
|
|
|
|
@retval FALSE File doesn't come from FV.
|
|
|
|
**/
|
|
|
|
BOOLEAN
|
|
|
|
FileFromFv (
|
|
|
|
IN CONST EFI_DEVICE_PATH_PROTOCOL *File
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_HANDLE DeviceHandle;
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
|
|
|
|
|
|
|
|
//
|
|
|
|
// First check to see if File is from a Firmware Volume
|
|
|
|
//
|
|
|
|
DeviceHandle = NULL;
|
|
|
|
TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;
|
|
|
|
Status = gBS->LocateDevicePath (
|
|
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
|
|
&TempDevicePath,
|
|
|
|
&DeviceHandle
|
|
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
|
|
Status = gBS->OpenProtocol (
|
|
|
|
DeviceHandle,
|
|
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
NULL,
|
|
|
|
EFI_OPEN_PROTOCOL_TEST_PROTOCOL
|
|
|
|
);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Find the deferred image which matches the device path.
|
|
|
|
|
|
|
|
@param[in] ImageDevicePath A pointer to the device path of a image.
|
|
|
|
@param[in] BootOption Whether the image is a boot option.
|
|
|
|
|
|
|
|
@return Pointer to the found deferred image or NULL if not found.
|
|
|
|
**/
|
|
|
|
DEFERRED_3RD_PARTY_IMAGE_INFO *
|
|
|
|
LookupImage (
|
|
|
|
IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,
|
|
|
|
IN BOOLEAN BootOption
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINTN Index;
|
|
|
|
UINTN DevicePathSize;
|
|
|
|
|
|
|
|
DevicePathSize = GetDevicePathSize (ImageDevicePath);
|
|
|
|
|
|
|
|
for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {
|
|
|
|
if (CompareMem (ImageDevicePath, mDeferred3rdPartyImage.ImageInfo[Index].ImageDevicePath, DevicePathSize) == 0) {
|
|
|
|
ASSERT (mDeferred3rdPartyImage.ImageInfo[Index].BootOption == BootOption);
|
|
|
|
return &mDeferred3rdPartyImage.ImageInfo[Index];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Add the image info to a deferred image list.
|
|
|
|
|
|
|
|
@param[in] ImageDevicePath A pointer to the device path of a image.
|
|
|
|
@param[in] BootOption Whether the image is a boot option.
|
|
|
|
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
QueueImage (
|
|
|
|
IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,
|
|
|
|
IN BOOLEAN BootOption
|
|
|
|
)
|
|
|
|
{
|
|
|
|
DEFERRED_3RD_PARTY_IMAGE_INFO *ImageInfo;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Expand memory for the new deferred image.
|
|
|
|
//
|
|
|
|
ImageInfo = ReallocatePool (
|
|
|
|
mDeferred3rdPartyImage.Count * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),
|
|
|
|
(mDeferred3rdPartyImage.Count + 1) * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),
|
|
|
|
mDeferred3rdPartyImage.ImageInfo
|
|
|
|
);
|
|
|
|
if (ImageInfo == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mDeferred3rdPartyImage.ImageInfo = ImageInfo;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Save the deferred image information.
|
|
|
|
//
|
|
|
|
ImageInfo = &mDeferred3rdPartyImage.ImageInfo[mDeferred3rdPartyImage.Count];
|
|
|
|
ImageInfo->ImageDevicePath = DuplicateDevicePath (ImageDevicePath);
|
|
|
|
if (ImageInfo->ImageDevicePath == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ImageInfo->BootOption = BootOption;
|
|
|
|
ImageInfo->Loaded = FALSE;
|
|
|
|
mDeferred3rdPartyImage.Count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
Returns information about a deferred image.
|
|
|
|
|
|
|
|
This function returns information about a single deferred image. The deferred images are
|
|
|
|
numbered consecutively, starting with 0. If there is no image which corresponds to
|
|
|
|
ImageIndex, then EFI_NOT_FOUND is returned. All deferred images may be returned by
|
|
|
|
iteratively calling this function until EFI_NOT_FOUND is returned.
|
|
|
|
Image may be NULL and ImageSize set to 0 if the decision to defer execution was made
|
|
|
|
because of the location of the executable image, rather than its actual contents.
|
|
|
|
|
|
|
|
@param[in] This Points to this instance of the EFI_DEFERRED_IMAGE_LOAD_PROTOCOL.
|
|
|
|
@param[in] ImageIndex Zero-based index of the deferred index.
|
|
|
|
@param[out] ImageDevicePath On return, points to a pointer to the device path of the image.
|
|
|
|
The device path should not be freed by the caller.
|
|
|
|
@param[out] Image On return, points to the first byte of the image or NULL if the
|
|
|
|
image is not available. The image should not be freed by the caller
|
|
|
|
unless LoadImage() has been successfully called.
|
|
|
|
@param[out] ImageSize On return, the size of the image, or 0 if the image is not available.
|
|
|
|
@param[out] BootOption On return, points to TRUE if the image was intended as a boot option
|
|
|
|
or FALSE if it was not intended as a boot option.
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS Image information returned successfully.
|
|
|
|
@retval EFI_NOT_FOUND ImageIndex does not refer to a valid image.
|
|
|
|
@retval EFI_INVALID_PARAMETER ImageDevicePath is NULL or Image is NULL or ImageSize is NULL or
|
|
|
|
BootOption is NULL.
|
|
|
|
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
GetDefferedImageInfo (
|
|
|
|
IN EFI_DEFERRED_IMAGE_LOAD_PROTOCOL *This,
|
|
|
|
IN UINTN ImageIndex,
|
|
|
|
OUT EFI_DEVICE_PATH_PROTOCOL **ImageDevicePath,
|
|
|
|
OUT VOID **Image,
|
|
|
|
OUT UINTN *ImageSize,
|
|
|
|
OUT BOOLEAN *BootOption
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINTN Index;
|
|
|
|
UINTN NewCount;
|
|
|
|
|
|
|
|
if ((This == NULL) || (ImageSize == NULL) || (Image == NULL)) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ImageDevicePath == NULL) || (BootOption == NULL)) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Remove the loaded images from the defer list in the first call.
|
|
|
|
//
|
|
|
|
if (ImageIndex == 0) {
|
|
|
|
NewCount = 0;
|
|
|
|
for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {
|
|
|
|
if (!mDeferred3rdPartyImage.ImageInfo[Index].Loaded) {
|
|
|
|
CopyMem (
|
|
|
|
&mDeferred3rdPartyImage.ImageInfo[NewCount],
|
|
|
|
&mDeferred3rdPartyImage.ImageInfo[Index],
|
|
|
|
sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO)
|
|
|
|
);
|
|
|
|
NewCount++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mDeferred3rdPartyImage.Count = NewCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ImageIndex >= mDeferred3rdPartyImage.Count) {
|
|
|
|
return EFI_NOT_FOUND;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Get the request deferred image.
|
|
|
|
//
|
|
|
|
*ImageDevicePath = mDeferred3rdPartyImage.ImageInfo[ImageIndex].ImageDevicePath;
|
|
|
|
*BootOption = mDeferred3rdPartyImage.ImageInfo[ImageIndex].BootOption;
|
|
|
|
*Image = NULL;
|
|
|
|
*ImageSize = 0;
|
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Callback function executed when the EndOfDxe event group is signaled.
|
|
|
|
|
|
|
|
@param[in] Event Event whose notification function is being invoked.
|
|
|
|
@param[in] Context The pointer to the notification function's context, which
|
|
|
|
is implementation-dependent.
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
EFIAPI
|
|
|
|
EndOfDxe (
|
|
|
|
IN EFI_EVENT Event,
|
|
|
|
IN VOID *Context
|
|
|
|
)
|
|
|
|
{
|
|
|
|
mEndOfDxe = TRUE;
|
|
|
|
}
|
|
|
|
|
2016-11-01 09:42:21 +01:00
|
|
|
/**
|
|
|
|
Event notification for gEfiDxeSmmReadyToLockProtocolGuid event.
|
|
|
|
|
|
|
|
This function reports failure if any deferred image is loaded before
|
|
|
|
this callback.
|
|
|
|
Platform should publish ReadyToLock protocol immediately after signaling
|
|
|
|
of the End of DXE Event.
|
|
|
|
|
|
|
|
@param Event The Event that is being processed, not used.
|
|
|
|
@param Context Event Context, not used.
|
|
|
|
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
EFIAPI
|
|
|
|
DxeSmmReadyToLock (
|
|
|
|
IN EFI_EVENT Event,
|
|
|
|
IN VOID *Context
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
VOID *Interface;
|
|
|
|
|
|
|
|
Status = gBS->LocateProtocol (&gEfiDxeSmmReadyToLockProtocolGuid, NULL, &Interface);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gBS->CloseEvent (Event);
|
|
|
|
|
|
|
|
if (mImageLoadedAfterEndOfDxe) {
|
|
|
|
//
|
|
|
|
// Platform should not dispatch the 3rd party images after signaling EndOfDxe event
|
|
|
|
// but before publishing DxeSmmReadyToLock protocol.
|
|
|
|
//
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"[Security] 3rd party images must be dispatched after DxeSmmReadyToLock Protocol installation!\n"
|
|
|
|
));
|
|
|
|
REPORT_STATUS_CODE (
|
|
|
|
EFI_ERROR_CODE | EFI_ERROR_UNRECOVERED,
|
|
|
|
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_EC_ILLEGAL_SOFTWARE_STATE)
|
|
|
|
);
|
|
|
|
ASSERT (FALSE);
|
|
|
|
CpuDeadLoop ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-31 09:31:44 +01:00
|
|
|
/**
|
|
|
|
Defer the 3rd party image load and installs Deferred Image Load Protocol.
|
|
|
|
|
|
|
|
@param[in] File This is a pointer to the device path of the file that
|
|
|
|
is being dispatched. This will optionally be used for
|
|
|
|
logging.
|
|
|
|
@param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service.
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The file is not 3rd party image and can be loaded immediately.
|
|
|
|
@retval EFI_ACCESS_DENIED The file is 3rd party image and needs deferred.
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
Defer3rdPartyImageLoad (
|
|
|
|
IN CONST EFI_DEVICE_PATH_PROTOCOL *File,
|
|
|
|
IN BOOLEAN BootPolicy
|
|
|
|
)
|
|
|
|
{
|
|
|
|
DEFERRED_3RD_PARTY_IMAGE_INFO *ImageInfo;
|
|
|
|
|
|
|
|
//
|
|
|
|
// Ignore if File is NULL.
|
|
|
|
//
|
|
|
|
if (File == NULL) {
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (FileFromFv (File)) {
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
ImageInfo = LookupImage (File, BootPolicy);
|
|
|
|
|
|
|
|
DEBUG_CODE (
|
|
|
|
CHAR16 *DevicePathStr;
|
|
|
|
DevicePathStr = ConvertDevicePathToText (File, FALSE, FALSE);
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_INFO,
|
|
|
|
"[Security] 3rd party image[%p] %s EndOfDxe: %s.\n", ImageInfo,
|
|
|
|
mEndOfDxe ? L"can be loaded after": L"is deferred to load before",
|
|
|
|
DevicePathStr
|
|
|
|
));
|
|
|
|
if (DevicePathStr != NULL) {
|
|
|
|
FreePool (DevicePathStr);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
if (mEndOfDxe) {
|
2016-11-01 09:42:21 +01:00
|
|
|
mImageLoadedAfterEndOfDxe = TRUE;
|
2016-10-31 09:31:44 +01:00
|
|
|
//
|
|
|
|
// The image might be first time loaded after EndOfDxe,
|
|
|
|
// So ImageInfo can be NULL.
|
|
|
|
//
|
|
|
|
if (ImageInfo != NULL) {
|
|
|
|
ImageInfo->Loaded = TRUE;
|
|
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
|
|
// The image might be second time loaded before EndOfDxe,
|
|
|
|
// So ImageInfo can be non-NULL.
|
|
|
|
//
|
|
|
|
if (ImageInfo == NULL) {
|
|
|
|
QueueImage (File, BootPolicy);
|
|
|
|
}
|
|
|
|
return EFI_ACCESS_DENIED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
Installs DeferredImageLoad Protocol and listens EndOfDxe event.
|
|
|
|
**/
|
|
|
|
VOID
|
|
|
|
Defer3rdPartyImageLoadInitialize (
|
|
|
|
VOID
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
EFI_HANDLE Handle;
|
|
|
|
EFI_EVENT Event;
|
2016-11-01 09:42:21 +01:00
|
|
|
VOID *Registration;
|
2016-10-31 09:31:44 +01:00
|
|
|
|
|
|
|
Handle = NULL;
|
|
|
|
Status = gBS->InstallMultipleProtocolInterfaces (
|
|
|
|
&Handle,
|
|
|
|
&gEfiDeferredImageLoadProtocolGuid,
|
|
|
|
&mDeferredImageLoad,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
|
|
|
|
Status = gBS->CreateEventEx (
|
|
|
|
EVT_NOTIFY_SIGNAL,
|
|
|
|
TPL_CALLBACK,
|
|
|
|
EndOfDxe,
|
|
|
|
NULL,
|
|
|
|
&gEfiEndOfDxeEventGroupGuid,
|
|
|
|
&Event
|
|
|
|
);
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
2016-11-01 09:42:21 +01:00
|
|
|
|
|
|
|
EfiCreateProtocolNotifyEvent (
|
|
|
|
&gEfiDxeSmmReadyToLockProtocolGuid,
|
|
|
|
TPL_CALLBACK,
|
|
|
|
DxeSmmReadyToLock,
|
|
|
|
NULL,
|
|
|
|
&Registration
|
|
|
|
);
|
2016-10-31 09:31:44 +01:00
|
|
|
}
|