X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=MdeModulePkg%2FCore%2FPei%2FMemory%2FMemoryServices.c;h=6b3a64a811cd3cff475fd708420d721a88995b8c;hb=HEAD;hp=e623c0109924d0e66308c17c0e6df8444d43f86f;hpb=859b72fa7e3ff1cf1d7476a3446af4ebbb5fe3e6;p=mirror_edk2.git diff --git a/MdeModulePkg/Core/Pei/Memory/MemoryServices.c b/MdeModulePkg/Core/Pei/Memory/MemoryServices.c index e623c01099..3b2e15699f 100644 --- a/MdeModulePkg/Core/Pei/Memory/MemoryServices.c +++ b/MdeModulePkg/Core/Pei/Memory/MemoryServices.c @@ -1,269 +1,715 @@ -/*++ - -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 EFI_PEI_SERVICES **PeiServices, - IN EFI_PEI_STARTUP_DESCRIPTOR *PeiStartupDescriptor, + IN PEI_CORE_INSTANCE *PrivateData, + IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData, IN PEI_CORE_INSTANCE *OldCoreData ) -/*++ +{ + PrivateData->SwitchStackSignal = FALSE; -Routine Description: + // + // 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; - Initialize the memory services. + PeiCoreBuildHobHandoffInfoTable ( + BOOT_WITH_FULL_CONFIGURATION, + (EFI_PHYSICAL_ADDRESS)(UINTN)SecCoreData->PeiTemporaryRamBase, + (UINTN)SecCoreData->PeiTemporaryRamSize + ); -Arguments: + // + // Set Ps to point to ServiceTableShadow in Cache + // + PrivateData->Ps = &(PrivateData->ServiceTableShadow); + } + + return; +} + +/** - PeiServices - The PEI core services table. - PeiStartupDescriptor - Information and services provided by SEC phase. - OldCoreData - Pointer to the PEI Core data. - NULL if being run in non-permament memory mode. + This function registers the found memory configuration with the PEI Foundation. -Returns: + 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. - None + @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; - UINT64 SizeOfCarHeap; + PEI_CORE_INSTANCE *PrivateData; + DEBUG ((DEBUG_INFO, "PeiInstallPeiMemory MemoryBegin 0x%LX, MemoryLength 0x%LX\n", MemoryBegin, MemoryLength)); PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); - PrivateData->SwitchStackSignal = FALSE; - if (OldCoreData == NULL) { + // + // 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; + } - PrivateData->PeiMemoryInstalled = FALSE; + PrivateData->PhysicalMemoryBegin = MemoryBegin; + PrivateData->PhysicalMemoryLength = MemoryLength; + PrivateData->FreePhysicalMemoryTop = MemoryBegin + MemoryLength; - PrivateData->BottomOfCarHeap = (VOID *) (((UINTN)(VOID *)(&PrivateData)) - & (~((PeiStartupDescriptor->SizeOfCacheAsRam) - 1))); - PrivateData->TopOfCarHeap = (VOID *)((UINTN)(PrivateData->BottomOfCarHeap) + PeiStartupDescriptor->SizeOfCacheAsRam); - // - // SizeOfCarHeap is 1/2 (arbitrary) of CacheAsRam Size. - // - SizeOfCarHeap = (UINT64) PeiStartupDescriptor->SizeOfCacheAsRam; - SizeOfCarHeap = RShiftU64 (SizeOfCarHeap, 1); - - DEBUG_CODE_BEGIN (); - PrivateData->SizeOfCacheAsRam = PeiStartupDescriptor->SizeOfCacheAsRam; - PrivateData->MaxTopOfCarHeap = (VOID *) ((UINTN) PrivateData->BottomOfCarHeap + (UINTN) SizeOfCarHeap); - DEBUG_CODE_END (); + PrivateData->SwitchStackSignal = TRUE; - PrivateData->HobList.Raw = PrivateData->BottomOfCarHeap; - - PeiCoreBuildHobHandoffInfoTable ( - BOOT_WITH_FULL_CONFIGURATION, - (EFI_PHYSICAL_ADDRESS) (UINTN) PrivateData->BottomOfCarHeap, - (UINTN) SizeOfCarHeap - ); + return EFI_SUCCESS; +} + +/** + Migrate memory pages allocated in pre-memory phase. + Copy memory pages at temporary heap top to permanent heap top. + + @param[in] Private Pointer to the private data passed in from caller. + @param[in] TemporaryRamMigrated Temporary memory has been migrated to permanent memory. + +**/ +VOID +MigrateMemoryPages ( + IN PEI_CORE_INSTANCE *Private, + IN BOOLEAN TemporaryRamMigrated + ) +{ + EFI_PHYSICAL_ADDRESS NewMemPagesBase; + EFI_PHYSICAL_ADDRESS MemPagesBase; + + Private->MemoryPages.Size = (UINTN)(Private->HobList.HandoffInformationTable->EfiMemoryTop - + Private->HobList.HandoffInformationTable->EfiFreeMemoryTop); + if (Private->MemoryPages.Size == 0) { // - // Copy PeiServices from ROM to Cache in PrivateData + // No any memory page allocated in pre-memory phase. // - CopyMem (&(PrivateData->ServiceTableShadow), *PeiServices, sizeof (EFI_PEI_SERVICES)); + return; + } + Private->MemoryPages.Base = Private->HobList.HandoffInformationTable->EfiFreeMemoryTop; + + 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) { // - // Set PS to point to ServiceTableShadow in Cache + // 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. // - PrivateData->PS = &(PrivateData->ServiceTableShadow); + 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 { - // - // Set PS to point to ServiceTableShadow in Cache one time after the - // stack switched to main memory - // - PrivateData->PS = &(PrivateData->ServiceTableShadow); -} + CopyMem ((VOID *)(UINTN)NewMemPagesBase, (VOID *)(UINTN)Private->MemoryPages.Base, Private->MemoryPages.Size); + } - return; + 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 -PeiInstallPeiMemory ( - IN EFI_PEI_SERVICES **PeiServices, - IN EFI_PHYSICAL_ADDRESS MemoryBegin, - IN UINT64 MemoryLength +/** + 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; + } + } + } +} -Routine Description: +/** + Migrate the base address in firmware volume allocation HOBs + from temporary memory to PEI installed memory. - Install the permanent memory is now available. - Creates HOB (PHIT and Stack). + @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. -Arguments: +**/ +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; + } + } + } +} - PeiServices - The PEI core services table. - MemoryBegin - Start of memory address. - MemoryLength - Length of memory. +/** + Migrate MemoryBaseAddress in memory allocation HOBs + from the temporary memory to PEI installed memory. -Returns: + @param[in] PrivateData Pointer to PeiCore's private data structure. - Status - EFI_SUCCESS - ---*/ +**/ +VOID +ConvertMemoryAllocationHobs ( + IN PEI_CORE_INSTANCE *PrivateData + ) { - PEI_CORE_INSTANCE *PrivateData; - EFI_HOB_HANDOFF_INFO_TABLE *OldHandOffHob; - EFI_HOB_HANDOFF_INFO_TABLE *NewHandOffHob; - UINT64 PeiStackSize; - UINT64 EfiFreeMemorySize; - EFI_PHYSICAL_ADDRESS PhysicalAddressOfOldHob; - - PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; + EFI_PHYSICAL_ADDRESS OldMemPagesBase; + UINTN OldMemPagesSize; - PrivateData->SwitchStackSignal = TRUE; - PrivateData->PeiMemoryInstalled = TRUE; + if (PrivateData->MemoryPages.Size == 0) { + // + // No any memory page allocated in pre-memory phase. + // + return; + } + + 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; + } + } + + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); + } +} - PrivateData->StackBase = MemoryBegin; - - PeiStackSize = RShiftU64 (MemoryLength, 1); - if (PEI_STACK_SIZE > PeiStackSize) { - PrivateData->StackSize = PeiStackSize; +/** + 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. + + @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 + ) +{ + EFI_PEI_HOB_POINTERS Hob; + EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob; + + // + // Search unused(freed) memory allocation HOB. + // + 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) { + // + // Reuse the unused(freed) memory allocation HOB. + // + 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 { - PrivateData->StackSize = PEI_STACK_SIZE; + // + // No unused(freed) memory allocation HOB found. + // Build memory allocation HOB normally. + // + BuildMemoryAllocationHob ( + BaseAddress, + Length, + MemoryType + ); } +} - OldHandOffHob = PrivateData->HobList.HandoffInformationTable; +/** + Update or split memory allocation HOB for memory pages allocate and free. - PrivateData->HobList.Raw = (VOID *)((UINTN)(MemoryBegin + PrivateData->StackSize)); - NewHandOffHob = PrivateData->HobList.HandoffInformationTable; - PhysicalAddressOfOldHob = (EFI_PHYSICAL_ADDRESS) (UINTN) OldHandOffHob; + @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. - EfiFreeMemorySize = OldHandOffHob->EfiFreeMemoryBottom - PhysicalAddressOfOldHob; - - DEBUG ((EFI_D_INFO, "HOBLIST address before memory init = 0x%08x\n", OldHandOffHob)); - DEBUG ((EFI_D_INFO, "HOBLIST address after memory init = 0x%08x\n", NewHandOffHob)); +**/ +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 + ); + } - CopyMem ( - NewHandOffHob, - OldHandOffHob, - (UINTN)EfiFreeMemorySize - ); + if (Memory > MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress) { + // + // First pages need to be split out. + // + InternalBuildMemoryAllocationHob ( + MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress, + Memory - MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress, + MemoryAllocationHob->AllocDescriptor.MemoryType + ); + } + + // + // Update the memory allocation HOB. + // + MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress = Memory; + MemoryAllocationHob->AllocDescriptor.MemoryLength = Bytes; + MemoryAllocationHob->AllocDescriptor.MemoryType = MemoryType; +} - NewHandOffHob->EfiMemoryTop = MemoryBegin + MemoryLength; - NewHandOffHob->EfiFreeMemoryTop = NewHandOffHob->EfiMemoryTop; - NewHandOffHob->EfiMemoryBottom = MemoryBegin; - - NewHandOffHob->EfiFreeMemoryBottom = (UINTN)NewHandOffHob + EfiFreeMemorySize; - - NewHandOffHob->EfiEndOfHobList = (UINTN)NewHandOffHob + - (OldHandOffHob->EfiEndOfHobList - - PhysicalAddressOfOldHob); +/** + Merge adjacent free memory ranges in memory allocation HOBs. - ConvertPpiPointers (PeiServices, OldHandOffHob, NewHandOffHob); + @retval TRUE There are free memory ranges merged. + @retval FALSE No free memory ranges merged. - BuildStackHob (PrivateData->StackBase, PrivateData->StackSize); - +**/ +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 EFI_SUCCESS; + 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 -EFIAPI -PeiAllocatePages ( - IN EFI_PEI_SERVICES **PeiServices, - IN EFI_MEMORY_TYPE MemoryType, - IN UINTN Pages, - OUT EFI_PHYSICAL_ADDRESS *Memory +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; + } -Routine Description: + // + // Continue to find. + // + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); + } - Memory allocation service on permanent memory, - not usable prior to the memory installation. + 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; + } +} -Arguments: +/** + The purpose of the service is to publish an interface that allows + PEIMs to allocate memory ranges that are managed by the PEI Foundation. - PeiServices - The PEI core services table. - MemoryType - Type of memory to allocate. - Pages - Number of pages to allocate. - Memory - Pointer of memory allocated. + 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. -Returns: + @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. - 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. + @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 + ) { - PEI_CORE_INSTANCE *PrivateData; - EFI_PEI_HOB_POINTERS Hob; - EFI_PHYSICAL_ADDRESS Offset; + 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; - // - // Check if Hob already available - // - if (!PrivateData->PeiMemoryInstalled) { + if (Hob.Raw == NULL) { + // + // HOB is not initialized yet. + // return EFI_NOT_AVAILABLE_YET; } - Hob.Raw = PrivateData->HobList.Raw; + 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 + )); + } - // - // Check to see if on 4k boundary - // - Offset = Hob.HandoffInformationTable->EfiFreeMemoryTop & 0xFFF; + 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) { - Hob.HandoffInformationTable->EfiFreeMemoryTop -= 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 (Hob.HandoffInformationTable->EfiFreeMemoryTop - ((Pages * EFI_PAGE_SIZE) + sizeof (EFI_HOB_MEMORY_ALLOCATION)) < - Hob.HandoffInformationTable->EfiFreeMemoryBottom) { - 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) (Hob.HandoffInformationTable->EfiFreeMemoryTop - Hob.HandoffInformationTable->EfiFreeMemoryBottom)))); - 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 // - Hob.HandoffInformationTable->EfiFreeMemoryTop -= Pages * EFI_PAGE_SIZE; + *(FreeMemoryTop) -= Pages * EFI_PAGE_SIZE; // // Update the value for the caller // - *Memory = Hob.HandoffInformationTable->EfiFreeMemoryTop; + *Memory = *(FreeMemoryTop); // // Create a memory allocation HOB. // - BuildMemoryAllocationHob ( - Hob.HandoffInformationTable->EfiFreeMemoryTop, - Pages * EFI_PAGE_SIZE + Offset, + InternalBuildMemoryAllocationHob ( + *(FreeMemoryTop), + Pages * EFI_PAGE_SIZE, MemoryType ); @@ -271,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 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; + } -Routine Description: + PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices); + Hob.Raw = PrivateData->HobList.Raw; - Memory allocation service on the CAR. + if (Hob.Raw == NULL) { + // + // HOB is not initialized yet. + // + return EFI_NOT_AVAILABLE_YET; + } -Arguments: + 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; + } + + Hob.Raw = GET_NEXT_HOB (Hob); + Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw); + } - PeiServices - The PEI core services table. + if (MemoryAllocationHob != NULL) { + UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, Memory, Bytes, EfiConventionalMemory); + FreeMemoryAllocationHob (PrivateData, MemoryAllocationHob); + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + +/** - 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; }