reactos/ntoskrnl/ke/amd64/except.c

495 lines
14 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ke/amd64/except.c
* PURPOSE: Exception Dispatching for amd64
* PROGRAMMER: Timo Kreuzer (timo.kreuzer@reactos.org)
* Alex Ionescu (alex.ionescu@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
extern KI_INTERRUPT_DISPATCH_ENTRY KiUnexpectedRange[256];
/* GLOBALS *******************************************************************/
KIDT_INIT KiInterruptInitTable[] =
{
/* Id, Dpl, IST, ServiceRoutine */
{0x00, 0x00, 0x00, KiDivideErrorFault},
{0x01, 0x00, 0x00, KiDebugTrapOrFault},
{0x02, 0x00, 0x03, KiNmiInterrupt},
{0x03, 0x03, 0x00, KiBreakpointTrap},
{0x04, 0x03, 0x00, KiOverflowTrap},
{0x05, 0x00, 0x00, KiBoundFault},
{0x06, 0x00, 0x00, KiInvalidOpcodeFault},
{0x07, 0x00, 0x00, KiNpxNotAvailableFault},
{0x08, 0x00, 0x01, KiDoubleFaultAbort},
{0x09, 0x00, 0x00, KiNpxSegmentOverrunAbort},
{0x0A, 0x00, 0x00, KiInvalidTssFault},
{0x0B, 0x00, 0x00, KiSegmentNotPresentFault},
{0x0C, 0x00, 0x00, KiStackFault},
{0x0D, 0x00, 0x00, KiGeneralProtectionFault},
{0x0E, 0x00, 0x00, KiPageFault},
{0x10, 0x00, 0x00, KiFloatingErrorFault},
{0x11, 0x00, 0x00, KiAlignmentFault},
{0x12, 0x00, 0x02, KiMcheckAbort},
{0x13, 0x00, 0x00, KiXmmException},
{0x1F, 0x00, 0x00, KiApcInterrupt},
{0x2C, 0x03, 0x00, KiRaiseAssertion},
{0x2D, 0x03, 0x00, KiDebugServiceTrap},
{0x2F, 0x00, 0x00, KiDpcInterrupt},
{0xE1, 0x00, 0x00, KiIpiInterrupt},
{0, 0, 0, 0}
};
KIDTENTRY64 KiIdt[256];
KDESCRIPTOR KiIdtDescriptor = {{0}, sizeof(KiIdt) - 1, KiIdt};
/* FUNCTIONS *****************************************************************/
VOID
INIT_FUNCTION
NTAPI
KeInitExceptions(VOID)
{
int i, j;
/* Initialize the Idt */
for (j = i = 0; i < 256; i++)
{
ULONG64 Offset;
if (KiInterruptInitTable[j].InterruptId == i)
{
Offset = (ULONG64)KiInterruptInitTable[j].ServiceRoutine;
KiIdt[i].Dpl = KiInterruptInitTable[j].Dpl;
KiIdt[i].IstIndex = KiInterruptInitTable[j].IstIndex;
j++;
}
else
{
Offset = (ULONG64)&KiUnexpectedRange[i]._Op_push;
KiIdt[i].Dpl = 0;
KiIdt[i].IstIndex = 0;
}
KiIdt[i].OffsetLow = Offset & 0xffff;
KiIdt[i].Selector = KGDT64_R0_CODE;
KiIdt[i].Type = 0x0e;
KiIdt[i].Reserved0 = 0;
KiIdt[i].Present = 1;
KiIdt[i].OffsetMiddle = (Offset >> 16) & 0xffff;
KiIdt[i].OffsetHigh = (Offset >> 32);
KiIdt[i].Reserved1 = 0;
}
KeGetPcr()->IdtBase = KiIdt;
__lidt(&KiIdtDescriptor.Limit);
}
static
VOID
KiDispatchExceptionToUser(
IN PKTRAP_FRAME TrapFrame,
IN PCONTEXT Context,
IN PEXCEPTION_RECORD ExceptionRecord)
{
EXCEPTION_RECORD LocalExceptRecord;
ULONG Size;
ULONG64 UserRsp;
PCONTEXT UserContext;
PEXCEPTION_RECORD UserExceptionRecord;
/* Make sure we have a valid SS */
if (TrapFrame->SegSs != (KGDT64_R3_DATA | RPL_MASK))
{
/* Raise an access violation instead */
LocalExceptRecord.ExceptionCode = STATUS_ACCESS_VIOLATION;
LocalExceptRecord.ExceptionFlags = 0;
LocalExceptRecord.NumberParameters = 0;
ExceptionRecord = &LocalExceptRecord;
}
/* Calculate the size of the exception record */
Size = FIELD_OFFSET(EXCEPTION_RECORD, ExceptionInformation) +
ExceptionRecord->NumberParameters * sizeof(ULONG64);
/* Get new stack pointer and align it to 16 bytes */
UserRsp = (Context->Rsp - Size - sizeof(CONTEXT)) & ~15;
/* Get pointers to the usermode context and exception record */
UserContext = (PVOID)UserRsp;
UserExceptionRecord = (PVOID)(UserRsp + sizeof(CONTEXT));
/* Set up the user-stack */
_SEH2_TRY
{
/* Probe stack and copy Context */
ProbeForWrite(UserContext, sizeof(CONTEXT), sizeof(ULONG64));
*UserContext = *Context;
/* Probe stack and copy exception record */
ProbeForWrite(UserExceptionRecord, Size, sizeof(ULONG64));
*UserExceptionRecord = *ExceptionRecord;
}
_SEH2_EXCEPT((LocalExceptRecord = *_SEH2_GetExceptionInformation()->ExceptionRecord),
EXCEPTION_EXECUTE_HANDLER)
{
// FIXME: handle stack overflow
/* Nothing we can do here */
_SEH2_YIELD(return);
}
_SEH2_END;
/* Now set the two params for the user-mode dispatcher */
TrapFrame->Rcx = (ULONG64)UserContext;
TrapFrame->Rdx = (ULONG64)UserExceptionRecord;
/* Set new Stack Pointer */
TrapFrame->Rsp = UserRsp;
/* Force correct segments */
TrapFrame->SegCs = KGDT64_R3_CODE | RPL_MASK;
TrapFrame->SegDs = KGDT64_R3_DATA | RPL_MASK;
TrapFrame->SegEs = KGDT64_R3_DATA | RPL_MASK;
TrapFrame->SegFs = KGDT64_R3_CMTEB | RPL_MASK;
TrapFrame->SegGs = KGDT64_R3_DATA | RPL_MASK;
TrapFrame->SegSs = KGDT64_R3_DATA | RPL_MASK;
/* Set RIP to the User-mode Dispatcher */
TrapFrame->Rip = (ULONG64)KeUserExceptionDispatcher;
/* Exit to usermode */
KiServiceExit2(TrapFrame);
}
static
VOID
KiPageInDirectory(PVOID ImageBase, USHORT Directory)
{
volatile CHAR *Pointer;
ULONG Size;
/* Get a pointer to the debug directory */
Pointer = RtlImageDirectoryEntryToData(ImageBase, 1, Directory, &Size);
if (!Pointer) return;
/* Loop all pages */
while ((LONG)Size > 0)
{
/* Touch it, to page it in */
(void)*Pointer;
Pointer += PAGE_SIZE;
Size -= PAGE_SIZE;
}
}
VOID
KiPrepareUserDebugData(void)
{
PLDR_DATA_TABLE_ENTRY LdrEntry;
PPEB_LDR_DATA PebLdr;
PLIST_ENTRY ListEntry;
PTEB Teb;
/* Get the Teb for this process */
Teb = KeGetCurrentThread()->Teb;
if (!Teb) return;
_SEH2_TRY
{
/* Get a pointer to the loader data */
PebLdr = Teb->ProcessEnvironmentBlock->Ldr;
if (!PebLdr) _SEH2_YIELD(return);
/* Now loop all entries in the module list */
for (ListEntry = PebLdr->InLoadOrderModuleList.Flink;
ListEntry != &PebLdr->InLoadOrderModuleList;
ListEntry = ListEntry->Flink)
{
/* Get the loader entry */
LdrEntry = CONTAINING_RECORD(ListEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
KiPageInDirectory((PVOID)LdrEntry->DllBase,
IMAGE_DIRECTORY_ENTRY_DEBUG);
KiPageInDirectory((PVOID)LdrEntry->DllBase,
IMAGE_DIRECTORY_ENTRY_EXCEPTION);
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
}
_SEH2_END;
}
VOID
NTAPI
KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord,
IN PKEXCEPTION_FRAME ExceptionFrame,
IN PKTRAP_FRAME TrapFrame,
IN KPROCESSOR_MODE PreviousMode,
IN BOOLEAN FirstChance)
{
CONTEXT Context;
/* Increase number of Exception Dispatches */
KeGetCurrentPrcb()->KeExceptionDispatchCount++;
/* Set the context flags */
Context.ContextFlags = CONTEXT_ALL;
/* Get a Context */
KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
/* Look at our exception code */
switch (ExceptionRecord->ExceptionCode)
{
/* Breakpoint */
case STATUS_BREAKPOINT:
/* Decrement RIP by one */
Context.Rip--;
break;
/* Internal exception */
case KI_EXCEPTION_ACCESS_VIOLATION:
/* Set correct code */
ExceptionRecord->ExceptionCode = STATUS_ACCESS_VIOLATION;
if (PreviousMode == UserMode)
{
/* FIXME: Handle no execute */
}
break;
}
/* Handle kernel-mode first, it's simpler */
if (PreviousMode == KernelMode)
{
/* Check if this is a first-chance exception */
if (FirstChance)
{
/* Break into the debugger for the first time */
if (KiDebugRoutine(TrapFrame,
ExceptionFrame,
ExceptionRecord,
&Context,
PreviousMode,
FALSE))
{
/* Exception was handled */
goto Handled;
}
/* If the Debugger couldn't handle it, dispatch the exception */
if (RtlDispatchException(ExceptionRecord, &Context)) goto Handled;
}
/* This is a second-chance exception, only for the debugger */
if (KiDebugRoutine(TrapFrame,
ExceptionFrame,
ExceptionRecord,
&Context,
PreviousMode,
TRUE))
{
/* Exception was handled */
goto Handled;
}
/* Third strike; you're out */
KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,
ExceptionRecord->ExceptionCode,
(ULONG_PTR)ExceptionRecord->ExceptionAddress,
(ULONG_PTR)TrapFrame,
0);
}
else
{
/* User mode exception, was it first-chance? */
if (FirstChance)
{
/*
* Break into the kernel debugger unless a user mode debugger
* is present or user mode exceptions are ignored, except if this
* is a debug service which we must always pass to KD
*/
if ((!(PsGetCurrentProcess()->DebugPort) &&
!(KdIgnoreUmExceptions)) ||
(KdIsThisAKdTrap(ExceptionRecord, &Context, PreviousMode)))
{
/* Make sure the debugger can access debug directories */
KiPrepareUserDebugData();
/* Call the kernel debugger */
if (KiDebugRoutine(TrapFrame,
ExceptionFrame,
ExceptionRecord,
&Context,
PreviousMode,
FALSE))
{
/* Exception was handled */
goto Handled;
}
}
/* Forward exception to user mode debugger */
if (DbgkForwardException(ExceptionRecord, TRUE, FALSE)) return;
//KiDispatchExceptionToUser()
__debugbreak();
}
/* Try second chance */
if (DbgkForwardException(ExceptionRecord, TRUE, TRUE))
{
/* Handled, get out */
return;
}
else if (DbgkForwardException(ExceptionRecord, FALSE, TRUE))
{
/* Handled, get out */
return;
}
/* 3rd strike, kill the process */
DPRINT1("Kill %.16s, ExceptionCode: %lx, ExceptionAddress: %lx, BaseAddress: %lx\n",
PsGetCurrentProcess()->ImageFileName,
ExceptionRecord->ExceptionCode,
ExceptionRecord->ExceptionAddress,
PsGetCurrentProcess()->SectionBaseAddress);
ZwTerminateProcess(NtCurrentProcess(), ExceptionRecord->ExceptionCode);
KeBugCheckEx(KMODE_EXCEPTION_NOT_HANDLED,
ExceptionRecord->ExceptionCode,
(ULONG_PTR)ExceptionRecord->ExceptionAddress,
(ULONG_PTR)TrapFrame,
0);
}
Handled:
/* Convert the context back into Trap/Exception Frames */
KeContextToTrapFrame(&Context,
ExceptionFrame,
TrapFrame,
Context.ContextFlags,
PreviousMode);
return;
}
NTSTATUS
NTAPI
KeRaiseUserException(IN NTSTATUS ExceptionCode)
{
UNIMPLEMENTED;
return STATUS_UNSUCCESSFUL;
}
VOID
DECLSPEC_NORETURN
KiSystemFatalException(IN ULONG ExceptionCode,
IN PKTRAP_FRAME TrapFrame)
{
/* Bugcheck the system */
KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP,
ExceptionCode,
0,
0,
0,
TrapFrame);
}
NTSTATUS
NTAPI
KiNpxNotAvailableFaultHandler(
IN PKTRAP_FRAME TrapFrame)
{
UNIMPLEMENTED;
KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame);
return -1;
}
NTSTATUS
NTAPI
KiGeneralProtectionFaultHandler(
IN PKTRAP_FRAME TrapFrame)
{
PUCHAR Instructions;
/* Check for user-mode GPF */
if (TrapFrame->SegCs & 3)
{
UNIMPLEMENTED;
ASSERT(FALSE);
}
/* Check for lazy segment load */
if (TrapFrame->SegDs != (KGDT64_R3_DATA | RPL_MASK))
{
/* Fix it */
TrapFrame->SegDs = (KGDT64_R3_DATA | RPL_MASK);
return STATUS_SUCCESS;
}
else if (TrapFrame->SegEs != (KGDT64_R3_DATA | RPL_MASK))
{
/* Fix it */
TrapFrame->SegEs = (KGDT64_R3_DATA | RPL_MASK);
return STATUS_SUCCESS;
}
/* Check for nested exception */
if ((TrapFrame->Rip >= (ULONG64)KiGeneralProtectionFaultHandler) &&
(TrapFrame->Rip < (ULONG64)KiGeneralProtectionFaultHandler))
{
/* Not implemented */
UNIMPLEMENTED;
ASSERT(FALSE);
}
/* Get Instruction Pointer */
Instructions = (PUCHAR)TrapFrame->Rip;
/* Check for IRET */
if (Instructions[0] == 0x48 && Instructions[1] == 0xCF)
{
/* Not implemented */
UNIMPLEMENTED;
ASSERT(FALSE);
}
/* Check for RDMSR/WRMSR */
if ((Instructions[0] == 0xF) && // 2-byte opcode
((Instructions[1] == 0x30) || // RDMSR
(Instructions[1] == 0x32))) // WRMSR
{
/* Unknown CPU MSR, so raise an access violation */
return STATUS_ACCESS_VIOLATION;
}
ASSERT(FALSE);
return STATUS_UNSUCCESSFUL;
}
NTSTATUS
NTAPI
KiXmmExceptionHandler(
IN PKTRAP_FRAME TrapFrame)
{
UNIMPLEMENTED;
KeBugCheckWithTf(TRAP_CAUSE_UNKNOWN, 13, 0, 0, 1, TrapFrame);
return -1;
}