diff options
Diffstat (limited to 'ArmPkg/Library/BdsLib/BdsAppLoader.c')
-rw-r--r-- | ArmPkg/Library/BdsLib/BdsAppLoader.c | 328 |
1 files changed, 217 insertions, 111 deletions
diff --git a/ArmPkg/Library/BdsLib/BdsAppLoader.c b/ArmPkg/Library/BdsLib/BdsAppLoader.c index 16b45ceb4c..d14e3c5dcc 100644 --- a/ArmPkg/Library/BdsLib/BdsAppLoader.c +++ b/ArmPkg/Library/BdsLib/BdsAppLoader.c @@ -1,111 +1,217 @@ -/** @file
-*
-* Copyright (c) 2011, ARM Limited. All rights reserved.
-*
-* 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 "BdsInternal.h"
-
-EFI_STATUS
-BdsLoadPeCoff (
- IN BDS_FILE *EfiAppFile
- )
-{
- EFI_STATUS Status;
- EFI_HANDLE ImageHandle;
- MEDIA_FW_VOL_FILEPATH_DEVICE_PATH NewNode;
- EFI_DEVICE_PATH_PROTOCOL *DevicePath;
-
- // Only support loading from FV right now
- ASSERT(EfiAppFile->Type == BDS_FILETYPE_FV);
-
- // Generate the Device Path for the file
- DevicePath = DuplicateDevicePath(EfiAppFile->DevicePath);
- EfiInitializeFwVolDevicepathNode (&NewNode, &(EfiAppFile->File.Fv.Guid));
- DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&NewNode);
-
- Status = gBS->LoadImage (TRUE, gImageHandle, DevicePath, NULL, 0, &ImageHandle);
- if (!EFI_ERROR (Status)) {
- //
- // Before calling the image, enable the Watchdog Timer for
- // the 5 Minute period
- //
- gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
- Status = gBS->StartImage (ImageHandle, NULL, NULL);
- //
- // Clear the Watchdog Timer after the image returns
- //
- gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
- }
-
- return Status;
-}
-
-EFI_STATUS BdsLoadApplicationFromPath(
- IN CHAR16* EfiAppPath
-) {
- EFI_STATUS Status;
- BDS_FILE EfiAppFile;
-
- // Need to connect every drivers to ensure no dependencies are missing for the application
- Status = BdsConnectAllDrivers();
- if (EFI_ERROR(Status)) {
- DEBUG ((EFI_D_ERROR, "FAIL to connect all drivers\n"));
- return Status;
- }
-
- // Locate the application from a device path
- Status = BdsLoadFilePath(EfiAppPath, &EfiAppFile);
- if (EFI_ERROR(Status)) {
- DEBUG ((EFI_D_ERROR, "ERROR: Do not find EFI application %s\n",EfiAppPath));
- return Status;
- }
-
- // Start the application
- Status = BdsLoadPeCoff(&EfiAppFile);
-
- return Status;
-}
-
-EFI_STATUS BdsLoadApplication(
- IN CHAR16* EfiApp
-) {
- EFI_STATUS Status;
- UINTN NoHandles, HandleIndex;
- EFI_HANDLE *Handles;
- BDS_FILE EfiAppFile;
-
- // Need to connect every drivers to ensure no dependencies are missing for the application
- Status = BdsConnectAllDrivers();
- if (EFI_ERROR(Status)) {
- DEBUG ((EFI_D_ERROR, "FAIL to connect all drivers\n"));
- return Status;
- }
-
- // Search the application in any Firmware Volume
- Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &NoHandles, &Handles);
- if (EFI_ERROR (Status) || (NoHandles == 0)) {
- DEBUG ((EFI_D_ERROR, "FAIL to find Firmware Volume\n"));
- return Status;
- }
-
- // Search in all Firmware Volume for the EFI Application
- for (HandleIndex = 0; HandleIndex < NoHandles; HandleIndex++) {
- Status = BdsLoadFileFromFirmwareVolume(Handles[HandleIndex],EfiApp,EFI_FV_FILETYPE_APPLICATION,&EfiAppFile);
- if (!EFI_ERROR (Status)) {
- // Start the application
- Status = BdsLoadPeCoff(&EfiAppFile);
- return Status;
- }
- }
-
- return Status;
-}
+/** @file +* +* Copyright (c) 2011, ARM Limited. All rights reserved. +* +* 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 "BdsInternal.h" + +//#include <Library/PeCoffLib.h> +#include <Library/DxeServicesLib.h> + +//TODO: RemoveMe +#include <Protocol/DevicePathToText.h> + +/** + Retrieves the magic value from the PE/COFF header. + + @param Hdr The buffer in which to return the PE32, PE32+, or TE header. + + @return EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC - Image is PE32 + @return EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC - Image is PE32+ + +**/ +UINT16 +PeCoffLoaderGetPeHeaderMagicValue ( + IN EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr + ) +{ + // + // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value + // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the + // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC + // then override the returned value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC + // + if (Hdr.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + return EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC; + } + // + // Return the magic value from the PC/COFF Optional Header + // + return Hdr.Pe32->OptionalHeader.Magic; +} + +STATIC +BOOLEAN +IsLoadableImage ( + VOID* Image + ) +{ + UINT16 Magic; + EFI_IMAGE_DOS_HEADER *DosHeader; + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Header; + + if (Image == NULL) { + return FALSE; + } + + DosHeader = (EFI_IMAGE_DOS_HEADER*)Image; + if (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + Header.Pe32 = (EFI_IMAGE_NT_HEADERS32*)((UINT8*)Image + DosHeader->e_lfanew); + } else { + Header.Pe32 = (EFI_IMAGE_NT_HEADERS32*)(Image); + } + + if (Header.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { + // It is a TE Image + return TRUE; + } else if (Header.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { + Magic = PeCoffLoaderGetPeHeaderMagicValue (Header); + if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { + // It is a PE32 Image + return TRUE; + } else if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + // It is a PE32+ Image + return TRUE; + } else { + DEBUG ((EFI_D_ERROR,"BdsLoadBinaryFromPath(): Fail unrecognized PE Image\n")); + } + } else { + DEBUG ((EFI_D_ERROR,"BdsLoadBinaryFromPath(): Fail unrecognize image\n")); + } + + return FALSE; +} + +STATIC +EFI_STATUS +BdsLoadFileFromFirmwareVolume ( + IN EFI_HANDLE FvHandle, + IN CHAR16 *FilePath, + IN EFI_FV_FILETYPE FileTypeFilter, + OUT EFI_DEVICE_PATH **EfiAppDevicePath + ) +{ + EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol; + VOID *Key; + EFI_STATUS Status, FileStatus; + EFI_GUID NameGuid; + EFI_FV_FILETYPE FileType; + EFI_FV_FILE_ATTRIBUTES Attributes; + UINTN Size; + UINTN UiStringLen; + CHAR16 *UiSection; + UINT32 Authentication; + EFI_DEVICE_PATH *FvDevicePath; + MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileDevicePath; + + Status = gBS->HandleProtocol (FvHandle,&gEfiFirmwareVolume2ProtocolGuid, (VOID **)&FvProtocol); + if (EFI_ERROR(Status)) { + return Status; + } + + // Length of FilePath + UiStringLen = StrLen (FilePath); + + // Allocate Key + Key = AllocatePool (FvProtocol->KeySize); + ASSERT (Key != NULL); + ZeroMem (Key, FvProtocol->KeySize); + + do { + // Search in all files + FileType = FileTypeFilter; + + Status = FvProtocol->GetNextFile (FvProtocol, Key, &FileType, &NameGuid, &Attributes, &Size); + if (!EFI_ERROR (Status)) { + UiSection = NULL; + FileStatus = FvProtocol->ReadSection ( + FvProtocol, + &NameGuid, + EFI_SECTION_USER_INTERFACE, + 0, + (VOID **)&UiSection, + &Size, + &Authentication + ); + if (!EFI_ERROR (FileStatus)) { + if (StrnCmp (FilePath, UiSection, UiStringLen) == 0) { + // + // We found a UiString match. + // + Status = gBS->HandleProtocol (FvHandle, &gEfiDevicePathProtocolGuid, (VOID **)&FvDevicePath); + + // Generate the Device Path for the file + //DevicePath = DuplicateDevicePath(FvDevicePath); + EfiInitializeFwVolDevicepathNode (&FileDevicePath, &NameGuid); + *EfiAppDevicePath = AppendDevicePathNode (FvDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)&FileDevicePath); + + FreePool (Key); + FreePool (UiSection); + return FileStatus; + } + FreePool (UiSection); + } + } + } while (!EFI_ERROR (Status)); + + FreePool(Key); + return Status; +} + +/** + Start an EFI Application from any Firmware Volume + + @param EfiApp EFI Application Name + + @retval EFI_SUCCESS All drivers have been connected + @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found + @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. + +**/ +EFI_STATUS +BdsLoadApplication ( + IN EFI_HANDLE ParentImageHandle, + IN CHAR16* EfiApp + ) +{ + EFI_STATUS Status; + UINTN NoHandles, HandleIndex; + EFI_HANDLE *Handles; + EFI_DEVICE_PATH *FvDevicePath; + EFI_DEVICE_PATH *EfiAppDevicePath; + + // Need to connect every drivers to ensure no dependencies are missing for the application + Status = BdsConnectAllDrivers(); + if (EFI_ERROR(Status)) { + DEBUG ((EFI_D_ERROR, "FAIL to connect all drivers\n")); + return Status; + } + + // Search the application in any Firmware Volume + Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &NoHandles, &Handles); + if (EFI_ERROR (Status) || (NoHandles == 0)) { + DEBUG ((EFI_D_ERROR, "FAIL to find Firmware Volume\n")); + return Status; + } + + // Search in all Firmware Volume for the EFI Application + for (HandleIndex = 0; HandleIndex < NoHandles; HandleIndex++) { + Status = BdsLoadFileFromFirmwareVolume (Handles[HandleIndex], EfiApp, EFI_FV_FILETYPE_APPLICATION, &EfiAppDevicePath); + if (!EFI_ERROR (Status)) { + // Start the application + Status = BdsStartEfiApplication (ParentImageHandle, EfiAppDevicePath); + return Status; + } + } + + return Status; +} |