X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=MdeModulePkg%2FCore%2FPei%2FMemory%2FMemoryServices.c;h=6b3a64a811cd3cff475fd708420d721a88995b8c;hb=HEAD;hp=a64bb3c6339d53db2c940bc715edbcbe689a2b1d;hpb=173b35e4694885abec8f72a2ac6bdb9aa1ab15ca;p=mirror_edk2.git diff --git a/MdeModulePkg/Core/Pei/Memory/MemoryServices.c b/MdeModulePkg/Core/Pei/Memory/MemoryServices.c index a64bb3c633..3b2e15699f 100644 --- a/MdeModulePkg/Core/Pei/Memory/MemoryServices.c +++ b/MdeModulePkg/Core/Pei/Memory/MemoryServices.c @@ -1,90 +1,73 @@ -/*++ - -Copyright (c) 2006, 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. +/** @file + EFI PEI Core memory services -Module Name: +Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.
+SPDX-License-Identifier: BSD-2-Clause-Patent - MemoryServices.c +**/ -Abstract: +#include "PeiMain.h" - EFI PEI Core memory services +/** ---*/ + Initialize the memory services. -#include + @param PrivateData Points to PeiCore's private instance data. + @param SecCoreData Points to a data structure containing information about the PEI core's operating + environment, such as the size and location of temporary RAM, the stack location and + the BFV location. + @param OldCoreData Pointer to the PEI Core data. + NULL if being run in non-permanent memory mode. +**/ VOID InitializeMemoryServices ( IN PEI_CORE_INSTANCE *PrivateData, IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData, IN PEI_CORE_INSTANCE *OldCoreData ) -/*++ - -Routine Description: - - Initialize the memory services. - -Arguments: - - PeiServices - The PEI core services table. - SecCoreData - Points to a data structure containing information about the PEI core's operating - environment, such as the size and location of temporary RAM, the stack location and - the BFV location. - - OldCoreData - Pointer to the PEI Core data. - NULL if being run in non-permament memory mode. - -Returns: - - None - ---*/ { - - PrivateData->SwitchStackSignal = FALSE; + PrivateData->SwitchStackSignal = FALSE; + // + // First entering PeiCore, following code will initialized some field + // in PeiCore's private data according to hand off data from SEC core. + // if (OldCoreData == NULL) { - PrivateData->PeiMemoryInstalled = FALSE; + PrivateData->HobList.Raw = SecCoreData->PeiTemporaryRamBase; - PrivateData->BottomOfCarHeap = SecCoreData->PeiTemporaryRamBase; - PrivateData->TopOfCarHeap = (VOID *)((UINTN)(PrivateData->BottomOfCarHeap) + SecCoreData->PeiTemporaryRamSize); - PrivateData->SizeOfTemporaryMemory = SecCoreData->TemporaryRamSize; - PrivateData->StackSize = (UINT64) SecCoreData->StackSize; - - DEBUG_CODE_BEGIN (); - PrivateData->SizeOfCacheAsRam = SecCoreData->PeiTemporaryRamSize + SecCoreData->StackSize; - PrivateData->MaxTopOfCarHeap = (VOID *) ((UINTN) PrivateData->BottomOfCarHeap + (UINTN) PrivateData->SizeOfCacheAsRam); - PrivateData->StackBase = (EFI_PHYSICAL_ADDRESS) (UINTN) SecCoreData->StackBase; - PrivateData->StackSize = (UINT64) SecCoreData->StackSize; - DEBUG_CODE_END (); - - PrivateData->HobList.Raw = PrivateData->BottomOfCarHeap; - PeiCoreBuildHobHandoffInfoTable ( BOOT_WITH_FULL_CONFIGURATION, - (EFI_PHYSICAL_ADDRESS) (UINTN) PrivateData->BottomOfCarHeap, - (UINTN) SecCoreData->PeiTemporaryRamSize + (EFI_PHYSICAL_ADDRESS)(UINTN)SecCoreData->PeiTemporaryRamBase, + (UINTN)SecCoreData->PeiTemporaryRamSize ); // - // Set PS to point to ServiceTableShadow in Cache + // Set Ps to point to ServiceTableShadow in Cache // - PrivateData->PS = &(PrivateData->ServiceTableShadow); + PrivateData->Ps = &(PrivateData->ServiceTableShadow); } - + return; } +/** + + 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 permanent 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 ( @@ -92,122 +75,624 @@ PeiInstallPeiMemory ( IN EFI_PHYSICAL_ADDRESS MemoryBegin, IN UINT64 MemoryLength ) -/*++ +{ + PEI_CORE_INSTANCE *PrivateData; -Routine Description: + DEBUG ((DEBUG_INFO, "PeiInstallPeiMemory MemoryBegin 0x%LX, MemoryLength 0x%LX\n", MemoryBegin, MemoryLength)); + PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); - Install the permanent memory is now available. - Creates HOB (PHIT and Stack). + // + // 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_SUCCESS in release tip to ignore it. + // + if (PrivateData->PeiMemoryInstalled) { + DEBUG ((DEBUG_ERROR, "ERROR: PeiInstallPeiMemory is called more than once!\n")); + ASSERT (FALSE); + return EFI_SUCCESS; + } -Arguments: + PrivateData->PhysicalMemoryBegin = MemoryBegin; + PrivateData->PhysicalMemoryLength = MemoryLength; + PrivateData->FreePhysicalMemoryTop = MemoryBegin + MemoryLength; + + PrivateData->SwitchStackSignal = TRUE; - PeiServices - The PEI core services table. - MemoryBegin - Start of memory address. - MemoryLength - Length of memory. + return EFI_SUCCESS; +} + +/** + Migrate memory pages allocated in pre-memory phase. + Copy memory pages at temporary heap top to permanent heap top. -Returns: + @param[in] Private Pointer to the private data passed in from caller. + @param[in] TemporaryRamMigrated Temporary memory has been migrated to permanent memory. - Status - EFI_SUCCESS - ---*/ +**/ +VOID +MigrateMemoryPages ( + IN PEI_CORE_INSTANCE *Private, + IN BOOLEAN TemporaryRamMigrated + ) { - PEI_CORE_INSTANCE *PrivateData; + EFI_PHYSICAL_ADDRESS NewMemPagesBase; + EFI_PHYSICAL_ADDRESS MemPagesBase; - DEBUG ((EFI_D_INFO, "PeiInstallPeiMemory MemoryBegin 0x%LX, MemoryLength 0x%LX\n", MemoryBegin, MemoryLength)); - PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); + Private->MemoryPages.Size = (UINTN)(Private->HobList.HandoffInformationTable->EfiMemoryTop - + Private->HobList.HandoffInformationTable->EfiFreeMemoryTop); + if (Private->MemoryPages.Size == 0) { + // + // No any memory page allocated in pre-memory phase. + // + return; + } - PrivateData->PhysicalMemoryBegin = MemoryBegin; - PrivateData->PhysicalMemoryLength = MemoryLength; - PrivateData->FreePhysicalMemoryTop = MemoryBegin + MemoryLength; - - PrivateData->SwitchStackSignal = TRUE; + Private->MemoryPages.Base = Private->HobList.HandoffInformationTable->EfiFreeMemoryTop; - return EFI_SUCCESS; + ASSERT (Private->MemoryPages.Size <= Private->FreePhysicalMemoryTop); + NewMemPagesBase = Private->FreePhysicalMemoryTop - Private->MemoryPages.Size; + NewMemPagesBase &= ~(UINT64)EFI_PAGE_MASK; + ASSERT (NewMemPagesBase >= Private->PhysicalMemoryBegin); + // + // Copy memory pages at temporary heap top to permanent heap top. + // + if (TemporaryRamMigrated) { + // + // Memory pages at temporary heap top has been migrated to permanent heap, + // Here still needs to copy them from permanent heap to permanent heap top. + // + MemPagesBase = Private->MemoryPages.Base; + if (Private->HeapOffsetPositive) { + MemPagesBase += Private->HeapOffset; + } else { + MemPagesBase -= Private->HeapOffset; + } + + CopyMem ((VOID *)(UINTN)NewMemPagesBase, (VOID *)(UINTN)MemPagesBase, Private->MemoryPages.Size); + } else { + CopyMem ((VOID *)(UINTN)NewMemPagesBase, (VOID *)(UINTN)Private->MemoryPages.Base, Private->MemoryPages.Size); + } + + if (NewMemPagesBase >= Private->MemoryPages.Base) { + Private->MemoryPages.OffsetPositive = TRUE; + Private->MemoryPages.Offset = (UINTN)(NewMemPagesBase - Private->MemoryPages.Base); + } else { + Private->MemoryPages.OffsetPositive = FALSE; + Private->MemoryPages.Offset = (UINTN)(Private->MemoryPages.Base - NewMemPagesBase); + } + + DEBUG ((DEBUG_INFO, "Pages Offset = 0x%lX\n", (UINT64)Private->MemoryPages.Offset)); + + Private->FreePhysicalMemoryTop = NewMemPagesBase; } -EFI_STATUS -EFIAPI -PeiAllocatePages ( - IN CONST EFI_PEI_SERVICES **PeiServices, - IN EFI_MEMORY_TYPE MemoryType, - IN UINTN Pages, - OUT EFI_PHYSICAL_ADDRESS *Memory +/** + Removes any FV HOBs whose base address is not in PEI installed memory. + + @param[in] Private Pointer to PeiCore's private data structure. + +**/ +VOID +RemoveFvHobsInTemporaryMemory ( + IN PEI_CORE_INSTANCE *Private + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_FIRMWARE_VOLUME *FirmwareVolumeHob; + + DEBUG ((DEBUG_INFO, "Removing FVs in FV HOB not already migrated to permanent memory.\n")); + + for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { + if ((GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV) || (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV2) || (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV3)) { + FirmwareVolumeHob = Hob.FirmwareVolume; + DEBUG ((DEBUG_INFO, " Found FV HOB.\n")); + DEBUG (( + DEBUG_INFO, + " BA=%016lx L=%016lx\n", + FirmwareVolumeHob->BaseAddress, + FirmwareVolumeHob->Length + )); + if ( + !( + ((EFI_PHYSICAL_ADDRESS)(UINTN)FirmwareVolumeHob->BaseAddress >= Private->PhysicalMemoryBegin) && + (((EFI_PHYSICAL_ADDRESS)(UINTN)FirmwareVolumeHob->BaseAddress + (FirmwareVolumeHob->Length - 1)) < Private->FreePhysicalMemoryTop) + ) + ) + { + DEBUG ((DEBUG_INFO, " Removing FV HOB to an FV in T-RAM (was not migrated).\n")); + Hob.Header->HobType = EFI_HOB_TYPE_UNUSED; + } + } + } +} + +/** + Migrate the base address in firmware volume allocation HOBs + from temporary memory to PEI installed memory. + + @param[in] PrivateData Pointer to PeiCore's private data structure. + @param[in] OrgFvHandle Address of FV Handle in temporary memory. + @param[in] FvHandle Address of FV Handle in permanent memory. + +**/ +VOID +ConvertFvHob ( + IN PEI_CORE_INSTANCE *PrivateData, + IN UINTN OrgFvHandle, + IN UINTN FvHandle ) -/*++ +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_FIRMWARE_VOLUME *FirmwareVolumeHob; + EFI_HOB_FIRMWARE_VOLUME2 *FirmwareVolume2Hob; + EFI_HOB_FIRMWARE_VOLUME3 *FirmwareVolume3Hob; + + DEBUG ((DEBUG_INFO, "Converting FVs in FV HOB.\n")); + + for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) { + if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV) { + FirmwareVolumeHob = Hob.FirmwareVolume; + if (FirmwareVolumeHob->BaseAddress == OrgFvHandle) { + FirmwareVolumeHob->BaseAddress = FvHandle; + } + } else if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV2) { + FirmwareVolume2Hob = Hob.FirmwareVolume2; + if (FirmwareVolume2Hob->BaseAddress == OrgFvHandle) { + FirmwareVolume2Hob->BaseAddress = FvHandle; + } + } else if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV3) { + FirmwareVolume3Hob = Hob.FirmwareVolume3; + if (FirmwareVolume3Hob->BaseAddress == OrgFvHandle) { + FirmwareVolume3Hob->BaseAddress = FvHandle; + } + } + } +} + +/** + Migrate MemoryBaseAddress in memory allocation HOBs + from the temporary memory to PEI installed memory. -Routine Description: + @param[in] PrivateData Pointer to PeiCore's private data structure. + +**/ +VOID +ConvertMemoryAllocationHobs ( + IN PEI_CORE_INSTANCE *PrivateData + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; + EFI_PHYSICAL_ADDRESS OldMemPagesBase; + UINTN OldMemPagesSize; - Memory allocation service on permanent memory, - not usable prior to the memory installation. + if (PrivateData->MemoryPages.Size == 0) { + // + // No any memory page allocated in pre-memory phase. + // + return; + } -Arguments: + OldMemPagesBase = PrivateData->MemoryPages.Base; + OldMemPagesSize = PrivateData->MemoryPages.Size; + + MemoryAllocationHob = NULL; + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); + while (Hob.Raw != NULL) { + MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; + if ((MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress >= OldMemPagesBase) && + (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress < (OldMemPagesBase + OldMemPagesSize)) + ) + { + if (PrivateData->MemoryPages.OffsetPositive) { + MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress += PrivateData->MemoryPages.Offset; + } else { + MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress -= PrivateData->MemoryPages.Offset; + } + } - PeiServices - The PEI core services table. - MemoryType - Type of memory to allocate. - Pages - Number of pages to allocate. - Memory - Pointer of memory allocated. + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); + } +} -Returns: +/** + Internal function to build a HOB for the memory allocation. + It will search and reuse the unused(freed) memory allocation HOB, + or build memory allocation HOB normally if no unused(freed) memory allocation HOB found. - Status - EFI_SUCCESS The allocation was successful - EFI_INVALID_PARAMETER Only AllocateAnyAddress is supported. - EFI_NOT_AVAILABLE_YET Called with permanent memory not available - EFI_OUT_OF_RESOURCES There is not enough HOB heap to satisfy the requirement - to allocate the number of pages. + @param[in] BaseAddress The 64 bit physical address of the memory. + @param[in] Length The length of the memory allocation in bytes. + @param[in] MemoryType The type of memory allocated by this HOB. ---*/ +**/ +VOID +InternalBuildMemoryAllocationHob ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN EFI_MEMORY_TYPE MemoryType + ) { - PEI_CORE_INSTANCE *PrivateData; - EFI_PEI_HOB_POINTERS Hob; - EFI_PHYSICAL_ADDRESS Offset; - EFI_PHYSICAL_ADDRESS *FreeMemoryTop; - EFI_PHYSICAL_ADDRESS *FreeMemoryBottom; + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; - PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); - Hob.Raw = PrivateData->HobList.Raw; - // - // Check if Hob already available + // Search unused(freed) memory allocation HOB. // - if (!PrivateData->PeiMemoryInstalled) { + MemoryAllocationHob = NULL; + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_UNUSED); + while (Hob.Raw != NULL) { + if (Hob.Header->HobLength == sizeof (EFI_HOB_MEMORY_ALLOCATION)) { + MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; + break; + } + + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_UNUSED, Hob.Raw); + } + + if (MemoryAllocationHob != NULL) { // - // When PeiInstallMemory is called but CAR has *not* been moved to temporary memory, - // the AllocatePage will dependent the field of PEI_CORE_INSTANCE structure. + // Reuse the unused(freed) memory allocation HOB. // - if (!PrivateData->SwitchStackSignal) { - return EFI_NOT_AVAILABLE_YET; - } else { - FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop); - FreeMemoryBottom = &(PrivateData->PhysicalMemoryBegin); - } + MemoryAllocationHob->Header.HobType = EFI_HOB_TYPE_MEMORY_ALLOCATION; + ZeroMem (&(MemoryAllocationHob->AllocDescriptor.Name), sizeof (EFI_GUID)); + MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress = BaseAddress; + MemoryAllocationHob->AllocDescriptor.MemoryLength = Length; + MemoryAllocationHob->AllocDescriptor.MemoryType = MemoryType; + // + // Zero the reserved space to match HOB spec + // + ZeroMem (MemoryAllocationHob->AllocDescriptor.Reserved, sizeof (MemoryAllocationHob->AllocDescriptor.Reserved)); } else { - FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop); - FreeMemoryBottom = &(Hob.HandoffInformationTable->EfiFreeMemoryBottom); + // + // No unused(freed) memory allocation HOB found. + // Build memory allocation HOB normally. + // + BuildMemoryAllocationHob ( + BaseAddress, + Length, + MemoryType + ); } +} - +/** + Update or split memory allocation HOB for memory pages allocate and free. + + @param[in, out] MemoryAllocationHob Pointer to the memory allocation HOB + that needs to be updated or split. + On output, it will be filled with + the input Memory, Bytes and MemoryType. + @param[in] Memory Memory to allocate or free. + @param[in] Bytes Bytes to allocate or free. + @param[in] MemoryType EfiConventionalMemory for pages free, + others for pages allocate. + +**/ +VOID +UpdateOrSplitMemoryAllocationHob ( + IN OUT EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob, + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINT64 Bytes, + IN EFI_MEMORY_TYPE MemoryType + ) +{ + if ((Memory + Bytes) < + (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress + MemoryAllocationHob->AllocDescriptor.MemoryLength)) + { + // + // Last pages need to be split out. + // + InternalBuildMemoryAllocationHob ( + Memory + Bytes, + (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress + MemoryAllocationHob->AllocDescriptor.MemoryLength) - (Memory + Bytes), + MemoryAllocationHob->AllocDescriptor.MemoryType + ); + } + + if (Memory > MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress) { + // + // First pages need to be split out. + // + InternalBuildMemoryAllocationHob ( + MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress, + Memory - MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress, + MemoryAllocationHob->AllocDescriptor.MemoryType + ); + } // - // Check to see if on 4k boundary + // Update the memory allocation HOB. // - Offset = *(FreeMemoryTop) & 0xFFF; - + MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress = Memory; + MemoryAllocationHob->AllocDescriptor.MemoryLength = Bytes; + MemoryAllocationHob->AllocDescriptor.MemoryType = MemoryType; +} + +/** + Merge adjacent free memory ranges in memory allocation HOBs. + + @retval TRUE There are free memory ranges merged. + @retval FALSE No free memory ranges merged. + +**/ +BOOLEAN +MergeFreeMemoryInMemoryAllocationHob ( + VOID + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_PEI_HOB_POINTERS Hob2; + EFI_HOB_MEMORY_ALLOCATION *MemoryHob; + EFI_HOB_MEMORY_ALLOCATION *MemoryHob2; + UINT64 Start; + UINT64 End; + BOOLEAN Merged; + + Merged = FALSE; + + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); + while (Hob.Raw != NULL) { + if (Hob.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) { + MemoryHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; + Start = MemoryHob->AllocDescriptor.MemoryBaseAddress; + End = MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength; + + Hob2.Raw = GET_NEXT_HOB (Hob); + Hob2.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); + while (Hob2.Raw != NULL) { + if (Hob2.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) { + MemoryHob2 = (EFI_HOB_MEMORY_ALLOCATION *)Hob2.Raw; + if (Start == (MemoryHob2->AllocDescriptor.MemoryBaseAddress + MemoryHob2->AllocDescriptor.MemoryLength)) { + // + // Merge adjacent two free memory ranges. + // + MemoryHob2->AllocDescriptor.MemoryLength += MemoryHob->AllocDescriptor.MemoryLength; + Merged = TRUE; + // + // Mark MemoryHob to be unused(freed). + // + MemoryHob->Header.HobType = EFI_HOB_TYPE_UNUSED; + break; + } else if (End == MemoryHob2->AllocDescriptor.MemoryBaseAddress) { + // + // Merge adjacent two free memory ranges. + // + MemoryHob2->AllocDescriptor.MemoryBaseAddress = MemoryHob->AllocDescriptor.MemoryBaseAddress; + MemoryHob2->AllocDescriptor.MemoryLength += MemoryHob->AllocDescriptor.MemoryLength; + Merged = TRUE; + // + // Mark MemoryHob to be unused(freed). + // + MemoryHob->Header.HobType = EFI_HOB_TYPE_UNUSED; + break; + } + } + + Hob2.Raw = GET_NEXT_HOB (Hob2); + Hob2.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob2.Raw); + } + } + + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); + } + + return Merged; +} + +/** + Find free memory by searching memory allocation HOBs. + + @param[in] MemoryType The type of memory to allocate. + @param[in] Pages The number of contiguous 4 KB pages to allocate. + @param[in] Granularity Page allocation granularity. + @param[out] 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_NOT_FOUND No memory allocation HOB with big enough free memory found. + +**/ +EFI_STATUS +FindFreeMemoryFromMemoryAllocationHob ( + IN EFI_MEMORY_TYPE MemoryType, + IN UINTN Pages, + IN UINTN Granularity, + OUT EFI_PHYSICAL_ADDRESS *Memory + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; + UINT64 Bytes; + EFI_PHYSICAL_ADDRESS BaseAddress; + + Bytes = LShiftU64 (Pages, EFI_PAGE_SHIFT); + + BaseAddress = 0; + MemoryAllocationHob = NULL; + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); + while (Hob.Raw != NULL) { + if ((Hob.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) && + (Hob.MemoryAllocation->AllocDescriptor.MemoryLength >= Bytes)) + { + // + // Found one memory allocation HOB with big enough free memory. + // + MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; + BaseAddress = MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress + + MemoryAllocationHob->AllocDescriptor.MemoryLength - Bytes; + // + // Make sure the granularity could be satisfied. + // + BaseAddress &= ~((EFI_PHYSICAL_ADDRESS)Granularity - 1); + if (BaseAddress >= MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress) { + break; + } + + BaseAddress = 0; + MemoryAllocationHob = NULL; + } + + // + // Continue to find. + // + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); + } + + if (MemoryAllocationHob != NULL) { + UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, BaseAddress, Bytes, MemoryType); + *Memory = BaseAddress; + return EFI_SUCCESS; + } else { + if (MergeFreeMemoryInMemoryAllocationHob ()) { + // + // Retry if there are free memory ranges merged. + // + return FindFreeMemoryFromMemoryAllocationHob (MemoryType, Pages, Granularity, Memory); + } + + return EFI_NOT_FOUND; + } +} + +/** + The purpose of the service is to publish an interface that allows + PEIMs to allocate memory ranges that are managed by the PEI Foundation. + + Prior to InstallPeiMemory() being called, PEI will allocate pages from the heap. + After InstallPeiMemory() is called, PEI will allocate pages within the region + of memory provided by InstallPeiMemory() service in a best-effort fashion. + Location-specific allocations are not managed by the PEI foundation code. + + @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, EfiReservedMemoryType, 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 + ) +{ + EFI_STATUS Status; + PEI_CORE_INSTANCE *PrivateData; + EFI_PEI_HOB_POINTERS Hob; + EFI_PHYSICAL_ADDRESS *FreeMemoryTop; + EFI_PHYSICAL_ADDRESS *FreeMemoryBottom; + UINTN RemainingPages; + UINTN Granularity; + UINTN Padding; + + if ((MemoryType != EfiLoaderCode) && + (MemoryType != EfiLoaderData) && + (MemoryType != EfiRuntimeServicesCode) && + (MemoryType != EfiRuntimeServicesData) && + (MemoryType != EfiBootServicesCode) && + (MemoryType != EfiBootServicesData) && + (MemoryType != EfiACPIReclaimMemory) && + (MemoryType != EfiReservedMemoryType) && + (MemoryType != EfiACPIMemoryNVS)) + { + return EFI_INVALID_PARAMETER; + } + + Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY; + + PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); + Hob.Raw = PrivateData->HobList.Raw; + + if (Hob.Raw == NULL) { + // + // HOB is not initialized yet. + // + return EFI_NOT_AVAILABLE_YET; + } + + if ((RUNTIME_PAGE_ALLOCATION_GRANULARITY > DEFAULT_PAGE_ALLOCATION_GRANULARITY) && + ((MemoryType == EfiACPIReclaimMemory) || + (MemoryType == EfiACPIMemoryNVS) || + (MemoryType == EfiRuntimeServicesCode) || + (MemoryType == EfiRuntimeServicesData))) + { + Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY; + + DEBUG (( + DEBUG_INFO, + "AllocatePages: aligning allocation to %d KB\n", + Granularity / SIZE_1KB + )); + } + + if (!PrivateData->PeiMemoryInstalled && PrivateData->SwitchStackSignal) { + // + // When PeiInstallMemory is called but temporary memory has *not* been moved to permanent memory, + // the AllocatePage will depend on the field of PEI_CORE_INSTANCE structure. + // + FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop); + FreeMemoryBottom = &(PrivateData->PhysicalMemoryBegin); + } else { + FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop); + FreeMemoryBottom = &(Hob.HandoffInformationTable->EfiFreeMemoryBottom); + } + // + // Check to see if on correct boundary for the memory type. // If not aligned, make the allocation aligned. // - if (Offset != 0) { - *(FreeMemoryTop) -= Offset; + Padding = *(FreeMemoryTop) & (Granularity - 1); + if ((UINTN)(*FreeMemoryTop - *FreeMemoryBottom) < Padding) { + DEBUG ((DEBUG_ERROR, "AllocatePages failed: Out of space after padding.\n")); + return EFI_OUT_OF_RESOURCES; } - + + *(FreeMemoryTop) -= Padding; + if (Padding >= EFI_PAGE_SIZE) { + // + // Create a memory allocation HOB to cover + // the pages that we will lose to rounding + // + InternalBuildMemoryAllocationHob ( + *(FreeMemoryTop), + Padding & ~(UINTN)EFI_PAGE_MASK, + EfiConventionalMemory + ); + } + + // + // Verify that there is sufficient memory to satisfy the allocation. + // + RemainingPages = (UINTN)(*FreeMemoryTop - *FreeMemoryBottom) >> EFI_PAGE_SHIFT; // - // Verify that there is sufficient memory to satisfy the allocation + // The number of remaining pages needs to be greater than or equal to that of the request pages. // - if (*(FreeMemoryTop) - ((Pages * EFI_PAGE_SIZE) + sizeof (EFI_HOB_MEMORY_ALLOCATION)) < - *(FreeMemoryBottom)) { - DEBUG ((EFI_D_ERROR, "AllocatePages failed: No 0x%x Pages is available.\n", Pages)); - DEBUG ((EFI_D_ERROR, "There is only left 0x%x pages memory resource to be allocated.\n", \ - EFI_SIZE_TO_PAGES ((UINTN) (*(FreeMemoryTop) - *(FreeMemoryBottom))))); - return EFI_OUT_OF_RESOURCES; + Pages = ALIGN_VALUE (Pages, EFI_SIZE_TO_PAGES (Granularity)); + if (RemainingPages < Pages) { + // + // Try to find free memory by searching memory allocation HOBs. + // + Status = FindFreeMemoryFromMemoryAllocationHob (MemoryType, Pages, Granularity, Memory); + if (!EFI_ERROR (Status)) { + return Status; + } + + DEBUG ((DEBUG_ERROR, "AllocatePages failed: No 0x%lx Pages is available.\n", (UINT64)Pages)); + DEBUG ((DEBUG_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 @@ -222,7 +707,7 @@ Returns: // // Create a memory allocation HOB. // - BuildMemoryAllocationHob ( + InternalBuildMemoryAllocationHob ( *(FreeMemoryTop), Pages * EFI_PAGE_SIZE, MemoryType @@ -232,51 +717,198 @@ Returns: } } +/** + Mark the memory allocation HOB to be unused(freed) and update *FreeMemoryTop + if MemoryBaseAddress == *FreeMemoryTop. + + @param[in] PrivateData Pointer to PeiCore's private data structure. + @param[in, out] MemoryAllocationHobToFree Pointer to memory allocation HOB to be freed. + +**/ +VOID +FreeMemoryAllocationHob ( + IN PEI_CORE_INSTANCE *PrivateData, + IN OUT EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHobToFree + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_PHYSICAL_ADDRESS *FreeMemoryTop; + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; + + Hob.Raw = PrivateData->HobList.Raw; + + if (!PrivateData->PeiMemoryInstalled && PrivateData->SwitchStackSignal) { + // + // When PeiInstallMemory is called but temporary memory has *not* been moved to permanent memory, + // use the FreePhysicalMemoryTop field of PEI_CORE_INSTANCE structure. + // + FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop); + } else { + FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop); + } + + if (MemoryAllocationHobToFree->AllocDescriptor.MemoryBaseAddress == *FreeMemoryTop) { + // + // Update *FreeMemoryTop. + // + *FreeMemoryTop += MemoryAllocationHobToFree->AllocDescriptor.MemoryLength; + // + // Mark the memory allocation HOB to be unused(freed). + // + MemoryAllocationHobToFree->Header.HobType = EFI_HOB_TYPE_UNUSED; + + MemoryAllocationHob = NULL; + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); + while (Hob.Raw != NULL) { + if ((Hob.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) && + (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress == *FreeMemoryTop)) + { + // + // Found memory allocation HOB that has EfiConventionalMemory MemoryType and + // MemoryBaseAddress == new *FreeMemoryTop. + // + MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; + break; + } + + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); + } + + // + // Free memory allocation HOB iteratively. + // + if (MemoryAllocationHob != NULL) { + FreeMemoryAllocationHob (PrivateData, MemoryAllocationHob); + } + } +} + +/** + Frees memory pages. + + @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. + @param[in] Memory The base physical address of the pages to be freed. + @param[in] Pages The number of contiguous 4 KB pages to free. + @retval EFI_SUCCESS The requested pages were freed. + @retval EFI_INVALID_PARAMETER Memory is not a page-aligned address or Pages is invalid. + @retval EFI_NOT_FOUND The requested memory pages were not allocated with + AllocatePages(). + +**/ EFI_STATUS EFIAPI -PeiAllocatePool ( - IN CONST EFI_PEI_SERVICES **PeiServices, - IN UINTN Size, - OUT VOID **Buffer +PeiFreePages ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN EFI_PHYSICAL_ADDRESS Memory, + IN UINTN Pages ) -/*++ +{ + PEI_CORE_INSTANCE *PrivateData; + UINT64 Bytes; + UINT64 Start; + UINT64 End; + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; + + Bytes = LShiftU64 (Pages, EFI_PAGE_SHIFT); + Start = Memory; + End = Start + Bytes - 1; + + if ((Pages == 0) || ((Start & EFI_PAGE_MASK) != 0) || (Start >= End)) { + return EFI_INVALID_PARAMETER; + } + + PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); + Hob.Raw = PrivateData->HobList.Raw; + + if (Hob.Raw == NULL) { + // + // HOB is not initialized yet. + // + return EFI_NOT_AVAILABLE_YET; + } -Routine Description: + MemoryAllocationHob = NULL; + Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION); + while (Hob.Raw != NULL) { + if ((Hob.MemoryAllocation->AllocDescriptor.MemoryType != EfiConventionalMemory) && + (Memory >= Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress) && + ((Memory + Bytes) <= (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress + Hob.MemoryAllocation->AllocDescriptor.MemoryLength))) + { + // + // Found the memory allocation HOB that includes the memory pages to be freed. + // + MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *)Hob.Raw; + break; + } - Memory allocation service on the CAR. + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); + } -Arguments: + if (MemoryAllocationHob != NULL) { + UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, Memory, Bytes, EfiConventionalMemory); + FreeMemoryAllocationHob (PrivateData, MemoryAllocationHob); + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} - PeiServices - The PEI core services table. +/** - Size - Amount of memory required + Pool allocation service. Before permanent memory is discovered, the pool will + be allocated in the heap in temporary memory. Generally, the size of the heap in temporary + memory does not exceed 64K, so the biggest pool size could be allocated is + 64K. - Buffer - Address of pointer to the buffer + @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. + @param Size Amount of memory required + @param Buffer Address of pointer to the buffer -Returns: + @retval EFI_SUCCESS The allocation was successful + @retval EFI_OUT_OF_RESOURCES There is not enough heap to satisfy the requirement + to allocate the requested size. - Status - EFI_SUCCESS The allocation was successful - EFI_OUT_OF_RESOURCES There is not enough heap to satisfy the requirement - to allocate the requested size. - ---*/ +**/ +EFI_STATUS +EFIAPI +PeiAllocatePool ( + IN CONST EFI_PEI_SERVICES **PeiServices, + IN UINTN Size, + OUT VOID **Buffer + ) { - EFI_STATUS Status; - EFI_HOB_MEMORY_POOL *Hob; - - // - // If some "post-memory" PEIM wishes to allocate larger pool, - // it should use AllocatePages service instead. - // - ASSERT (Size < 0x10000 - sizeof (EFI_HOB_MEMORY_POOL)); - Status = PeiServicesCreateHob ( + EFI_STATUS Status; + EFI_HOB_MEMORY_POOL *Hob; + + // + // If some "post-memory" PEIM wishes to allocate larger pool, + // it should use AllocatePages service instead. + // + + // + // Generally, the size of heap in temporary memory does not exceed 64K, + // HobLength is multiples of 8 bytes, so the maximum size of pool is 0xFFF8 - sizeof (EFI_HOB_MEMORY_POOL) + // + if (Size > (0xFFF8 - sizeof (EFI_HOB_MEMORY_POOL))) { + return EFI_OUT_OF_RESOURCES; + } + + Status = PeiServicesCreateHob ( EFI_HOB_TYPE_MEMORY_POOL, (UINT16)(sizeof (EFI_HOB_MEMORY_POOL) + Size), (VOID **)&Hob ); - *Buffer = Hob+1; + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + *Buffer = NULL; + } else { + *Buffer = Hob + 1; + } return Status; }