]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c
UefiCpuPkg/PiSmmCpuDxeSmm: Add paging protection.
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / X64 / PageTbl.c
index 9cee784156eb0338e647d62464591e6319bd6110..b3e50a4f20e119c57b0a9b2650664b72ec55754c 100644 (file)
@@ -18,6 +18,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 #define ACC_MAX_BIT                 BIT3\r
 LIST_ENTRY                          mPagePool = INITIALIZE_LIST_HEAD_VARIABLE (mPagePool);\r
 BOOLEAN                             m1GPageTableSupport = FALSE;\r
+UINT8                               mPhysicalAddressBits;\r
+BOOLEAN                             mCpuSmmStaticPageTable;\r
 \r
 /**\r
   Check if 1-GByte pages is supported by processor or not.\r
@@ -85,6 +87,146 @@ 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
+\r
+  //\r
+  // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
+  //\r
+  ASSERT (PhysicalAddressBits <= 52);\r
+  if (PhysicalAddressBits > 48) {\r
+    PhysicalAddressBits = 48;\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                                         NumberOfPml4EntriesNeeded;\r
+  UINTN                                         NumberOfPdpEntriesNeeded;\r
+  UINTN                                         IndexOfPml4Entries;\r
+  UINTN                                         IndexOfPdpEntries;\r
+  UINTN                                         IndexOfPageDirectoryEntries;\r
+  UINT64                                        *PageMapLevel4Entry;\r
+  UINT64                                        *PageMap;\r
+  UINT64                                        *PageDirectoryPointerEntry;\r
+  UINT64                                        *PageDirectory1GEntry;\r
+  UINT64                                        *PageDirectoryEntry;\r
+\r
+  if (mPhysicalAddressBits <= 39 ) {\r
+    NumberOfPml4EntriesNeeded = 1;\r
+    NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (mPhysicalAddressBits - 30));\r
+  } else {\r
+    NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (mPhysicalAddressBits - 39));\r
+    NumberOfPdpEntriesNeeded = 512;\r
+  }\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
+  PageAddress        = 0;\r
+  for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {\r
+    //\r
+    // Each PML4 entry points to a page of Page Directory Pointer entries.\r
+    //\r
+    PageDirectoryPointerEntry = (UINT64 *) ((*PageMapLevel4Entry) & gPhyMask);\r
+    if (PageDirectoryPointerEntry == NULL) {\r
+      PageDirectoryPointerEntry = AllocatePageTableMemory (1);\r
+      ASSERT(PageDirectoryPointerEntry != NULL);\r
+      ZeroMem (PageDirectoryPointerEntry, EFI_PAGES_TO_SIZE(1));\r
+\r
+      *PageMapLevel4Entry = ((UINTN)PageDirectoryPointerEntry & gPhyMask)  | 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 & gPhyMask) | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;\r
+      }\r
+    } else {\r
+      PageAddress = BASE_4GB;\r
+      for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; 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) & 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 | 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 = (UINT64)PageAddress | IA32_PG_PS | PAGE_ATTRIBUTE_BITS;\r
+        }\r
+      }\r
+    }\r
+  }\r
+}\r
+\r
 /**\r
   Create PageTable for SMM use.\r
 \r
@@ -108,11 +250,17 @@ SmmInitPageTable (
   //\r
   InitializeSpinLock (mPFLock);\r
 \r
+  mCpuSmmStaticPageTable = PcdGetBool (PcdCpuSmmStaticPageTable);\r
   m1GPageTableSupport = Is1GPageSupport ();\r
+  DEBUG ((DEBUG_INFO, "1GPageTableSupport - 0x%x\n", m1GPageTableSupport));\r
+  DEBUG ((DEBUG_INFO, "PcdCpuSmmStaticPageTable - 0x%x\n", mCpuSmmStaticPageTable));\r
+\r
+  mPhysicalAddressBits = CalculateMaximumSupportAddress ();\r
+  DEBUG ((DEBUG_INFO, "PhysicalAddressBits - 0x%x\n", mPhysicalAddressBits));\r
   //\r
   // Generate PAE page table for the first 4GB memory space\r
   //\r
-  Pages = Gen4GPageTable (PAGE_TABLE_PAGES + 1, FALSE);\r
+  Pages = Gen4GPageTable (FALSE);\r
 \r
   //\r
   // Set IA32_PG_PMNT bit to mask this entry\r
@@ -125,21 +273,28 @@ 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 + PAGE_ATTRIBUTE_BITS;\r
+  PTEntry = (UINT64*)AllocatePageTableMemory (1);\r
+  ASSERT (PTEntry != NULL);\r
+  *PTEntry = Pages | PAGE_ATTRIBUTE_BITS;\r
   ZeroMem (PTEntry + 1, EFI_PAGE_SIZE - sizeof (*PTEntry));\r
+\r
   //\r
   // Set sub-entries number\r
   //\r
   SetSubEntriesNum (PTEntry, 3);\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 (mCpuSmmStaticPageTable) {\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
@@ -561,7 +716,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
@@ -612,8 +767,8 @@ 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
@@ -654,13 +809,18 @@ SmiPFHandler (
 \r
   PFAddress = AsmReadCr2 ();\r
 \r
+  if (mCpuSmmStaticPageTable && (PFAddress >= LShiftU64 (1, (mPhysicalAddressBits - 1)))) {\r
+    DEBUG ((DEBUG_ERROR, "Do not support address 0x%lx by processor!\n", PFAddress));\r
+    CpuDeadLoop ();\r
+  }\r
+\r
   //\r
   // If a page fault occurs in SMRAM range, it should be in a SMM stack guard page.\r
   //\r
   if ((FeaturePcdGet (PcdCpuSmmStackGuard)) &&\r
       (PFAddress >= mCpuHotPlugData.SmrrBase) &&\r
       (PFAddress < (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize))) {\r
-    DEBUG ((EFI_D_ERROR, "SMM stack overflow!\n"));\r
+    DEBUG ((DEBUG_ERROR, "SMM stack overflow!\n"));\r
     CpuDeadLoop ();\r
   }\r
 \r
@@ -670,7 +830,7 @@ SmiPFHandler (
   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
+      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
@@ -689,3 +849,87 @@ SmiPFHandler (
 \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
+  UINT64                *L1PageTable;\r
+  UINT64                *L2PageTable;\r
+  UINT64                *L3PageTable;\r
+  UINT64                *L4PageTable;\r
+  BOOLEAN               IsSplitted;\r
+  BOOLEAN               PageTableSplitted;\r
+\r
+  if (!mCpuSmmStaticPageTable) {\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
+  AsmWriteCr0 (AsmReadCr0() & ~CR0_WP);\r
+\r
+  do {\r
+    DEBUG ((DEBUG_INFO, "Start...\n"));\r
+    PageTableSplitted = FALSE;\r
+\r
+    L4PageTable = (UINT64 *)GetPageTableBase ();\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] & 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] & 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] & 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
+  } while (PageTableSplitted);\r
+\r
+  //\r
+  // Enable write protection, after page table updated.\r
+  //\r
+  AsmWriteCr0 (AsmReadCr0() | CR0_WP);\r
+\r
+  return ;\r
+}\r