]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c
UefiCpuPkg/PiSmmCpu: Add Shadow Stack Support for X86 SMM.
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / SmmCpuMemoryManagement.c
index 85e06b2e6a0d08064754bf11430e0dbf8d0bfdf9..48f5f954cbc9b03beed8a9661f6090ab74cc1438 100644 (file)
@@ -1,6 +1,6 @@
 /** @file\r
 \r
-Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
 This program and the accompanying materials\r
 are licensed and made available under the terms and conditions of the BSD License\r
 which accompanies this distribution.  The full text of the license may be found at\r
@@ -13,8 +13,12 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 \r
 #include "PiSmmCpuDxeSmm.h"\r
 \r
-#define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
-  ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))\r
+//\r
+// attributes for reserved memory before it is promoted to system memory\r
+//\r
+#define EFI_MEMORY_PRESENT      0x0100000000000000ULL\r
+#define EFI_MEMORY_INITIALIZED  0x0200000000000000ULL\r
+#define EFI_MEMORY_TESTED       0x0400000000000000ULL\r
 \r
 #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \\r
   ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))\r
@@ -23,12 +27,34 @@ EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap;
 UINTN                 mUefiMemoryMapSize;\r
 UINTN                 mUefiDescriptorSize;\r
 \r
+EFI_GCD_MEMORY_SPACE_DESCRIPTOR   *mGcdMemSpace       = NULL;\r
+UINTN                             mGcdMemNumberOfDesc = 0;\r
+\r
+EFI_MEMORY_ATTRIBUTES_TABLE  *mUefiMemoryAttributesTable = NULL;\r
+\r
 PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {\r
   {Page4K,  SIZE_4KB, PAGING_4K_ADDRESS_MASK_64},\r
   {Page2M,  SIZE_2MB, PAGING_2M_ADDRESS_MASK_64},\r
   {Page1G,  SIZE_1GB, PAGING_1G_ADDRESS_MASK_64},\r
 };\r
 \r
+UINTN  mInternalGr3;\r
+\r
+/**\r
+  Set the internal page table base address.\r
+  If it is non zero, further MemoryAttribute modification will be on this page table.\r
+  If it is zero, further MemoryAttribute modification will be on real page table.\r
+\r
+  @param Cr3 page table base.\r
+**/\r
+VOID\r
+SetPageTableBase (\r
+  IN UINTN   Cr3\r
+  )\r
+{\r
+  mInternalGr3 = Cr3;\r
+}\r
+\r
 /**\r
   Return page table base.\r
 \r
@@ -39,6 +65,9 @@ GetPageTableBase (
   VOID\r
   )\r
 {\r
+  if (mInternalGr3 != 0) {\r
+    return mInternalGr3;\r
+  }\r
   return (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);\r
 }\r
 \r
@@ -211,6 +240,17 @@ ConvertPageEntryAttribute (
   if ((Attributes & EFI_MEMORY_RO) != 0) {\r
     if (IsSet) {\r
       NewPageEntry &= ~(UINT64)IA32_PG_RW;\r
+      if (mInternalGr3 != 0) {\r
+        // Environment setup\r
+        // ReadOnly page need set Dirty bit for shadow stack\r
+        NewPageEntry |= IA32_PG_D;\r
+        // Clear user bit for supervisor shadow stack\r
+        NewPageEntry &= ~(UINT64)IA32_PG_U;\r
+      } else {\r
+        // Runtime update\r
+        // Clear dirty bit for non shadow stack, to protect RO page.\r
+        NewPageEntry &= ~(UINT64)IA32_PG_D;\r
+      }\r
     } else {\r
       NewPageEntry |= IA32_PG_RW;\r
     }\r
@@ -652,7 +692,59 @@ SmmClearMemoryAttributes (
   return SmmClearMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);\r
 }\r
 \r
+/**\r
+  Set ShadowStack memory.\r
 \r
+  @param[in]  Cr3              The page table base address.\r
+  @param[in]  BaseAddress      The physical address that is the start address of a memory region.\r
+  @param[in]  Length           The size in bytes of the memory region.\r
+\r
+  @retval EFI_SUCCESS           The shadow stack memory is set.\r
+**/\r
+EFI_STATUS\r
+SetShadowStack (\r
+  IN  UINTN                                      Cr3,\r
+  IN  EFI_PHYSICAL_ADDRESS                       BaseAddress,\r
+  IN  UINT64                                     Length\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  SetPageTableBase (Cr3);\r
+\r
+  Status = SmmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO);\r
+\r
+  SetPageTableBase (0);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Set not present memory.\r
+\r
+  @param[in]  Cr3              The page table base address.\r
+  @param[in]  BaseAddress      The physical address that is the start address of a memory region.\r
+  @param[in]  Length           The size in bytes of the memory region.\r
+\r
+  @retval EFI_SUCCESS           The not present memory is set.\r
+**/\r
+EFI_STATUS\r
+SetNotPresentPage (\r
+  IN  UINTN                                      Cr3,\r
+  IN  EFI_PHYSICAL_ADDRESS                       BaseAddress,\r
+  IN  UINT64                                     Length\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  SetPageTableBase (Cr3);\r
+\r
+  Status = SmmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RP);\r
+\r
+  SetPageTableBase (0);\r
+\r
+  return Status;\r
+}\r
 \r
 /**\r
   Retrieves a pointer to the system configuration table from the SMM System Table\r
@@ -1022,6 +1114,80 @@ MergeMemoryMapForNotPresentEntry (
   return ;\r
 }\r
 \r
+/**\r
+  This function caches the GCD memory map information.\r
+**/\r
+VOID\r
+GetGcdMemoryMap (\r
+  VOID\r
+  )\r
+{\r
+  UINTN                            NumberOfDescriptors;\r
+  EFI_GCD_MEMORY_SPACE_DESCRIPTOR  *MemSpaceMap;\r
+  EFI_STATUS                       Status;\r
+  UINTN                            Index;\r
+\r
+  Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemSpaceMap);\r
+  if (EFI_ERROR (Status)) {\r
+    return ;\r
+  }\r
+\r
+  mGcdMemNumberOfDesc = 0;\r
+  for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
+    if (MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved &&\r
+        (MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==\r
+          (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)\r
+          ) {\r
+      mGcdMemNumberOfDesc++;\r
+    }\r
+  }\r
+\r
+  mGcdMemSpace = AllocateZeroPool (mGcdMemNumberOfDesc * sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR));\r
+  ASSERT (mGcdMemSpace != NULL);\r
+  if (mGcdMemSpace == NULL) {\r
+    mGcdMemNumberOfDesc = 0;\r
+    gBS->FreePool (MemSpaceMap);\r
+    return ;\r
+  }\r
+\r
+  mGcdMemNumberOfDesc = 0;\r
+  for (Index = 0; Index < NumberOfDescriptors; Index++) {\r
+    if (MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved &&\r
+        (MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==\r
+          (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)\r
+          ) {\r
+      CopyMem (\r
+        &mGcdMemSpace[mGcdMemNumberOfDesc],\r
+        &MemSpaceMap[Index],\r
+        sizeof(EFI_GCD_MEMORY_SPACE_DESCRIPTOR)\r
+        );\r
+      mGcdMemNumberOfDesc++;\r
+    }\r
+  }\r
+\r
+  gBS->FreePool (MemSpaceMap);\r
+}\r
+\r
+/**\r
+  Get UEFI MemoryAttributesTable.\r
+**/\r
+VOID\r
+GetUefiMemoryAttributesTable (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS                   Status;\r
+  EFI_MEMORY_ATTRIBUTES_TABLE  *MemoryAttributesTable;\r
+  UINTN                        MemoryAttributesTableSize;\r
+\r
+  Status = EfiGetSystemConfigurationTable (&gEfiMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);\r
+  if (!EFI_ERROR (Status) && (MemoryAttributesTable != NULL)) {\r
+    MemoryAttributesTableSize = sizeof(EFI_MEMORY_ATTRIBUTES_TABLE) + MemoryAttributesTable->DescriptorSize * MemoryAttributesTable->NumberOfEntries;\r
+    mUefiMemoryAttributesTable = AllocateCopyPool (MemoryAttributesTableSize, MemoryAttributesTable);\r
+    ASSERT (mUefiMemoryAttributesTable != NULL);\r
+  }\r
+}\r
+\r
 /**\r
   This function caches the UEFI memory map information.\r
 **/\r
