]> git.proxmox.com Git - mirror_edk2.git/commitdiff
UefiCpuPkg/CpuDxe: implement non-stop mode for uefi
authorJian J Wang <jian.j.wang@intel.com>
Mon, 20 Aug 2018 03:31:00 +0000 (11:31 +0800)
committerJian J Wang <jian.j.wang@intel.com>
Wed, 29 Aug 2018 23:22:30 +0000 (07:22 +0800)
Same as SMM profile feature, a special #PF is used to set page attribute
to 'present' and a special #DB handler to reset it back to 'not-present',
right after the instruction causing #PF got executed.

Since the new #PF handler won't enter into dead-loop, the instruction
which caused the #PF will get chance to re-execute with accessible pages.

The exception message will still be printed out on debug console so that
the developer/QA can find that there's potential heap overflow or null
pointer access occurred.

Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
Acked-by: Laszlo Ersek <lersek@redhat.com>
UefiCpuPkg/CpuDxe/CpuDxe.h
UefiCpuPkg/CpuDxe/CpuDxe.inf
UefiCpuPkg/CpuDxe/CpuMp.c
UefiCpuPkg/CpuDxe/CpuPageTable.c

index 540f5f2dbf0fe4e8a4671c3e77d183f5ef281429..7d65e39e90d9be9acacabfe28b4acb37edac839f 100644 (file)
                                        EFI_MEMORY_RO    \\r
                                        )\r
 \r
