diff --git a/reactos/ntoskrnl/include/internal/ke.h b/reactos/ntoskrnl/include/internal/ke.h index 426ad855c92..824dec58e8d 100644 --- a/reactos/ntoskrnl/include/internal/ke.h +++ b/reactos/ntoskrnl/include/internal/ke.h @@ -138,6 +138,7 @@ extern ULONG KeTimeAdjustment; extern ULONG_PTR KiBugCheckData[5]; extern ULONG KiFreezeFlag; extern ULONG KiDPCTimeout; +extern PGDI_BATCHFLUSH_ROUTINE KeGdiFlushUserBatch; /* MACROS *************************************************************************/ diff --git a/reactos/ntoskrnl/include/internal/trap_x.h b/reactos/ntoskrnl/include/internal/trap_x.h index fbd430cb66c..fab0a1836bc 100644 --- a/reactos/ntoskrnl/include/internal/trap_x.h +++ b/reactos/ntoskrnl/include/internal/trap_x.h @@ -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; +} diff --git a/reactos/ntoskrnl/ke/i386/trap.s b/reactos/ntoskrnl/ke/i386/trap.s index 4e17362f30e..cae1edfc073 100644 --- a/reactos/ntoskrnl/ke/i386/trap.s +++ b/reactos/ntoskrnl/ke/i386/trap.s @@ -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 - /* Exit the system call */ - mov ecx, ebp - mov edx, eax - jmp @KiServiceExit@8 + /* 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 -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 */ diff --git a/reactos/ntoskrnl/ke/i386/traphdlr.c b/reactos/ntoskrnl/ke/i386/traphdlr.c index eba22843bdf..1bbb520b43c 100644 --- a/reactos/ntoskrnl/ke/i386/traphdlr.c +++ b/reactos/ntoskrnl/ke/i386/traphdlr.c @@ -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,