/** @file | |
Copyright (c) 2015 - 2017, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include <Uefi.h> | |
#include <Library/BaseLib.h> | |
#include <Library/UefiDriverEntryPoint.h> | |
#include <Library/BaseMemoryLib.h> | |
#include <Library/DebugLib.h> | |
#include <Library/PeCoffLib.h> | |
#include <Library/UefiBootServicesTableLib.h> | |
#include <Library/DxeServicesLib.h> | |
#include <Library/CacheMaintenanceLib.h> | |
#include <Library/UefiLib.h> | |
/** | |
Relocate this image under 4G memory. | |
@param ImageHandle Handle of driver image. | |
@param SystemTable Pointer to system table. | |
@retval EFI_SUCCESS Image successfully relocated. | |
@retval EFI_ABORTED Failed to relocate image. | |
**/ | |
EFI_STATUS | |
RelocateImageUnder4GIfNeeded ( | |
IN EFI_HANDLE ImageHandle, | |
IN EFI_SYSTEM_TABLE *SystemTable | |
) | |
{ | |
EFI_STATUS Status; | |
UINT8 *Buffer; | |
UINTN BufferSize; | |
EFI_HANDLE NewImageHandle; | |
UINTN Pages; | |
EFI_PHYSICAL_ADDRESS FfsBuffer; | |
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; | |
VOID *Interface; | |
// | |
// If it is already <4G, no need do relocate | |
// | |
if ((UINTN)RelocateImageUnder4GIfNeeded < 0xFFFFFFFF) { | |
return EFI_SUCCESS; | |
} | |
// | |
// If locate gEfiCallerIdGuid success, it means 2nd entry. | |
// | |
Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &Interface); | |
if (!EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "FspNotifyDxe - 2nd entry\n")); | |
return EFI_SUCCESS; | |
} | |
DEBUG ((DEBUG_INFO, "FspNotifyDxe - 1st entry\n")); | |
// | |
// Here we install a dummy handle | |
// | |
NewImageHandle = NULL; | |
Status = gBS->InstallProtocolInterface ( | |
&NewImageHandle, | |
&gEfiCallerIdGuid, | |
EFI_NATIVE_INTERFACE, | |
NULL | |
); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Reload image itself to <4G mem | |
// | |
Status = GetSectionFromAnyFv ( | |
&gEfiCallerIdGuid, | |
EFI_SECTION_PE32, | |
0, | |
(VOID **)&Buffer, | |
&BufferSize | |
); | |
ASSERT_EFI_ERROR (Status); | |
ImageContext.Handle = Buffer; | |
ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory; | |
// | |
// Get information about the image being loaded | |
// | |
Status = PeCoffLoaderGetImageInfo (&ImageContext); | |
ASSERT_EFI_ERROR (Status); | |
if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { | |
Pages = EFI_SIZE_TO_PAGES ((UINTN)(ImageContext.ImageSize + ImageContext.SectionAlignment)); | |
} else { | |
Pages = EFI_SIZE_TO_PAGES ((UINTN)ImageContext.ImageSize); | |
} | |
FfsBuffer = 0xFFFFFFFF; | |
Status = gBS->AllocatePages ( | |
AllocateMaxAddress, | |
EfiBootServicesCode, | |
Pages, | |
&FfsBuffer | |
); | |
ASSERT_EFI_ERROR (Status); | |
ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer; | |
// | |
// Align buffer on section boundary | |
// | |
ImageContext.ImageAddress += ImageContext.SectionAlignment - 1; | |
ImageContext.ImageAddress &= ~((EFI_PHYSICAL_ADDRESS)ImageContext.SectionAlignment - 1); | |
// | |
// Load the image to our new buffer | |
// | |
Status = PeCoffLoaderLoadImage (&ImageContext); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Relocate the image in our new buffer | |
// | |
Status = PeCoffLoaderRelocateImage (&ImageContext); | |
ASSERT_EFI_ERROR (Status); | |
// | |
// Free the buffer allocated by ReadSection since the image has been relocated in the new buffer | |
// | |
gBS->FreePool (Buffer); | |
// | |
// Flush the instruction cache so the image data is written before we execute it | |
// | |
InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); | |
DEBUG ((DEBUG_INFO, "Loading driver at 0x%08x EntryPoint=0x%08x\n", (UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.EntryPoint)); | |
Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint))(NewImageHandle, gST); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_ERROR, "Error: Image at 0x%08x start failed: %r\n", ImageContext.ImageAddress, Status)); | |
gBS->FreePages (FfsBuffer, Pages); | |
} | |
// | |
// return error to unload >4G copy, if we already relocate itself to <4G. | |
// | |
return EFI_ALREADY_STARTED; | |
} |