UefiCpuPkg/PiSmmCpuDxeSmm: implement non-stop mode for SMM

Since SMM profile feature has already implemented non-stop mode if #PF
occurred, this patch just makes use of the existing implementation to
accommodate heap guard and NULL pointer detection feature.

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>
diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c b/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
index 9300a23..a32b736 100644
--- a/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
+++ b/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/PageTbl.c
@@ -38,7 +38,9 @@
 

   mPhysicalAddressBits = 32;

 

-  if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {

+  if (FeaturePcdGet (PcdCpuSmmProfileEnable) ||

+      HEAP_GUARD_NONSTOP_MODE ||

+      NULL_DETECTION_NONSTOP_MODE) {

     //

     // Set own Page Fault entry instead of the default one, because SMM Profile

     // feature depends on IRET instruction to do Single Step

@@ -129,6 +131,11 @@
           DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);

         );

       }

+

+      if (HEAP_GUARD_NONSTOP_MODE) {

+        GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);

+        goto Exit;

+      }

     }

     CpuDeadLoop ();

   }

@@ -146,6 +153,26 @@
       );

       CpuDeadLoop ();

     }

+

+    //

+    // If NULL pointer was just accessed

+    //

+    if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 &&

+        (PFAddress < EFI_PAGE_SIZE)) {

+      DumpCpuContext (InterruptType, SystemContext);

+      DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));

+      DEBUG_CODE (

+        DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);

+      );

+

+      if (NULL_DETECTION_NONSTOP_MODE) {

+        GuardPagePFHandler (SystemContext.SystemContextIa32->ExceptionData);

+        goto Exit;

+      }

+

+      CpuDeadLoop ();

+    }

+

     if (IsSmmCommBufferForbiddenAddress (PFAddress)) {

       DumpCpuContext (InterruptType, SystemContext);

       DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%x)!\n", PFAddress));

@@ -156,19 +183,6 @@
     }

   }

 

-  //

-  // If NULL pointer was just accessed

-  //

-  if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 &&

-      (PFAddress < EFI_PAGE_SIZE)) {

-    DumpCpuContext (InterruptType, SystemContext);

-    DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));

-    DEBUG_CODE (

-      DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextIa32->Eip);

-    );

-    CpuDeadLoop ();

-  }

-

   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {

     SmmProfilePFHandler (

       SystemContext.SystemContextIa32->Eip,

@@ -179,6 +193,7 @@
     SmiDefaultPFHandler ();

   }

 

+Exit:

   ReleaseSpinLock (mPFLock);

 }

 

diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiException.nasm b/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiException.nasm
index fa02c10..879fa0b 100644
--- a/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiException.nasm
+++ b/UefiCpuPkg/PiSmmCpuDxeSmm/Ia32/SmiException.nasm
@@ -20,6 +20,7 @@
 

 extern  ASM_PFX(FeaturePcdGet (PcdCpuSmmProfileEnable))

 extern  ASM_PFX(SmiPFHandler)

+extern  ASM_PFX(mSetupDebugTrap)

 

 global  ASM_PFX(gcSmiIdtr)

 global  ASM_PFX(gcSmiGdtr)

@@ -673,7 +674,7 @@
     mov     esp, ebp

 

 ; Set single step DB# if SMM profile is enabled and page fault exception happens

-    cmp     byte [dword ASM_PFX(FeaturePcdGet (PcdCpuSmmProfileEnable))], 0

+    cmp     byte [dword ASM_PFX(mSetupDebugTrap)], 0

     jz      @Done2

 

 ; Create return context for iretd in stub function

diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c
index b4fe0bc..a743cf6 100644
--- a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c
+++ b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.c
@@ -52,6 +52,11 @@
 BOOLEAN                   mSmmProfileStart = FALSE;

 

 //

+// The flag indicates if #DB will be setup in #PF handler.

+//

+BOOLEAN                   mSetupDebugTrap = FALSE;

+

+//

 // Record the page fault exception count for one instruction execution.

 //

 UINTN                     *mPFEntryCount;

@@ -229,7 +234,9 @@
   UINTN  CpuIndex;

   UINTN  PFEntry;

 

