CloverBootloader/UefiCpuPkg/Library/CpuExceptionHandlerLib/Ia32/ArchExceptionHandler.c

428 lines
14 KiB
C

/** @file
IA32 CPU Exception Handler functons.
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 (
IN IA32_IDT_GATE_DESCRIPTOR *IdtEntry,
IN UINTN InterruptHandler
)
{
IdtEntry->Bits.OffsetLow = (UINT16)(UINTN)InterruptHandler;
IdtEntry->Bits.OffsetHigh = (UINT16)((UINTN)InterruptHandler >> 16);
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 (UINTN)IdtEntry->Bits.OffsetLow + (((UINTN)IdtEntry->Bits.OffsetHigh) << 16);
}
/**
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].OldFlags = SystemContext.SystemContextIa32->Eflags;
ReservedVectors[ExceptionType].OldCs = SystemContext.SystemContextIa32->Cs;
ReservedVectors[ExceptionType].OldIp = SystemContext.SystemContextIa32->Eip;
ReservedVectors[ExceptionType].ExceptionData = SystemContext.SystemContextIa32->ExceptionData;
//
// Clear IF flag to avoid old IDT handler enable interrupt by IRET
//
Eflags.UintN = SystemContext.SystemContextIa32->Eflags;
Eflags.Bits.IF = 0;
SystemContext.SystemContextIa32->Eflags = Eflags.UintN;
//
// Modify the EIP in stack, then old IDT handler will return to HookAfterStubBegin.
//
SystemContext.SystemContextIa32->Eip = (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.SystemContextIa32->Eflags = ReservedVectors[ExceptionType].OldFlags;
SystemContext.SystemContextIa32->Cs = ReservedVectors[ExceptionType].OldCs;
SystemContext.SystemContextIa32->Eip = ReservedVectors[ExceptionType].OldIp;
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
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;
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->Uint64 = 0;
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->Uint64 = 0;
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;
}
ZeroMem (Tss, sizeof (*Tss));
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.
@param[in] ExceptionType Exception type.
@param[in] SystemContext Processor context to be display.
**/
VOID
EFIAPI
DumpCpuContext (
IN EFI_EXCEPTION_TYPE ExceptionType,
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
InternalPrintMessage (
"!!!! IA32 Exception Type - %02x(%a) CPU Apic ID - %08x !!!!\n",
ExceptionType,
GetExceptionNameStr (ExceptionType),
GetApicId ()
);
if ((mErrorCodeFlag & (1 << ExceptionType)) != 0) {
InternalPrintMessage (
"ExceptionData - %08x",
SystemContext.SystemContextIa32->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.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0,
(SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_RSVD) != 0,
(SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_US) != 0,
(SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_WR) != 0,
(SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_P) != 0,
(SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_PK) != 0,
(SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_SS) != 0,
(SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_SGX) != 0
);
}
InternalPrintMessage ("\n");
}
InternalPrintMessage (
"EIP - %08x, CS - %08x, EFLAGS - %08x\n",
SystemContext.SystemContextIa32->Eip,
SystemContext.SystemContextIa32->Cs,
SystemContext.SystemContextIa32->Eflags
);
InternalPrintMessage (
"EAX - %08x, ECX - %08x, EDX - %08x, EBX - %08x\n",
SystemContext.SystemContextIa32->Eax,
SystemContext.SystemContextIa32->Ecx,
SystemContext.SystemContextIa32->Edx,
SystemContext.SystemContextIa32->Ebx
);
InternalPrintMessage (
"ESP - %08x, EBP - %08x, ESI - %08x, EDI - %08x\n",
SystemContext.SystemContextIa32->Esp,
SystemContext.SystemContextIa32->Ebp,
SystemContext.SystemContextIa32->Esi,
SystemContext.SystemContextIa32->Edi
);
InternalPrintMessage (
"DS - %08x, ES - %08x, FS - %08x, GS - %08x, SS - %08x\n",
SystemContext.SystemContextIa32->Ds,
SystemContext.SystemContextIa32->Es,
SystemContext.SystemContextIa32->Fs,
SystemContext.SystemContextIa32->Gs,
SystemContext.SystemContextIa32->Ss
);
InternalPrintMessage (
"CR0 - %08x, CR2 - %08x, CR3 - %08x, CR4 - %08x\n",
SystemContext.SystemContextIa32->Cr0,
SystemContext.SystemContextIa32->Cr2,
SystemContext.SystemContextIa32->Cr3,
SystemContext.SystemContextIa32->Cr4
);
InternalPrintMessage (
"DR0 - %08x, DR1 - %08x, DR2 - %08x, DR3 - %08x\n",
SystemContext.SystemContextIa32->Dr0,
SystemContext.SystemContextIa32->Dr1,
SystemContext.SystemContextIa32->Dr2,
SystemContext.SystemContextIa32->Dr3
);
InternalPrintMessage (
"DR6 - %08x, DR7 - %08x\n",
SystemContext.SystemContextIa32->Dr6,
SystemContext.SystemContextIa32->Dr7
);
InternalPrintMessage (
"GDTR - %08x %08x, IDTR - %08x %08x\n",
SystemContext.SystemContextIa32->Gdtr[0],
SystemContext.SystemContextIa32->Gdtr[1],
SystemContext.SystemContextIa32->Idtr[0],
SystemContext.SystemContextIa32->Idtr[1]
);
InternalPrintMessage (
"LDTR - %08x, TR - %08x\n",
SystemContext.SystemContextIa32->Ldtr,
SystemContext.SystemContextIa32->Tr
);
InternalPrintMessage (
"FXSAVE_STATE - %08x\n",
&SystemContext.SystemContextIa32->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 EIP
//
if ((ExceptionType == EXCEPT_IA32_PAGE_FAULT) &&
((SystemContext.SystemContextIa32->ExceptionData & IA32_PF_EC_ID) != 0)) {
//
// The EIP in SystemContext could not be used
// if it is page fault with I/D set.
//
DumpModuleImageInfo ((*(UINTN *)(UINTN)SystemContext.SystemContextIa32->Esp));
} else {
DumpModuleImageInfo (SystemContext.SystemContextIa32->Eip);
}
}