/** 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 ) { PEI_CORE_INSTANCE *Private; VOID* MemoryBuffer; Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); if (Private->PeiMemoryInstalled && ((Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME) || PcdGetBool (PcdShadowPeimOnS3Boot)) && (EFI_IMAGE_MACHINE_TYPE_SUPPORTED(EFI_IMAGE_MACHINE_X64) || EFI_IMAGE_MACHINE_TYPE_SUPPORTED(EFI_IMAGE_MACHINE_IA32))) { // // 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) { MemoryBuffer = AllocatePages (0x400 / EFI_PAGE_SIZE + 1); ASSERT (MemoryBuffer != NULL); CopyMem (MemoryBuffer, (CONST VOID *) (UINTN) PeiImageReadForShadow, 0x400); Private->ShadowedImageRead = (PE_COFF_LOADER_READ_FILE) (UINTN) MemoryBuffer; } ImageContext->ImageRead = Private->ShadowedImageRead; } else { ImageContext->ImageRead = PeiImageRead; } return EFI_SUCCESS; }
/** Gets the pointer to the HOB List. @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param HobList Pointer to the HOB List. @retval EFI_SUCCESS Get the pointer of HOB List @retval EFI_NOT_AVAILABLE_YET the HOB List is not yet published @retval EFI_INVALID_PARAMETER HobList is NULL (in debug mode) **/ EFI_STATUS EFIAPI PeiGetHobList ( IN CONST EFI_PEI_SERVICES **PeiServices, IN OUT VOID **HobList ) { PEI_CORE_INSTANCE *PrivateData; // // Only check this parameter in debug mode // DEBUG_CODE_BEGIN (); if (HobList == NULL) { return EFI_INVALID_PARAMETER; } DEBUG_CODE_END (); PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS(PeiServices); *HobList = PrivateData->HobList.Raw; 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 ) { PEI_CORE_INSTANCE *Private; VOID* MemoryBuffer; Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); if (!Private->PeiMemoryInstalled || (Private->HobList.HandoffInformationTable->BootMode == BOOT_ON_S3_RESUME) || EFI_IMAGE_MACHINE_TYPE_SUPPORTED(EFI_IMAGE_MACHINE_IA64)) { ImageContext->ImageRead = PeiImageRead; } else { if (Private->ShadowedImageRead == NULL) { MemoryBuffer = AllocatePages (0x400 / EFI_PAGE_SIZE + 1); ASSERT (MemoryBuffer != NULL); CopyMem (MemoryBuffer, (CONST VOID *) (UINTN) PeiImageReadForShadow, 0x400); Private->ShadowedImageRead = (PE_COFF_LOADER_READ_FILE) (UINTN) MemoryBuffer; } ImageContext->ImageRead = Private->ShadowedImageRead; } return EFI_SUCCESS; }
/** This function registers the found memory configuration with the PEI Foundation. The usage model is that the PEIM that discovers the permanent memory shall invoke this service. This routine will hold discoveried memory information into PeiCore's private data, and set SwitchStackSignal flag. After PEIM who discovery memory is dispatched, PeiDispatcher will migrate temporary memory to permenement memory. @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param MemoryBegin Start of memory address. @param MemoryLength Length of memory. @return EFI_SUCCESS Always success. **/ EFI_STATUS EFIAPI PeiInstallPeiMemory ( IN CONST EFI_PEI_SERVICES **PeiServices, IN EFI_PHYSICAL_ADDRESS MemoryBegin, IN UINT64 MemoryLength ) { PEI_CORE_INSTANCE *PrivateData; DEBUG ((EFI_D_INFO, "PeiInstallPeiMemory MemoryBegin 0x%LX, MemoryLength 0x%LX\n", MemoryBegin, MemoryLength)); PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); // // PEI_SERVICE.InstallPeiMemory should only be called one time during whole PEI phase. // If it is invoked more than one time, ASSERT information is given for developer debugging in debug tip and // simply return EFI_SUCESS in release tip to ignore it. // if (PrivateData->PeiMemoryInstalled) { DEBUG ((EFI_D_ERROR, "ERROR: PeiInstallPeiMemory is called more than once!\n")); ASSERT (PrivateData->PeiMemoryInstalled); return EFI_SUCCESS; } PrivateData->PhysicalMemoryBegin = MemoryBegin; PrivateData->PhysicalMemoryLength = MemoryLength; PrivateData->FreePhysicalMemoryTop = MemoryBegin + MemoryLength; PrivateData->SwitchStackSignal = TRUE; return EFI_SUCCESS; }
/** This function reinstalls an interface in the PEI PPI database by GUID. The purpose of the service is to publish an interface that other parties can use to replace an interface of the same name in the protocol database with a different interface. @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param OldPpi Pointer to the old PEI PPI Descriptors. @param NewPpi Pointer to the new PEI PPI Descriptors. @retval EFI_SUCCESS if the operation was successful @retval EFI_INVALID_PARAMETER if OldPpi or NewPpi is NULL @retval EFI_INVALID_PARAMETER if NewPpi is not valid @retval EFI_NOT_FOUND if the PPI was not in the database **/ EFI_STATUS EFIAPI PeiReInstallPpi ( IN CONST EFI_PEI_SERVICES **PeiServices, IN CONST EFI_PEI_PPI_DESCRIPTOR *OldPpi, IN CONST EFI_PEI_PPI_DESCRIPTOR *NewPpi ) { PEI_CORE_INSTANCE *PrivateData; INTN Index; if ((OldPpi == NULL) || (NewPpi == NULL)) { return EFI_INVALID_PARAMETER; } if ((NewPpi->Flags & EFI_PEI_PPI_DESCRIPTOR_PPI) == 0) { return EFI_INVALID_PARAMETER; } PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS(PeiServices); // // Find the old PPI instance in the database. If we can not find it, // return the EFI_NOT_FOUND error. // for (Index = 0; Index < PrivateData->PpiData.PpiListEnd; Index++) { if (OldPpi == PrivateData->PpiData.PpiListPtrs[Index].Ppi) { break; } } if (Index == PrivateData->PpiData.PpiListEnd) { return EFI_NOT_FOUND; } // // Remove the old PPI from the database, add the new one. // DEBUG((EFI_D_INFO, "Reinstall PPI: %g\n", NewPpi->Guid)); ASSERT (Index < (INTN)(PcdGet32 (PcdPeiCoreMaxPpiSupported))); PrivateData->PpiData.PpiListPtrs[Index].Ppi = (EFI_PEI_PPI_DESCRIPTOR *) NewPpi; // // Dispatch any callback level notifies for the newly installed PPI. // DispatchNotify ( PrivateData, EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK, Index, Index+1, PrivateData->PpiData.DispatchListEnd, PrivateData->PpiData.NotifyListEnd ); return EFI_SUCCESS; }
/** Locate a given named PPI. @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param Guid Pointer to GUID of the PPI. @param Instance Instance Number to discover. @param PpiDescriptor Pointer to reference the found descriptor. If not NULL, returns a pointer to the descriptor (includes flags, etc) @param Ppi Pointer to reference the found PPI @retval EFI_SUCCESS if the PPI is in the database @retval EFI_NOT_FOUND if the PPI is not in the database **/ EFI_STATUS EFIAPI PeiLocatePpi ( IN CONST EFI_PEI_SERVICES **PeiServices, IN CONST EFI_GUID *Guid, IN UINTN Instance, IN OUT EFI_PEI_PPI_DESCRIPTOR **PpiDescriptor, IN OUT VOID **Ppi ) { PEI_CORE_INSTANCE *PrivateData; INTN Index; EFI_GUID *CheckGuid; EFI_PEI_PPI_DESCRIPTOR *TempPtr; PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS(PeiServices); // // Search the data base for the matching instance of the GUIDed PPI. // for (Index = 0; Index < PrivateData->PpiData.PpiListEnd; Index++) { TempPtr = PrivateData->PpiData.PpiListPtrs[Index].Ppi; CheckGuid = TempPtr->Guid; // // Don't use CompareGuid function here for performance reasons. // Instead we compare the GUID as INT32 at a time and branch // on the first failed comparison. // if ((((INT32 *)Guid)[0] == ((INT32 *)CheckGuid)[0]) && (((INT32 *)Guid)[1] == ((INT32 *)CheckGuid)[1]) && (((INT32 *)Guid)[2] == ((INT32 *)CheckGuid)[2]) && (((INT32 *)Guid)[3] == ((INT32 *)CheckGuid)[3])) { if (Instance == 0) { if (PpiDescriptor != NULL) { *PpiDescriptor = TempPtr; } if (Ppi != NULL) { *Ppi = TempPtr->Ppi; } return EFI_SUCCESS; } Instance--; } } return EFI_NOT_FOUND; }
/** Provide a callback for when the security PPI is installed. This routine will cache installed security PPI into PeiCore's private data. @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param NotifyDescriptor The descriptor for the notification event. @param Ppi Pointer to the PPI in question. @return Always success **/ EFI_STATUS EFIAPI SecurityPpiNotifyCallback ( IN EFI_PEI_SERVICES **PeiServices, IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor, IN VOID *Ppi ) { PEI_CORE_INSTANCE *PrivateData; // // Get PEI Core private data // PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); // // If there isn't a security PPI installed, use the one from notification // if (PrivateData->PrivateSecurityPpi == NULL) { PrivateData->PrivateSecurityPpi = (EFI_PEI_SECURITY2_PPI *)Ppi; } return EFI_SUCCESS; }
/** This function installs a notification service to be called back when a given interface is installed or reinstalled. The purpose of the service is to publish an interface that other parties can use to call additional PPIs that may materialize later. @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param NotifyList Pointer to list of Descriptors to notify upon. @param Single TRUE if only single entry in the NotifyList. FALSE if the NotifyList is ended with an entry which has the EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST flag set in its Flags field. @retval EFI_SUCCESS if successful @retval EFI_OUT_OF_RESOURCES if no space in the database @retval EFI_INVALID_PARAMETER if not a good descriptor **/ EFI_STATUS InternalPeiNotifyPpi ( IN CONST EFI_PEI_SERVICES **PeiServices, IN CONST EFI_PEI_NOTIFY_DESCRIPTOR *NotifyList, IN BOOLEAN Single ) { PEI_CORE_INSTANCE *PrivateData; INTN Index; INTN NotifyIndex; INTN LastCallbackNotify; EFI_PEI_NOTIFY_DESCRIPTOR *NotifyPtr; UINTN NotifyDispatchCount; NotifyDispatchCount = 0; if (NotifyList == NULL) { return EFI_INVALID_PARAMETER; } PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS(PeiServices); Index = PrivateData->PpiData.NotifyListEnd; LastCallbackNotify = Index; // // This is loop installs all Notify descriptors in the NotifyList. It is // terminated by the EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST being set in the last // EFI_PEI_NOTIFY_DESCRIPTOR in the list. // for (;;) { // // Since PpiData is used for NotifyList and InstallList, max resource // is reached if the Install reaches the PpiList // PcdPeiCoreMaxPpiSupported can be set to a larger value in DSC to satisfy more Notify PPIs requirement. // if (Index == PrivateData->PpiData.PpiListEnd - 1) { return EFI_OUT_OF_RESOURCES; } // // If some of the PPI data is invalid restore original Notify PPI database value // if ((NotifyList->Flags & EFI_PEI_PPI_DESCRIPTOR_NOTIFY_TYPES) == 0) { PrivateData->PpiData.NotifyListEnd = LastCallbackNotify; DEBUG((EFI_D_ERROR, "ERROR -> InstallNotify: %g %p\n", NotifyList->Guid, NotifyList->Notify)); return EFI_INVALID_PARAMETER; } if ((NotifyList->Flags & EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH) != 0) { NotifyDispatchCount ++; } PrivateData->PpiData.PpiListPtrs[Index].Notify = (EFI_PEI_NOTIFY_DESCRIPTOR *) NotifyList; PrivateData->PpiData.NotifyListEnd--; DEBUG((EFI_D_INFO, "Register PPI Notify: %g\n", NotifyList->Guid)); if (Single) { // // Only single entry in the NotifyList. // break; } else if ((NotifyList->Flags & EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) == EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) { // // Continue until the end of the Notify List. // break; } // // Go the next descriptor. Remember the NotifyList moves down. // NotifyList++; Index--; } // // If there is Dispatch Notify PPI installed put them on the bottom // if (NotifyDispatchCount > 0) { for (NotifyIndex = LastCallbackNotify; NotifyIndex > PrivateData->PpiData.NotifyListEnd; NotifyIndex--) { if ((PrivateData->PpiData.PpiListPtrs[NotifyIndex].Notify->Flags & EFI_PEI_PPI_DESCRIPTOR_NOTIFY_DISPATCH) != 0) { NotifyPtr = PrivateData->PpiData.PpiListPtrs[NotifyIndex].Notify; for (Index = NotifyIndex; Index < PrivateData->PpiData.DispatchListEnd; Index++){ PrivateData->PpiData.PpiListPtrs[Index].Notify = PrivateData->PpiData.PpiListPtrs[Index + 1].Notify; } PrivateData->PpiData.PpiListPtrs[Index].Notify = NotifyPtr; PrivateData->PpiData.DispatchListEnd--; } } LastCallbackNotify -= NotifyDispatchCount; } // // Dispatch any callback level notifies for all previously installed PPIs. // DispatchNotify ( PrivateData, EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK, 0, PrivateData->PpiData.PpiListEnd, LastCallbackNotify, PrivateData->PpiData.NotifyListEnd ); return EFI_SUCCESS; }
/** This function installs an interface in the PEI PPI database by GUID. The purpose of the service is to publish an interface that other parties can use to call additional PEIMs. @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param PpiList Pointer to a list of PEI PPI Descriptors. @param Single TRUE if only single entry in the PpiList. FALSE if the PpiList is ended with an entry which has the EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST flag set in its Flags field. @retval EFI_SUCCESS if all PPIs in PpiList are successfully installed. @retval EFI_INVALID_PARAMETER if PpiList is NULL pointer if any PPI in PpiList is not valid @retval EFI_OUT_OF_RESOURCES if there is no more memory resource to install PPI **/ EFI_STATUS InternalPeiInstallPpi ( IN CONST EFI_PEI_SERVICES **PeiServices, IN CONST EFI_PEI_PPI_DESCRIPTOR *PpiList, IN BOOLEAN Single ) { PEI_CORE_INSTANCE *PrivateData; INTN Index; INTN LastCallbackInstall; if (PpiList == NULL) { return EFI_INVALID_PARAMETER; } PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS(PeiServices); Index = PrivateData->PpiData.PpiListEnd; LastCallbackInstall = Index; // // This is loop installs all PPI descriptors in the PpiList. It is terminated // by the EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST being set in the last // EFI_PEI_PPI_DESCRIPTOR in the list. // for (;;) { // // Since PpiData is used for NotifyList and PpiList, max resource // is reached if the Install reaches the NotifyList // PcdPeiCoreMaxPpiSupported can be set to a larger value in DSC to satisfy more PPI requirement. // if (Index == PrivateData->PpiData.NotifyListEnd + 1) { return EFI_OUT_OF_RESOURCES; } // // Check if it is a valid PPI. // If not, rollback list to exclude all in this list. // Try to indicate which item failed. // if ((PpiList->Flags & EFI_PEI_PPI_DESCRIPTOR_PPI) == 0) { PrivateData->PpiData.PpiListEnd = LastCallbackInstall; DEBUG((EFI_D_ERROR, "ERROR -> InstallPpi: %g %p\n", PpiList->Guid, PpiList->Ppi)); return EFI_INVALID_PARAMETER; } DEBUG((EFI_D_INFO, "Install PPI: %g\n", PpiList->Guid)); PrivateData->PpiData.PpiListPtrs[Index].Ppi = (EFI_PEI_PPI_DESCRIPTOR*) PpiList; PrivateData->PpiData.PpiListEnd++; if (Single) { // // Only single entry in the PpiList. // break; } else if ((PpiList->Flags & EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) == EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST) { // // Continue until the end of the PPI List. // break; } PpiList++; Index++; } // // Dispatch any callback level notifies for newly installed PPIs. // DispatchNotify ( PrivateData, EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK, LastCallbackInstall, PrivateData->PpiData.PpiListEnd, PrivateData->PpiData.DispatchListEnd, PrivateData->PpiData.NotifyListEnd ); return EFI_SUCCESS; }
/** 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 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 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; Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); ReturnStatus = EFI_SUCCESS; IsXipImage = FALSE; ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = Pe32Data; Status = GetImageReadFunction (&ImageContext); ASSERT_EFI_ERROR (Status); Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // XIP image that ImageAddress is same to Image handle. // if (ImageContext.ImageAddress == (EFI_PHYSICAL_ADDRESS)(UINTN) Pe32Data) { IsXipImage = TRUE; } // // When Image has no reloc section, it can't be relocated into memory. // if (ImageContext.RelocationsStripped && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME || 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, boot mode is not S3, and image is relocatable. // if ((!ImageContext.RelocationsStripped) && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME || 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. // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocatePages (EFI_SIZE_TO_PAGES ((UINT32) AlignImageSize)); } } else { ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocatePages (EFI_SIZE_TO_PAGES ((UINT32) AlignImageSize)); } if (ImageContext.ImageAddress != 0) { // // 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)) { 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 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; } // // Initilize 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) || (!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; } } } // // 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 into memory. If the image is not relocatable, it will not be loaded into memory and be loaded as XIP 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 **/ EFI_STATUS LoadAndRelocatePeCoffImage ( 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; Private = PEI_CORE_INSTANCE_FROM_PS_THIS (GetPeiServicesTablePointer ()); ZeroMem (&ImageContext, sizeof (ImageContext)); ImageContext.Handle = Pe32Data; Status = GetImageReadFunction (&ImageContext); ASSERT_EFI_ERROR (Status); Status = PeCoffLoaderGetImageInfo (&ImageContext); if (EFI_ERROR (Status)) { return Status; } // // When Image has no reloc section, it can't be relocated into memory. // if (ImageContext.RelocationsStripped && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { 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, boot mode is not S3, and image is relocatable. // if ((!ImageContext.RelocationsStripped) && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) { 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. // ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocatePages (EFI_SIZE_TO_PAGES ((UINT32) ImageContext.ImageSize)); } } else { ImageContext.ImageAddress = (EFI_PHYSICAL_ADDRESS)(UINTN) AllocatePages (EFI_SIZE_TO_PAGES ((UINT32) ImageContext.ImageSize)); } ASSERT (ImageContext.ImageAddress != 0); if (ImageContext.ImageAddress == 0) { return EFI_OUT_OF_RESOURCES; } // // 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); } } // // Load the image to our new buffer // Status = PeCoffLoaderLoadImage (&ImageContext); if (EFI_ERROR (Status)) { 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.RelocationsStripped) && (Private->PeiMemoryInstalled) && (Private->HobList.HandoffInformationTable->BootMode != BOOT_ON_S3_RESUME)) { InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize); } *ImageAddress = ImageContext.ImageAddress; *ImageSize = ImageContext.ImageSize; *EntryPoint = ImageContext.EntryPoint; return EFI_SUCCESS; }
/** The purpose of the service is to publish an interface that allows PEIMs to allocate memory ranges that are managed by the PEI Foundation. @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @param MemoryType The type of memory to allocate. @param Pages The number of contiguous 4 KB pages to allocate. @param Memory Pointer to a physical address. On output, the address is set to the base of the page range that was allocated. @retval EFI_SUCCESS The memory range was successfully allocated. @retval EFI_OUT_OF_RESOURCES The pages could not be allocated. @retval EFI_INVALID_PARAMETER Type is not equal to EfiLoaderCode, EfiLoaderData, EfiRuntimeServicesCode, EfiRuntimeServicesData, EfiBootServicesCode, EfiBootServicesData, EfiACPIReclaimMemory, or EfiACPIMemoryNVS. **/ EFI_STATUS EFIAPI PeiAllocatePages ( IN CONST EFI_PEI_SERVICES **PeiServices, IN EFI_MEMORY_TYPE MemoryType, IN UINTN Pages, OUT EFI_PHYSICAL_ADDRESS *Memory ) { PEI_CORE_INSTANCE *PrivateData; EFI_PEI_HOB_POINTERS Hob; EFI_PHYSICAL_ADDRESS *FreeMemoryTop; EFI_PHYSICAL_ADDRESS *FreeMemoryBottom; UINTN RemainingPages; if ((MemoryType != EfiLoaderCode) && (MemoryType != EfiLoaderData) && (MemoryType != EfiRuntimeServicesCode) && (MemoryType != EfiRuntimeServicesData) && (MemoryType != EfiBootServicesCode) && (MemoryType != EfiBootServicesData) && (MemoryType != EfiACPIReclaimMemory) && (MemoryType != EfiACPIMemoryNVS)) { return EFI_INVALID_PARAMETER; } PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); Hob.Raw = PrivateData->HobList.Raw; // // Check if Hob already available // if (!PrivateData->PeiMemoryInstalled) { // // When PeiInstallMemory is called but temporary memory has *not* been moved to temporary memory, // the AllocatePage will depend on the field of PEI_CORE_INSTANCE structure. // if (!PrivateData->SwitchStackSignal) { return EFI_NOT_AVAILABLE_YET; } else { FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop); FreeMemoryBottom = &(PrivateData->PhysicalMemoryBegin); } } else { FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop); FreeMemoryBottom = &(Hob.HandoffInformationTable->EfiFreeMemoryBottom); } // // Check to see if on 4k boundary, If not aligned, make the allocation aligned. // *(FreeMemoryTop) -= *(FreeMemoryTop) & 0xFFF; // // Verify that there is sufficient memory to satisfy the allocation // RemainingPages = EFI_SIZE_TO_PAGES ((UINTN) (*FreeMemoryTop - *FreeMemoryBottom)); // // For page allocation, the overhead sizeof (EFI_HOB_MEMORY_ALLOCATION) needs one extra page. // So the number of remaining pages needs to be greater than that of the request pages. // if (RemainingPages <= Pages) { DEBUG ((EFI_D_ERROR, "AllocatePages failed: No 0x%lx Pages is available.\n", (UINT64) Pages)); DEBUG ((EFI_D_ERROR, "There is only left 0x%lx pages memory resource to be allocated.\n", (UINT64) RemainingPages)); return EFI_OUT_OF_RESOURCES; } else { // // Update the PHIT to reflect the memory usage // *(FreeMemoryTop) -= Pages * EFI_PAGE_SIZE; // // Update the value for the caller // *Memory = *(FreeMemoryTop); // // Create a memory allocation HOB. // BuildMemoryAllocationHob ( *(FreeMemoryTop), Pages * EFI_PAGE_SIZE, MemoryType ); return EFI_SUCCESS; } }