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