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