--- /dev/null
+/** @file\r
+ SMM Memory page management functions.\r
+\r
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>\r
+ This program and the accompanying materials are licensed and made available \r
+ under the terms and conditions of the BSD License which accompanies this \r
+ distribution. The full text of the license may be found at \r
+ http://opensource.org/licenses/bsd-license.php \r
+\r
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, \r
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. \r
+\r
+**/\r
+\r
+#include "PiSmmCore.h"\r
+\r
+#define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT)\r
+\r
+typedef struct {\r
+ LIST_ENTRY Link;\r
+ UINTN NumberOfPages;\r
+} FREE_PAGE_LIST;\r
+\r
+LIST_ENTRY mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap);\r
+\r
+/**\r
+ Internal Function. Allocate n pages from given free page node.\r
+\r
+ @param Pages The free page node.\r
+ @param NumberOfPages Number of pages to be allocated.\r
+ @param MaxAddress Request to allocate memory below this address.\r
+\r
+ @return Memory address of allocated pages.\r
+\r
+**/\r
+UINTN\r
+InternalAllocPagesOnOneNode (\r
+ IN OUT FREE_PAGE_LIST *Pages,\r
+ IN UINTN NumberOfPages,\r
+ IN UINTN MaxAddress\r
+ )\r
+{\r
+ UINTN Top;\r
+ UINTN Bottom;\r
+ FREE_PAGE_LIST *Node;\r
+\r
+ Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages);\r
+ if (Top > Pages->NumberOfPages) {\r
+ Top = Pages->NumberOfPages;\r
+ }\r
+ Bottom = Top - NumberOfPages;\r
+\r
+ if (Top < Pages->NumberOfPages) {\r
+ Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top));\r
+ Node->NumberOfPages = Pages->NumberOfPages - Top;\r
+ InsertHeadList (&Pages->Link, &Node->Link);\r
+ }\r
+\r
+ if (Bottom > 0) {\r
+ Pages->NumberOfPages = Bottom;\r
+ } else {\r
+ RemoveEntryList (&Pages->Link);\r
+ }\r
+\r
+ return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom);\r
+}\r
+\r
+/**\r
+ Internal Function. Allocate n pages from free page list below MaxAddress.\r
+\r
+ @param FreePageList The free page node.\r
+ @param NumberOfPages Number of pages to be allocated.\r
+ @param MaxAddress Request to allocate memory below this address.\r
+\r
+ @return Memory address of allocated pages.\r
+\r
+**/\r
+UINTN\r
+InternalAllocMaxAddress (\r
+ IN OUT LIST_ENTRY *FreePageList,\r
+ IN UINTN NumberOfPages,\r
+ IN UINTN MaxAddress\r
+ )\r
+{\r
+ LIST_ENTRY *Node;\r
+ FREE_PAGE_LIST *Pages;\r
+\r
+ for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) {\r
+ Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);\r
+ if (Pages->NumberOfPages >= NumberOfPages &&\r
+ (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) {\r
+ return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress);\r
+ }\r
+ }\r
+ return (UINTN)(-1);\r
+}\r
+\r
+/**\r
+ Internal Function. Allocate n pages from free page list at given address.\r
+\r
+ @param FreePageList The free page node.\r
+ @param NumberOfPages Number of pages to be allocated.\r
+ @param MaxAddress Request to allocate memory below this address.\r
+\r
+ @return Memory address of allocated pages.\r
+\r
+**/\r
+UINTN\r
+InternalAllocAddress (\r
+ IN OUT LIST_ENTRY *FreePageList,\r
+ IN UINTN NumberOfPages,\r
+ IN UINTN Address\r
+ )\r
+{\r
+ UINTN EndAddress;\r
+ LIST_ENTRY *Node;\r
+ FREE_PAGE_LIST *Pages;\r
+\r
+ if ((Address & EFI_PAGE_MASK) != 0) {\r
+ return ~Address;\r
+ }\r
+\r
+ EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages);\r
+ for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) {\r
+ Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);\r
+ if ((UINTN)Pages <= Address) {\r
+ if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) {\r
+ break;\r
+ }\r
+ return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress);\r
+ }\r
+ }\r
+ return ~Address;\r
+}\r
+\r
+/**\r
+ Allocates pages from the memory map.\r
+\r
+ @param Type The type of allocation to perform.\r
+ @param MemoryType The type of memory to turn the allocated pages\r
+ into.\r
+ @param NumberOfPages The number of pages to allocate.\r
+ @param Memory A pointer to receive the base allocated memory\r
+ address.\r
+\r
+ @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in spec.\r
+ @retval EFI_NOT_FOUND Could not allocate pages match the requirement.\r
+ @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.\r
+ @retval EFI_SUCCESS Pages successfully allocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmAllocatePages (\r
+ IN EFI_ALLOCATE_TYPE Type,\r
+ IN EFI_MEMORY_TYPE MemoryType,\r
+ IN UINTN NumberOfPages,\r
+ OUT EFI_PHYSICAL_ADDRESS *Memory\r
+ )\r
+{\r
+ UINTN RequestedAddress;\r
+\r
+ if (MemoryType != EfiRuntimeServicesCode &&\r
+ MemoryType != EfiRuntimeServicesData) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // We don't track memory type in SMM\r
+ //\r
+ RequestedAddress = (UINTN)*Memory;\r
+ switch (Type) {\r
+ case AllocateAnyPages:\r
+ RequestedAddress = (UINTN)(-1);\r
+ case AllocateMaxAddress:\r
+ *Memory = InternalAllocMaxAddress (\r
+ &mSmmMemoryMap,\r
+ NumberOfPages,\r
+ RequestedAddress\r
+ );\r
+ if (*Memory == (UINTN)-1) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ } \r
+ break;\r
+ case AllocateAddress:\r
+ *Memory = InternalAllocAddress (\r
+ &mSmmMemoryMap,\r
+ NumberOfPages,\r
+ RequestedAddress\r
+ );\r
+ if (*Memory != RequestedAddress) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ break;\r
+ default:\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Internal Function. Merge two adjacent nodes.\r
+\r
+ @param First The first of two nodes to merge.\r
+\r
+ @return Pointer to node after merge (if success) or pointer to next node (if fail).\r
+\r
+**/\r
+FREE_PAGE_LIST *\r
+InternalMergeNodes (\r
+ IN FREE_PAGE_LIST *First\r
+ )\r
+{\r
+ FREE_PAGE_LIST *Next;\r
+\r
+ Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);\r
+ ASSERT (\r
+ TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);\r
+\r
+ if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {\r
+ First->NumberOfPages += Next->NumberOfPages;\r
+ RemoveEntryList (&Next->Link);\r
+ Next = First;\r
+ }\r
+ return Next;\r
+}\r
+\r
+/**\r
+ Frees previous allocated pages.\r
+\r
+ @param Memory Base address of memory being freed.\r
+ @param NumberOfPages The number of pages to free.\r
+\r
+ @retval EFI_NOT_FOUND Could not find the entry that covers the range.\r
+ @retval EFI_INVALID_PARAMETER Address not aligned.\r
+ @return EFI_SUCCESS Pages successfully freed.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+SmmFreePages (\r
+ IN EFI_PHYSICAL_ADDRESS Memory,\r
+ IN UINTN NumberOfPages\r
+ )\r
+{\r
+ LIST_ENTRY *Node;\r
+ FREE_PAGE_LIST *Pages;\r
+\r
+ if ((Memory & EFI_PAGE_MASK) != 0) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ Pages = NULL;\r
+ Node = mSmmMemoryMap.ForwardLink;\r
+ while (Node != &mSmmMemoryMap) {\r
+ Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);\r
+ if (Memory < (UINTN)Pages) {\r
+ break;\r
+ }\r
+ Node = Node->ForwardLink;\r
+ }\r
+\r
+ if (Node != &mSmmMemoryMap &&\r
+ Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (Node->BackLink != &mSmmMemoryMap) {\r
+ Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);\r
+ if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+ }\r
+\r
+ Pages = (FREE_PAGE_LIST*)(UINTN)Memory;\r
+ Pages->NumberOfPages = NumberOfPages;\r
+ InsertTailList (Node, &Pages->Link);\r
+\r
+ if (Pages->Link.BackLink != &mSmmMemoryMap) {\r
+ Pages = InternalMergeNodes (\r
+ BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)\r
+ );\r
+ }\r
+\r
+ if (Node != &mSmmMemoryMap) {\r
+ InternalMergeNodes (Pages);\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ Add free SMRAM region for use by memory service.\r
+\r
+ @param MemBase Base address of memory region.\r
+ @param MemLength Length of the memory region.\r
+ @param Type Memory type.\r
+ @param Attributes Memory region state.\r
+\r
+**/\r
+VOID\r
+SmmAddMemoryRegion (\r
+ IN EFI_PHYSICAL_ADDRESS MemBase,\r
+ IN UINT64 MemLength,\r
+ IN EFI_MEMORY_TYPE Type,\r
+ IN UINT64 Attributes\r
+ )\r
+{\r
+ UINTN AlignedMemBase;\r
+\r
+ AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;\r
+ MemLength -= AlignedMemBase - MemBase;\r
+ SmmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength));\r
+}\r