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;
}