/** @file | |
UEFI Memory page management functions. | |
Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "DxeMain.h" | |
#include "Imem.h" | |
#include "HeapGuard.h" | |
#include <Pi/PiDxeCis.h> | |
// | |
// Entry for tracking the memory regions for each memory type to coalesce similar memory types | |
// | |
typedef struct { | |
EFI_PHYSICAL_ADDRESS BaseAddress; | |
EFI_PHYSICAL_ADDRESS MaximumAddress; | |
UINT64 CurrentNumberOfPages; | |
UINT64 NumberOfPages; | |
UINTN InformationIndex; | |
BOOLEAN Special; | |
BOOLEAN Runtime; | |
} EFI_MEMORY_TYPE_STATISTICS; | |
// | |
// MemoryMap - The current memory map | |
// | |
UINTN mMemoryMapKey = 0; | |
#define MAX_MAP_DEPTH 6 | |
/// | |
/// mMapDepth - depth of new descriptor stack | |
/// | |
UINTN mMapDepth = 0; | |
/// | |
/// mMapStack - space to use as temp storage to build new map descriptors | |
/// | |
MEMORY_MAP mMapStack[MAX_MAP_DEPTH]; | |
UINTN mFreeMapStack = 0; | |
/// | |
/// This list maintain the free memory map list | |
/// | |
LIST_ENTRY mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList); | |
BOOLEAN mMemoryTypeInformationInitialized = FALSE; | |
EFI_MEMORY_TYPE_STATISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1] = { | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiReservedMemoryType | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderCode | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderData | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesCode | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesData | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesCode | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesData | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiConventionalMemory | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiUnusableMemory | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIReclaimMemory | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIMemoryNVS | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIO | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIOPortSpace | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiPalCode | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiPersistentMemory | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiUnacceptedMemoryType | |
{ 0, MAX_ALLOC_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE } // EfiMaxMemoryType | |
}; | |
EFI_PHYSICAL_ADDRESS mDefaultMaximumAddress = MAX_ALLOC_ADDRESS; | |
EFI_PHYSICAL_ADDRESS mDefaultBaseAddress = MAX_ALLOC_ADDRESS; | |
EFI_MEMORY_TYPE_INFORMATION gMemoryTypeInformation[EfiMaxMemoryType + 1] = { | |
{ EfiReservedMemoryType, 0 }, | |
{ EfiLoaderCode, 0 }, | |
{ EfiLoaderData, 0 }, | |
{ EfiBootServicesCode, 0 }, | |
{ EfiBootServicesData, 0 }, | |
{ EfiRuntimeServicesCode, 0 }, | |
{ EfiRuntimeServicesData, 0 }, | |
{ EfiConventionalMemory, 0 }, | |
{ EfiUnusableMemory, 0 }, | |
{ EfiACPIReclaimMemory, 0 }, | |
{ EfiACPIMemoryNVS, 0 }, | |
{ EfiMemoryMappedIO, 0 }, | |
{ EfiMemoryMappedIOPortSpace, 0 }, | |
{ EfiPalCode, 0 }, | |
{ EfiPersistentMemory, 0 }, | |
{ EfiGcdMemoryTypeUnaccepted, 0 }, | |
{ EfiMaxMemoryType, 0 } | |
}; | |
// | |
// Only used when load module at fixed address feature is enabled. True means the memory is alreay successfully allocated | |
// and ready to load the module in to specified address.or else, the memory is not ready and module will be loaded at a | |
// address assigned by DXE core. | |
// | |
GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN gLoadFixedAddressCodeMemoryReady = FALSE; | |
/** | |
Enter critical section by gaining lock on gMemoryLock. | |
**/ | |
VOID | |
CoreAcquireMemoryLock ( | |
VOID | |
) | |
{ | |
CoreAcquireLock (&gMemoryLock); | |
} | |
/** | |
Exit critical section by releasing lock on gMemoryLock. | |
**/ | |
VOID | |
CoreReleaseMemoryLock ( | |
VOID | |
) | |
{ | |
CoreReleaseLock (&gMemoryLock); | |
} | |
/** | |
Internal function. Removes a descriptor entry. | |
@param Entry The entry to remove | |
**/ | |
VOID | |
RemoveMemoryMapEntry ( | |
IN OUT MEMORY_MAP *Entry | |
) | |
{ | |
RemoveEntryList (&Entry->Link); | |
Entry->Link.ForwardLink = NULL; | |
if (Entry->FromPages) { | |
// | |
// Insert the free memory map descriptor to the end of mFreeMemoryMapEntryList | |
// | |
InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link); | |
} | |
} | |
/** | |
Internal function. Adds a ranges to the memory map. | |
The range must not already exist in the map. | |
@param Type The type of memory range to add | |
@param Start The starting address in the memory range Must be | |
paged aligned | |
@param End The last address in the range Must be the last | |
byte of a page | |
@param Attribute The attributes of the memory range to add | |
**/ | |
VOID | |
CoreAddRange ( | |
IN EFI_MEMORY_TYPE Type, | |
IN EFI_PHYSICAL_ADDRESS Start, | |
IN EFI_PHYSICAL_ADDRESS End, | |
IN UINT64 Attribute | |
) | |
{ | |
LIST_ENTRY *Link; | |
MEMORY_MAP *Entry; | |
ASSERT ((Start & EFI_PAGE_MASK) == 0); | |
ASSERT (End > Start); | |
ASSERT_LOCKED (&gMemoryLock); | |
DEBUG ((DEBUG_PAGE, "AddRange: %lx-%lx to %d\n", Start, End, Type)); | |
// | |
// If memory of type EfiConventionalMemory is being added that includes the page | |
// starting at address 0, then zero the page starting at address 0. This has | |
// two benifits. It helps find NULL pointer bugs and it also maximizes | |
// compatibility with operating systems that may evaluate memory in this page | |
// for legacy data structures. If memory of any other type is added starting | |
// at address 0, then do not zero the page at address 0 because the page is being | |
// used for other purposes. | |
// | |
if ((Type == EfiConventionalMemory) && (Start == 0) && (End >= EFI_PAGE_SIZE - 1)) { | |
if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT0) == 0) { | |
SetMem ((VOID *)(UINTN)Start, EFI_PAGE_SIZE, 0); | |
} | |
} | |
// | |
// Memory map being altered so updated key | |
// | |
mMemoryMapKey += 1; | |
// | |
// UEFI 2.0 added an event group for notificaiton on memory map changes. | |
// So we need to signal this Event Group every time the memory map changes. | |
// If we are in EFI 1.10 compatability mode no event groups will be | |
// found and nothing will happen we we call this function. These events | |
// will get signaled but since a lock is held around the call to this | |
// function the notificaiton events will only be called after this function | |
// returns and the lock is released. | |
// | |
CoreNotifySignalList (&gEfiEventMemoryMapChangeGuid); | |
// | |
// Look for adjoining memory descriptor | |
// | |
// Two memory descriptors can only be merged if they have the same Type | |
// and the same Attribute | |
// | |
Link = gMemoryMap.ForwardLink; | |
while (Link != &gMemoryMap) { | |
Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
Link = Link->ForwardLink; | |
if (Entry->Type != Type) { | |
continue; | |
} | |
if (Entry->Attribute != Attribute) { | |
continue; | |
} | |
if (Entry->End + 1 == Start) { | |
Start = Entry->Start; | |
RemoveMemoryMapEntry (Entry); | |
} else if (Entry->Start == End + 1) { | |
End = Entry->End; | |
RemoveMemoryMapEntry (Entry); | |
} | |
} | |
// | |
// Add descriptor | |
// | |
mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE; | |
mMapStack[mMapDepth].FromPages = FALSE; | |
mMapStack[mMapDepth].Type = Type; | |
mMapStack[mMapDepth].Start = Start; | |
mMapStack[mMapDepth].End = End; | |
mMapStack[mMapDepth].VirtualStart = 0; | |
mMapStack[mMapDepth].Attribute = Attribute; | |
InsertTailList (&gMemoryMap, &mMapStack[mMapDepth].Link); | |
mMapDepth += 1; | |
ASSERT (mMapDepth < MAX_MAP_DEPTH); | |
return; | |
} | |
/** | |
Internal function. Deque a descriptor entry from the mFreeMemoryMapEntryList. | |
If the list is emtry, then allocate a new page to refuel the list. | |
Please Note this algorithm to allocate the memory map descriptor has a property | |
that the memory allocated for memory entries always grows, and will never really be freed | |
For example, if the current boot uses 2000 memory map entries at the maximum point, but | |
ends up with only 50 at the time the OS is booted, then the memory associated with the 1950 | |
memory map entries is still allocated from EfiBootServicesMemory. | |
@return The Memory map descriptor dequed from the mFreeMemoryMapEntryList | |
**/ | |
MEMORY_MAP * | |
AllocateMemoryMapEntry ( | |
VOID | |
) | |
{ | |
MEMORY_MAP *FreeDescriptorEntries; | |
MEMORY_MAP *Entry; | |
UINTN Index; | |
if (IsListEmpty (&mFreeMemoryMapEntryList)) { | |
// | |
// The list is empty, to allocate one page to refuel the list | |
// | |
FreeDescriptorEntries = CoreAllocatePoolPages ( | |
EfiBootServicesData, | |
EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION_GRANULARITY), | |
DEFAULT_PAGE_ALLOCATION_GRANULARITY, | |
FALSE | |
); | |
if (FreeDescriptorEntries != NULL) { | |
// | |
// Enque the free memmory map entries into the list | |
// | |
for (Index = 0; Index < DEFAULT_PAGE_ALLOCATION_GRANULARITY / sizeof (MEMORY_MAP); Index++) { | |
FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE; | |
InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link); | |
} | |
} else { | |
return NULL; | |
} | |
} | |
// | |
// dequeue the first descriptor from the list | |
// | |
Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
RemoveEntryList (&Entry->Link); | |
return Entry; | |
} | |
/** | |
Internal function. Moves any memory descriptors that are on the | |
temporary descriptor stack to heap. | |
**/ | |
VOID | |
CoreFreeMemoryMapStack ( | |
VOID | |
) | |
{ | |
MEMORY_MAP *Entry; | |
MEMORY_MAP *Entry2; | |
LIST_ENTRY *Link2; | |
ASSERT_LOCKED (&gMemoryLock); | |
// | |
// If already freeing the map stack, then return | |
// | |
if (mFreeMapStack != 0) { | |
return; | |
} | |
// | |
// Move the temporary memory descriptor stack into pool | |
// | |
mFreeMapStack += 1; | |
while (mMapDepth != 0) { | |
// | |
// Deque an memory map entry from mFreeMemoryMapEntryList | |
// | |
Entry = AllocateMemoryMapEntry (); | |
ASSERT (Entry); | |
// | |
// Update to proper entry | |
// | |
mMapDepth -= 1; | |
if (mMapStack[mMapDepth].Link.ForwardLink != NULL) { | |
// | |
// Move this entry to general memory | |
// | |
RemoveEntryList (&mMapStack[mMapDepth].Link); | |
mMapStack[mMapDepth].Link.ForwardLink = NULL; | |
CopyMem (Entry, &mMapStack[mMapDepth], sizeof (MEMORY_MAP)); | |
Entry->FromPages = TRUE; | |
// | |
// Find insertion location | |
// | |
for (Link2 = gMemoryMap.ForwardLink; Link2 != &gMemoryMap; Link2 = Link2->ForwardLink) { | |
Entry2 = CR (Link2, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
if (Entry2->FromPages && (Entry2->Start > Entry->Start)) { | |
break; | |
} | |
} | |
InsertTailList (Link2, &Entry->Link); | |
} else { | |
// | |
// This item of mMapStack[mMapDepth] has already been dequeued from gMemoryMap list, | |
// so here no need to move it to memory. | |
// | |
InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link); | |
} | |
} | |
mFreeMapStack -= 1; | |
} | |
/** | |
Find untested but initialized memory regions in GCD map and convert them to be DXE allocatable. | |
**/ | |
BOOLEAN | |
PromoteMemoryResource ( | |
VOID | |
) | |
{ | |
LIST_ENTRY *Link; | |
EFI_GCD_MAP_ENTRY *Entry; | |
BOOLEAN Promoted; | |
EFI_PHYSICAL_ADDRESS StartAddress; | |
EFI_PHYSICAL_ADDRESS EndAddress; | |
EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor; | |
DEBUG ((DEBUG_PAGE, "Promote the memory resource\n")); | |
CoreAcquireGcdMemoryLock (); | |
Promoted = FALSE; | |
Link = mGcdMemorySpaceMap.ForwardLink; | |
while (Link != &mGcdMemorySpaceMap) { | |
Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE); | |
if ((Entry->GcdMemoryType == EfiGcdMemoryTypeReserved) && | |
(Entry->EndAddress < MAX_ALLOC_ADDRESS) && | |
((Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) == | |
(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED))) | |
{ | |
// | |
// Update the GCD map | |
// | |
if ((Entry->Capabilities & EFI_MEMORY_MORE_RELIABLE) == EFI_MEMORY_MORE_RELIABLE) { | |
Entry->GcdMemoryType = EfiGcdMemoryTypeMoreReliable; | |
} else { | |
Entry->GcdMemoryType = EfiGcdMemoryTypeSystemMemory; | |
} | |
Entry->Capabilities |= EFI_MEMORY_TESTED; | |
Entry->ImageHandle = gDxeCoreImageHandle; | |
Entry->DeviceHandle = NULL; | |
// | |
// Add to allocable system memory resource | |
// | |
CoreAddRange ( | |
EfiConventionalMemory, | |
Entry->BaseAddress, | |
Entry->EndAddress, | |
Entry->Capabilities & ~(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME) | |
); | |
CoreFreeMemoryMapStack (); | |
Promoted = TRUE; | |
} | |
Link = Link->ForwardLink; | |
} | |
CoreReleaseGcdMemoryLock (); | |
if (!Promoted) { | |
// | |
// If freed-memory guard is enabled, we could promote pages from | |
// guarded free pages. | |
// | |
Promoted = PromoteGuardedFreePages (&StartAddress, &EndAddress); | |
if (Promoted) { | |
if (!EFI_ERROR (CoreGetMemorySpaceDescriptor (StartAddress, &Descriptor))) { | |
CoreAddRange ( | |
EfiConventionalMemory, | |
StartAddress, | |
EndAddress, | |
Descriptor.Capabilities & ~(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | | |
EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME) | |
); | |
} | |
} | |
} | |
return Promoted; | |
} | |
/** | |
This function try to allocate Runtime code & Boot time code memory range. If LMFA enabled, 2 patchable PCD | |
PcdLoadFixAddressRuntimeCodePageNumber & PcdLoadFixAddressBootTimeCodePageNumber which are set by tools will record the | |
size of boot time and runtime code. | |
**/ | |
VOID | |
CoreLoadingFixedAddressHook ( | |
VOID | |
) | |
{ | |
UINT32 RuntimeCodePageNumber; | |
UINT32 BootTimeCodePageNumber; | |
EFI_PHYSICAL_ADDRESS RuntimeCodeBase; | |
EFI_PHYSICAL_ADDRESS BootTimeCodeBase; | |
EFI_STATUS Status; | |
// | |
// Make sure these 2 areas are not initialzied. | |
// | |
if (!gLoadFixedAddressCodeMemoryReady) { | |
RuntimeCodePageNumber = PcdGet32 (PcdLoadFixAddressRuntimeCodePageNumber); | |
BootTimeCodePageNumber = PcdGet32 (PcdLoadFixAddressBootTimeCodePageNumber); | |
RuntimeCodeBase = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressConfigurationTable.DxeCodeTopAddress - EFI_PAGES_TO_SIZE (RuntimeCodePageNumber)); | |
BootTimeCodeBase = (EFI_PHYSICAL_ADDRESS)(RuntimeCodeBase - EFI_PAGES_TO_SIZE (BootTimeCodePageNumber)); | |
// | |
// Try to allocate runtime memory. | |
// | |
Status = CoreAllocatePages ( | |
AllocateAddress, | |
EfiRuntimeServicesCode, | |
RuntimeCodePageNumber, | |
&RuntimeCodeBase | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// Runtime memory allocation failed | |
// | |
return; | |
} | |
// | |
// Try to allocate boot memory. | |
// | |
Status = CoreAllocatePages ( | |
AllocateAddress, | |
EfiBootServicesCode, | |
BootTimeCodePageNumber, | |
&BootTimeCodeBase | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// boot memory allocation failed. Free Runtime code range and will try the allocation again when | |
// new memory range is installed. | |
// | |
CoreFreePages ( | |
RuntimeCodeBase, | |
RuntimeCodePageNumber | |
); | |
return; | |
} | |
gLoadFixedAddressCodeMemoryReady = TRUE; | |
} | |
return; | |
} | |
/** | |
Sets the preferred memory range to use for the Memory Type Information bins. | |
This service must be called before fist call to CoreAddMemoryDescriptor(). | |
If the location of the Memory Type Information bins has already been | |
established or the size of the range provides is smaller than all the | |
Memory Type Information bins, then the range provides is not used. | |
@param Start The start address of the Memory Type Information range. | |
@param Length The size, in bytes, of the Memory Type Information range. | |
**/ | |
VOID | |
CoreSetMemoryTypeInformationRange ( | |
IN EFI_PHYSICAL_ADDRESS Start, | |
IN UINT64 Length | |
) | |
{ | |
EFI_PHYSICAL_ADDRESS Top; | |
EFI_MEMORY_TYPE Type; | |
UINTN Index; | |
UINTN Size; | |
// | |
// Return if Memory Type Information bin locations have already been set | |
// | |
if (mMemoryTypeInformationInitialized) { | |
DEBUG ((DEBUG_ERROR, "%a: Ignored. Bins already set.\n", __func__)); | |
return; | |
} | |
// | |
// Return if size of the Memory Type Information bins is greater than Length | |
// | |
Size = 0; | |
for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { | |
// | |
// Make sure the memory type in the gMemoryTypeInformation[] array is valid | |
// | |
Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); | |
if ((UINT32)Type > EfiMaxMemoryType) { | |
continue; | |
} | |
Size += EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages); | |
} | |
if (Size > Length) { | |
return; | |
} | |
// | |
// Loop through each memory type in the order specified by the | |
// gMemoryTypeInformation[] array | |
// | |
Top = Start + Length; | |
for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { | |
// | |
// Make sure the memory type in the gMemoryTypeInformation[] array is valid | |
// | |
Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); | |
if ((UINT32)Type > EfiMaxMemoryType) { | |
continue; | |
} | |
if (gMemoryTypeInformation[Index].NumberOfPages != 0) { | |
mMemoryTypeStatistics[Type].MaximumAddress = Top - 1; | |
Top -= EFI_PAGES_TO_SIZE (gMemoryTypeInformation[Index].NumberOfPages); | |
mMemoryTypeStatistics[Type].BaseAddress = Top; | |
// | |
// If the current base address is the lowest address so far, then update | |
// the default maximum address | |
// | |
if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) { | |
mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1; | |
} | |
mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; | |
gMemoryTypeInformation[Index].NumberOfPages = 0; | |
} | |
} | |
// | |
// If the number of pages reserved for a memory type is 0, then all | |
// allocations for that type should be in the default range. | |
// | |
for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { | |
for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { | |
if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) { | |
mMemoryTypeStatistics[Type].InformationIndex = Index; | |
} | |
} | |
mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0; | |
if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { | |
mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress; | |
} | |
} | |
mMemoryTypeInformationInitialized = TRUE; | |
} | |
/** | |
Called to initialize the memory map and add descriptors to | |
the current descriptor list. | |
The first descriptor that is added must be general usable | |
memory as the addition allocates heap. | |
@param Type The type of memory to add | |
@param Start The starting address in the memory range Must be | |
page aligned | |
@param NumberOfPages The number of pages in the range | |
@param Attribute Attributes of the memory to add | |
@return None. The range is added to the memory map | |
**/ | |
VOID | |
CoreAddMemoryDescriptor ( | |
IN EFI_MEMORY_TYPE Type, | |
IN EFI_PHYSICAL_ADDRESS Start, | |
IN UINT64 NumberOfPages, | |
IN UINT64 Attribute | |
) | |
{ | |
EFI_PHYSICAL_ADDRESS End; | |
EFI_STATUS Status; | |
UINTN Index; | |
UINTN FreeIndex; | |
if ((Start & EFI_PAGE_MASK) != 0) { | |
return; | |
} | |
if ((Type >= EfiMaxMemoryType) && (Type < MEMORY_TYPE_OEM_RESERVED_MIN)) { | |
return; | |
} | |
CoreAcquireMemoryLock (); | |
End = Start + LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT) - 1; | |
CoreAddRange (Type, Start, End, Attribute); | |
CoreFreeMemoryMapStack (); | |
CoreReleaseMemoryLock (); | |
ApplyMemoryProtectionPolicy ( | |
EfiMaxMemoryType, | |
Type, | |
Start, | |
LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT) | |
); | |
// | |
// If Loading Module At Fixed Address feature is enabled. try to allocate memory with Runtime code & Boot time code type | |
// | |
if (PcdGet64 (PcdLoadModuleAtFixAddressEnable) != 0) { | |
CoreLoadingFixedAddressHook (); | |
} | |
// | |
// Check to see if the statistics for the different memory types have already been established | |
// | |
if (mMemoryTypeInformationInitialized) { | |
return; | |
} | |
// | |
// Loop through each memory type in the order specified by the gMemoryTypeInformation[] array | |
// | |
for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { | |
// | |
// Make sure the memory type in the gMemoryTypeInformation[] array is valid | |
// | |
Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); | |
if ((UINT32)Type > EfiMaxMemoryType) { | |
continue; | |
} | |
if (gMemoryTypeInformation[Index].NumberOfPages != 0) { | |
// | |
// Allocate pages for the current memory type from the top of available memory | |
// | |
Status = CoreAllocatePages ( | |
AllocateAnyPages, | |
Type, | |
gMemoryTypeInformation[Index].NumberOfPages, | |
&mMemoryTypeStatistics[Type].BaseAddress | |
); | |
if (EFI_ERROR (Status)) { | |
// | |
// If an error occurs allocating the pages for the current memory type, then | |
// free all the pages allocates for the previous memory types and return. This | |
// operation with be retied when/if more memory is added to the system | |
// | |
for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) { | |
// | |
// Make sure the memory type in the gMemoryTypeInformation[] array is valid | |
// | |
Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[FreeIndex].Type); | |
if ((UINT32)Type > EfiMaxMemoryType) { | |
continue; | |
} | |
if (gMemoryTypeInformation[FreeIndex].NumberOfPages != 0) { | |
CoreFreePages ( | |
mMemoryTypeStatistics[Type].BaseAddress, | |
gMemoryTypeInformation[FreeIndex].NumberOfPages | |
); | |
mMemoryTypeStatistics[Type].BaseAddress = 0; | |
mMemoryTypeStatistics[Type].MaximumAddress = MAX_ALLOC_ADDRESS; | |
} | |
} | |
return; | |
} | |
// | |
// Compute the address at the top of the current statistics | |
// | |
mMemoryTypeStatistics[Type].MaximumAddress = | |
mMemoryTypeStatistics[Type].BaseAddress + | |
LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1; | |
// | |
// If the current base address is the lowest address so far, then update the default | |
// maximum address | |
// | |
if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) { | |
mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1; | |
} | |
} | |
} | |
// | |
// There was enough system memory for all the the memory types were allocated. So, | |
// those memory areas can be freed for future allocations, and all future memory | |
// allocations can occur within their respective bins | |
// | |
for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { | |
// | |
// Make sure the memory type in the gMemoryTypeInformation[] array is valid | |
// | |
Type = (EFI_MEMORY_TYPE)(gMemoryTypeInformation[Index].Type); | |
if ((UINT32)Type > EfiMaxMemoryType) { | |
continue; | |
} | |
if (gMemoryTypeInformation[Index].NumberOfPages != 0) { | |
CoreFreePages ( | |
mMemoryTypeStatistics[Type].BaseAddress, | |
gMemoryTypeInformation[Index].NumberOfPages | |
); | |
mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages; | |
gMemoryTypeInformation[Index].NumberOfPages = 0; | |
} | |
} | |
// | |
// If the number of pages reserved for a memory type is 0, then all allocations for that type | |
// should be in the default range. | |
// | |
for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { | |
for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) { | |
if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) { | |
mMemoryTypeStatistics[Type].InformationIndex = Index; | |
} | |
} | |
mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0; | |
if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ALLOC_ADDRESS) { | |
mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress; | |
} | |
} | |
mMemoryTypeInformationInitialized = TRUE; | |
} | |
/** | |
Internal function. Converts a memory range to the specified type or attributes. | |
The range must exist in the memory map. Either ChangingType or | |
ChangingAttributes must be set, but not both. | |
@param Start The first address of the range Must be page | |
aligned | |
@param NumberOfPages The number of pages to convert | |
@param ChangingType Boolean indicating that type value should be changed | |
@param NewType The new type for the memory range | |
@param ChangingAttributes Boolean indicating that attributes value should be changed | |
@param NewAttributes The new attributes for the memory range | |
@retval EFI_INVALID_PARAMETER Invalid parameter | |
@retval EFI_NOT_FOUND Could not find a descriptor cover the specified | |
range or convertion not allowed. | |
@retval EFI_SUCCESS Successfully converts the memory range to the | |
specified type. | |
**/ | |
EFI_STATUS | |
CoreConvertPagesEx ( | |
IN UINT64 Start, | |
IN UINT64 NumberOfPages, | |
IN BOOLEAN ChangingType, | |
IN EFI_MEMORY_TYPE NewType, | |
IN BOOLEAN ChangingAttributes, | |
IN UINT64 NewAttributes | |
) | |
{ | |
UINT64 NumberOfBytes; | |
UINT64 End; | |
UINT64 RangeEnd; | |
UINT64 Attribute; | |
EFI_MEMORY_TYPE MemType; | |
LIST_ENTRY *Link; | |
MEMORY_MAP *Entry; | |
Entry = NULL; | |
NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT); | |
End = Start + NumberOfBytes - 1; | |
ASSERT (NumberOfPages); | |
ASSERT ((Start & EFI_PAGE_MASK) == 0); | |
ASSERT (End > Start); | |
ASSERT_LOCKED (&gMemoryLock); | |
ASSERT ((ChangingType == FALSE) || (ChangingAttributes == FALSE)); | |
if ((NumberOfPages == 0) || ((Start & EFI_PAGE_MASK) != 0) || (Start >= End)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Convert the entire range | |
// | |
while (Start < End) { | |
// | |
// Find the entry that the covers the range | |
// | |
for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { | |
Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
if ((Entry->Start <= Start) && (Entry->End > Start)) { | |
break; | |
} | |
} | |
if (Link == &gMemoryMap) { | |
DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: failed to find range %lx - %lx\n", Start, End)); | |
return EFI_NOT_FOUND; | |
} | |
// | |
// If we are converting the type of the range from EfiConventionalMemory to | |
// another type, we have to ensure that the entire range is covered by a | |
// single entry. | |
// | |
if (ChangingType && (NewType != EfiConventionalMemory)) { | |
if (Entry->End < End) { | |
DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: range %lx - %lx covers multiple entries\n", Start, End)); | |
return EFI_NOT_FOUND; | |
} | |
} | |
// | |
// Convert range to the end, or to the end of the descriptor | |
// if that's all we've got | |
// | |
RangeEnd = End; | |
ASSERT (Entry != NULL); | |
if (Entry->End < End) { | |
RangeEnd = Entry->End; | |
} | |
if (ChangingType) { | |
DEBUG ((DEBUG_PAGE, "ConvertRange: %lx-%lx to type %d\n", Start, RangeEnd, NewType)); | |
} | |
if (ChangingAttributes) { | |
DEBUG ((DEBUG_PAGE, "ConvertRange: %lx-%lx to attr %lx\n", Start, RangeEnd, NewAttributes)); | |
} | |
if (ChangingType) { | |
// | |
// Debug code - verify conversion is allowed | |
// | |
if (!((NewType == EfiConventionalMemory) ? 1 : 0) ^ ((Entry->Type == EfiConventionalMemory) ? 1 : 0)) { | |
DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: Incompatible memory types, ")); | |
if (Entry->Type == EfiConventionalMemory) { | |
DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "the pages to free have been freed\n")); | |
} else { | |
DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "the pages to allocate have been allocated\n")); | |
} | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Update counters for the number of pages allocated to each memory type | |
// | |
if ((UINT32)Entry->Type < EfiMaxMemoryType) { | |
if (((Start >= mMemoryTypeStatistics[Entry->Type].BaseAddress) && (Start <= mMemoryTypeStatistics[Entry->Type].MaximumAddress)) || | |
((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress))) | |
{ | |
if (NumberOfPages > mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages) { | |
mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages = 0; | |
} else { | |
mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages -= NumberOfPages; | |
} | |
} | |
} | |
if ((UINT32)NewType < EfiMaxMemoryType) { | |
if (((Start >= mMemoryTypeStatistics[NewType].BaseAddress) && (Start <= mMemoryTypeStatistics[NewType].MaximumAddress)) || | |
((Start >= mDefaultBaseAddress) && (Start <= mDefaultMaximumAddress))) | |
{ | |
mMemoryTypeStatistics[NewType].CurrentNumberOfPages += NumberOfPages; | |
if (mMemoryTypeStatistics[NewType].CurrentNumberOfPages > gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages) { | |
gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages = (UINT32)mMemoryTypeStatistics[NewType].CurrentNumberOfPages; | |
} | |
} | |
} | |
} | |
// | |
// Pull range out of descriptor | |
// | |
if (Entry->Start == Start) { | |
// | |
// Clip start | |
// | |
Entry->Start = RangeEnd + 1; | |
} else if (Entry->End == RangeEnd) { | |
// | |
// Clip end | |
// | |
Entry->End = Start - 1; | |
} else { | |
// | |
// Pull it out of the center, clip current | |
// | |
// | |
// Add a new one | |
// | |
mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE; | |
mMapStack[mMapDepth].FromPages = FALSE; | |
mMapStack[mMapDepth].Type = Entry->Type; | |
mMapStack[mMapDepth].Start = RangeEnd+1; | |
mMapStack[mMapDepth].End = Entry->End; | |
// | |
// Inherit Attribute from the Memory Descriptor that is being clipped | |
// | |
mMapStack[mMapDepth].Attribute = Entry->Attribute; | |
Entry->End = Start - 1; | |
ASSERT (Entry->Start < Entry->End); | |
Entry = &mMapStack[mMapDepth]; | |
InsertTailList (&gMemoryMap, &Entry->Link); | |
mMapDepth += 1; | |
ASSERT (mMapDepth < MAX_MAP_DEPTH); | |
} | |
// | |
// The new range inherits the same Attribute as the Entry | |
// it is being cut out of unless attributes are being changed | |
// | |
if (ChangingType) { | |
Attribute = Entry->Attribute; | |
MemType = NewType; | |
} else { | |
Attribute = NewAttributes; | |
MemType = Entry->Type; | |
} | |
// | |
// If the descriptor is empty, then remove it from the map | |
// | |
if (Entry->Start == Entry->End + 1) { | |
RemoveMemoryMapEntry (Entry); | |
Entry = NULL; | |
} | |
// | |
// Add our new range in. Don't do this for freed pages if freed-memory | |
// guard is enabled. | |
// | |
if (!IsHeapGuardEnabled (GUARD_HEAP_TYPE_FREED) || | |
!ChangingType || | |
(MemType != EfiConventionalMemory)) | |
{ | |
CoreAddRange (MemType, Start, RangeEnd, Attribute); | |
} | |
if (ChangingType && (MemType == EfiConventionalMemory)) { | |
// | |
// Avoid calling DEBUG_CLEAR_MEMORY() for an address of 0 because this | |
// macro will ASSERT() if address is 0. Instead, CoreAddRange() guarantees | |
// that the page starting at address 0 is always filled with zeros. | |
// | |
if (Start == 0) { | |
if (RangeEnd > EFI_PAGE_SIZE) { | |
DEBUG_CLEAR_MEMORY ((VOID *)(UINTN)EFI_PAGE_SIZE, (UINTN)(RangeEnd - EFI_PAGE_SIZE + 1)); | |
} | |
} else { | |
DEBUG_CLEAR_MEMORY ((VOID *)(UINTN)Start, (UINTN)(RangeEnd - Start + 1)); | |
} | |
} | |
// | |
// Move any map descriptor stack to general pool | |
// | |
CoreFreeMemoryMapStack (); | |
// | |
// Bump the starting address, and convert the next range | |
// | |
Start = RangeEnd + 1; | |
} | |
// | |
// Converted the whole range, done | |
// | |
return EFI_SUCCESS; | |
} | |
/** | |
Internal function. Converts a memory range to the specified type. | |
The range must exist in the memory map. | |
@param Start The first address of the range Must be page | |
aligned | |
@param NumberOfPages The number of pages to convert | |
@param NewType The new type for the memory range | |
@retval EFI_INVALID_PARAMETER Invalid parameter | |
@retval EFI_NOT_FOUND Could not find a descriptor cover the specified | |
range or convertion not allowed. | |
@retval EFI_SUCCESS Successfully converts the memory range to the | |
specified type. | |
**/ | |
EFI_STATUS | |
CoreConvertPages ( | |
IN UINT64 Start, | |
IN UINT64 NumberOfPages, | |
IN EFI_MEMORY_TYPE NewType | |
) | |
{ | |
return CoreConvertPagesEx (Start, NumberOfPages, TRUE, NewType, FALSE, 0); | |
} | |
/** | |
Internal function. Converts a memory range to use new attributes. | |
@param Start The first address of the range Must be page | |
aligned | |
@param NumberOfPages The number of pages to convert | |
@param NewAttributes The new attributes value for the range. | |
**/ | |
VOID | |
CoreUpdateMemoryAttributes ( | |
IN EFI_PHYSICAL_ADDRESS Start, | |
IN UINT64 NumberOfPages, | |
IN UINT64 NewAttributes | |
) | |
{ | |
CoreAcquireMemoryLock (); | |
// | |
// Update the attributes to the new value | |
// | |
CoreConvertPagesEx (Start, NumberOfPages, FALSE, (EFI_MEMORY_TYPE)0, TRUE, NewAttributes); | |
CoreReleaseMemoryLock (); | |
} | |
/** | |
Internal function. Finds a consecutive free page range below | |
the requested address. | |
@param MaxAddress The address that the range must be below | |
@param MinAddress The address that the range must be above | |
@param NumberOfPages Number of pages needed | |
@param NewType The type of memory the range is going to be | |
turned into | |
@param Alignment Bits to align with | |
@param NeedGuard Flag to indicate Guard page is needed or not | |
@return The base address of the range, or 0 if the range was not found | |
**/ | |
UINT64 | |
CoreFindFreePagesI ( | |
IN UINT64 MaxAddress, | |
IN UINT64 MinAddress, | |
IN UINT64 NumberOfPages, | |
IN EFI_MEMORY_TYPE NewType, | |
IN UINTN Alignment, | |
IN BOOLEAN NeedGuard | |
) | |
{ | |
UINT64 NumberOfBytes; | |
UINT64 Target; | |
UINT64 DescStart; | |
UINT64 DescEnd; | |
UINT64 DescNumberOfBytes; | |
LIST_ENTRY *Link; | |
MEMORY_MAP *Entry; | |
if ((MaxAddress < EFI_PAGE_MASK) || (NumberOfPages == 0)) { | |
return 0; | |
} | |
if ((MaxAddress & EFI_PAGE_MASK) != EFI_PAGE_MASK) { | |
// | |
// If MaxAddress is not aligned to the end of a page | |
// | |
// | |
// Change MaxAddress to be 1 page lower | |
// | |
MaxAddress -= (EFI_PAGE_MASK + 1); | |
// | |
// Set MaxAddress to a page boundary | |
// | |
MaxAddress &= ~(UINT64)EFI_PAGE_MASK; | |
// | |
// Set MaxAddress to end of the page | |
// | |
MaxAddress |= EFI_PAGE_MASK; | |
} | |
NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT); | |
Target = 0; | |
for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { | |
Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
// | |
// If it's not a free entry, don't bother with it | |
// | |
if (Entry->Type != EfiConventionalMemory) { | |
continue; | |
} | |
DescStart = Entry->Start; | |
DescEnd = Entry->End; | |
// | |
// If desc is past max allowed address or below min allowed address, skip it | |
// | |
if ((DescStart >= MaxAddress) || (DescEnd < MinAddress)) { | |
continue; | |
} | |
// | |
// If desc ends past max allowed address, clip the end | |
// | |
if (DescEnd >= MaxAddress) { | |
DescEnd = MaxAddress; | |
} | |
DescEnd = ((DescEnd + 1) & (~((UINT64)Alignment - 1))) - 1; | |
// Skip if DescEnd is less than DescStart after alignment clipping | |
if (DescEnd < DescStart) { | |
continue; | |
} | |
// | |
// Compute the number of bytes we can used from this | |
// descriptor, and see it's enough to satisfy the request | |
// | |
DescNumberOfBytes = DescEnd - DescStart + 1; | |
if (DescNumberOfBytes >= NumberOfBytes) { | |
// | |
// If the start of the allocated range is below the min address allowed, skip it | |
// | |
if ((DescEnd - NumberOfBytes + 1) < MinAddress) { | |
continue; | |
} | |
// | |
// If this is the best match so far remember it | |
// | |
if (DescEnd > Target) { | |
if (NeedGuard) { | |
DescEnd = AdjustMemoryS ( | |
DescEnd + 1 - DescNumberOfBytes, | |
DescNumberOfBytes, | |
NumberOfBytes | |
); | |
if (DescEnd == 0) { | |
continue; | |
} | |
} | |
Target = DescEnd; | |
} | |
} | |
} | |
// | |
// If this is a grow down, adjust target to be the allocation base | |
// | |
Target -= NumberOfBytes - 1; | |
// | |
// If we didn't find a match, return 0 | |
// | |
if ((Target & EFI_PAGE_MASK) != 0) { | |
return 0; | |
} | |
return Target; | |
} | |
/** | |
Internal function. Finds a consecutive free page range below | |
the requested address | |
@param MaxAddress The address that the range must be below | |
@param NoPages Number of pages needed | |
@param NewType The type of memory the range is going to be | |
turned into | |
@param Alignment Bits to align with | |
@param NeedGuard Flag to indicate Guard page is needed or not | |
@return The base address of the range, or 0 if the range was not found. | |
**/ | |
UINT64 | |
FindFreePages ( | |
IN UINT64 MaxAddress, | |
IN UINT64 NoPages, | |
IN EFI_MEMORY_TYPE NewType, | |
IN UINTN Alignment, | |
IN BOOLEAN NeedGuard | |
) | |
{ | |
UINT64 Start; | |
// | |
// Attempt to find free pages in the preferred bin based on the requested memory type | |
// | |
if (((UINT32)NewType < EfiMaxMemoryType) && (MaxAddress >= mMemoryTypeStatistics[NewType].MaximumAddress)) { | |
Start = CoreFindFreePagesI ( | |
mMemoryTypeStatistics[NewType].MaximumAddress, | |
mMemoryTypeStatistics[NewType].BaseAddress, | |
NoPages, | |
NewType, | |
Alignment, | |
NeedGuard | |
); | |
if (Start != 0) { | |
return Start; | |
} | |
} | |
// | |
// Attempt to find free pages in the default allocation bin | |
// | |
if (MaxAddress >= mDefaultMaximumAddress) { | |
Start = CoreFindFreePagesI ( | |
mDefaultMaximumAddress, | |
0, | |
NoPages, | |
NewType, | |
Alignment, | |
NeedGuard | |
); | |
if (Start != 0) { | |
if (Start < mDefaultBaseAddress) { | |
mDefaultBaseAddress = NeedGuard ? Start - EFI_PAGE_SIZE : Start; | |
} | |
return Start; | |
} | |
} | |
// | |
// The allocation did not succeed in any of the prefered bins even after | |
// promoting resources. Attempt to find free pages anywhere is the requested | |
// address range. If this allocation fails, then there are not enough | |
// resources anywhere to satisfy the request. | |
// | |
Start = CoreFindFreePagesI ( | |
MaxAddress, | |
0, | |
NoPages, | |
NewType, | |
Alignment, | |
NeedGuard | |
); | |
if (Start != 0) { | |
return Start; | |
} | |
// | |
// If allocations from the preferred bins fail, then attempt to promote memory resources. | |
// | |
if (!PromoteMemoryResource ()) { | |
return 0; | |
} | |
// | |
// If any memory resources were promoted, then re-attempt the allocation | |
// | |
return FindFreePages (MaxAddress, NoPages, NewType, Alignment, NeedGuard); | |
} | |
/** | |
Allocates pages from the memory map. | |
@param Type The type of allocation to perform | |
@param MemoryType The type of memory to turn the allocated pages | |
into | |
@param NumberOfPages The number of pages to allocate | |
@param Memory A pointer to receive the base allocated memory | |
address | |
@param NeedGuard Flag to indicate Guard page is needed or not | |
@return Status. On success, Memory is filled in with the base address allocated | |
@retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in | |
spec. | |
@retval EFI_NOT_FOUND Could not allocate pages match the requirement. | |
@retval EFI_OUT_OF_RESOURCES No enough pages to allocate. | |
@retval EFI_SUCCESS Pages successfully allocated. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CoreInternalAllocatePages ( | |
IN EFI_ALLOCATE_TYPE Type, | |
IN EFI_MEMORY_TYPE MemoryType, | |
IN UINTN NumberOfPages, | |
IN OUT EFI_PHYSICAL_ADDRESS *Memory, | |
IN BOOLEAN NeedGuard | |
) | |
{ | |
EFI_STATUS Status; | |
UINT64 Start; | |
UINT64 NumberOfBytes; | |
UINT64 End; | |
UINT64 MaxAddress; | |
UINTN Alignment; | |
EFI_MEMORY_TYPE CheckType; | |
if ((UINT32)Type >= MaxAllocateType) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (((MemoryType >= EfiMaxMemoryType) && (MemoryType < MEMORY_TYPE_OEM_RESERVED_MIN)) || | |
(MemoryType == EfiConventionalMemory) || (MemoryType == EfiPersistentMemory) || (MemoryType == EfiUnacceptedMemoryType)) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
if (Memory == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; | |
if ((MemoryType == EfiReservedMemoryType) || | |
(MemoryType == EfiACPIMemoryNVS) || | |
(MemoryType == EfiRuntimeServicesCode) || | |
(MemoryType == EfiRuntimeServicesData)) | |
{ | |
Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; | |
} | |
// | |
// The heap guard system does not support non-EFI_PAGE_SIZE alignments. | |
// Architectures that require larger RUNTIME_PAGE_ALLOCATION_GRANULARITY | |
// will have the runtime memory regions unguarded. OSes do not | |
// map guard pages anyway, so this is a minimal loss. Not guarding prevents | |
// alignment mismatches | |
// | |
if (Alignment != EFI_PAGE_SIZE) { | |
NeedGuard = FALSE; | |
} | |
if (Type == AllocateAddress) { | |
if ((*Memory & (Alignment - 1)) != 0) { | |
return EFI_NOT_FOUND; | |
} | |
} | |
NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1; | |
NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1); | |
// | |
// If this is for below a particular address, then | |
// | |
Start = *Memory; | |
// | |
// The max address is the max natively addressable address for the processor | |
// | |
MaxAddress = MAX_ALLOC_ADDRESS; | |
// | |
// Check for Type AllocateAddress, | |
// if NumberOfPages is 0 or | |
// if (NumberOfPages << EFI_PAGE_SHIFT) is above MAX_ALLOC_ADDRESS or | |
// if (Start + NumberOfBytes) rolls over 0 or | |
// if Start is above MAX_ALLOC_ADDRESS or | |
// if End is above MAX_ALLOC_ADDRESS, | |
// if Start..End overlaps any tracked MemoryTypeStatistics range | |
// return EFI_NOT_FOUND. | |
// | |
if (Type == AllocateAddress) { | |
if ((NumberOfPages == 0) || | |
(NumberOfPages > RShiftU64 (MaxAddress, EFI_PAGE_SHIFT))) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT); | |
End = Start + NumberOfBytes - 1; | |
if ((Start >= End) || | |
(Start > MaxAddress) || | |
(End > MaxAddress)) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
// | |
// A driver is allowed to call AllocatePages using an AllocateAddress type. This type of | |
// AllocatePage request the exact physical address if it is not used. The existing code | |
// will allow this request even in 'special' pages. The problem with this is that the | |
// reason to have 'special' pages for OS hibernate/resume is defeated as memory is | |
// fragmented. | |
// | |
for (CheckType = (EFI_MEMORY_TYPE)0; CheckType < EfiMaxMemoryType; CheckType++) { | |
if ((MemoryType != CheckType) && | |
mMemoryTypeStatistics[CheckType].Special && | |
(mMemoryTypeStatistics[CheckType].NumberOfPages > 0)) | |
{ | |
if ((Start >= mMemoryTypeStatistics[CheckType].BaseAddress) && | |
(Start <= mMemoryTypeStatistics[CheckType].MaximumAddress)) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
if ((End >= mMemoryTypeStatistics[CheckType].BaseAddress) && | |
(End <= mMemoryTypeStatistics[CheckType].MaximumAddress)) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
if ((Start < mMemoryTypeStatistics[CheckType].BaseAddress) && | |
(End > mMemoryTypeStatistics[CheckType].MaximumAddress)) | |
{ | |
return EFI_NOT_FOUND; | |
} | |
} | |
} | |
} | |
if (Type == AllocateMaxAddress) { | |
MaxAddress = Start; | |
} | |
CoreAcquireMemoryLock (); | |
// | |
// If not a specific address, then find an address to allocate | |
// | |
if (Type != AllocateAddress) { | |
Start = FindFreePages ( | |
MaxAddress, | |
NumberOfPages, | |
MemoryType, | |
Alignment, | |
NeedGuard | |
); | |
if (Start == 0) { | |
Status = EFI_OUT_OF_RESOURCES; | |
goto Done; | |
} | |
} | |
// | |
// Convert pages from FreeMemory to the requested type | |
// | |
if (NeedGuard) { | |
Status = CoreConvertPagesWithGuard (Start, NumberOfPages, MemoryType); | |
} else { | |
Status = CoreConvertPages (Start, NumberOfPages, MemoryType); | |
} | |
if (EFI_ERROR (Status)) { | |
// | |
// If requested memory region is unavailable it may be untested memory | |
// Attempt to promote memory resources, then re-attempt the allocation | |
// | |
if (PromoteMemoryResource ()) { | |
if (NeedGuard) { | |
Status = CoreConvertPagesWithGuard (Start, NumberOfPages, MemoryType); | |
} else { | |
Status = CoreConvertPages (Start, NumberOfPages, MemoryType); | |
} | |
} | |
} | |
Done: | |
CoreReleaseMemoryLock (); | |
if (!EFI_ERROR (Status)) { | |
if (NeedGuard) { | |
SetGuardForMemory (Start, NumberOfPages); | |
} | |
*Memory = Start; | |
} | |
return Status; | |
} | |
/** | |
Allocates pages from the memory map. | |
@param Type The type of allocation to perform | |
@param MemoryType The type of memory to turn the allocated pages | |
into | |
@param NumberOfPages The number of pages to allocate | |
@param Memory A pointer to receive the base allocated memory | |
address | |
@return Status. On success, Memory is filled in with the base address allocated | |
@retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in | |
spec. | |
@retval EFI_NOT_FOUND Could not allocate pages match the requirement. | |
@retval EFI_OUT_OF_RESOURCES No enough pages to allocate. | |
@retval EFI_SUCCESS Pages successfully allocated. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CoreAllocatePages ( | |
IN EFI_ALLOCATE_TYPE Type, | |
IN EFI_MEMORY_TYPE MemoryType, | |
IN UINTN NumberOfPages, | |
OUT EFI_PHYSICAL_ADDRESS *Memory | |
) | |
{ | |
EFI_STATUS Status; | |
BOOLEAN NeedGuard; | |
NeedGuard = IsPageTypeToGuard (MemoryType, Type) && !mOnGuarding; | |
Status = CoreInternalAllocatePages ( | |
Type, | |
MemoryType, | |
NumberOfPages, | |
Memory, | |
NeedGuard | |
); | |
if (!EFI_ERROR (Status)) { | |
CoreUpdateProfile ( | |
(EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0), | |
MemoryProfileActionAllocatePages, | |
MemoryType, | |
EFI_PAGES_TO_SIZE (NumberOfPages), | |
(VOID *)(UINTN)*Memory, | |
NULL | |
); | |
InstallMemoryAttributesTableOnMemoryAllocation (MemoryType); | |
ApplyMemoryProtectionPolicy ( | |
EfiConventionalMemory, | |
MemoryType, | |
*Memory, | |
EFI_PAGES_TO_SIZE (NumberOfPages) | |
); | |
} | |
return Status; | |
} | |
/** | |
Frees previous allocated pages. | |
@param Memory Base address of memory being freed | |
@param NumberOfPages The number of pages to free | |
@param MemoryType Pointer to memory type | |
@retval EFI_NOT_FOUND Could not find the entry that covers the range | |
@retval EFI_INVALID_PARAMETER Address not aligned | |
@return EFI_SUCCESS -Pages successfully freed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CoreInternalFreePages ( | |
IN EFI_PHYSICAL_ADDRESS Memory, | |
IN UINTN NumberOfPages, | |
OUT EFI_MEMORY_TYPE *MemoryType OPTIONAL | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
MEMORY_MAP *Entry; | |
UINTN Alignment; | |
BOOLEAN IsGuarded; | |
// | |
// Free the range | |
// | |
CoreAcquireMemoryLock (); | |
// | |
// Find the entry that the covers the range | |
// | |
IsGuarded = FALSE; | |
Entry = NULL; | |
for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { | |
Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
if ((Entry->Start <= Memory) && (Entry->End > Memory)) { | |
break; | |
} | |
} | |
if (Link == &gMemoryMap) { | |
Status = EFI_NOT_FOUND; | |
goto Done; | |
} | |
if (Entry == NULL) { | |
ASSERT (Entry != NULL); | |
Status = EFI_NOT_FOUND; | |
goto Done; | |
} | |
Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY; | |
if ((Entry->Type == EfiReservedMemoryType) || | |
(Entry->Type == EfiACPIMemoryNVS) || | |
(Entry->Type == EfiRuntimeServicesCode) || | |
(Entry->Type == EfiRuntimeServicesData)) | |
{ | |
Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; | |
} | |
if ((Memory & (Alignment - 1)) != 0) { | |
Status = EFI_INVALID_PARAMETER; | |
goto Done; | |
} | |
NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1; | |
NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1); | |
if (MemoryType != NULL) { | |
*MemoryType = Entry->Type; | |
} | |
IsGuarded = IsPageTypeToGuard (Entry->Type, AllocateAnyPages) && | |
IsMemoryGuarded (Memory); | |
if (IsGuarded) { | |
Status = CoreConvertPagesWithGuard ( | |
Memory, | |
NumberOfPages, | |
EfiConventionalMemory | |
); | |
} else { | |
Status = CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory); | |
} | |
Done: | |
CoreReleaseMemoryLock (); | |
return Status; | |
} | |
/** | |
Frees previous allocated pages. | |
@param Memory Base address of memory being freed | |
@param NumberOfPages The number of pages to free | |
@retval EFI_NOT_FOUND Could not find the entry that covers the range | |
@retval EFI_INVALID_PARAMETER Address not aligned | |
@return EFI_SUCCESS -Pages successfully freed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CoreFreePages ( | |
IN EFI_PHYSICAL_ADDRESS Memory, | |
IN UINTN NumberOfPages | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_MEMORY_TYPE MemoryType; | |
Status = CoreInternalFreePages (Memory, NumberOfPages, &MemoryType); | |
if (!EFI_ERROR (Status)) { | |
GuardFreedPagesChecked (Memory, NumberOfPages); | |
CoreUpdateProfile ( | |
(EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0), | |
MemoryProfileActionFreePages, | |
MemoryType, | |
EFI_PAGES_TO_SIZE (NumberOfPages), | |
(VOID *)(UINTN)Memory, | |
NULL | |
); | |
InstallMemoryAttributesTableOnMemoryAllocation (MemoryType); | |
ApplyMemoryProtectionPolicy ( | |
MemoryType, | |
EfiConventionalMemory, | |
Memory, | |
EFI_PAGES_TO_SIZE (NumberOfPages) | |
); | |
} | |
return Status; | |
} | |
/** | |
This function checks to see if the last memory map descriptor in a memory map | |
can be merged with any of the other memory map descriptors in a memorymap. | |
Memory descriptors may be merged if they are adjacent and have the same type | |
and attributes. | |
@param MemoryMap A pointer to the start of the memory map. | |
@param MemoryMapDescriptor A pointer to the last descriptor in MemoryMap. | |
@param DescriptorSize The size, in bytes, of an individual | |
EFI_MEMORY_DESCRIPTOR. | |
@return A pointer to the next available descriptor in MemoryMap | |
**/ | |
EFI_MEMORY_DESCRIPTOR * | |
MergeMemoryMapDescriptor ( | |
IN EFI_MEMORY_DESCRIPTOR *MemoryMap, | |
IN EFI_MEMORY_DESCRIPTOR *MemoryMapDescriptor, | |
IN UINTN DescriptorSize | |
) | |
{ | |
// | |
// Traverse the array of descriptors in MemoryMap | |
// | |
for ( ; MemoryMap != MemoryMapDescriptor; MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize)) { | |
// | |
// Check to see if the Type fields are identical. | |
// | |
if (MemoryMap->Type != MemoryMapDescriptor->Type) { | |
continue; | |
} | |
// | |
// Check to see if the Attribute fields are identical. | |
// | |
if (MemoryMap->Attribute != MemoryMapDescriptor->Attribute) { | |
continue; | |
} | |
// | |
// Check to see if MemoryMapDescriptor is immediately above MemoryMap | |
// | |
if (MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages) == MemoryMapDescriptor->PhysicalStart) { | |
// | |
// Merge MemoryMapDescriptor into MemoryMap | |
// | |
MemoryMap->NumberOfPages += MemoryMapDescriptor->NumberOfPages; | |
// | |
// Return MemoryMapDescriptor as the next available slot int he MemoryMap array | |
// | |
return MemoryMapDescriptor; | |
} | |
// | |
// Check to see if MemoryMapDescriptor is immediately below MemoryMap | |
// | |
if (MemoryMap->PhysicalStart - EFI_PAGES_TO_SIZE ((UINTN)MemoryMapDescriptor->NumberOfPages) == MemoryMapDescriptor->PhysicalStart) { | |
// | |
// Merge MemoryMapDescriptor into MemoryMap | |
// | |
MemoryMap->PhysicalStart = MemoryMapDescriptor->PhysicalStart; | |
MemoryMap->VirtualStart = MemoryMapDescriptor->VirtualStart; | |
MemoryMap->NumberOfPages += MemoryMapDescriptor->NumberOfPages; | |
// | |
// Return MemoryMapDescriptor as the next available slot int he MemoryMap array | |
// | |
return MemoryMapDescriptor; | |
} | |
} | |
// | |
// MemoryMapDescrtiptor could not be merged with any descriptors in MemoryMap. | |
// | |
// Return the slot immediately after MemoryMapDescriptor as the next available | |
// slot in the MemoryMap array | |
// | |
return NEXT_MEMORY_DESCRIPTOR (MemoryMapDescriptor, DescriptorSize); | |
} | |
/** | |
This function returns a copy of the current memory map. The map is an array of | |
memory descriptors, each of which describes a contiguous block of memory. | |
@param MemoryMapSize A pointer to the size, in bytes, of the | |
MemoryMap buffer. On input, this is the size of | |
the buffer allocated by the caller. On output, | |
it is the size of the buffer returned by the | |
firmware if the buffer was large enough, or the | |
size of the buffer needed to contain the map if | |
the buffer was too small. | |
@param MemoryMap A pointer to the buffer in which firmware places | |
the current memory map. | |
@param MapKey A pointer to the location in which firmware | |
returns the key for the current memory map. | |
@param DescriptorSize A pointer to the location in which firmware | |
returns the size, in bytes, of an individual | |
EFI_MEMORY_DESCRIPTOR. | |
@param DescriptorVersion A pointer to the location in which firmware | |
returns the version number associated with the | |
EFI_MEMORY_DESCRIPTOR. | |
@retval EFI_SUCCESS The memory map was returned in the MemoryMap | |
buffer. | |
@retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current | |
buffer size needed to hold the memory map is | |
returned in MemoryMapSize. | |
@retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
CoreGetMemoryMap ( | |
IN OUT UINTN *MemoryMapSize, | |
IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, | |
OUT UINTN *MapKey, | |
OUT UINTN *DescriptorSize, | |
OUT UINT32 *DescriptorVersion | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Size; | |
UINTN BufferSize; | |
UINTN NumberOfEntries; | |
LIST_ENTRY *Link; | |
MEMORY_MAP *Entry; | |
EFI_GCD_MAP_ENTRY *GcdMapEntry; | |
EFI_GCD_MAP_ENTRY MergeGcdMapEntry; | |
EFI_MEMORY_TYPE Type; | |
EFI_MEMORY_DESCRIPTOR *MemoryMapStart; | |
EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; | |
// | |
// Make sure the parameters are valid | |
// | |
if (MemoryMapSize == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
CoreAcquireGcdMemoryLock (); | |
// | |
// Count the number of Reserved and runtime MMIO entries | |
// And, count the number of Persistent entries. | |
// | |
NumberOfEntries = 0; | |
for (Link = mGcdMemorySpaceMap.ForwardLink; Link != &mGcdMemorySpaceMap; Link = Link->ForwardLink) { | |
GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE); | |
if ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypePersistent) || | |
(GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) || | |
((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) && | |
((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME))) | |
{ | |
NumberOfEntries++; | |
} | |
} | |
Size = sizeof (EFI_MEMORY_DESCRIPTOR); | |
// | |
// Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will | |
// prevent people from having pointer math bugs in their code. | |
// now you have to use *DescriptorSize to make things work. | |
// | |
Size += sizeof (UINT64) - (Size % sizeof (UINT64)); | |
if (DescriptorSize != NULL) { | |
*DescriptorSize = Size; | |
} | |
if (DescriptorVersion != NULL) { | |
*DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION; | |
} | |
CoreAcquireMemoryLock (); | |
// | |
// Compute the buffer size needed to fit the entire map | |
// | |
BufferSize = Size * NumberOfEntries; | |
for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { | |
BufferSize += Size; | |
} | |
if (*MemoryMapSize < BufferSize) { | |
Status = EFI_BUFFER_TOO_SMALL; | |
goto Done; | |
} | |
if (MemoryMap == NULL) { | |
Status = EFI_INVALID_PARAMETER; | |
goto Done; | |
} | |
// | |
// Build the map | |
// | |
ZeroMem (MemoryMap, BufferSize); | |
MemoryMapStart = MemoryMap; | |
for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { | |
Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
ASSERT (Entry->VirtualStart == 0); | |
// | |
// Convert internal map into an EFI_MEMORY_DESCRIPTOR | |
// | |
MemoryMap->Type = Entry->Type; | |
MemoryMap->PhysicalStart = Entry->Start; | |
MemoryMap->VirtualStart = Entry->VirtualStart; | |
MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT); | |
// | |
// If the memory type is EfiConventionalMemory, then determine if the range is part of a | |
// memory type bin and needs to be converted to the same memory type as the rest of the | |
// memory type bin in order to minimize EFI Memory Map changes across reboots. This | |
// improves the chances for a successful S4 resume in the presence of minor page allocation | |
// differences across reboots. | |
// | |
if (MemoryMap->Type == EfiConventionalMemory) { | |
for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) { | |
if (mMemoryTypeStatistics[Type].Special && | |
(mMemoryTypeStatistics[Type].NumberOfPages > 0) && | |
(Entry->Start >= mMemoryTypeStatistics[Type].BaseAddress) && | |
(Entry->End <= mMemoryTypeStatistics[Type].MaximumAddress)) | |
{ | |
MemoryMap->Type = Type; | |
} | |
} | |
} | |
MemoryMap->Attribute = Entry->Attribute; | |
if (MemoryMap->Type < EfiMaxMemoryType) { | |
if (mMemoryTypeStatistics[MemoryMap->Type].Runtime) { | |
MemoryMap->Attribute |= EFI_MEMORY_RUNTIME; | |
} | |
} | |
// | |
// Check to see if the new Memory Map Descriptor can be merged with an | |
// existing descriptor if they are adjacent and have the same attributes | |
// | |
MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size); | |
} | |
ZeroMem (&MergeGcdMapEntry, sizeof (MergeGcdMapEntry)); | |
GcdMapEntry = NULL; | |
for (Link = mGcdMemorySpaceMap.ForwardLink; ; Link = Link->ForwardLink) { | |
if (Link != &mGcdMemorySpaceMap) { | |
// | |
// Merge adjacent same type and attribute GCD memory range | |
// | |
GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE); | |
if ((MergeGcdMapEntry.Capabilities == GcdMapEntry->Capabilities) && | |
(MergeGcdMapEntry.Attributes == GcdMapEntry->Attributes) && | |
(MergeGcdMapEntry.GcdMemoryType == GcdMapEntry->GcdMemoryType) && | |
(MergeGcdMapEntry.GcdIoType == GcdMapEntry->GcdIoType)) | |
{ | |
MergeGcdMapEntry.EndAddress = GcdMapEntry->EndAddress; | |
continue; | |
} | |
} | |
if ((MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) || | |
((MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) && | |
((MergeGcdMapEntry.Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME))) | |
{ | |
// | |
// Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR, | |
// it will be recorded as page PhysicalStart and NumberOfPages. | |
// | |
ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0); | |
ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0); | |
// | |
// Create EFI_MEMORY_DESCRIPTOR for every Reserved and runtime MMIO GCD entries | |
// | |
MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; | |
MemoryMap->VirtualStart = 0; | |
MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); | |
MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) | | |
(MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); | |
if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) { | |
MemoryMap->Type = EfiReservedMemoryType; | |
} else if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) { | |
if ((MergeGcdMapEntry.Attributes & EFI_MEMORY_PORT_IO) == EFI_MEMORY_PORT_IO) { | |
MemoryMap->Type = EfiMemoryMappedIOPortSpace; | |
} else { | |
MemoryMap->Type = EfiMemoryMappedIO; | |
} | |
} | |
// | |
// Check to see if the new Memory Map Descriptor can be merged with an | |
// existing descriptor if they are adjacent and have the same attributes | |
// | |
MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size); | |
} | |
if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypePersistent) { | |
// | |
// Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR, | |
// it will be recorded as page PhysicalStart and NumberOfPages. | |
// | |
ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0); | |
ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0); | |
// | |
// Create EFI_MEMORY_DESCRIPTOR for every Persistent GCD entries | |
// | |
MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; | |
MemoryMap->VirtualStart = 0; | |
MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); | |
MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV | | |
(MergeGcdMapEntry.Capabilities & (EFI_CACHE_ATTRIBUTE_MASK | EFI_MEMORY_ATTRIBUTE_MASK)); | |
MemoryMap->Type = EfiPersistentMemory; | |
// | |
// Check to see if the new Memory Map Descriptor can be merged with an | |
// existing descriptor if they are adjacent and have the same attributes | |
// | |
MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size); | |
} | |
if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeUnaccepted) { | |
// | |
// Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR, | |
// it will be recorded as page PhysicalStart and NumberOfPages. | |
// | |
ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0); | |
ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0); | |
// | |
// Create EFI_MEMORY_DESCRIPTOR for every Unaccepted GCD entries | |
// | |
MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress; | |
MemoryMap->VirtualStart = 0; | |
MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT); | |
MemoryMap->Attribute = MergeGcdMapEntry.Attributes | | |
(MergeGcdMapEntry.Capabilities & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP | EFI_MEMORY_RO | | |
EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB)); | |
MemoryMap->Type = EfiUnacceptedMemoryType; | |
// | |
// Check to see if the new Memory Map Descriptor can be merged with an | |
// existing descriptor if they are adjacent and have the same attributes | |
// | |
MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size); | |
} | |
if (Link == &mGcdMemorySpaceMap) { | |
// | |
// break loop when arrive at head. | |
// | |
break; | |
} | |
if (GcdMapEntry != NULL) { | |
// | |
// Copy new GCD map entry for the following GCD range merge | |
// | |
CopyMem (&MergeGcdMapEntry, GcdMapEntry, sizeof (MergeGcdMapEntry)); | |
} | |
} | |
// | |
// Compute the size of the buffer actually used after all memory map descriptor merge operations | |
// | |
BufferSize = ((UINT8 *)MemoryMap - (UINT8 *)MemoryMapStart); | |
// | |
// Note: Some OSs will treat EFI_MEMORY_DESCRIPTOR.Attribute as really | |
// set attributes and change memory paging attribute accordingly. | |
// But current EFI_MEMORY_DESCRIPTOR.Attribute is assigned by | |
// value from Capabilities in GCD memory map. This might cause | |
// boot problems. Clearing all page-access permission related | |
// capabilities can workaround it. Following code is supposed to | |
// be removed once the usage of EFI_MEMORY_DESCRIPTOR.Attribute | |
// is clarified in UEFI spec and adopted by both EDK-II Core and | |
// all supported OSs. | |
// | |
MemoryMapEnd = MemoryMap; | |
MemoryMap = MemoryMapStart; | |
while (MemoryMap < MemoryMapEnd) { | |
MemoryMap->Attribute &= ~(UINT64)EFI_MEMORY_ACCESS_MASK; | |
MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size); | |
} | |
MergeMemoryMap (MemoryMapStart, &BufferSize, Size); | |
MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMapStart + BufferSize); | |
Status = EFI_SUCCESS; | |
Done: | |
// | |
// Update the map key finally | |
// | |
if (MapKey != NULL) { | |
*MapKey = mMemoryMapKey; | |
} | |
CoreReleaseMemoryLock (); | |
CoreReleaseGcdMemoryLock (); | |
*MemoryMapSize = BufferSize; | |
DEBUG_CODE ( | |
DumpGuardedMemoryBitmap (); | |
); | |
return Status; | |
} | |
/** | |
Internal function. Used by the pool functions to allocate pages | |
to back pool allocation requests. | |
@param PoolType The type of memory for the new pool pages | |
@param NumberOfPages No of pages to allocate | |
@param Alignment Bits to align. | |
@param NeedGuard Flag to indicate Guard page is needed or not | |
@return The allocated memory, or NULL | |
**/ | |
VOID * | |
CoreAllocatePoolPages ( | |
IN EFI_MEMORY_TYPE PoolType, | |
IN UINTN NumberOfPages, | |
IN UINTN Alignment, | |
IN BOOLEAN NeedGuard | |
) | |
{ | |
UINT64 Start; | |
// | |
// Find the pages to convert | |
// | |
Start = FindFreePages ( | |
MAX_ALLOC_ADDRESS, | |
NumberOfPages, | |
PoolType, | |
Alignment, | |
NeedGuard | |
); | |
// | |
// Convert it to boot services data | |
// | |
if (Start == 0) { | |
DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "AllocatePoolPages: failed to allocate %d pages\n", (UINT32)NumberOfPages)); | |
} else { | |
if (NeedGuard) { | |
CoreConvertPagesWithGuard (Start, NumberOfPages, PoolType); | |
} else { | |
CoreConvertPages (Start, NumberOfPages, PoolType); | |
} | |
} | |
return (VOID *)(UINTN)Start; | |
} | |
/** | |
Internal function. Frees pool pages allocated via AllocatePoolPages () | |
@param Memory The base address to free | |
@param NumberOfPages The number of pages to free | |
**/ | |
VOID | |
CoreFreePoolPages ( | |
IN EFI_PHYSICAL_ADDRESS Memory, | |
IN UINTN NumberOfPages | |
) | |
{ | |
CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory); | |
} | |
/** | |
Make sure the memory map is following all the construction rules, | |
it is the last time to check memory map error before exit boot services. | |
@param MapKey Memory map key | |
@retval EFI_INVALID_PARAMETER Memory map not consistent with construction | |
rules. | |
@retval EFI_SUCCESS Valid memory map. | |
**/ | |
EFI_STATUS | |
CoreTerminateMemoryMap ( | |
IN UINTN MapKey | |
) | |
{ | |
EFI_STATUS Status; | |
LIST_ENTRY *Link; | |
MEMORY_MAP *Entry; | |
Status = EFI_SUCCESS; | |
CoreAcquireMemoryLock (); | |
if (MapKey == mMemoryMapKey) { | |
// | |
// Make sure the memory map is following all the construction rules | |
// This is the last chance we will be able to display any messages on | |
// the console devices. | |
// | |
for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) { | |
Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
if (Entry->Type < EfiMaxMemoryType) { | |
if (mMemoryTypeStatistics[Entry->Type].Runtime) { | |
ASSERT (Entry->Type != EfiACPIReclaimMemory); | |
ASSERT (Entry->Type != EfiACPIMemoryNVS); | |
if ((Entry->Start & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) { | |
DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n")); | |
Status = EFI_INVALID_PARAMETER; | |
goto Done; | |
} | |
if (((Entry->End + 1) & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) { | |
DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n")); | |
Status = EFI_INVALID_PARAMETER; | |
goto Done; | |
} | |
} | |
} | |
} | |
// | |
// The map key they gave us matches what we expect. Fall through and | |
// return success. In an ideal world we would clear out all of | |
// EfiBootServicesCode and EfiBootServicesData. However this function | |
// is not the last one called by ExitBootServices(), so we have to | |
// preserve the memory contents. | |
// | |
} else { | |
Status = EFI_INVALID_PARAMETER; | |
} | |
Done: | |
CoreReleaseMemoryLock (); | |
return Status; | |
} |