Author: sir_richard Date: Tue Jan 19 10:20:40 2010 New Revision: 45147
URL: http://svn.reactos.org/svn/reactos?rev=45147&view=rev Log: [NTOS]: Convert system call handling to C. Only kernel system calls are done this way for now, not SYSENTER calls from user-mode. A small ASM trampoline is used inline for the call itself.
Modified: 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/trap_x.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/include/internal/t... ============================================================================== --- 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:20:40 2010 @@ -111,15 +111,55 @@ while (TRUE); } } + +FORCEINLINE +VOID +KiExitSystemCallDebugChecks(IN ULONG SystemCall, + IN PKTRAP_FRAME TrapFrame) +{ + KIRQL OldIrql; + + /* Check if this was a user call */ + if (KiUserMode(TrapFrame)) + { + /* Make sure we are not returning with elevated IRQL */ + OldIrql = KeGetCurrentIrql(); + if (OldIrql != PASSIVE_LEVEL) + { + /* Forcibly put us in a sane state */ + KeGetPcr()->CurrentIrql = PASSIVE_LEVEL; + _disable(); + + /* Fail */ + KeBugCheckEx(IRQL_GT_ZERO_AT_SYSTEM_SERVICE, + SystemCall, + OldIrql, + 0, + 0); + } + + /* Make sure we're not attached and that APCs are not disabled */ + if ((KeGetCurrentThread()->ApcStateIndex != CurrentApcEnvironment) || + (KeGetCurrentThread()->CombinedApcDisable != 0)) + { + /* Fail */ + KeBugCheckEx(APC_INDEX_MISMATCH, + SystemCall, + KeGetCurrentThread()->ApcStateIndex, + KeGetCurrentThread()->CombinedApcDisable, + 0); + } + } +} #else #define KiExitTrapDebugChecks(x, y) #define KiFillTrapFrameDebug(x) +#define KiExitSystemCallDebugChecks(x, y) #endif
// // Helper Code // - BOOLEAN FORCEINLINE KiUserTrap(IN PKTRAP_FRAME TrapFrame) @@ -354,3 +394,45 @@ : "%esp" ); } + +NTSTATUS +FORCEINLINE +KiSystemCallTrampoline(IN PVOID Handler, + IN PVOID Arguments, + IN ULONG StackBytes) +{ + NTSTATUS Result; + + /* + * This sequence does a RtlCopyMemory(Stack - StackBytes, Arguments, StackBytes) + * and then calls the function associated with the system call. + * + * It's done in assembly for two reasons: we need to muck with the stack, + * and the call itself restores the stack back for us. The only way to do + * this in C is to do manual C handlers for every possible number of args on + * the stack, and then have the handler issue a call by pointer. This is + * wasteful since it'll basically push the values twice and require another + * level of call indirection. + * + * The ARM kernel currently does this, but it should probably be changed + * later to function like this as well. + * + */ + __asm__ __volatile__ + ( + "subl %1, %%esp\n" + "movl %%esp, %%edi\n" + "movl %2, %%esi\n" + "shrl $2, %1\n" + "rep movsd\n" + "call *%3\n" + "movl %%eax, %0\n" + : "=r"(Result) + : "c"(StackBytes), + "d"(Arguments), + "r"(Handler) + : "%esp", "%esi", "%edi" + ); + + return Result; +}
Modified: trunk/reactos/ntoskrnl/ke/i386/trap.s URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/trap.s?rev... ============================================================================== --- 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:20:40 2010 @@ -66,8 +66,6 @@ .globl _KiSystemService
/* And special system-defined software traps: */ -.globl _NtRaiseException@12 -.globl _NtContinue@8 .globl _KiDispatchInterrupt@0
/* Interrupt template entrypoints */ @@ -84,7 +82,6 @@
/* We implement the following trap exit points: */ .globl _Kei386EoiHelper@0 /* Exit from interrupt or H/W trap */ -.globl _Kei386EoiHelper2ndEntry /* Exit from unexpected interrupt */
.globl _KiIdtDescriptor _KiIdtDescriptor: @@ -115,14 +112,20 @@ .text
.func KiSystemService -TRAP_FIXUPS kss_a, kss_t, DoNotFixupV86, DoNotFixupAbios _KiSystemService:
- /* Enter the shared system call prolog */ - SYSCALL_PROLOG kss_a, kss_t - - /* Jump to the actual handler */ - jmp SharedCode + /* Make space for trap frame on the stack */ + sub esp, KTRAP_FRAME_EIP + + /* 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 caller stack, EAX is the ID */ + mov ecx, esp + jmp _KiSystemServiceHandler .endfunc
.func KiFastCallEntry
Modified: trunk/reactos/ntoskrnl/ke/i386/traphdlr.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ke/i386/traphdlr.c... ============================================================================== --- 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:20:40 2010 @@ -1691,6 +1691,165 @@ KiDebugHandler(TrapFrame, TrapFrame->Eax, TrapFrame->Ecx, TrapFrame->Edx); }
+VOID +FASTCALL +KiSystemCall(IN ULONG SystemCallNumber, + IN PVOID Arguments) +{ + PKTHREAD Thread; + PKTRAP_FRAME TrapFrame; + PKSERVICE_TABLE_DESCRIPTOR DescriptorTable; + ULONG Id, Offset, StackBytes, Result; + PVOID Handler; + + /* Loop because we might need to try this twice in case of a GUI call */ + while (TRUE) + { + /* Decode the system call number */ + Offset = (SystemCallNumber >> SERVICE_TABLE_SHIFT) & SERVICE_TABLE_MASK; + Id = SystemCallNumber & SERVICE_NUMBER_MASK; + + /* Get current thread, trap frame, and descriptor table */ + Thread = KeGetCurrentThread(); + TrapFrame = Thread->TrapFrame; + DescriptorTable = (PVOID)((ULONG_PTR)Thread->ServiceTable + Offset); + + /* Validate the system call number */ + if (__builtin_expect(Id > DescriptorTable->Limit, 0)) + { + /* Check if this is a GUI call */ + if (__builtin_expect(!(Offset & SERVICE_TABLE_TEST), 0)) + { + /* Fail the call */ + Result = STATUS_INVALID_SYSTEM_SERVICE; + goto ExitCall; + } + + /* GUI calls are not yet supported */ + UNIMPLEMENTED; + while (TRUE); + + /* Try the call again */ + continue; + } + + /* If we made it here, the call is good */ + break; + } + + /* Check if this is a GUI call */ + if (__builtin_expect(Offset & SERVICE_TABLE_TEST, 0)) + { + /* Get the batch count and flush if necessary */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Increase system call count */ + KeGetCurrentPrcb()->KeSystemCalls++; + + /* FIXME: Increase individual counts on debug systems */ + //KiIncreaseSystemCallCount(DescriptorTable, Id); + + /* Get stack bytes */ + StackBytes = DescriptorTable->Number[Id]; + + /* Probe caller stack */ + if (__builtin_expect((Arguments < (PVOID)MmUserProbeAddress) && !(KiUserTrap(TrapFrame)), 0)) + { + /* Access violation */ + UNIMPLEMENTED; + while (TRUE); + } + + /* Get the handler and make the system call */ + Handler = (PVOID)DescriptorTable->Base[Id]; + Result = KiSystemCallTrampoline(Handler, Arguments, StackBytes); + + /* Make sure we're exiting correctly */ + KiExitSystemCallDebugChecks(Id, TrapFrame); + + /* Restore the old trap frame */ +ExitCall: + Thread->TrapFrame = (PKTRAP_FRAME)TrapFrame->Edx; + + /* Exit from system call */ + KiServiceExit(TrapFrame, Result); +} + +VOID +FORCEINLINE +KiSystemCallHandler(IN PKTRAP_FRAME TrapFrame, + IN ULONG ServiceNumber, + IN PVOID Arguments, + IN PKTHREAD Thread, + IN KPROCESSOR_MODE PreviousMode, + IN KPROCESSOR_MODE PreviousPreviousMode, + IN USHORT SegFs) +{ + /* No error code */ + TrapFrame->ErrCode = 0; + + /* Save previous mode and FS segment */ + TrapFrame->PreviousPreviousMode = PreviousPreviousMode; + TrapFrame->SegFs = SegFs; + + /* Save the SEH chain and terminate it for now */ + TrapFrame->ExceptionList = KeGetPcr()->Tib.ExceptionList; + KeGetPcr()->Tib.ExceptionList = EXCEPTION_CHAIN_END; + + /* Clear DR7 and check for debugging */ + TrapFrame->Dr7 = 0; + if (__builtin_expect(Thread->DispatcherHeader.DebugActive & 0xFF, 0)) + { + UNIMPLEMENTED; + while (TRUE); + } + + /* Set thread fields */ + Thread->TrapFrame = TrapFrame; + Thread->PreviousMode = PreviousMode; + + /* Set debug header */ + KiFillTrapFrameDebug(TrapFrame); + + /* Enable interrupts and make the call */ + _enable(); + KiSystemCall(ServiceNumber, Arguments); +} + +VOID +__attribute__((regparm(3))) +KiSystemServiceHandler(IN ULONG ServiceNumber, + IN PVOID Arguments, + IN PKTRAP_FRAME TrapFrame) +{ + USHORT SegFs; + PKTHREAD Thread; + + /* Save and fixup FS */ + SegFs = Ke386GetFs(); + Ke386SetFs(KGDT_R0_PCR); + + /* Get the current thread */ + Thread = KeGetCurrentThread(); + + /* Chain trap frames */ + TrapFrame->Edx = (ULONG_PTR)Thread->TrapFrame; + + /* Clear direction flag */ + Ke386ClearDirectionFlag(); + + /* Call the shared handler (inline) */ + KiSystemCallHandler(TrapFrame, + ServiceNumber, + Arguments, + Thread, + KiUserTrap(TrapFrame), + Thread->PreviousMode, + SegFs); +} + /* HARDWARE INTERRUPTS ********************************************************/
/*