[NTOS]: Implement GUI thread promotion during the first GUI system call in C. This...
authorSir Richard <sir_richard@svn.reactos.org>
Tue, 19 Jan 2010 09:45:30 +0000 (09:45 +0000)
committerSir Richard <sir_richard@svn.reactos.org>
Tue, 19 Jan 2010 09:45:30 +0000 (09:45 +0000)
[NTOS]: Implement SYSENTER system calls in C as well.

All system calls are now handled in C. This code will be further optimized/refined soon.

svn path=/trunk/; revision=45148

reactos/ntoskrnl/include/internal/ke.h
reactos/ntoskrnl/include/internal/trap_x.h
reactos/ntoskrnl/ke/i386/trap.s
reactos/ntoskrnl/ke/i386/traphdlr.c

index 426ad85..824dec5 100644 (file)
@@ -138,6 +138,7 @@ extern ULONG KeTimeAdjustment;
 extern ULONG_PTR KiBugCheckData[5];
 extern ULONG KiFreezeFlag;
 extern ULONG KiDPCTimeout;
+extern PGDI_BATCHFLUSH_ROUTINE KeGdiFlushUserBatch;
 
 /* MACROS *************************************************************************/
 
index fbd430c..fab0a18 100644 (file)
@@ -436,3 +436,42 @@ KiSystemCallTrampoline(IN PVOID Handler,
     
     return Result;
 }
+
+NTSTATUS
+FORCEINLINE
+KiConvertToGuiThread(VOID)
+{
+    NTSTATUS Result;  
+    PVOID StackFrame;
+
+    /*
+     * Converting to a GUI thread safely updates ESP in-place as well as the
+     * current Thread->TrapFrame and EBP when KeSwitchKernelStack is called.
+     *
+     * However, PsConvertToGuiThread "helpfully" restores EBP to the original
+     * caller's value, since it is considered a nonvolatile register. As such,
+     * as soon as we're back after the conversion and we try to store the result
+     * which will probably be in some stack variable (EBP-based), we'll crash as
+     * we are touching the de-allocated non-expanded stack.
+     *
+     * Thus we need a way to update our EBP before EBP is touched, and the only
+     * way to guarantee this is to do the call itself in assembly, use the EAX
+     * register to store the result, fixup EBP, and then let the C code continue
+     * on its merry way.
+     *
+     */
+    __asm__ __volatile__
+    (
+        "movl %%ebp, %1\n"
+        "subl %%esp, %1\n"
+        "call _PsConvertToGuiThread@0\n"
+        "addl %%esp, %1\n"
+        "movl %1, %%ebp\n"
+        "movl %%eax, %0\n"
+        : "=r"(Result), "=r"(StackFrame)
+        :
+        : "%esp", "%ecx", "%edx"
+    );
+        
+    return Result;
+}
index 4e17362..cae1edf 100644 (file)
@@ -129,261 +129,31 @@ _KiSystemService:
 .endfunc
 
 .func KiFastCallEntry
-TRAP_FIXUPS FastCallDrSave, FastCallDrReturn, DoNotFixupV86, DoNotFixupAbios
 _KiFastCallEntry:
 
