/** @file Produce Load File Protocol for UEFI Applications in Firmware Volumes Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #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) EFI_EVENT 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 occured @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; }