- Update KTHREAD and KUSER_SHARED_DATA to latest versions. This should make 2K3 drive...
[reactos.git] / reactos / ntoskrnl / ke / i386 / exp.c
index bc8e0d9..0d7d18d 100644 (file)
 #define NDEBUG
 #include <internal/debug.h>
 
+/*
+ * FIXMES:
+ *  - Put back VEH.
+ *  - Clean up file.
+ *  - Sanitize some context fields.
+ *  - Add PSEH handler when an exception occurs in an exception (KiCopyExceptionRecord).
+ *  - Implement official stack trace functions (exported) and remove stuff here.
+ *  - Forward exceptions to user-mode debugger.
+ */
+
+VOID
+NTAPI
+Ki386AdjustEsp0(IN PKTRAP_FRAME TrapFrame);
+
 /* GLOBALS *****************************************************************/
 
 #define FLAG_IF (1<<9)
@@ -110,32 +124,31 @@ BOOLEAN STDCALL
 KiRosPrintAddress(PVOID address)
 {
    PLIST_ENTRY current_entry;
-   MODULE_TEXT_SECTION* current;
-   extern LIST_ENTRY ModuleTextListHead;
+   PLDR_DATA_TABLE_ENTRY current;
+   extern LIST_ENTRY ModuleListHead;
    ULONG_PTR RelativeAddress;
    ULONG i = 0;
 
    do
    {
-     current_entry = ModuleTextListHead.Flink;
+     current_entry = ModuleListHead.Flink;
 
-     while (current_entry != &ModuleTextListHead &&
-            current_entry != NULL)
+     while (current_entry != &ModuleListHead)
        {
           current =
-            CONTAINING_RECORD(current_entry, MODULE_TEXT_SECTION, ListEntry);
+            CONTAINING_RECORD(current_entry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
 
-          if (address >= (PVOID)current->Base &&
-              address < (PVOID)(current->Base + current->Length))
+          if (address >= (PVOID)current->DllBase &&
+              address < (PVOID)((ULONG_PTR)current->DllBase + current->SizeOfImage))
             {
-              RelativeAddress = (ULONG_PTR) address - current->Base;
-              DbgPrint("<%ws: %x>", current->Name, RelativeAddress);
+              RelativeAddress = (ULONG_PTR) address - (ULONG_PTR) current->DllBase;
+              DbgPrint("<%wZ: %x>", &current->FullDllName, RelativeAddress);
               return(TRUE);
             }
           current_entry = current_entry->Flink;
        }
 
-     address = (PVOID)((ULONG_PTR)address & ~KERNEL_BASE);
+     address = (PVOID)((ULONG_PTR)address & ~(ULONG_PTR)MmSystemRangeStart);
    } while(++i <= 1);
 
    return(FALSE);
@@ -173,12 +186,12 @@ KiKernelTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr, PVOID Cr2)
   /* FIXME: Which exceptions are noncontinuable? */
   Er.ExceptionFlags = 0;
 
-  KiDispatchException(&Er, 0, Tf, KernelMode, TRUE);
+  KiDispatchException(&Er, NULL, Tf, KernelMode, TRUE);
 
   return(0);
 }
 
-ULONG
+VOID
 KiDoubleFaultHandler(VOID)
 {
   unsigned int cr2;
@@ -384,10 +397,10 @@ KiDoubleFaultHandler(VOID)
 
    DbgPrint("\n");
    for(;;);
-   return 0;
 }
 
 VOID
