| /** @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); | |
| Section = (EFI_COMMON_SECTION_HEADER *)(((EFI_FFS_FILE_HEADER2 *)FileHeader) + 1); | |
| } else { | |
| FileSize = FFS_FILE_SIZE (FileHeader); | |
| Section = (EFI_COMMON_SECTION_HEADER *)(FileHeader + 1); | |
| } | |
| FileSize -= sizeof (EFI_FFS_FILE_HEADER); | |
| 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; | |
| } |