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:
Jian J Wang 2017-12-07 20:15:12 +08:00 committed by Star Zeng
parent 364a54742f
commit 0ff5aa9cae
14 changed files with 984 additions and 3 deletions

View File

@ -48,6 +48,17 @@
0xb21d9148, 0x9211, 0x4d8f, { 0xad, 0xd3, 0x66, 0xb1, 0x89, 0xc9, 0x2c, 0x83 } \
}
#define CPU_STACK_SWITCH_EXCEPTION_NUMBER \
FixedPcdGetSize (PcdCpuStackSwitchExceptionList)
#define CPU_STACK_SWITCH_EXCEPTION_LIST \
FixedPcdGetPtr (PcdCpuStackSwitchExceptionList)
#define CPU_KNOWN_GOOD_STACK_SIZE \
FixedPcdGet32 (PcdCpuKnownGoodStackSize)
#define CPU_TSS_GDT_SIZE (SIZE_2KB + CPU_TSS_DESC_SIZE + CPU_TSS_SIZE)
//
// Record exception handler information
//
@ -288,5 +299,33 @@ CommonExceptionHandlerWorker (
IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
);
/**
Setup separate stack for specific exceptions.
@param[in] StackSwitchData Pointer to data required for setuping up
stack switch.
@retval EFI_SUCCESS The exceptions have been successfully
initialized with new stack.
@retval EFI_INVALID_PARAMETER StackSwitchData contains invalid content.
**/
EFI_STATUS
ArchSetupExcpetionStack (
IN CPU_EXCEPTION_INIT_DATA *StackSwitchData
);
/**
Return address map of exception handler template so that C code can generate
exception tables. The template is only for exceptions using task gate instead
of interrupt gate.
@param AddressMap Pointer to a buffer where the address map is returned.
**/
VOID
EFIAPI
AsmGetTssTemplateMap (
OUT EXCEPTION_HANDLER_TEMPLATE_MAP *AddressMap
);
#endif

View File

@ -30,6 +30,7 @@
[Sources.Ia32]
Ia32/ExceptionHandlerAsm.asm
Ia32/ExceptionHandlerAsm.nasm
Ia32/ExceptionTssEntryAsm.nasm
Ia32/ExceptionHandlerAsm.S
Ia32/ArchExceptionHandler.c
Ia32/ArchInterruptDefs.h
@ -47,6 +48,11 @@
PeiDxeSmmCpuException.c
DxeException.c
[Pcd]
gEfiMdeModulePkgTokenSpaceGuid.PcdCpuStackGuard
gUefiCpuPkgTokenSpaceGuid.PcdCpuStackSwitchExceptionList
gUefiCpuPkgTokenSpaceGuid.PcdCpuKnownGoodStackSize
[Packages]
MdePkg/MdePkg.dec
MdeModulePkg/MdeModulePkg.dec

View File

@ -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;
}

View File