@@ -1081,6 +1247,16 @@ GetUefiMemoryMap (
   ASSERT (mUefiMemoryMap != NULL);\r
 \r
   gBS->FreePool (MemoryMap);\r
+\r
+  //\r
+  // Get additional information from GCD memory map.\r
+  //\r
+  GetGcdMemoryMap ();\r
+\r
+  //\r
+  // Get UEFI memory attributes table.\r
+  //\r
+  GetUefiMemoryAttributesTable ();\r
 }\r
 \r
 /**\r
@@ -1099,36 +1275,85 @@ SetUefiMemMapAttributes (
   EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
   UINTN                 MemoryMapEntryCount;\r
   UINTN                 Index;\r
+  EFI_MEMORY_DESCRIPTOR *Entry;\r
 \r
   DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));\r
 \r
-  if (mUefiMemoryMap == NULL) {\r
-    DEBUG ((DEBUG_INFO, "UefiMemoryMap - NULL\n"));\r
-    return ;\r
+  if (mUefiMemoryMap != NULL) {\r
+    MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
+    MemoryMap = mUefiMemoryMap;\r
+    for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
+      if (IsUefiPageNotPresent(MemoryMap)) {\r
+        Status = SmmSetMemoryAttributes (\r
+                   MemoryMap->PhysicalStart,\r
+                   EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
+                   EFI_MEMORY_RP\r
+                   );\r
+        DEBUG ((\r
+          DEBUG_INFO,\r
+          "UefiMemory protection: 0x%lx - 0x%lx %r\n",\r
+          MemoryMap->PhysicalStart,\r
+          MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
+          Status\r
+          ));\r
+      }\r
+      MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);\r
+    }\r
   }\r
+  //\r
+  // Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().\r
+  //\r
 \r
-  MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
-  MemoryMap = mUefiMemoryMap;\r
-  for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
-    if (IsUefiPageNotPresent(MemoryMap)) {\r
+  //\r
+  // Set untested memory as not present.\r
+  //\r
+  if (mGcdMemSpace != NULL) {\r
+    for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {\r
       Status = SmmSetMemoryAttributes (\r
-                 MemoryMap->PhysicalStart,\r
-                 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
+                 mGcdMemSpace[Index].BaseAddress,\r
+                 mGcdMemSpace[Index].Length,\r
                  EFI_MEMORY_RP\r
                  );\r
       DEBUG ((\r
         DEBUG_INFO,\r
-        "UefiMemory protection: 0x%lx - 0x%lx %r\n",\r
-        MemoryMap->PhysicalStart,\r
-        MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),\r
+        "GcdMemory protection: 0x%lx - 0x%lx %r\n",\r
+        mGcdMemSpace[Index].BaseAddress,\r
+        mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length,\r
         Status\r
         ));\r
     }\r
-    MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);\r
   }\r
+  //\r
+  // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().\r
+  //\r
 \r
   //\r
-  // Do free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().\r
+  // Set UEFI runtime memory with EFI_MEMORY_RO as not present.\r
+  //\r
+  if (mUefiMemoryAttributesTable != NULL) {\r
+    Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);\r
+    for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {\r
+      if (Entry->Type == EfiRuntimeServicesCode || Entry->Type == EfiRuntimeServicesData) {\r
+        if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {\r
+          Status = SmmSetMemoryAttributes (\r
+                     Entry->PhysicalStart,\r
+                     EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages),\r
+                     EFI_MEMORY_RP\r
+                     );\r
+          DEBUG ((\r
+            DEBUG_INFO,\r
+            "UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n",\r
+            Entry->PhysicalStart,\r
+            Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages),\r
+            Status\r
+            ));\r
+        }\r
+      }\r
+      Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);\r
+    }\r
+  }\r
+  //\r
+  // Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().\r
   //\r
 }\r
 \r
@@ -1148,21 +1373,44 @@ IsSmmCommBufferForbiddenAddress (
   EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
   UINTN                 MemoryMapEntryCount;\r
   UINTN                 Index;\r
-\r
-  if (mUefiMemoryMap == NULL) {\r
-    return FALSE;\r
+  EFI_MEMORY_DESCRIPTOR *Entry;\r
+\r
+  if (mUefiMemoryMap != NULL) {\r
+    MemoryMap = mUefiMemoryMap;\r
+    MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
+    for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
+      if (IsUefiPageNotPresent (MemoryMap)) {\r
+        if ((Address >= MemoryMap->PhysicalStart) &&\r
+            (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages)) ) {\r
+          return TRUE;\r
+        }\r
+      }\r
+      MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);\r
+    }\r
   }\r
 \r
-  MemoryMap = mUefiMemoryMap;\r
-  MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;\r
-  for (Index = 0; Index < MemoryMapEntryCount; Index++) {\r
-    if (IsUefiPageNotPresent (MemoryMap)) {\r
-      if ((Address >= MemoryMap->PhysicalStart) &&\r
-          (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages)) ) {\r
+  if (mGcdMemSpace != NULL) {\r
+    for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {\r
+      if ((Address >= mGcdMemSpace[Index].BaseAddress) &&\r
+          (Address < mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length) ) {\r
         return TRUE;\r
       }\r
     }\r
-    MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);\r
+  }\r
+\r
+  if (mUefiMemoryAttributesTable != NULL) {\r
+    Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);\r
+    for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {\r
+      if (Entry->Type == EfiRuntimeServicesCode || Entry->Type == EfiRuntimeServicesData) {\r
+        if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {\r
+          if ((Address >= Entry->PhysicalStart) &&\r
+              (Address < Entry->PhysicalStart + LShiftU64 (Entry->NumberOfPages, EFI_PAGE_SHIFT))) {\r
+            return TRUE;\r
+          }\r
+          Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);\r
+        }\r
+      }\r
+    }\r
   }\r
   return FALSE;\r
 }\r