[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.

svn path=/trunk/; revision=45147
This commit is contained in:
Sir Richard 2010-01-19 09:20:40 +00:00
parent 9ef69503ed
commit d4efc1a06c
3 changed files with 253 additions and 9 deletions

View file

@ -111,15 +111,55 @@ KiExitTrapDebugChecks(IN PKTRAP_FRAME TrapFrame,
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 @@ KiEditedTrapReturn(IN PKTRAP_FRAME TrapFrame)
: "%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;
}

View file

@ -66,8 +66,6 @@ GENERATE_IDT_STUBS /* INT 30-FF: UNEXPECTED INTERRUPTS */
.globl _KiSystemService
/* And special system-defined software traps: */
.globl _NtRaiseException@12
.globl _NtContinue@8
.globl _KiDispatchInterrupt@0
/* Interrupt template entrypoints */
@ -84,7 +82,6 @@ GENERATE_IDT_STUBS /* INT 30-FF: UNEXPECTED INTERRUPTS */
/* 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 @@ _IsrOverflowMsg:
.text
.func KiSystemService
TRAP_FIXUPS kss_a, kss_t, DoNotFixupV86, DoNotFixupAbios
_KiSystemService:
/* Enter the shared system call prolog */
SYSCALL_PROLOG kss_a, kss_t
/* Make space for trap frame on the stack */
sub esp, KTRAP_FRAME_EIP
/* Jump to the actual handler */
jmp SharedCode
/* 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

View file

@ -1691,6 +1691,165 @@ KiDebugServiceHandler(IN PKTRAP_FRAME TrapFrame)
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 ********************************************************/
/*