273 lines
8.2 KiB
C
273 lines
8.2 KiB
C
/** @file
|
|
Image relocation related functions. In current design, only support relocate
|
|
BS driver to runtime driver.
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
|
|
#include <Library/ImageRelocationLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/DxeServicesLib.h>
|
|
#include <Library/DevicePathLib.h>
|
|
|
|
#include <Protocol/LoadedImage.h>
|
|
|
|
/*
|
|
Get PE32 section header, section type is EFI_SECTION_PE32 (0x10)
|
|
|
|
@param[in] ImageBufferSize Image buffer size in bytes.
|
|
@param[in] ImageBuffer The buffer to save the contents of image.
|
|
@param[in] Section Output double pointer to EFI_SECTION_PE32.
|
|
|
|
@retval EFI_SUCCESS Get start adress of PE32 section header successful.
|
|
@retval EFI_NOT_FOUND Cannot find PE32 section in this input image.
|
|
*/
|
|
EFI_STATUS
|
|
GetPe32SectionHeader (
|
|
IN UINTN ImageBufferSize,
|
|
IN UINT8 *ImageBuffer,
|
|
OUT EFI_COMMON_SECTION_HEADER **Section
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_COMMON_SECTION_HEADER *CommonSectionHeader;
|
|
UINT8 *WorkingHeaderAddress;
|
|
|
|
CommonSectionHeader = (EFI_COMMON_SECTION_HEADER *) ImageBuffer;
|
|
//
|
|
// Search PE32 header until out of ImageBuffer
|
|
//
|
|
Status = EFI_NOT_FOUND;
|
|
while ((UINTN) (ImageBuffer + ImageBufferSize) > (UINTN) CommonSectionHeader) {
|
|
if (CommonSectionHeader->Type == EFI_SECTION_PE32) {
|
|
*Section = CommonSectionHeader;
|
|
Status = EFI_SUCCESS;
|
|
break;
|
|
}
|
|
//
|
|
// Calculate and push to next header, alignment is 4 btyes
|
|
//
|
|
WorkingHeaderAddress = (UINT8 *) CommonSectionHeader;
|
|
WorkingHeaderAddress += IS_SECTION2 (CommonSectionHeader) ? SECTION2_SIZE (CommonSectionHeader) : SECTION_SIZE (CommonSectionHeader);
|
|
CommonSectionHeader = (EFI_COMMON_SECTION_HEADER *) (((UINTN) WorkingHeaderAddress + 0x03) & (~(UINTN) 3));
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Retrieves the magic value from the PE/COFF header.
|
|
|
|
@param Pe32Header 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_UNION *Pe32Header
|
|
)
|
|
{
|
|
//
|
|
// Reference from BasePeCoff.c, function PeCoffLoaderGetPeHeaderMagicValue
|
|
//
|
|
if ((Pe32Header->Pe32.FileHeader.Machine == IMAGE_FILE_MACHINE_IA64) && (Pe32Header->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 Pe32Header->Pe32.OptionalHeader.Magic;
|
|
}
|
|
|
|
/**
|
|
Force change image type to EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER (0x0C)
|
|
|
|
@param Pe32Header Pe32 Header location
|
|
|
|
@retval EFI_SUCCESS Image type changed successful.
|
|
@retval EFI_UNSUPPORTED Image not supported.
|
|
**/
|
|
EFI_STATUS
|
|
UpdateSubSystemTypeToRuntimeDriver (
|
|
IN OUT EFI_COMMON_SECTION_HEADER *Pe32Header
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_IMAGE_DOS_HEADER *DosHeader;
|
|
UINT32 PeCoffHeaderOffset;
|
|
EFI_IMAGE_OPTIONAL_HEADER_UNION *ImageHeader;
|
|
UINT16 MagicNumber;
|
|
|
|
PeCoffHeaderOffset = 0;
|
|
|
|
//
|
|
// Chech PE32 is DOS image
|
|
//
|
|
DosHeader = (EFI_IMAGE_DOS_HEADER *) Pe32Header;
|
|
if (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
|
|
PeCoffHeaderOffset = DosHeader->e_lfanew;
|
|
}
|
|
|
|
ImageHeader = (EFI_IMAGE_OPTIONAL_HEADER_UNION *) ((UINTN)Pe32Header + PeCoffHeaderOffset);
|
|
|
|
Status = EFI_UNSUPPORTED;
|
|
if (ImageHeader->Te.Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
|
|
ImageHeader->Te.Subsystem = EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER;
|
|
Status = EFI_SUCCESS;
|
|
|
|
} else if (ImageHeader->Pe32.Signature == EFI_IMAGE_NT_SIGNATURE) {
|
|
MagicNumber = PeCoffLoaderGetPeHeaderMagicValue ((VOID *) ImageHeader);
|
|
|
|
if (MagicNumber == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
|
ImageHeader->Pe32.OptionalHeader.Subsystem = EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER;
|
|
Status = EFI_SUCCESS;
|
|
|
|
} else if (MagicNumber == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
|
|
ImageHeader->Pe32Plus.OptionalHeader.Subsystem = EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER;
|
|
Status = EFI_SUCCESS;
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Function uses image handle to check this driver is runtime driver or not
|
|
|
|
@param[in] ImageHandle Input Image handle.
|
|
|
|
@retval TRUE This is a runtime driver.
|
|
@retval FALSE This isn't a runtime driver.
|
|
**/
|
|
BOOLEAN
|
|
IsRuntimeDriver (
|
|
IN EFI_HANDLE ImageHandle
|
|
)
|
|
{
|
|
BOOLEAN MatchedTypeFound;
|
|
EFI_STATUS Status;
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
|
|
MatchedTypeFound = FALSE;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
ImageHandle,
|
|
&gEfiLoadedImageProtocolGuid,
|
|
(VOID **)&LoadedImage
|
|
);
|
|
if (!EFI_ERROR (Status) && LoadedImage->ImageCodeType == EfiRuntimeServicesCode) {
|
|
MatchedTypeFound = TRUE;
|
|
}
|
|
|
|
return MatchedTypeFound;
|
|
}
|
|
|
|
|
|
/**
|
|
Relocation this driver to RuntimeService from DXE
|
|
It will read image from FV, then LoadImage and StartImage to relocation driver
|
|
|
|
@param[in] ParentImageHandle Parent image to load runtime image
|
|
|
|
@retval EFI_SUCCESS Relocation success
|
|
@retval others Failed from another driver
|
|
**/
|
|
EFI_STATUS
|
|
RelocateImageToRuntimeDriver (
|
|
IN EFI_HANDLE ParentImageHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINT8 *ImageBuffer;
|
|
UINTN ImageBufferSize;
|
|
EFI_HANDLE ImageHandle;
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
EFI_GUID FileName;
|
|
EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *CompleteFilePath;
|
|
|
|
ImageBuffer = NULL;
|
|
CompleteFilePath = NULL;
|
|
|
|
//
|
|
// Create file path device for read file and load image
|
|
//
|
|
Status = gBS->HandleProtocol (
|
|
ParentImageHandle,
|
|
&gEfiLoadedImageProtocolGuid,
|
|
(VOID **)&LoadedImage
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
FileName = ((MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) (LoadedImage->FilePath))->FvFileName;
|
|
Status = GetSectionFromFv (
|
|
&FileName,
|
|
EFI_SECTION_PE32,
|
|
0,
|
|
(VOID **)&ImageBuffer,
|
|
&ImageBufferSize
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
Status = UpdateSubSystemTypeToRuntimeDriver ((EFI_COMMON_SECTION_HEADER *) ImageBuffer);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
Status = gBS->HandleProtocol (
|
|
LoadedImage->DeviceHandle,
|
|
&gEfiDevicePathProtocolGuid,
|
|
(VOID*)&ImageDevicePath
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
CompleteFilePath = AppendDevicePath (ImageDevicePath, LoadedImage->FilePath);
|
|
ASSERT (CompleteFilePath != NULL);
|
|
|
|
ImageHandle = NULL;
|
|
Status = gBS->LoadImage (
|
|
FALSE,
|
|
ParentImageHandle,
|
|
CompleteFilePath,
|
|
(VOID *) ImageBuffer,
|
|
ImageBufferSize,
|
|
&ImageHandle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
goto Done;
|
|
}
|
|
|
|
Status = gBS->StartImage (
|
|
ImageHandle,
|
|
NULL,
|
|
NULL
|
|
);
|
|
Done:
|
|
|
|
if (ImageBuffer != NULL) {
|
|
FreePool (ImageBuffer);
|
|
}
|
|
|
|
if (CompleteFilePath != NULL) {
|
|
FreePool (CompleteFilePath);
|
|
}
|
|
|
|
return Status;
|
|
} |