diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.c | 421 |
1 files changed, 421 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.c new file mode 100644 index 00000000..345bf066 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.c @@ -0,0 +1,421 @@ +/** @file + Produce Load File Protocol for UEFI Applications in Firmware Volumes + + Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR> + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include <PiDxe.h> + +#include <Guid/LzmaDecompress.h> +#include <Protocol/LoadFile.h> +#include <Protocol/DevicePath.h> +#include <Protocol/FirmwareVolume2.h> +#include <Protocol/FirmwareVolumeBlock.h> + +#include <Library/DebugLib.h> +#include <Library/UefiLib.h> +#include <Library/BaseMemoryLib.h> +#include <Library/UefiDriverEntryPoint.h> +#include <Library/UefiBootServicesTableLib.h> +#include <Library/MemoryAllocationLib.h> +#include <Library/DevicePathLib.h> + +#define LOAD_FILE_ON_FV2_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('l', 'f', 'f', 'v') + +typedef struct { + UINTN Signature; + EFI_LOAD_FILE_PROTOCOL LoadFile; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + EFI_GUID NameGuid; + LIST_ENTRY Link; +} LOAD_FILE_ON_FV2_PRIVATE_DATA; + +#define LOAD_FILE_ON_FV2_PRIVATE_DATA_FROM_THIS(a) CR (a, LOAD_FILE_ON_FV2_PRIVATE_DATA, LoadFile, LOAD_FILE_ON_FV2_PRIVATE_DATA_SIGNATURE) +#define LOAD_FILE_ON_FV2_PRIVATE_DATA_FROM_LINK(a) CR (a, LOAD_FILE_ON_FV2_PRIVATE_DATA, Link, LOAD_FILE_ON_FV2_PRIVATE_DATA_SIGNATURE) + +VOID *mFvRegistration; +LIST_ENTRY mPrivateDataList; + +/** + Causes the driver to load a specified file from firmware volume. + + @param[in] This Protocol instance pointer. + @param[in] FilePath The device specific path of the file to load. + @param[in] BootPolicy If TRUE, indicates that the request originates from the + boot manager is attempting to load FilePath as a boot + selection. If FALSE, then FilePath must match an exact file + to be loaded. + @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return + code of EFI_SUCCESS, the amount of data transferred to + Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL, + the size of Buffer required to retrieve the requested file. + @param[in] Buffer The memory buffer to transfer the file to. IF Buffer is NULL, + then no the size of the requested file is returned in + BufferSize. + + @retval EFI_SUCCESS The file was loaded. + @retval EFI_UNSUPPORTED The device does not support the provided BootPolicy. + @retval EFI_INVALID_PARAMETER FilePath is not a valid device path, or + BufferSize is NULL. + @retval EFI_DEVICE_ERROR The file was not loaded due to a device error. + @retval EFI_NOT_FOUND The file was not found. + @retval EFI_OUT_OF_RESOURCES An allocation failure occurred. + @retval EFI_ACCESS_DENIED The firmware volume is configured to + disallow reads. +**/ +EFI_STATUS +EFIAPI +LoadFileOnFv2LoadFile ( + IN EFI_LOAD_FILE_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + IN VOID *Buffer OPTIONAL + ) +{ + EFI_STATUS Status; + LOAD_FILE_ON_FV2_PRIVATE_DATA *Private; + VOID *Pe32Buffer; + UINTN Pe32BufferSize; + UINT32 AuthenticationStatus; + + if (This == NULL || BufferSize == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Only support BootPolicy + // + if (!BootPolicy) { + return EFI_UNSUPPORTED; + } + + // + // Get private context data + // + Private = LOAD_FILE_ON_FV2_PRIVATE_DATA_FROM_THIS (This); + + // + // Determine the size of the PE32 section + // + Pe32Buffer = NULL; + Pe32BufferSize = 0; + Status = Private->Fv->ReadSection ( + Private->Fv, + &Private->NameGuid, + EFI_SECTION_PE32, + 0, + &Pe32Buffer, + &Pe32BufferSize, + &AuthenticationStatus + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // If the buffer passed in is not large enough, return the size of the required + // buffer in BufferSize and return EFI_BUFFER_TOO_SMALL + // + if (*BufferSize < Pe32BufferSize || Buffer == NULL) { + *BufferSize = Pe32BufferSize; + return EFI_BUFFER_TOO_SMALL; + } + + // + // The buffer passed in is large enough, so read the PE32 section directly into + // the buffer, update BufferSize with the actual size read, and return the status + // from ReadSection() + // + return Private->Fv->ReadSection ( + Private->Fv, + &Private->NameGuid, + EFI_SECTION_PE32, + 0, + &Buffer, + BufferSize, + &AuthenticationStatus + ); +} + +LOAD_FILE_ON_FV2_PRIVATE_DATA mLoadFileOnFv2PrivateDataTemplate = { + LOAD_FILE_ON_FV2_PRIVATE_DATA_SIGNATURE, + { + LoadFileOnFv2LoadFile + } +}; + +/** + Check if the FFS has been installed LoadFileProtocol for it. + + @param[in] NameGuid Point to FFS File GUID to be checked. + + @retval TRUE The FFS's FileLoadProtocol is in list. + @retval FALSE The FFS's FileLoadProtocol is not in list. + +**/ +BOOLEAN +EFIAPI +IsInPrivateList ( + IN EFI_GUID *NameGuid +) +{ + LIST_ENTRY *Entry; + LOAD_FILE_ON_FV2_PRIVATE_DATA *PrivateData; + + if (IsListEmpty (&mPrivateDataList)) { + return FALSE; + } + + for(Entry = (&mPrivateDataList)->ForwardLink; Entry != (&mPrivateDataList); Entry = Entry->ForwardLink) { + PrivateData = LOAD_FILE_ON_FV2_PRIVATE_DATA_FROM_LINK (Entry); + if (CompareGuid (NameGuid, &PrivateData->NameGuid)) { + DEBUG ((DEBUG_INFO, "LoadFileOnFv2:FileLoadProtocol has been installed in:%g\n", NameGuid)); + return TRUE; + } + } + return FALSE; +} + +/** + Create file device path based on FFS file GUID and UI name. + + @param Device Handle to Firmware Volume. + @param NameGuid Point to FFS file GUID. + @param FileName Point to FFS UI section name. + + @return the combined device path +**/ +EFI_DEVICE_PATH_PROTOCOL * +EFIAPI +CreateFileDevicePath ( + IN EFI_HANDLE Device, + IN EFI_GUID *NameGuid, + IN CONST CHAR16 *FileName + ) +{ + UINTN Size; + FILEPATH_DEVICE_PATH *FilePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *FileDevicePath; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode; + + EfiInitializeFwVolDevicepathNode (&FileNode, NameGuid); + DevicePath = AppendDevicePathNode ( + DevicePathFromHandle (Device), + (EFI_DEVICE_PATH_PROTOCOL *) &FileNode + ); + + Size = StrSize (FileName); + FileDevicePath = AllocatePool (Size + SIZE_OF_FILEPATH_DEVICE_PATH + END_DEVICE_PATH_LENGTH); + if (FileDevicePath != NULL) { + FilePath = (FILEPATH_DEVICE_PATH *) FileDevicePath; + FilePath->Header.Type = MEDIA_DEVICE_PATH; + FilePath->Header.SubType = MEDIA_FILEPATH_DP; + CopyMem (&FilePath->PathName, FileName, Size); + SetDevicePathNodeLength (&FilePath->Header, Size + SIZE_OF_FILEPATH_DEVICE_PATH); + SetDevicePathEndNode (NextDevicePathNode (&FilePath->Header)); + + DevicePath = AppendDevicePath (DevicePath, FileDevicePath); + FreePool (FileDevicePath); + } + + return DevicePath; +} + +/** + Install LoadFile Protocol for Application FFS. + + @param Handle FV Handle. + +**/ +VOID +EFIAPI +InstallFileLoadProtocol ( + EFI_HANDLE Handle +) +{ + EFI_STATUS Status; + LOAD_FILE_ON_FV2_PRIVATE_DATA *Private; + EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; + EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL *Fvb; + EFI_PHYSICAL_ADDRESS Address; + EFI_FV_FILETYPE FileType; + UINTN Key; + EFI_GUID NameGuid; + EFI_FV_FILE_ATTRIBUTES Attributes; + UINTN Size; + EFI_HANDLE LoadFileHandle; + UINT32 AuthenticationStatus; + CHAR16 *UiName; + UINTN UiNameSize; + + DEBUG ((DEBUG_INFO, "LoadFileOnFv2:Find a FV!\n")); + Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **)&Fv); + ASSERT_EFI_ERROR (Status); + Status = gBS->HandleProtocol (Handle, &gEfiFirmwareVolumeBlockProtocolGuid, (VOID **)&Fvb); + Fvb->GetPhysicalAddress (Fvb, &Address); + DEBUG ((DEBUG_INFO, "LoadFileOnFv2:Fvb->Address=%x \n", Address)); + + // + // Use Firmware Volume 2 Protocol to search for a FFS files of type + // EFI_FV_FILETYPE_APPLICATION and produce a LoadFile protocol for + // each one found. + // + FileType = EFI_FV_FILETYPE_APPLICATION; + Key = 0; + while (TRUE) { + Status = Fv->GetNextFile (Fv, &Key, &FileType, &NameGuid, &Attributes, &Size); + if (EFI_ERROR (Status)) { + break; + } + + UiName = NULL; + Status = Fv->ReadSection ( + Fv, + &NameGuid, + EFI_SECTION_USER_INTERFACE, + 0, + (VOID **)&UiName, + &UiNameSize, + &AuthenticationStatus + ); + if (EFI_ERROR (Status)) { + continue; + } + if (!IsInPrivateList (&NameGuid)) { + Private = (LOAD_FILE_ON_FV2_PRIVATE_DATA *)AllocateCopyPool (sizeof (mLoadFileOnFv2PrivateDataTemplate), &mLoadFileOnFv2PrivateDataTemplate); + ASSERT (Private != NULL); + Private->Fv = Fv; + Private->DevicePath = CreateFileDevicePath (Handle, &NameGuid, UiName); + CopyGuid (&Private->NameGuid, &NameGuid); + LoadFileHandle = NULL; + DEBUG ((DEBUG_INFO, "Find a APPLICATION in this FV!\n")); + Status = gBS->InstallMultipleProtocolInterfaces ( + &LoadFileHandle, + &gEfiDevicePathProtocolGuid, Private->DevicePath, + &gEfiLoadFileProtocolGuid, &Private->LoadFile, + NULL + ); + if (!EFI_ERROR (Status)) { + InsertTailList (&mPrivateDataList, &Private->Link); + } else { + DEBUG ((DEBUG_ERROR, "Application with the same name %s has been installed.!\n", UiName)); + FreePool (Private->DevicePath); + FreePool (Private); + } + } + } +} + +/** + This notification function is invoked when an instance of the + LzmaCustomDecompressGuid is produced. It installs another instance of the + EFI_FIRMWARE_VOLUME_PROTOCOL on the handle of the FFS. This notification function + also handles the situation when LZMA decoder driver loaded later than FirmwareVolume driver. + + @param Event The event that occurred + @param Context Context of event. Not used in this nofication function. + +**/ +VOID +EFIAPI +FvNotificationEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + UINTN BufferSize; + EFI_HANDLE *Handle; + UINTN Index; + EFI_HANDLE *CurHandle; + + + Handle = NULL; + Index = 0; + BufferSize = sizeof (EFI_HANDLE); + Handle = AllocateZeroPool (BufferSize); + if (Handle == NULL) { + return; + } + Status = gBS->LocateHandle ( + ByProtocol, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + &BufferSize, + Handle + ); + if (EFI_BUFFER_TOO_SMALL == Status) { + FreePool (Handle); + Handle = AllocateZeroPool (BufferSize); + if (Handle == NULL) { + return; + } + Status = gBS->LocateHandle ( + ByProtocol, + &gEfiFirmwareVolume2ProtocolGuid, + NULL, + &BufferSize, + Handle + ); + if (EFI_ERROR (Status)) { + return; + } + } else if (EFI_ERROR (Status)) { + return; + } + + CurHandle = Handle; + for (Index=0; Index < BufferSize/sizeof (EFI_HANDLE); Index++) { + CurHandle = Handle + Index; + // + // Install LoadFile Protocol + // + InstallFileLoadProtocol (*CurHandle); + } + if (Handle != NULL) { + FreePool (Handle); + } +} + +/** + Entry point function initializes global variables and installs notifications. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. +**/ +EFI_STATUS +EFIAPI +LoadFileOnFv2Intialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + InitializeListHead (&mPrivateDataList); + + EfiCreateProtocolNotifyEvent ( + &gEfiFirmwareVolume2ProtocolGuid, + TPL_CALLBACK, + FvNotificationEvent, + NULL, + &mFvRegistration + ); + + EfiCreateProtocolNotifyEvent ( + &gLzmaCustomDecompressGuid, + TPL_CALLBACK, + FvNotificationEvent, + NULL, + &mFvRegistration + ); + + return EFI_SUCCESS; +} + |