--- /dev/null
+/** @file\r
+ Implement defer image load services for user identification in UEFI2.2.\r
+\r
+Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+#include "Defer3rdPartyImageLoad.h"\r
+\r
+//\r
+// The structure to save the deferred 3rd party image information.\r
+//\r
+typedef struct {\r
+ EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;\r
+ BOOLEAN BootOption;\r
+ BOOLEAN Loaded;\r
+} DEFERRED_3RD_PARTY_IMAGE_INFO;\r
+\r
+//\r
+// The table to save the deferred 3rd party image item.\r
+//\r
+typedef struct {\r
+ UINTN Count; ///< deferred 3rd party image count\r
+ DEFERRED_3RD_PARTY_IMAGE_INFO *ImageInfo; ///< deferred 3rd party image item\r
+} DEFERRED_3RD_PARTY_IMAGE_TABLE;\r
+\r
+BOOLEAN mEndOfDxe = FALSE;\r
+DEFERRED_3RD_PARTY_IMAGE_TABLE mDeferred3rdPartyImage = {\r
+ 0, // Deferred image count\r
+ NULL // The deferred image info\r
+};\r
+\r
+EFI_DEFERRED_IMAGE_LOAD_PROTOCOL mDeferredImageLoad = {\r
+ GetDefferedImageInfo\r
+};\r
+\r
+/**\r
+ Return whether the file comes from FV.\r
+\r
+ @param[in] File This is a pointer to the device path of the file\r
+ that is being dispatched.\r
+\r
+ @retval TRUE File comes from FV.\r
+ @retval FALSE File doesn't come from FV.\r
+**/\r
+BOOLEAN\r
+FileFromFv (\r
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *File\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE DeviceHandle;\r
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
+\r
+ //\r
+ // First check to see if File is from a Firmware Volume\r
+ //\r
+ DeviceHandle = NULL;\r
+ TempDevicePath = (EFI_DEVICE_PATH_PROTOCOL *) File;\r
+ Status = gBS->LocateDevicePath (\r
+ &gEfiFirmwareVolume2ProtocolGuid,\r
+ &TempDevicePath,\r
+ &DeviceHandle\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ Status = gBS->OpenProtocol (\r
+ DeviceHandle,\r
+ &gEfiFirmwareVolume2ProtocolGuid,\r
+ NULL,\r
+ NULL,\r
+ NULL,\r
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Find the deferred image which matches the device path.\r
+\r
+ @param[in] ImageDevicePath A pointer to the device path of a image.\r
+ @param[in] BootOption Whether the image is a boot option.\r
+\r
+ @return Pointer to the found deferred image or NULL if not found.\r
+**/\r
+DEFERRED_3RD_PARTY_IMAGE_INFO *\r
+LookupImage (\r
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,\r
+ IN BOOLEAN BootOption\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN DevicePathSize;\r
+\r
+ DevicePathSize = GetDevicePathSize (ImageDevicePath);\r
+\r
+ for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {\r
+ if (CompareMem (ImageDevicePath, mDeferred3rdPartyImage.ImageInfo[Index].ImageDevicePath, DevicePathSize) == 0) {\r
+ ASSERT (mDeferred3rdPartyImage.ImageInfo[Index].BootOption == BootOption);\r
+ return &mDeferred3rdPartyImage.ImageInfo[Index];\r
+ }\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Add the image info to a deferred image list.\r
+\r
+ @param[in] ImageDevicePath A pointer to the device path of a image.\r
+ @param[in] BootOption Whether the image is a boot option.\r
+\r
+**/\r
+VOID\r
+QueueImage (\r
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath,\r
+ IN BOOLEAN BootOption\r
+ )\r
+{\r
+ DEFERRED_3RD_PARTY_IMAGE_INFO *ImageInfo;\r
+\r
+ //\r
+ // Expand memory for the new deferred image.\r
+ //\r
+ ImageInfo = ReallocatePool (\r
+ mDeferred3rdPartyImage.Count * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),\r
+ (mDeferred3rdPartyImage.Count + 1) * sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO),\r
+ mDeferred3rdPartyImage.ImageInfo\r
+ );\r
+ if (ImageInfo == NULL) {\r
+ return;\r
+ }\r
+ mDeferred3rdPartyImage.ImageInfo = ImageInfo;\r
+\r
+ //\r
+ // Save the deferred image information.\r
+ //\r
+ ImageInfo = &mDeferred3rdPartyImage.ImageInfo[mDeferred3rdPartyImage.Count];\r
+ ImageInfo->ImageDevicePath = DuplicateDevicePath (ImageDevicePath);\r
+ if (ImageInfo->ImageDevicePath == NULL) {\r
+ return;\r
+ }\r
+ ImageInfo->BootOption = BootOption;\r
+ ImageInfo->Loaded = FALSE;\r
+ mDeferred3rdPartyImage.Count++;\r
+}\r
+\r
+\r
+/**\r
+ Returns information about a deferred image.\r
+\r
+ This function returns information about a single deferred image. The deferred images are\r
+ numbered consecutively, starting with 0. If there is no image which corresponds to\r
+ ImageIndex, then EFI_NOT_FOUND is returned. All deferred images may be returned by\r
+ iteratively calling this function until EFI_NOT_FOUND is returned.\r
+ Image may be NULL and ImageSize set to 0 if the decision to defer execution was made\r
+ because of the location of the executable image, rather than its actual contents.\r
+\r
+ @param[in] This Points to this instance of the EFI_DEFERRED_IMAGE_LOAD_PROTOCOL.\r
+ @param[in] ImageIndex Zero-based index of the deferred index.\r
+ @param[out] ImageDevicePath On return, points to a pointer to the device path of the image.\r
+ The device path should not be freed by the caller.\r
+ @param[out] Image On return, points to the first byte of the image or NULL if the\r
+ image is not available. The image should not be freed by the caller\r
+ unless LoadImage() has been successfully called.\r
+ @param[out] ImageSize On return, the size of the image, or 0 if the image is not available.\r
+ @param[out] BootOption On return, points to TRUE if the image was intended as a boot option\r
+ or FALSE if it was not intended as a boot option.\r
+\r
+ @retval EFI_SUCCESS Image information returned successfully.\r
+ @retval EFI_NOT_FOUND ImageIndex does not refer to a valid image.\r
+ @retval EFI_INVALID_PARAMETER ImageDevicePath is NULL or Image is NULL or ImageSize is NULL or\r
+ BootOption is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+GetDefferedImageInfo (\r
+ IN EFI_DEFERRED_IMAGE_LOAD_PROTOCOL *This,\r
+ IN UINTN ImageIndex,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **ImageDevicePath,\r
+ OUT VOID **Image,\r
+ OUT UINTN *ImageSize,\r
+ OUT BOOLEAN *BootOption\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN NewCount;\r
+\r
+ if ((This == NULL) || (ImageSize == NULL) || (Image == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if ((ImageDevicePath == NULL) || (BootOption == NULL)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ //\r
+ // Remove the loaded images from the defer list in the first call.\r
+ //\r
+ if (ImageIndex == 0) {\r
+ NewCount = 0;\r
+ for (Index = 0; Index < mDeferred3rdPartyImage.Count; Index++) {\r
+ if (!mDeferred3rdPartyImage.ImageInfo[Index].Loaded) {\r
+ CopyMem (\r
+ &mDeferred3rdPartyImage.ImageInfo[NewCount],\r
+ &mDeferred3rdPartyImage.ImageInfo[Index],\r
+ sizeof (DEFERRED_3RD_PARTY_IMAGE_INFO)\r
+ );\r
+ NewCount++;\r
+ }\r
+ }\r
+\r
+ mDeferred3rdPartyImage.Count = NewCount;\r
+ }\r
+\r
+ if (ImageIndex >= mDeferred3rdPartyImage.Count) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Get the request deferred image.\r
+ //\r
+ *ImageDevicePath = mDeferred3rdPartyImage.ImageInfo[ImageIndex].ImageDevicePath;\r
+ *BootOption = mDeferred3rdPartyImage.ImageInfo[ImageIndex].BootOption;\r
+ *Image = NULL;\r
+ *ImageSize = 0;\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Callback function executed when the EndOfDxe event group is signaled.\r
+\r
+ @param[in] Event Event whose notification function is being invoked.\r
+ @param[in] Context The pointer to the notification function's context, which\r
+ is implementation-dependent.\r
+**/\r
+VOID\r
+EFIAPI\r
+EndOfDxe (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ mEndOfDxe = TRUE;\r
+}\r
+\r
+/**\r
+ Defer the 3rd party image load and installs Deferred Image Load Protocol.\r
+\r
+ @param[in] File This is a pointer to the device path of the file that\r
+ is being dispatched. This will optionally be used for\r
+ logging.\r
+ @param[in] BootPolicy A boot policy that was used to call LoadImage() UEFI service.\r
+\r
+ @retval EFI_SUCCESS The file is not 3rd party image and can be loaded immediately.\r
+ @retval EFI_ACCESS_DENIED The file is 3rd party image and needs deferred.\r
+**/\r
+EFI_STATUS\r
+Defer3rdPartyImageLoad (\r
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *File,\r
+ IN BOOLEAN BootPolicy\r
+ )\r
+{\r
+ DEFERRED_3RD_PARTY_IMAGE_INFO *ImageInfo;\r
+\r
+ //\r
+ // Ignore if File is NULL.\r
+ //\r
+ if (File == NULL) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (FileFromFv (File)) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ ImageInfo = LookupImage (File, BootPolicy);\r
+\r
+ DEBUG_CODE (\r
+ CHAR16 *DevicePathStr;\r
+ DevicePathStr = ConvertDevicePathToText (File, FALSE, FALSE);\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "[Security] 3rd party image[%p] %s EndOfDxe: %s.\n", ImageInfo,\r
+ mEndOfDxe ? L"can be loaded after": L"is deferred to load before",\r
+ DevicePathStr\r
+ ));\r
+ if (DevicePathStr != NULL) {\r
+ FreePool (DevicePathStr);\r
+ }\r
+ );\r
+\r
+ if (mEndOfDxe) {\r
+ //\r
+ // The image might be first time loaded after EndOfDxe,\r
+ // So ImageInfo can be NULL.\r
+ //\r
+ if (ImageInfo != NULL) {\r
+ ImageInfo->Loaded = TRUE;\r
+ }\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ //\r
+ // The image might be second time loaded before EndOfDxe,\r
+ // So ImageInfo can be non-NULL.\r
+ //\r
+ if (ImageInfo == NULL) {\r
+ QueueImage (File, BootPolicy);\r
+ }\r
+ return EFI_ACCESS_DENIED;\r
+ }\r
+}\r
+\r
+/**\r
+ Installs DeferredImageLoad Protocol and listens EndOfDxe event.\r
+**/\r
+VOID\r
+Defer3rdPartyImageLoadInitialize (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+ EFI_EVENT Event;\r
+\r
+ Handle = NULL;\r
+ Status = gBS->InstallMultipleProtocolInterfaces (\r
+ &Handle,\r
+ &gEfiDeferredImageLoadProtocolGuid,\r
+ &mDeferredImageLoad,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ Status = gBS->CreateEventEx (\r
+ EVT_NOTIFY_SIGNAL,\r
+ TPL_CALLBACK,\r
+ EndOfDxe,\r
+ NULL,\r
+ &gEfiEndOfDxeEventGroupGuid,\r
+ &Event\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+}\r