-  if (!mSmmProfileStart) {

+  if (!mSmmProfileStart &&

+      !HEAP_GUARD_NONSTOP_MODE &&

+      !NULL_DETECTION_NONSTOP_MODE) {

     return;

   }

   CpuIndex = GetCpuIndex ();

@@ -1174,7 +1181,9 @@
   //

   // Skip SMM profile initialization if feature is disabled

   //

-  if (!FeaturePcdGet (PcdCpuSmmProfileEnable)) {

+  if (!FeaturePcdGet (PcdCpuSmmProfileEnable) &&

+      !HEAP_GUARD_NONSTOP_MODE &&

+      !NULL_DETECTION_NONSTOP_MODE) {

     return;

   }

 

@@ -1187,6 +1196,11 @@
   // Initialize profile IDT.

   //

   InitIdtr ();

+

+  //

+  // Tell #PF handler to prepare a #DB subsequently.

+  //

+  mSetupDebugTrap = TRUE;

 }

 

 /**

@@ -1295,6 +1309,46 @@
 }

 

 /**

+  Handler for Page Fault triggered by Guard page.

+

+  @param  ErrorCode  The Error code of exception.

+

+**/

+VOID

+GuardPagePFHandler (

+  UINTN ErrorCode

+  )

+{

+  UINT64                *PageTable;

+  UINT64                PFAddress;

+  UINT64                RestoreAddress;

+  UINTN                 RestorePageNumber;

+  UINTN                 CpuIndex;

+

+  PageTable         = (UINT64 *)AsmReadCr3 ();

+  PFAddress         = AsmReadCr2 ();

+  CpuIndex          = GetCpuIndex ();

+

+  //

+  // Memory operation cross pages, like "rep mov" instruction, will cause

+  // infinite loop between this and Debug Trap handler. We have to make sure

+  // that current page and the page followed are both in PRESENT state.

+  //

+  RestorePageNumber = 2;

+  RestoreAddress = PFAddress;

+  while (RestorePageNumber > 0) {

+    RestorePageTableBelow4G (PageTable, RestoreAddress, CpuIndex, ErrorCode);

+    RestoreAddress += EFI_PAGE_SIZE;

+    RestorePageNumber--;

+  }

+

+  //

+  // Flush TLB

+  //

+  CpuFlushTlb ();

+}

+

+/**

   The Page fault handler to save SMM profile data.

 

   @param  Rip        The RIP when exception happens.

diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.h b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.h
index 04a3dfb..c2a4823 100644
--- a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.h
+++ b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfile.h
@@ -114,6 +114,17 @@
   VOID

   );

 

+/**

+  Handler for Page Fault triggered by Guard page.

+

+  @param  ErrorCode  The Error code of exception.

+

+**/

+VOID

+GuardPagePFHandler (

+  UINTN ErrorCode

+  );

+

 //

 // The flag indicates if execute-disable is supported by processor.

 //

@@ -122,5 +133,9 @@
 // The flag indicates if execute-disable is enabled on processor.

 //

 extern BOOLEAN    mXdEnabled;

+//

+// The flag indicates if #DB will be setup in #PF handler.

+//

+extern BOOLEAN    mSetupDebugTrap;

 

 #endif // _SMM_PROFILE_H_

diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfileInternal.h b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfileInternal.h
index 1613e9c..bacb2f8 100644
--- a/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfileInternal.h
+++ b/UefiCpuPkg/PiSmmCpuDxeSmm/SmmProfileInternal.h
@@ -64,6 +64,12 @@
 #define   MSR_DEBUG_CTL_BTINT        0x100

 #define MSR_DS_AREA                  0x600

 

+#define HEAP_GUARD_NONSTOP_MODE      \

+        ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT6|BIT3|BIT2)) > BIT6)

+

+#define NULL_DETECTION_NONSTOP_MODE  \

+        ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT6|BIT1)) > BIT6)

+

 typedef struct {

   EFI_PHYSICAL_ADDRESS   Base;

   EFI_PHYSICAL_ADDRESS   Top;

diff --git a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c
index 0fe944f..5bb7d57 100644
--- a/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c
+++ b/UefiCpuPkg/PiSmmCpuDxeSmm/X64/PageTbl.c
@@ -300,7 +300,9 @@
     }

   }

 

-  if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {

+  if (FeaturePcdGet (PcdCpuSmmProfileEnable) ||

+      HEAP_GUARD_NONSTOP_MODE ||

+      NULL_DETECTION_NONSTOP_MODE) {

     //

     // Set own Page Fault entry instead of the default one, because SMM Profile

     // feature depends on IRET instruction to do Single Step

@@ -846,6 +848,11 @@
           DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);

         );

       }

+

+      if (HEAP_GUARD_NONSTOP_MODE) {

+        GuardPagePFHandler (SystemContext.SystemContextX64->ExceptionData);

+        goto Exit;

+      }

     }

     CpuDeadLoop ();

   }

@@ -863,6 +870,26 @@
       );

       CpuDeadLoop ();

     }

+

+    //

+    // If NULL pointer was just accessed

+    //

+    if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 &&

+        (PFAddress < EFI_PAGE_SIZE)) {

+      DumpCpuContext (InterruptType, SystemContext);

+      DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));

+      DEBUG_CODE (

+        DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);

+      );

+

+      if (NULL_DETECTION_NONSTOP_MODE) {

+        GuardPagePFHandler (SystemContext.SystemContextX64->ExceptionData);

+        goto Exit;

+      }

+

+      CpuDeadLoop ();

+    }

+

     if (IsSmmCommBufferForbiddenAddress (PFAddress)) {

       DumpCpuContext (InterruptType, SystemContext);

       DEBUG ((DEBUG_ERROR, "Access SMM communication forbidden address (0x%lx)!\n", PFAddress));

@@ -873,19 +900,6 @@
     }

   }

 

-  //

-  // If NULL pointer was just accessed

-  //

-  if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0 &&

-      (PFAddress < EFI_PAGE_SIZE)) {

-    DumpCpuContext (InterruptType, SystemContext);

-    DEBUG ((DEBUG_ERROR, "!!! NULL pointer access !!!\n"));

-    DEBUG_CODE (

-      DumpModuleInfoByIp ((UINTN)SystemContext.SystemContextX64->Rip);

-    );

-    CpuDeadLoop ();

-  }

-

   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {

     SmmProfilePFHandler (

       SystemContext.SystemContextX64->Rip,

@@ -895,6 +909,7 @@
     SmiDefaultPFHandler ();

   }

 

+Exit:

   ReleaseSpinLock (mPFLock);

 }