+NTAPI
 KiDumpTrapFrame(PKTRAP_FRAME Tf, ULONG Parameter1, ULONG Parameter2)
 {
   ULONG cr3_;
@@ -470,24 +483,21 @@ KiTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr)
  *        Complete CPU context
  */
 {
-   unsigned int cr2;
+   ULONG_PTR cr2;
    NTSTATUS Status;
    ULONG Esp0;
 
+   ASSERT(ExceptionNr != 14);
+
    /* Store the exception number in an unused field in the trap frame. */
-   Tf->DebugArgMark = (PVOID)ExceptionNr;
+   Tf->DebugArgMark = ExceptionNr;
 
    /* Use the address of the trap frame as approximation to the ring0 esp */
    Esp0 = (ULONG)&Tf->Eip;
 
    /* Get CR2 */
    cr2 = Ke386GetCr2();
-   Tf->DebugPointer = (PVOID)cr2;
-
-   if (ExceptionNr == 14 && Tf->Eflags & FLAG_IF)
-   {
-     Ke386EnableInterrupts();
-   }
+   Tf->DebugPointer = cr2;
 
    /*
     * If this was a V86 mode exception then handle it specially
@@ -524,26 +534,6 @@ KiTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr)
        return(0);
      }
 
-   /*
-    * Maybe handle the page fault and return
-    */
-   if (ExceptionNr == 14)
-     {
-        if (Ke386NoExecute && Tf->ErrorCode & 0x10 && cr2 >= KERNEL_BASE)
-       {
-           KEBUGCHECKWITHTF(ATTEMPTED_EXECUTE_OF_NOEXECUTE_MEMORY, 0, 0, 0, 0, Tf);
-       }
-       Status = MmPageFault(Tf->Cs&0xffff,
-                            &Tf->Eip,
-                            &Tf->Eax,
-                            cr2,
-                            Tf->ErrorCode);
-       if (NT_SUCCESS(Status))
-         {
-            return(0);
-         }
-     }
-
    /*
     * Check for a breakpoint that was only for the attention of the debugger.
     */
@@ -581,20 +571,172 @@ KiTrapHandler(PKTRAP_FRAME Tf, ULONG ExceptionNr)
     }
 }
 
+ULONG
+NTAPI
+KiEspFromTrapFrame(IN PKTRAP_FRAME TrapFrame)
+{
+    /* Check if this is user-mode or V86 */
+    if ((TrapFrame->Cs & 1) || (TrapFrame->Eflags & X86_EFLAGS_VM))
+    {
+        /* Return it directly */
+        return TrapFrame->Esp;
+    }
+    else
+    {
+        /* Edited frame */
+        if (!(TrapFrame->Cs & FRAME_EDITED))
+        {
+            /* Return edited value */
+            return TrapFrame->TempEsp;
+        }
+        else
+        {
+            /* Virgin frame, calculate */
+            return (ULONG)&TrapFrame->Esp;
+        }
+    }
+}
+
+VOID
+NTAPI
+KiEspToTrapFrame(IN PKTRAP_FRAME TrapFrame,
+                 IN ULONG Esp)
+{
+    ULONG Previous = KiEspFromTrapFrame(TrapFrame);
+
+    /* Check if this is user-mode or V86 */
+    if ((TrapFrame->Cs & 1) || (TrapFrame->Eflags & X86_EFLAGS_VM))
+    {
+        /* Write it directly */
+        TrapFrame->Esp = Esp;
+    }
+    else
+    {
+        /* Don't allow ESP to be lowered, this is illegal */
+        if (Esp < Previous)
+        {
+            KeBugCheck(SET_OF_INVALID_CONTEXT);
+        }
+
+        /* Create an edit frame, check if it was alrady */
+        if (!(TrapFrame->Cs & FRAME_EDITED))
+        {
+            /* Update the value */
+            TrapFrame->TempEsp = Esp;
+        }
+        else
+        {
+            /* Check if ESP changed */
+            if (Previous != Esp)
+            {
+                /* Save CS */
+                TrapFrame->TempCs = TrapFrame->Cs;
+                TrapFrame->Cs &= ~FRAME_EDITED;
+
+                /* Save ESP */
+                TrapFrame->TempEsp = Esp;
+            }
+        }
+    }
+}
+
+ULONG
+NTAPI
+KiSsFromTrapFrame(IN PKTRAP_FRAME TrapFrame)
+{
+    /* If this was V86 Mode */
+    if (TrapFrame->Eflags & X86_EFLAGS_VM)
+    {
+        /* Just return it */
+        return TrapFrame->Ss;
+    }
+    else if (TrapFrame->Cs & 1)
+    {
+        /* Usermode, return the User SS */
+        return TrapFrame->Ss | 3;
+    }
+    else
+    {
+        /* Kernel mode */
+        return KERNEL_DS;
+    }
+}
+
+VOID
+NTAPI
+KiSsToTrapFrame(IN PKTRAP_FRAME TrapFrame,
+                IN ULONG Ss)
+{
+    /* Remove the high-bits */
+    Ss &= 0xFFFF;
+
+    /* If this was V86 Mode */
+    if (TrapFrame->Eflags & X86_EFLAGS_VM)
+    {
+        /* Just write it */
+        TrapFrame->Ss = Ss;
+    }
+    else if (TrapFrame->Cs & 1)
+    {
+        /* Usermode, save the User SS */
+        TrapFrame->Ss = Ss | 3;
+    }
+}
+
 BOOLEAN
