UefiCpuPkg/CpuDxe: implement non-stop mode for uefi

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>
This commit is contained in:
Jian J Wang 2018-08-20 11:31:00 +08:00
parent 16b918bbaf
commit dcc026217f
4 changed files with 237 additions and 6 deletions

View File

@ -57,6 +57,12 @@
EFI_MEMORY_RO \ EFI_MEMORY_RO \
) )
#define HEAP_GUARD_NONSTOP_MODE \
((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT6|BIT1|BIT0)) > BIT6)
#define NULL_DETECTION_NONSTOP_MODE \
((PcdGet8 (PcdNullPointerDetectionPropertyMask) & (BIT6|BIT0)) > BIT6)
/** /**
Flush CPU data cache. If the instruction cache is fully coherent Flush CPU data cache. If the instruction cache is fully coherent
with all DMA operations then function can just return EFI_SUCCESS. with all DMA operations then function can just return EFI_SUCCESS.
@ -273,7 +279,40 @@ RefreshGcdMemoryAttributesFromPaging (
VOID VOID
); );
/**
Special handler for #DB exception, which will restore the page attributes
(not-present). It should work with #PF handler which will set pages to
'present'.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
DebugExceptionHandler (
IN EFI_EXCEPTION_TYPE InterruptType,
IN EFI_SYSTEM_CONTEXT SystemContext
);
/**
Special handler for #PF exception, which will set the pages which caused
#PF to be 'present'. The attribute of those pages should be restored in
the subsequent #DB handler.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
PageFaultExceptionHandler (
IN EFI_EXCEPTION_TYPE InterruptType,
IN EFI_SYSTEM_CONTEXT SystemContext
);
extern BOOLEAN mIsAllocatingPageTable; extern BOOLEAN mIsAllocatingPageTable;
extern UINTN mNumberOfProcessors;
#endif #endif

View File

@ -46,6 +46,7 @@
ReportStatusCodeLib ReportStatusCodeLib
MpInitLib MpInitLib
TimerLib TimerLib
PeCoffGetEntryPointLib
[Sources] [Sources]
CpuDxe.c CpuDxe.c
@ -79,6 +80,8 @@
[Pcd] [Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdPteMemoryEncryptionAddressOrMask ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdHeapGuardPropertyMask ## CONSUMES
gEfiMdeModulePkgTokenSpaceGuid.PcdNullPointerDetectionPropertyMask ## CONSUMES
gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList ## CONSUMES gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList ## CONSUMES
gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize ## CONSUMES gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize ## CONSUMES

View File

@ -673,10 +673,6 @@ InitializeMpExceptionStackSwitchHandlers (
UINT8 *GdtBuffer; UINT8 *GdtBuffer;
UINT8 *StackTop; UINT8 *StackTop;
if (!PcdGetBool (PcdCpuStackGuard)) {
return;
}
ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList); ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList);
NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber; NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber;
@ -790,6 +786,32 @@ InitializeMpExceptionStackSwitchHandlers (
} }
} }
/**
Initializes MP exceptions handlers for special features, such as Heap Guard
and Stack Guard.
**/
VOID
InitializeMpExceptionHandlers (
VOID
)
{
//
// Enable non-stop mode for #PF triggered by Heap Guard or NULL Pointer
// Detection.
//
if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {
RegisterCpuInterruptHandler (EXCEPT_IA32_DEBUG, DebugExceptionHandler);
RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, PageFaultExceptionHandler);
}
//
// Setup stack switch for Stack Guard feature.
//
if (PcdGetBool (PcdCpuStackGuard)) {
InitializeMpExceptionStackSwitchHandlers ();
}
}
/** /**
Initialize Multi-processor support. Initialize Multi-processor support.
@ -814,9 +836,9 @@ InitializeMpSupport (
DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors)); DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors));
// //
// Initialize exception stack switch handlers for each logic processor. // Initialize special exception handlers for each logic processor.
// //
InitializeMpExceptionStackSwitchHandlers (); InitializeMpExceptionHandlers ();
// //
// Update CPU healthy information from Guided HOB // Update CPU healthy information from Guided HOB

View File

@ -22,6 +22,10 @@
#include <Library/MemoryAllocationLib.h> #include <Library/MemoryAllocationLib.h>
#include <Library/DebugLib.h> #include <Library/DebugLib.h>
#include <Library/UefiBootServicesTableLib.h> #include <Library/UefiBootServicesTableLib.h>
#include <Library/PeCoffGetEntryPointLib.h>
#include <Library/SerialPortLib.h>
#include <Library/SynchronizationLib.h>
#include <Library/PrintLib.h>
#include <Protocol/MpService.h> #include <Protocol/MpService.h>
#include <Protocol/SmmBase2.h> #include <Protocol/SmmBase2.h>
#include <Register/Cpuid.h> #include <Register/Cpuid.h>
@ -73,6 +77,10 @@
#define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
#define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
#define MAX_PF_ENTRY_COUNT 10
#define MAX_DEBUG_MESSAGE_LENGTH 0x100
#define IA32_PF_EC_ID BIT4
typedef enum { typedef enum {
PageNone, PageNone,
Page4K, Page4K,
@ -102,6 +110,12 @@ PAGE_TABLE_POOL *mPageTablePool = NULL;
PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext; PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext;
EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL; EFI_SMM_BASE2_PROTOCOL *mSmmBase2 = NULL;
//
// Record the page fault exception count for one instruction execution.
//
UINTN *mPFEntryCount;
UINT64 *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT];
/** /**
Check if current execution environment is in SMM mode or not, via Check if current execution environment is in SMM mode or not, via
EFI_SMM_BASE2_PROTOCOL. EFI_SMM_BASE2_PROTOCOL.
@ -1135,6 +1149,150 @@ AllocatePageTableMemory (
return Buffer; return Buffer;
} }
/**
Special handler for #DB exception, which will restore the page attributes
(not-present). It should work with #PF handler which will set pages to
'present'.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
DebugExceptionHandler (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
UINTN CpuIndex;
UINTN PFEntry;
BOOLEAN IsWpEnabled;
MpInitLibWhoAmI (&CpuIndex);
//
// Clear last PF entries
//
IsWpEnabled = IsReadOnlyPageWriteProtected ();
if (IsWpEnabled) {
DisableReadOnlyPageWriteProtect ();
}
for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) {
if (mLastPFEntryPointer[CpuIndex][PFEntry] != NULL) {
*mLastPFEntryPointer[CpuIndex][PFEntry] &= ~IA32_PG_P;
}
}
if (IsWpEnabled) {
EnableReadOnlyPageWriteProtect ();
}
//
// Reset page fault exception count for next page fault.
//
mPFEntryCount[CpuIndex] = 0;
//
// Flush TLB
//
CpuFlushTlb ();
//
// Clear TF in EFLAGS
//
if (mPagingContext.MachineType == IMAGE_FILE_MACHINE_I386) {
SystemContext.SystemContextIa32->Eflags &= (UINT32)~BIT8;
} else {
SystemContext.SystemContextX64->Rflags &= (UINT64)~BIT8;
}
}
/**
Special handler for #PF exception, which will set the pages which caused
#PF to be 'present'. The attribute of those pages should be restored in
the subsequent #DB handler.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
PageFaultExceptionHandler (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
EFI_STATUS Status;
UINT64 PFAddress;
PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext;
PAGE_ATTRIBUTE PageAttribute;
UINT64 Attributes;
UINT64 *PageEntry;
UINTN Index;
UINTN CpuIndex;
UINTN PageNumber;
BOOLEAN NonStopMode;
PFAddress = AsmReadCr2 () & ~EFI_PAGE_MASK;
if (PFAddress < BASE_4KB) {
NonStopMode = NULL_DETECTION_NONSTOP_MODE ? TRUE : FALSE;
} else {
NonStopMode = HEAP_GUARD_NONSTOP_MODE ? TRUE : FALSE;
}
if (NonStopMode) {
MpInitLibWhoAmI (&CpuIndex);
GetCurrentPagingContext (&PagingContext);
//
// Memory operation cross page boundary, 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.
//
PageNumber = 2;
while (PageNumber > 0) {
PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);
ASSERT(PageEntry != NULL);
if (PageEntry != NULL) {
Attributes = GetAttributesFromPageEntry (PageEntry);
if ((Attributes & EFI_MEMORY_RP) != 0) {
Attributes &= ~EFI_MEMORY_RP;
Status = AssignMemoryPageAttributes (&PagingContext, PFAddress,
EFI_PAGE_SIZE, Attributes, NULL);
if (!EFI_ERROR(Status)) {
Index = mPFEntryCount[CpuIndex];
//
// Re-retrieve page entry because above calling might update page
// table due to table split.
//
PageEntry = GetPageTableEntry (&PagingContext, PFAddress, &PageAttribute);
mLastPFEntryPointer[CpuIndex][Index++] = PageEntry;
mPFEntryCount[CpuIndex] = Index;
}
}
}
PFAddress += EFI_PAGE_SIZE;
--PageNumber;
}
}
//
// Initialize the serial port before dumping.
//
SerialPortInitialize ();
//
// Display ExceptionType, CPU information and Image information
//
DumpCpuContext (ExceptionType, SystemContext);
if (!NonStopMode) {
CpuDeadLoop ();
}
}
/** /**
Initialize the Page Table lib. Initialize the Page Table lib.
**/ **/
@ -1158,6 +1316,15 @@ InitializePageTableLib (
EnableReadOnlyPageWriteProtect (); EnableReadOnlyPageWriteProtect ();
} }
if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {
mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mNumberOfProcessors);
ASSERT (mPFEntryCount != NULL);
mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])
AllocateZeroPool (sizeof (mLastPFEntryPointer[0]) * mNumberOfProcessors);
ASSERT (mLastPFEntryPointer != NULL);
}
DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType)); DEBUG ((DEBUG_INFO, "CurrentPagingContext:\n", CurrentPagingContext.MachineType));
DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType)); DEBUG ((DEBUG_INFO, " MachineType - 0x%x\n", CurrentPagingContext.MachineType));
DEBUG ((DEBUG_INFO, " PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase)); DEBUG ((DEBUG_INFO, " PageTableBase - 0x%x\n", CurrentPagingContext.ContextData.X64.PageTableBase));