/** @file x64 CPU Exception Handler. Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.
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.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); } }