/** @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 with a given Guid within a valid FV. | |
If input Guid is NULL, will locate the first section having the given file type | |
@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 Guid Will ignore if it is NULL. | |
@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 | |
FvFindFileByTypeGuid ( | |
IN EFI_FIRMWARE_VOLUME_HEADER *FvHeader, | |
IN EFI_FV_FILETYPE FileType, | |
IN EFI_GUID *Guid OPTIONAL, | |
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) { | |
if ((Guid == NULL) || CompareGuid (&File->Name, Guid)) { | |
*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 SectionType 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 = FvFindFileByTypeGuid (PayloadFv, EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE, NULL, &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 = FvFindFileByTypeGuid (DxeCoreFv, EFI_FV_FILETYPE_DXE_CORE, NULL, &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; | |
} | |
/** | |
Find DXE core from FV and build DXE core HOBs. | |
@param[in] DxeFv The FV where to find the DXE core. | |
@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 | |
UniversalLoadDxeCore ( | |
IN EFI_FIRMWARE_VOLUME_HEADER *DxeFv, | |
OUT PHYSICAL_ADDRESS *DxeCoreEntryPoint | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_FFS_FILE_HEADER *FileHeader; | |
VOID *PeCoffImage; | |
EFI_PHYSICAL_ADDRESS ImageAddress; | |
UINT64 ImageSize; | |
// | |
// Find DXE core file from DXE FV | |
// | |
Status = FvFindFileByTypeGuid (DxeFv, EFI_FV_FILETYPE_DXE_CORE, NULL, &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; | |
} |