blob: 1118be7fe0d7d6c754ba39ec42756b8997010918 [file]
/** @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);
}
}
/**
Helper function to evaluate if memory regions intersect.
@param Start1 The address of the first byte in the first memory region.
@param End1 The address of the last byte in the first memory region.
@param Start2 The address of the first byte in the second memory region.
@param End2 The address of the last byte in the second memory region.
@return TRUE if the memory regions intersect, FALSE otherwise.
**/
static
BOOLEAN
MemoryRegionsIntersect (
IN EFI_PHYSICAL_ADDRESS Start1,
IN EFI_PHYSICAL_ADDRESS End1,
IN EFI_PHYSICAL_ADDRESS Start2,
IN EFI_PHYSICAL_ADDRESS End2
)
{
ASSERT (Start1 <= End1);
ASSERT (Start2 <= End2);
return ((Start1 <= End2) && (Start2 <= End1));
}
/**
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 ();
// If entry allocation failed once, it is unlikely to succeed moving forward
// However, we can try since we're in the middle of moving list nodes
if (Entry == NULL) {
ASSERT (Entry != NULL);
continue;
}
//
// 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;
UINT64 Size;
UINT64 Alignment;
UINT64 BinSize;
//
// 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
//
Top = Start + 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;
}
if (gMemoryTypeInformation[Index].NumberOfPages != 0) {
Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
if ((gMemoryTypeInformation[Index].Type == EfiReservedMemoryType) ||
(gMemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) ||
(gMemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) ||
(gMemoryTypeInformation[Index].Type == EfiRuntimeServicesData))
{
Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
}
BinSize = EFI_PAGES_TO_SIZE ((UINTN)gMemoryTypeInformation[Index].NumberOfPages);
BinSize = ALIGN_VALUE (BinSize, Alignment);
Size += BinSize;
if (Size > Length) {
return;
}
Top -= BinSize;
Size += (Top & (Alignment - 1));
if (Size > Length) {
return;
}
Top &= ~(Alignment - 1);
}
}
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) {
Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
if ((gMemoryTypeInformation[Index].Type == EfiReservedMemoryType) ||
(gMemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) ||
(gMemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) ||
(gMemoryTypeInformation[Index].Type == EfiRuntimeServicesData))
{
Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
}
BinSize = EFI_PAGES_TO_SIZE ((UINTN)gMemoryTypeInformation[Index].NumberOfPages);
BinSize = ALIGN_VALUE (BinSize, Alignment);
Top = (Top - BinSize) & ~(Alignment - 1);
mMemoryTypeStatistics[Type].BaseAddress = Top;
mMemoryTypeStatistics[Type].MaximumAddress = Top + BinSize - 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;
}
mMemoryTypeStatistics[Type].NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)BinSize);
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;
UINT64 Alignment;
UINT64 BinSize;
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) {
Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
if ((gMemoryTypeInformation[Index].Type == EfiReservedMemoryType) ||
(gMemoryTypeInformation[Index].Type == EfiACPIMemoryNVS) ||
(gMemoryTypeInformation[Index].Type == EfiRuntimeServicesCode) ||
(gMemoryTypeInformation[Index].Type == EfiRuntimeServicesData))
{
Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
}
BinSize = EFI_PAGES_TO_SIZE ((UINTN)gMemoryTypeInformation[Index].NumberOfPages);
BinSize = ALIGN_VALUE (BinSize, Alignment);
gMemoryTypeInformation[Index].NumberOfPages = (UINT32)EFI_SIZE_TO_PAGES ((UINTN)BinSize);
//
// 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) || (Entry == NULL)) {
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;
if (Entry == NULL) {
ASSERT (Entry != NULL);
return EFI_NOT_FOUND;
}
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;
UINT64 ProposedStart;
UINT64 ProposedSize;
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;
}
//
// Don't allocate out of Special-Purpose memory.
//
if ((Entry->Attribute & EFI_MEMORY_SP) != 0) {
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) {
ProposedStart = DescEnd + 1 - DescNumberOfBytes;
ProposedSize = NumberOfBytes;
DescEnd = AdjustMemoryS (
&ProposedStart,
DescNumberOfBytes,
&ProposedSize
);
// Check if there was not enough space in the descriptor for the allocation after adjusting for the guard
// or if the adjusted range is outside of the bin we are searching within
if ((DescEnd == 0) || (ProposedStart < MinAddress) || (ProposedStart + ProposedSize - 1 > MaxAddress)) {
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) {
// Page 0 is not allowed to be allocated as it is reserved for null pointer detection
if (Start == 0) {
return EFI_NOT_FOUND;
}
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;
UINT64 Attributes;
// check if this memory is returned to the core as RW at a minimum. If the memory attribute protocol is not installed,
// then we assume that the memory is RW by default and continue to free it.
if (gMemoryAttributeProtocol != NULL) {
Status = gMemoryAttributeProtocol->GetMemoryAttributes (
gMemoryAttributeProtocol,
Memory,
EFI_PAGES_TO_SIZE (NumberOfPages),
&Attributes
);
// if we failed to get the attributes, or if the memory is read-only or read-protected,
// then we leak the memory and return success. This is done because the UEFI spec does not specify whether pages
// should be freed with any specific permission attributes. As such, there exist bootloaders in the wild that will
// free memory that is marked RO, which can crash the core if DebugClearMemory is enabled or can be passed out to a
// driver in the next AllocatePages() call, which can cause a crash later on. It is deemed lower risk to leak the
// memory than to attempt to fix up the attributes as that requires syncing the GCD and the page table.
if ((Status == EFI_NO_MAPPING) ||
(!EFI_ERROR (Status) && ((Attributes & EFI_MEMORY_RO) || (Attributes & EFI_MEMORY_RP))))
{
DEBUG ((
DEBUG_WARN,
"%a: Memory %llx for %llx Pages failed to get attributes with status %r or it is read-only or read-protected. "
"Attributes: %llx. Leaking memory!\n",
__func__,
Memory,
NumberOfPages,
Status,
Attributes
));
return EFI_SUCCESS;
}
}
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);
}
/**
Sets the memory type for an EFI memory descriptor and updates its attributes.
This function sets the Type field of the provided EFI_MEMORY_DESCRIPTOR and
then updates the memory map entry attributes based on the memory type statistics.
@param MemoryMap Pointer to the EFI_MEMORY_DESCRIPTOR to update
@param Type The memory type to set
**/
static
VOID
SetEfiMemoryDescriptorType (
IN EFI_MEMORY_DESCRIPTOR *MemoryMap,
IN EFI_MEMORY_TYPE Type
)
{
if (MemoryMap == NULL) {
ASSERT (MemoryMap != NULL);
return;
}
MemoryMap->Type = Type;
if (MemoryMap->Type >= EfiMaxMemoryType) {
return;
}
if (mMemoryTypeStatistics[MemoryMap->Type].Runtime) {
MemoryMap->Attribute |= EFI_MEMORY_RUNTIME;
} else {
MemoryMap->Attribute &= ~EFI_MEMORY_RUNTIME;
}
}
/**
Helper function to perform additional validation on the memory map. This routine
verifies memory map entries do not cross special memory-type bin boundaries.
It is intended for debugging and validation purposes only and should not be used
in production paths.
@param MemoryMap Pointer to the buffer containing the current memory map.
@param MemoryMapSize Size, in bytes, of the memory map buffer.
@param DescriptorSize Size, in bytes, of an EFI_MEMORY_DESCRIPTOR.
**/
static
VOID
CoreMemoryMapSanityCheck (
IN EFI_MEMORY_DESCRIPTOR *MemoryMap,
CONST IN UINTN MemoryMapSize,
CONST IN UINTN DescriptorSize
)
{
EFI_MEMORY_DESCRIPTOR *CurrentMemoryMap;
EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
UINT64 EntryStart;
UINT64 EntryEnd;
EFI_MEMORY_TYPE Type;
MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);
CurrentMemoryMap = MemoryMap;
while (CurrentMemoryMap < MemoryMapEnd) {
// Verify that any memory map entry that overlaps with a special memory type
// bin must be completely contained within the bin and have the same type as
// the bin.
EntryStart = CurrentMemoryMap->PhysicalStart;
EntryEnd = EntryStart + LShiftU64 (CurrentMemoryMap->NumberOfPages, EFI_PAGE_SHIFT) - 1;
for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) {
// Check if this memory map entry overlaps with a special memory type bin.
if (mMemoryTypeStatistics[Type].Special &&
(mMemoryTypeStatistics[Type].NumberOfPages > 0) &&
MemoryRegionsIntersect (
EntryStart,
EntryEnd,
mMemoryTypeStatistics[Type].BaseAddress,
mMemoryTypeStatistics[Type].MaximumAddress
)
)
{
// Verify that it is completely contained within the bin.
if ((EntryStart < mMemoryTypeStatistics[Type].BaseAddress) ||
(EntryEnd > mMemoryTypeStatistics[Type].MaximumAddress))
{
DEBUG ((
DEBUG_ERROR,
"%a: Memory Map entry (Type %d, Start 0x%lx, End 0x%lx) does not fit within special memory type bin (Type %d, Start 0x%lx, End 0x%lx).\n",
__func__,
CurrentMemoryMap->Type,
EntryStart,
EntryEnd,
Type,
mMemoryTypeStatistics[Type].BaseAddress,
mMemoryTypeStatistics[Type].MaximumAddress
));
ASSERT (FALSE);
}
// It is contained within the bin, the type must match the bin type.
if (CurrentMemoryMap->Type != (UINT32)Type) {
DEBUG ((
DEBUG_ERROR,
"%a: Memory Map entry type does not match special memory type bin. Bin Type %d, Type %d, Start 0x%lx, End 0x%lx\n",
__func__,
Type,
CurrentMemoryMap->Type,
EntryStart,
EntryEnd
));
ASSERT (FALSE);
}
}
}
CurrentMemoryMap = NEXT_MEMORY_DESCRIPTOR (CurrentMemoryMap, 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;
UINT64 BinStart;
UINT64 BinEnd;
UINT64 EntryStart;
UINT64 EntryEnd;
BOOLEAN Modified;
//
// 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 == EfiGcdMemoryTypeUnaccepted) ||
((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;
}
//
// Add extra entries for each non-zero sized special memory type bin that may
// be split. Worst case is that a memory entry overlaps entire bin and
// requires an extra entry below the bin and an extra entry above the bin.
//
for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) {
if (mMemoryTypeStatistics[Type].Special &&
(mMemoryTypeStatistics[Type].NumberOfPages > 0))
{
BufferSize += 2 * 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;
MemoryMapEnd = MemoryMapStart;
for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
ASSERT (Entry->VirtualStart == 0);
MemoryMapEnd->PhysicalStart = Entry->Start;
MemoryMapEnd->VirtualStart = 0;
MemoryMapEnd->NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)(Entry->End - Entry->Start + 1));
MemoryMapEnd->Attribute = Entry->Attribute;
SetEfiMemoryDescriptorType (MemoryMapEnd, Entry->Type);
MemoryMapEnd = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMapEnd, Size);
if ((UINTN)((UINT8 *)MemoryMapEnd - (UINT8 *)MemoryMapStart) > *MemoryMapSize) {
Status = EFI_BUFFER_TOO_SMALL;
goto Done;
}
}
//
// Loop through all memory bins and split memory entries of type
// EfiConventionalMemory if the memory map entry overlaps the beginning or end
// of a memory bin. Convert memory map entries of type EfiConventionalMemory
// that are completely contained within a memory bin to the memory bin type.
//
for (Type = (EFI_MEMORY_TYPE)0; Type < EfiMaxMemoryType; Type++) {
//
// If memory bin is empty or not special, then no split or conversion is required
//
if (mMemoryTypeStatistics[Type].NumberOfPages == 0) {
continue;
}
if (!mMemoryTypeStatistics[Type].Special) {
continue;
}
BinStart = mMemoryTypeStatistics[Type].BaseAddress;
BinEnd = mMemoryTypeStatistics[Type].MaximumAddress;
Modified = TRUE;
while (Modified) {
BufferSize = ((UINT8 *)MemoryMapEnd - (UINT8 *)MemoryMapStart);
MergeMemoryMap (MemoryMapStart, &BufferSize, Size);
MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMapStart + BufferSize);
Modified = FALSE;
for (MemoryMap = MemoryMapStart; MemoryMap < MemoryMapEnd; MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size)) {
//
// If the memory map entry is not EfiConventionalMemory, then no split or conversion is required
//
if (MemoryMap->Type != EfiConventionalMemory) {
continue;
}
EntryStart = MemoryMap->PhysicalStart;
EntryEnd = EntryStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages) - 1;
//
// If the memory map entry does not overlap the memory bin, then no
// split or conversion is required
//
if ((EntryEnd < BinStart) || (EntryStart > BinEnd)) {
continue;
}
//
// If the memory map entry is completely contained within the memory
// bin, then no split is needed and only the type should be converted.
//
if ((EntryStart >= BinStart) && (EntryEnd <= BinEnd)) {
SetEfiMemoryDescriptorType (MemoryMap, Type);
//
// Memory map was modified. Restart processing from the beginning.
//
Modified = TRUE;
break;
}
//
// If the memory map entry starts below the memory bin start, then split
// at the memory bin start.
//
if (EntryStart < BinStart) {
//
// Shrink original entry to end at the beginning of the bin and
// keep the original EfiConventionalMemory type and PhysicalAddress
//
MemoryMap->NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)(BinStart - EntryStart));
//
// Insert a new entry of type memory bin type for the part after the memory bin start
// Copy the current memory map entry contents into the inserted entry
//
CopyMem (
NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size),
MemoryMap,
((UINT8 *)MemoryMapEnd - (UINT8 *)MemoryMap)
);
MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size);
MemoryMapEnd = NEXT_MEMORY_DESCRIPTOR (MemoryMapEnd, Size);
if ((UINTN)((UINT8 *)MemoryMapEnd - (UINT8 *)MemoryMapStart) > *MemoryMapSize) {
//
// Unexpected condition since max expected size for worst case splits was
// computed and returned.
//
Status = EFI_BUFFER_TOO_SMALL;
goto Done;
}
MemoryMap->PhysicalStart = BinStart;
MemoryMap->NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)(EntryEnd - BinStart + 1));
SetEfiMemoryDescriptorType (MemoryMap, Type);
if (EntryEnd > BinEnd) {
//
// Shrink new entry to only cover the memory bin
//
MemoryMap->NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)(BinEnd - BinStart + 1));
//
// The new entry extends beyond the memory bin end.
// Create a new entry of type EfiConventionalMemory.
//
CopyMem (
NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size),
MemoryMap,
((UINT8 *)MemoryMapEnd - (UINT8 *)MemoryMap)
);
MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size);
MemoryMapEnd = NEXT_MEMORY_DESCRIPTOR (MemoryMapEnd, Size);
if ((UINTN)((UINT8 *)MemoryMapEnd - (UINT8 *)MemoryMapStart) > *MemoryMapSize) {
//
// Unexpected condition since max expected size for worst case splits was
// computed and returned.
//
Status = EFI_BUFFER_TOO_SMALL;
goto Done;
}
MemoryMap->PhysicalStart = BinEnd + 1;
MemoryMap->NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)(EntryEnd - BinEnd));
SetEfiMemoryDescriptorType (MemoryMap, EfiConventionalMemory);
}
//
// Memory map was modified. Restart processing from the beginning
//
Modified = TRUE;
break;
}
//
// If the memory map entry ends above the memory bin end, then split at the memory bin end
//
if (EntryEnd > BinEnd) {
//
// Shrink the original entry for part before the memory bin end
//
MemoryMap->PhysicalStart = EntryStart;
MemoryMap->NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)(BinEnd - EntryStart + 1));
SetEfiMemoryDescriptorType (MemoryMap, Type);
//
// Create new entry for the part before the memory bin end
// EntryStart is guaranteed to be >= BinStart here, so the entire
// entry can be converted to the memory bin type
//
CopyMem (
NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size),
MemoryMap,
((UINT8 *)MemoryMapEnd - (UINT8 *)MemoryMap)
);
MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size);
MemoryMapEnd = NEXT_MEMORY_DESCRIPTOR (MemoryMapEnd, Size);
if ((UINTN)((UINT8 *)MemoryMapEnd - (UINT8 *)MemoryMapStart) > *MemoryMapSize) {
Status = EFI_BUFFER_TOO_SMALL;
goto Done;
}
MemoryMap->PhysicalStart = BinEnd + 1;
MemoryMap->NumberOfPages = EFI_SIZE_TO_PAGES ((UINTN)(EntryEnd - BinEnd));
SetEfiMemoryDescriptorType (MemoryMap, EfiConventionalMemory);
//
// Memory map was modified, so restart processing from the beginning
//
Modified = TRUE;
break;
}
}
}
}
MemoryMap = MemoryMapEnd;
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 ((UINTN)((UINT8 *)MemoryMap - (UINT8 *)MemoryMapStart) > *MemoryMapSize) {
//
// Unexpected condition since max expected size for worst case splits was
// computed and returned.
//
Status = EFI_BUFFER_TOO_SMALL;
goto Done;
}
}
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 ((UINTN)((UINT8 *)MemoryMap - (UINT8 *)MemoryMapStart) > *MemoryMapSize) {
//
// Unexpected condition since max expected size for worst case splits was
// computed and returned.
//
Status = EFI_BUFFER_TOO_SMALL;
goto Done;
}
}
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 ((UINTN)((UINT8 *)MemoryMap - (UINT8 *)MemoryMapStart) > *MemoryMapSize) {
//
// Unexpected condition since max expected size for worst case splits was
// computed and returned.
//
Status = EFI_BUFFER_TOO_SMALL;
goto Done;
}
}
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);
DEBUG_CODE_BEGIN ();
CoreMemoryMapSanityCheck (MemoryMapStart, BufferSize, *DescriptorSize);
DEBUG_CODE_END ();
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;
}