Author: sir_richard
Date: Tue Jan 19 10:45:30 2010
New Revision: 45148
URL:
http://svn.reactos.org/svn/reactos?rev=45148&view=rev
Log:
[NTOS]: Implement GUI thread promotion during the first GUI system call in C. This is
tricky due to EBP, and actually requires some tiny inline ASM magic to make it work
right.
[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.
Modified:
trunk/reactos/ntoskrnl/include/internal/ke.h
trunk/reactos/ntoskrnl/include/internal/trap_x.h
trunk/reactos/ntoskrnl/ke/i386/trap.s
trunk/reactos/ntoskrnl/ke/i386/traphdlr.c
Modified: trunk/reactos/ntoskrnl/include/internal/ke.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/…
==============================================================================
--- trunk/reactos/ntoskrnl/include/internal/ke.h [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/include/internal/ke.h [iso-8859-1] Tue Jan 19 10:45:30 2010
@@ -138,6 +138,7 @@
extern ULONG_PTR KiBugCheckData[5];
extern ULONG KiFreezeFlag;
extern ULONG KiDPCTimeout;
+extern PGDI_BATCHFLUSH_ROUTINE KeGdiFlushUserBatch;
/* MACROS *************************************************************************/
Modified: trunk/reactos/ntoskrnl/include/internal/trap_x.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/…
==============================================================================
--- trunk/reactos/ntoskrnl/include/internal/trap_x.h [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/include/internal/trap_x.h [iso-8859-1] Tue Jan 19 10:45:30
2010
@@ -436,3 +436,42 @@
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;
+}
Modified: trunk/reactos/ntoskrnl/ke/i386/trap.s
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/trap.s?re…
==============================================================================
--- trunk/reactos/ntoskrnl/ke/i386/trap.s [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/ke/i386/trap.s [iso-8859-1] Tue Jan 19 10:45:30 2010
@@ -129,260 +129,30 @@
.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
- /* Exit the system call */
- mov ecx, ebp
- mov edx, eax
- jmp @KiServiceExit@8
-.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
+ /* 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
+
+ /* Call C handler -- note that EDX is the user stack, and EAX the syscall */
+ mov ecx, esp
+ add edx, 8
+ jmp _KiFastCallEntryHandler
+.endfunc
.func Kei386EoiHelper@0
_Kei386EoiHelper@0:
Modified: trunk/reactos/ntoskrnl/ke/i386/traphdlr.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/traphdlr.…
==============================================================================
--- trunk/reactos/ntoskrnl/ke/i386/traphdlr.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/ke/i386/traphdlr.c [iso-8859-1] Tue Jan 19 10:45:30 2010
@@ -1465,6 +1465,7 @@
}
/* Check for syscall fault */
+#if 0
if ((TrapFrame->Eip == (ULONG_PTR)CopyParams) ||
(TrapFrame->Eip == (ULONG_PTR)ReadBatch))
{
@@ -1472,7 +1473,7 @@
UNIMPLEMENTED;
while (TRUE);
}
-
+#endif
/* Check for VDM trap */
ASSERT((KiVdmTrap(TrapFrame)) == FALSE);
@@ -1725,9 +1726,14 @@
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 @@
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 */
@@ -1816,6 +1821,39 @@
/* Enable interrupts and make the call */
_enable();
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