]> 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 d841dd014c4bfb236874d440a800dc2d0604d12d..48f5f954cbc9b03beed8a9661f6090ab74cc1438 100644 (file)
@@ -1,6 +1,6 @@
 /** @file\r
 \r
-Copyright (c) 2016, 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,24 @@ 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
+\r
+EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap;\r
+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
@@ -22,6 +38,23 @@ PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = {
   {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
@@ -32,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
@@ -112,7 +148,7 @@ GetPageTableEntry (
       return NULL;\r
     }\r
 \r
-    L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64);\r
+    L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
   } else {\r
     L3PageTable = (UINT64 *)GetPageTableBase ();\r
   }\r
@@ -126,7 +162,7 @@ GetPageTableEntry (
     return &L3PageTable[Index3];\r
   }\r
 \r
-  L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64);\r
+  L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
   if (L2PageTable[Index2] == 0) {\r
     *PageAttribute = PageNone;\r
     return NULL;\r
@@ -138,7 +174,7 @@ GetPageTableEntry (
   }\r
 \r
   // 4k\r
-  L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64);\r
+  L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
   if ((L1PageTable[Index1] == 0) && (Address != 0)) {\r
     *PageAttribute = PageNone;\r
     return NULL;\r
@@ -204,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
@@ -297,9 +344,9 @@ SplitPage (
       }\r
       BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;\r
       for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
-        NewPageEntry[Index] = BaseAddress + SIZE_4KB * Index + ((*PageEntry) & PAGE_PROGATE_BITS);\r
+        NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);\r
       }\r
-      (*PageEntry) = (UINT64)(UINTN)NewPageEntry + PAGE_ATTRIBUTE_BITS;\r
+      (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
       return RETURN_SUCCESS;\r
     } else {\r
       return RETURN_UNSUPPORTED;\r
@@ -318,9 +365,9 @@ SplitPage (
       }\r
       BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;\r
       for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {\r
-        NewPageEntry[Index] = BaseAddress + SIZE_2MB * Index + IA32_PG_PS + ((*PageEntry) & PAGE_PROGATE_BITS);\r
+        NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | mAddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);\r
       }\r
-      (*PageEntry) = (UINT64)(UINTN)NewPageEntry + PAGE_ATTRIBUTE_BITS;\r
+      (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
       return RETURN_SUCCESS;\r
     } else {\r
       return RETURN_UNSUPPORTED;\r
@@ -373,6 +420,7 @@ ConvertMemoryPageAttributes (
   PAGE_ATTRIBUTE                    SplitAttribute;\r
   RETURN_STATUS                     Status;\r
   BOOLEAN                           IsEntryModified;\r
+  EFI_PHYSICAL_ADDRESS              MaximumSupportMemAddress;\r
 \r
   ASSERT (Attributes != 0);\r
   ASSERT ((Attributes & ~(EFI_MEMORY_RP | EFI_MEMORY_RO | EFI_MEMORY_XP)) == 0);\r
@@ -384,6 +432,17 @@ ConvertMemoryPageAttributes (
     return RETURN_INVALID_PARAMETER;\r
   }\r
 \r
+  MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);\r
+  if (BaseAddress > MaximumSupportMemAddress) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+  if (Length > MaximumSupportMemAddress) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+  if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {\r
+    return RETURN_UNSUPPORTED;\r
+  }\r
+\r
 //  DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));\r
 \r
   if (IsSplitted != NULL) {\r
@@ -533,12 +592,12 @@ SmmSetMemoryAttributesEx (
                                 BaseAddress and Length cannot be modified.\r
   @retval EFI_INVALID_PARAMETER Length is zero.\r
                                 Attributes specified an illegal combination of attributes that\r
-                                cannot be set together.\r
+                                cannot be cleared together.\r
   @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of\r
                                 the memory resource range.\r
   @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory\r
                                 resource range specified by BaseAddress and Length.\r
-                                The bit mask of attributes is not support for the memory resource\r
+                                The bit mask of attributes is not supported for the memory resource\r
                                 range specified by BaseAddress and Length.\r
 \r
 **/\r