-    /* Enter the fast system call prolog */
-    FASTCALL_PROLOG FastCallDrSave, FastCallDrReturn
-
-SharedCode:
-
-    /*
-     * Find out which table offset to use. Converts 0x1124 into 0x10.
-     * The offset is related to the Table Index as such: Offset = TableIndex x 10
-     */
-    mov edi, eax
-    shr edi, SERVICE_TABLE_SHIFT
-    and edi, SERVICE_TABLE_MASK
-    mov ecx, edi
-
-    /* Now add the thread's base system table to the offset */
-    add edi, [esi+KTHREAD_SERVICE_TABLE]
-
-    /* Get the true syscall ID and check it */
-    mov ebx, eax
-    and eax, SERVICE_NUMBER_MASK
-    cmp eax, [edi+SERVICE_DESCRIPTOR_LIMIT]
-
-    /* Invalid ID, try to load Win32K Table */
-    jnb KiBBTUnexpectedRange
-
-    /* Check if this was Win32K */
-    cmp ecx, SERVICE_TABLE_TEST
-    jnz NotWin32K
-
-    /* Get the TEB */
-    mov ecx, PCR[KPCR_TEB]
-
-    /* Check if we should flush the User Batch */
-    xor ebx, ebx
-_ReadBatch:
-    or ebx, [ecx+TEB_GDI_BATCH_COUNT]
-    jz NotWin32K
-
-    /* Flush it */
-    push edx
-    push eax
-    call [_KeGdiFlushUserBatch]
-    pop eax
-    pop edx
-
-NotWin32K:
-    /* Increase total syscall count */
-    inc dword ptr PCR[KPCR_SYSTEM_CALLS]
-
-#if DBG
-    /* Increase per-syscall count */
-    mov ecx, [edi+SERVICE_DESCRIPTOR_COUNT]
-    jecxz NoCountTable
-    inc dword ptr [ecx+eax*4]
-#endif
-
-    /* Users's current stack frame pointer is source */
-NoCountTable:
-    mov esi, edx
-
-    /* Allocate room for argument list from kernel stack */
-    mov ebx, [edi+SERVICE_DESCRIPTOR_NUMBER]
-    xor ecx, ecx
-    mov cl, [eax+ebx]
-
-    /* Get pointer to function */
-    mov edi, [edi+SERVICE_DESCRIPTOR_BASE]
-    mov ebx, [edi+eax*4]
-
-    /* Allocate space on our stack */
-    sub esp, ecx
-
-    /* Set the size of the arguments and the destination */
-    shr ecx, 2
-    mov edi, esp
-
-    /* Make sure we're within the User Probe Address */
-    cmp esi, _MmUserProbeAddress
-    jnb AccessViolation
-
-_CopyParams:
-    /* Copy the parameters */
-    rep movsd
-
-    /* Do the System Call */
-    call ebx
-
-AfterSysCall:
-#if DBG
-    /* Make sure the user-mode call didn't return at elevated IRQL */
-    test byte ptr [ebp+KTRAP_FRAME_CS], MODE_MASK
-    jz SkipCheck
-    mov esi, eax                /* We need to save the syscall's return val */
-    call _KeGetCurrentIrql@0
-    or al, al
-    jnz InvalidIrql
-    mov eax, esi                /* Restore it */
-
-    /* Get our temporary current thread pointer for sanity check */
-    mov ecx, PCR[KPCR_CURRENT_THREAD]
-
-    /* Make sure that we are not attached and that APCs are not disabled */
-    mov dl, [ecx+KTHREAD_APC_STATE_INDEX]
-    or dl, dl
-    jnz InvalidIndex
-    mov edx, [ecx+KTHREAD_COMBINED_APC_DISABLE]
-    or edx, edx
-    jnz InvalidIndex
-#endif
-
-SkipCheck:
-
-    /* Deallocate the kernel stack frame  */
-    mov esp, ebp
-
-KeReturnFromSystemCall:
-
-    /* Get the Current Thread */
-    mov ecx, PCR[KPCR_CURRENT_THREAD]
-
-    /* Restore the old trap frame pointer */
-    mov edx, [ebp+KTRAP_FRAME_EDX]
-    mov [ecx+KTHREAD_TRAP_FRAME], edx
+    /* Sane FS segment */
+    mov ecx, KGDT_R0_PCR
+    mov fs, cx
+    
+    /* Sane stack and frame */
+    mov esp, PCR[KPCR_TSS]
+    mov esp, [esp+KTSS_ESP0]
+    
+    /* Make space for trap frame on the stack */
+    sub esp, KTRAP_FRAME_V86_ES
+    
+    /* Save EBP, EBX, ESI, EDI only! */
+    mov [esp+KTRAP_FRAME_EBX], ebx
+    mov [esp+KTRAP_FRAME_ESI], esi
+    mov [esp+KTRAP_FRAME_EDI], edi
+    mov [esp+KTRAP_FRAME_EBP], ebp
     