+#define HEAP_GUARD_NONSTOP_MODE       \\r
+        ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT6|BIT1|BIT0)) > BIT6)\r
+\r
+#define NULL_DETECTION_NONSTOP_MODE   \\r
+        ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT6|BIT0)) > BIT6)\r
+\r
 /**\r
   Flush CPU data cache. If the instruction cache is fully coherent\r
   with all DMA operations then function can just return EFI_SUCCESS.\r
@@ -273,7 +279,40 @@ RefreshGcdMemoryAttributesFromPaging (
   VOID\r
   );\r
 \r
+/**\r
+  Special handler for #DB exception, which will restore the page attributes\r
+  (not-present). It should work with #PF handler which will set pages to\r
+  'present'.\r
+\r
+  @param ExceptionType  Exception type.\r
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+DebugExceptionHandler (\r
+  IN EFI_EXCEPTION_TYPE   InterruptType,\r
+  IN EFI_SYSTEM_CONTEXT   SystemContext\r
+  );\r
+\r
+/**\r
+  Special handler for #PF exception, which will set the pages which caused\r
+  #PF to be 'present'. The attribute of those pages should be restored in\r
+  the subsequent #DB handler.\r
+\r
+  @param ExceptionType  Exception type.\r
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PageFaultExceptionHandler (\r
+  IN EFI_EXCEPTION_TYPE   InterruptType,\r
+  IN EFI_SYSTEM_CONTEXT   SystemContext\r
+  );\r
+\r
 extern BOOLEAN mIsAllocatingPageTable;\r
+extern UINTN   mNumberOfProcessors;\r
 \r
 #endif\r
 \r
index 6a199b72f7387ce2331b84aea4d615b383b9378a..97a381b04665d69532f978fb73899617cb4ad42b 100644 (file)
@@ -46,6 +46,7 @@
   ReportStatusCodeLib\r
   MpInitLib\r
   TimerLib\r
+  PeCoffGetEntryPointLib\r
 \r
 [Sources]\r
   CpuDxe.c\r
@@ -79,6 +80,8 @@
 [Pcd]\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask    ## CONSUMES\r
   gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard                       ## CONSUMES\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask               ## CONSUMES\r
+  gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask    ## CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList              ## CONSUMES\r
   gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize                    ## CONSUMES\r
 \r
index 82145e762443ebdf9791d1e1757b2d87d02a53cc..5b3c87d24414a041649d4e45d113b3130080bda4 100644 (file)
@@ -673,10 +673,6 @@ InitializeMpExceptionStackSwitchHandlers (
   UINT8                           *GdtBuffer;\r
   UINT8                           *StackTop;\r
 \r
-  if (!PcdGetBool (PcdCpuStackGuard)) {\r
-    return;\r
-  }\r
-\r
   ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList);\r
   NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber;\r
 \r
@@ -790,6 +786,32 @@ InitializeMpExceptionStackSwitchHandlers (
   }\r
 }\r
 \r
+/**\r
+  Initializes MP exceptions handlers for special features, such as Heap Guard\r
+  and Stack Guard.\r
+**/\r
+VOID\r
+InitializeMpExceptionHandlers (\r
+  VOID\r
+  )\r
+{\r
+  //\r
+  // Enable non-stop mode for #PF triggered by Heap Guard or NULL Pointer\r
+  // Detection.\r
+  //\r
+  if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {\r
+    RegisterCpuInterruptHandler (EXCEPT_IA32_DEBUG, DebugExceptionHandler);\r
+    RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, PageFaultExceptionHandler);\r
+  }\r
+\r
+  //\r
+  // Setup stack switch for Stack Guard feature.\r
+  //\r
+  if (PcdGetBool (PcdCpuStackGuard)) {\r
+    InitializeMpExceptionStackSwitchHandlers ();\r
+  }\r
+}\r
+\r
 /**\r
   Initialize Multi-processor support.\r
 \r
@@ -814,9 +836,9 @@ InitializeMpSupport (
   DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors));\r
 \r
   //\r
-  // Initialize exception stack switch handlers for each logic processor.\r
+  // Initialize special exception handlers for each logic processor.\r
   //\r
-  InitializeMpExceptionStackSwitchHandlers ();\r
+  InitializeMpExceptionHandlers ();\r
 \r
   //\r
   // Update CPU healthy information from Guided HOB\r
index df021798c00988e2668b0ae1cb9b4a9b43e528e8..97257fbe48dfa2a27599c6e626d31e5fe6647298 100644 (file)
 #include <Library/MemoryAllocationLib.h>\r
 #include <Library/DebugLib.h>\r
 #include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/PeCoffGetEntryPointLib.h>\r
+#include <Library/SerialPortLib.h>\r
+#include <Library/SynchronizationLib.h>\r
+#include <Library/PrintLib.h>\r
 #include <Protocol/MpService.h>\r
 #include <Protocol/SmmBase2.h>\r
 #include <Register/Cpuid.h>\r
 #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull\r
 #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull\r
 \r
+#define MAX_PF_ENTRY_COUNT        10\r
+#define MAX_DEBUG_MESSAGE_LENGTH  0x100\r
+#define IA32_PF_EC_ID             BIT4\r
+\r
 typedef enum {\r
   PageNone,\r
   Page4K,\r
@@ -102,6 +110,12 @@ PAGE_TABLE_POOL                   *mPageTablePool = NULL;
 PAGE_TABLE_LIB_PAGING_CONTEXT     mPagingContext;\r
 EFI_SMM_BASE2_PROTOCOL            *mSmmBase2 = NULL;\r
 \r
+//\r
+// Record the page fault exception count for one instruction execution.\r
+//\r
+UINTN                     *mPFEntryCount;\r
+UINT64                    *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT];\r
+\r
 /**\r
  Check if current execution environment is in SMM mode or not, via\r
  EFI_SMM_BASE2_PROTOCOL.\r
@@ -1135,6 +1149,150 @@ AllocatePageTableMemory (
   return Buffer;\r
 }\r
 \r
+/**\r
+  Special handler for #DB exception, which will restore the page attributes\r
+  (not-present). It should work with #PF handler which will set pages to\r
+  'present'.\r
+\r
+  @param ExceptionType  Exception type.\r
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+DebugExceptionHandler (\r
+  IN EFI_EXCEPTION_TYPE   ExceptionType,\r
+  IN EFI_SYSTEM_CONTEXT   SystemContext\r
+  )\r
+{\r
+  UINTN     CpuIndex;\r
+  UINTN     PFEntry;\r
+  BOOLEAN   IsWpEnabled;\r
+\r
+  MpInitLibWhoAmI (&CpuIndex);\r
+\r
+  //\r
+  // Clear last PF entries\r
+  //\r
+  IsWpEnabled = IsReadOnlyPageWriteProtected ();\r
+  if (IsWpEnabled) {\r
+    DisableReadOnlyPageWriteProtect ();\r
+  }\r
+\r
+  for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) {\r
+    if (mLastPFEntryPointer[CpuIndex][PFEntry] != NULL) {\r
+      *mLastPFEntryPointer[CpuIndex][PFEntry] &= ~IA32_PG_P;\r
+    }\r
+  }\r
+\r
+  if (IsWpEnabled) {\r
+    EnableReadOnlyPageWriteProtect ();\r
+  }\r
+\r
+  //\r
+  // Reset page fault exception count for next page fault.\r
+  //\r
+  mPFEntryCount[CpuIndex] = 0;\r
+\r
+  //\r
+  // Flush TLB\r
+  //\r
+  CpuFlushTlb ();\r
+\r
+  //\r
+  // Clear TF in EFLAGS\r
+  //\r
+  if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) {\r
+    SystemContext.SystemContextIa32->Eflags &= (UINT32)~BIT8;\r
+  } else {\r
+    SystemContext.SystemContextX64->Rflags &= (UINT64)~BIT8;\r
+  }\r
+}\r
+\r
+/**\r
+  Special handler for #PF exception, which will set the pages which caused\r
+  #PF to be 'present'. The attribute of those pages should be restored in\r
+  the subsequent #DB handler.\r
+\r
+  @param ExceptionType  Exception type.\r
+  @param SystemContext  Pointer to EFI_SYSTEM_CONTEXT.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+PageFaultExceptionHandler (\r
+  IN EFI_EXCEPTION_TYPE   ExceptionType,\r
+  IN EFI_SYSTEM_CONTEXT   SystemContext\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  UINT64                          PFAddress;\r
+  PAGE_TABLE_LIB_PAGING_CONTEXT   PagingContext;\r
+  PAGE_ATTRIBUTE                  PageAttribute;\r
+  UINT64                          Attributes;\r
+  UINT64                          *PageEntry;\r
+  UINTN                           Index;\r
+  UINTN                           CpuIndex;\r
+  UINTN                           PageNumber;\r
+  BOOLEAN                         NonStopMode;\r
+\r
+  PFAddress = AsmReadCr2 () & ~EFI_PAGE_MASK;\r
+  if (PFAddress < BASE_4KB) {\r
+    NonStopMode = NULL_DETECTION_NONSTOP_MODE ? TRUE : FALSE;\r
+  } else {\r
+    NonStopMode = HEAP_GUARD_NONSTOP_MODE ? TRUE : FALSE;\r
+  }\r
+\r
+  if (NonStopMode) {\r
+    MpInitLibWhoAmI (&CpuIndex);\r
+    GetCurrentPagingContext (&PagingContext);\r
+    //\r
+    // Memory operation cross page boundary, like "rep mov" instruction, will\r
+    // cause infinite loop between this and Debug Trap handler. We have to make\r
+    // sure that current page and the page followed are both in PRESENT state.\r
+    //\r
+    PageNumber = 2;\r
+    while (PageNumber > 0) {\r
+      PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);\r
+      ASSERT(PageEntry != NULL);\r
+\r
+      if (PageEntry != NULL) {\r
+        Attributes = GetAttributesFromPageEntry (PageEntry);\r
+        if ((Attributes & EFI_MEMORY_RP) != 0) {\r
+          Attributes &= ~EFI_MEMORY_RP;\r
+          Status = AssignMemoryPageAttributes (&PagingContext, PFAddress,\r
+                                               EFI_PAGE_SIZE, Attributes, NULL);\r
+          if (!EFI_ERROR(Status)) {\r
+            Index = mPFEntryCount[CpuIndex];\r
+            //\r
+            // Re-retrieve page entry because above calling might update page\r
+            // table due to table split.\r
+            //\r
+            PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);\r
+            mLastPFEntryPointer[CpuIndex][Index++] = PageEntry;\r
+            mPFEntryCount[CpuIndex] = Index;\r
+          }\r
+        }\r
+      }\r
+\r
+      PFAddress += EFI_PAGE_SIZE;\r
+      --PageNumber;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Initialize the serial port before dumping.\r
+  //\r
+  SerialPortInitialize ();\r
+  //\r
+  // Display ExceptionType, CPU information and Image information\r
+  //\r
+  DumpCpuContext (ExceptionType, SystemContext);\r
+  if (!NonStopMode) {\r
+    CpuDeadLoop ();\r
+  }\r
+}\r
+\r
 /**\r
   Initialize the Page Table lib.\r
 **/\r
@@ -1158,6 +1316,15 @@ InitializePageTableLib (
     EnableReadOnlyPageWriteProtect ();\r
   }\r
 \r
+  if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {\r
+    mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mNumberOfProcessors);\r
+    ASSERT (mPFEntryCount != NULL);\r
+\r
+    mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])\r
+                          AllocateZeroPool (sizeof (mLastPFEntryPointer[0]) * mNumberOfProcessors);\r
+    ASSERT (mLastPFEntryPointer != NULL);\r
+  }\r
+\r
   DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType));\r
   DEBUG ((DEBUG_INFO, "  MachineType   - 0x%x\n", CurrentPagingContext.MachineType));\r
   DEBUG ((DEBUG_INFO, "  PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase));\r