REF: https://bugzilla.tianocore.org/show_bug.cgi?id=3100 It is not necessary to have a PEI phase in the UEFI payload since no specific PEI task is required. This patch adds a UefiPayloadEntry driver to get UEFI Payload required information from the bootloaders, convert them into a HOB list, load DXE core and transfer control to it. Here is the change details: 1) Removed PEI phase, including Peicore, BlSupportPei, SecCore, etc. 2) Added UefiPayloadEntry driver. this is the only driver before DXE core. 3) Added Pure X64 support, dropped Pure IA32 (Could add later if required) 64bit payload with 32bit entry point is still supported. 4) Use one DSC file UefiPayloadPkg.dsc to support X64 and IA32X64 build. Removed UefiPayloadIa32.dsc and UefiPayloadIa32X64.dsc Tested with SBL and coreboot on QEMU. Signed-off-by: Guo Dong <guo.dong@intel.com> Reviewed-by: Maurice Ma <maurice.ma@intel.com> Reviewed-by: Benjamin You <benjamin.you@intel.com>
308 lines
8.3 KiB
C
308 lines
8.3 KiB
C
/** @file
|
|
|
|
Copyright (c) 2020, Intel Corporation. All rights reserved.<BR>
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
**/
|
|
|
|
#include "UefiPayloadEntry.h"
|
|
|
|
/**
|
|
Allocate pages for code.
|
|
|
|
@param[in] Pages Number of pages to be allocated.
|
|
|
|
@return Allocated memory.
|
|
**/
|
|
VOID*
|
|
AllocateCodePages (
|
|
IN UINTN Pages
|
|
)
|
|
{
|
|
VOID *Alloc;
|
|
EFI_PEI_HOB_POINTERS Hob;
|
|
|
|
Alloc = AllocatePages (Pages);
|
|
if (Alloc == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
// find the HOB we just created, and change the type to EfiBootServicesCode
|
|
Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
|
|
while (Hob.Raw != NULL) {
|
|
if (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress == (UINTN)Alloc) {
|
|
Hob.MemoryAllocation->AllocDescriptor.MemoryType = EfiBootServicesCode;
|
|
return Alloc;
|
|
}
|
|
Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, GET_NEXT_HOB (Hob));
|
|
}
|
|
|
|
ASSERT (FALSE);
|
|
|
|
FreePages (Alloc, Pages);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
Loads and relocates a PE/COFF image
|
|
|
|
@param[in] PeCoffImage Point to a Pe/Coff image.
|
|
@param[out] ImageAddress The image memory address after relocation.
|
|
@param[out] ImageSize The image size.
|
|
@param[out] EntryPoint The image entry point.
|
|
|
|
@return EFI_SUCCESS If the image is loaded and relocated successfully.
|
|
@return Others If the image failed to load or relocate.
|
|
**/
|
|
EFI_STATUS
|
|
LoadPeCoffImage (
|
|
IN VOID *PeCoffImage,
|
|
OUT EFI_PHYSICAL_ADDRESS *ImageAddress,
|
|
OUT UINT64 *ImageSize,
|
|
OUT EFI_PHYSICAL_ADDRESS *EntryPoint
|
|
)
|
|
{
|
|
RETURN_STATUS Status;
|
|
PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
|
|
VOID *Buffer;
|
|
|
|
ZeroMem (&ImageContext, sizeof (ImageContext));
|
|
|
|
ImageContext.Handle = PeCoffImage;
|
|
ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
|
|
|
|
Status = PeCoffLoaderGetImageInfo (&ImageContext);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Allocate Memory for the image
|
|
//
|
|
Buffer = AllocateCodePages (EFI_SIZE_TO_PAGES((UINT32)ImageContext.ImageSize));
|
|
if (Buffer == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Buffer;
|
|
|
|
//
|
|
// Load the image to our new buffer
|
|
//
|
|
Status = PeCoffLoaderLoadImage (&ImageContext);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Relocate the image in our new buffer
|
|
//
|
|
Status = PeCoffLoaderRelocateImage (&ImageContext);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
*ImageAddress = ImageContext.ImageAddress;
|
|
*ImageSize = ImageContext.ImageSize;
|
|
*EntryPoint = ImageContext.EntryPoint;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This function searchs a given file type within a valid FV.
|
|
|
|
@param FvHeader A pointer to firmware volume header that contains the set of files
|
|
to be searched.
|
|
@param FileType File type to be searched.
|
|
@param FileHeader A pointer to the discovered file, if successful.
|
|
|
|
@retval EFI_SUCCESS Successfully found FileType
|
|
@retval EFI_NOT_FOUND File type can't be found.
|
|
**/
|
|
EFI_STATUS
|
|
FvFindFile (
|
|
IN EFI_FIRMWARE_VOLUME_HEADER *FvHeader,
|
|
IN EFI_FV_FILETYPE FileType,
|
|
OUT EFI_FFS_FILE_HEADER **FileHeader
|
|
)
|
|
{
|
|
EFI_PHYSICAL_ADDRESS CurrentAddress;
|
|
EFI_PHYSICAL_ADDRESS EndOfFirmwareVolume;
|
|
EFI_FFS_FILE_HEADER *File;
|
|
UINT32 Size;
|
|
EFI_PHYSICAL_ADDRESS EndOfFile;
|
|
|
|
CurrentAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) FvHeader;
|
|
EndOfFirmwareVolume = CurrentAddress + FvHeader->FvLength;
|
|
|
|
//
|
|
// Loop through the FFS files
|
|
//
|
|
for (EndOfFile = CurrentAddress + FvHeader->HeaderLength; ; ) {
|
|
CurrentAddress = (EndOfFile + 7) & 0xfffffffffffffff8ULL;
|
|
if (CurrentAddress > EndOfFirmwareVolume) {
|
|
break;
|
|
}
|
|
|
|
File = (EFI_FFS_FILE_HEADER*)(UINTN) CurrentAddress;
|
|
if (IS_FFS_FILE2 (File)) {
|
|
Size = FFS_FILE2_SIZE (File);
|
|
if (Size <= 0x00FFFFFF) {
|
|
break;
|
|
}
|
|
} else {
|
|
Size = FFS_FILE_SIZE (File);
|
|
if (Size < sizeof (EFI_FFS_FILE_HEADER)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
EndOfFile = CurrentAddress + Size;
|
|
if (EndOfFile > EndOfFirmwareVolume) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Look for file type
|
|
//
|
|
if (File->Type == FileType) {
|
|
*FileHeader = File;
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
|
|
/**
|
|
This function searchs a given section type within a valid FFS file.
|
|
|
|
@param FileHeader A pointer to the file header that contains the set of sections to
|
|
be searched.
|
|
@param SearchType The value of the section type to search.
|
|
@param SectionData A pointer to the discovered section, if successful.
|
|
|
|
@retval EFI_SUCCESS The section was found.
|
|
@retval EFI_NOT_FOUND The section was not found.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
FileFindSection (
|
|
IN EFI_FFS_FILE_HEADER *FileHeader,
|
|
IN EFI_SECTION_TYPE SectionType,
|
|
OUT VOID **SectionData
|
|
)
|
|
{
|
|
UINT32 FileSize;
|
|
EFI_COMMON_SECTION_HEADER *Section;
|
|
UINT32 SectionSize;
|
|
UINT32 Index;
|
|
|
|
if (IS_FFS_FILE2 (FileHeader)) {
|
|
FileSize = FFS_FILE2_SIZE (FileHeader);
|
|
} else {
|
|
FileSize = FFS_FILE_SIZE (FileHeader);
|
|
}
|
|
FileSize -= sizeof (EFI_FFS_FILE_HEADER);
|
|
|
|
Section = (EFI_COMMON_SECTION_HEADER *)(FileHeader + 1);
|
|
Index = 0;
|
|
while (Index < FileSize) {
|
|
if (Section->Type == SectionType) {
|
|
if (IS_SECTION2 (Section)) {
|
|
*SectionData = (VOID *)((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER2));
|
|
} else {
|
|
*SectionData = (VOID *)((UINT8 *) Section + sizeof (EFI_COMMON_SECTION_HEADER));
|
|
}
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
if (IS_SECTION2 (Section)) {
|
|
SectionSize = SECTION2_SIZE (Section);
|
|
} else {
|
|
SectionSize = SECTION_SIZE (Section);
|
|
}
|
|
|
|
SectionSize = GET_OCCUPIED_SIZE (SectionSize, 4);
|
|
ASSERT (SectionSize != 0);
|
|
Index += SectionSize;
|
|
|
|
Section = (EFI_COMMON_SECTION_HEADER *)((UINT8 *)Section + SectionSize);
|
|
}
|
|
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
|
|
/**
|
|
Find DXE core from FV and build DXE core HOBs.
|
|
|
|
@param[out] DxeCoreEntryPoint DXE core entry point
|
|
|
|
@retval EFI_SUCCESS If it completed successfully.
|
|
@retval EFI_NOT_FOUND If it failed to load DXE FV.
|
|
**/
|
|
EFI_STATUS
|
|
LoadDxeCore (
|
|
OUT PHYSICAL_ADDRESS *DxeCoreEntryPoint
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_FIRMWARE_VOLUME_HEADER *PayloadFv;
|
|
EFI_FIRMWARE_VOLUME_HEADER *DxeCoreFv;
|
|
EFI_FFS_FILE_HEADER *FileHeader;
|
|
VOID *PeCoffImage;
|
|
EFI_PHYSICAL_ADDRESS ImageAddress;
|
|
UINT64 ImageSize;
|
|
|
|
PayloadFv = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)PcdGet32 (PcdPayloadFdMemBase);
|
|
|
|
//
|
|
// DXE FV is inside Payload FV. Here find DXE FV from Payload FV
|
|
//
|
|
Status = FvFindFile (PayloadFv, EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, &FileHeader);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
Status = FileFindSection (FileHeader, EFI_SECTION_FIRMWARE_VOLUME_IMAGE, (VOID **)&DxeCoreFv);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Report DXE FV to DXE core
|
|
//
|
|
BuildFvHob ((EFI_PHYSICAL_ADDRESS) (UINTN) DxeCoreFv, DxeCoreFv->FvLength);
|
|
|
|
//
|
|
// Find DXE core file from DXE FV
|
|
//
|
|
Status = FvFindFile (DxeCoreFv, EFI_FV_FILETYPE_DXE_CORE, &FileHeader);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
Status = FileFindSection (FileHeader, EFI_SECTION_PE32, (VOID **)&PeCoffImage);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Get DXE core info
|
|
//
|
|
Status = LoadPeCoffImage (PeCoffImage, &ImageAddress, &ImageSize, DxeCoreEntryPoint);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
BuildModuleHob (&FileHeader->Name, ImageAddress, EFI_SIZE_TO_PAGES ((UINT32) ImageSize) * EFI_PAGE_SIZE, *DxeCoreEntryPoint);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|