@ -107,6 +107,194 @@ ArchRestoreExceptionContext (
SystemContext.SystemContextIa32->ExceptionData = ReservedVectors[ExceptionType].ExceptionData;
}
/**
Setup separate stack for given exceptions.
@param[in] StackSwitchData Pointer to data required for setuping up
stack switch.
@retval EFI_SUCCESS The exceptions have been successfully
initialized with new stack.
@retval EFI_INVALID_PARAMETER StackSwitchData contains invalid content.
**/
EFI_STATUS
ArchSetupExcpetionStack (
IN CPU_EXCEPTION_INIT_DATA *StackSwitchData
)
{
IA32_DESCRIPTOR Gdtr;
IA32_DESCRIPTOR Idtr;
IA32_IDT_GATE_DESCRIPTOR *IdtTable;
IA32_TSS_DESCRIPTOR *TssDesc;
IA32_TASK_STATE_SEGMENT *Tss;
UINTN StackTop;
UINTN Index;
UINTN Vector;
UINTN TssBase;
UINTN GdtSize;
EXCEPTION_HANDLER_TEMPLATE_MAP TemplateMap;
if (StackSwitchData == NULL ||
StackSwitchData->Ia32.Revision != CPU_EXCEPTION_INIT_DATA_REV ||
StackSwitchData->Ia32.KnownGoodStackTop == 0 ||
StackSwitchData->Ia32.KnownGoodStackSize == 0 ||
StackSwitchData->Ia32.StackSwitchExceptions == NULL ||
StackSwitchData->Ia32.StackSwitchExceptionNumber == 0 ||
StackSwitchData->Ia32.StackSwitchExceptionNumber > CPU_EXCEPTION_NUM ||
StackSwitchData->Ia32.GdtTable == NULL ||
StackSwitchData->Ia32.IdtTable == NULL ||
StackSwitchData->Ia32.ExceptionTssDesc == NULL ||
StackSwitchData->Ia32.ExceptionTss == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// The caller is responsible for that the GDT table, no matter the existing
// one or newly allocated, has enough space to hold descriptors for exception
// task-state segments.
//
if (((UINTN)StackSwitchData->Ia32.GdtTable & (IA32_GDT_ALIGNMENT - 1)) != 0) {
return EFI_INVALID_PARAMETER;
}
if ((UINTN)StackSwitchData->Ia32.ExceptionTssDesc < (UINTN)(StackSwitchData->Ia32.GdtTable)) {
return EFI_INVALID_PARAMETER;
}
if ((UINTN)StackSwitchData->Ia32.ExceptionTssDesc + StackSwitchData->Ia32.ExceptionTssDescSize >
((UINTN)(StackSwitchData->Ia32.GdtTable) + StackSwitchData->Ia32.GdtTableSize)) {
return EFI_INVALID_PARAMETER;
}
//
// We need one descriptor and one TSS for current task and every exception
// specified.
//
if (StackSwitchData->Ia32.ExceptionTssDescSize <
sizeof (IA32_TSS_DESCRIPTOR) * (StackSwitchData->Ia32.StackSwitchExceptionNumber + 1)) {
return EFI_INVALID_PARAMETER;
}
if (StackSwitchData->Ia32.ExceptionTssSize <
sizeof (IA32_TASK_STATE_SEGMENT) * (StackSwitchData->Ia32.StackSwitchExceptionNumber + 1)) {
return EFI_INVALID_PARAMETER;
}
TssDesc = StackSwitchData->Ia32.ExceptionTssDesc;
Tss = StackSwitchData->Ia32.ExceptionTss;
//
// Initialize new GDT table and/or IDT table, if any
//
AsmReadIdtr (&Idtr);
AsmReadGdtr (&Gdtr);
GdtSize = (UINTN)TssDesc +
sizeof (IA32_TSS_DESCRIPTOR) *
(StackSwitchData->Ia32.StackSwitchExceptionNumber + 1) -
(UINTN)(StackSwitchData->Ia32.GdtTable);
if ((UINTN)StackSwitchData->Ia32.GdtTable != Gdtr.Base) {
CopyMem (StackSwitchData->Ia32.GdtTable, (VOID *)Gdtr.Base, Gdtr.Limit + 1);
Gdtr.Base = (UINTN)StackSwitchData->Ia32.GdtTable;
Gdtr.Limit = (UINT16)GdtSize - 1;
}
if ((UINTN)StackSwitchData->Ia32.IdtTable != Idtr.Base) {
Idtr.Base = (UINTN)StackSwitchData->Ia32.IdtTable;
}
if (StackSwitchData->Ia32.IdtTableSize > 0) {
Idtr.Limit = (UINT16)(StackSwitchData->Ia32.IdtTableSize - 1);
}
//
// Fixup current task descriptor. Task-state segment for current task will
// be filled by processor during task switching.
//
TssBase = (UINTN)Tss;
TssDesc->Bits.LimitLow = sizeof(IA32_TASK_STATE_SEGMENT) - 1;
TssDesc->Bits.BaseLow = (UINT16)TssBase;
TssDesc->Bits.BaseMid = (UINT8)(TssBase >> 16);
TssDesc->Bits.Type = IA32_GDT_TYPE_TSS;
TssDesc->Bits.P = 1;
TssDesc->Bits.LimitHigh = 0;
TssDesc->Bits.BaseHigh = (UINT8)(TssBase >> 24);
//
// Fixup exception task descriptor and task-state segment
//
AsmGetTssTemplateMap (&TemplateMap);
StackTop = StackSwitchData->Ia32.KnownGoodStackTop - CPU_STACK_ALIGNMENT;
StackTop = (UINTN)ALIGN_POINTER (StackTop, CPU_STACK_ALIGNMENT);
IdtTable = StackSwitchData->Ia32.IdtTable;
for (Index = 0; Index < StackSwitchData->Ia32.StackSwitchExceptionNumber; ++Index) {
TssDesc += 1;
Tss += 1;
//
// Fixup TSS descriptor
//
TssBase = (UINTN)Tss;
TssDesc->Bits.LimitLow = sizeof(IA32_TASK_STATE_SEGMENT) - 1;
TssDesc->Bits.BaseLow = (UINT16)TssBase;
TssDesc->Bits.BaseMid = (UINT8)(TssBase >> 16);
TssDesc->Bits.Type = IA32_GDT_TYPE_TSS;
TssDesc->Bits.P = 1;
TssDesc->Bits.LimitHigh = 0;
TssDesc->Bits.BaseHigh = (UINT8)(TssBase >> 24);
//
// Fixup TSS
//
Vector = StackSwitchData->Ia32.StackSwitchExceptions[Index];
if (Vector >= CPU_EXCEPTION_NUM ||
Vector >= (Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)) {
continue;
}
Tss->EIP = (UINT32)(TemplateMap.ExceptionStart
+ Vector * TemplateMap.ExceptionStubHeaderSize);
Tss->EFLAGS = 0x2;
Tss->ESP = StackTop;
Tss->CR3 = AsmReadCr3 ();
Tss->ES = AsmReadEs ();
Tss->CS = AsmReadCs ();
Tss->SS = AsmReadSs ();
Tss->DS = AsmReadDs ();
Tss->FS = AsmReadFs ();
Tss->GS = AsmReadGs ();
StackTop -= StackSwitchData->Ia32.KnownGoodStackSize;
//
// Update IDT to use Task Gate for given exception
//
IdtTable[Vector].Bits.OffsetLow = 0;
IdtTable[Vector].Bits.Selector = (UINT16)((UINTN)TssDesc - Gdtr.Base);
IdtTable[Vector].Bits.Reserved_0 = 0;
IdtTable[Vector].Bits.GateType = IA32_IDT_GATE_TYPE_TASK;
IdtTable[Vector].Bits.OffsetHigh = 0;
}
//
// Publish GDT
//
AsmWriteGdtr (&Gdtr);
//
// Load current task
//
AsmWriteTr ((UINT16)((UINTN)StackSwitchData->Ia32.ExceptionTssDesc - Gdtr.Base));
//
// Publish IDT
//
AsmWriteIdtr (&Idtr);
return EFI_SUCCESS;
}
/**
Display processor context.

View File

@ -41,4 +41,12 @@ typedef struct {
UINT8 HookAfterStubHeaderCode[HOOKAFTER_STUB_SIZE];
} RESERVED_VECTORS_DATA;
#define CPU_TSS_DESC_SIZE \
(sizeof (IA32_TSS_DESCRIPTOR) * \
(PcdGetSize (PcdCpuStackSwitchExceptionList) + 1))
#define CPU_TSS_SIZE \
(sizeof (IA32_TASK_STATE_SEGMENT) * \
(PcdGetSize (PcdCpuStackSwitchExceptionList) + 1))
#endif

View File

@ -0,0 +1,398 @@
;------------------------------------------------------------------------------ ;
; Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
; This program and the accompanying materials
; are licensed and made available under the terms and conditions of the BSD License
; which accompanies this distribution. The full text of the license may be found at
; http://opensource.org/licenses/bsd-license.php.
;
; THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
; WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
;
; Module Name:
;
; ExceptionTssEntryAsm.Asm
;
; Abstract:
;
; IA32 CPU Exception Handler with Separate Stack
;
; Notes:
;
;------------------------------------------------------------------------------
;
; IA32 TSS Memory Layout Description
;
struc IA32_TSS
resw 1
resw 1
.ESP0: resd 1
.SS0: resw 1
resw 1
.ESP1: resd 1
.SS1: resw 1
resw 1
.ESP2: resd 1
.SS2: resw 1
resw 1
._CR3: resd 1
.EIP: resd 1
.EFLAGS: resd 1
._EAX: resd 1
._ECX: resd 1
._EDX: resd 1
._EBX: resd 1
._ESP: resd 1
._EBP: resd 1
._ESI: resd 1
._EDI: resd 1
._ES: resw 1
resw 1
._CS: resw 1
resw 1
._SS: resw 1
resw 1
._DS: resw 1
resw 1
._FS: resw 1
resw 1
._GS: resw 1
resw 1
.LDT: resw 1
resw 1
resw 1
resw 1
endstruc
;
; CommonExceptionHandler()
;
extern ASM_PFX(CommonExceptionHandler)
SECTION .data
SECTION .text
ALIGN 8
;
; Exception handler stub table
;
AsmExceptionEntryBegin:
%assign Vector 0
%rep 32
DoIret%[Vector]:
iretd
ASM_PFX(ExceptionTaskSwtichEntry%[Vector]):
db 0x6a ; push #VectorNum
db %[Vector]
mov eax, ASM_PFX(CommonTaskSwtichEntryPoint)
call eax
mov esp, eax ; Restore stack top
jmp DoIret%[Vector]
%assign Vector Vector+1
%endrep
AsmExceptionEntryEnd:
;
; Common part of exception handler
;
global ASM_PFX(CommonTaskSwtichEntryPoint)
ASM_PFX(CommonTaskSwtichEntryPoint):
;
; Stack:
; +---------------------+ <-- EBP - 8
; + TSS Base +
; +---------------------+ <-- EBP - 4
; + CPUID.EDX +
; +---------------------+ <-- EBP
; + EIP +
; +---------------------+ <-- EBP + 4
; + Vector Number +
; +---------------------+ <-- EBP + 8
; + Error Code +
; +---------------------+
;
mov ebp, esp ; Stack frame
; Use CPUID to determine if FXSAVE/FXRESTOR and DE are supported
mov eax, 1
cpuid
push edx
; Get TSS base of interrupted task through PreviousTaskLink field in
; current TSS base
sub esp, 8
sgdt [esp + 2]
mov eax, [esp + 4] ; GDT base
add esp, 8
xor ebx, ebx
str bx ; Current TR
mov ecx, [eax + ebx + 2]
shl ecx, 8
mov cl, [eax + ebx + 7]
ror ecx, 8 ; ecx = Current TSS base
push ecx ; keep it in stack for later use
movzx ebx, word [ecx] ; Previous Task Link
mov ecx, [eax + ebx + 2]
shl ecx, 8
mov cl, [eax + ebx + 7]
ror ecx, 8 ; ecx = Previous TSS base
;
; Align stack to make sure that EFI_FX_SAVE_STATE_IA32 of EFI_SYSTEM_CONTEXT_IA32
; is 16-byte aligned
;
and esp, 0xfffffff0
sub esp, 12
;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;
push dword [ecx + IA32_TSS._EAX]
push dword [ecx + IA32_TSS._ECX]
push dword [ecx + IA32_TSS._EDX]
push dword [ecx + IA32_TSS._EBX]
push dword [ecx + IA32_TSS._ESP]
push dword [ecx + IA32_TSS._EBP]
push dword [ecx + IA32_TSS._ESI]
push dword [ecx + IA32_TSS._EDI]
;; UINT32 Gs, Fs, Es, Ds, Cs, Ss;
movzx eax, word [ecx + IA32_TSS._SS]
push eax
movzx eax, word [ecx + IA32_TSS._CS]
push eax
movzx eax, word [ecx + IA32_TSS._DS]
push eax
movzx eax, word [ecx + IA32_TSS._ES]
push eax
movzx eax, word [ecx + IA32_TSS._FS]
push eax
movzx eax, word [ecx + IA32_TSS._GS]
push eax
;; UINT32 Eip;
push dword [ecx + IA32_TSS.EIP]
;; UINT32 Gdtr[2], Idtr[2];
sub esp, 8
sidt [esp]
mov eax, [esp + 2]
xchg eax, [esp]
and eax, 0xFFFF
mov [esp+4], eax
sub esp, 8
sgdt [esp]
mov eax, [esp + 2]
xchg eax, [esp]
and eax, 0xFFFF
mov [esp+4], eax
;; UINT32 Ldtr, Tr;
mov eax, ebx ; ebx still keeps selector of interrupted task
push eax
movzx eax, word [ecx + IA32_TSS.LDT]
push eax
;; UINT32 EFlags;
push dword [ecx + IA32_TSS.EFLAGS]
;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4;
mov eax, cr4
push eax ; push cr4 firstly
mov edx, [ebp - 4] ; cpuid.edx
test edx, BIT24 ; Test for FXSAVE/FXRESTOR support
jz .1
or eax, BIT9 ; Set CR4.OSFXSR
.1:
test edx, BIT2 ; Test for Debugging Extensions support
jz .2
or eax, BIT3 ; Set CR4.DE
.2:
mov cr4, eax
mov eax, cr3
push eax
mov eax, cr2
push eax
xor eax, eax
push eax
mov eax, cr0
push eax
;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
mov eax, dr7
push eax
mov eax, dr6
push eax
mov eax, dr3
push eax
mov eax, dr2
push eax
mov eax, dr1
push eax
mov eax, dr0
push eax
;; FX_SAVE_STATE_IA32 FxSaveState;
;; Clear TS bit in CR0 to avoid Device Not Available Exception (#NM)
;; when executing fxsave/fxrstor instruction
test edx, BIT24 ; Test for FXSAVE/FXRESTOR support.
; edx still contains result from CPUID above
jz .3
clts
sub esp, 512
mov edi, esp
db 0xf, 0xae, 0x7 ;fxsave [edi]
.3:
;; UINT32 ExceptionData;
push dword [ebp + 8]
;; UEFI calling convention for IA32 requires that Direction flag in EFLAGs is clear
cld
;; call into exception handler
mov esi, ecx ; Keep TSS base to avoid overwrite
mov eax, ASM_PFX(CommonExceptionHandler)
;; Prepare parameter and call
mov edx, esp
push edx ; EFI_SYSTEM_CONTEXT
push dword [ebp + 4] ; EFI_EXCEPTION_TYPE (vector number)
;
; Call External Exception Handler
;
call eax
add esp, 8 ; Restore stack before calling
mov ecx, esi ; Restore TSS base
;; UINT32 ExceptionData;
add esp, 4
;; FX_SAVE_STATE_IA32 FxSaveState;
mov edx, [ebp - 4] ; cpuid.edx
test edx, BIT24 ; Test for FXSAVE/FXRESTOR support
jz .4
mov esi, esp
db 0xf, 0xae, 0xe ; fxrstor [esi]
.4:
add esp, 512
;; UINT32 Dr0, Dr1, Dr2, Dr3, Dr6, Dr7;
;; Skip restoration of DRx registers to support debuggers
;; that set breakpoints in interrupt/exception context
add esp, 4 * 6
;; UINT32 Cr0, Cr1, Cr2, Cr3, Cr4;
pop eax
mov cr0, eax
add esp, 4 ; not for Cr1
pop eax
mov cr2, eax
pop eax
mov dword [ecx + IA32_TSS._CR3], eax
pop eax
mov cr4, eax
;; UINT32 EFlags;
pop dword [ecx + IA32_TSS.EFLAGS]
mov ebx, dword [ecx + IA32_TSS.EFLAGS]
btr ebx, 9 ; Do 'cli'
mov dword [ecx + IA32_TSS.EFLAGS], ebx
;; UINT32 Ldtr, Tr;
;; UINT32 Gdtr[2], Idtr[2];
;; Best not let anyone mess with these particular registers...
add esp, 24
;; UINT32 Eip;
pop dword [ecx + IA32_TSS.EIP]
;; UINT32 Gs, Fs, Es, Ds, Cs, Ss;
;; NOTE - modified segment registers could hang the debugger... We
;; could attempt to insulate ourselves against this possibility,
;; but that poses risks as well.
;;
pop eax
o16 mov [ecx + IA32_TSS._GS], ax
pop eax
o16 mov [ecx + IA32_TSS._FS], ax
pop eax
o16 mov [ecx + IA32_TSS._ES], ax
pop eax
o16 mov [ecx + IA32_TSS._DS], ax
pop eax
o16 mov [ecx + IA32_TSS._CS], ax
pop eax
o16 mov [ecx + IA32_TSS._SS], ax
;; UINT32 Edi, Esi, Ebp, Esp, Ebx, Edx, Ecx, Eax;
pop dword [ecx + IA32_TSS._EDI]
pop dword [ecx + IA32_TSS._ESI]
add esp, 4 ; not for ebp
add esp, 4 ; not for esp
pop dword [ecx + IA32_TSS._EBX]
pop dword [ecx + IA32_TSS._EDX]
pop dword [ecx + IA32_TSS._ECX]
pop dword [ecx + IA32_TSS._EAX]
; Set single step DB# to allow debugger to able to go back to the EIP
; where the exception is triggered.
;; Create return context for iretd in stub function
mov eax, dword [ecx + IA32_TSS._ESP] ; Get old stack pointer
mov ebx, dword [ecx + IA32_TSS.EIP]
mov [eax - 0xc], ebx ; create EIP in old stack
movzx ebx, word [ecx + IA32_TSS._CS]
mov [eax - 0x8], ebx ; create CS in old stack
mov ebx, dword [ecx + IA32_TSS.EFLAGS]
bts ebx, 8
mov [eax - 0x4], ebx ; create eflags in old stack
mov dword [ecx + IA32_TSS.EFLAGS], ebx ; update eflags in old TSS
mov eax, dword [ecx + IA32_TSS._ESP] ; Get old stack pointer
sub eax, 0xc ; minus 12 byte
mov dword [ecx + IA32_TSS._ESP], eax ; Set new stack pointer
;; Replace the EIP of interrupted task with stub function
mov eax, ASM_PFX(SingleStepStubFunction)
mov dword [ecx + IA32_TSS.EIP], eax
mov ecx, [ebp - 8] ; Get current TSS base
mov eax, dword [ecx + IA32_TSS._ESP] ; Return current stack top
mov esp, ebp
ret
global ASM_PFX(SingleStepStubFunction)
ASM_PFX(SingleStepStubFunction):
;
; we need clean TS bit in CR0 to execute
; x87 FPU/MMX/SSE/SSE2/SSE3/SSSE3/SSE4 instructions.
;
clts
iretd
global ASM_PFX(AsmGetTssTemplateMap)
ASM_PFX(AsmGetTssTemplateMap):
push ebp ; C prolog
mov ebp, esp
pushad
mov ebx, dword [ebp + 0x8]
mov dword [ebx], ASM_PFX(ExceptionTaskSwtichEntry0)
mov dword [ebx + 0x4], (AsmExceptionEntryEnd - AsmExceptionEntryBegin) / 32
mov dword [ebx + 0x8], 0
popad
pop ebp
ret

View File

@ -177,4 +177,36 @@ RegisterCpuInterruptHandler (
)
{
return EFI_UNSUPPORTED;
}
}
/**
Initializes all CPU exceptions entries with optional extra initializations.
By default, this method should include all functionalities implemented by
InitializeCpuExceptionHandlers(), plus extra initialization works, if any.
This could be done by calling InitializeCpuExceptionHandlers() directly
in this method besides the extra works.
InitData is optional and its use and content are processor arch dependent.
The typical usage of it is to convey resources which have to be reserved
elsewhere and are necessary for the extra initializations of exception.
@param[in] VectorInfo Pointer to reserved vector list.
@param[in] InitData Pointer to data optional for extra initializations
of exception.
@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
)
{
return InitializeCpuExceptionHandlers (VectorInfo);
}

View File

@ -30,6 +30,7 @@
[Sources.Ia32]
Ia32/ExceptionHandlerAsm.asm
Ia32/ExceptionHandlerAsm.nasm
Ia32/ExceptionTssEntryAsm.nasm
Ia32/ExceptionHandlerAsm.S
Ia32/ArchExceptionHandler.c
Ia32/ArchInterruptDefs.h

View File

@ -176,4 +176,36 @@ RegisterCpuInterruptHandler (
)
{
return EFI_UNSUPPORTED;
}
}
/**
Initializes all CPU exceptions entries with optional extra initializations.
By default, this method should include all functionalities implemented by
InitializeCpuExceptionHandlers(), plus extra initialization works, if any.
This could be done by calling InitializeCpuExceptionHandlers() directly
in this method besides the extra works.
InitData is optional and its use and content are processor arch dependent.
The typical usage of it is to convey resources which have to be reserved
elsewhere and are necessary for the extra initializations of exception.
@param[in] VectorInfo Pointer to reserved vector list.
@param[in] InitData Pointer to data optional for extra initializations
of exception.
@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
)
{
return InitializeCpuExceptionHandlers (VectorInfo);
}

View File

@ -30,6 +30,7 @@
[Sources.Ia32]
Ia32/ExceptionHandlerAsm.asm
Ia32/ExceptionHandlerAsm.nasm
Ia32/ExceptionTssEntryAsm.nasm
Ia32/ExceptionHandlerAsm.S
Ia32/ArchExceptionHandler.c
Ia32/ArchInterruptDefs.h

View File

@ -30,6 +30,7 @@
[Sources.Ia32]
Ia32/ExceptionHandlerAsm.asm
Ia32/ExceptionHandlerAsm.nasm
Ia32/ExceptionTssEntryAsm.nasm
Ia32/ExceptionHandlerAsm.S
Ia32/ArchExceptionHandler.c
Ia32/ArchInterruptDefs.h

View File

@ -125,4 +125,36 @@ RegisterCpuInterruptHandler (
)
{
return RegisterCpuInterruptHandlerWorker (InterruptType, InterruptHandler, &mExceptionHandlerData);
}
}
/**
Initializes all CPU exceptions entries with optional extra initializations.
By default, this method should include all functionalities implemented by
InitializeCpuExceptionHandlers(), plus extra initialization works, if any.
This could be done by calling InitializeCpuExceptionHandlers() directly
in this method besides the extra works.
InitData is optional and its use and content are processor arch dependent.
The typical usage of it is to convey resources which have to be reserved
elsewhere and are necessary for the extra initializations of exception.
@param[in] VectorInfo Pointer to reserved vector list.
@param[in] InitData Pointer to data optional for extra initializations
of exception.
@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
)
{
return InitializeCpuExceptionHandlers (VectorInfo);
}

View File

@ -112,6 +112,162 @@ ArchRestoreExceptionContext (
SystemContext.SystemContextX64->ExceptionData = ReservedVectors[ExceptionType].ExceptionData;
}
/**
Setup separate stack for given exceptions.
@param[in] StackSwitchData Pointer to data required for setuping up
stack switch.
@retval EFI_SUCCESS The exceptions have been successfully
initialized with new stack.
@retval EFI_INVALID_PARAMETER StackSwitchData contains invalid content.
**/
EFI_STATUS
ArchSetupExcpetionStack (
IN CPU_EXCEPTION_INIT_DATA *StackSwitchData
)
{
IA32_DESCRIPTOR Gdtr;
IA32_DESCRIPTOR Idtr;
IA32_IDT_GATE_DESCRIPTOR *IdtTable;
IA32_TSS_DESCRIPTOR *TssDesc;
IA32_TASK_STATE_SEGMENT *Tss;
UINTN StackTop;
UINTN Index;
UINTN Vector;
UINTN TssBase;
UINTN GdtSize;
if (StackSwitchData == NULL ||
StackSwitchData->Ia32.Revision != CPU_EXCEPTION_INIT_DATA_REV ||
StackSwitchData->X64.KnownGoodStackTop == 0 ||
StackSwitchData->X64.KnownGoodStackSize == 0 ||
StackSwitchData->X64.StackSwitchExceptions == NULL ||
StackSwitchData->X64.StackSwitchExceptionNumber == 0 ||
StackSwitchData->X64.StackSwitchExceptionNumber > CPU_EXCEPTION_NUM ||
StackSwitchData->X64.GdtTable == NULL ||
StackSwitchData->X64.IdtTable == NULL ||
StackSwitchData->X64.ExceptionTssDesc == NULL ||
StackSwitchData->X64.ExceptionTss == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// The caller is responsible for that the GDT table, no matter the existing
// one or newly allocated, has enough space to hold descriptors for exception
// task-state segments.
//
if (((UINTN)StackSwitchData->X64.GdtTable & (IA32_GDT_ALIGNMENT - 1)) != 0) {
return EFI_INVALID_PARAMETER;
}
if ((UINTN)StackSwitchData->X64.ExceptionTssDesc < (UINTN)(StackSwitchData->X64.GdtTable)) {
return EFI_INVALID_PARAMETER;
}
if (((UINTN)StackSwitchData->X64.ExceptionTssDesc + StackSwitchData->X64.ExceptionTssDescSize) >
((UINTN)(StackSwitchData->X64.GdtTable) + StackSwitchData->X64.GdtTableSize)) {
return EFI_INVALID_PARAMETER;
}
//
// One task gate descriptor and one task-state segment are needed.
//
if (StackSwitchData->X64.ExceptionTssDescSize < sizeof (IA32_TSS_DESCRIPTOR)) {
return EFI_INVALID_PARAMETER;
}
if (StackSwitchData->X64.ExceptionTssSize < sizeof (IA32_TASK_STATE_SEGMENT)) {
return EFI_INVALID_PARAMETER;
}
//
// Interrupt stack table supports only 7 vectors.
//
TssDesc = StackSwitchData->X64.ExceptionTssDesc;
Tss = StackSwitchData->X64.ExceptionTss;
if (StackSwitchData->X64.StackSwitchExceptionNumber > ARRAY_SIZE (Tss->IST)) {
return EFI_INVALID_PARAMETER;
}
//
// Initialize new GDT table and/or IDT table, if any
//
AsmReadIdtr (&Idtr);
AsmReadGdtr (&Gdtr);
GdtSize = (UINTN)TssDesc + sizeof (IA32_TSS_DESCRIPTOR) -
(UINTN)(StackSwitchData->X64.GdtTable);
if ((UINTN)StackSwitchData->X64.GdtTable != Gdtr.Base) {
CopyMem (StackSwitchData->X64.GdtTable, (VOID *)Gdtr.Base, Gdtr.Limit + 1);
Gdtr.Base = (UINTN)StackSwitchData->X64.GdtTable;
Gdtr.Limit = (UINT16)GdtSize - 1;
}
if ((UINTN)StackSwitchData->X64.IdtTable != Idtr.Base) {
Idtr.Base = (UINTN)StackSwitchData->X64.IdtTable;
}
if (StackSwitchData->X64.IdtTableSize > 0) {
Idtr.Limit = (UINT16)(StackSwitchData->X64.IdtTableSize - 1);
}
//
// Fixup current task descriptor. Task-state segment for current task will
// be filled by processor during task switching.
//
TssBase = (UINTN)Tss;
TssDesc->Bits.LimitLow = sizeof(IA32_TASK_STATE_SEGMENT) - 1;
TssDesc->Bits.BaseLow = (UINT16)TssBase;
TssDesc->Bits.BaseMidl = (UINT8)(TssBase >> 16);
TssDesc->Bits.Type = IA32_GDT_TYPE_TSS;
TssDesc->Bits.P = 1;
TssDesc->Bits.LimitHigh = 0;
TssDesc->Bits.BaseMidh = (UINT8)(TssBase >> 24);
TssDesc->Bits.BaseHigh = (UINT32)(TssBase >> 32);
//
// Fixup exception task descriptor and task-state segment
//
StackTop = StackSwitchData->X64.KnownGoodStackTop - CPU_STACK_ALIGNMENT;
StackTop = (UINTN)ALIGN_POINTER (StackTop, CPU_STACK_ALIGNMENT);
IdtTable = StackSwitchData->X64.IdtTable;
for (Index = 0; Index < StackSwitchData->X64.StackSwitchExceptionNumber; ++Index) {
//
// Fixup IST
//
Tss->IST[Index] = StackTop;
StackTop -= StackSwitchData->X64.KnownGoodStackSize;
//
// Set the IST field to enable corresponding IST
//
Vector = StackSwitchData->X64.StackSwitchExceptions[Index];
if (Vector >= CPU_EXCEPTION_NUM ||
Vector >= (Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR)) {
continue;
}
IdtTable[Vector].Bits.Reserved_0 = (UINT8)(Index + 1);
}
//
// Publish GDT
//
AsmWriteGdtr (&Gdtr);
//
// Load current task
//
AsmWriteTr ((UINT16)((UINTN)StackSwitchData->X64.ExceptionTssDesc - Gdtr.Base));
//
// Publish IDT
//
AsmWriteIdtr (&Idtr);
return EFI_SUCCESS;
}
/**
Display CPU information.

View File

@ -43,4 +43,7 @@ typedef struct {
UINT8 HookAfterStubHeaderCode[HOOKAFTER_STUB_SIZE];
} RESERVED_VECTORS_DATA;
#define CPU_TSS_DESC_SIZE sizeof (IA32_TSS_DESCRIPTOR)
#define CPU_TSS_SIZE sizeof (IA32_TASK_STATE_SEGMENT)
#endif