blob: cd7dccd48141025da8a1644fb0a6165b1e982f79 [file] [log] [blame]
/** @file
x64 CPU Exception Handler.
Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "CpuExceptionCommon.h"
/**
Return address map of exception handler template so that C code can generate
exception tables.
@param IdtEntry Pointer to IDT entry to be updated.
@param InterruptHandler IDT handler value.
**/
VOID
ArchUpdateIdtEntry (
OUT IA32_IDT_GATE_DESCRIPTOR *IdtEntry,
IN UINTN InterruptHandler
)
{
IdtEntry->Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler;
IdtEntry->Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16);
IdtEntry->Bits.OffsetUpper = (UINT32)((UINTN)InterruptHandler >> 32);
IdtEntry->Bits.GateType = IA32_IDT_GATE_TYPE_INTERRUPT_32;
}
/**
Read IDT handler value from IDT entry.
@param IdtEntry Pointer to IDT entry to be read.
**/
UINTN
ArchGetIdtHandler (
IN IA32_IDT_GATE_DESCRIPTOR *IdtEntry
)
{
return IdtEntry->Bits.OffsetLow + (((UINTN)IdtEntry->Bits.OffsetHigh) << 16) +
(((UINTN)IdtEntry->Bits.OffsetUpper) << 32);
}
/**
Save CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case.
@param[in] ExceptionType Exception type.
@param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT.
@param[in] ExceptionHandlerData Pointer to exception handler data.
**/
VOID
ArchSaveExceptionContext (
IN UINTN ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext,
IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
)
{
IA32_EFLAGS32 Eflags;
RESERVED_VECTORS_DATA *ReservedVectors;
ReservedVectors = ExceptionHandlerData->ReservedVectors;
//
// Save Exception context in global variable in first entry of the exception handler.
// So when original exception handler returns to the new exception handler (second entry),
// the Eflags/Cs/Eip/ExceptionData can be used.
//
ReservedVectors[ExceptionType].OldSs = SystemContext.SystemContextX64->Ss;
ReservedVectors[ExceptionType].OldSp = SystemContext.SystemContextX64->Rsp;
ReservedVectors[ExceptionType].OldFlags = SystemContext.SystemContextX64->Rflags;
ReservedVectors[ExceptionType].OldCs = SystemContext.SystemContextX64->Cs;
ReservedVectors[ExceptionType].OldIp = SystemContext.SystemContextX64->Rip;
ReservedVectors[ExceptionType].ExceptionData = SystemContext.SystemContextX64->ExceptionData;
//
// Clear IF flag to avoid old IDT handler enable interrupt by IRET
//
Eflags.UintN = SystemContext.SystemContextX64->Rflags;
Eflags.Bits.IF = 0;
SystemContext.SystemContextX64->Rflags = Eflags.UintN;
//
// Modify the EIP in stack, then old IDT handler will return to HookAfterStubBegin.
//
SystemContext.SystemContextX64->Rip = (UINTN)ReservedVectors[ExceptionType].HookAfterStubHeaderCode;
}
/**
Restore CPU exception context when handling EFI_VECTOR_HANDOFF_HOOK_AFTER case.
@param[in] ExceptionType Exception type.
@param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT.
@param[in] ExceptionHandlerData Pointer to exception handler data.
**/
VOID
ArchRestoreExceptionContext (
IN UINTN ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext,
IN EXCEPTION_HANDLER_DATA *ExceptionHandlerData
)
{
RESERVED_VECTORS_DATA *ReservedVectors;
ReservedVectors = ExceptionHandlerData->ReservedVectors;
SystemContext.SystemContextX64->Ss = ReservedVectors[ExceptionType].OldSs;
SystemContext.SystemContextX64->Rsp = ReservedVectors[ExceptionType].OldSp;
SystemContext.SystemContextX64->Rflags = ReservedVectors[ExceptionType].OldFlags;
SystemContext.SystemContextX64->Cs = ReservedVectors[ExceptionType].OldCs;
SystemContext.SystemContextX64->Rip = ReservedVectors[ExceptionType].OldIp;
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
ArchSetupExceptionStack (
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->Uint128.Uint64 = 0;
TssDesc->Uint128.Uint64_1 = 0;
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
//
ZeroMem (Tss, sizeof (*Tss));
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.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
EFIAPI
DumpCpuContext (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
InternalPrintMessage (
"!!!! X64 Exception Type - %02x(%a) CPU Apic ID - %08x !!!!\n",
ExceptionType,
GetExceptionNameStr (ExceptionType),
GetApicId ()
);
if ((mErrorCodeFlag & (1 << ExceptionType)) != 0) {
InternalPrintMessage (
"ExceptionData - %016lx",
SystemContext.SystemContextX64->ExceptionData
);
if (ExceptionType == EXCEPT_IA32_PAGE_FAULT) {
InternalPrintMessage (
" I:%x R:%x U:%x W:%x P:%x PK:%x SS:%x SGX:%x",
(SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0,
(SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_RSVD) != 0,
(SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_US) != 0,
(SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_WR) != 0,
(SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_P) != 0,
(SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_PK) != 0,
(SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_SS) != 0,
(SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_SGX) != 0
);
}
InternalPrintMessage ("\n");
}
InternalPrintMessage (
"RIP - %016lx, CS - %016lx, RFLAGS - %016lx\n",
SystemContext.SystemContextX64->Rip,
SystemContext.SystemContextX64->Cs,
SystemContext.SystemContextX64->Rflags
);
InternalPrintMessage (
"RAX - %016lx, RCX - %016lx, RDX - %016lx\n",
SystemContext.SystemContextX64->Rax,
SystemContext.SystemContextX64->Rcx,
SystemContext.SystemContextX64->Rdx
);
InternalPrintMessage (
"RBX - %016lx, RSP - %016lx, RBP - %016lx\n",
SystemContext.SystemContextX64->Rbx,
SystemContext.SystemContextX64->Rsp,
SystemContext.SystemContextX64->Rbp
);
InternalPrintMessage (
"RSI - %016lx, RDI - %016lx\n",
SystemContext.SystemContextX64->Rsi,
SystemContext.SystemContextX64->Rdi
);
InternalPrintMessage (
"R8 - %016lx, R9 - %016lx, R10 - %016lx\n",
SystemContext.SystemContextX64->R8,
SystemContext.SystemContextX64->R9,
SystemContext.SystemContextX64->R10
);
InternalPrintMessage (
"R11 - %016lx, R12 - %016lx, R13 - %016lx\n",
SystemContext.SystemContextX64->R11,
SystemContext.SystemContextX64->R12,
SystemContext.SystemContextX64->R13
);
InternalPrintMessage (
"R14 - %016lx, R15 - %016lx\n",
SystemContext.SystemContextX64->R14,
SystemContext.SystemContextX64->R15
);
InternalPrintMessage (
"DS - %016lx, ES - %016lx, FS - %016lx\n",
SystemContext.SystemContextX64->Ds,
SystemContext.SystemContextX64->Es,
SystemContext.SystemContextX64->Fs
);
InternalPrintMessage (
"GS - %016lx, SS - %016lx\n",
SystemContext.SystemContextX64->Gs,
SystemContext.SystemContextX64->Ss
);
InternalPrintMessage (
"CR0 - %016lx, CR2 - %016lx, CR3 - %016lx\n",
SystemContext.SystemContextX64->Cr0,
SystemContext.SystemContextX64->Cr2,
SystemContext.SystemContextX64->Cr3
);
InternalPrintMessage (
"CR4 - %016lx, CR8 - %016lx\n",
SystemContext.SystemContextX64->Cr4,
SystemContext.SystemContextX64->Cr8
);
InternalPrintMessage (
"DR0 - %016lx, DR1 - %016lx, DR2 - %016lx\n",
SystemContext.SystemContextX64->Dr0,
SystemContext.SystemContextX64->Dr1,
SystemContext.SystemContextX64->Dr2
);
InternalPrintMessage (
"DR3 - %016lx, DR6 - %016lx, DR7 - %016lx\n",
SystemContext.SystemContextX64->Dr3,
SystemContext.SystemContextX64->Dr6,
SystemContext.SystemContextX64->Dr7
);
InternalPrintMessage (
"GDTR - %016lx %016lx, LDTR - %016lx\n",
SystemContext.SystemContextX64->Gdtr[0],
SystemContext.SystemContextX64->Gdtr[1],
SystemContext.SystemContextX64->Ldtr
);
InternalPrintMessage (
"IDTR - %016lx %016lx, TR - %016lx\n",
SystemContext.SystemContextX64->Idtr[0],
SystemContext.SystemContextX64->Idtr[1],
SystemContext.SystemContextX64->Tr
);
InternalPrintMessage (
"FXSAVE_STATE - %016lx\n",
&SystemContext.SystemContextX64->FxSaveState
);
}
/**
Display CPU information.
@param ExceptionType Exception type.
@param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
**/
VOID
DumpImageAndCpuContent (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
DumpCpuContext (ExceptionType, SystemContext);
//
// Dump module image base and module entry point by RIP
//
if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
((SystemContext.SystemContextX64->ExceptionData & IA32_PF_EC_ID) != 0))
{
//
// The RIP in SystemContext could not be used
// if it is page fault with I/D set.
//
DumpModuleImageInfo ((*(UINTN *)(UINTN)SystemContext.SystemContextX64->Rsp));
} else {
DumpModuleImageInfo (SystemContext.SystemContextX64->Rip);
}
}