]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Core/PiSmmCore/Page.c
Add PI SMM IPL and PI SMM Core
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Page.c
diff --git a/MdeModulePkg/Core/PiSmmCore/Page.c b/MdeModulePkg/Core/PiSmmCore/Page.c
new file mode 100644 (file)
index 0000000..ec4dd4f
--- /dev/null
@@ -0,0 +1,318 @@
+/** @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