-STDCALL
-KeContextToTrapFrame(PCONTEXT Context,
-                     PKTRAP_FRAME TrapFrame)
+NTAPI
+KeContextToTrapFrame(IN PCONTEXT Context,
+                     IN OUT PKEXCEPTION_FRAME ExceptionFrame,
+                     IN OUT PKTRAP_FRAME TrapFrame,
+                     IN KPROCESSOR_MODE PreviousMode)
 {
+    BOOLEAN V86Switch = FALSE;
+
     /* Start with the basic Registers */
     if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
     {
-        TrapFrame->Esp = Context->Esp;
-        TrapFrame->Ss = Context->SegSs;
-        TrapFrame->Cs = Context->SegCs;
-        TrapFrame->Eip = Context->Eip;
+        /* Check if we went through a V86 switch */
+        if ((Context->EFlags & X86_EFLAGS_VM) !=
+            (TrapFrame->Eflags & X86_EFLAGS_VM))
+        {
+            /* We did, remember this for later */
+            V86Switch = TRUE;
+        }
+
+        /* Copy EFLAGS. FIXME: Needs to be sanitized */
         TrapFrame->Eflags = Context->EFlags;
+
+        /* Copy EBP and EIP */
         TrapFrame->Ebp = Context->Ebp;
+        TrapFrame->Eip = Context->Eip;
+
+        /* Check if we were in V86 Mode */
+        if (TrapFrame->Eflags & X86_EFLAGS_VM)
+        {
+            /* Simply copy the CS value */
+            TrapFrame->Cs = Context->SegCs;
+        }
+        else
+        {
+            /* We weren't in V86, so sanitize the CS (FIXME!) */
+            TrapFrame->Cs = Context->SegCs;
+
+            /* Don't let it under 8, that's invalid */
+            if ((PreviousMode !=KernelMode) && (TrapFrame->Cs < 8))
+            {
+                /* Force it to User CS */
+                TrapFrame->Cs = USER_CS;
+            }
+        }
+
+        /* Handle SS Specially for validation */
+        KiSsToTrapFrame(TrapFrame, Context->SegSs);
+
+        /* Write ESP back; take into account Edited Trap Frames */
+        KiEspToTrapFrame(TrapFrame, Context->Esp);
+
+        /* Handle our V86 Bias if we went through a switch */
+        if (V86Switch) Ki386AdjustEsp0(TrapFrame);
     }
 
     /* Process the Integer Registers */
@@ -611,21 +753,61 @@ KeContextToTrapFrame(PCONTEXT Context,
     /* Process the Context Segments */
     if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS)
     {
-        TrapFrame->Ds = Context->SegDs;
-        TrapFrame->Es = Context->SegEs;
-        TrapFrame->Fs = Context->SegFs;
-        TrapFrame->Gs = Context->SegGs;
+        /* Check if we were in V86 Mode */
+        if (TrapFrame->Eflags & X86_EFLAGS_VM)
+        {
+            /* Copy the V86 Segments directlry */
+            TrapFrame->V86_Ds = Context->SegDs;
+            TrapFrame->V86_Es = Context->SegEs;
+            TrapFrame->V86_Fs = Context->SegFs;
+            TrapFrame->V86_Gs = Context->SegGs;
+        }
+        else if (!(TrapFrame->Cs & 1))
+        {
+            /* For user mode, write the values directly */
+            TrapFrame->Ds = USER_DS;
+            TrapFrame->Es = USER_DS;
+            TrapFrame->Fs = Context->SegFs;
+            TrapFrame->Gs = 0;
+        }
+        else
+        {
+            /* For kernel-mode, return the values */
+            TrapFrame->Ds = Context->SegDs;
+            TrapFrame->Es = Context->SegEs;
+            TrapFrame->Fs = Context->SegFs;
+
+            /* Handle GS specially */
+            if (TrapFrame->Cs == USER_CS)
+            {
+                /* Don't use it, if user */
+                TrapFrame->Gs = 0;
+            }
+            else
+            {
+                /* Copy it if kernel */
+                TrapFrame->Gs = Context->SegGs;
+            }
+        }
     }
 
     /* Handle the Debug Registers */
     if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS)
     {
+        /* FIXME: All these should be sanitized */
         TrapFrame->Dr0 = Context->Dr0;
         TrapFrame->Dr1 = Context->Dr1;
         TrapFrame->Dr2 = Context->Dr2;
         TrapFrame->Dr3 = Context->Dr3;
         TrapFrame->Dr6 = Context->Dr6;
         TrapFrame->Dr7 = Context->Dr7;
+
+        /* Check if usermode */
+        if (PreviousMode != KernelMode)
+        {
+            /* Set the Debug Flag */
+            KeGetCurrentThread()->DispatcherHeader.DebugActive = (Context->Dr7 & DR7_ACTIVE);
+        }
     }
 
     /* Handle FPU and Extended Registers */
