Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Liming Gao <liming.gao@intel.com> Reviewed-by: Feng Tian <feng.tian@intel.com>
		
			
				
	
	
		
			428 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
|   Produce Load File Protocol for UEFI Applications in Firmware Volumes
 | |
| 
 | |
|   Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR>
 | |
|   This program and the accompanying materials
 | |
|   are licensed and made available under the terms and conditions of the BSD License
 | |
|   which accompanies this distribution.  The full text of the license may be found at
 | |
|   http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
|   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
|   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #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)
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 |