audk/EdkModulePkg/Core/Dxe/Mem/Page.c

1657 lines
43 KiB
C

/*++
Copyright (c) 2007, Intel Corporation
All rights reserved. This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
Module Name:
page.c
Abstract:
EFI Memory page management
Revision History
--*/
#include <DxeMain.h>
#define EFI_DEFAULT_PAGE_ALLOCATION_ALIGNMENT (EFI_PAGE_SIZE)
//
// Entry for tracking the memory regions for each memory type to help cooalese like memory types
//
typedef struct {
EFI_PHYSICAL_ADDRESS BaseAddress;
EFI_PHYSICAL_ADDRESS MaximumAddress;
UINT64 CurrentNumberOfPages;
UINTN InformationIndex;
} EFI_MEMORY_TYPE_STAISTICS;
//
// MemoryMap - The current memory map
//
UINTN mMemoryMapKey = 0;
//
// mMapStack - space to use as temp storage to build new map descriptors
// mMapDepth - depth of new descriptor stack
//
#define MAX_MAP_DEPTH 6
UINTN mMapDepth = 0;
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_STAISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1] = {
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiReservedMemoryType
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiLoaderCode
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiLoaderData
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiBootServicesCode
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiBootServicesData
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiRuntimeServicesCode
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiRuntimeServicesData
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiConventionalMemory
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiUnusableMemory
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiACPIReclaimMemory
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiACPIMemoryNVS
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiMemoryMappedIO
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiMemoryMappedIOPortSpace
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType }, // EfiPalCode
{ 0, EFI_MAX_ADDRESS, 0, EfiMaxMemoryType } // EfiMaxMemoryType
};
EFI_PHYSICAL_ADDRESS mDefaultMaximumAddress = EFI_MAX_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 },
{ EfiMaxMemoryType, 0 }
};
//
// Internal prototypes
//
STATIC
VOID
PromoteMemoryResource (
VOID
);
STATIC
VOID
CoreAddRange (
IN EFI_MEMORY_TYPE Type,
IN EFI_PHYSICAL_ADDRESS Start,
IN EFI_PHYSICAL_ADDRESS End,
IN UINT64 Attribute
);
STATIC
VOID
CoreFreeMemoryMapStack (
VOID
);
STATIC
EFI_STATUS
CoreConvertPages (
IN UINT64 Start,
IN UINT64 NumberOfPages,
IN EFI_MEMORY_TYPE NewType
);
STATIC
VOID
RemoveMemoryMapEntry (
MEMORY_MAP *Entry
);
STATIC
MEMORY_MAP *
AllocateMemoryMapEntry (
VOID
);
VOID
CoreAcquireMemoryLock (
VOID
)
/*++
Routine Description:
Enter critical section by gaining lock on gMemoryLock
Arguments:
None
Returns:
None
--*/
{
CoreAcquireLock (&gMemoryLock);
}
VOID
CoreReleaseMemoryLock (
VOID
)
/*++
Routine Description:
Exit critical section by releasing lock on gMemoryLock
Arguments:
None
Returns:
None
--*/
{
CoreReleaseLock (&gMemoryLock);
}
STATIC
VOID
PromoteMemoryResource (
VOID
)
/*++
Routine Description:
Find untested but initialized memory regions in GCD map and convert them to be DXE allocatable.
Arguments:
None
Returns:
None
--*/
{
LIST_ENTRY *Link;
EFI_GCD_MAP_ENTRY *Entry;
DEBUG ((EFI_D_ERROR | EFI_D_PAGE, "Promote the memory resource\n"));
CoreAcquireGcdMemoryLock ();
Link = mGcdMemorySpaceMap.ForwardLink;
while (Link != &mGcdMemorySpaceMap) {
Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
if (Entry->GcdMemoryType == EfiGcdMemoryTypeReserved &&
Entry->EndAddress < EFI_MAX_ADDRESS &&
(Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) {
//
// Update the GCD map
//
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 ();
}
Link = Link->ForwardLink;
}
CoreReleaseGcdMemoryLock ();
return;
}
VOID
CoreAddMemoryDescriptor (
IN EFI_MEMORY_TYPE Type,
IN EFI_PHYSICAL_ADDRESS Start,
IN UINT64 NumberOfPages,
IN UINT64 Attribute
)
/*++
Routine Description:
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.
Arguments:
Type - The type of memory to add
Start - The starting address in the memory range
Must be page aligned
NumberOfPages - The number of pages in the range
Attribute - Attributes of the memory to add
Returns:
None. The range is added to the memory map
--*/
{
EFI_PHYSICAL_ADDRESS End;
EFI_STATUS Status;
UINTN Index;
UINTN FreeIndex;
if ((Start & EFI_PAGE_MASK) != 0) {
return;
}
if (Type >= EfiMaxMemoryType && Type <= 0x7fffffff) {
return;
}
CoreAcquireMemoryLock ();
End = Start + LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT) - 1;
CoreAddRange (Type, Start, End, Attribute);
CoreFreeMemoryMapStack ();
CoreReleaseMemoryLock ();
//
// 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 (Type < 0 || 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 (Type < 0 || Type > EfiMaxMemoryType) {
continue;
}
if (gMemoryTypeInformation[FreeIndex].NumberOfPages != 0) {
CoreFreePages (
mMemoryTypeStatistics[Type].BaseAddress,
gMemoryTypeInformation[FreeIndex].NumberOfPages
);
mMemoryTypeStatistics[Type].BaseAddress = 0;
mMemoryTypeStatistics[Type].MaximumAddress = EFI_MAX_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 (Type < 0 || Type > EfiMaxMemoryType) {
continue;
}
if (gMemoryTypeInformation[Index].NumberOfPages != 0) {
CoreFreePages (
mMemoryTypeStatistics[Type].BaseAddress,
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 == EFI_MAX_ADDRESS) {
mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress;
}
}
mMemoryTypeInformationInitialized = TRUE;
}
STATIC
VOID
CoreAddRange (
IN EFI_MEMORY_TYPE Type,
IN EFI_PHYSICAL_ADDRESS Start,
IN EFI_PHYSICAL_ADDRESS End,
IN UINT64 Attribute
)
/*++
Routine Description:
Internal function. Adds a ranges to the memory map.
The range must not already exist in the map.
Arguments:
Type - The type of memory range to add
Start - The starting address in the memory range
Must be paged aligned
End - The last address in the range
Must be the last byte of a page
Attribute - The attributes of the memory range to add
Returns:
None. The range is added to the memory map
--*/
{
LIST_ENTRY *Link;
MEMORY_MAP *Entry;
ASSERT ((Start & EFI_PAGE_MASK) == 0);
ASSERT (End > Start) ;
ASSERT_LOCKED (&gMemoryLock);
DEBUG ((EFI_D_PAGE, "AddRange: %lx-%lx to %d\n", Start, End, Type));
//
// 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 funciton
// 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 ;
}
STATIC
VOID
CoreFreeMemoryMapStack (
VOID
)
/*++
Routine Description:
Internal function. Moves any memory descriptors that are on the
temporary descriptor stack to heap.
Arguments:
None
Returns:
None
--*/
{
MEMORY_MAP *Entry;
MEMORY_MAP *Entry2;
LIST_ENTRY *Link2;
ASSERT_LOCKED (&gMemoryLock);
//
// If already freeing the map stack, then return
//
if (mFreeMapStack) {
return ;
}
//
// Move the temporary memory descriptor stack into pool
//
mFreeMapStack += 1;
while (mMapDepth) {
//
// 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;
}
STATIC
VOID
RemoveMemoryMapEntry (
MEMORY_MAP *Entry
)
/*++
Routine Description:
Internal function. Removes a descriptor entry.
Arguments:
Entry - The entry to remove
Returns:
None
--*/
{
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);
}
}
STATIC
MEMORY_MAP *
AllocateMemoryMapEntry (
VOID
)
/*++
Routine Description:
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.
Arguments:
NONE
Returns:
The Memory map descriptor dequed from the mFreeMemoryMapEntryList
--*/
{
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), DEFAULT_PAGE_ALLOCATION);
if(FreeDescriptorEntries != NULL) {
//
// Enque the free memmory map entries into the list
//
for (Index = 0; Index< DEFAULT_PAGE_ALLOCATION / 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;
}
STATIC
EFI_STATUS
CoreConvertPages (
IN UINT64 Start,
IN UINT64 NumberOfPages,
IN EFI_MEMORY_TYPE NewType
)
/*++
Routine Description:
Internal function. Converts a memory range to the specified type.
The range must exist in the memory map.
Arguments:
Start - The first address of the range
Must be page aligned
NumberOfPages - The number of pages to convert
NewType - The new type for the memory range
Returns:
EFI_INVALID_PARAMETER - Invalid parameter
EFI_NOT_FOUND - Could not find a descriptor cover the specified range
or convertion not allowed.
EFI_SUCCESS - Successfully converts the memory range to the specified type.
--*/
{
UINT64 NumberOfBytes;
UINT64 End;
UINT64 RangeEnd;
UINT64 Attribute;
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);
if (NumberOfPages == 0 || (Start & EFI_PAGE_MASK ) || (Start > (Start + NumberOfBytes))) {
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 ((EFI_D_ERROR | EFI_D_PAGE, "ConvertPages: failed to find range %lx - %lx\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->End < End) {
RangeEnd = Entry->End;
}
DEBUG ((EFI_D_PAGE, "ConvertRange: %lx-%lx to %d\n", Start, RangeEnd, NewType));
//
// Debug code - verify conversion is allowed
//
if (!(NewType == EfiConventionalMemory ? 1 : 0) ^ (Entry->Type == EfiConventionalMemory ? 1 : 0)) {
DEBUG ((EFI_D_ERROR , "ConvertPages: Incompatible memory types\n"));
return EFI_NOT_FOUND;
}
//
// Update counters for the number of pages allocated to each memory type
//
if (Entry->Type >= 0 && Entry->Type < EfiMaxMemoryType) {
if (Start >= mMemoryTypeStatistics[Entry->Type].BaseAddress &&
Start <= mMemoryTypeStatistics[Entry->Type].MaximumAddress) {
if (NumberOfPages > mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages) {
mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages = 0;
} else {
mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages -= NumberOfPages;
}
}
}
if (NewType >= 0 && NewType < EfiMaxMemoryType) {
if (Start >= mMemoryTypeStatistics[NewType].BaseAddress && Start <= mMemoryTypeStatistics[NewType].MaximumAddress) {
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
//
Attribute = Entry->Attribute;
//
// 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
//
CoreAddRange (NewType, Start, RangeEnd, Attribute);
//
// 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;
}
STATIC
UINT64
CoreFindFreePagesI (
IN UINT64 MaxAddress,
IN UINT64 NumberOfPages,
IN EFI_MEMORY_TYPE NewType,
IN UINTN Alignment
)
/*++
Routine Description:
Internal function. Finds a consecutive free page range below
the requested address.
Arguments:
MaxAddress - The address that the range must be below
NumberOfPages - Number of pages needed
NewType - The type of memory the range is going to be turned into
Alignment - Bits to align with
Returns:
The base address of the range, or 0 if the range was not found
--*/
{
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 &= ~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, skip it
//
if (DescStart >= MaxAddress) {
continue;
}
//
// If desc ends past max allowed address, clip the end
//
if (DescEnd >= MaxAddress) {
DescEnd = MaxAddress;
}
DescEnd = ((DescEnd + 1) & (~(Alignment - 1))) - 1;
//
// 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 this is the best match so far remember it
//
if (DescEnd > Target) {
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;
}
STATIC
UINT64
FindFreePages (
IN UINT64 MaxAddress,
IN UINT64 NoPages,
IN EFI_MEMORY_TYPE NewType,
IN UINTN Alignment
)
/*++
Routine Description:
Internal function. Finds a consecutive free page range below
the requested address
Arguments:
MaxAddress - The address that the range must be below
NoPages - Number of pages needed
NewType - The type of memory the range is going to be turned into
Alignment - Bits to align with
Returns:
The base address of the range, or 0 if the range was not found.
--*/
{
UINT64 NewMaxAddress;
UINT64 Start;
NewMaxAddress = MaxAddress;
if (NewType >= 0 && NewType < EfiMaxMemoryType && NewMaxAddress >= mMemoryTypeStatistics[NewType].MaximumAddress) {
NewMaxAddress = mMemoryTypeStatistics[NewType].MaximumAddress;
} else {
if (NewMaxAddress > mDefaultMaximumAddress) {
NewMaxAddress = mDefaultMaximumAddress;
}
}
Start = CoreFindFreePagesI (NewMaxAddress, NoPages, NewType, Alignment);
if (!Start) {
Start = CoreFindFreePagesI (MaxAddress, NoPages, NewType, Alignment);
if (!Start) {
//
// Here means there may be no enough memory to use, so try to go through
// all the memory descript to promote the untested memory directly
//
PromoteMemoryResource ();
//
// Allocate memory again after the memory resource re-arranged
//
Start = CoreFindFreePagesI (MaxAddress, NoPages, NewType, Alignment);
}
}
return Start;
}
EFI_STATUS
EFIAPI
CoreAllocatePages (
IN EFI_ALLOCATE_TYPE Type,
IN EFI_MEMORY_TYPE MemoryType,
IN UINTN NumberOfPages,
IN OUT EFI_PHYSICAL_ADDRESS *Memory
)
/*++
Routine Description:
Allocates pages from the memory map.
Arguments:
Type - The type of allocation to perform
MemoryType - The type of memory to turn the allocated pages into
NumberOfPages - The number of pages to allocate
Memory - A pointer to receive the base allocated memory address
Returns:
Status. On success, Memory is filled in with the base address allocated
EFI_INVALID_PARAMETER - Parameters violate checking rules defined in spec.
EFI_NOT_FOUND - Could not allocate pages match the requirement.
EFI_OUT_OF_RESOURCES - No enough pages to allocate.
EFI_SUCCESS - Pages successfully allocated.
--*/
{
EFI_STATUS Status;
UINT64 Start;
UINT64 MaxAddress;
UINTN Alignment;
if (Type < AllocateAnyPages || Type >= (UINTN) MaxAllocateType) {
return EFI_INVALID_PARAMETER;
}
if ((MemoryType >= EfiMaxMemoryType && MemoryType <= 0x7fffffff) ||
MemoryType == EfiConventionalMemory) {
return EFI_INVALID_PARAMETER;
}
Alignment = EFI_DEFAULT_PAGE_ALLOCATION_ALIGNMENT;
if (MemoryType == EfiACPIReclaimMemory ||
MemoryType == EfiACPIMemoryNVS ||
MemoryType == EfiRuntimeServicesCode ||
MemoryType == EfiRuntimeServicesData) {
Alignment = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;
}
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 = EFI_MAX_ADDRESS;
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);
if (Start == 0) {
Status = EFI_OUT_OF_RESOURCES;
goto Done;
}
}
//
// Convert pages from FreeMemory to the requested type
//
Status = CoreConvertPages (Start, NumberOfPages, MemoryType);
Done:
CoreReleaseMemoryLock ();
if (!EFI_ERROR (Status)) {
*Memory = Start;
}
return Status;
}
EFI_STATUS
EFIAPI
CoreFreePages (
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN NumberOfPages
)
/*++
Routine Description:
Frees previous allocated pages.
Arguments:
Memory - Base address of memory being freed
NumberOfPages - The number of pages to free
Returns:
EFI_NOT_FOUND - Could not find the entry that covers the range
EFI_INVALID_PARAMETER - Address not aligned
EFI_SUCCESS -Pages successfully freed.
--*/
{
EFI_STATUS Status;
LIST_ENTRY *Link;
MEMORY_MAP *Entry;
UINTN Alignment;
//
// Free the range
//
CoreAcquireMemoryLock ();
//
// Find the entry that the covers the range
//
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) {
CoreReleaseMemoryLock ();
return EFI_NOT_FOUND;
}
Alignment = EFI_DEFAULT_PAGE_ALLOCATION_ALIGNMENT;
if (Entry->Type == EfiACPIReclaimMemory ||
Entry->Type == EfiACPIMemoryNVS ||
Entry->Type == EfiRuntimeServicesCode ||
Entry->Type == EfiRuntimeServicesData) {
Alignment = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;
}
if ((Memory & (Alignment - 1)) != 0) {
CoreReleaseMemoryLock ();
return EFI_INVALID_PARAMETER;
}
NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;
NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);
Status = CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory);
CoreReleaseMemoryLock ();
if (EFI_ERROR (Status)) {
return Status;
}
//
// Destroy the contents
//
if (Memory < EFI_MAX_ADDRESS) {
DEBUG_CLEAR_MEMORY ((VOID *)(UINTN)Memory, NumberOfPages << EFI_PAGE_SHIFT);
}
return Status;
}
EFI_STATUS
EFIAPI
CoreGetMemoryMap (
IN OUT UINTN *MemoryMapSize,
IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
OUT UINTN *MapKey,
OUT UINTN *DescriptorSize,
OUT UINT32 *DescriptorVersion
)
/*++
Routine Description:
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.
Arguments:
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.
MemoryMap - A pointer to the buffer in which firmware places the current memory map.
MapKey - A pointer to the location in which firmware returns the key for the
current memory map.
DescriptorSize - A pointer to the location in which firmware returns the size, in
bytes, of an individual EFI_MEMORY_DESCRIPTOR.
DescriptorVersion - A pointer to the location in which firmware returns the version
number associated with the EFI_MEMORY_DESCRIPTOR.
Returns:
EFI_SUCCESS - The memory map was returned in the MemoryMap buffer.
EFI_BUFFER_TOO_SMALL - The MemoryMap buffer was too small. The current buffer size
needed to hold the memory map is returned in MemoryMapSize.
EFI_INVALID_PARAMETER - One of the parameters has an invalid value.
--*/
{
EFI_STATUS Status;
UINTN Size;
UINTN BufferSize;
UINTN NumberOfRuntimeEntries;
LIST_ENTRY *Link;
MEMORY_MAP *Entry;
EFI_GCD_MAP_ENTRY *GcdMapEntry;
//
// Make sure the parameters are valid
//
if (MemoryMapSize == NULL) {
return EFI_INVALID_PARAMETER;
}
CoreAcquireGcdMemoryLock ();
//
// Count the number of Reserved and MMIO entries that are marked for runtime use
//
NumberOfRuntimeEntries = 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 == EfiGcdMemoryTypeReserved) ||
(GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo)) {
if ((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) {
NumberOfRuntimeEntries++;
}
}
}
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 * NumberOfRuntimeEntries;
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, Size);
for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {
Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
ASSERT (Entry->VirtualStart == 0);
MemoryMap->Type = Entry->Type;
MemoryMap->PhysicalStart = Entry->Start;
MemoryMap->VirtualStart = Entry->VirtualStart;
MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT);
switch (Entry->Type) {
case EfiRuntimeServicesCode:
case EfiRuntimeServicesData:
case EfiPalCode:
MemoryMap->Attribute = Entry->Attribute | EFI_MEMORY_RUNTIME;
break;
default:
MemoryMap->Attribute = Entry->Attribute;
break;
}
MemoryMap = NextMemoryDescriptor (MemoryMap, Size);
}
for (Link = mGcdMemorySpaceMap.ForwardLink; Link != &mGcdMemorySpaceMap; Link = Link->ForwardLink) {
GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);
if ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) ||
(GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo)) {
if ((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) {
MemoryMap->PhysicalStart = GcdMapEntry->BaseAddress;
MemoryMap->VirtualStart = 0;
MemoryMap->NumberOfPages = RShiftU64 ((GcdMapEntry->EndAddress - GcdMapEntry->BaseAddress + 1), EFI_PAGE_SHIFT);
MemoryMap->Attribute = GcdMapEntry->Attributes & ~EFI_MEMORY_PORT_IO;
if (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) {
MemoryMap->Type = EfiReservedMemoryType;
} else if (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
if ((GcdMapEntry->Attributes & EFI_MEMORY_PORT_IO) == EFI_MEMORY_PORT_IO) {
MemoryMap->Type = EfiMemoryMappedIOPortSpace;
} else {
MemoryMap->Type = EfiMemoryMappedIO;
}
}
MemoryMap = NextMemoryDescriptor (MemoryMap, Size);
}
}
}
Status = EFI_SUCCESS;
Done:
CoreReleaseMemoryLock ();
CoreReleaseGcdMemoryLock ();
//
// Update the map key finally
//
if (MapKey != NULL) {
*MapKey = mMemoryMapKey;
}
*MemoryMapSize = BufferSize;
return Status;
}
VOID *
CoreAllocatePoolPages (
IN EFI_MEMORY_TYPE PoolType,
IN UINTN NumberOfPages,
IN UINTN Alignment
)
/*++
Routine Description:
Internal function. Used by the pool functions to allocate pages
to back pool allocation requests.
Arguments:
PoolType - The type of memory for the new pool pages
NumberOfPages - No of pages to allocate
Alignment - Bits to align.
Returns:
The allocated memory, or NULL
--*/
{
UINT64 Start;
//
// Find the pages to convert
//
Start = FindFreePages (EFI_MAX_ADDRESS, NumberOfPages, PoolType, Alignment);
//
// Convert it to boot services data
//
if (Start == 0) {
DEBUG ((EFI_D_ERROR | EFI_D_PAGE, "AllocatePoolPages: failed to allocate %d pages\n", NumberOfPages));
} else {
CoreConvertPages (Start, NumberOfPages, PoolType);
}
return (VOID *)(UINTN)Start;
}
VOID
CoreFreePoolPages (
IN EFI_PHYSICAL_ADDRESS Memory,
IN UINTN NumberOfPages
)
/*++
Routine Description:
Internal function. Frees pool pages allocated via AllocatePoolPages ()
Arguments:
Memory - The base address to free
NumberOfPages - The number of pages to free
Returns:
None
--*/
{
CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory);
}
EFI_STATUS
CoreTerminateMemoryMap (
IN UINTN MapKey
)
/*++
Routine Description:
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.
Arguments:
MapKey - Memory map key
Returns:
EFI_INVALID_PARAMETER - Memory map not consistent with construction rules.
EFI_SUCCESS - Valid memory map.
--*/
{
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->Attribute & EFI_MEMORY_RUNTIME) {
if (Entry->Type == EfiACPIReclaimMemory || Entry->Type == EfiACPIMemoryNVS) {
DEBUG((EFI_D_ERROR, "ExitBootServices: ACPI memory entry has RUNTIME attribute set.\n"));
CoreReleaseMemoryLock ();
return EFI_INVALID_PARAMETER;
}
if (Entry->Start & (EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT - 1)) {
DEBUG((EFI_D_ERROR, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n"));
CoreReleaseMemoryLock ();
return EFI_INVALID_PARAMETER;
}
if ((Entry->End + 1) & (EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT - 1)) {
DEBUG((EFI_D_ERROR, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n"));
CoreReleaseMemoryLock ();
return EFI_INVALID_PARAMETER;
}
}
}
//
// 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;
}
CoreReleaseMemoryLock ();
return Status;
}