@@ -633,79 +815,120 @@ KeContextToTrapFrame(PCONTEXT Context,
 }
 
 VOID
-KeTrapFrameToContext(PKTRAP_FRAME TrapFrame,
-                    PCONTEXT Context)
+NTAPI
+KeTrapFrameToContext(IN PKTRAP_FRAME TrapFrame,
+                     IN PKEXCEPTION_FRAME ExceptionFrame,
+                     IN OUT PCONTEXT Context)
 {
-   if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
-     {
-       Context->SegSs = TrapFrame->Ss;
-       Context->Esp = TrapFrame->Esp;
-       Context->SegCs = TrapFrame->Cs;
-       Context->Eip = TrapFrame->Eip;
-       Context->EFlags = TrapFrame->Eflags;
-       Context->Ebp = TrapFrame->Ebp;
-     }
-   if ((Context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
-     {
-       Context->Eax = TrapFrame->Eax;
-       Context->Ebx = TrapFrame->Ebx;
-       Context->Ecx = TrapFrame->Ecx;
-       /*
-        * NOTE: In the trap frame which is built on entry to a system
-        * call TrapFrame->Edx will actually hold the address of the
-        * previous TrapFrame. I don't believe leaking this information
-        * has security implications. Also EDX holds the address of the
-        * arguments to the system call in progress so it isn't of much
-        * interest to the debugger.
-        */
-       Context->Edx = TrapFrame->Edx;
-       Context->Esi = TrapFrame->Esi;
-       Context->Edi = TrapFrame->Edi;
-     }
-   if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS)
-     {
-       Context->SegDs = TrapFrame->Ds;
-       Context->SegEs = TrapFrame->Es;
-       Context->SegFs = TrapFrame->Fs;
-       Context->SegGs = TrapFrame->Gs;
-     }
-   if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS)
-     {
-       /*
-        * FIXME: Implement this case
-        */
-       Context->ContextFlags &= (~CONTEXT_DEBUG_REGISTERS) | CONTEXT_i386;
-     }
-   if ((Context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT)
-     {
-       /*
-        * FIXME: Implement this case
-        *
-        * I think this should only be filled for FPU exceptions, otherwise I
-         * would not know where to get it from as it can be the current state
-        * of the FPU or already saved in the thread's FPU save area.
-        *  -blight
-        */
-       Context->ContextFlags &= (~CONTEXT_FLOATING_POINT) | CONTEXT_i386;
-     }
-#if 0
-   if ((Context->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)
-     {
-       /*
-        * FIXME: Investigate this
-        *
-        * This is the XMM state (first 512 bytes of FXSAVE_FORMAT/FX_SAVE_AREA)
-        * This should only be filled in case of a SIMD exception I think, so
-        * this is not the right place (like for FPU the state could already be
-        * saved in the thread's FX_SAVE_AREA or still be in the CPU)
-        *  -blight
-        */
-        Context->ContextFlags &= ~CONTEXT_EXTENDED_REGISTERS;
-     }
-#endif
+    PFX_SAVE_AREA FxSaveArea = NULL;
+
+    /* Start with the Control flags */
+    if ((Context->ContextFlags & CONTEXT_CONTROL) == CONTEXT_CONTROL)
+    {
+        /* EBP, EIP and EFLAGS */
+        Context->Ebp = TrapFrame->Ebp;
+        Context->Eip = TrapFrame->Eip;
+        Context->EFlags = TrapFrame->Eflags;
+
+        /* Return the correct CS */
+        if (!(TrapFrame->Cs & FRAME_EDITED) &&
+            !(TrapFrame->Eflags & X86_EFLAGS_VM))
+        {
+            /* Get it from the Temp location */
+            Context->SegCs = TrapFrame->TempCs & 0xFFFF;
+        }
+        else
+        {
+            /* Return it directly */
+            Context->SegCs = TrapFrame->Cs & 0xFFFF;
+        }
+
+        /* Get the Ss and ESP */
+        Context->SegSs = KiSsFromTrapFrame(TrapFrame);
+        Context->Esp = KiEspFromTrapFrame(TrapFrame);
+    }
+
+    /* Handle the Segments */
+    if ((Context->ContextFlags & CONTEXT_SEGMENTS) == CONTEXT_SEGMENTS)
+    {
+        /* Do V86 Mode first */
+        if (TrapFrame->Eflags & X86_EFLAGS_VM)
+        {
+            /* Return from the V86 location */
+            Context->SegGs = TrapFrame->V86_Gs & 0xFFFF;
+            Context->SegFs = TrapFrame->V86_Fs & 0xFFFF;
+            Context->SegEs = TrapFrame->V86_Es & 0xFFFF;
+            Context->SegDs = TrapFrame->V86_Ds & 0xFFFF;
+        }
+        else
+        {
+            /* Check if this was a Kernel Trap */
+            if (TrapFrame->Cs == KERNEL_CS)
+            {
+                /* Set valid selectors */
+                TrapFrame->Gs = 0;
+                TrapFrame->Fs = PCR_SELECTOR;
+                TrapFrame->Es = USER_DS;
+                TrapFrame->Ds = USER_DS;
+            }
+
+            /* Return the segments */
+            Context->SegGs = TrapFrame->Gs & 0xFFFF;
+            Context->SegFs = TrapFrame->Fs & 0xFFFF;
+            Context->SegEs = TrapFrame->Es & 0xFFFF;
+            Context->SegDs = TrapFrame->Ds & 0xFFFF;
+        }
+    }
+
+    /* Handle the simple registers */
+    if ((Context->ContextFlags & CONTEXT_INTEGER) == CONTEXT_INTEGER)
+    {
+        /* Return them directly */
+        Context->Eax = TrapFrame->Eax;
+        Context->Ebx = TrapFrame->Ebx;
+        Context->Ecx = TrapFrame->Ecx;
+        Context->Edx = TrapFrame->Edx;
+        Context->Esi = TrapFrame->Esi;
+        Context->Edi = TrapFrame->Edi;
+    }
+
+    if ((Context->ContextFlags & CONTEXT_DEBUG_REGISTERS) == CONTEXT_DEBUG_REGISTERS)
+    {
+        /*
+         * FIXME: Implement this case
+         */
+        Context->ContextFlags &= (~CONTEXT_DEBUG_REGISTERS) | CONTEXT_i386;
+    }
+    if ((Context->ContextFlags & CONTEXT_FLOATING_POINT) == CONTEXT_FLOATING_POINT)
+    {
+        FxSaveArea = KiGetFpuState(KeGetCurrentThread());
+        if (FxSaveArea != NULL)
+        {
+            KiFxSaveAreaToFloatingSaveArea(&Context->FloatSave, FxSaveArea);
+        }
+        else
+        {
+            Context->ContextFlags &= (~CONTEXT_FLOATING_POINT) | CONTEXT_i386;
+        }
+    }
+    if ((Context->ContextFlags & CONTEXT_EXTENDED_REGISTERS) == CONTEXT_EXTENDED_REGISTERS)
+    {
+        if (FxSaveArea == NULL)
+            FxSaveArea = KiGetFpuState(KeGetCurrentThread());
+        if (FxSaveArea != NULL)
+        {
+            memcpy(Context->ExtendedRegisters, &FxSaveArea->U.FxArea,
+                   min(sizeof (Context->ExtendedRegisters), sizeof (FxSaveArea->U.FxArea)) );
+        }
+        else
+        {
+            Context->ContextFlags &= (~CONTEXT_EXTENDED_REGISTERS) | CONTEXT_i386;
+        }
+    }
 }
 
 VOID
+NTAPI
 KeDumpStackFrames(PULONG Frame)
 {
        PULONG StackBase, StackEnd;
@@ -730,7 +953,7 @@ KeDumpStackFrames(PULONG Frame)
                }
 
                StackBase = Frame;
-               StackEnd = mbi.BaseAddress + mbi.RegionSize;
+               StackEnd = (PULONG)((ULONG_PTR)mbi.BaseAddress + mbi.RegionSize);
 
                while ( Frame >= StackBase && Frame < StackEnd )
                {
@@ -766,7 +989,7 @@ KeRosDumpStackFrames ( PULONG Frame, ULONG FrameCount )
                if ( !Frame )
                {
 #if defined __GNUC__
-                       __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
+                       __asm__("mov %%ebp, %0" : "=r" (Frame) : );
 #elif defined(_MSC_VER)
                        __asm mov [Frame], ebp
 #endif
@@ -787,7 +1010,7 @@ KeRosDumpStackFrames ( PULONG Frame, ULONG FrameCount )
                }
 
                StackBase = Frame;
-               StackEnd = mbi.BaseAddress + mbi.RegionSize;
+               StackEnd = (PULONG)((ULONG_PTR)mbi.BaseAddress + mbi.RegionSize);
 
                while ( Frame >= StackBase && Frame < StackEnd && i++ < FrameCount )
                {
@@ -820,7 +1043,7 @@ KeRosGetStackFrames ( PULONG Frames, ULONG FrameCount )
        _SEH_TRY
        {
 #if defined __GNUC__
-               __asm__("mov %%ebp, %%ebx" : "=b" (Frame) : );
+               __asm__("mov %%ebp, %0" : "=r" (Frame) : );
 #elif defined(_MSC_VER)
                __asm mov [Frame], ebp
 #endif
@@ -839,7 +1062,7 @@ KeRosGetStackFrames ( PULONG Frames, ULONG FrameCount )
                }
 
                StackBase = Frame;
-               StackEnd = mbi.BaseAddress + mbi.RegionSize;
+               StackEnd = (PULONG)((ULONG_PTR)mbi.BaseAddress + mbi.RegionSize);
 
                while ( Count < FrameCount && Frame >= StackBase && Frame < StackEnd )
                {
@@ -889,7 +1112,9 @@ set_task_gate(unsigned int sel, unsigned task_sel)
   KiIdt[sel].b = 0x8500;
 }
 
-VOID INIT_FUNCTION
+VOID
+INIT_FUNCTION
+NTAPI
 KeInitExceptions(VOID)
 /*
  * FUNCTION: Initalize CPU exception handling
@@ -932,6 +1157,168 @@ KeInitExceptions(VOID)
    set_system_call_gate(0x2e,(int)KiSystemService);
 }
 
+VOID
+NTAPI
+KiDispatchException(PEXCEPTION_RECORD ExceptionRecord,
+                    PKEXCEPTION_FRAME ExceptionFrame,
+                    PKTRAP_FRAME TrapFrame,
+                    KPROCESSOR_MODE PreviousMode,
+                    BOOLEAN FirstChance)
+{
+    CONTEXT Context;
+    KD_CONTINUE_TYPE Action;
+    ULONG_PTR Stack, NewStack;
+    ULONG Size;
+    BOOLEAN UserDispatch = FALSE;
+    DPRINT("KiDispatchException() called\n");
+
+    /* Increase number of Exception Dispatches */
+    KeGetCurrentPrcb()->KeExceptionDispatchCount++;
+
+    /* Set the context flags */
+    Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
+
+    /* Check if User Mode */
+    if (PreviousMode == UserMode)
+    {
+        extern ULONG FxsrSupport;
+        /* Add the FPU Flag */
+        Context.ContextFlags |= CONTEXT_FLOATING_POINT;
+        if (FxsrSupport)
+            Context.ContextFlags |= CONTEXT_EXTENDED_REGISTERS;
+    }
+
+    /* Get a Context */
+    KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
+
+    /* Handle kernel-mode first, it's simpler */
+    if (PreviousMode == KernelMode)
+    {
+        /* Check if this is a first-chance exception */
+        if (FirstChance == TRUE)
+        {
+            /* Break into the debugger for the first time */
+            Action = KdpEnterDebuggerException(ExceptionRecord,
+                                               PreviousMode,
+                                               &Context,
+                                               TrapFrame,
+                                               TRUE,
+                                               TRUE);
+
+            /* If the debugger said continue, then continue */
+            if (Action == kdContinue) goto Handled;
+
+            /* If the Debugger couldn't handle it, dispatch the exception */
+            if (RtlDispatchException(ExceptionRecord, &Context))
+            {
+                /* It was handled by an exception handler, continue */
+                goto Handled;
+            }
+        }
+
+        /* This is a second-chance exception, only for the debugger */
+        Action = KdpEnterDebuggerException(ExceptionRecord,
+                                           PreviousMode,
+                                           &Context,
+                                           TrapFrame,
+                                           FALSE,
+                                           FALSE);
+
+        /* If the debugger said continue, then continue */
+        if (Action == kdContinue) goto Handled;
+
+        /* Third strike; you're out */
+        KEBUGCHECKWITHTF(KMODE_EXCEPTION_NOT_HANDLED,
+                         ExceptionRecord->ExceptionCode,
+                         (ULONG_PTR)ExceptionRecord->ExceptionAddress,
+                         ExceptionRecord->ExceptionInformation[0],
+                         ExceptionRecord->ExceptionInformation[1],
+                         TrapFrame);
+    }
+    else
+    {
+        /* User mode exception, was it first-chance? */
+        if (FirstChance)
+        {
+            /* Enter Debugger if available */
+            Action = KdpEnterDebuggerException(ExceptionRecord,
+                                               PreviousMode,
+                                               &Context,
+                                               TrapFrame,
+                                               TRUE,
+                                               TRUE);
+
+            /* Exit if we're continuing */
+            if (Action == kdContinue) goto Handled;
+
+            /* FIXME: Forward exception to user mode debugger */
+
+            /* Set up the user-stack */
+            _SEH_TRY
+            {
+                /* Align context size and get stack pointer */
+                Size = (sizeof(CONTEXT) + 3) & ~3;
+                Stack = (Context.Esp & ~3) - Size;
+                DPRINT("Stack: %lx\n", Stack);
+
+                /* Probe stack and copy Context */
+                ProbeForWrite((PVOID)Stack, Size, sizeof(ULONG));
+                RtlCopyMemory((PVOID)Stack, &Context, sizeof(CONTEXT));
+
+                /* Align exception record size and get stack pointer */
+                Size = (sizeof(EXCEPTION_RECORD) - 
+                        (EXCEPTION_MAXIMUM_PARAMETERS - ExceptionRecord->NumberParameters) *
+                        sizeof(ULONG) + 3) & ~3;
+                NewStack = Stack - Size;
+                DPRINT("NewStack: %lx\n", NewStack);
+
+                /* Probe stack and copy exception record. Don't forget to add the two params */
+                ProbeForWrite((PVOID)(NewStack - 2 * sizeof(ULONG_PTR)),
+                              Size +  2 * sizeof(ULONG_PTR),
+                              sizeof(ULONG));
+                RtlCopyMemory((PVOID)NewStack, ExceptionRecord, Size);
+
+                /* Now write the two params for the user-mode dispatcher */
+                *(PULONG_PTR)(NewStack - 1 * sizeof(ULONG_PTR)) = Stack;
+                *(PULONG_PTR)(NewStack - 2 * sizeof(ULONG_PTR)) = NewStack;
+
+                /* Set new Stack Pointer */
+                KiEspToTrapFrame(TrapFrame, NewStack - 2 * sizeof(ULONG_PTR));
+
+                /* Set EIP to the User-mode Dispathcer */
+                TrapFrame->Eip = (ULONG)KeUserExceptionDispatcher;
+                UserDispatch = TRUE;
+                _SEH_LEAVE;
+            }
+            _SEH_HANDLE
+            {
+                /* Do second-chance */
+            }
+            _SEH_END;
+        }
+
+        /* If we dispatch to user, return now */
+        if (UserDispatch) return;
+
+        /* FIXME: Forward the exception to the debugger for 2nd chance */
+
+        /* 3rd strike, kill the thread */
+        DPRINT1("Unhandled UserMode exception, terminating thread\n");
+        ZwTerminateThread(NtCurrentThread(), ExceptionRecord->ExceptionCode);
+        KEBUGCHECKWITHTF(KMODE_EXCEPTION_NOT_HANDLED,
+                         ExceptionRecord->ExceptionCode,
+                         (ULONG_PTR)ExceptionRecord->ExceptionAddress,
+                         ExceptionRecord->ExceptionInformation[0],
+                         ExceptionRecord->ExceptionInformation[1],
+                         TrapFrame);
+    }
+
+Handled:
+    /* Convert the context back into Trap/Exception Frames */
+    KeContextToTrapFrame(&Context, NULL, TrapFrame, PreviousMode);
+    return;
+}
+
 /*
  * @implemented
  */
@@ -948,36 +1335,7 @@ KeRaiseUserException(IN NTSTATUS ExceptionCode)
     } _SEH_END;
 
    OldEip = Thread->TrapFrame->Eip;
-   Thread->TrapFrame->Eip = (ULONG_PTR)LdrpGetSystemDllRaiseExceptionDispatcher();
+   Thread->TrapFrame->Eip = (ULONG_PTR)KeRaiseUserExceptionDispatcher;
    return((NTSTATUS)OldEip);
 }
 
-/*
- * @implemented
- */
-NTSTATUS
-STDCALL
-NtRaiseException (
-    IN PEXCEPTION_RECORD ExceptionRecord,
-    IN PCONTEXT Context,
-    IN BOOLEAN SearchFrames)
-{
-    PKTHREAD Thread = KeGetCurrentThread();
-    PKTRAP_FRAME TrapFrame = Thread->TrapFrame;
-    PKTRAP_FRAME PrevTrapFrame = (PKTRAP_FRAME)TrapFrame->Edx;
-
-    KeGetCurrentKPCR()->Tib.ExceptionList = TrapFrame->ExceptionList;
-
-    KiDispatchException(ExceptionRecord,
-                        Context,
-                        TrapFrame,
-                        KeGetPreviousMode(),
-                        SearchFrames);
-
-    /* Restore the user context */
-    Thread->TrapFrame = PrevTrapFrame;
-    __asm__("mov %%ebx, %%esp;\n" "jmp _KiServiceExit": : "b" (TrapFrame));
-
-    /* We never get here */
-    return(STATUS_SUCCESS);
-}