@@ -585,7 +644,7 @@ SmmClearMemoryAttributesEx (
                                 the memory resource range.\r
   @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory\r
                                 resource range specified by BaseAddress and Length.\r
-                                The bit mask of attributes is not support for the memory resource\r
+                                The bit mask of attributes is not supported for the memory resource\r
                                 range specified by BaseAddress and Length.\r
 \r
 **/\r
@@ -613,12 +672,12 @@ SmmSetMemoryAttributes (
                                 BaseAddress and Length cannot be modified.\r
   @retval EFI_INVALID_PARAMETER Length is zero.\r
                                 Attributes specified an illegal combination of attributes that\r
-                                cannot be set together.\r
+                                cannot be cleared together.\r
   @retval EFI_OUT_OF_RESOURCES  There are not enough system resources to modify the attributes of\r
                                 the memory resource range.\r
   @retval EFI_UNSUPPORTED       The processor does not support one or more bytes of the memory\r
                                 resource range specified by BaseAddress and Length.\r
-                                The bit mask of attributes is not support for the memory resource\r
+                                The bit mask of attributes is not supported for the memory resource\r
                                 range specified by BaseAddress and Length.\r
 \r
 **/\r
@@ -633,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
@@ -684,7 +795,7 @@ PatchSmmSaveStateMap (
 \r
   TileCodeSize = GetSmiHandlerSize ();\r
   TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);\r
-  TileDataSize = sizeof (SMRAM_SAVE_STATE_MAP) + sizeof (PROCESSOR_SMM_DESCRIPTOR);\r
+  TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);\r
   TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);\r
   TileSize = TileDataSize + TileCodeSize - 1;\r
   TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);\r
