]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c
UefiCpuPkg/PiSmmCpu: Use new PCD PcdCpuSmmRestrictedMemoryAccess
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / X64 / PageTbl.c
index a7d790fd8aa044662ac40608c2b293a54fd95c05..7516f3505540ee09a8d909b5f85ee712bab27332 100644 (file)
@@ -1,14 +1,10 @@
 /** @file\r
 Page Fault (#PF) handler for X64 processors\r
 \r
-Copyright (c) 2009 - 2015, 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
-http://opensource.org/licenses/bsd-license.php\r
+Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>\r
+Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\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
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
@@ -16,9 +12,30 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 \r
 #define PAGE_TABLE_PAGES            8\r
 #define ACC_MAX_BIT                 BIT3\r
+\r
 LIST_ENTRY                          mPagePool = INITIALIZE_LIST_HEAD_VARIABLE (mPagePool);\r
-SPIN_LOCK                           mPFLock;\r
 BOOLEAN                             m1GPageTableSupport = FALSE;\r
+BOOLEAN                             mCpuSmmRestrictedMemoryAccess;\r
+BOOLEAN                             m5LevelPagingSupport;\r
+X86_ASSEMBLY_PATCH_LABEL            gPatch5LevelPagingSupport;\r
+\r
+/**\r
+  Disable CET.\r
+**/\r
+VOID\r
+EFIAPI\r
+DisableCet (\r
+  VOID\r
+  );\r
+\r
+/**\r
+  Enable CET.\r
+**/\r
+VOID\r
+EFIAPI\r
+EnableCet (\r
+  VOID\r
+  );\r
 \r
 /**\r
   Check if 1-GByte pages is supported by processor or not.\r
@@ -45,6 +62,31 @@ Is1GPageSupport (
   return FALSE;\r
 }\r
 \r
+/**\r
+  Check if 5-level paging is supported by processor or not.\r
+\r
+  @retval TRUE   5-level paging is supported.\r
+  @retval FALSE  5-level paging is not supported.\r
+\r
+**/\r
+BOOLEAN\r
+Is5LevelPagingSupport (\r
+  VOID\r
+  )\r
+{\r
+  CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_ECX EcxFlags;\r
+\r
+  AsmCpuidEx (\r
+    CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS,\r
+    CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO,\r
+    NULL,\r
+    NULL,\r
+    &EcxFlags.Uint32,\r
+    NULL\r
+    );\r
+  return (BOOLEAN) (EcxFlags.Bits.FiveLevelPage != 0);\r
+}\r
+\r
 /**\r
   Set sub-entries number in entry.\r
 \r
@@ -86,6 +128,186 @@ GetSubEntriesNum (
   return BitFieldRead64 (*Entry, 52, 60);\r
 }\r
 \r
+/**\r
+  Calculate the maximum support address.\r
+\r
+  @return the maximum support address.\r
+**/\r
+UINT8\r
+CalculateMaximumSupportAddress (\r
+  VOID\r
+  )\r
+{\r
+  UINT32                                        RegEax;\r
+  UINT8                                         PhysicalAddressBits;\r
+  VOID                                          *Hob;\r
+\r
+  //\r
+  // Get physical address bits supported.\r
+  //\r
+  Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
+  if (Hob != NULL) {\r
+    PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
+  } else {\r
+    AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
+    if (RegEax >= 0x80000008) {\r
+      AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
+      PhysicalAddressBits = (UINT8) RegEax;\r
+    } else {\r
+      PhysicalAddressBits = 36;\r
+    }\r
+  }\r
+  return PhysicalAddressBits;\r
+}\r
+\r
+/**\r
+  Set static page table.\r
+\r
+  @param[in] PageTable     Address of page table.\r
+**/\r
+VOID\r
+SetStaticPageTable (\r
+  IN UINTN               PageTable\r
+  )\r
+{\r
+  UINT64                                        PageAddress;\r
+  UINTN                                         NumberOfPml5EntriesNeeded;\r
+  UINTN                                         NumberOfPml4EntriesNeeded;\r
+  UINTN                                         NumberOfPdpEntriesNeeded;\r
+  UINTN                                         IndexOfPml5Entries;\r
+  UINTN                                         IndexOfPml4Entries;\r
+  UINTN                                         IndexOfPdpEntries;\r
+  UINTN                                         IndexOfPageDirectoryEntries;\r
+  UINT64                                        *PageMapLevel5Entry;\r
+  UINT64                                        *PageMapLevel4Entry;\r
+  UINT64                                        *PageMap;\r
+  UINT64                                        *PageDirectoryPointerEntry;\r
+  UINT64                                        *PageDirectory1GEntry;\r
+  UINT64                                        *PageDirectoryEntry;\r
+\r
+  //\r
+  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses\r
+  //  when 5-Level Paging is disabled.\r
+  //\r
+  ASSERT (mPhysicalAddressBits <= 52);\r
+  if (!m5LevelPagingSupport && mPhysicalAddressBits > 48) {\r
+    mPhysicalAddressBits = 48;\r
+  }\r
+\r
+  NumberOfPml5EntriesNeeded = 1;\r
+  if (mPhysicalAddressBits > 48) {\r
+    NumberOfPml5EntriesNeeded = (UINTN) LShiftU64 (1, mPhysicalAddressBits - 48);\r
+    mPhysicalAddressBits = 48;\r
+  }\r
+\r
+  NumberOfPml4EntriesNeeded = 1;\r
+  if (mPhysicalAddressBits > 39) {\r
+    NumberOfPml4EntriesNeeded = (UINTN) LShiftU64 (1, mPhysicalAddressBits - 39);\r
+    mPhysicalAddressBits = 39;\r
+  }\r
+\r
+  NumberOfPdpEntriesNeeded = 1;\r
+  ASSERT (mPhysicalAddressBits > 30);\r
+  NumberOfPdpEntriesNeeded = (UINTN) LShiftU64 (1, mPhysicalAddressBits - 30);\r
+\r
+  //\r
+  // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.\r
+  //\r
+  PageMap         = (VOID *) PageTable;\r
+\r
+  PageMapLevel4Entry = PageMap;\r
+  PageMapLevel5Entry = NULL;\r
+  if (m5LevelPagingSupport) {\r
+    //\r
+    // By architecture only one PageMapLevel5 exists - so lets allocate storage for it.\r
+    //\r
+    PageMapLevel5Entry = PageMap;\r
+  }\r
+  PageAddress        = 0;\r
+\r
+  for ( IndexOfPml5Entries = 0\r
+      ; IndexOfPml5Entries < NumberOfPml5EntriesNeeded\r
+      ; IndexOfPml5Entries++, PageMapLevel5Entry++) {\r
+    //\r
+    // Each PML5 entry points to a page of PML4 entires.\r
+    // So lets allocate space for them and fill them in in the IndexOfPml4Entries loop.\r
+    // When 5-Level Paging is disabled, below allocation happens only once.\r
+    //\r
+    if (m5LevelPagingSupport) {\r
+      PageMapLevel4Entry = (UINT64 *) ((*PageMapLevel5Entry) & ~mAddressEncMask & gPhyMask);\r
+      if (PageMapLevel4Entry == NULL) {\r
+        PageMapLevel4Entry = AllocatePageTableMemory (1);\r
+        ASSERT(PageMapLevel4Entry != NULL);\r
+        ZeroMem (PageMapLevel4Entry, EFI_PAGES_TO_SIZE(1));\r
+\r
+        *PageMapLevel5Entry = (UINT64)(UINTN)PageMapLevel4Entry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
+      }\r
+    }\r
+\r
+    for (IndexOfPml4Entries = 0; IndexOfPml4Entries < (NumberOfPml5EntriesNeeded == 1 ? NumberOfPml4EntriesNeeded : 512); IndexOfPml4Entries++, PageMapLevel4Entry++) {\r
+      //\r
+      // Each PML4 entry points to a page of Page Directory Pointer entries.\r
+      //\r
+      PageDirectoryPointerEntry = (UINT64 *) ((*PageMapLevel4Entry) & ~mAddressEncMask & gPhyMask);\r
+      if (PageDirectoryPointerEntry == NULL) {\r
+        PageDirectoryPointerEntry = AllocatePageTableMemory (1);\r
+        ASSERT(PageDirectoryPointerEntry != NULL);\r
+        ZeroMem (PageDirectoryPointerEntry, EFI_PAGES_TO_SIZE(1));\r
+\r
+        *PageMapLevel4Entry = (UINT64)(UINTN)PageDirectoryPointerEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
+      }\r
+\r
+      if (m1GPageTableSupport) {\r
+        PageDirectory1GEntry = PageDirectoryPointerEntry;\r
+        for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {\r
+          if (IndexOfPml4Entries == 0 && IndexOfPageDirectoryEntries < 4) {\r
+            //\r
+            // Skip the < 4G entries\r
+            //\r
+            continue;\r
+          }\r
+          //\r
+          // Fill in the Page Directory entries\r
+          //\r
+          *PageDirectory1GEntry = PageAddress | mAddressEncMask | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;\r
+        }\r
+      } else {\r
+        PageAddress = BASE_4GB;\r
+        for (IndexOfPdpEntries = 0; IndexOfPdpEntries < (NumberOfPml4EntriesNeeded == 1 ? NumberOfPdpEntriesNeeded : 512); IndexOfPdpEntries++, PageDirectoryPointerEntry++) {\r
+          if (IndexOfPml4Entries == 0 && IndexOfPdpEntries < 4) {\r
+            //\r
+            // Skip the < 4G entries\r
+            //\r
+            continue;\r
+          }\r
+          //\r
+          // Each Directory Pointer entries points to a page of Page Directory entires.\r
+          // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.\r
+          //\r
+          PageDirectoryEntry = (UINT64 *) ((*PageDirectoryPointerEntry) & ~mAddressEncMask & gPhyMask);\r
+          if (PageDirectoryEntry == NULL) {\r
+            PageDirectoryEntry = AllocatePageTableMemory (1);\r
+            ASSERT(PageDirectoryEntry != NULL);\r
+            ZeroMem (PageDirectoryEntry, EFI_PAGES_TO_SIZE(1));\r
+\r
+            //\r
+            // Fill in a Page Directory Pointer Entries\r
+            //\r
+            *PageDirectoryPointerEntry = (UINT64)(UINTN)PageDirectoryEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
+          }\r
+\r
+          for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {\r
+            //\r
+            // Fill in the Page Directory entries\r
+            //\r
+            *PageDirectoryEntry = PageAddress | mAddressEncMask | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;\r
+          }\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
 /**\r
   Create PageTable for SMM use.\r
 \r
@@ -103,17 +325,28 @@ SmmInitPageTable (
   UINTN                             Index;\r
   UINTN                             PageFaultHandlerHookAddress;\r
   IA32_IDT_GATE_DESCRIPTOR          *IdtEntry;\r
+  EFI_STATUS                        Status;\r
+  UINT64                            *Pml4Entry;\r
+  UINT64                            *Pml5Entry;\r
 \r
   //\r
   // Initialize spin lock\r
   //\r
-  InitializeSpinLock (&mPFLock);\r
-\r
-  m1GPageTableSupport = Is1GPageSupport ();\r
+  InitializeSpinLock (mPFLock);\r
+\r
+  mCpuSmmRestrictedMemoryAccess = PcdGetBool (PcdCpuSmmRestrictedMemoryAccess);\r
+  m1GPageTableSupport           = Is1GPageSupport ();\r
+  m5LevelPagingSupport          = Is5LevelPagingSupport ();\r
+  mPhysicalAddressBits          = CalculateMaximumSupportAddress ();\r
+  PatchInstructionX86 (gPatch5LevelPagingSupport, m5LevelPagingSupport, 1);\r
+  DEBUG ((DEBUG_INFO, "5LevelPaging Support            - %d\n", m5LevelPagingSupport));\r
+  DEBUG ((DEBUG_INFO, "1GPageTable Support             - %d\n", m1GPageTableSupport));\r
+  DEBUG ((DEBUG_INFO, "PcdCpuSmmRestrictedMemoryAccess - %d\n", mCpuSmmRestrictedMemoryAccess));\r
+  DEBUG ((DEBUG_INFO, "PhysicalAddressBits             - %d\n", mPhysicalAddressBits));\r
   //\r
   // Generate PAE page table for the first 4GB memory space\r
   //\r
-  Pages = Gen4GPageTable (PAGE_TABLE_PAGES + 1);\r
+  Pages = Gen4GPageTable (FALSE);\r
 \r
   //\r
   // Set IA32_PG_PMNT bit to mask this entry\r
@@ -126,24 +359,53 @@ SmmInitPageTable (
   //\r
   // Fill Page-Table-Level4 (PML4) entry\r
   //\r
-  PTEntry = (UINT64*)(UINTN)(Pages - EFI_PAGES_TO_SIZE (PAGE_TABLE_PAGES + 1));\r
-  *PTEntry = Pages + IA32_PG_P;\r
-  ZeroMem (PTEntry + 1, EFI_PAGE_SIZE - sizeof (*PTEntry));\r
+  Pml4Entry = (UINT64*)AllocatePageTableMemory (1);\r
+  ASSERT (Pml4Entry != NULL);\r
+  *Pml4Entry = Pages | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
+  ZeroMem (Pml4Entry + 1, EFI_PAGE_SIZE - sizeof (*Pml4Entry));\r
+\r
   //\r
   // Set sub-entries number\r
   //\r
-  SetSubEntriesNum (PTEntry, 3);\r
+  SetSubEntriesNum (Pml4Entry, 3);\r
+  PTEntry = Pml4Entry;\r
 \r
-  //\r
-  // Add remaining pages to page pool\r
-  //\r
-  FreePage = (LIST_ENTRY*)(PTEntry + EFI_PAGE_SIZE / sizeof (*PTEntry));\r
-  while ((UINTN)FreePage < Pages) {\r
-    InsertTailList (&mPagePool, FreePage);\r
-    FreePage += EFI_PAGE_SIZE / sizeof (*FreePage);\r
+  if (m5LevelPagingSupport) {\r
+    //\r
+    // Fill PML5 entry\r
+    //\r
+    Pml5Entry = (UINT64*)AllocatePageTableMemory (1);\r
+    ASSERT (Pml5Entry != NULL);\r
+    *Pml5Entry = (UINTN) Pml4Entry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
+    ZeroMem (Pml5Entry + 1, EFI_PAGE_SIZE - sizeof (*Pml5Entry));\r
+    //\r
+    // Set sub-entries number\r
+    //\r
+    SetSubEntriesNum (Pml5Entry, 1);\r
+    PTEntry = Pml5Entry;\r
   }\r
 \r
-  if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
+  if (mCpuSmmRestrictedMemoryAccess) {\r
+    //\r
+    // When access to non-SMRAM memory is restricted, create page table\r
+    // that covers all memory space.\r
+    //\r
+    SetStaticPageTable ((UINTN)PTEntry);\r
+  } else {\r
+    //\r
+    // Add pages to page pool\r
+    //\r
+    FreePage = (LIST_ENTRY*)AllocatePageTableMemory (PAGE_TABLE_PAGES);\r
+    ASSERT (FreePage != NULL);\r
+    for (Index = 0; Index < PAGE_TABLE_PAGES; Index++) {\r
+      InsertTailList (&mPagePool, FreePage);\r
+      FreePage += EFI_PAGE_SIZE / sizeof (*FreePage);\r
+    }\r
+  }\r
+\r
+  if (FeaturePcdGet (PcdCpuSmmProfileEnable) ||\r
+      HEAP_GUARD_NONSTOP_MODE ||\r
+      NULL_DETECTION_NONSTOP_MODE) {\r
     //\r
     // Set own Page Fault entry instead of the default one, because SMM Profile\r
     // feature depends on IRET instruction to do Single Step\r
@@ -161,7 +423,8 @@ SmmInitPageTable (
     //\r
     // Register Smm Page Fault Handler\r
     //\r
-    SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);\r
+    Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_PAGE_FAULT, SmiPFHandler);\r
+    ASSERT_EFI_ERROR (Status);\r
   }\r
 \r
   //\r
@@ -172,7 +435,7 @@ SmmInitPageTable (
   }\r
 \r
   //\r
-  // Return the address of PML4 (to set CR3)\r
+  // Return the address of PML4/PML5 (to set CR3)\r
   //\r
   return (UINT32)(UINTN)PTEntry;\r
 }\r
@@ -264,12 +527,16 @@ ReclaimPages (
   VOID\r
   )\r
 {\r
+  UINT64                       Pml5Entry;\r
+  UINT64                       *Pml5;\r
   UINT64                       *Pml4;\r
   UINT64                       *Pdpt;\r
   UINT64                       *Pdt;\r
+  UINTN                        Pml5Index;\r
   UINTN                        Pml4Index;\r
   UINTN                        PdptIndex;\r
   UINTN                        PdtIndex;\r
+  UINTN                        MinPml5;\r
   UINTN                        MinPml4;\r
   UINTN                        MinPdpt;\r
   UINTN                        MinPdt;\r
@@ -279,122 +546,167 @@ ReclaimPages (
   BOOLEAN                      PML4EIgnore;\r
   BOOLEAN                      PDPTEIgnore;\r
   UINT64                       *ReleasePageAddress;\r
+  IA32_CR4                     Cr4;\r
+  BOOLEAN                      Enable5LevelPaging;\r
+  UINT64                       PFAddress;\r
+  UINT64                       PFAddressPml5Index;\r
+  UINT64                       PFAddressPml4Index;\r
+  UINT64                       PFAddressPdptIndex;\r
+  UINT64                       PFAddressPdtIndex;\r
 \r
   Pml4 = NULL;\r
   Pdpt = NULL;\r
   Pdt  = NULL;\r
   MinAcc  = (UINT64)-1;\r
   MinPml4 = (UINTN)-1;\r
+  MinPml5 = (UINTN)-1;\r
   MinPdpt = (UINTN)-1;\r
   MinPdt  = (UINTN)-1;\r
   Acc     = 0;\r
   ReleasePageAddress = 0;\r
+  PFAddress = AsmReadCr2 ();\r
+  PFAddressPml5Index = BitFieldRead64 (PFAddress, 48, 48 + 8);\r
+  PFAddressPml4Index = BitFieldRead64 (PFAddress, 39, 39 + 8);\r
+  PFAddressPdptIndex = BitFieldRead64 (PFAddress, 30, 30 + 8);\r
+  PFAddressPdtIndex = BitFieldRead64 (PFAddress, 21, 21 + 8);\r
+\r
+  Cr4.UintN = AsmReadCr4 ();\r
+  Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);\r
+  Pml5 = (UINT64*)(UINTN)(AsmReadCr3 () & gPhyMask);\r
+\r
+  if (!Enable5LevelPaging) {\r
+    //\r
+    // Create one fake PML5 entry for 4-Level Paging\r
+    // so that the page table parsing logic only handles 5-Level page structure.\r
+    //\r
+    Pml5Entry = (UINTN) Pml5 | IA32_PG_P;\r
+    Pml5 = &Pml5Entry;\r
+  }\r
 \r
   //\r
   // First, find the leaf entry has the smallest access record value\r
   //\r
-  Pml4 = (UINT64*)(UINTN)(AsmReadCr3 () & gPhyMask);\r
-  for (Pml4Index = 0; Pml4Index < EFI_PAGE_SIZE / sizeof (*Pml4); Pml4Index++) {\r
-    if ((Pml4[Pml4Index] & IA32_PG_P) == 0 || (Pml4[Pml4Index] & IA32_PG_PMNT) != 0) {\r
+  for (Pml5Index = 0; Pml5Index < (Enable5LevelPaging ? (EFI_PAGE_SIZE / sizeof (*Pml4)) : 1); Pml5Index++) {\r
+    if ((Pml5[Pml5Index] & IA32_PG_P) == 0 || (Pml5[Pml5Index] & IA32_PG_PMNT) != 0) {\r
       //\r
-      // If the PML4 entry is not present or is masked, skip it\r
+      // If the PML5 entry is not present or is masked, skip it\r
       //\r
       continue;\r
     }\r
-    Pdpt = (UINT64*)(UINTN)(Pml4[Pml4Index] & gPhyMask);\r
-    PML4EIgnore = FALSE;\r
-    for (PdptIndex = 0; PdptIndex < EFI_PAGE_SIZE / sizeof (*Pdpt); PdptIndex++) {\r
-      if ((Pdpt[PdptIndex] & IA32_PG_P) == 0 || (Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {\r
+    Pml4 = (UINT64*)(UINTN)(Pml5[Pml5Index] & gPhyMask);\r
+    for (Pml4Index = 0; Pml4Index < EFI_PAGE_SIZE / sizeof (*Pml4); Pml4Index++) {\r
+      if ((Pml4[Pml4Index] & IA32_PG_P) == 0 || (Pml4[Pml4Index] & IA32_PG_PMNT) != 0) {\r
         //\r
-        // If the PDPT entry is not present or is masked, skip it\r
+        // If the PML4 entry is not present or is masked, skip it\r
         //\r
-        if ((Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {\r
-          //\r
-          // If the PDPT entry is masked, we will ignore checking the PML4 entry\r
-          //\r
-          PML4EIgnore = TRUE;\r
-        }\r
         continue;\r
       }\r
-      if ((Pdpt[PdptIndex] & IA32_PG_PS) == 0) {\r
-        //\r
-        // It's not 1-GByte pages entry, it should be a PDPT entry,\r
-        // we will not check PML4 entry more\r
-        //\r
-        PML4EIgnore = TRUE;\r
-        Pdt =  (UINT64*)(UINTN)(Pdpt[PdptIndex] & gPhyMask);\r
-        PDPTEIgnore = FALSE;\r
-        for (PdtIndex = 0; PdtIndex < EFI_PAGE_SIZE / sizeof(*Pdt); PdtIndex++) {\r
-          if ((Pdt[PdtIndex] & IA32_PG_P) == 0 || (Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {\r
+      Pdpt = (UINT64*)(UINTN)(Pml4[Pml4Index] & ~mAddressEncMask & gPhyMask);\r
+      PML4EIgnore = FALSE;\r
+      for (PdptIndex = 0; PdptIndex < EFI_PAGE_SIZE / sizeof (*Pdpt); PdptIndex++) {\r
+        if ((Pdpt[PdptIndex] & IA32_PG_P) == 0 || (Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {\r
+          //\r
+          // If the PDPT entry is not present or is masked, skip it\r
+          //\r
+          if ((Pdpt[PdptIndex] & IA32_PG_PMNT) != 0) {\r
             //\r
-            // If the PD entry is not present or is masked, skip it\r
+            // If the PDPT entry is masked, we will ignore checking the PML4 entry\r
             //\r
-            if ((Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {\r
+            PML4EIgnore = TRUE;\r
+          }\r
+          continue;\r
+        }\r
+        if ((Pdpt[PdptIndex] & IA32_PG_PS) == 0) {\r
+          //\r
+          // It's not 1-GByte pages entry, it should be a PDPT entry,\r
+          // we will not check PML4 entry more\r
+          //\r
+          PML4EIgnore = TRUE;\r
+          Pdt = (UINT64*)(UINTN)(Pdpt[PdptIndex] & ~mAddressEncMask & gPhyMask);\r
+          PDPTEIgnore = FALSE;\r
+          for (PdtIndex = 0; PdtIndex < EFI_PAGE_SIZE / sizeof(*Pdt); PdtIndex++) {\r
+            if ((Pdt[PdtIndex] & IA32_PG_P) == 0 || (Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {\r
+              //\r
+              // If the PD entry is not present or is masked, skip it\r
+              //\r
+              if ((Pdt[PdtIndex] & IA32_PG_PMNT) != 0) {\r
+                //\r
+                // If the PD entry is masked, we will not PDPT entry more\r
+                //\r
+                PDPTEIgnore = TRUE;\r
+              }\r
+              continue;\r
+            }\r
+            if ((Pdt[PdtIndex] & IA32_PG_PS) == 0) {\r
               //\r
-              // If the PD entry is masked, we will not PDPT entry more\r
+              // It's not 2 MByte page table entry, it should be PD entry\r
+              // we will find the entry has the smallest access record value\r
               //\r
               PDPTEIgnore = TRUE;\r
+              if (PdtIndex != PFAddressPdtIndex || PdptIndex != PFAddressPdptIndex ||\r
+                  Pml4Index != PFAddressPml4Index || Pml5Index != PFAddressPml5Index) {\r
+                Acc = GetAndUpdateAccNum (Pdt + PdtIndex);\r
+                if (Acc < MinAcc) {\r
+                  //\r
+                  // If the PD entry has the smallest access record value,\r
+                  // save the Page address to be released\r
+                  //\r
+                  MinAcc  = Acc;\r
+                  MinPml5 = Pml5Index;\r
+                  MinPml4 = Pml4Index;\r
+                  MinPdpt = PdptIndex;\r
+                  MinPdt  = PdtIndex;\r
+                  ReleasePageAddress = Pdt + PdtIndex;\r
+                }\r
+              }\r
             }\r
-            continue;\r
           }\r
-          if ((Pdt[PdtIndex] & IA32_PG_PS) == 0) {\r
+          if (!PDPTEIgnore) {\r
             //\r
-            // It's not 2 MByte page table entry, it should be PD entry\r
-            // we will find the entry has the smallest access record value\r
+            // If this PDPT entry has no PDT entries pointer to 4 KByte pages,\r
+            // it should only has the entries point to 2 MByte Pages\r
             //\r
-            PDPTEIgnore = TRUE;\r
-            Acc = GetAndUpdateAccNum (Pdt + PdtIndex);\r
-            if (Acc < MinAcc) {\r
-              //\r
-              // If the PD entry has the smallest access record value,\r
-              // save the Page address to be released\r
-              //\r
-              MinAcc  = Acc;\r
-              MinPml4 = Pml4Index;\r
-              MinPdpt = PdptIndex;\r
-              MinPdt  = PdtIndex;\r
-              ReleasePageAddress = Pdt + PdtIndex;\r
+            if (PdptIndex != PFAddressPdptIndex || Pml4Index != PFAddressPml4Index ||\r
+                Pml5Index != PFAddressPml5Index) {\r
+              Acc = GetAndUpdateAccNum (Pdpt + PdptIndex);\r
+              if (Acc < MinAcc) {\r
+                //\r
+                // If the PDPT entry has the smallest access record value,\r
+                // save the Page address to be released\r
+                //\r
+                MinAcc  = Acc;\r
+                MinPml5 = Pml5Index;\r
+                MinPml4 = Pml4Index;\r
+                MinPdpt = PdptIndex;\r
+                MinPdt  = (UINTN)-1;\r
+                ReleasePageAddress = Pdpt + PdptIndex;\r
+              }\r
             }\r
           }\r
         }\r
-        if (!PDPTEIgnore) {\r
-          //\r
-          // If this PDPT entry has no PDT entries pointer to 4 KByte pages,\r
-          // it should only has the entries point to 2 MByte Pages\r
-          //\r
-          Acc = GetAndUpdateAccNum (Pdpt + PdptIndex);\r
+      }\r
+      if (!PML4EIgnore) {\r
+        //\r
+        // If PML4 entry has no the PDPT entry pointer to 2 MByte pages,\r
+        // it should only has the entries point to 1 GByte Pages\r
+        //\r
+        if (Pml4Index != PFAddressPml4Index || Pml5Index != PFAddressPml5Index) {\r
+          Acc = GetAndUpdateAccNum (Pml4 + Pml4Index);\r
           if (Acc < MinAcc) {\r
             //\r
-            // If the PDPT entry has the smallest access record value,\r
+            // If the PML4 entry has the smallest access record value,\r
             // save the Page address to be released\r
             //\r
             MinAcc  = Acc;\r
+            MinPml5 = Pml5Index;\r
             MinPml4 = Pml4Index;\r
-            MinPdpt = PdptIndex;\r
+            MinPdpt = (UINTN)-1;\r
             MinPdt  = (UINTN)-1;\r
-            ReleasePageAddress = Pdpt + PdptIndex;\r
+            ReleasePageAddress = Pml4 + Pml4Index;\r
           }\r
         }\r
       }\r
     }\r
-    if (!PML4EIgnore) {\r
-      //\r
-      // If PML4 entry has no the PDPT entry pointer to 2 MByte pages,\r
-      // it should only has the entries point to 1 GByte Pages\r
-      //\r
-      Acc = GetAndUpdateAccNum (Pml4 + Pml4Index);\r
-      if (Acc < MinAcc) {\r
-        //\r
-        // If the PML4 entry has the smallest access record value,\r
-        // save the Page address to be released\r
-        //\r
-        MinAcc  = Acc;\r
-        MinPml4 = Pml4Index;\r
-        MinPdpt = (UINTN)-1;\r
-        MinPdt  = (UINTN)-1;\r
-        ReleasePageAddress = Pml4 + Pml4Index;\r
-      }\r
-    }\r
   }\r
   //\r
   // Make sure one PML4/PDPT/PD entry is selected\r
@@ -404,7 +716,7 @@ ReclaimPages (
   //\r
   // Secondly, insert the page pointed by this entry into page pool and clear this entry\r
   //\r
-  InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(*ReleasePageAddress & gPhyMask));\r
+  InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(*ReleasePageAddress & ~mAddressEncMask & gPhyMask));\r
   *ReleasePageAddress = 0;\r
 \r
   //\r
@@ -416,14 +728,16 @@ ReclaimPages (
       //\r
       // If 4 KByte Page Table is released, check the PDPT entry\r
       //\r
-      Pdpt = (UINT64*)(UINTN)(Pml4[MinPml4] & gPhyMask);\r
+      Pml4 = (UINT64 *) (UINTN) (Pml5[MinPml5] & gPhyMask);\r
+      Pdpt = (UINT64*)(UINTN)(Pml4[MinPml4] & ~mAddressEncMask & gPhyMask);\r
       SubEntriesNum = GetSubEntriesNum(Pdpt + MinPdpt);\r
-      if (SubEntriesNum == 0) {\r
+      if (SubEntriesNum == 0 &&\r
+          (MinPdpt != PFAddressPdptIndex || MinPml4 != PFAddressPml4Index || MinPml5 != PFAddressPml5Index)) {\r
         //\r
         // Release the empty Page Directory table if there was no more 4 KByte Page Table entry\r
         // clear the Page directory entry\r
         //\r
-        InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pdpt[MinPdpt] & gPhyMask));\r
+        InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pdpt[MinPdpt] & ~mAddressEncMask & gPhyMask));\r
         Pdpt[MinPdpt] = 0;\r
         //\r
         // Go on checking the PML4 table\r
@@ -434,7 +748,7 @@ ReclaimPages (
       //\r
       // Update the sub-entries filed in PDPT entry and exit\r
       //\r
-      SetSubEntriesNum (Pdpt + MinPdpt, SubEntriesNum - 1);\r
+      SetSubEntriesNum (Pdpt + MinPdpt, (SubEntriesNum - 1) & 0x1FF);\r
       break;\r
     }\r
     if (MinPdpt != (UINTN)-1) {\r
@@ -442,12 +756,12 @@ ReclaimPages (
       // One 2MB Page Table is released or Page Directory table is released, check the PML4 entry\r
       //\r
       SubEntriesNum = GetSubEntriesNum (Pml4 + MinPml4);\r
-      if (SubEntriesNum == 0) {\r
+      if (SubEntriesNum == 0 && (MinPml4 != PFAddressPml4Index || MinPml5 != PFAddressPml5Index)) {\r
         //\r
         // Release the empty PML4 table if there was no more 1G KByte Page Table entry\r
         // clear the Page directory entry\r
         //\r
-        InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pml4[MinPml4] & gPhyMask));\r
+        InsertTailList (&mPagePool, (LIST_ENTRY*)(UINTN)(Pml4[MinPml4] & ~mAddressEncMask & gPhyMask));\r
         Pml4[MinPml4] = 0;\r
         MinPdpt = (UINTN)-1;\r
         continue;\r
@@ -455,7 +769,7 @@ ReclaimPages (
       //\r
       // Update the sub-entries filed in PML4 entry and exit\r
       //\r
-      SetSubEntriesNum (Pml4 + MinPml4, SubEntriesNum - 1);\r
+      SetSubEntriesNum (Pml4 + MinPml4, (SubEntriesNum - 1) & 0x1FF);\r
       break;\r
     }\r
     //\r
@@ -507,7 +821,7 @@ SmiDefaultPFHandler (
   )\r
 {\r
   UINT64                            *PageTable;\r
-  UINT64                            *Pml4;\r
+  UINT64                            *PageTableTop;\r
   UINT64                            PFAddress;\r
   UINTN                             StartBit;\r
   UINTN                             EndBit;\r
@@ -518,6 +832,8 @@ SmiDefaultPFHandler (
   UINTN                             PageAttribute;\r
   EFI_STATUS                        Status;\r
   UINT64                            *UpperEntry;\r
+  BOOLEAN                           Enable5LevelPaging;\r
+  IA32_CR4                          Cr4;\r
 \r
   //\r
   // Set default SMM page attribute\r
@@ -527,9 +843,12 @@ SmiDefaultPFHandler (
   PageAttribute = 0;\r
 \r
   EndBit = 0;\r
-  Pml4 = (UINT64*)(AsmReadCr3 () & gPhyMask);\r
+  PageTableTop = (UINT64*)(AsmReadCr3 () & gPhyMask);\r
   PFAddress = AsmReadCr2 ();\r
 \r
+  Cr4.UintN = AsmReadCr4 ();\r
+  Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 != 0);\r
+\r
   Status = GetPlatformPageTableAttribute (PFAddress, &PageSize, &NumOfPages, &PageAttribute);\r
   //\r
   // If platform not support page table attribute, set default SMM page attribute\r
@@ -562,7 +881,7 @@ SmiDefaultPFHandler (
     break;\r
   case SmmPageSize1G:\r
     if (!m1GPageTableSupport) {\r
-      DEBUG ((EFI_D_ERROR, "1-GByte pages is not supported!"));\r
+      DEBUG ((DEBUG_ERROR, "1-GByte pages is not supported!"));\r
       ASSERT (FALSE);\r
     }\r
     //\r
@@ -583,15 +902,15 @@ SmiDefaultPFHandler (
   }\r
 \r
   for (Index = 0; Index < NumOfPages; Index++) {\r
-    PageTable  = Pml4;\r
+    PageTable  = PageTableTop;\r
     UpperEntry = NULL;\r
-    for (StartBit = 39; StartBit > EndBit; StartBit -= 9) {\r
+    for (StartBit = Enable5LevelPaging ? 48 : 39; StartBit > EndBit; StartBit -= 9) {\r
       PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);\r
       if ((PageTable[PTIndex] & IA32_PG_P) == 0) {\r
         //\r
         // If the entry is not present, allocate one page from page pool for it\r
         //\r
-        PageTable[PTIndex] = AllocPage () | IA32_PG_RW | IA32_PG_P;\r
+        PageTable[PTIndex] = AllocPage () | mAddressEncMask | PAGE_ATTRIBUTE_BITS;\r
       } else {\r
         //\r
         // Save the upper entry address\r
@@ -604,7 +923,7 @@ SmiDefaultPFHandler (
       //\r
       PageTable[PTIndex] |= (UINT64)IA32_PG_A;\r
       SetAccNum (PageTable + PTIndex, 7);\r
-      PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & gPhyMask);\r
+      PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & ~mAddressEncMask & gPhyMask);\r
     }\r
 \r
     PTIndex = BitFieldRead64 (PFAddress, StartBit, StartBit + 8);\r
@@ -613,17 +932,17 @@ SmiDefaultPFHandler (
       // Check if the entry has already existed, this issue may occur when the different\r
       // size page entries created under the same entry\r
       //\r
-      DEBUG ((EFI_D_ERROR, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable, PTIndex, PageTable[PTIndex]));\r
-      DEBUG ((EFI_D_ERROR, "New page table overlapped with old page table!\n"));\r
+      DEBUG ((DEBUG_ERROR, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable, PTIndex, PageTable[PTIndex]));\r
+      DEBUG ((DEBUG_ERROR, "New page table overlapped with old page table!\n"));\r
       ASSERT (FALSE);\r
     }\r
     //\r
     // Fill the new entry\r
     //\r
-    PageTable[PTIndex] = (PFAddress & gPhyMask & ~((1ull << EndBit) - 1)) |\r
-                         PageAttribute | IA32_PG_A | IA32_PG_RW | IA32_PG_P;\r
+    PageTable[PTIndex] = ((PFAddress | mAddressEncMask) & gPhyMask & ~((1ull << EndBit) - 1)) |\r
+                         PageAttribute | IA32_PG_A | PAGE_ATTRIBUTE_BITS;\r
     if (UpperEntry != NULL) {\r
-      SetSubEntriesNum (UpperEntry, GetSubEntriesNum (UpperEntry) + 1);\r
+      SetSubEntriesNum (UpperEntry, (GetSubEntriesNum (UpperEntry) + 1) & 0x1FF);\r
     }\r
     //\r
     // Get the next page address if we need to create more page tables\r
@@ -643,39 +962,105 @@ SmiDefaultPFHandler (
 VOID\r
 EFIAPI\r
 SmiPFHandler (\r
-    IN EFI_EXCEPTION_TYPE   InterruptType,\r
-    IN EFI_SYSTEM_CONTEXT   SystemContext\r
+  IN EFI_EXCEPTION_TYPE   InterruptType,\r
+  IN EFI_SYSTEM_CONTEXT   SystemContext\r
   )\r
 {\r
   UINTN             PFAddress;\r
+  UINTN             GuardPageAddress;\r
+  UINTN             CpuIndex;\r
 \r
   ASSERT (InterruptType == EXCEPT_IA32_PAGE_FAULT);\r
 \r
-  AcquireSpinLock (&mPFLock);\r
+  AcquireSpinLock (mPFLock);\r
 \r
   PFAddress = AsmReadCr2 ();\r
 \r
+  if (mCpuSmmRestrictedMemoryAccess && (PFAddress >= LShiftU64 (1, (mPhysicalAddressBits - 1)))) {\r
+    DumpCpuContext (InterruptType, SystemContext);\r
+    DEBUG ((DEBUG_ERROR, "Do not support address 0x%lx by processor!\n", PFAddress));\r
+    CpuDeadLoop ();\r
+    goto Exit;\r
+  }\r
+\r
   //\r
-  // If a page fault occurs in SMRAM range, it should be in a SMM stack guard page.\r
+  // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,\r
+  // or SMM page protection violation.\r
   //\r
-  if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&\r
-      (PFAddress >= mCpuHotPlugData.SmrrBase) &&\r
+  if ((PFAddress >= mCpuHotPlugData.SmrrBase) &&\r
       (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {\r
-    DEBUG ((EFI_D_ERROR, "SMM stack overflow!\n"));\r
+    DumpCpuContext (InterruptType, SystemContext);\r
+    CpuIndex = GetCpuIndex ();\r
+    GuardPageAddress = (mSmmStackArrayBase + EFI_PAGE_SIZE + CpuIndex * mSmmStackSize);\r
+    if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&\r
+        (PFAddress >= GuardPageAddress) &&\r
+        (PFAddress < (GuardPageAddress + EFI_PAGE_SIZE))) {\r
+      DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));\r
+    } else {\r
+      if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) {\r
+        DEBUG ((DEBUG_ERROR, "SMM exception at execution (0x%lx)\n", PFAddress));\r
+        DEBUG_CODE (\r
+          DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp);\r
+        );\r
+      } else {\r
+        DEBUG ((DEBUG_ERROR, "SMM exception at access (0x%lx)\n", PFAddress));\r
+        DEBUG_CODE (\r
+          DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);\r
+        );\r
+      }\r
+\r
+      if (HEAP_GUARD_NONSTOP_MODE) {\r
+        GuardPagePFHandler (SystemContext.SystemContextX64->ExceptionData);\r
+        goto Exit;\r
+      }\r
+    }\r
     CpuDeadLoop ();\r
+    goto Exit;\r
   }\r
 \r
   //\r
-  // If a page fault occurs in SMM range\r
+  // If a page fault occurs in non-SMRAM range.\r
   //\r
   if ((PFAddress < mCpuHotPlugData.SmrrBase) ||\r
       (PFAddress >= mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {\r
     if ((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0) {\r
-      DEBUG ((EFI_D_ERROR, "Code executed on IP(0x%lx) out of SMM range after SMM is locked!\n", PFAddress));\r
+      DumpCpuContext (InterruptType, SystemContext);\r
+      DEBUG ((DEBUG_ERROR, "Code executed on IP(0x%lx) out of SMM range after SMM is locked!\n", PFAddress));\r
       DEBUG_CODE (\r
         DumpModuleInfoByIp (*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp);\r
       );\r
       CpuDeadLoop ();\r
+      goto Exit;\r
+    }\r
+\r
+    //\r
+    // If NULL pointer was just accessed\r
+    //\r
+    if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 &&\r
+        (PFAddress < EFI_PAGE_SIZE)) {\r
+      DumpCpuContext (InterruptType, SystemContext);\r
+      DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));\r
+      DEBUG_CODE (\r
+        DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);\r
+      );\r
+\r
+      if (NULL_DETECTION_NONSTOP_MODE) {\r
+        GuardPagePFHandler (SystemContext.SystemContextX64->ExceptionData);\r
+        goto Exit;\r
+      }\r
+\r
+      CpuDeadLoop ();\r
+      goto Exit;\r
+    }\r
+\r
+    if (mCpuSmmRestrictedMemoryAccess && IsSmmCommBufferForbiddenAddress (PFAddress)) {\r
+      DumpCpuContext (InterruptType, SystemContext);\r
+      DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%lx)!\n", PFAddress));\r
+      DEBUG_CODE (\r
+        DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);\r
+      );\r
+      CpuDeadLoop ();\r
+      goto Exit;\r
     }\r
   }\r
 \r
@@ -688,5 +1073,182 @@ SmiPFHandler (
     SmiDefaultPFHandler ();\r
   }\r
 \r
-  ReleaseSpinLock (&mPFLock);\r
+Exit:\r
+  ReleaseSpinLock (mPFLock);\r
+}\r
+\r
+/**\r
+  This function sets memory attribute for page table.\r
+**/\r
+VOID\r
+SetPageTableAttributes (\r
+  VOID\r
+  )\r
+{\r
+  UINTN                 Index2;\r
+  UINTN                 Index3;\r
+  UINTN                 Index4;\r
+  UINTN                 Index5;\r
+  UINT64                *L1PageTable;\r
+  UINT64                *L2PageTable;\r
+  UINT64                *L3PageTable;\r
+  UINT64                *L4PageTable;\r
+  UINT64                *L5PageTable;\r
+  BOOLEAN               IsSplitted;\r
+  BOOLEAN               PageTableSplitted;\r
+  BOOLEAN               CetEnabled;\r
+  IA32_CR4              Cr4;\r
+  BOOLEAN               Enable5LevelPaging;\r
+\r
+  Cr4.UintN = AsmReadCr4 ();\r
+  Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);\r
+\r
+  //\r
+  // Don't mark page table memory as read-only if\r
+  //  - no restriction on access to non-SMRAM memory; or\r
+  //  - SMM heap guard feature enabled; or\r
+  //      BIT2: SMM page guard enabled\r
+  //      BIT3: SMM pool guard enabled\r
+  //  - SMM profile feature enabled\r
+  //\r
+  if (!mCpuSmmRestrictedMemoryAccess ||\r
+      ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) ||\r
+      FeaturePcdGet (PcdCpuSmmProfileEnable)) {\r
+    //\r
+    // Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time.\r
+    //\r
+    ASSERT (!(mCpuSmmRestrictedMemoryAccess &&\r
+              (PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0));\r
+\r
+    //\r
+    // Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time.\r
+    //\r
+    ASSERT (!(mCpuSmmRestrictedMemoryAccess && FeaturePcdGet (PcdCpuSmmProfileEnable)));\r
+    return ;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));\r
+\r
+  //\r
+  // Disable write protection, because we need mark page table to be write protected.\r
+  // We need *write* page table memory, to mark itself to be *read only*.\r
+  //\r
+  CetEnabled = ((AsmReadCr4() & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;\r
+  if (CetEnabled) {\r
+    //\r
+    // CET must be disabled if WP is disabled.\r
+    //\r
+    DisableCet();\r
+  }\r
+  AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);\r
+\r
+  do {\r
+    DEBUG ((DEBUG_INFO, "Start...\n"));\r
+    PageTableSplitted = FALSE;\r
+    L5PageTable = NULL;\r
+    if (Enable5LevelPaging) {\r
+      L5PageTable = (UINT64 *)GetPageTableBase ();\r
+      SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L5PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
+      PageTableSplitted = (PageTableSplitted || IsSplitted);\r
+    }\r
+\r
+    for (Index5 = 0; Index5 < (Enable5LevelPaging ? SIZE_4KB/sizeof(UINT64) : 1); Index5++) {\r
+      if (Enable5LevelPaging) {\r
+        L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
+        if (L4PageTable == NULL) {\r
+          continue;\r
+        }\r
+      } else {\r
+        L4PageTable = (UINT64 *)GetPageTableBase ();\r
+      }\r
+      SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L4PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
+      PageTableSplitted = (PageTableSplitted || IsSplitted);\r
+\r
+      for (Index4 = 0; Index4 < SIZE_4KB/sizeof(UINT64); Index4++) {\r
+        L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
+        if (L3PageTable == NULL) {\r
+          continue;\r
+        }\r
+\r
+        SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L3PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
+        PageTableSplitted = (PageTableSplitted || IsSplitted);\r
+\r
+        for (Index3 = 0; Index3 < SIZE_4KB/sizeof(UINT64); Index3++) {\r
+          if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {\r
+            // 1G\r
+            continue;\r
+          }\r
+          L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
+          if (L2PageTable == NULL) {\r
+            continue;\r
+          }\r
+\r
+          SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L2PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
+          PageTableSplitted = (PageTableSplitted || IsSplitted);\r
+\r
+          for (Index2 = 0; Index2 < SIZE_4KB/sizeof(UINT64); Index2++) {\r
+            if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {\r
+              // 2M\r
+              continue;\r
+            }\r
+            L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);\r
+            if (L1PageTable == NULL) {\r
+              continue;\r
+            }\r
+            SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS)(UINTN)L1PageTable, SIZE_4KB, EFI_MEMORY_RO, &IsSplitted);\r
+            PageTableSplitted = (PageTableSplitted || IsSplitted);\r
+          }\r
+        }\r
+      }\r
+    }\r
+  } while (PageTableSplitted);\r
+\r
+  //\r
+  // Enable write protection, after page table updated.\r
+  //\r
+  AsmWriteCr0 (AsmReadCr0() | CR0_WP);\r
+  if (CetEnabled) {\r
+    //\r
+    // re-enable CET.\r
+    //\r
+    EnableCet();\r
+  }\r
+\r
+  return ;\r
+}\r
+\r
+/**\r
+  This function reads CR2 register when on-demand paging is enabled.\r
+\r
+  @param[out]  *Cr2  Pointer to variable to hold CR2 register value.\r
+**/\r
+VOID\r
+SaveCr2 (\r
+  OUT UINTN  *Cr2\r
+  )\r
+{\r
+  if (!mCpuSmmRestrictedMemoryAccess) {\r
+    //\r
+    // On-demand paging is enabled when access to non-SMRAM is not restricted.\r
+    //\r
+    *Cr2 = AsmReadCr2 ();\r
+  }\r
+}\r
+\r
+/**\r
+  This function restores CR2 register when on-demand paging is enabled.\r
+\r
+  @param[in]  Cr2  Value to write into CR2 register.\r
+**/\r
+VOID\r
+RestoreCr2 (\r
+  IN UINTN  Cr2\r
+  )\r
+{\r
+  if (!mCpuSmmRestrictedMemoryAccess) {\r
+    //\r
+    // On-demand paging is enabled when access to non-SMRAM is not restricted.\r
+    //\r
+    AsmWriteCr2 (Cr2);\r
+  }\r
 }\r