-    /* Exit the system call */
-    mov ecx, ebp
-    mov edx, eax
-    jmp @KiServiceExit@8
+    /* Call C handler -- note that EDX is the user stack, and EAX the syscall */
+    mov ecx, esp
+    add edx, 8
+    jmp _KiFastCallEntryHandler
 .endfunc
 
-KiBBTUnexpectedRange:
-
-    /* If this isn't a Win32K call, fail */
-    cmp ecx, SERVICE_TABLE_TEST
-    jne InvalidCall
-
-    /* Set up Win32K Table */
-    push edx
-    push ebx
-    call _PsConvertToGuiThread@0
-
-    /* Check return code */
-    or eax, eax
-
-    /* Restore registers */
-    pop eax
-    pop edx
-
-    /* Reset trap frame address */
-    mov ebp, esp
-    mov [esi+KTHREAD_TRAP_FRAME], ebp
-
-    /* Try the Call again, if we suceeded */
-    jz SharedCode
-
-    /*
-     * The Shadow Table should have a special byte table which tells us
-     * whether we should return FALSE, -1 or STATUS_INVALID_SYSTEM_SERVICE.
-     */
-
-    /* Get the table limit and base */
-    lea edx, _KeServiceDescriptorTableShadow + SERVICE_TABLE_TEST
-    mov ecx, [edx+SERVICE_DESCRIPTOR_LIMIT]
-    mov edx, [edx+SERVICE_DESCRIPTOR_BASE]
-
-    /* Get the table address and add our index into the array */
-    lea edx, [edx+ecx*4]
-    and eax, SERVICE_NUMBER_MASK
-    add edx, eax
-
-    /* Find out what we should return */
-    movsx eax, byte ptr [edx]
-    or eax, eax
-
-    /* Return either 0 or -1, we've set it in EAX */
-    jle KeReturnFromSystemCall
-
-    /* Set STATUS_INVALID_SYSTEM_SERVICE */
-    mov eax, STATUS_INVALID_SYSTEM_SERVICE
-    jmp KeReturnFromSystemCall
-
-InvalidCall:
-
-    /* Invalid System Call */
-    mov eax, STATUS_INVALID_SYSTEM_SERVICE
-    jmp KeReturnFromSystemCall
-
-AccessViolation:
-
-    /* Check if this came from kernel-mode */
-    test byte ptr [ebp+KTRAP_FRAME_CS], MODE_MASK
-
-    /* It's fine, go ahead with it */
-    jz _CopyParams
-
-    /* Caller sent invalid parameters, fail here */
-    mov eax, STATUS_ACCESS_VIOLATION
-    jmp AfterSysCall
-
-BadStack:
-
-    /* Restore ESP0 stack */
-    mov ecx, PCR[KPCR_TSS]
-    mov esp, ss:[ecx+KTSS_ESP0]
-
-    /* Generate V86M Stack for Trap 6 */
-    push 0
-    push 0
-    push 0
-    push 0
-
-    /* Generate interrupt stack for Trap 6 */
-    push KGDT_R3_DATA + RPL_MASK
-    push 0
-    push 0x20202
-    push KGDT_R3_CODE + RPL_MASK
-    push 0
-    jmp _KiTrap06
-
-#if DBG
-InvalidIrql:
-    /* Save current IRQL */
-    push PCR[KPCR_IRQL]
-
-    /* Set us at passive */
-    mov dword ptr PCR[KPCR_IRQL], 0
-    cli
-
-    /* Bugcheck */
-    push 0
-    push 0
-    push eax
-    push ebx
-    push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
-    call _KeBugCheckEx@20
-
-InvalidIndex:
-
-    /* Get the index and APC state */
-    movzx eax, byte ptr [ecx+KTHREAD_APC_STATE_INDEX]
-    mov edx, [ecx+KTHREAD_COMBINED_APC_DISABLE]
-
-    /* Bugcheck */
-    push 0
-    push edx
-    push eax
-    push ebx
-    push APC_INDEX_MISMATCH
-    call _KeBugCheckEx@20
-    ret
-#endif
-
 .func Kei386EoiHelper@0
 _Kei386EoiHelper@0:
     /* Call the C EOI Helper */
