- Use _SEH2_YIELD when returning from an exception instead of returning outside the...
[reactos.git] / reactos / ntoskrnl / ke / i386 / usercall.c
index 29378cb..a07a055 100644 (file)
-/* $Id: usercall.c,v 1.1 1999/11/12 12:01:16 dwelch Exp $
- *
- * COPYRIGHT:       See COPYING in the top level directory
- * PROJECT:         ReactOS kernel
- * FILE:            ntoskrnl/hal/x86/usercall.c
- * PURPOSE:         2E interrupt handler
- * PROGRAMMER:      David Welch (david.welch@seh.ox.ac.uk)
- * UPDATE HISTORY:
- *                  ???
+/*
+ * PROJECT:         ReactOS Kernel
+ * LICENSE:         GPL - See COPYING in the top level directory
+ * FILE:            ntoskrnl/ke/i386/usercall.c
+ * PURPOSE:         User-mode Callout Mechanisms (APC and Win32K Callbacks)
+ * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
  */
 
-#include <ddk/ntddk.h>
-#include <internal/ntoskrnl.h>
-#include <internal/ke.h>
-#include <internal/symbol.h>
-#include <internal/i386/segment.h>
-#include <internal/mmhal.h>
+/* INCLUDES ******************************************************************/
 
+#include <ntoskrnl.h>
 #define NDEBUG
-#include <internal/debug.h>
-#include <internal/service.h>
-
-#include <ddk/defines.h>
+#include <debug.h>
 
-extern SERVICE_TABLE _SystemServiceTable[];
+extern PGDI_BATCHFLUSH_ROUTINE KeGdiFlushUserBatch;
 
-/* The service dispatcher will take the service number passed in
- * by the user mode process, logical and it with ServiceNumberMask
- * and compare the resulting value with ServiceNumberValue.  If the
- * value matches, The passed service number will be and'ed with the
- * inverse of ServiceNumberMask to obtain the index into the ServiceTable
- * for the service to call
- */
-typedef struct _HAL_DISPATCH_TABLE_ENTRY
-{
-  DWORD  ServiceNumberMask;
-  DWORD  ServiceNumberValue;
-  PSERVICE_TABLE  ServiceTable;
-  DWORD  TableCount;
-} HAL_DISPATCH_TABLE_ENTRY, *PHAL_DISPATCH_TABLE_ENTRY;
+/* PRIVATE FUNCTIONS *********************************************************/
 
