UefiCpuPkg/CpuExceptionHandlerLib: Add stack switch support
If Stack Guard is enabled and there's really a stack overflow happened during boot, a Page Fault exception will be triggered. Because the stack is out of usage, the exception handler, which shares the stack with normal UEFI driver, cannot be executed and cannot dump the processor information. Without those information, it's very difficult for the BIOS developers locate the root cause of stack overflow. And without a workable stack, the developer cannot event use single step to debug the UEFI driver with JTAG debugger. In order to make sure the exception handler to execute normally after stack overflow. We need separate stacks for exception handlers in case of unusable stack. IA processor allows to switch to a new stack during handling interrupt and exception. But X64 and IA32 provides different ways to make it. X64 provides interrupt stack table (IST) to allow maximum 7 different exceptions to have new stack for its handler. IA32 doesn't have IST mechanism and can only use task gate to do it since task switch allows to load a new stack through its task-state segment (TSS). The new API, InitializeCpuExceptionHandlersEx, is implemented to complete extra initialization for stack switch of exception handler. Since setting up stack switch needs allocating new memory for new stack, new GDT table and task-state segment but the initialization method will be called in different phases which have no consistent way to reserve those memory, this new API is allowed to pass the reserved resources to complete the extra works. This is cannot be done by original InitializeCpuExceptionHandlers. Considering exception handler initialization for MP situation, this new API is also necessary, because AP is not supposed to allocate memory. So the memory needed for stack switch have to be reserved in BSP before waking up AP and then pass them to InitializeCpuExceptionHandlersEx afterwards. Since Stack Guard feature is available only for DXE phase at this time, the new API is fully implemented for DXE only. Other phases implement a dummy one which just calls InitializeCpuExceptionHandlers(). Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Eric Dong <eric.dong@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Suggested-by: Ayellet Wolman <ayellet.wolman@intel.com> Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Jian J Wang <jian.j.wang@intel.com> Reviewed-by: Jeff Fan <vanjeff_919@hotmail.com> Reviewed-by: Jiewen.yao@intel.com
This commit is contained in:
@@ -25,6 +25,10 @@ UINTN mEnabledInterruptNum = 0;
|
||||
|
||||
EXCEPTION_HANDLER_DATA mExceptionHandlerData;
|
||||
|
||||
UINT8 mNewStack[CPU_STACK_SWITCH_EXCEPTION_NUMBER *
|
||||
CPU_KNOWN_GOOD_STACK_SIZE];
|
||||
UINT8 mNewGdt[CPU_TSS_GDT_SIZE];
|
||||
|
||||
/**
|
||||
Common exception handler.
|
||||
|
||||
@@ -197,3 +201,83 @@ RegisterCpuInterruptHandler (
|
||||
{
|
||||
return RegisterCpuInterruptHandlerWorker (InterruptType, InterruptHandler, &mExceptionHandlerData);
|
||||
}
|
||||
|
||||
/**
|
||||
Initializes CPU exceptions entries and setup stack switch for given exceptions.
|
||||
|
||||
This method will call InitializeCpuExceptionHandlers() to setup default
|
||||
exception handlers unless indicated not to do it explicitly.
|
||||
|
||||
If InitData is passed with NULL, this method will use the resource reserved
|
||||
by global variables to initialize it; Otherwise it will use data in InitData
|
||||
to setup stack switch. This is for the different use cases in DxeCore and
|
||||
Cpu MP exception initialization.
|
||||
|
||||
@param[in] VectorInfo Pointer to reserved vector list.
|
||||
@param[in] InitData Pointer to data required to setup stack switch for
|
||||
given exceptions.
|
||||
|
||||
@retval EFI_SUCCESS The exceptions have been successfully
|
||||
initialized.
|
||||
@retval EFI_INVALID_PARAMETER VectorInfo or InitData contains invalid
|
||||
content.
|
||||
|
||||
**/
|
||||
EFI_STATUS
|
||||
EFIAPI
|
||||
InitializeCpuExceptionHandlersEx (
|
||||
IN EFI_VECTOR_HANDOFF_INFO *VectorInfo OPTIONAL,
|
||||
IN CPU_EXCEPTION_INIT_DATA *InitData OPTIONAL
|
||||
)
|
||||
{
|
||||
EFI_STATUS Status;
|
||||
CPU_EXCEPTION_INIT_DATA EssData;
|
||||
IA32_DESCRIPTOR Idtr;
|
||||
IA32_DESCRIPTOR Gdtr;
|
||||
|
||||
//
|
||||
// To avoid repeat initialization of default handlers, the caller should pass
|
||||
// an extended init data with InitDefaultHandlers set to FALSE. There's no
|
||||
// need to call this method to just initialize default handlers. Call non-ex
|
||||
// version instead; or this method must be implemented as a simple wrapper of
|
||||
// non-ex version of it, if this version has to be called.
|
||||
//
|
||||
if (InitData == NULL || InitData->X64.InitDefaultHandlers) {
|
||||
Status = InitializeCpuExceptionHandlers (VectorInfo);
|
||||
} else {
|
||||
Status = EFI_SUCCESS;
|
||||
}
|
||||
|
||||
if (!EFI_ERROR (Status)) {
|
||||
//
|
||||
// Initializing stack switch is only necessary for Stack Guard functionality.
|
||||
//
|
||||
if (PcdGetBool (PcdCpuStackGuard)) {
|
||||
if (InitData == NULL) {
|
||||
SetMem (mNewGdt, sizeof (mNewGdt), 0);
|
||||
|
||||
AsmReadIdtr (&Idtr);
|
||||
AsmReadGdtr (&Gdtr);
|
||||
|
||||
EssData.X64.Revision = CPU_EXCEPTION_INIT_DATA_REV;
|
||||
EssData.X64.KnownGoodStackTop = (UINTN)mNewStack;
|
||||
EssData.X64.KnownGoodStackSize = CPU_KNOWN_GOOD_STACK_SIZE;
|
||||
EssData.X64.StackSwitchExceptions = CPU_STACK_SWITCH_EXCEPTION_LIST;
|
||||
EssData.X64.StackSwitchExceptionNumber = CPU_STACK_SWITCH_EXCEPTION_NUMBER;
|
||||
EssData.X64.IdtTable = (VOID *)Idtr.Base;
|
||||
EssData.X64.IdtTableSize = Idtr.Limit + 1;
|
||||
EssData.X64.GdtTable = mNewGdt;
|
||||
EssData.X64.GdtTableSize = sizeof (mNewGdt);
|
||||
EssData.X64.ExceptionTssDesc = mNewGdt + Gdtr.Limit + 1;
|
||||
EssData.X64.ExceptionTssDescSize = CPU_TSS_DESC_SIZE;
|
||||
EssData.X64.ExceptionTss = mNewGdt + Gdtr.Limit + 1 + CPU_TSS_DESC_SIZE;
|
||||
EssData.X64.ExceptionTssSize = CPU_TSS_SIZE;
|
||||
|
||||
InitData = &EssData;
|
||||
}
|
||||
Status = ArchSetupExcpetionStack (InitData);
|
||||
}
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
Reference in New Issue
Block a user