summaryrefslogtreecommitdiff
path: root/MdeModulePkg/Core/PiSmmCore/Page.c
diff options
context:
space:
mode:
Diffstat (limited to 'MdeModulePkg/Core/PiSmmCore/Page.c')
-rw-r--r--MdeModulePkg/Core/PiSmmCore/Page.c2198
1 files changed, 1099 insertions, 1099 deletions
diff --git a/MdeModulePkg/Core/PiSmmCore/Page.c b/MdeModulePkg/Core/PiSmmCore/Page.c
index 255964c23a..9c2fc94456 100644
--- a/MdeModulePkg/Core/PiSmmCore/Page.c
+++ b/MdeModulePkg/Core/PiSmmCore/Page.c
@@ -1,1099 +1,1099 @@
-/** @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;
-}
+/** @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;
+}