diff --git a/reactos/ntoskrnl/include/internal/i386/asmmacro.S b/reactos/ntoskrnl/include/internal/i386/asmmacro.S index fff9d4bd122..360ba24b3f6 100644 --- a/reactos/ntoskrnl/include/internal/i386/asmmacro.S +++ b/reactos/ntoskrnl/include/internal/i386/asmmacro.S @@ -220,6 +220,28 @@ _KiUnexpectedInterrupt&Number: .endr .endm +// +// @name GENERATE_TRAP_HANDLER +// +// This macro creates a kernel trap handler. +// +// @param None. +// +// @remark None. +// +.macro GENERATE_TRAP_HANDLER Name, ErrorCode +.func Name +_&Name: + .if \ErrorCode + push 0 + .endif + pushad + sub esp, KTRAP_FRAME_LENGTH - KTRAP_FRAME_PREVIOUS_MODE + mov ecx, esp + call @&Name&Handler@4 +.endfunc +.endm + // // @name GENERATE_HAL_INT_HANDLER // diff --git a/reactos/ntoskrnl/include/internal/i386/intrin_i.h b/reactos/ntoskrnl/include/internal/i386/intrin_i.h index 3047816c7f0..4c038f956c9 100644 --- a/reactos/ntoskrnl/include/internal/i386/intrin_i.h +++ b/reactos/ntoskrnl/include/internal/i386/intrin_i.h @@ -52,6 +52,7 @@ Ke386GetTr(VOID) #define _Ke386SetSeg(N,X) __asm__ __volatile__("movl %0,%%" #N : :"r" (X)); #define Ke386FnInit() __asm__("fninit\n\t"); +#define Ke386ClearDirectionFlag() __asm__ __volatile__ ("cld") // @@ -64,6 +65,9 @@ Ke386GetTr(VOID) // #define Ke386GetSs() _Ke386GetSeg(ss) #define Ke386GetFs() _Ke386GetSeg(fs) +#define Ke386GetDs() _Ke386GetSeg(ds) +#define Ke386GetEs() _Ke386GetSeg(es) +#define Ke386GetGs() _Ke386GetSeg(gs) #define Ke386SetFs(X) _Ke386SetSeg(fs, X) #define Ke386SetDs(X) _Ke386SetSeg(ds, X) #define Ke386SetEs(X) _Ke386SetSeg(es, X) diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index b8649c3dd26..659b05938dd 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -72,6 +72,20 @@ typedef struct _KNMI_HANDLER_CALLBACK PVOID Handle; } KNMI_HANDLER_CALLBACK, *PKNMI_HANDLER_CALLBACK; +typedef union _KTRAP_STATE_BITS +{ + struct + { + UCHAR SystemCall:1; + UCHAR PreviousMode:1; + UCHAR Segments:1; + UCHAR Volatiles:1; + UCHAR Full:1; + UCHAR Reserved:3; + }; + UCHAR Bits; +} KTRAP_STATE_BITS, *PKTRAP_STATE_BITS; + typedef PCHAR (NTAPI *PKE_BUGCHECK_UNICODE_TO_ANSI)( IN PUNICODE_STRING Unicode, @@ -225,6 +239,12 @@ extern ULONG KiDPCTimeout; #endif +#define KTS_SYSCALL_BIT (((KTRAP_STATE_BITS) { { .SystemCall = TRUE } }).Bits) +#define KTS_PM_BIT (((KTRAP_STATE_BITS) { { .PreviousMode = TRUE } }).Bits) +#define KTS_SEG_BIT (((KTRAP_STATE_BITS) { { .Segments = TRUE } }).Bits) +#define KTS_VOL_BIT (((KTRAP_STATE_BITS) { { .Volatiles = TRUE } }).Bits) +#define KTS_FULL_BIT (((KTRAP_STATE_BITS) { { .Full = TRUE } }).Bits) + /* INTERNAL KERNEL FUNCTIONS ************************************************/ VOID @@ -863,6 +883,18 @@ KeBugCheckWithTf( PKTRAP_FRAME Tf ); +VOID +NTAPI +KiDispatchExceptionFromTrapFrame( + IN NTSTATUS Code, + IN ULONG_PTR Address, + IN ULONG ParameterCount, + IN ULONG_PTR Parameter1, + IN ULONG_PTR Parameter2, + IN ULONG_PTR Parameter3, + IN PKTRAP_FRAME TrapFrame +); + BOOLEAN NTAPI KiHandleNmi(VOID); diff --git a/reactos/ntoskrnl/include/internal/trap_x.h b/reactos/ntoskrnl/include/internal/trap_x.h new file mode 100644 index 00000000000..928eae335b5 --- /dev/null +++ b/reactos/ntoskrnl/include/internal/trap_x.h @@ -0,0 +1,243 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: BSD - See COPYING.ARM in the top level directory + * FILE: ntoskrnl/include/trap_x.h + * PURPOSE: Internal Inlined Functions for the Trap Handling Code + * PROGRAMMERS: ReactOS Portable Systems Group + */ + +// +// Debug Macros +// +#if YDEBUG +VOID +NTAPI +KiDumpTrapFrame(IN PKTRAP_FRAME TrapFrame) +{ + /* Dump the whole thing */ + DPRINT1("DbgEbp: %x\n", TrapFrame->DbgEbp); + DPRINT1("DbgEip: %x\n", TrapFrame->DbgEip); + DPRINT1("DbgArgMark: %x\n", TrapFrame->DbgArgMark); + DPRINT1("DbgArgPointer: %x\n", TrapFrame->DbgArgPointer); + DPRINT1("TempSegCs: %x\n", TrapFrame->TempSegCs); + DPRINT1("TempEsp: %x\n", TrapFrame->TempEsp); + DPRINT1("Dr0: %x\n", TrapFrame->Dr0); + DPRINT1("Dr1: %x\n", TrapFrame->Dr1); + DPRINT1("Dr2: %x\n", TrapFrame->Dr2); + DPRINT1("Dr3: %x\n", TrapFrame->Dr3); + DPRINT1("Dr6: %x\n", TrapFrame->Dr6); + DPRINT1("Dr7: %x\n", TrapFrame->Dr7); + DPRINT1("SegGs: %x\n", TrapFrame->SegGs); + DPRINT1("SegEs: %x\n", TrapFrame->SegEs); + DPRINT1("SegDs: %x\n", TrapFrame->SegDs); + DPRINT1("Edx: %x\n", TrapFrame->Edx); + DPRINT1("Ecx: %x\n", TrapFrame->Ecx); + DPRINT1("Eax: %x\n", TrapFrame->Eax); + DPRINT1("PreviousPreviousMode: %x\n", TrapFrame->PreviousPreviousMode); + DPRINT1("ExceptionList: %x\n", TrapFrame->ExceptionList); + DPRINT1("SegFs: %x\n", TrapFrame->SegFs); + DPRINT1("Edi: %x\n", TrapFrame->Edi); + DPRINT1("Esi: %x\n", TrapFrame->Esi); + DPRINT1("Ebx: %x\n", TrapFrame->Ebx); + DPRINT1("Ebp: %x\n", TrapFrame->Ebp); + DPRINT1("ErrCode: %x\n", TrapFrame->ErrCode); + DPRINT1("Eip: %x\n", TrapFrame->Eip); + DPRINT1("SegCs: %x\n", TrapFrame->SegCs); + DPRINT1("EFlags: %x\n", TrapFrame->EFlags); + DPRINT1("HardwareEsp: %x\n", TrapFrame->HardwareEsp); + DPRINT1("HardwareSegSs: %x\n", TrapFrame->HardwareSegSs); + DPRINT1("V86Es: %x\n", TrapFrame->V86Es); + DPRINT1("V86Ds: %x\n", TrapFrame->V86Ds); + DPRINT1("V86Fs: %x\n", TrapFrame->V86Fs); + DPRINT1("V86Gs: %x\n", TrapFrame->V86Gs); +} + +FORCEINLINE +VOID +KiFillTrapFrameDebug(IN PKTRAP_FRAME TrapFrame) +{ + /* Set the debug information */ + TrapFrame->DbgArgPointer = TrapFrame->Edx; + TrapFrame->DbgArgMark = 0xBADB0D00; + TrapFrame->DbgEip = TrapFrame->Eip; + TrapFrame->DbgEbp = TrapFrame->Ebp; +} + +FORCEINLINE +VOID +KiExitTrapDebugChecks(IN PKTRAP_FRAME TrapFrame, + IN KTRAP_STATE_BITS StateBits) +{ + /* Make sure interrupts are disabled */ + if (__readeflags() & EFLAGS_INTERRUPT_MASK) + { + DPRINT1("Exiting with interrupts enabled: %lx\n", __readeflags()); + while (TRUE); + } + + /* Make sure this is a real trap frame */ + if (TrapFrame->DbgArgMark != 0xBADB0D00) + { + DPRINT1("Exiting with an invalid trap frame? (No MAGIC in trap frame)\n"); + KiDumpTrapFrame(TrapFrame); + while (TRUE); + } + + /* Make sure we're not in user-mode or something */ + if (Ke386GetFs() != KGDT_R0_PCR) + { + DPRINT1("Exiting with an invalid FS: %lx\n", Ke386GetFs()); + while (TRUE); + } + + /* Make sure we have a valid SEH chain */ + if (KeGetPcr()->Tib.ExceptionList == 0) + { + DPRINT1("Exiting with NULL exception chain: %p\n", KeGetPcr()->Tib.ExceptionList); + while (TRUE); + } + + /* Make sure we're restoring a valid SEH chain */ + if (TrapFrame->ExceptionList == 0) + { + DPRINT1("Entered a trap with a NULL exception chain: %p\n", TrapFrame->ExceptionList); + while (TRUE); + } + + /* If we're ignoring previous mode, make sure caller doesn't actually want it */ + if (!(StateBits.PreviousMode) && (TrapFrame->PreviousPreviousMode != -1)) + { + DPRINT1("Exiting a trap witout restoring previous mode, yet previous mode seems valid: %lx", TrapFrame->PreviousPreviousMode); + while (TRUE); + } +} +#else +#define KiExitTrapDebugChecks(x, y) +#define KiFillTrapFrameDebug(x) +#endif + +// +// Helper Code +// + +BOOLEAN +FORCEINLINE +KiUserTrap(IN PKTRAP_FRAME TrapFrame) +{ + /* Anything else but Ring 0 is Ring 3 */ + return (TrapFrame->SegCs != KGDT_R0_CODE); +} + +BOOLEAN +FORCEINLINE +KiVdmTrap(IN PKTRAP_FRAME TrapFrame) +{ + /* Either the V8086 flag is on, or this is user-mode with a VDM */ + return ((TrapFrame->EFlags & EFLAGS_V86_MASK) || + ((KiUserTrap(TrapFrame)) && (PsGetCurrentProcess()->VdmObjects))); +} + +VOID +FORCEINLINE +KiTrapFrameFromPushaStack(IN PKTRAP_FRAME TrapFrame) +{ + /* + * This sequence is Bavarian Alchemist Black Magic + * + * *** DO NOT MODIFY *** + */ + TrapFrame->Edx = TrapFrame->Esi; + TrapFrame->Esi = TrapFrame->PreviousPreviousMode; + TrapFrame->Ecx = TrapFrame->Ebx; + TrapFrame->Ebx = TrapFrame->Edi; + TrapFrame->Edi = TrapFrame->Eax; + TrapFrame->Eax = TrapFrame->Ebp; + TrapFrame->Ebp = (ULONG)TrapFrame->ExceptionList; + TrapFrame->TempEsp = TrapFrame->SegFs; +} + +VOID +FORCEINLINE +KiPushaStackFromTrapFrame(IN PKTRAP_FRAME TrapFrame) +{ + /* + * This sequence is Bavarian Alchemist Black Magic + * + * *** DO NOT MODIFY *** + */ + TrapFrame->SegFs = TrapFrame->TempEsp; + TrapFrame->ExceptionList = (PVOID)TrapFrame->Ebp; + TrapFrame->Ebp = TrapFrame->Eax; + TrapFrame->Eax = TrapFrame->Edi; + TrapFrame->Edi = TrapFrame->Ebx; + TrapFrame->Ebx = TrapFrame->Ecx; + TrapFrame->PreviousPreviousMode = TrapFrame->Esi; + TrapFrame->Esi = TrapFrame->Edx; +} + +VOID +FORCEINLINE +KiCheckForApcDelivery(IN PKTRAP_FRAME TrapFrame) +{ + PKTHREAD Thread; + KIRQL OldIrql; + + /* Check for V8086 or user-mode trap */ + if ((TrapFrame->EFlags & EFLAGS_V86_MASK) || + (KiUserTrap(TrapFrame))) + { + /* Get the thread */ + Thread = KeGetCurrentThread(); + while (TRUE) + { + /* Turn off the alerted state for kernel mode */ + Thread->Alerted[KernelMode] = FALSE; + + /* Are there pending user APCs? */ + if (Thread->ApcState.UserApcPending) + { + /* Raise to APC level and enable interrupts */ + OldIrql = KfRaiseIrql(APC_LEVEL); + _enable(); + + /* Deliver APCs */ + KiDeliverApc(UserMode, NULL, TrapFrame); + + /* Restore IRQL and disable interrupts once again */ + KfLowerIrql(OldIrql); + _disable(); + } + } + } +} + +VOID +FORCEINLINE +KiDispatchException0Args(IN NTSTATUS Code, + IN ULONG_PTR Address, + IN PKTRAP_FRAME TrapFrame) +{ + /* Helper for exceptions with no arguments */ + KiDispatchExceptionFromTrapFrame(Code, Address, 0, 0, 0, 0, TrapFrame); +} + +FORCEINLINE +VOID +KiTrapReturn(IN PKTRAP_FRAME TrapFrame) +{ + /* Restore registers */ + KiPushaStackFromTrapFrame(TrapFrame); + + /* Regular interrupt exit */ + __asm__ __volatile__ + ( + "movl %0, %%esp\n" + "addl %1, %%esp\n" + "popa\n" + "addl $4, %%esp\n" + "iret\n" + : + : "r"(TrapFrame), "i"(KTRAP_FRAME_LENGTH - KTRAP_FRAME_PREVIOUS_MODE) + : "%esp" + ); +} \ No newline at end of file diff --git a/reactos/ntoskrnl/ke/i386/exp.c b/reactos/ntoskrnl/ke/i386/exp.c index c80ee0364fa..c529a170f90 100644 --- a/reactos/ntoskrnl/ke/i386/exp.c +++ b/reactos/ntoskrnl/ke/i386/exp.c @@ -772,6 +772,7 @@ KeTrapFrameToContext(IN PKTRAP_FRAME TrapFrame, /* Otherwise clear DR registers */ Context->Dr0 = Context->Dr1 = + Context->Dr2 = Context->Dr3 = Context->Dr6 = Context->Dr7 = 0; @@ -838,8 +839,8 @@ KiDispatchException(IN PEXCEPTION_RECORD ExceptionRecord, /* Set the context flags */ Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; - /* Check if User Mode or if the debugger is enabled */ - if ((PreviousMode == UserMode) || (KdDebuggerEnabled)) + /* Check if User Mode or if the kernel debugger is enabled */ + if ((PreviousMode == UserMode) || (KeGetPcr()->KdVersionBlock)) { /* Add the FPU Flag */ Context.ContextFlags |= CONTEXT_FLOATING_POINT; diff --git a/reactos/ntoskrnl/ke/i386/traphdlr.c b/reactos/ntoskrnl/ke/i386/traphdlr.c new file mode 100644 index 00000000000..1aab909aea3 --- /dev/null +++ b/reactos/ntoskrnl/ke/i386/traphdlr.c @@ -0,0 +1,231 @@ +/* + * PROJECT: ReactOS Kernel + * LICENSE: BSD - See COPYING.ARM in the top level directory + * FILE: ntoskrnl/ke/i386/traphdlr.c + * PURPOSE: Kernel Trap Handlers + * PROGRAMMERS: ReactOS Portable Systems Group + */ + +/* INCLUDES *****************************************************************/ + +#include +#define NDEBUG +#include +#include "internal/trap_x.h" + +/* TRAP EXIT CODE *************************************************************/ + +VOID +FASTCALL +KiExitTrap(IN PKTRAP_FRAME TrapFrame, + IN UCHAR State) +{ + KTRAP_STATE_BITS StateBits = { .Bits = State }; + KiExitTrapDebugChecks(TrapFrame, StateBits); + + /* Restore the SEH handler chain */ + KeGetPcr()->Tib.ExceptionList = TrapFrame->ExceptionList; + + /* Check if the previous mode must be restored */ + if (StateBits.PreviousMode) + { + /* Not handled yet */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Check if there are active debug registers */ + if (TrapFrame->Dr7 & ~DR7_RESERVED_MASK) + { + /* Not handled yet */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Check if this was a V8086 trap */ + if (TrapFrame->EFlags & EFLAGS_V86_MASK) + { + /* Not handled yet */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Check if the trap frame was edited */ + if (!(TrapFrame->SegCs & FRAME_EDITED)) + { + /* Not handled yet */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Check if all registers must be restored */ + if (StateBits.Full) + { + /* Only do the restore if we made a transition from user-mode */ + if (KiUserTrap(TrapFrame)) + { + /* Restore segments */ + Ke386SetGs(TrapFrame->SegGs); + Ke386SetEs(TrapFrame->SegEs); + Ke386SetDs(TrapFrame->SegDs); + } + } + + /* Check if we came from user-mode */ + if (KiUserTrap(TrapFrame)) + { + /* Check if the caller wants segments restored */ + if (StateBits.Segments) + { + /* Restore them */ + Ke386SetGs(TrapFrame->SegGs); + Ke386SetEs(TrapFrame->SegEs); + Ke386SetDs(TrapFrame->SegDs); + } + + /* Always restore FS since it goes from KPCR to TEB */ + Ke386SetFs(TrapFrame->SegFs); + } + + /* Check for ABIOS code segment */ + if (TrapFrame->SegCs == 0x80) + { + /* Not handled yet */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Check for system call */ + if (StateBits.SystemCall) + { + /* Not handled yet */ + UNIMPLEMENTED; + while (TRUE); + } + else + { + /* Return from interrupt */ + KiTrapReturn(TrapFrame); + } +} + +VOID +FASTCALL +KiEoiHelper(IN PKTRAP_FRAME TrapFrame) +{ + /* Disable interrupts until we return */ + _disable(); + + /* Check for APC delivery */ + KiCheckForApcDelivery(TrapFrame); + + /* Now exit the trap for real */ + KiExitTrap(TrapFrame, KTS_SEG_BIT | KTS_VOL_BIT); +} + +/* TRAP ENTRY CODE ************************************************************/ + +VOID +FASTCALL +KiEnterTrap(IN PKTRAP_FRAME TrapFrame) +{ + /* Save registers */ + KiTrapFrameFromPushaStack(TrapFrame); + + /* Save segments and then switch to correct ones */ + TrapFrame->SegFs = Ke386GetFs(); + TrapFrame->SegGs = Ke386GetGs(); + TrapFrame->SegDs = Ke386GetDs(); + TrapFrame->SegEs = Ke386GetEs(); + Ke386SetFs(KGDT_R0_PCR); + Ke386SetDs(KGDT_R3_DATA | RPL_MASK); + Ke386SetEs(KGDT_R3_DATA | RPL_MASK); + + /* Save exception list and bogus previous mode */ + TrapFrame->PreviousPreviousMode = -1; + TrapFrame->ExceptionList = KeGetPcr()->Tib.ExceptionList; + + /* Check for 16-bit stack */ + if ((ULONG_PTR)TrapFrame < 0x10000) + { + UNIMPLEMENTED; + while (TRUE); + } + + /* Check for V86 mode */ + if (TrapFrame->EFlags & EFLAGS_V86_MASK) + { + UNIMPLEMENTED; + while (TRUE); + } + + /* Clear direction flag */ + Ke386ClearDirectionFlag(); + + /* Flush DR7 and check for debugging */ + TrapFrame->Dr7 = 0; + if (KeGetCurrentThread()->DispatcherHeader.DebugActive & 0xFF) + { + UNIMPLEMENTED; + while (TRUE); + } + + /* Set debug header */ + KiFillTrapFrameDebug(TrapFrame); +} + +/* EXCEPTION CODE *************************************************************/ + +VOID +FASTCALL +KiSystemFatalException(IN ULONG ExceptionCode, + IN PKTRAP_FRAME TrapFrame) +{ + /* Bugcheck the system */ + KeBugCheckWithTf(UNEXPECTED_KERNEL_MODE_TRAP, + ExceptionCode, + 0, + 0, + 0, + TrapFrame); +} + +VOID +NTAPI +KiDispatchExceptionFromTrapFrame(IN NTSTATUS Code, + IN ULONG_PTR Address, + IN ULONG ParameterCount, + IN ULONG_PTR Parameter1, + IN ULONG_PTR Parameter2, + IN ULONG_PTR Parameter3, + IN PKTRAP_FRAME TrapFrame) +{ + EXCEPTION_RECORD ExceptionRecord; + + /* Build the exception record */ + ExceptionRecord.ExceptionCode = Code; + ExceptionRecord.ExceptionFlags = 0; + ExceptionRecord.ExceptionRecord = NULL; + ExceptionRecord.ExceptionAddress = (PVOID)Address; + ExceptionRecord.NumberParameters = ParameterCount; + if (ParameterCount) + { + /* Copy extra parameters */ + ExceptionRecord.ExceptionInformation[0] = Parameter1; + ExceptionRecord.ExceptionInformation[1] = Parameter2; + ExceptionRecord.ExceptionInformation[2] = Parameter3; + } + + /* Now go dispatch the exception */ + KiDispatchException(&ExceptionRecord, + NULL, + TrapFrame, + TrapFrame->EFlags & EFLAGS_V86_MASK ? + -1 : TrapFrame->SegCs & MODE_MASK, + TRUE); + + /* Return from this trap */ + KiEoiHelper(TrapFrame); +} + +/* EOF */ diff --git a/reactos/ntoskrnl/ntoskrnl-generic.rbuild b/reactos/ntoskrnl/ntoskrnl-generic.rbuild index c89d8915c99..9f8a27b73e6 100644 --- a/reactos/ntoskrnl/ntoskrnl-generic.rbuild +++ b/reactos/ntoskrnl/ntoskrnl-generic.rbuild @@ -51,6 +51,7 @@ systimer.S thrdini.c trap.s + traphdlr.c usercall_asm.S usercall.c v86vdm.c