| /** @file | |
| SMM Memory page management functions. | |
| Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "PiSmmCore.h" | |
| #include <Library/SmmServicesTableLib.h> | |
| #define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT) | |
| LIST_ENTRY mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap); | |
| // | |
| // For GetMemoryMap() | |
| // | |
| #define MEMORY_MAP_SIGNATURE SIGNATURE_32('m','m','a','p') | |
| typedef struct { | |
| UINTN Signature; | |
| LIST_ENTRY Link; | |
| BOOLEAN FromStack; | |
| EFI_MEMORY_TYPE Type; | |
| UINT64 Start; | |
| UINT64 End; | |
| } MEMORY_MAP; | |
| LIST_ENTRY gMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap); | |
| #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); | |
| /** | |
| Allocates pages from the memory map. | |
| @param[in] Type The type of allocation to perform. | |
| @param[in] MemoryType The type of memory to turn the allocated pages | |
| into. | |
| @param[in] NumberOfPages The number of pages to allocate. | |
| @param[out] Memory A pointer to receive the base allocated memory | |
| address. | |
| @param[in] AddRegion If this memory is new added region. | |
| @param[in] NeedGuard Flag to indicate Guard page is needed | |
| or not | |
| @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 | |
| SmmInternalAllocatePagesEx ( | |
| IN EFI_ALLOCATE_TYPE Type, | |
| IN EFI_MEMORY_TYPE MemoryType, | |
| IN UINTN NumberOfPages, | |
| OUT EFI_PHYSICAL_ADDRESS *Memory, | |
| IN BOOLEAN AddRegion, | |
| IN BOOLEAN NeedGuard | |
| ); | |
| /** | |
| 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. | |
| @return The Memory map descriptor dequeued from the mFreeMemoryMapEntryList | |
| **/ | |
| MEMORY_MAP * | |
| AllocateMemoryMapEntry ( | |
| VOID | |
| ) | |
| { | |
| EFI_PHYSICAL_ADDRESS Mem; | |
| EFI_STATUS Status; | |
| MEMORY_MAP *FreeDescriptorEntries; | |
| MEMORY_MAP *Entry; | |
| UINTN Index; | |
| // DEBUG((DEBUG_INFO, "AllocateMemoryMapEntry\n")); | |
| if (IsListEmpty (&mFreeMemoryMapEntryList)) { | |
| // DEBUG((DEBUG_INFO, "mFreeMemoryMapEntryList is empty\n")); | |
| // | |
| // The list is empty, to allocate one page to refuel the list | |
| // | |
| Status = SmmInternalAllocatePagesEx ( | |
| AllocateAnyPages, | |
| EfiRuntimeServicesData, | |
| EFI_SIZE_TO_PAGES (RUNTIME_PAGE_ALLOCATION_GRANULARITY), | |
| &Mem, | |
| TRUE, | |
| FALSE | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| if (!EFI_ERROR (Status)) { | |
| FreeDescriptorEntries = (MEMORY_MAP *)(UINTN)Mem; | |
| // DEBUG((DEBUG_INFO, "New FreeDescriptorEntries - 0x%x\n", FreeDescriptorEntries)); | |
| // | |
| // Enqueue the free memory map entries into the list | |
| // | |
| for (Index = 0; Index < RUNTIME_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; | |
| // | |
| // If already freeing the map stack, then return | |
| // | |
| if (mFreeMapStack != 0) { | |
| ASSERT (FALSE); | |
| return; | |
| } | |
| // | |
| // Move the temporary memory descriptor stack into pool | |
| // | |
| mFreeMapStack += 1; | |
| while (mMapDepth != 0) { | |
| // | |
| // Deque an memory map entry from mFreeMemoryMapEntryList | |
| // | |
| Entry = AllocateMemoryMapEntry (); | |
| ASSERT (Entry); | |
| // | |
| // Update to proper entry | |
| // | |
| mMapDepth -= 1; | |
| if (mMapStack[mMapDepth].Link.ForwardLink != NULL) { | |
| CopyMem (Entry, &mMapStack[mMapDepth], sizeof (MEMORY_MAP)); | |
| Entry->FromStack = FALSE; | |
| // | |
| // Move this entry to general memory | |
| // | |
| InsertTailList (&mMapStack[mMapDepth].Link, &Entry->Link); | |
| RemoveEntryList (&mMapStack[mMapDepth].Link); | |
| mMapStack[mMapDepth].Link.ForwardLink = NULL; | |
| } | |
| } | |
| mFreeMapStack -= 1; | |
| } | |
| /** | |
| Insert new entry from memory map. | |
| @param[in] Link The old memory map entry to be linked. | |
| @param[in] Start The start address of new memory map entry. | |
| @param[in] End The end address of new memory map entry. | |
| @param[in] Type The type of new memory map entry. | |
| @param[in] Next If new entry is inserted to the next of old entry. | |
| @param[in] AddRegion If this memory is new added region. | |
| **/ | |
| VOID | |
| InsertNewEntry ( | |
| IN LIST_ENTRY *Link, | |
| IN UINT64 Start, | |
| IN UINT64 End, | |
| IN EFI_MEMORY_TYPE Type, | |
| IN BOOLEAN Next, | |
| IN BOOLEAN AddRegion | |
| ) | |
| { | |
| MEMORY_MAP *Entry; | |
| Entry = &mMapStack[mMapDepth]; | |
| mMapDepth += 1; | |
| ASSERT (mMapDepth < MAX_MAP_DEPTH); | |
| Entry->FromStack = TRUE; | |
| Entry->Signature = MEMORY_MAP_SIGNATURE; | |
| Entry->Type = Type; | |
| Entry->Start = Start; | |
| Entry->End = End; | |
| if (Next) { | |
| InsertHeadList (Link, &Entry->Link); | |
| } else { | |
| InsertTailList (Link, &Entry->Link); | |
| } | |
| } | |
| /** | |
| Remove old entry from memory map. | |
| @param[in] Entry Memory map entry to be removed. | |
| **/ | |
| VOID | |
| RemoveOldEntry ( | |
| IN MEMORY_MAP *Entry | |
| ) | |
| { | |
| RemoveEntryList (&Entry->Link); | |
| Entry->Link.ForwardLink = NULL; | |
| if (!Entry->FromStack) { | |
| InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link); | |
| } | |
| } | |
| /** | |
| Update SMM memory map entry. | |
| @param[in] Type The type of allocation to perform. | |
| @param[in] Memory The base of memory address. | |
| @param[in] NumberOfPages The number of pages to allocate. | |
| @param[in] AddRegion If this memory is new added region. | |
| **/ | |
| VOID | |
| ConvertSmmMemoryMapEntry ( | |
| IN EFI_MEMORY_TYPE Type, | |
| IN EFI_PHYSICAL_ADDRESS Memory, | |
| IN UINTN NumberOfPages, | |
| IN BOOLEAN AddRegion | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| MEMORY_MAP *Entry; | |
| MEMORY_MAP *NextEntry; | |
| LIST_ENTRY *NextLink; | |
| MEMORY_MAP *PreviousEntry; | |
| LIST_ENTRY *PreviousLink; | |
| EFI_PHYSICAL_ADDRESS Start; | |
| EFI_PHYSICAL_ADDRESS End; | |
| Start = Memory; | |
| End = Memory + EFI_PAGES_TO_SIZE (NumberOfPages) - 1; | |
| // | |
| // Exclude memory region | |
| // | |
| Link = gMemoryMap.ForwardLink; | |
| while (Link != &gMemoryMap) { | |
| Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
| Link = Link->ForwardLink; | |
| // | |
| // --------------------------------------------------- | |
| // | +----------+ +------+ +------+ +------+ | | |
| // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|--- | |
| // +----------+ ^ +------+ +------+ +------+ | |
| // | | |
| // +------+ | |
| // |EntryX| | |
| // +------+ | |
| // | |
| if (Entry->Start > End) { | |
| if ((Entry->Start == End + 1) && (Entry->Type == Type)) { | |
| Entry->Start = Start; | |
| return; | |
| } | |
| InsertNewEntry ( | |
| &Entry->Link, | |
| Start, | |
| End, | |
| Type, | |
| FALSE, | |
| AddRegion | |
| ); | |
| return; | |
| } | |
| if ((Entry->Start <= Start) && (Entry->End >= End)) { | |
| if (Entry->Type != Type) { | |
| if (Entry->Start < Start) { | |
| // | |
| // --------------------------------------------------- | |
| // | +----------+ +------+ +------+ +------+ | | |
| // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|--- | |
| // +----------+ +------+ ^ +------+ +------+ | |
| // | | |
| // +------+ | |
| // |EntryA| | |
| // +------+ | |
| // | |
| InsertNewEntry ( | |
| &Entry->Link, | |
| Entry->Start, | |
| Start - 1, | |
| Entry->Type, | |
| FALSE, | |
| AddRegion | |
| ); | |
| } | |
| if (Entry->End > End) { | |
| // | |
| // --------------------------------------------------- | |
| // | +----------+ +------+ +------+ +------+ | | |
| // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|--- | |
| // +----------+ +------+ +------+ ^ +------+ | |
| // | | |
| // +------+ | |
| // |EntryZ| | |
| // +------+ | |
| // | |
| InsertNewEntry ( | |
| &Entry->Link, | |
| End + 1, | |
| Entry->End, | |
| Entry->Type, | |
| TRUE, | |
| AddRegion | |
| ); | |
| } | |
| // | |
| // Update this node | |
| // | |
| Entry->Start = Start; | |
| Entry->End = End; | |
| Entry->Type = Type; | |
| // | |
| // Check adjacent | |
| // | |
| NextLink = Entry->Link.ForwardLink; | |
| if (NextLink != &gMemoryMap) { | |
| NextEntry = CR (NextLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
| // | |
| // --------------------------------------------------- | |
| // | +----------+ +------+ +-----------------+ | | |
| // ---|gMemoryMep|---|Entry1|---|EntryX Entry3|--- | |
| // +----------+ +------+ +-----------------+ | |
| // | |
| if ((Entry->Type == NextEntry->Type) && (Entry->End + 1 == NextEntry->Start)) { | |
| Entry->End = NextEntry->End; | |
| RemoveOldEntry (NextEntry); | |
| } | |
| } | |
| PreviousLink = Entry->Link.BackLink; | |
| if (PreviousLink != &gMemoryMap) { | |
| PreviousEntry = CR (PreviousLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
| // | |
| // --------------------------------------------------- | |
| // | +----------+ +-----------------+ +------+ | | |
| // ---|gMemoryMep|---|Entry1 EntryX|---|Entry3|--- | |
| // +----------+ +-----------------+ +------+ | |
| // | |
| if ((PreviousEntry->Type == Entry->Type) && (PreviousEntry->End + 1 == Entry->Start)) { | |
| PreviousEntry->End = Entry->End; | |
| RemoveOldEntry (Entry); | |
| } | |
| } | |
| } | |
| return; | |
| } | |
| } | |
| // | |
| // --------------------------------------------------- | |
| // | +----------+ +------+ +------+ +------+ | | |
| // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|--- | |
| // +----------+ +------+ +------+ +------+ ^ | |
| // | | |
| // +------+ | |
| // |EntryX| | |
| // +------+ | |
| // | |
| Link = gMemoryMap.BackLink; | |
| if (Link != &gMemoryMap) { | |
| Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
| if ((Entry->End + 1 == Start) && (Entry->Type == Type)) { | |
| Entry->End = End; | |
| return; | |
| } | |
| } | |
| InsertNewEntry ( | |
| &gMemoryMap, | |
| Start, | |
| End, | |
| Type, | |
| FALSE, | |
| AddRegion | |
| ); | |
| return; | |
| } | |
| /** | |
| Return the count of Smm memory map entry. | |
| @return The count of Smm memory map entry. | |
| **/ | |
| UINTN | |
| GetSmmMemoryMapEntryCount ( | |
| VOID | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| UINTN Count; | |
| Count = 0; | |
| Link = gMemoryMap.ForwardLink; | |
| while (Link != &gMemoryMap) { | |
| Link = Link->ForwardLink; | |
| Count++; | |
| } | |
| return Count; | |
| } | |
| /** | |
| Internal Function. Allocate n pages from given free page node. | |
| @param Pages The free page node. | |
| @param NumberOfPages Number of pages to be allocated. | |
| @param MaxAddress Request to allocate memory below this address. | |
| @return Memory address of allocated pages. | |
| **/ | |
| UINTN | |
| InternalAllocPagesOnOneNode ( | |
| IN OUT FREE_PAGE_LIST *Pages, | |
| IN UINTN NumberOfPages, | |
| IN UINTN MaxAddress | |
| ) | |
| { | |
| UINTN Top; | |
| UINTN Bottom; | |
| FREE_PAGE_LIST *Node; | |
| Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages); | |
| if (Top > Pages->NumberOfPages) { | |
| Top = Pages->NumberOfPages; | |
| } | |
| Bottom = Top - NumberOfPages; | |
| if (Top < Pages->NumberOfPages) { | |
| Node = (FREE_PAGE_LIST *)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top)); | |
| Node->NumberOfPages = Pages->NumberOfPages - Top; | |
| InsertHeadList (&Pages->Link, &Node->Link); | |
| } | |
| if (Bottom > 0) { | |
| Pages->NumberOfPages = Bottom; | |
| } else { | |
| RemoveEntryList (&Pages->Link); | |
| } | |
| return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom); | |
| } | |
| /** | |
| Internal Function. Allocate n pages from free page list below MaxAddress. | |
| @param FreePageList The free page node. | |
| @param NumberOfPages Number of pages to be allocated. | |
| @param MaxAddress Request to allocate memory below this address. | |
| @return Memory address of allocated pages. | |
| **/ | |
| UINTN | |
| InternalAllocMaxAddress ( | |
| IN OUT LIST_ENTRY *FreePageList, | |
| IN UINTN NumberOfPages, | |
| IN UINTN MaxAddress | |
| ) | |
| { | |
| LIST_ENTRY *Node; | |
| FREE_PAGE_LIST *Pages; | |
| for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) { | |
| Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); | |
| if ((Pages->NumberOfPages >= NumberOfPages) && | |
| ((UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress)) | |
| { | |
| return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress); | |
| } | |
| } | |
| return (UINTN)(-1); | |
| } | |
| /** | |
| Internal Function. Allocate n pages from free page list at given address. | |
| @param FreePageList The free page node. | |
| @param NumberOfPages Number of pages to be allocated. | |
| @param MaxAddress Request to allocate memory below this address. | |
| @return Memory address of allocated pages. | |
| **/ | |
| UINTN | |
| InternalAllocAddress ( | |
| IN OUT LIST_ENTRY *FreePageList, | |
| IN UINTN NumberOfPages, | |
| IN UINTN Address | |
| ) | |
| { | |
| UINTN EndAddress; | |
| LIST_ENTRY *Node; | |
| FREE_PAGE_LIST *Pages; | |
| if ((Address & EFI_PAGE_MASK) != 0) { | |
| return ~Address; | |
| } | |
| EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages); | |
| for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) { | |
| Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); | |
| if ((UINTN)Pages <= Address) { | |
| if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) { | |
| break; | |
| } | |
| return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress); | |
| } | |
| } | |
| return ~Address; | |
| } | |
| /** | |
| Allocates pages from the memory map. | |
| @param[in] Type The type of allocation to perform. | |
| @param[in] MemoryType The type of memory to turn the allocated pages | |
| into. | |
| @param[in] NumberOfPages The number of pages to allocate. | |
| @param[out] Memory A pointer to receive the base allocated memory | |
| address. | |
| @param[in] AddRegion If this memory is new added region. | |
| @param[in] NeedGuard Flag to indicate Guard page is needed | |
| or not | |
| @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 | |
| SmmInternalAllocatePagesEx ( | |
| IN EFI_ALLOCATE_TYPE Type, | |
| IN EFI_MEMORY_TYPE MemoryType, | |
| IN UINTN NumberOfPages, | |
| OUT EFI_PHYSICAL_ADDRESS *Memory, | |
| IN BOOLEAN AddRegion, | |
| IN BOOLEAN NeedGuard | |
| ) | |
| { | |
| UINTN RequestedAddress; | |
| if ((MemoryType != EfiRuntimeServicesCode) && | |
| (MemoryType != EfiRuntimeServicesData)) | |
| { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| // | |
| // We don't track memory type in SMM | |
| // | |
| RequestedAddress = (UINTN)*Memory; | |
| switch (Type) { | |
| case AllocateAnyPages: | |
| RequestedAddress = (UINTN)(-1); | |
| case AllocateMaxAddress: | |
| if (NeedGuard) { | |
| *Memory = InternalAllocMaxAddressWithGuard ( | |
| &mSmmMemoryMap, | |
| NumberOfPages, | |
| RequestedAddress, | |
| MemoryType | |
| ); | |
| if (*Memory == (UINTN)-1) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } else { | |
| ASSERT (VerifyMemoryGuard (*Memory, NumberOfPages) == TRUE); | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| *Memory = InternalAllocMaxAddress ( | |
| &mSmmMemoryMap, | |
| NumberOfPages, | |
| RequestedAddress | |
| ); | |
| if (*Memory == (UINTN)-1) { | |
| return EFI_OUT_OF_RESOURCES; | |
| } | |
| break; | |
| case AllocateAddress: | |
| *Memory = InternalAllocAddress ( | |
| &mSmmMemoryMap, | |
| NumberOfPages, | |
| RequestedAddress | |
| ); | |
| if (*Memory != RequestedAddress) { | |
| return EFI_NOT_FOUND; | |
| } | |
| break; | |
| default: | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| // | |
| // Update SmmMemoryMap here. | |
| // | |
| ConvertSmmMemoryMapEntry (MemoryType, *Memory, NumberOfPages, AddRegion); | |
| if (!AddRegion) { | |
| CoreFreeMemoryMapStack (); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Allocates pages from the memory map. | |
| @param[in] Type The type of allocation to perform. | |
| @param[in] MemoryType The type of memory to turn the allocated pages | |
| into. | |
| @param[in] NumberOfPages The number of pages to allocate. | |
| @param[out] Memory A pointer to receive the base allocated memory | |
| address. | |
| @param[in] NeedGuard Flag to indicate Guard page is needed | |
| or not | |
| @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 | |
| SmmInternalAllocatePages ( | |
| IN EFI_ALLOCATE_TYPE Type, | |
| IN EFI_MEMORY_TYPE MemoryType, | |
| IN UINTN NumberOfPages, | |
| OUT EFI_PHYSICAL_ADDRESS *Memory, | |
| IN BOOLEAN NeedGuard | |
| ) | |
| { | |
| return SmmInternalAllocatePagesEx ( | |
| Type, | |
| MemoryType, | |
| NumberOfPages, | |
| Memory, | |
| FALSE, | |
| 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. | |
| @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 | |
| SmmAllocatePages ( | |
| 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); | |
| Status = SmmInternalAllocatePages ( | |
| Type, | |
| MemoryType, | |
| NumberOfPages, | |
| Memory, | |
| NeedGuard | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| SmmCoreUpdateProfile ( | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0), | |
| MemoryProfileActionAllocatePages, | |
| MemoryType, | |
| EFI_PAGES_TO_SIZE (NumberOfPages), | |
| (VOID *)(UINTN)*Memory, | |
| NULL | |
| ); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Internal Function. Merge two adjacent nodes. | |
| @param First The first of two nodes to merge. | |
| @return Pointer to node after merge (if success) or pointer to next node (if fail). | |
| **/ | |
| FREE_PAGE_LIST * | |
| InternalMergeNodes ( | |
| IN FREE_PAGE_LIST *First | |
| ) | |
| { | |
| FREE_PAGE_LIST *Next; | |
| Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link); | |
| ASSERT ( | |
| TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages | |
| ); | |
| if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) { | |
| First->NumberOfPages += Next->NumberOfPages; | |
| RemoveEntryList (&Next->Link); | |
| Next = First; | |
| } | |
| return Next; | |
| } | |
| /** | |
| Frees previous allocated pages. | |
| @param[in] Memory Base address of memory being freed. | |
| @param[in] NumberOfPages The number of pages to free. | |
| @param[in] AddRegion If this memory is new added region. | |
| @retval EFI_NOT_FOUND Could not find the entry that covers the range. | |
| @retval EFI_INVALID_PARAMETER Address not aligned, Address is zero or NumberOfPages is zero. | |
| @return EFI_SUCCESS Pages successfully freed. | |
| **/ | |
| EFI_STATUS | |
| SmmInternalFreePagesEx ( | |
| IN EFI_PHYSICAL_ADDRESS Memory, | |
| IN UINTN NumberOfPages, | |
| IN BOOLEAN AddRegion | |
| ) | |
| { | |
| LIST_ENTRY *Node; | |
| FREE_PAGE_LIST *Pages; | |
| if (((Memory & EFI_PAGE_MASK) != 0) || (Memory == 0) || (NumberOfPages == 0)) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Pages = NULL; | |
| Node = mSmmMemoryMap.ForwardLink; | |
| while (Node != &mSmmMemoryMap) { | |
| Pages = BASE_CR (Node, FREE_PAGE_LIST, Link); | |
| if (Memory < (UINTN)Pages) { | |
| break; | |
| } | |
| Node = Node->ForwardLink; | |
| } | |
| if ((Node != &mSmmMemoryMap) && | |
| (Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages)) | |
| { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| if (Node->BackLink != &mSmmMemoryMap) { | |
| Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link); | |
| if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| } | |
| Pages = (FREE_PAGE_LIST *)(UINTN)Memory; | |
| Pages->NumberOfPages = NumberOfPages; | |
| InsertTailList (Node, &Pages->Link); | |
| if (Pages->Link.BackLink != &mSmmMemoryMap) { | |
| Pages = InternalMergeNodes ( | |
| BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link) | |
| ); | |
| } | |
| if (Node != &mSmmMemoryMap) { | |
| InternalMergeNodes (Pages); | |
| } | |
| // | |
| // Update SmmMemoryMap here. | |
| // | |
| ConvertSmmMemoryMapEntry (EfiConventionalMemory, Memory, NumberOfPages, AddRegion); | |
| if (!AddRegion) { | |
| CoreFreeMemoryMapStack (); | |
| } | |
| return EFI_SUCCESS; | |
| } | |
| /** | |
| Frees previous allocated pages. | |
| @param[in] Memory Base address of memory being freed. | |
| @param[in] NumberOfPages The number of pages to free. | |
| @param[in] IsGuarded Is the memory to free guarded or not. | |
| @retval EFI_NOT_FOUND Could not find the entry that covers the range. | |
| @retval EFI_INVALID_PARAMETER Address not aligned, Address is zero or NumberOfPages is zero. | |
| @return EFI_SUCCESS Pages successfully freed. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SmmInternalFreePages ( | |
| IN EFI_PHYSICAL_ADDRESS Memory, | |
| IN UINTN NumberOfPages, | |
| IN BOOLEAN IsGuarded | |
| ) | |
| { | |
| if (IsGuarded) { | |
| return SmmInternalFreePagesExWithGuard (Memory, NumberOfPages, FALSE); | |
| } | |
| return SmmInternalFreePagesEx (Memory, NumberOfPages, FALSE); | |
| } | |
| /** | |
| Check whether the input range is in memory map. | |
| @param Memory Base address of memory being inputed. | |
| @param NumberOfPages The number of pages. | |
| @retval TRUE In memory map. | |
| @retval FALSE Not in memory map. | |
| **/ | |
| BOOLEAN | |
| InMemMap ( | |
| IN EFI_PHYSICAL_ADDRESS Memory, | |
| IN UINTN NumberOfPages | |
| ) | |
| { | |
| LIST_ENTRY *Link; | |
| MEMORY_MAP *Entry; | |
| EFI_PHYSICAL_ADDRESS Last; | |
| Last = Memory + EFI_PAGES_TO_SIZE (NumberOfPages) - 1; | |
| Link = gMemoryMap.ForwardLink; | |
| while (Link != &gMemoryMap) { | |
| Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
| Link = Link->ForwardLink; | |
| if ((Entry->Start <= Memory) && (Entry->End >= Last)) { | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| /** | |
| 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, Address is zero or NumberOfPages is zero. | |
| @return EFI_SUCCESS Pages successfully freed. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| SmmFreePages ( | |
| IN EFI_PHYSICAL_ADDRESS Memory, | |
| IN UINTN NumberOfPages | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| BOOLEAN IsGuarded; | |
| if (!InMemMap (Memory, NumberOfPages)) { | |
| return EFI_NOT_FOUND; | |
| } | |
| IsGuarded = IsHeapGuardEnabled () && IsMemoryGuarded (Memory); | |
| Status = SmmInternalFreePages (Memory, NumberOfPages, IsGuarded); | |
| if (!EFI_ERROR (Status)) { | |
| SmmCoreUpdateProfile ( | |
| (EFI_PHYSICAL_ADDRESS)(UINTN)RETURN_ADDRESS (0), | |
| MemoryProfileActionFreePages, | |
| EfiMaxMemoryType, | |
| EFI_PAGES_TO_SIZE (NumberOfPages), | |
| (VOID *)(UINTN)Memory, | |
| NULL | |
| ); | |
| } | |
| return Status; | |
| } | |
| /** | |
| Add free SMRAM region for use by memory service. | |
| @param MemBase Base address of memory region. | |
| @param MemLength Length of the memory region. | |
| @param Type Memory type. | |
| @param Attributes Memory region state. | |
| **/ | |
| VOID | |
| SmmAddMemoryRegion ( | |
| IN EFI_PHYSICAL_ADDRESS MemBase, | |
| IN UINT64 MemLength, | |
| IN EFI_MEMORY_TYPE Type, | |
| IN UINT64 Attributes | |
| ) | |
| { | |
| UINTN AlignedMemBase; | |
| // | |
| // Add EfiRuntimeServicesData for memory regions that is already allocated, needs testing, or needs ECC initialization | |
| // | |
| if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) { | |
| Type = EfiRuntimeServicesData; | |
| } else { | |
| Type = EfiConventionalMemory; | |
| } | |
| DEBUG ((DEBUG_INFO, "SmmAddMemoryRegion\n")); | |
| DEBUG ((DEBUG_INFO, " MemBase - 0x%lx\n", MemBase)); | |
| DEBUG ((DEBUG_INFO, " MemLength - 0x%lx\n", MemLength)); | |
| DEBUG ((DEBUG_INFO, " Type - 0x%x\n", Type)); | |
| DEBUG ((DEBUG_INFO, " Attributes - 0x%lx\n", Attributes)); | |
| // | |
| // Align range on an EFI_PAGE_SIZE boundary | |
| // | |
| AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK; | |
| MemLength -= AlignedMemBase - MemBase; | |
| if (Type == EfiConventionalMemory) { | |
| SmmInternalFreePagesEx (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE); | |
| } else { | |
| ConvertSmmMemoryMapEntry (EfiRuntimeServicesData, AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE); | |
| } | |
| CoreFreeMemoryMapStack (); | |
| } | |
| /** | |
| 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[in, out] 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[in, out] MemoryMap A pointer to the buffer in which firmware places | |
| the current memory map. | |
| @param[out] MapKey A pointer to the location in which firmware | |
| returns the key for the current memory map. | |
| @param[out] DescriptorSize A pointer to the location in which firmware | |
| returns the size, in bytes, of an individual | |
| EFI_MEMORY_DESCRIPTOR. | |
| @param[out] 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 | |
| SmmCoreGetMemoryMap ( | |
| IN OUT UINTN *MemoryMapSize, | |
| IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, | |
| OUT UINTN *MapKey, | |
| OUT UINTN *DescriptorSize, | |
| OUT UINT32 *DescriptorVersion | |
| ) | |
| { | |
| UINTN Count; | |
| LIST_ENTRY *Link; | |
| MEMORY_MAP *Entry; | |
| UINTN Size; | |
| UINTN BufferSize; | |
| 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; | |
| } | |
| Count = GetSmmMemoryMapEntryCount (); | |
| BufferSize = Size * Count; | |
| if (*MemoryMapSize < BufferSize) { | |
| *MemoryMapSize = BufferSize; | |
| return EFI_BUFFER_TOO_SMALL; | |
| } | |
| *MemoryMapSize = BufferSize; | |
| if (MemoryMap == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| ZeroMem (MemoryMap, BufferSize); | |
| Link = gMemoryMap.ForwardLink; | |
| while (Link != &gMemoryMap) { | |
| Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE); | |
| Link = Link->ForwardLink; | |
| MemoryMap->Type = Entry->Type; | |
| MemoryMap->PhysicalStart = Entry->Start; | |
| MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT); | |
| MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size); | |
| } | |
| return EFI_SUCCESS; | |
| } |