https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Hao Wu <hao.a.wu@intel.com> Reviewed-by: Jian J Wang <jian.j.wang@intel.com>
		
			
				
	
	
		
			409 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			409 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Implement defer image load services for user identification in UEFI2.2.
 | 
						|
 | 
						|
Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
 | 
						|
SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
 | 
						|
**/
 | 
						|
#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;
 | 
						|
 | 
						|
BOOLEAN                          mImageLoadedAfterEndOfDxe   = FALSE;
 | 
						|
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;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  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 ();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  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) {
 | 
						|
    mImageLoadedAfterEndOfDxe = TRUE;
 | 
						|
    //
 | 
						|
    // 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;
 | 
						|
  VOID                                 *Registration;
 | 
						|
 | 
						|
  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);
 | 
						|
 | 
						|
  EfiCreateProtocolNotifyEvent (
 | 
						|
    &gEfiDxeSmmReadyToLockProtocolGuid,
 | 
						|
    TPL_CALLBACK,
 | 
						|
    DxeSmmReadyToLock,
 | 
						|
    NULL,
 | 
						|
    &Registration
 | 
						|
    );
 | 
						|
}
 |