| /** @file | |
| IA32 CPU Exception Handler functons. | |
| Copyright (c) 2012 - 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. | |
| **/ | |
| #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 | |
| // | |
| 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 the stub code | |
| // | |
| 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 | |
| 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. | |
| @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 S:%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_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); | |
| } | |
| } |