diff options
Diffstat (limited to 'MdeModulePkg/Core/Pei/Image/Image.c')
| -rw-r--r-- | MdeModulePkg/Core/Pei/Image/Image.c | 1964 |
1 files changed, 982 insertions, 982 deletions
diff --git a/MdeModulePkg/Core/Pei/Image/Image.c b/MdeModulePkg/Core/Pei/Image/Image.c index cee9f09c6e..46d7f60fc4 100644 --- a/MdeModulePkg/Core/Pei/Image/Image.c +++ b/MdeModulePkg/Core/Pei/Image/Image.c @@ -1,982 +1,982 @@ -/** @file
- Pei Core Load Image Support
-
-Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.<BR>
-SPDX-License-Identifier: BSD-2-Clause-Patent
-
-**/
-
-#include "PeiMain.h"
-
-EFI_PEI_LOAD_FILE_PPI mPeiLoadImagePpi = {
- PeiLoadImageLoadImageWrapper
-};
-
-EFI_PEI_PPI_DESCRIPTOR gPpiLoadFilePpiList = {
- (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
- &gEfiPeiLoadFilePpiGuid,
- &mPeiLoadImagePpi
-};
-
-/**
-
- Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file.
- The function is used for XIP code to have optimized memory copy.
-
- @param FileHandle - The handle to the PE/COFF file
- @param FileOffset - The offset, in bytes, into the file to read
- @param ReadSize - The number of bytes to read from the file starting at FileOffset
- @param Buffer - A pointer to the buffer to read the data into.
-
- @return EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset
-
-**/
-EFI_STATUS
-EFIAPI
-PeiImageRead (
- IN VOID *FileHandle,
- IN UINTN FileOffset,
- IN UINTN *ReadSize,
- OUT VOID *Buffer
- )
-{
- CHAR8 *Destination8;
- CHAR8 *Source8;
-
- Destination8 = Buffer;
- Source8 = (CHAR8 *)((UINTN)FileHandle + FileOffset);
- if (Destination8 != Source8) {
- CopyMem (Destination8, Source8, *ReadSize);
- }
-
- return EFI_SUCCESS;
-}
-
-/**
- To check memory usage bit map array to figure out if the memory range the image will be loaded in is available or not. If
- memory range is available, the function will mark the corresponding bits to 1 which indicates the memory range is used.
- The function is only invoked when load modules at fixed address feature is enabled.
-
- @param Private Pointer to the private data passed in from caller
- @param ImageBase The base address the image will be loaded at.
- @param ImageSize The size of the image
-
- @retval EFI_SUCCESS The memory range the image will be loaded in is available
- @retval EFI_NOT_FOUND The memory range the image will be loaded in is not available
-**/
-EFI_STATUS
-CheckAndMarkFixLoadingMemoryUsageBitMap (
- IN PEI_CORE_INSTANCE *Private,
- IN EFI_PHYSICAL_ADDRESS ImageBase,
- IN UINT32 ImageSize
- )
-{
- UINT32 DxeCodePageNumber;
- UINT64 ReservedCodeSize;
- EFI_PHYSICAL_ADDRESS PeiCodeBase;
- UINT32 BaseOffsetPageNumber;
- UINT32 TopOffsetPageNumber;
- UINT32 Index;
- UINT64 *MemoryUsageBitMap;
-
- //
- // The reserved code range includes RuntimeCodePage range, Boot time code range and PEI code range.
- //
- DxeCodePageNumber = PcdGet32 (PcdLoadFixAddressBootTimeCodePageNumber);
- DxeCodePageNumber += PcdGet32 (PcdLoadFixAddressRuntimeCodePageNumber);
- ReservedCodeSize = EFI_PAGES_TO_SIZE (DxeCodePageNumber + PcdGet32 (PcdLoadFixAddressPeiCodePageNumber));
- PeiCodeBase = Private->LoadModuleAtFixAddressTopAddress - ReservedCodeSize;
-
- //
- // Test the memory range for loading the image in the PEI code range.
- //
- if (((Private->LoadModuleAtFixAddressTopAddress - EFI_PAGES_TO_SIZE (DxeCodePageNumber)) < (ImageBase + ImageSize)) ||
- (PeiCodeBase > ImageBase))
- {
- return EFI_NOT_FOUND;
- }
-
- //
- // Test if the memory is available or not.
- //
- MemoryUsageBitMap = Private->PeiCodeMemoryRangeUsageBitMap;
- BaseOffsetPageNumber = EFI_SIZE_TO_PAGES ((UINT32)(ImageBase - PeiCodeBase));
- TopOffsetPageNumber = EFI_SIZE_TO_PAGES ((UINT32)(ImageBase + ImageSize - PeiCodeBase));
- for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index++) {
- if ((MemoryUsageBitMap[Index / 64] & LShiftU64 (1, (Index % 64))) != 0) {
- //
- // This page is already used.
- //
- return EFI_NOT_FOUND;
- }
- }
-
- //
- // Being here means the memory range is available. So mark the bits for the memory range
- //
- for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index++) {
- MemoryUsageBitMap[Index / 64] |= LShiftU64 (1, (Index % 64));
- }
-
- return EFI_SUCCESS;
-}
-
-/**
-
- Get the fixed loading address from image header assigned by build tool. This function only be called
- when Loading module at Fixed address feature enabled.
-
- @param ImageContext Pointer to the image context structure that describes the PE/COFF
- image that needs to be examined by this function.
- @param Private Pointer to the private data passed in from caller
-
- @retval EFI_SUCCESS An fixed loading address is assigned to this image by build tools .
- @retval EFI_NOT_FOUND The image has no assigned fixed loading address.
-
-**/
-EFI_STATUS
-GetPeCoffImageFixLoadingAssignedAddress (
- IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext,
- IN PEI_CORE_INSTANCE *Private
- )
-{
- UINTN SectionHeaderOffset;
- EFI_STATUS Status;
- EFI_IMAGE_SECTION_HEADER SectionHeader;
- EFI_IMAGE_OPTIONAL_HEADER_UNION *ImgHdr;
- EFI_PHYSICAL_ADDRESS FixLoadingAddress;
- UINT16 Index;
- UINTN Size;
- UINT16 NumberOfSections;
- UINT64 ValueInSectionHeader;
-
- FixLoadingAddress = 0;
- Status = EFI_NOT_FOUND;
-
- //
- // Get PeHeader pointer
- //
- ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8 *)ImageContext->Handle + ImageContext->PeCoffHeaderOffset);
- if (ImageContext->IsTeImage) {
- //
- // for TE image, the fix loading address is saved in first section header that doesn't point
- // to code section.
- //
- SectionHeaderOffset = sizeof (EFI_TE_IMAGE_HEADER);
- NumberOfSections = ImgHdr->Te.NumberOfSections;
- } else {
- SectionHeaderOffset = ImageContext->PeCoffHeaderOffset +
- sizeof (UINT32) +
- sizeof (EFI_IMAGE_FILE_HEADER) +
- ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader;
- NumberOfSections = ImgHdr->Pe32.FileHeader.NumberOfSections;
- }
-
- //
- // Get base address from the first section header that doesn't point to code section.
- //
- for (Index = 0; Index < NumberOfSections; Index++) {
- //
- // Read section header from file
- //
- Size = sizeof (EFI_IMAGE_SECTION_HEADER);
- Status = ImageContext->ImageRead (
- ImageContext->Handle,
- SectionHeaderOffset,
- &Size,
- &SectionHeader
- );
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- Status = EFI_NOT_FOUND;
-
- if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) {
- //
- // Build tool will save the address in PointerToRelocations & PointerToLineNumbers fields in the first section header
- // that doesn't point to code section in image header, as well as ImageBase field of image header. A notable thing is
- // that for PEIM, the value in ImageBase field may not be equal to the value in PointerToRelocations & PointerToLineNumbers because
- // for XIP PEIM, ImageBase field holds the image base address running on the Flash. And PointerToRelocations & PointerToLineNumbers
- // hold the image base address when it is shadow to the memory. And there is an assumption that when the feature is enabled, if a
- // module is assigned a loading address by tools, PointerToRelocations & PointerToLineNumbers fields should NOT be Zero, or
- // else, these 2 fields should be set to Zero
- //
- ValueInSectionHeader = ReadUnaligned64 ((UINT64 *)&SectionHeader.PointerToRelocations);
- if (ValueInSectionHeader != 0) {
- //
- // Found first section header that doesn't point to code section.
- //
- if ((INT64)PcdGet64 (PcdLoadModuleAtFixAddressEnable) > 0) {
- //
- // When LMFA feature is configured as Load Module at Fixed Absolute Address mode, PointerToRelocations & PointerToLineNumbers field
- // hold the absolute address of image base running in memory
- //
- FixLoadingAddress = ValueInSectionHeader;
- } else {
- //
- // When LMFA feature is configured as Load Module at Fixed offset mode, PointerToRelocations & PointerToLineNumbers field
- // hold the offset relative to a platform-specific top address.
- //
- FixLoadingAddress = (EFI_PHYSICAL_ADDRESS)(Private->LoadModuleAtFixAddressTopAddress + (INT64)ValueInSectionHeader);
- }
-
- //
- // Check if the memory range is available.
- //
- Status = CheckAndMarkFixLoadingMemoryUsageBitMap (Private, FixLoadingAddress, (UINT32)ImageContext->ImageSize);
- if (!EFI_ERROR (Status)) {
- //
- // The assigned address is valid. Return the specified loading address
- //
- ImageContext->ImageAddress = FixLoadingAddress;
- }
- }
-
- break;
- }
-
- SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER);
- }
-
- DEBUG ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address 0x%11p. Status= %r \n", (VOID *)(UINTN)FixLoadingAddress, Status));
- return Status;
-}
-
-/**
-
- Loads and relocates a PE/COFF image into memory.
- If the image is not relocatable, it will not be loaded into memory and be loaded as XIP image.
-
- @param FileHandle - Pointer to the FFS file header of the image.
- @param Pe32Data - The base address of the PE/COFF file that is to be loaded and relocated
- @param ImageAddress - The base address of the relocated PE/COFF image
- @param ImageSize - The size of the relocated PE/COFF image
- @param EntryPoint - The entry point of the relocated PE/COFF image
-
- @retval EFI_SUCCESS The file was loaded and relocated
- @retval EFI_OUT_OF_RESOURCES There was not enough memory to load and relocate the PE/COFF file
- @retval EFI_WARN_BUFFER_TOO_SMALL
- There is not enough heap to allocate the requested size.
- This will not prevent the XIP image from being invoked.
-
-**/
-EFI_STATUS
-LoadAndRelocatePeCoffImage (
- IN EFI_PEI_FILE_HANDLE FileHandle,
- IN VOID *Pe32Data,
- OUT EFI_PHYSICAL_ADDRESS *ImageAddress,
- OUT UINT64 *ImageSize,
- OUT EFI_PHYSICAL_ADDRESS *EntryPoint
- )
-{
- EFI_STATUS Status;
- PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
- PEI_CORE_INSTANCE *Private;
- UINT64 AlignImageSize;
- BOOLEAN IsXipImage;
- EFI_STATUS ReturnStatus;
- BOOLEAN IsS3Boot;
- BOOLEAN IsPeiModule;
- BOOLEAN IsRegisterForShadow;
- EFI_FV_FILE_INFO FileInfo;
-
- Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ());
-
- ReturnStatus = EFI_SUCCESS;
- IsXipImage = FALSE;
- ZeroMem (&ImageContext, sizeof (ImageContext));
- ImageContext.Handle = Pe32Data;
- ImageContext.ImageRead = PeiImageRead;
-
- Status = PeCoffLoaderGetImageInfo (&ImageContext);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- //
- // Initialize local IsS3Boot and IsRegisterForShadow variable
- //
- IsS3Boot = FALSE;
- if (Private->HobList.HandoffInformationTable->BootMode == BOOT_ON_S3_RESUME) {
- IsS3Boot = TRUE;
- }
-
- IsRegisterForShadow = FALSE;
- if ( (Private->CurrentFileHandle == FileHandle)
- && (Private->Fv[Private->CurrentPeimFvCount].PeimState[Private->CurrentPeimCount] == PEIM_STATE_REGISTER_FOR_SHADOW))
- {
- IsRegisterForShadow = TRUE;
- }
-
- //
- // XIP image that ImageAddress is same to Image handle.
- //
- if (ImageContext.ImageAddress == (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data) {
- IsXipImage = TRUE;
- }
-
- //
- // Get file type first
- //
- Status = PeiServicesFfsGetFileInfo (FileHandle, &FileInfo);
- ASSERT_EFI_ERROR (Status);
-
- //
- // Check whether the file type is PEI module.
- //
- IsPeiModule = FALSE;
- if ((FileInfo.FileType == EFI_FV_FILETYPE_PEI_CORE) ||
- (FileInfo.FileType == EFI_FV_FILETYPE_PEIM) ||
- (FileInfo.FileType == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER))
- {
- IsPeiModule = TRUE;
- }
-
- //
- // When Image has no reloc section, it can't be relocated into memory.
- //
- if (ImageContext.RelocationsStripped && (Private->PeiMemoryInstalled) &&
- ((!IsPeiModule) || PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes) ||
- (!IsS3Boot && (PcdGetBool (PcdShadowPeimOnBoot) || IsRegisterForShadow)) ||
- (IsS3Boot && PcdGetBool (PcdShadowPeimOnS3Boot)))
- )
- {
- DEBUG ((DEBUG_INFO|DEBUG_LOAD, "The image at 0x%08x without reloc section can't be loaded into memory\n", (UINTN)Pe32Data));
- }
-
- //
- // Set default base address to current image address.
- //
- ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data;
-
- //
- // Allocate Memory for the image when memory is ready, and image is relocatable.
- // On normal boot, PcdShadowPeimOnBoot decides whether load PEIM or PeiCore into memory.
- // On S3 boot, PcdShadowPeimOnS3Boot decides whether load PEIM or PeiCore into memory.
- //
- if ((!ImageContext.RelocationsStripped) && (Private->PeiMemoryInstalled) &&
- ((!IsPeiModule) || PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes) ||
- (!IsS3Boot && (PcdGetBool (PcdShadowPeimOnBoot) || IsRegisterForShadow)) ||
- (IsS3Boot && PcdGetBool (PcdShadowPeimOnS3Boot)))
- )
- {
- //
- // Allocate more buffer to avoid buffer overflow.
- //
- if (ImageContext.IsTeImage) {
- AlignImageSize = ImageContext.ImageSize + ((EFI_TE_IMAGE_HEADER *)Pe32Data)->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER);
- } else {
- AlignImageSize = ImageContext.ImageSize;
- }
-
- if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) {
- AlignImageSize += ImageContext.SectionAlignment;
- }
-
- if ((PcdGet64 (PcdLoadModuleAtFixAddressEnable) != 0) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) {
- Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext, Private);
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n"));
- //
- // The PEIM is not assigned valid address, try to allocate page to load it.
- //
- Status = PeiServicesAllocatePages (
- EfiBootServicesCode,
- EFI_SIZE_TO_PAGES ((UINT32)AlignImageSize),
- &ImageContext.ImageAddress
- );
- }
- } else {
- Status = PeiServicesAllocatePages (
- EfiBootServicesCode,
- EFI_SIZE_TO_PAGES ((UINT32)AlignImageSize),
- &ImageContext.ImageAddress
- );
- }
-
- if (!EFI_ERROR (Status)) {
- //
- // Adjust the Image Address to make sure it is section alignment.
- //
- if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) {
- ImageContext.ImageAddress =
- (ImageContext.ImageAddress + ImageContext.SectionAlignment - 1) &
- ~((UINTN)ImageContext.SectionAlignment - 1);
- }
-
- //
- // Fix alignment requirement when Load IPF TeImage into memory.
- // Skip the reserved space for the stripped PeHeader when load TeImage into memory.
- //
- if (ImageContext.IsTeImage) {
- ImageContext.ImageAddress = ImageContext.ImageAddress +
- ((EFI_TE_IMAGE_HEADER *)Pe32Data)->StrippedSize -
- sizeof (EFI_TE_IMAGE_HEADER);
- }
- } else {
- //
- // No enough memory resource.
- //
- if (IsXipImage) {
- //
- // XIP image can still be invoked.
- //
- ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data;
- ReturnStatus = EFI_WARN_BUFFER_TOO_SMALL;
- } else {
- //
- // Non XIP image can't be loaded because no enough memory is allocated.
- //
- ASSERT (FALSE);
- return EFI_OUT_OF_RESOURCES;
- }
- }
- }
-
- //
- // Load the image to our new buffer
- //
- Status = PeCoffLoaderLoadImage (&ImageContext);
- if (EFI_ERROR (Status)) {
- if (ImageContext.ImageError == IMAGE_ERROR_INVALID_SECTION_ALIGNMENT) {
- DEBUG ((DEBUG_ERROR, "PEIM Image Address 0x%11p doesn't meet with section alignment 0x%x.\n", (VOID *)(UINTN)ImageContext.ImageAddress, ImageContext.SectionAlignment));
- }
-
- return Status;
- }
-
- //
- // Relocate the image in our new buffer
- //
- Status = PeCoffLoaderRelocateImage (&ImageContext);
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- //
- // Flush the instruction cache so the image data is written before we execute it
- //
- if (ImageContext.ImageAddress != (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data) {
- InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
- }
-
- *ImageAddress = ImageContext.ImageAddress;
- *ImageSize = ImageContext.ImageSize;
- *EntryPoint = ImageContext.EntryPoint;
-
- return ReturnStatus;
-}
-
-/**
- Loads and relocates a PE/COFF image in place.
-
- @param Pe32Data The base address of the PE/COFF file that is to be loaded and relocated
- @param ImageAddress The base address of the relocated PE/COFF image
-
- @retval EFI_SUCCESS The file was loaded and relocated.
- @retval Others The file not be loaded and error occurred.
-
-**/
-EFI_STATUS
-LoadAndRelocatePeCoffImageInPlace (
- IN VOID *Pe32Data,
- IN VOID *ImageAddress
- )
-{
- EFI_STATUS Status;
- PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
-
- ZeroMem (&ImageContext, sizeof (ImageContext));
- ImageContext.Handle = Pe32Data;
- ImageContext.ImageRead = PeiImageRead;
-
- Status = PeCoffLoaderGetImageInfo (&ImageContext);
- if (EFI_ERROR (Status)) {
- ASSERT_EFI_ERROR (Status);
- return Status;
- }
-
- ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)ImageAddress;
-
- //
- // Load the image in place
- //
- Status = PeCoffLoaderLoadImage (&ImageContext);
- if (EFI_ERROR (Status)) {
- ASSERT_EFI_ERROR (Status);
- return Status;
- }
-
- //
- // Relocate the image in place
- //
- Status = PeCoffLoaderRelocateImage (&ImageContext);
- if (EFI_ERROR (Status)) {
- ASSERT_EFI_ERROR (Status);
- return Status;
- }
-
- //
- // Flush the instruction cache so the image data is written before we execute it
- //
- if (ImageContext.ImageAddress != (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data) {
- InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
- }
-
- return Status;
-}
-
-/**
- Find the PE32 Data for an FFS file.
-
- @param FileHandle Pointer to the FFS file header of the image.
- @param Pe32Data Pointer to a (VOID *) PE32 Data pointer.
-
- @retval EFI_SUCCESS Image is successfully loaded.
- @retval EFI_NOT_FOUND Fail to locate PE32 Data.
-
-**/
-EFI_STATUS
-PeiGetPe32Data (
- IN EFI_PEI_FILE_HANDLE FileHandle,
- OUT VOID **Pe32Data
- )
-{
- EFI_STATUS Status;
- EFI_SECTION_TYPE SearchType1;
- EFI_SECTION_TYPE SearchType2;
- UINT32 AuthenticationState;
-
- *Pe32Data = NULL;
-
- if (FeaturePcdGet (PcdPeiCoreImageLoaderSearchTeSectionFirst)) {
- SearchType1 = EFI_SECTION_TE;
- SearchType2 = EFI_SECTION_PE32;
- } else {
- SearchType1 = EFI_SECTION_PE32;
- SearchType2 = EFI_SECTION_TE;
- }
-
- //
- // Try to find a first exe section (if PcdPeiCoreImageLoaderSearchTeSectionFirst
- // is true, TE will be searched first).
- //
- Status = PeiServicesFfsFindSectionData3 (
- SearchType1,
- 0,
- FileHandle,
- Pe32Data,
- &AuthenticationState
- );
- //
- // If we didn't find a first exe section, try to find the second exe section.
- //
- if (EFI_ERROR (Status)) {
- Status = PeiServicesFfsFindSectionData3 (
- SearchType2,
- 0,
- FileHandle,
- Pe32Data,
- &AuthenticationState
- );
- }
-
- return Status;
-}
-
-/**
- Loads a PEIM into memory for subsequent execution. If there are compressed
- images or images that need to be relocated into memory for performance reasons,
- this service performs that transformation.
-
- @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation
- @param FileHandle Pointer to the FFS file header of the image.
- @param ImageAddressArg Pointer to PE/TE image.
- @param ImageSizeArg Size of PE/TE image.
- @param EntryPoint Pointer to entry point of specified image file for output.
- @param AuthenticationState - Pointer to attestation authentication state of image.
-
- @retval EFI_SUCCESS Image is successfully loaded.
- @retval EFI_NOT_FOUND Fail to locate necessary PPI.
- @retval EFI_UNSUPPORTED Image Machine Type is not supported.
- @retval EFI_WARN_BUFFER_TOO_SMALL
- There is not enough heap to allocate the requested size.
- This will not prevent the XIP image from being invoked.
-
-**/
-EFI_STATUS
-PeiLoadImageLoadImage (
- IN CONST EFI_PEI_SERVICES **PeiServices,
- IN EFI_PEI_FILE_HANDLE FileHandle,
- OUT EFI_PHYSICAL_ADDRESS *ImageAddressArg OPTIONAL,
- OUT UINT64 *ImageSizeArg OPTIONAL,
- OUT EFI_PHYSICAL_ADDRESS *EntryPoint,
- OUT UINT32 *AuthenticationState
- )
-{
- EFI_STATUS Status;
- VOID *Pe32Data;
- EFI_PHYSICAL_ADDRESS ImageAddress;
- UINT64 ImageSize;
- EFI_PHYSICAL_ADDRESS ImageEntryPoint;
- UINT16 Machine;
- EFI_SECTION_TYPE SearchType1;
- EFI_SECTION_TYPE SearchType2;
-
- *EntryPoint = 0;
- ImageSize = 0;
- *AuthenticationState = 0;
-
- if (FeaturePcdGet (PcdPeiCoreImageLoaderSearchTeSectionFirst)) {
- SearchType1 = EFI_SECTION_TE;
- SearchType2 = EFI_SECTION_PE32;
- } else {
- SearchType1 = EFI_SECTION_PE32;
- SearchType2 = EFI_SECTION_TE;
- }
-
- //
- // Try to find a first exe section (if PcdPeiCoreImageLoaderSearchTeSectionFirst
- // is true, TE will be searched first).
- //
- Status = PeiServicesFfsFindSectionData3 (
- SearchType1,
- 0,
- FileHandle,
- &Pe32Data,
- AuthenticationState
- );
- //
- // If we didn't find a first exe section, try to find the second exe section.
- //
- if (EFI_ERROR (Status)) {
- Status = PeiServicesFfsFindSectionData3 (
- SearchType2,
- 0,
- FileHandle,
- &Pe32Data,
- AuthenticationState
- );
- if (EFI_ERROR (Status)) {
- //
- // PEI core only carry the loader function for TE and PE32 executables
- // If this two section does not exist, just return.
- //
- return Status;
- }
- }
-
- DEBUG ((DEBUG_INFO, "Loading PEIM %g\n", FileHandle));
-
- //
- // If memory is installed, perform the shadow operations
- //
- Status = LoadAndRelocatePeCoffImage (
- FileHandle,
- Pe32Data,
- &ImageAddress,
- &ImageSize,
- &ImageEntryPoint
- );
-
- if (EFI_ERROR (Status)) {
- return Status;
- }
-
- //
- // Got the entry point from the loaded Pe32Data
- //
- Pe32Data = (VOID *)((UINTN)ImageAddress);
- *EntryPoint = ImageEntryPoint;
-
- Machine = PeCoffLoaderGetMachineType (Pe32Data);
-
- if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Machine)) {
- if (!EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED (Machine)) {
- return EFI_UNSUPPORTED;
- }
- }
-
- if (ImageAddressArg != NULL) {
- *ImageAddressArg = ImageAddress;
- }
-
- if (ImageSizeArg != NULL) {
- *ImageSizeArg = ImageSize;
- }
-
- DEBUG_CODE_BEGIN ();
- CHAR8 *AsciiString;
- CHAR8 EfiFileName[512];
- INT32 Index;
- INT32 StartIndex;
-
- //
- // Print debug message: Loading PEIM at 0x12345678 EntryPoint=0x12345688 Driver.efi
- //
- if (Machine != EFI_IMAGE_MACHINE_IA64) {
- DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Loading PEIM at 0x%11p EntryPoint=0x%11p ", (VOID *)(UINTN)ImageAddress, (VOID *)(UINTN)*EntryPoint));
- } else {
- //
- // For IPF Image, the real entry point should be print.
- //
- DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Loading PEIM at 0x%11p EntryPoint=0x%11p ", (VOID *)(UINTN)ImageAddress, (VOID *)(UINTN)(*(UINT64 *)(UINTN)*EntryPoint)));
- }
-
- //
- // Print Module Name by PeImage PDB file name.
- //
- AsciiString = PeCoffLoaderGetPdbPointer (Pe32Data);
-
- if (AsciiString != NULL) {
- StartIndex = 0;
- for (Index = 0; AsciiString[Index] != 0; Index++) {
- if ((AsciiString[Index] == '\\') || (AsciiString[Index] == '/')) {
- StartIndex = Index + 1;
- }
- }
-
- //
- // Copy the PDB file name to our temporary string, and replace .pdb with .efi
- // The PDB file name is limited in the range of 0~511.
- // If the length is bigger than 511, trim the redundant characters to avoid overflow in array boundary.
- //
- for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) {
- EfiFileName[Index] = AsciiString[Index + StartIndex];
- if (EfiFileName[Index] == 0) {
- EfiFileName[Index] = '.';
- }
-
- if (EfiFileName[Index] == '.') {
- EfiFileName[Index + 1] = 'e';
- EfiFileName[Index + 2] = 'f';
- EfiFileName[Index + 3] = 'i';
- EfiFileName[Index + 4] = 0;
- break;
- }
- }
-
- if (Index == sizeof (EfiFileName) - 4) {
- EfiFileName[Index] = 0;
- }
-
- DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName));
- }
-
- DEBUG_CODE_END ();
-
- DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n"));
-
- return EFI_SUCCESS;
-}
-
-/**
- The wrapper function of PeiLoadImageLoadImage().
-
- @param This - Pointer to EFI_PEI_LOAD_FILE_PPI.
- @param FileHandle - Pointer to the FFS file header of the image.
- @param ImageAddressArg - Pointer to PE/TE image.
- @param ImageSizeArg - Size of PE/TE image.
- @param EntryPoint - Pointer to entry point of specified image file for output.
- @param AuthenticationState - Pointer to attestation authentication state of image.
-
- @return Status of PeiLoadImageLoadImage().
-
-**/
-EFI_STATUS
-EFIAPI
-PeiLoadImageLoadImageWrapper (
- IN CONST EFI_PEI_LOAD_FILE_PPI *This,
- IN EFI_PEI_FILE_HANDLE FileHandle,
- OUT EFI_PHYSICAL_ADDRESS *ImageAddressArg OPTIONAL,
- OUT UINT64 *ImageSizeArg OPTIONAL,
- OUT EFI_PHYSICAL_ADDRESS *EntryPoint,
- OUT UINT32 *AuthenticationState
- )
-{
- return PeiLoadImageLoadImage (
- GetPeiServicesTablePointer (),
- FileHandle,
- ImageAddressArg,
- ImageSizeArg,
- EntryPoint,
- AuthenticationState
- );
-}
-
-/**
- Check whether the input image has the relocation.
-
- @param Pe32Data Pointer to the PE/COFF or TE image.
-
- @retval TRUE Relocation is stripped.
- @retval FALSE Relocation is not stripped.
-
-**/
-BOOLEAN
-RelocationIsStrip (
- IN VOID *Pe32Data
- )
-{
- EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
- EFI_IMAGE_DOS_HEADER *DosHdr;
-
- ASSERT (Pe32Data != NULL);
-
- DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data;
- if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) {
- //
- // DOS image header is present, so read the PE header after the DOS image header.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)Pe32Data + (UINTN)((DosHdr->e_lfanew) & 0x0ffff));
- } else {
- //
- // DOS image header is not present, so PE header is at the image base.
- //
- Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data;
- }
-
- //
- // Three cases with regards to relocations:
- // - Image has base relocs, RELOCS_STRIPPED==0 => image is relocatable
- // - Image has no base relocs, RELOCS_STRIPPED==1 => Image is not relocatable
- // - Image has no base relocs, RELOCS_STRIPPED==0 => Image is relocatable but
- // has no base relocs to apply
- // Obviously having base relocations with RELOCS_STRIPPED==1 is invalid.
- //
- // Look at the file header to determine if relocations have been stripped, and
- // save this info in the image context for later use.
- //
- if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
- if ((Hdr.Te->DataDirectory[0].Size == 0) && (Hdr.Te->DataDirectory[0].VirtualAddress == 0)) {
- return TRUE;
- } else {
- return FALSE;
- }
- } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) {
- if ((Hdr.Pe32->FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) != 0) {
- return TRUE;
- } else {
- return FALSE;
- }
- }
-
- return FALSE;
-}
-
-/**
- Routine to load image file for subsequent execution by LoadFile Ppi.
- If any LoadFile Ppi is not found, the build-in support function for the PE32+/TE
- XIP image format is used.
-
- @param PeiServices - An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation
- @param FileHandle - Pointer to the FFS file header of the image.
- @param PeimState - The dispatch state of the input PEIM handle.
- @param EntryPoint - Pointer to entry point of specified image file for output.
- @param AuthenticationState - Pointer to attestation authentication state of image.
-
- @retval EFI_SUCCESS - Image is successfully loaded.
- @retval EFI_NOT_FOUND - Fail to locate necessary PPI
- @retval Others - Fail to load file.
-
-**/
-EFI_STATUS
-PeiLoadImage (
- IN CONST EFI_PEI_SERVICES **PeiServices,
- IN EFI_PEI_FILE_HANDLE FileHandle,
- IN UINT8 PeimState,
- OUT EFI_PHYSICAL_ADDRESS *EntryPoint,
- OUT UINT32 *AuthenticationState
- )
-{
- EFI_STATUS PpiStatus;
- EFI_STATUS Status;
- UINTN Index;
- EFI_PEI_LOAD_FILE_PPI *LoadFile;
- EFI_PHYSICAL_ADDRESS ImageAddress;
- UINT64 ImageSize;
- BOOLEAN IsStrip;
-
- IsStrip = FALSE;
- //
- // If any instances of PEI_LOAD_FILE_PPI are installed, they are called.
- // one at a time, until one reports EFI_SUCCESS.
- //
- Index = 0;
- do {
- PpiStatus = PeiServicesLocatePpi (
- &gEfiPeiLoadFilePpiGuid,
- Index,
- NULL,
- (VOID **)&LoadFile
- );
- if (!EFI_ERROR (PpiStatus)) {
- Status = LoadFile->LoadFile (
- LoadFile,
- FileHandle,
- &ImageAddress,
- &ImageSize,
- EntryPoint,
- AuthenticationState
- );
- if (!EFI_ERROR (Status) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) {
- //
- // The shadowed PEIM must be relocatable.
- //
- if (PeimState == PEIM_STATE_REGISTER_FOR_SHADOW) {
- IsStrip = RelocationIsStrip ((VOID *)(UINTN)ImageAddress);
- ASSERT (!IsStrip);
- if (IsStrip) {
- return EFI_UNSUPPORTED;
- }
- }
-
- //
- // The image to be started must have the machine type supported by PeiCore.
- //
- ASSERT (EFI_IMAGE_MACHINE_TYPE_SUPPORTED (PeCoffLoaderGetMachineType ((VOID *)(UINTN)ImageAddress)));
- if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (PeCoffLoaderGetMachineType ((VOID *)(UINTN)ImageAddress))) {
- return EFI_UNSUPPORTED;
- }
-
- return EFI_SUCCESS;
- }
- }
-
- Index++;
- } while (!EFI_ERROR (PpiStatus));
-
- return PpiStatus;
-}
-
-/**
-
- Install Pei Load File PPI.
-
-
- @param PrivateData - Pointer to PEI_CORE_INSTANCE.
- @param OldCoreData - Pointer to PEI_CORE_INSTANCE.
-
-**/
-VOID
-InitializeImageServices (
- IN PEI_CORE_INSTANCE *PrivateData,
- IN PEI_CORE_INSTANCE *OldCoreData
- )
-{
- if (OldCoreData == NULL) {
- //
- // The first time we are XIP (running from FLASH). We need to remember the
- // FLASH address so we can reinstall the memory version that runs faster
- //
- PrivateData->XipLoadFile = &gPpiLoadFilePpiList;
- PeiServicesInstallPpi (PrivateData->XipLoadFile);
- } else {
- //
- // 2nd time we are running from memory so replace the XIP version with the
- // new memory version.
- //
- PeiServicesReInstallPpi (PrivateData->XipLoadFile, &gPpiLoadFilePpiList);
- }
-}
+/** @file + Pei Core Load Image Support + +Copyright (c) 2006 - 2021, Intel Corporation. All rights reserved.<BR> +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "PeiMain.h" + +EFI_PEI_LOAD_FILE_PPI mPeiLoadImagePpi = { + PeiLoadImageLoadImageWrapper +}; + +EFI_PEI_PPI_DESCRIPTOR gPpiLoadFilePpiList = { + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gEfiPeiLoadFilePpiGuid, + &mPeiLoadImagePpi +}; + +/** + + Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file. + The function is used for XIP code to have optimized memory copy. + + @param FileHandle - The handle to the PE/COFF file + @param FileOffset - The offset, in bytes, into the file to read + @param ReadSize - The number of bytes to read from the file starting at FileOffset + @param Buffer - A pointer to the buffer to read the data into. + + @return EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset + +**/ +EFI_STATUS +EFIAPI +PeiImageRead ( + IN VOID *FileHandle, + IN UINTN FileOffset, + IN UINTN *ReadSize, + OUT VOID *Buffer + ) +{ + CHAR8 *Destination8; + CHAR8 *Source8; + + Destination8 = Buffer; + Source8 = (CHAR8 *)((UINTN)FileHandle + FileOffset); + if (Destination8 != Source8) { + CopyMem (Destination8, Source8, *ReadSize); + } + + return EFI_SUCCESS; +} + +/** + To check memory usage bit map array to figure out if the memory range the image will be loaded in is available or not. If + memory range is available, the function will mark the corresponding bits to 1 which indicates the memory range is used. + The function is only invoked when load modules at fixed address feature is enabled. + + @param Private Pointer to the private data passed in from caller + @param ImageBase The base address the image will be loaded at. + @param ImageSize The size of the image + + @retval EFI_SUCCESS The memory range the image will be loaded in is available + @retval EFI_NOT_FOUND The memory range the image will be loaded in is not available +**/ +EFI_STATUS +CheckAndMarkFixLoadingMemoryUsageBitMap ( + IN PEI_CORE_INSTANCE *Private, + IN EFI_PHYSICAL_ADDRESS ImageBase, + IN UINT32 ImageSize + ) +{ + UINT32 DxeCodePageNumber; + UINT64 ReservedCodeSize; + EFI_PHYSICAL_ADDRESS PeiCodeBase; + UINT32 BaseOffsetPageNumber; + UINT32 TopOffsetPageNumber; + UINT32 Index; + UINT64 *MemoryUsageBitMap; + + // + // The reserved code range includes RuntimeCodePage range, Boot time code range and PEI code range. + // + DxeCodePageNumber = PcdGet32 (PcdLoadFixAddressBootTimeCodePageNumber); + DxeCodePageNumber += PcdGet32 (PcdLoadFixAddressRuntimeCodePageNumber); + ReservedCodeSize = EFI_PAGES_TO_SIZE (DxeCodePageNumber + PcdGet32 (PcdLoadFixAddressPeiCodePageNumber)); + PeiCodeBase = Private->LoadModuleAtFixAddressTopAddress - ReservedCodeSize; + + // + // Test the memory range for loading the image in the PEI code range. + // + if (((Private->LoadModuleAtFixAddressTopAddress - EFI_PAGES_TO_SIZE (DxeCodePageNumber)) < (ImageBase + ImageSize)) || + (PeiCodeBase > ImageBase)) + { + return EFI_NOT_FOUND; + } + + // + // Test if the memory is available or not. + // + MemoryUsageBitMap = Private->PeiCodeMemoryRangeUsageBitMap; + BaseOffsetPageNumber = EFI_SIZE_TO_PAGES ((UINT32)(ImageBase - PeiCodeBase)); + TopOffsetPageNumber = EFI_SIZE_TO_PAGES ((UINT32)(ImageBase + ImageSize - PeiCodeBase)); + for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index++) { + if ((MemoryUsageBitMap[Index / 64] & LShiftU64 (1, (Index % 64))) != 0) { + // + // This page is already used. + // + return EFI_NOT_FOUND; + } + } + + // + // Being here means the memory range is available. So mark the bits for the memory range + // + for (Index = BaseOffsetPageNumber; Index < TopOffsetPageNumber; Index++) { + MemoryUsageBitMap[Index / 64] |= LShiftU64 (1, (Index % 64)); + } + + return EFI_SUCCESS; +} + +/** + + Get the fixed loading address from image header assigned by build tool. This function only be called + when Loading module at Fixed address feature enabled. + + @param ImageContext Pointer to the image context structure that describes the PE/COFF + image that needs to be examined by this function. + @param Private Pointer to the private data passed in from caller + + @retval EFI_SUCCESS An fixed loading address is assigned to this image by build tools . + @retval EFI_NOT_FOUND The image has no assigned fixed loading address. + +**/ +EFI_STATUS +GetPeCoffImageFixLoadingAssignedAddress ( + IN OUT PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext, + IN PEI_CORE_INSTANCE *Private + ) +{ + UINTN SectionHeaderOffset; + EFI_STATUS Status; + EFI_IMAGE_SECTION_HEADER SectionHeader; + EFI_IMAGE_OPTIONAL_HEADER_UNION *ImgHdr; + EFI_PHYSICAL_ADDRESS FixLoadingAddress; + UINT16 Index; + UINTN Size; + UINT16 NumberOfSections; + UINT64 ValueInSectionHeader; + + FixLoadingAddress = 0; + Status = EFI_NOT_FOUND; + + // + // Get PeHeader pointer + // + ImgHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((CHAR8 *)ImageContext->Handle + ImageContext->PeCoffHeaderOffset); + if (ImageContext->IsTeImage) { + // + // for TE image, the fix loading address is saved in first section header that doesn't point + // to code section. + // + SectionHeaderOffset = sizeof (EFI_TE_IMAGE_HEADER); + NumberOfSections = ImgHdr->Te.NumberOfSections; + } else { + SectionHeaderOffset = ImageContext->PeCoffHeaderOffset + + sizeof (UINT32) + + sizeof (EFI_IMAGE_FILE_HEADER) + + ImgHdr->Pe32.FileHeader.SizeOfOptionalHeader; + NumberOfSections = ImgHdr->Pe32.FileHeader.NumberOfSections; + } + + // + // Get base address from the first section header that doesn't point to code section. + // + for (Index = 0; Index < NumberOfSections; Index++) { + // + // Read section header from file + // + Size = sizeof (EFI_IMAGE_SECTION_HEADER); + Status = ImageContext->ImageRead ( + ImageContext->Handle, + SectionHeaderOffset, + &Size, + &SectionHeader + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = EFI_NOT_FOUND; + + if ((SectionHeader.Characteristics & EFI_IMAGE_SCN_CNT_CODE) == 0) { + // + // Build tool will save the address in PointerToRelocations & PointerToLineNumbers fields in the first section header + // that doesn't point to code section in image header, as well as ImageBase field of image header. A notable thing is + // that for PEIM, the value in ImageBase field may not be equal to the value in PointerToRelocations & PointerToLineNumbers because + // for XIP PEIM, ImageBase field holds the image base address running on the Flash. And PointerToRelocations & PointerToLineNumbers + // hold the image base address when it is shadow to the memory. And there is an assumption that when the feature is enabled, if a + // module is assigned a loading address by tools, PointerToRelocations & PointerToLineNumbers fields should NOT be Zero, or + // else, these 2 fields should be set to Zero + // + ValueInSectionHeader = ReadUnaligned64 ((UINT64 *)&SectionHeader.PointerToRelocations); + if (ValueInSectionHeader != 0) { + // + // Found first section header that doesn't point to code section. + // + if ((INT64)PcdGet64 (PcdLoadModuleAtFixAddressEnable) > 0) { + // + // When LMFA feature is configured as Load Module at Fixed Absolute Address mode, PointerToRelocations & PointerToLineNumbers field + // hold the absolute address of image base running in memory + // + FixLoadingAddress = ValueInSectionHeader; + } else { + // + // When LMFA feature is configured as Load Module at Fixed offset mode, PointerToRelocations & PointerToLineNumbers field + // hold the offset relative to a platform-specific top address. + // + FixLoadingAddress = (EFI_PHYSICAL_ADDRESS)(Private->LoadModuleAtFixAddressTopAddress + (INT64)ValueInSectionHeader); + } + + // + // Check if the memory range is available. + // + Status = CheckAndMarkFixLoadingMemoryUsageBitMap (Private, FixLoadingAddress, (UINT32)ImageContext->ImageSize); + if (!EFI_ERROR (Status)) { + // + // The assigned address is valid. Return the specified loading address + // + ImageContext->ImageAddress = FixLoadingAddress; + } + } + + break; + } + + SectionHeaderOffset += sizeof (EFI_IMAGE_SECTION_HEADER); + } + + DEBUG ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address 0x%11p. Status= %r \n", (VOID *)(UINTN)FixLoadingAddress, Status)); + return Status; +} + +/** + + Loads and relocates a PE/COFF image into memory. + If the image is not relocatable, it will not be loaded into memory and be loaded as XIP image. + + @param FileHandle - Pointer to the FFS file header of the image. + @param Pe32Data - The base address of the PE/COFF file that is to be loaded and relocated + @param ImageAddress - The base address of the relocated PE/COFF image + @param ImageSize - The size of the relocated PE/COFF image + @param EntryPoint - The entry point of the relocated PE/COFF image + + @retval EFI_SUCCESS The file was loaded and relocated + @retval EFI_OUT_OF_RESOURCES There was not enough memory to load and relocate the PE/COFF file + @retval EFI_WARN_BUFFER_TOO_SMALL + There is not enough heap to allocate the requested size. + This will not prevent the XIP image from being invoked. + +**/ +EFI_STATUS +LoadAndRelocatePeCoffImage ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN VOID *Pe32Data, + OUT EFI_PHYSICAL_ADDRESS *ImageAddress, + OUT UINT64 *ImageSize, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint + ) +{ + EFI_STATUS Status; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + PEI_CORE_INSTANCE *Private; + UINT64 AlignImageSize; + BOOLEAN IsXipImage; + EFI_STATUS ReturnStatus; + BOOLEAN IsS3Boot; + BOOLEAN IsPeiModule; + BOOLEAN IsRegisterForShadow; + EFI_FV_FILE_INFO FileInfo; + + Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); + + ReturnStatus = EFI_SUCCESS; + IsXipImage = FALSE; + ZeroMem (&ImageContext, sizeof (ImageContext)); + ImageContext.Handle = Pe32Data; + ImageContext.ImageRead = PeiImageRead; + + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Initialize local IsS3Boot and IsRegisterForShadow variable + // + IsS3Boot = FALSE; + if (Private->HobList.HandoffInformationTable->BootMode == BOOT_ON_S3_RESUME) { + IsS3Boot = TRUE; + } + + IsRegisterForShadow = FALSE; + if ( (Private->CurrentFileHandle == FileHandle) + && (Private->Fv[Private->CurrentPeimFvCount].PeimState[Private->CurrentPeimCount] == PEIM_STATE_REGISTER_FOR_SHADOW)) + { + IsRegisterForShadow = TRUE; + } + + // + // XIP image that ImageAddress is same to Image handle. + // + if (ImageContext.ImageAddress == (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data) { + IsXipImage = TRUE; + } + + // + // Get file type first + // + Status = PeiServicesFfsGetFileInfo (FileHandle, &FileInfo); + ASSERT_EFI_ERROR (Status); + + // + // Check whether the file type is PEI module. + // + IsPeiModule = FALSE; + if ((FileInfo.FileType == EFI_FV_FILETYPE_PEI_CORE) || + (FileInfo.FileType == EFI_FV_FILETYPE_PEIM) || + (FileInfo.FileType == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER)) + { + IsPeiModule = TRUE; + } + + // + // When Image has no reloc section, it can't be relocated into memory. + // + if (ImageContext.RelocationsStripped && (Private->PeiMemoryInstalled) && + ((!IsPeiModule) || PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes) || + (!IsS3Boot && (PcdGetBool (PcdShadowPeimOnBoot) || IsRegisterForShadow)) || + (IsS3Boot && PcdGetBool (PcdShadowPeimOnS3Boot))) + ) + { + DEBUG ((DEBUG_INFO|DEBUG_LOAD, "The image at 0x%08x without reloc section can't be loaded into memory\n", (UINTN)Pe32Data)); + } + + // + // Set default base address to current image address. + // + ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data; + + // + // Allocate Memory for the image when memory is ready, and image is relocatable. + // On normal boot, PcdShadowPeimOnBoot decides whether load PEIM or PeiCore into memory. + // On S3 boot, PcdShadowPeimOnS3Boot decides whether load PEIM or PeiCore into memory. + // + if ((!ImageContext.RelocationsStripped) && (Private->PeiMemoryInstalled) && + ((!IsPeiModule) || PcdGetBool (PcdMigrateTemporaryRamFirmwareVolumes) || + (!IsS3Boot && (PcdGetBool (PcdShadowPeimOnBoot) || IsRegisterForShadow)) || + (IsS3Boot && PcdGetBool (PcdShadowPeimOnS3Boot))) + ) + { + // + // Allocate more buffer to avoid buffer overflow. + // + if (ImageContext.IsTeImage) { + AlignImageSize = ImageContext.ImageSize + ((EFI_TE_IMAGE_HEADER *)Pe32Data)->StrippedSize - sizeof (EFI_TE_IMAGE_HEADER); + } else { + AlignImageSize = ImageContext.ImageSize; + } + + if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { + AlignImageSize += ImageContext.SectionAlignment; + } + + if ((PcdGet64 (PcdLoadModuleAtFixAddressEnable) != 0) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { + Status = GetPeCoffImageFixLoadingAssignedAddress (&ImageContext, Private); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO|DEBUG_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n")); + // + // The PEIM is not assigned valid address, try to allocate page to load it. + // + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + EFI_SIZE_TO_PAGES ((UINT32)AlignImageSize), + &ImageContext.ImageAddress + ); + } + } else { + Status = PeiServicesAllocatePages ( + EfiBootServicesCode, + EFI_SIZE_TO_PAGES ((UINT32)AlignImageSize), + &ImageContext.ImageAddress + ); + } + + if (!EFI_ERROR (Status)) { + // + // Adjust the Image Address to make sure it is section alignment. + // + if (ImageContext.SectionAlignment > EFI_PAGE_SIZE) { + ImageContext.ImageAddress = + (ImageContext.ImageAddress + ImageContext.SectionAlignment - 1) & + ~((UINTN)ImageContext.SectionAlignment - 1); + } + + // + // Fix alignment requirement when Load IPF TeImage into memory. + // Skip the reserved space for the stripped PeHeader when load TeImage into memory. + // + if (ImageContext.IsTeImage) { + ImageContext.ImageAddress = ImageContext.ImageAddress + + ((EFI_TE_IMAGE_HEADER *)Pe32Data)->StrippedSize - + sizeof (EFI_TE_IMAGE_HEADER); + } + } else { + // + // No enough memory resource. + // + if (IsXipImage) { + // + // XIP image can still be invoked. + // + ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data; + ReturnStatus = EFI_WARN_BUFFER_TOO_SMALL; + } else { + // + // Non XIP image can't be loaded because no enough memory is allocated. + // + ASSERT (FALSE); + return EFI_OUT_OF_RESOURCES; + } + } + } + + // + // Load the image to our new buffer + // + Status = PeCoffLoaderLoadImage (&ImageContext); + if (EFI_ERROR (Status)) { + if (ImageContext.ImageError == IMAGE_ERROR_INVALID_SECTION_ALIGNMENT) { + DEBUG ((DEBUG_ERROR, "PEIM Image Address 0x%11p doesn't meet with section alignment 0x%x.\n", (VOID *)(UINTN)ImageContext.ImageAddress, ImageContext.SectionAlignment)); + } + + return Status; + } + + // + // Relocate the image in our new buffer + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Flush the instruction cache so the image data is written before we execute it + // + if (ImageContext.ImageAddress != (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data) { + InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); + } + + *ImageAddress = ImageContext.ImageAddress; + *ImageSize = ImageContext.ImageSize; + *EntryPoint = ImageContext.EntryPoint; + + return ReturnStatus; +} + +/** + Loads and relocates a PE/COFF image in place. + + @param Pe32Data The base address of the PE/COFF file that is to be loaded and relocated + @param ImageAddress The base address of the relocated PE/COFF image + + @retval EFI_SUCCESS The file was loaded and relocated. + @retval Others The file not be loaded and error occurred. + +**/ +EFI_STATUS +LoadAndRelocatePeCoffImageInPlace ( + IN VOID *Pe32Data, + IN VOID *ImageAddress + ) +{ + EFI_STATUS Status; + PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; + + ZeroMem (&ImageContext, sizeof (ImageContext)); + ImageContext.Handle = Pe32Data; + ImageContext.ImageRead = PeiImageRead; + + Status = PeCoffLoaderGetImageInfo (&ImageContext); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)ImageAddress; + + // + // Load the image in place + // + Status = PeCoffLoaderLoadImage (&ImageContext); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // + // Relocate the image in place + // + Status = PeCoffLoaderRelocateImage (&ImageContext); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // + // Flush the instruction cache so the image data is written before we execute it + // + if (ImageContext.ImageAddress != (EFI_PHYSICAL_ADDRESS)(UINTN)Pe32Data) { + InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); + } + + return Status; +} + +/** + Find the PE32 Data for an FFS file. + + @param FileHandle Pointer to the FFS file header of the image. + @param Pe32Data Pointer to a (VOID *) PE32 Data pointer. + + @retval EFI_SUCCESS Image is successfully loaded. + @retval EFI_NOT_FOUND Fail to locate PE32 Data. + +**/ +EFI_STATUS +PeiGetPe32Data ( + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT VOID **Pe32Data + ) +{ + EFI_STATUS Status; + EFI_SECTION_TYPE SearchType1; + EFI_SECTION_TYPE SearchType2; + UINT32 AuthenticationState; + + *Pe32Data = NULL; + + if (FeaturePcdGet (PcdPeiCoreImageLoaderSearchTeSectionFirst)) { + SearchType1 = EFI_SECTION_TE; + SearchType2 = EFI_SECTION_PE32; + } else { + SearchType1 = EFI_SECTION_PE32; + SearchType2 = EFI_SECTION_TE; + } + + // + // Try to find a first exe section (if PcdPeiCoreImageLoaderSearchTeSectionFirst + // is true, TE will be searched first). + // + Status = PeiServicesFfsFindSectionData3 ( + SearchType1, + 0, + FileHandle, + Pe32Data, + &AuthenticationState + ); + // + // If we didn't find a first exe section, try to find the second exe section. + // + if (EFI_ERROR (Status)) { + Status = PeiServicesFfsFindSectionData3 ( + SearchType2, + 0, + FileHandle, + Pe32Data, + &AuthenticationState + ); + } + + return Status; +} + +/** + Loads a PEIM into memory for subsequent execution. If there are compressed + images or images that need to be relocated into memory for performance reasons, + this service performs that transformation. + + @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation + @param FileHandle Pointer to the FFS file header of the image. + @param ImageAddressArg Pointer to PE/TE image. + @param ImageSizeArg Size of PE/TE image. + @param EntryPoint Pointer to entry point of specified image file for output. + @param AuthenticationState - Pointer to attestation authentication state of image. + + @retval EFI_SUCCESS Image is successfully loaded. + @retval EFI_NOT_FOUND Fail to locate necessary PPI. + @retval EFI_UNSUPPORTED Image Machine Type is not supported. + @retval EFI_WARN_BUFFER_TOO_SMALL + There is not enough heap to allocate the requested size. + This will not prevent the XIP image from being invoked. + +**/ +EFI_STATUS +PeiLoadImageLoadImage ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT EFI_PHYSICAL_ADDRESS *ImageAddressArg OPTIONAL, + OUT UINT64 *ImageSizeArg OPTIONAL, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint, + OUT UINT32 *AuthenticationState + ) +{ + EFI_STATUS Status; + VOID *Pe32Data; + EFI_PHYSICAL_ADDRESS ImageAddress; + UINT64 ImageSize; + EFI_PHYSICAL_ADDRESS ImageEntryPoint; + UINT16 Machine; + EFI_SECTION_TYPE SearchType1; + EFI_SECTION_TYPE SearchType2; + + *EntryPoint = 0; + ImageSize = 0; + *AuthenticationState = 0; + + if (FeaturePcdGet (PcdPeiCoreImageLoaderSearchTeSectionFirst)) { + SearchType1 = EFI_SECTION_TE; + SearchType2 = EFI_SECTION_PE32; + } else { + SearchType1 = EFI_SECTION_PE32; + SearchType2 = EFI_SECTION_TE; + } + + // + // Try to find a first exe section (if PcdPeiCoreImageLoaderSearchTeSectionFirst + // is true, TE will be searched first). + // + Status = PeiServicesFfsFindSectionData3 ( + SearchType1, + 0, + FileHandle, + &Pe32Data, + AuthenticationState + ); + // + // If we didn't find a first exe section, try to find the second exe section. + // + if (EFI_ERROR (Status)) { + Status = PeiServicesFfsFindSectionData3 ( + SearchType2, + 0, + FileHandle, + &Pe32Data, + AuthenticationState + ); + if (EFI_ERROR (Status)) { + // + // PEI core only carry the loader function for TE and PE32 executables + // If this two section does not exist, just return. + // + return Status; + } + } + + DEBUG ((DEBUG_INFO, "Loading PEIM %g\n", FileHandle)); + + // + // If memory is installed, perform the shadow operations + // + Status = LoadAndRelocatePeCoffImage ( + FileHandle, + Pe32Data, + &ImageAddress, + &ImageSize, + &ImageEntryPoint + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Got the entry point from the loaded Pe32Data + // + Pe32Data = (VOID *)((UINTN)ImageAddress); + *EntryPoint = ImageEntryPoint; + + Machine = PeCoffLoaderGetMachineType (Pe32Data); + + if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Machine)) { + if (!EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED (Machine)) { + return EFI_UNSUPPORTED; + } + } + + if (ImageAddressArg != NULL) { + *ImageAddressArg = ImageAddress; + } + + if (ImageSizeArg != NULL) { + *ImageSizeArg = ImageSize; + } + + DEBUG_CODE_BEGIN (); + CHAR8 *AsciiString; + CHAR8 EfiFileName[512]; + INT32 Index; + INT32 StartIndex; + + // + // Print debug message: Loading PEIM at 0x12345678 EntryPoint=0x12345688 Driver.efi + // + if (Machine != EFI_IMAGE_MACHINE_IA64) { + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Loading PEIM at 0x%11p EntryPoint=0x%11p ", (VOID *)(UINTN)ImageAddress, (VOID *)(UINTN)*EntryPoint)); + } else { + // + // For IPF Image, the real entry point should be print. + // + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Loading PEIM at 0x%11p EntryPoint=0x%11p ", (VOID *)(UINTN)ImageAddress, (VOID *)(UINTN)(*(UINT64 *)(UINTN)*EntryPoint))); + } + + // + // Print Module Name by PeImage PDB file name. + // + AsciiString = PeCoffLoaderGetPdbPointer (Pe32Data); + + if (AsciiString != NULL) { + StartIndex = 0; + for (Index = 0; AsciiString[Index] != 0; Index++) { + if ((AsciiString[Index] == '\\') || (AsciiString[Index] == '/')) { + StartIndex = Index + 1; + } + } + + // + // Copy the PDB file name to our temporary string, and replace .pdb with .efi + // The PDB file name is limited in the range of 0~511. + // If the length is bigger than 511, trim the redundant characters to avoid overflow in array boundary. + // + for (Index = 0; Index < sizeof (EfiFileName) - 4; Index++) { + EfiFileName[Index] = AsciiString[Index + StartIndex]; + if (EfiFileName[Index] == 0) { + EfiFileName[Index] = '.'; + } + + if (EfiFileName[Index] == '.') { + EfiFileName[Index + 1] = 'e'; + EfiFileName[Index + 2] = 'f'; + EfiFileName[Index + 3] = 'i'; + EfiFileName[Index + 4] = 0; + break; + } + } + + if (Index == sizeof (EfiFileName) - 4) { + EfiFileName[Index] = 0; + } + + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "%a", EfiFileName)); + } + + DEBUG_CODE_END (); + + DEBUG ((DEBUG_INFO | DEBUG_LOAD, "\n")); + + return EFI_SUCCESS; +} + +/** + The wrapper function of PeiLoadImageLoadImage(). + + @param This - Pointer to EFI_PEI_LOAD_FILE_PPI. + @param FileHandle - Pointer to the FFS file header of the image. + @param ImageAddressArg - Pointer to PE/TE image. + @param ImageSizeArg - Size of PE/TE image. + @param EntryPoint - Pointer to entry point of specified image file for output. + @param AuthenticationState - Pointer to attestation authentication state of image. + + @return Status of PeiLoadImageLoadImage(). + +**/ +EFI_STATUS +EFIAPI +PeiLoadImageLoadImageWrapper ( + IN CONST EFI_PEI_LOAD_FILE_PPI *This, + IN EFI_PEI_FILE_HANDLE FileHandle, + OUT EFI_PHYSICAL_ADDRESS *ImageAddressArg OPTIONAL, + OUT UINT64 *ImageSizeArg OPTIONAL, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint, + OUT UINT32 *AuthenticationState + ) +{ + return PeiLoadImageLoadImage ( + GetPeiServicesTablePointer (), + FileHandle, + ImageAddressArg, + ImageSizeArg, + EntryPoint, + AuthenticationState + ); +} + +/** + Check whether the input image has the relocation. + + @param Pe32Data Pointer to the PE/COFF or TE image. + + @retval TRUE Relocation is stripped. + @retval FALSE Relocation is not stripped. + +**/ +BOOLEAN +RelocationIsStrip ( + IN VOID *Pe32Data + ) +{ + EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; + EFI_IMAGE_DOS_HEADER *DosHdr; + + ASSERT (Pe32Data != NULL); + + DosHdr = (EFI_IMAGE_DOS_HEADER *)Pe32Data; + if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { + // + // DOS image header is present, so read the PE header after the DOS image header. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINTN)Pe32Data + (UINTN)((DosHdr->e_lfanew) & 0x0ffff)); + } else { + // + // DOS image header is not present, so PE header is at the image base. + // + Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)Pe32Data; + } + + // + // Three cases with regards to relocations: + // - Image has base relocs, RELOCS_STRIPPED==0 => image is relocatable + // - Image has no base relocs, RELOCS_STRIPPED==1 => Image is not relocatable + // - Image has no base relocs, RELOCS_STRIPPED==0 => Image is relocatable but + // has no base relocs to apply + // Obviously having base relocations with RELOCS_STRIPPED==1 is invalid. + // + // Look at the file header to determine if relocations have been stripped, and + // save this info in the image context for later use. + // + if (Hdr.Te->Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { + if ((Hdr.Te->DataDirectory[0].Size == 0) && (Hdr.Te->DataDirectory[0].VirtualAddress == 0)) { + return TRUE; + } else { + return FALSE; + } + } else if (Hdr.Pe32->Signature == EFI_IMAGE_NT_SIGNATURE) { + if ((Hdr.Pe32->FileHeader.Characteristics & EFI_IMAGE_FILE_RELOCS_STRIPPED) != 0) { + return TRUE; + } else { + return FALSE; + } + } + + return FALSE; +} + +/** + Routine to load image file for subsequent execution by LoadFile Ppi. + If any LoadFile Ppi is not found, the build-in support function for the PE32+/TE + XIP image format is used. + + @param PeiServices - An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation + @param FileHandle - Pointer to the FFS file header of the image. + @param PeimState - The dispatch state of the input PEIM handle. + @param EntryPoint - Pointer to entry point of specified image file for output. + @param AuthenticationState - Pointer to attestation authentication state of image. + + @retval EFI_SUCCESS - Image is successfully loaded. + @retval EFI_NOT_FOUND - Fail to locate necessary PPI + @retval Others - Fail to load file. + +**/ +EFI_STATUS +PeiLoadImage ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PEI_FILE_HANDLE FileHandle, + IN UINT8 PeimState, + OUT EFI_PHYSICAL_ADDRESS *EntryPoint, + OUT UINT32 *AuthenticationState + ) +{ + EFI_STATUS PpiStatus; + EFI_STATUS Status; + UINTN Index; + EFI_PEI_LOAD_FILE_PPI *LoadFile; + EFI_PHYSICAL_ADDRESS ImageAddress; + UINT64 ImageSize; + BOOLEAN IsStrip; + + IsStrip = FALSE; + // + // If any instances of PEI_LOAD_FILE_PPI are installed, they are called. + // one at a time, until one reports EFI_SUCCESS. + // + Index = 0; + do { + PpiStatus = PeiServicesLocatePpi ( + &gEfiPeiLoadFilePpiGuid, + Index, + NULL, + (VOID **)&LoadFile + ); + if (!EFI_ERROR (PpiStatus)) { + Status = LoadFile->LoadFile ( + LoadFile, + FileHandle, + &ImageAddress, + &ImageSize, + EntryPoint, + AuthenticationState + ); + if (!EFI_ERROR (Status) || (Status == EFI_WARN_BUFFER_TOO_SMALL)) { + // + // The shadowed PEIM must be relocatable. + // + if (PeimState == PEIM_STATE_REGISTER_FOR_SHADOW) { + IsStrip = RelocationIsStrip ((VOID *)(UINTN)ImageAddress); + ASSERT (!IsStrip); + if (IsStrip) { + return EFI_UNSUPPORTED; + } + } + + // + // The image to be started must have the machine type supported by PeiCore. + // + ASSERT (EFI_IMAGE_MACHINE_TYPE_SUPPORTED (PeCoffLoaderGetMachineType ((VOID *)(UINTN)ImageAddress))); + if (!EFI_IMAGE_MACHINE_TYPE_SUPPORTED (PeCoffLoaderGetMachineType ((VOID *)(UINTN)ImageAddress))) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; + } + } + + Index++; + } while (!EFI_ERROR (PpiStatus)); + + return PpiStatus; +} + +/** + + Install Pei Load File PPI. + + + @param PrivateData - Pointer to PEI_CORE_INSTANCE. + @param OldCoreData - Pointer to PEI_CORE_INSTANCE. + +**/ +VOID +InitializeImageServices ( + IN PEI_CORE_INSTANCE *PrivateData, + IN PEI_CORE_INSTANCE *OldCoreData + ) +{ + if (OldCoreData == NULL) { + // + // The first time we are XIP (running from FLASH). We need to remember the + // FLASH address so we can reinstall the memory version that runs faster + // + PrivateData->XipLoadFile = &gPpiLoadFilePpiList; + PeiServicesInstallPpi (PrivateData->XipLoadFile); + } else { + // + // 2nd time we are running from memory so replace the XIP version with the + // new memory version. + // + PeiServicesReInstallPpi (PrivateData->XipLoadFile, &gPpiLoadFilePpiList); + } +} |