index eba2284..1bbb520 100644 (file)
@@ -1465,6 +1465,7 @@ KiTrap0EHandler(IN PKTRAP_FRAME TrapFrame)
     }
     
     /* Check for syscall fault */
+#if 0
     if ((TrapFrame->Eip == (ULONG_PTR)CopyParams) ||
         (TrapFrame->Eip == (ULONG_PTR)ReadBatch))
     {
@@ -1472,7 +1473,7 @@ KiTrap0EHandler(IN PKTRAP_FRAME TrapFrame)
         UNIMPLEMENTED;
         while (TRUE);
     }
-
+#endif
     /* Check for VDM trap */
     ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
     
@@ -1725,9 +1726,14 @@ KiSystemCall(IN ULONG SystemCallNumber,
                 goto ExitCall;
             }
 
-            /* GUI calls are not yet supported */
-            UNIMPLEMENTED;
-            while (TRUE);
+            /* Convert us to a GUI thread -- must wrap in ASM to get new EBP */        
+            Result = KiConvertToGuiThread();
+            if (__builtin_expect(!NT_SUCCESS(Result), 0))
+            {
+                /* Figure out how we should fail to the user */
+                UNIMPLEMENTED;
+                while (TRUE);
+            }
             
             /* Try the call again */
             continue;
@@ -1741,8 +1747,7 @@ KiSystemCall(IN ULONG SystemCallNumber,
     if (__builtin_expect(Offset & SERVICE_TABLE_TEST, 0))
     {
         /* Get the batch count and flush if necessary */
-        UNIMPLEMENTED;
-        while (TRUE);
+        if (NtCurrentTeb()->GdiBatchCount) KeGdiFlushUserBatch();
     }
     
     /* Increase system call count */
@@ -1818,6 +1823,39 @@ KiSystemCallHandler(IN PKTRAP_FRAME TrapFrame,
     KiSystemCall(ServiceNumber, Arguments);   
 }
 
+VOID
+__attribute__((regparm(3)))
+KiFastCallEntryHandler(IN ULONG ServiceNumber,
+                       IN PVOID Arguments,
+                       IN PKTRAP_FRAME TrapFrame)
+{
+    PKTHREAD Thread;
+        
+    /* Fixup segments */
+    Ke386SetDs(KGDT_R3_DATA | RPL_MASK);
+    Ke386SetEs(KGDT_R3_DATA | RPL_MASK);
+    
+    /* Set up a fake INT Stack and enable interrupts */
+    TrapFrame->HardwareSegSs = KGDT_R3_DATA | RPL_MASK;
+    TrapFrame->HardwareEsp = (ULONG_PTR)Arguments - 8; // Stack is 2 frames down
+    TrapFrame->EFlags = __readeflags() | EFLAGS_INTERRUPT_MASK;
+    TrapFrame->SegCs = KGDT_R3_CODE | RPL_MASK;
+    TrapFrame->Eip = SharedUserData->SystemCallReturn;
+    __writeeflags(0x2);
+    
+    /* Get the current thread */
+    Thread = KeGetCurrentThread();
+
+    /* Call the shared handler (inline) */
+    KiSystemCallHandler(TrapFrame,
+                        ServiceNumber,
+                        Arguments,
+                        Thread,
+                        UserMode,
+                        Thread->PreviousMode,
+                        KGDT_R3_TEB | RPL_MASK);
+}
+
 VOID
 __attribute__((regparm(3)))
 KiSystemServiceHandler(IN ULONG ServiceNumber,