-static KSPIN_LOCK DispatchTableLock = {0,};
-static DWORD DispatchTableCount = 0;
-static HAL_DISPATCH_TABLE_ENTRY DispatchTables[16];
-
-NTSTATUS HalRegisterServiceTable(DWORD  Mask, 
-                                 DWORD  Value, 
-                                 PSERVICE_TABLE  Table,
-                                 DWORD  Count)
+/*++
+ * @name KiInitializeUserApc
+ *
+ *     Prepares the Context for a User-Mode APC called through NTDLL.DLL
+ *
+ * @param Reserved
+ *        Pointer to the Exception Frame on non-i386 builds.
+ *
+ * @param TrapFrame
+ *        Pointer to the Trap Frame.
+ *
+ * @param NormalRoutine
+ *        Pointer to the NormalRoutine to call.
+ *
+ * @param NormalContext
+ *        Pointer to the context to send to the Normal Routine.
+ *
+ * @param SystemArgument[1-2]
+ *        Pointer to a set of two parameters that contain untyped data.
+ *
+ * @return None.
+ *
+ * @remarks None.
+ *
+ *--*/
+VOID
+NTAPI
+KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame,
+                    IN PKTRAP_FRAME TrapFrame,
+                    IN PKNORMAL_ROUTINE NormalRoutine,
+                    IN PVOID NormalContext,
+                    IN PVOID SystemArgument1,
+                    IN PVOID SystemArgument2)
 {
-  NTSTATUS  Status;
-  KIRQL  OldLvl;
-  
-  KeAcquireSpinLock(&DispatchTableLock, &OldLvl);
-
-  Status = STATUS_SUCCESS;
-
-  /* FIXME: should check for invalid/overlapping service tables  */
-  DispatchTables[DispatchTableCount].ServiceNumberMask = Mask;
-  DispatchTables[DispatchTableCount].ServiceNumberValue = Value;
-  DispatchTables[DispatchTableCount].ServiceTable = Table;
-  DispatchTables[DispatchTableCount].TableCount = Count;
-  DispatchTableCount++;
-  
-  KeReleaseSpinLock(&DispatchTableLock, OldLvl);
-
-  return  Status;
+    CONTEXT Context;
+    ULONG_PTR Stack, AlignedEsp;
+    ULONG ContextLength;
+    EXCEPTION_RECORD SehExceptRecord;
+
+    /* Don't deliver APCs in V86 mode */
+    if (TrapFrame->EFlags & EFLAGS_V86_MASK) return;
+
+    /* Save the full context */
+    Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
+    KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
+
+    /* Protect with SEH */
+    _SEH2_TRY
+    {
+        /* Sanity check */
+        ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode);
+
+        /* Get the aligned size */
+        AlignedEsp = Context.Esp & ~3;
+        ContextLength = CONTEXT_ALIGNED_SIZE + (4 * sizeof(ULONG_PTR));
+        Stack = ((AlignedEsp - 8) & ~3) - ContextLength;
+
+        /* Probe the stack */
+        ProbeForWrite((PVOID)Stack, AlignedEsp - Stack, 1);
+        ASSERT(!(Stack & 3));
+
+        /* Copy data into it */
+        RtlCopyMemory((PVOID)(Stack + (4 * sizeof(ULONG_PTR))),
+                      &Context,
+                      sizeof(CONTEXT));
+
+        /* Run at APC dispatcher */
+        TrapFrame->Eip = (ULONG)KeUserApcDispatcher;
+        TrapFrame->HardwareEsp = Stack;
+
+        /* Setup Ring 3 state */
+        TrapFrame->SegCs = Ke386SanitizeSeg(KGDT_R3_CODE, UserMode);
+        TrapFrame->HardwareSegSs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode);
+        TrapFrame->SegDs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode);
+        TrapFrame->SegEs = Ke386SanitizeSeg(KGDT_R3_DATA, UserMode);
+        TrapFrame->SegFs = Ke386SanitizeSeg(KGDT_R3_TEB, UserMode);
+        TrapFrame->SegGs = 0;
+        TrapFrame->ErrCode = 0;
+
+        /* Sanitize EFLAGS */
+        TrapFrame->EFlags = Ke386SanitizeFlags(Context.EFlags, UserMode);
+
+        /* Check if thread has IOPL and force it enabled if so */
+        if (KeGetCurrentThread()->Iopl) TrapFrame->EFlags |= EFLAGS_IOPL;
+
+        /* Setup the stack */
+        *(PULONG_PTR)(Stack + 0 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalRoutine;
+        *(PULONG_PTR)(Stack + 1 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalContext;
+        *(PULONG_PTR)(Stack + 2 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument1;
+        *(PULONG_PTR)(Stack + 3 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument2;
+    }
+    _SEH2_EXCEPT((RtlCopyMemory(&SehExceptRecord, _SEH2_GetExceptionInformation()->ExceptionRecord, sizeof(EXCEPTION_RECORD)), EXCEPTION_EXECUTE_HANDLER))
+    {
+        /* Dispatch the exception */
+        SehExceptRecord.ExceptionAddress = (PVOID)TrapFrame->Eip;
+        KiDispatchException(&SehExceptRecord,
+                            ExceptionFrame,
+                            TrapFrame,
+                            UserMode,
+                            TRUE);
+    }
+    _SEH2_END;
 }
 
-#define _STR(x) #x
-#define STR(x) _STR(x)
-
-void PsBeginThreadWithContextInternal(void);
-   __asm__(
-     "\n\t.global _PsBeginThreadWithContextInternal\n\t"
-     "_PsBeginThreadWithContextInternal:\n\t"
-//     "pushl $1\n\t"
-//     "call _KeLowerIrql\n\t"
-     "call _PiBeforeBeginThread\n\t"
-//     "popl %eax\n\t"
-     "popl %eax\n\t"
-     "popl %eax\n\t"
-     "popl %eax\n\t"
-     "popl %eax\n\t"
-     "popl %eax\n\t"
-     "popl %eax\n\t"
-     "popl %eax\n\t"
-     "addl $112,%esp\n\t"
-     "popl %gs\n\t"
-     "popl %fs\n\t"
-     "popl %es\n\t"
-     "popl %ds\n\t"
-     "popl %edi\n\t"
-     "popl %esi\n\t"
-     "popl %ebx\n\t"
-     "popl %edx\n\t"
-     "popl %ecx\n\t"
-     "popl %eax\n\t"
-     "popl %ebp\n\t"
-     "iret\n\t");
+/* PUBLIC FUNCTIONS **********************************************************/
 
-VOID KiSystemCallHook(ULONG Nr)
+/*
+ * @implemented
+ */
+NTSTATUS
+NTAPI
+KeUserModeCallback(IN ULONG RoutineIndex,
+                   IN PVOID Argument,
+                   IN ULONG ArgumentLength,
+                   OUT PVOID *Result,
+                   OUT PULONG ResultLength)
 {
-//   DbgPrint("KiSystemCallHook(Nr %d) %d\n", Nr, KeGetCurrentIrql());
-//   DbgPrint("SystemCall %x\n", _SystemServiceTable[Nr].Function);
-   assert_irql(PASSIVE_LEVEL);
+    ULONG_PTR NewStack, OldStack;
+    PULONG UserEsp;
+    NTSTATUS CallbackStatus;
+    PEXCEPTION_REGISTRATION_RECORD ExceptionList;
+    PTEB Teb;
+    ULONG GdiBatchCount = 0;
+    ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE);
+    ASSERT(KeGetPreviousMode() == UserMode);
+
+    /* Get the current user-mode stack */
+    UserEsp = KiGetUserModeStackAddress();
+    OldStack = *UserEsp;
+
+    /* Enter a SEH Block */
+    _SEH2_TRY
+    {
+        /* Calculate and align the stack size */
+        NewStack = (OldStack - ArgumentLength) & ~3;
+
+        /* Make sure it's writable */
+        ProbeForWrite((PVOID)(NewStack - 6 * sizeof(ULONG_PTR)),
+                      ArgumentLength + 6 * sizeof(ULONG_PTR),
+                      sizeof(CHAR));
+
+        /* Copy the buffer into the stack */
+        RtlCopyMemory((PVOID)NewStack, Argument, ArgumentLength);
+
+        /* Write the arguments */
+        NewStack -= 24;
+        *(PULONG)NewStack = 0;
+        *(PULONG)(NewStack + 4) = RoutineIndex;
+        *(PULONG)(NewStack + 8) = (NewStack + 24);
+        *(PULONG)(NewStack + 12) = ArgumentLength;
+
+        /* Save the exception list */
+        Teb = KeGetCurrentThread()->Teb;
+        ExceptionList = Teb->Tib.ExceptionList;
+
+        /* Jump to user mode */
+        *UserEsp = NewStack;
+        CallbackStatus = KiCallUserMode(Result, ResultLength);
+        if (CallbackStatus != STATUS_CALLBACK_POP_STACK)
+        {
+            /* Only restore the exception list if we didn't crash in ring 3 */
+            Teb->Tib.ExceptionList = ExceptionList;
+            CallbackStatus = STATUS_SUCCESS;
+        }
+        else
+        {
+            /* Otherwise, pop the stack */
+            OldStack = *UserEsp;
+        }
+
+        /* Read the GDI Batch count */
+        GdiBatchCount = Teb->GdiBatchCount;
+    }
+    _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+    {
+        /* Get the SEH exception */
+        _SEH2_YIELD(return _SEH2_GetExceptionCode());
+    }
+    _SEH2_END;
+
+    /* Check if we have GDI Batch operations */
+    if (GdiBatchCount)
+    {
+          *UserEsp -= 256;
+          KeGdiFlushUserBatch();
+    }
+
+    /* Restore stack and return */
+    *UserEsp = OldStack;
+    return CallbackStatus;
 }
 
-void interrupt_handler2e(void);
-   __asm__("\n\t.global _interrupt_handler2e\n\t"
-           "_interrupt_handler2e:\n\t"
-           
-          /* Save the user context */
-          "pushl %ebp\n\t"       /* Ebp */
-          
-          "pushl %eax\n\t"       /* Eax */
-          "pushl %ecx\n\t"       /* Ecx */
-          "pushl %edx\n\t"       /* Edx */
-          "pushl %ebx\n\t"       /* Ebx */
-          "pushl %esi\n\t"       /* Esi */
-          "pushl %edi\n\t"       /* Edi */
-          
-          "pushl %ds\n\t"        /* SegDs */
-          "pushl %es\n\t"        /* SegEs */
-          "pushl %fs\n\t"        /* SegFs */
-          "pushl %gs\n\t"        /* SegGs */
-          
-          "subl $112,%esp\n\t"   /* FloatSave */
-          
-          "pushl $0\n\t"         /* Dr7 */
-          "pushl $0\n\t"         /* Dr6 */
-          "pushl $0\n\t"         /* Dr3 */
-          "pushl $0\n\t"         /* Dr2 */
-          "pushl $0\n\t"         /* Dr1 */
-          "pushl $0\n\t"         /* Dr0 */
-          
-          "pushl $0\n\t"         /* ContextFlags */
-          
-           /*  Set ES to kernel segment  */
-           "movw  $"STR(KERNEL_DS)",%bx\n\t"
-           "movw %bx,%es\n\t"
-           
-          /* Save pointer to user context as argument to system call */
-          "pushl %esp\n\t"
-          
-           /*  Allocate new Kernel stack frame  */
-           "movl %esp,%ebp\n\t"
-           
-           /*  Users's current stack frame pointer is source  */
-           "movl %edx,%esi\n\t"
-
-           /* FIXME: determine system service table to use  */
-           /* FIXME: chech to see if SS is valid/inrange  */
-           
-           /*  Allocate room for argument list from kernel stack  */
-           "movl %es:__SystemServiceTable(,%eax,8),%ecx\n\t"
-           "subl %ecx,%esp\n\t"
-           
-           /*  Copy the arguments from the user stack to the kernel stack  */
-           "movl %esp,%edi\n\t"
-           "rep\n\tmovsb\n\t"
-           
-           /*  DS is now also kernel segment  */
-           "movw %bx,%ds\n\t"
-           
-          /* Call system call hook */
-          "pushl %eax\n\t"
-          "call _KiSystemCallHook\n\t"
-          "popl %eax\n\t"
-          
-           /*  Make the system service call  */
-           "movl %ds:__SystemServiceTable+4(,%eax,8),%eax\n\t"
-           "call *%eax\n\t"
-           
-           /*  Deallocate the kernel stack frame  */
-           "movl %ebp,%esp\n\t"
-           
-          /* Remove pointer to user context from stack */
-          "addl $4,%esp\n\t"
-          
-           /*  Restore the user context  */
-          "addl $4,%esp\n\t"    /* UserContext */
-          "addl $24,%esp\n\t"   /* Dr[0-3,6-7] */
-          "addl $112,%esp\n\t"  /* FloatingSave */
-          "popl %gs\n\t"        /* SegGs */
-          "popl %fs\n\t"        /* SegFs */
-          "popl %es\n\t"        /* SegEs */
-          "popl %ds\n\t"        /* SegDs */
-          
-          "popl %edi\n\t"       /* Edi */
-          "popl %esi\n\t"       /* Esi */
-          "popl %ebx\n\t"       /* Ebx */
-          "popl %edx\n\t"       /* Edx */
-          "popl %ecx\n\t"       /* Ecx */
-          "addl $4,%esp\n\t"       /* Eax (Not restored) */
-          
-          "popl %ebp\n\t"       /* Ebp */
-          
-           "iret\n\t");
-
-
-void old_interrupt_handler2e(void);
-   __asm__("\n\t.global _old_interrupt_handler2e\n\t"
-           "_old_interrupt_handler2e:\n\t"
-           
-           /*  Save the users context  */
-           "pushl %ds\n\t"
-           "pushl %es\n\t"
-           "pushl %esi\n\t"
-           "pushl %edi\n\t"
-           "pushl %ebp\n\t"
-           "pushl %ebx\n\t"
-           
-           /*  Set ES to kernel segment  */
-           "movw  $"STR(KERNEL_DS)",%bx\n\t"
-           "movw %bx,%es\n\t"
-           
-           /*  Allocate new Kernel stack frame  */
-           "movl %esp,%ebp\n\t"
-           
-           /*  Users's current stack frame pointer is source  */
-           "movl %edx,%esi\n\t"
-
-           /* FIXME: determine system service table to use  */
-           /* FIXME: chech to see if SS is valid/inrange  */
-           
-           /*  Allocate room for argument list from kernel stack  */
-           "movl %es:__SystemServiceTable(,%eax,8),%ecx\n\t"
-           "subl %ecx,%esp\n\t"
-           
-           /*  Copy the arguments from the user stack to the kernel stack  */
-           "movl %esp,%edi\n\t"
-           "rep\n\tmovsb\n\t"
-           
-           /*  DS is now also kernel segment  */
-           "movw %bx,%ds\n\t"
-           
-          /* Call system call hook */
-          "pushl %eax\n\t"
-          "call _KiSystemCallHook\n\t"
-          "popl %eax\n\t"
-          
-           /*  Make the system service call  */
-           "movl %ds:__SystemServiceTable+4(,%eax,8),%eax\n\t"
-           "call *%eax\n\t"
-           
-           /*  Deallocate the kernel stack frame  */
-           "movl %ebp,%esp\n\t"
-           
-           /*  Restore the user context  */
-           "popl %ebx\n\t"
-           "popl %ebp\n\t"
-           "popl %edi\n\t"
-           "popl %esi\n\t"
-           "popl %es\n\t"
-           "popl %ds\n\t"
-           "iret\n\t");
-
-
+/* EOF */