X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=MdeModulePkg%2FCore%2FPei%2FMemory%2FMemoryServices.c;h=4dc62d2262dbcc76089ede584f6bfb043c9ec8e9;hb=d1102dba7210b95e41d06c2338a22ba6af248645;hp=2f9b9dea3149202a0c0d7fcd13a5135bdb443e8d;hpb=305d3c8e8da4b0bb0f3f8d216d8ac91fe33d5464;p=mirror_edk2.git diff --git a/MdeModulePkg/Core/Pei/Memory/MemoryServices.c b/MdeModulePkg/Core/Pei/Memory/MemoryServices.c index 2f9b9dea31..4dc62d2262 100644 --- a/MdeModulePkg/Core/Pei/Memory/MemoryServices.c +++ b/MdeModulePkg/Core/Pei/Memory/MemoryServices.c @@ -1,14 +1,14 @@ /** @file EFI PEI Core memory services - -Copyright (c) 2006 - 2015, 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. + +Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
+This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ @@ -33,7 +33,7 @@ InitializeMemoryServices ( IN PEI_CORE_INSTANCE *OldCoreData ) { - + PrivateData->SwitchStackSignal = FALSE; // @@ -44,7 +44,7 @@ InitializeMemoryServices ( PrivateData->PeiMemoryInstalled = FALSE; PrivateData->HobList.Raw = SecCoreData->PeiTemporaryRamBase; - + PeiCoreBuildHobHandoffInfoTable ( BOOT_WITH_FULL_CONFIGURATION, (EFI_PHYSICAL_ADDRESS) (UINTN) SecCoreData->PeiTemporaryRamBase, @@ -56,7 +56,7 @@ InitializeMemoryServices ( // PrivateData->Ps = &(PrivateData->ServiceTableShadow); } - + return; } @@ -68,7 +68,7 @@ InitializeMemoryServices ( 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. @@ -93,35 +93,404 @@ PeiInstallPeiMemory ( // 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 (FALSE); return EFI_SUCCESS; } - + PrivateData->PhysicalMemoryBegin = MemoryBegin; PrivateData->PhysicalMemoryLength = MemoryLength; PrivateData->FreePhysicalMemoryTop = MemoryBegin + MemoryLength; - + PrivateData->SwitchStackSignal = TRUE; - return EFI_SUCCESS; + 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) { + // + // No any memory page allocated in pre-memory phase. + // + 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) { + // + // 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; +} + +/** + Migrate MemoryBaseAddress in memory allocation HOBs + from the temporary memory to PEI installed memory. + + @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; + + 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); + } +} + +/** + 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 { + // + // 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 + ); + } + + // + // Update the memory allocation HOB. + // + 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; } /** - The purpose of the service is to publish an interface that allows + 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 + @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, + @retval EFI_INVALID_PARAMETER Type is not equal to EfiLoaderCode, EfiLoaderData, EfiRuntimeServicesCode, EfiRuntimeServicesData, EfiBootServicesCode, EfiBootServicesData, EfiACPIReclaimMemory, EfiReservedMemoryType, or EfiACPIMemoryNVS. @@ -135,11 +504,14 @@ PeiAllocatePages ( 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) && @@ -153,46 +525,81 @@ PeiAllocatePages ( 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; + } + + 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 temporary memory, + // 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. // - if (!PrivateData->SwitchStackSignal) { - return EFI_NOT_AVAILABLE_YET; - } else { - FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop); - FreeMemoryBottom = &(PrivateData->PhysicalMemoryBegin); - } + 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. + // Check to see if on correct boundary for the memory type. + // If not aligned, make the allocation aligned. // - *(FreeMemoryTop) -= *(FreeMemoryTop) & 0xFFF; + 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. - // For page allocation, the overhead sizeof (EFI_HOB_MEMORY_ALLOCATION) needs to be considered. // - if ((UINTN) (*FreeMemoryTop - *FreeMemoryBottom) < (UINTN) ALIGN_VALUE (sizeof (EFI_HOB_MEMORY_ALLOCATION), 8)) { - DEBUG ((EFI_D_ERROR, "AllocatePages failed: No space to build memory allocation hob.\n")); - return EFI_OUT_OF_RESOURCES; - } - RemainingPages = (UINTN)(*FreeMemoryTop - *FreeMemoryBottom - ALIGN_VALUE (sizeof (EFI_HOB_MEMORY_ALLOCATION), 8)) >> EFI_PAGE_SHIFT; + RemainingPages = (UINTN)(*FreeMemoryTop - *FreeMemoryBottom) >> EFI_PAGE_SHIFT; // // The number of remaining pages needs to be greater than or equal to that of the request pages. // + 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 ((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; @@ -210,7 +617,7 @@ PeiAllocatePages ( // // Create a memory allocation HOB. // - BuildMemoryAllocationHob ( + InternalBuildMemoryAllocationHob ( *(FreeMemoryTop), Pages * EFI_PAGE_SIZE, MemoryType @@ -220,11 +627,146 @@ PeiAllocatePages ( } } +/** + 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 +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; + } + + 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); + } + + if (MemoryAllocationHob != NULL) { + UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, Memory, Bytes, EfiConventionalMemory); + FreeMemoryAllocationHob (PrivateData, MemoryAllocationHob); + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + /** - Pool allocation service. Before permenent memory is discoveried, the pool will - be allocated the heap in the temporary memory. Genenrally, the size of heap in temporary - memory does not exceed to 64K, so the biggest pool size could be allocated is + Pool allocation service. Before permanent memory is discoveried, the pool will + be allocated the heap in the temporary memory. Genenrally, the size of heap in temporary + memory does not exceed to 64K, so the biggest pool size could be allocated is 64K. @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation. @@ -251,7 +793,7 @@ PeiAllocatePool ( // 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 to 64K, // HobLength is multiples of 8 bytes, so the maxmium size of pool is 0xFFF8 - sizeof (EFI_HOB_MEMORY_POOL) @@ -259,14 +801,14 @@ PeiAllocatePool ( 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 ); ASSERT_EFI_ERROR (Status); - *Buffer = Hob+1; + *Buffer = Hob+1; return Status; }