X-Git-Url: https://git.proxmox.com/?p=mirror_edk2.git;a=blobdiff_plain;f=MdeModulePkg%2FCore%2FPei%2FImage%2FImage.c;h=f07f48823117aa0373f7f6fb9f8abdac7819340e;hp=4035c756526a58064918fb94506d87013dcaa379;hb=c2c4199bc2b674166a8d48e1e0f45cf7c66a6d4c;hpb=e98cd821ebedd6472c12738bd53dc7cfd02bb4fb diff --git a/MdeModulePkg/Core/Pei/Image/Image.c b/MdeModulePkg/Core/Pei/Image/Image.c index 4035c75652..f07f488231 100644 --- a/MdeModulePkg/Core/Pei/Image/Image.c +++ b/MdeModulePkg/Core/Pei/Image/Image.c @@ -1,232 +1,372 @@ -/*++ - -Copyright (c) 2006 - 2007, Intel Corporation -All rights reserved. This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. - -Module Name: - - Image.c - -Abstract: - +/** @file Pei Core Load Image Support ---*/ - -#include - -/*++ - -Routine Description: - - Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file - -Arguments: - - FileHandle - The handle to the PE/COFF file - FileOffset - The offset, in bytes, into the file to read - ReadSize - The number of bytes to read from the file starting at FileOffset - Buffer - A pointer to the buffer to read the data into. - -Returns: - - EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset - ---*/ - -EFI_STATUS -PeiLoadImageLoadImage ( - IN 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 - ) -/*++ - -Routine Description: - - Routine for loading file image. - -Arguments: +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php - PeiServices - The PEI core services table. - FileHandle - Pointer to the FFS file header of the image. - ImageAddressArg - Pointer to PE/TE image. - ImageSizeArg - Size of PE/TE image. - EntryPoint - Pointer to entry point of specified image file for output. - AuthenticationState - Pointer to attestation authentication state of image. +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. -Returns: +**/ - Status - EFI_SUCCESS - Image is successfully loaded. - EFI_NOT_FOUND - Fail to locate necessary PPI - Others - Fail to load file. +#include "PeiMain.h" ---*/ -; -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 - ) -/*++ - -Routine Description: - - The wrapper function of PeiLoadImageLoadImage(). - -Arguments: - - This - Pointer to EFI_PEI_LOAD_FILE_PPI. - PeiServices - The PEI core services table. - FileHandle - Pointer to the FFS file header of the image. - ImageAddressArg - Pointer to PE/TE image. - ImageSizeArg - Size of PE/TE image. - EntryPoint - Pointer to entry point of specified image file for output. - AuthenticationState - Pointer to attestation authentication state of image. - -Returns: - - EFI_STATUS. - ---*/ -; - -STATIC EFI_PEI_LOAD_FILE_PPI mPeiLoadImagePpi = { +EFI_PEI_LOAD_FILE_PPI mPeiLoadImagePpi = { PeiLoadImageLoadImageWrapper }; -STATIC EFI_PEI_PPI_DESCRIPTOR gPpiLoadFilePpiList = { +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 OUT UINTN *ReadSize, + IN UINTN *ReadSize, OUT VOID *Buffer ) -/*++ - -Routine Description: +{ + CHAR8 *Destination8; + CHAR8 *Source8; + + Destination8 = Buffer; + Source8 = (CHAR8 *) ((UINTN) FileHandle + FileOffset); + if (Destination8 != Source8) { + CopyMem (Destination8, Source8, *ReadSize); + } - Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file + return EFI_SUCCESS; +} -Arguments: +/** - FileHandle - The handle to the PE/COFF file - FileOffset - The offset, in bytes, into the file to read - ReadSize - The number of bytes to read from the file starting at FileOffset - Buffer - A pointer to the buffer to read the data into. + Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file. + The function is implemented as PIC so as to support shadowing. -Returns: + @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. - EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset + @return EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset ---*/ +**/ +EFI_STATUS +EFIAPI +PeiImageReadForShadow ( + IN VOID *FileHandle, + IN UINTN FileOffset, + IN UINTN *ReadSize, + OUT VOID *Buffer + ) { - CHAR8 *Destination8; - CHAR8 *Source8; - UINTN Length; + volatile CHAR8 *Destination8; + CHAR8 *Source8; + UINTN Length; Destination8 = Buffer; Source8 = (CHAR8 *) ((UINTN) FileHandle + FileOffset); - Length = *ReadSize; - while (Length--) { - *(Destination8++) = *(Source8++); + if (Destination8 != Source8) { + Length = *ReadSize; + while ((Length--) > 0) { + *(Destination8++) = *(Source8++); + } } return EFI_SUCCESS; } +/** + + Support routine to get the Image read file function. + + @param ImageContext - The context of the image being loaded + + @retval EFI_SUCCESS - If Image function location is found + +**/ EFI_STATUS GetImageReadFunction ( IN PE_COFF_LOADER_IMAGE_CONTEXT *ImageContext ) -/*++ - -Routine Description: - - Support routine to return the Image Read - -Arguments: +{ +#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64) + PEI_CORE_INSTANCE *Private; + EFI_PHYSICAL_ADDRESS MemoryBuffer; + + Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); + MemoryBuffer = 0; + + if (Private->PeiMemoryInstalled && (((Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) && PcdGetBool (PcdShadowPeimOnBoot)) || + ((Private->HobList.HandoffInformationTable->BootMode == BOOT_ON_S3_RESUME) && PcdGetBool (PcdShadowPeimOnS3Boot)))) { + // + // Shadow algorithm makes lots of non ANSI C assumptions and only works for IA32 and X64 + // compilers that have been tested + // + if (Private->ShadowedImageRead == NULL) { + PeiServicesAllocatePages (EfiBootServicesCode, 0x400 / EFI_PAGE_SIZE + 1, &MemoryBuffer); + ASSERT (MemoryBuffer != 0); + CopyMem ((VOID *)(UINTN)MemoryBuffer, (CONST VOID *) (UINTN) PeiImageReadForShadow, 0x400); + Private->ShadowedImageRead = (PE_COFF_LOADER_READ_FILE) (UINTN) MemoryBuffer; + } - PeiServices - PEI Services Table + ImageContext->ImageRead = Private->ShadowedImageRead; + } else { + ImageContext->ImageRead = PeiImageRead; + } +#else + ImageContext->ImageRead = PeiImageRead; +#endif + 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 avalaible 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; +} +/** - ImageContext - The context of the image being loaded + 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. -Returns: + @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 - EFI_SUCCESS - If Image function location is found + @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 + ) { - VOID* MemoryBuffer; + 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 ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED INFO: Loading module at fixed address 0x%11p. Status= %r \n", (VOID *)(UINTN)FixLoadingAddress, Status)); + return Status; +} +/** - MemoryBuffer = AllocatePages (0x400 / EFI_PAGE_SIZE + 1); - ASSERT (MemoryBuffer != NULL); + 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. - CopyMem (MemoryBuffer, (CONST VOID *) (UINTN) PeiImageRead, 0x400); + @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 - ImageContext->ImageRead = (PE_COFF_LOADER_READ_FILE) (UINTN) MemoryBuffer; + @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. - return EFI_SUCCESS; -} - -STATIC +**/ 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 ) -/*++ - -Routine Description: - - Loads and relocates a PE/COFF image into memory. - -Arguments: - - Pe32Data - The base address of the PE/COFF file that is to be loaded and relocated - - ImageAddress - The base address of the relocated PE/COFF image - - ImageSize - The size of the relocated PE/COFF image - - EntryPoint - The entry point of the relocated PE/COFF image - -Returns: - - EFI_SUCCESS - The file was loaded and relocated - - EFI_OUT_OF_RESOURCES - There was not enough memory to load and relocate the PE/COFF file - ---*/ { 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; Status = GetImageReadFunction (&ImageContext); @@ -237,26 +377,128 @@ Returns: if (EFI_ERROR (Status)) { return Status; } + // - // When Image has no reloc section, it can't be relocated into memory. + // Initilize local IsS3Boot and IsRegisterForShadow variable // - if (ImageContext.RelocationsStripped) { - DEBUG ((EFI_D_ERROR, "The image at 0x%08x without reloc section can't be loaded into memory", (UINTN) Pe32Data)); - return EFI_INVALID_PARAMETER; + 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; + } + // - // Allocate Memory for the image + // Get file type first // - ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocatePages (EFI_SIZE_TO_PAGES ((UINT32) ImageContext.ImageSize)); - ASSERT (ImageContext.ImageAddress != 0); + Status = PeiServicesFfsGetFileInfo (FileHandle, &FileInfo); + ASSERT_EFI_ERROR (Status); // - // Skip the reserved space for the stripped PeHeader when load TeImage into memory. + // Check whether the file type is PEI module. // - if (ImageContext.IsTeImage) { - ImageContext.ImageAddress = ImageContext.ImageAddress + - ((EFI_TE_IMAGE_HEADER *) Pe32Data)->StrippedSize - - sizeof (EFI_TE_IMAGE_HEADER); + 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) || + (!IsS3Boot && (PcdGetBool (PcdShadowPeimOnBoot) || IsRegisterForShadow)) || (IsS3Boot && PcdGetBool (PcdShadowPeimOnS3Boot)))) { + DEBUG ((EFI_D_INFO|EFI_D_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) || + (!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 ((EFI_D_INFO|EFI_D_LOAD, "LOADING MODULE FIXED ERROR: Failed to load module at fixed address. \n")); + // + // The PEIM is not assiged 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; + } + } } // @@ -264,6 +506,9 @@ Returns: // 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; } // @@ -277,46 +522,46 @@ Returns: // // Flush the instruction cache so the image data is written before we execute it // - InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); + 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 EFI_SUCCESS; + return ReturnStatus; } +/** + 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 EFI_PEI_SERVICES **PeiServices, + 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 ) -/*++ - -Routine Description: - - Routine for loading file image. - -Arguments: - - PeiServices - The PEI core services table. - FileHandle - Pointer to the FFS file header of the image. - ImageAddressArg - Pointer to PE/TE image. - ImageSizeArg - Size of PE/TE image. - EntryPoint - Pointer to entry point of specified image file for output. - AuthenticationState - Pointer to attestation authentication state of image. - -Returns: - - Status - EFI_SUCCESS - Image is successfully loaded. - EFI_NOT_FOUND - Fail to locate necessary PPI - Others - Fail to load file. - ---*/ { EFI_STATUS Status; VOID *Pe32Data; @@ -324,78 +569,84 @@ Returns: UINT64 ImageSize; EFI_PHYSICAL_ADDRESS ImageEntryPoint; UINT16 Machine; - PEI_CORE_INSTANCE *Private; - VOID *EntryPointArg; + 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 TE section. + // Try to find a first exe section (if PcdPeiCoreImageLoaderSearchTeSectionFirst + // is true, TE will be searched first). // - Status = PeiServicesFfsFindSectionData ( - EFI_SECTION_TE, + Status = PeiServicesFfsFindSectionData3 ( + SearchType1, + 0, FileHandle, - &Pe32Data + &Pe32Data, + AuthenticationState ); // - // If we didn't find a TE section, try to find a PE32 section. + // If we didn't find a first exe section, try to find the second exe section. // if (EFI_ERROR (Status)) { - Status = PeiServicesFfsFindSectionData ( - EFI_SECTION_PE32, + Status = PeiServicesFfsFindSectionData3 ( + SearchType2, + 0, FileHandle, - &Pe32Data + &Pe32Data, + AuthenticationState ); if (EFI_ERROR (Status)) { // - // PEI core only carry the loader function fro TE and PE32 executables + // PEI core only carry the loader function for TE and PE32 executables // If this two section does not exist, just return. // return Status; } } - - Private = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); - if (Private->PeiMemoryInstalled && - (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { - // - // If memory is installed, perform the shadow operations - // - Status = LoadAndRelocatePeCoffImage ( - Pe32Data, - &ImageAddress, - &ImageSize, - &ImageEntryPoint - ); + DEBUG ((DEBUG_INFO, "Loading PEIM %g\n", FileHandle)); - if (EFI_ERROR (Status)) { - return Status; - } + // + // If memory is installed, perform the shadow operations + // + Status = LoadAndRelocatePeCoffImage ( + FileHandle, + Pe32Data, + &ImageAddress, + &ImageSize, + &ImageEntryPoint + ); - // - // Got the entry point from the loaded Pe32Data - // - Pe32Data = (VOID *) ((UINTN) ImageAddress); - *EntryPoint = ImageEntryPoint; - } else { - // - // Retrieve the entry point from the PE/COFF or TE image header - // - ImageAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) Pe32Data; - Status = PeCoffLoaderGetEntryPoint (Pe32Data, &EntryPointArg); - if (EFI_ERROR (Status)) { - return Status; - } - *EntryPoint = (EFI_PHYSICAL_ADDRESS) (UINTN) EntryPointArg; + ASSERT_EFI_ERROR (Status); + + + 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)) { - return EFI_UNSUPPORTED; + if (!EFI_IMAGE_MACHINE_CROSS_TYPE_SUPPORTED (Machine)) { + return EFI_UNSUPPORTED; + } } if (ImageAddressArg != NULL) { @@ -405,44 +656,62 @@ Returns: if (ImageSizeArg != NULL) { *ImageSizeArg = ImageSize; } - + DEBUG_CODE_BEGIN (); CHAR8 *AsciiString; - CHAR8 AsciiBuffer[512]; + CHAR8 EfiFileName[512]; INT32 Index; - INT32 Index1; + INT32 StartIndex; // // Print debug message: Loading PEIM at 0x12345678 EntryPoint=0x12345688 Driver.efi // - if (Machine != IMAGE_FILE_MACHINE_IA64) { - DEBUG ((EFI_D_INFO | EFI_D_LOAD, "Loading PEIM at 0x%08x EntryPoint=0x%08x ", (UINTN) ImageAddress, *EntryPoint)); + if (Machine != EFI_IMAGE_MACHINE_IA64) { + DEBUG ((EFI_D_INFO | EFI_D_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 ((EFI_D_INFO | EFI_D_LOAD, "Loading PEIM at 0x%08x EntryPoint=0x%08x ", (UINTN) ImageAddress, (UINTN) (*(UINT64 *)(UINTN)*EntryPoint))); + DEBUG ((EFI_D_INFO | EFI_D_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) { - for (Index = (INT32) AsciiStrLen (AsciiString) - 1; Index >= 0; Index --) { - if (AsciiString[Index] == '\\') { - break; + StartIndex = 0; + for (Index = 0; AsciiString[Index] != 0; Index++) { + if (AsciiString[Index] == '\\' || AsciiString[Index] == '/') { + StartIndex = Index + 1; } } - if (Index != 0) { - for (Index1 = 0; AsciiString[Index + 1 + Index1] != '.'; Index1 ++) { - AsciiBuffer [Index1] = AsciiString[Index + 1 + Index1]; + // + // 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 redudant 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; } - AsciiBuffer [Index1] = '\0'; - DEBUG ((EFI_D_INFO | EFI_D_LOAD, "%a.efi", AsciiBuffer)); } + + if (Index == sizeof (EfiFileName) - 4) { + EfiFileName[Index] = 0; + } + + DEBUG ((EFI_D_INFO | EFI_D_LOAD, "%a", EfiFileName)); } DEBUG_CODE_END (); @@ -454,6 +723,19 @@ Returns: } +/** + 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 ( @@ -464,27 +746,6 @@ PeiLoadImageLoadImageWrapper ( OUT EFI_PHYSICAL_ADDRESS *EntryPoint, OUT UINT32 *AuthenticationState ) -/*++ - -Routine Description: - - The wrapper function of PeiLoadImageLoadImage(). - -Arguments: - - This - Pointer to EFI_PEI_LOAD_FILE_PPI. - PeiServices - The PEI core services table. - FileHandle - Pointer to the FFS file header of the image. - ImageAddressArg - Pointer to PE/TE image. - ImageSizeArg - Size of PE/TE image. - EntryPoint - Pointer to entry point of specified image file for output. - AuthenticationState - Pointer to attestation authentication state of image. - -Returns: - - EFI_STATUS. - ---*/ { return PeiLoadImageLoadImage ( GetPeiServicesTablePointer (), @@ -496,33 +757,90 @@ Returns: ); } -EFI_STATUS -PeiLoadImage ( - IN EFI_PEI_SERVICES **PeiServices, - IN EFI_PEI_FILE_HANDLE FileHandle, - OUT EFI_PHYSICAL_ADDRESS *EntryPoint, - OUT UINT32 *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; -Routine Description: + ASSERT (Pe32Data != NULL); - Routine for load image file. + 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; + } -Arguments: + // + // 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; + } + } - PeiServices - The PEI core services table. - FileHandle - Pointer to the FFS file header of the image. - EntryPoint - Pointer to entry point of specified image file for output. - AuthenticationState - Pointer to attestation authentication state of image. + return FALSE; +} -Returns: +/** + 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. - Status - EFI_SUCCESS - Image is successfully loaded. - EFI_NOT_FOUND - Fail to locate necessary PPI - Others - Fail to load file. - ---*/ + @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; @@ -530,7 +848,9 @@ Returns: 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. @@ -545,57 +865,56 @@ Returns: ); if (!EFI_ERROR (PpiStatus)) { Status = LoadFile->LoadFile ( - LoadFile, - FileHandle, - &ImageAddress, + LoadFile, + FileHandle, + &ImageAddress, &ImageSize, EntryPoint, AuthenticationState ); - if (!EFI_ERROR (Status)) { - return Status; + 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)); - // - // If no instances reports EFI_SUCCESS, then build-in support for - // the PE32+/TE XIP image format is used. - // - Status = PeiLoadImageLoadImage ( - PeiServices, - FileHandle, - NULL, - NULL, - EntryPoint, - AuthenticationState - ); - return Status; + return PpiStatus; } -VOID -InitializeImageServices ( - IN PEI_CORE_INSTANCE *PrivateData, - IN PEI_CORE_INSTANCE *OldCoreData - ) -/*++ - -Routine Description: +/** Install Pei Load File PPI. -Arguments: - PrivateData - Pointer to PEI_CORE_INSTANCE. - OldCoreData - Pointer to PEI_CORE_INSTANCE. + @param PrivateData - Pointer to PEI_CORE_INSTANCE. + @param OldCoreData - Pointer to PEI_CORE_INSTANCE. -Returns: - - NONE. - ---*/ +**/ +VOID +InitializeImageServices ( + IN PEI_CORE_INSTANCE *PrivateData, + IN PEI_CORE_INSTANCE *OldCoreData + ) { if (OldCoreData == NULL) { // @@ -606,12 +925,13 @@ Returns: PeiServicesInstallPpi (PrivateData->XipLoadFile); } else { // - // 2nd time we are running from memory so replace the XIP version with the - // new memory version. + // 2nd time we are running from memory so replace the XIP version with the + // new memory version. // - PeiServicesReInstallPpi (PrivateData->XipLoadFile, &gPpiLoadFilePpiList); + PeiServicesReInstallPpi (PrivateData->XipLoadFile, &gPpiLoadFilePpiList); } } +