@@ -767,11 +878,10 @@ PatchGdtIdtMap (
 \r
   BaseAddress = mGdtBuffer;\r
   Size = ALIGN_VALUE(mGdtBufferSize, SIZE_4KB);\r
-  SmmSetMemoryAttributes (\r
-    BaseAddress,\r
-    Size,\r
-    EFI_MEMORY_RO\r
-    );\r
+  //\r
+  // The range should have been set to RO\r
+  // if it is allocated with EfiRuntimeServicesCode.\r
+  //\r
   SmmSetMemoryAttributes (\r
     BaseAddress,\r
     Size,\r
@@ -785,11 +895,10 @@ PatchGdtIdtMap (
 \r
   BaseAddress = gcSmiIdtr.Base;\r
   Size = ALIGN_VALUE(gcSmiIdtr.Limit + 1, SIZE_4KB);\r
-  SmmSetMemoryAttributes (\r
-    BaseAddress,\r
-    Size,\r
-    EFI_MEMORY_RO\r
-    );\r
+  //\r
+  // The range should have been set to RO\r
+  // if it is allocated with EfiRuntimeServicesCode.\r
+  //\r
   SmmSetMemoryAttributes (\r
     BaseAddress,\r
     Size,\r
@@ -871,3 +980,597 @@ SetMemMapAttributes (
 \r
   return ;\r
 }\r
+\r
+/**\r
+  Sort memory map entries based upon PhysicalStart, from low to high.\r
+\r
+  @param  MemoryMap              A pointer to the buffer in which firmware places\r
+                                 the current memory map.\r
+  @param  MemoryMapSize          Size, in bytes, of the MemoryMap buffer.\r
+  @param  DescriptorSize         Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
+**/\r
+STATIC\r
+VOID\r
+SortMemoryMap (\r
+  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,\r
+  IN UINTN                      MemoryMapSize,\r
+  IN UINTN                      DescriptorSize\r
+  )\r
+{\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *NextMemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEnd;\r
+  EFI_MEMORY_DESCRIPTOR       TempMemoryMap;\r
+\r
+  MemoryMapEntry = MemoryMap;\r
+  NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);\r
+  while (MemoryMapEntry < MemoryMapEnd) {\r
+    while (NextMemoryMapEntry < MemoryMapEnd) {\r
+      if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {\r
+        CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+        CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+        CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+      }\r
+\r
+      NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
+    }\r
+\r
+    MemoryMapEntry      = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+    NextMemoryMapEntry  = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+  }\r
+}\r
+\r
+/**\r
+  Return if a UEFI memory page should be marked as not present in SMM page table.\r
+  If the memory map entries type is\r
+  EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,\r
+  EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE.\r
+  Or return FALSE.\r
+\r
+  @param[in]  MemoryMap              A pointer to the memory descriptor.\r
+\r
+  @return TRUE  The memory described will be marked as not present in SMM page table.\r
+  @return FALSE The memory described will not be marked as not present in SMM page table.\r
+**/\r
+BOOLEAN\r
+IsUefiPageNotPresent (\r
+  IN EFI_MEMORY_DESCRIPTOR  *MemoryMap\r
+  )\r
+{\r
+  switch (MemoryMap->Type) {\r
+  case EfiLoaderCode:\r
+  case EfiLoaderData:\r
+  case EfiBootServicesCode:\r
+  case EfiBootServicesData:\r
+  case EfiConventionalMemory:\r
+  case EfiUnusableMemory:\r
+  case EfiACPIReclaimMemory:\r
+    return TRUE;\r
+  default:\r
+    return FALSE;\r
+  }\r
+}\r
+\r
+/**\r
+  Merge continous memory map entries whose type is\r
+  EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,\r
+  EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by\r
+  these entries will be set as NOT present in SMM page table.\r
+\r
+  @param[in, out]  MemoryMap              A pointer to the buffer in which firmware places\r
+                                          the current memory map.\r
+  @param[in, out]  MemoryMapSize          A pointer to the size, in bytes, of the\r
+                                          MemoryMap buffer. On input, this is the size of\r
+                                          the current memory map.  On output,\r
+                                          it is the size of new memory map after merge.\r
+  @param[in]       DescriptorSize         Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.\r
+**/\r
+STATIC\r
+VOID\r
+MergeMemoryMapForNotPresentEntry (\r
+  IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,\r
+  IN OUT UINTN                  *MemoryMapSize,\r
+  IN UINTN                      DescriptorSize\r
+  )\r
+{\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *MemoryMapEnd;\r
+  UINT64                      MemoryBlockLength;\r
+  EFI_MEMORY_DESCRIPTOR       *NewMemoryMapEntry;\r
+  EFI_MEMORY_DESCRIPTOR       *NextMemoryMapEntry;\r
+\r
+  MemoryMapEntry = MemoryMap;\r
+  NewMemoryMapEntry = MemoryMap;\r
+  MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);\r
+  while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {\r
+    CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));\r
+    NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+\r
+    do {\r
+      MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));\r
+      if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&\r
+          IsUefiPageNotPresent(MemoryMapEntry) && IsUefiPageNotPresent(NextMemoryMapEntry) &&\r
+          ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {\r
+        MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
+        if (NewMemoryMapEntry != MemoryMapEntry) {\r
+          NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;\r
+        }\r
+\r
+        NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
+        continue;\r
+      } else {\r
+        MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);\r
+        break;\r
+      }\r
+    } while (TRUE);\r
+\r
+    MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);\r
+    NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);\r
+  }\r
+\r
+  *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;\r
+\r
+  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
+VOID\r
+GetUefiMemoryMap (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  UINTN                 MapKey;\r
+  UINT32                DescriptorVersion;\r
+  EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
+  UINTN                 UefiMemoryMapSize;\r
+\r
+  DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));\r
+\r
+  UefiMemoryMapSize = 0;\r
+  MemoryMap = NULL;\r
+  Status = gBS->GetMemoryMap (\r
+                  &UefiMemoryMapSize,\r
+                  MemoryMap,\r
+                  &MapKey,\r
+                  &mUefiDescriptorSize,\r
+                  &DescriptorVersion\r
+                  );\r
+  ASSERT (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+  do {\r
+    Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap);\r
+    ASSERT (MemoryMap != NULL);\r
+    if (MemoryMap == NULL) {\r
+      return ;\r
+    }\r
+\r
+    Status = gBS->GetMemoryMap (\r
+                    &UefiMemoryMapSize,\r
+                    MemoryMap,\r
+                    &MapKey,\r
+                    &mUefiDescriptorSize,\r
+                    &DescriptorVersion\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      gBS->FreePool (MemoryMap);\r
+      MemoryMap = NULL;\r
+    }\r
+  } while (Status == EFI_BUFFER_TOO_SMALL);\r
+\r
+  if (MemoryMap == NULL) {\r
+    return ;\r
+  }\r
+\r
+  SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);\r
+  MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);\r
+\r
+  mUefiMemoryMapSize = UefiMemoryMapSize;\r
+  mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);\r
+  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
+  This function sets UEFI memory attribute according to UEFI memory map.\r
+\r
+  The normal memory region is marked as not present, such as\r
+  EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,\r
+  EfiUnusableMemory, EfiACPIReclaimMemory.\r
+**/\r
+VOID\r
+SetUefiMemMapAttributes (\r
+  VOID\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  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
+    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
+  //\r
+  // Set untested memory as not present.\r
+  //\r
+  if (mGcdMemSpace != NULL) {\r
+    for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {\r
+      Status = SmmSetMemoryAttributes (\r
+                 mGcdMemSpace[Index].BaseAddress,\r
+                 mGcdMemSpace[Index].Length,\r
+                 EFI_MEMORY_RP\r
+                 );\r
+      DEBUG ((\r
+        DEBUG_INFO,\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
+  }\r
+  //\r
+  // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().\r
+  //\r
+\r
+  //\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
+/**\r
+  Return if the Address is forbidden as SMM communication buffer.\r
+\r
+  @param[in] Address the address to be checked\r
+\r
+  @return TRUE  The address is forbidden as SMM communication buffer.\r
+  @return FALSE The address is allowed as SMM communication buffer.\r
+**/\r
+BOOLEAN\r
+IsSmmCommBufferForbiddenAddress (\r
+  IN UINT64  Address\r
+  )\r
+{\r
+  EFI_MEMORY_DESCRIPTOR *MemoryMap;\r
+  UINTN                 MemoryMapEntryCount;\r
+  UINTN                 Index;\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
+  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
+  }\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
+\r
+/**\r
+  This function set given attributes of the memory region specified by\r
+  BaseAddress and Length.\r
+\r
+  @param  This              The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.\r
+  @param  BaseAddress       The physical address that is the start address of\r
+                            a memory region.\r
+  @param  Length            The size in bytes of the memory region.\r
+  @param  Attributes        The bit mask of attributes to set for the memory\r
+                            region.\r
+\r
+  @retval EFI_SUCCESS           The attributes were set for the memory region.\r
+  @retval EFI_INVALID_PARAMETER Length is zero.\r
+                                Attributes specified an illegal combination of\r
+                                attributes that cannot be set together.\r
+  @retval EFI_UNSUPPORTED       The processor does not support one or more\r
+                                bytes of the memory resource range specified\r
+                                by BaseAddress and Length.\r
+                                The bit mask of attributes is not supported for\r
+                                the memory resource range specified by\r
+                                BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EdkiiSmmSetMemoryAttributes (\r
+  IN  EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL   *This,\r
+  IN  EFI_PHYSICAL_ADDRESS                  BaseAddress,\r
+  IN  UINT64                                Length,\r
+  IN  UINT64                                Attributes\r
+  )\r
+{\r
+  return SmmSetMemoryAttributes (BaseAddress, Length, Attributes);\r
+}\r
+\r
+/**\r
+  This function clears given attributes of the memory region specified by\r
+  BaseAddress and Length.\r
+\r
+  @param  This              The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.\r
+  @param  BaseAddress       The physical address that is the start address of\r
+                            a memory region.\r
+  @param  Length            The size in bytes of the memory region.\r
+  @param  Attributes        The bit mask of attributes to clear for the memory\r
+                            region.\r
+\r
+  @retval EFI_SUCCESS           The attributes were cleared for the memory region.\r
+  @retval EFI_INVALID_PARAMETER Length is zero.\r
+                                Attributes specified an illegal combination of\r
+                                attributes that cannot be cleared together.\r
+  @retval EFI_UNSUPPORTED       The processor does not support one or more\r
+                                bytes of the memory resource range specified\r
+                                by BaseAddress and Length.\r
+                                The bit mask of attributes is not supported for\r
+                                the memory resource range specified by\r
+                                BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EdkiiSmmClearMemoryAttributes (\r
+  IN  EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL   *This,\r
+  IN  EFI_PHYSICAL_ADDRESS                  BaseAddress,\r
+  IN  UINT64                                Length,\r
+  IN  UINT64                                Attributes\r
+  )\r
+{\r
+  return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);\r
+}\r
+\r
+/**\r
+  This function retrieves the attributes of the memory region specified by\r
+  BaseAddress and Length. If different attributes are got from different part\r
+  of the memory region, EFI_NO_MAPPING will be returned.\r
+\r
+  @param  This              The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.\r
+  @param  BaseAddress       The physical address that is the start address of\r
+                            a memory region.\r
+  @param  Length            The size in bytes of the memory region.\r
+  @param  Attributes        Pointer to attributes returned.\r
+\r
+  @retval EFI_SUCCESS           The attributes got for the memory region.\r
+  @retval EFI_INVALID_PARAMETER Length is zero.\r
+                                Attributes is NULL.\r
+  @retval EFI_NO_MAPPING        Attributes are not consistent cross the memory\r
+                                region.\r
+  @retval EFI_UNSUPPORTED       The processor does not support one or more\r
+                                bytes of the memory resource range specified\r
+                                by BaseAddress and Length.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EdkiiSmmGetMemoryAttributes (\r
+  IN  EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL   *This,\r
+  IN  EFI_PHYSICAL_ADDRESS                  BaseAddress,\r
+  IN  UINT64                                Length,\r
+  OUT UINT64                                *Attributes\r
+  )\r
+{\r
+  EFI_PHYSICAL_ADDRESS  Address;\r
+  UINT64                *PageEntry;\r
+  UINT64                MemAttr;\r
+  PAGE_ATTRIBUTE        PageAttr;\r
+  INT64                 Size;\r
+\r
+  if (Length < SIZE_4KB || Attributes == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Size = (INT64)Length;\r
+  MemAttr = (UINT64)-1;\r
+\r
+  do {\r
+\r
+    PageEntry = GetPageTableEntry (BaseAddress, &PageAttr);\r
+    if (PageEntry == NULL || PageAttr == PageNone) {\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+\r
+    //\r
+    // If the memory range is cross page table boundary, make sure they\r
+    // share the same attribute. Return EFI_NO_MAPPING if not.\r
+    //\r
+    *Attributes = GetAttributesFromPageEntry (PageEntry);\r
+    if (MemAttr != (UINT64)-1 && *Attributes != MemAttr) {\r
+      return EFI_NO_MAPPING;\r
+    }\r
+\r
+    switch (PageAttr) {\r
+    case Page4K:\r
+      Address     = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;\r
+      Size        -= (SIZE_4KB - (BaseAddress - Address));\r
+      BaseAddress += (SIZE_4KB - (BaseAddress - Address));\r
+      break;\r
+\r
+    case Page2M:\r
+      Address     = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;\r
+      Size        -= SIZE_2MB - (BaseAddress - Address);\r
+      BaseAddress += SIZE_2MB - (BaseAddress - Address);\r
+      break;\r
+\r
+    case Page1G:\r
+      Address     = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;\r
+      Size        -= SIZE_1GB - (BaseAddress - Address);\r
+      BaseAddress += SIZE_1GB - (BaseAddress - Address);\r
+      break;\r
+\r
+    default:\r
+      return EFI_UNSUPPORTED;\r
+    }\r
+\r
+    MemAttr = *Attributes;\r
+\r
+  } while (Size > 0);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r