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

svn path=/trunk/; revision=45148
This commit is contained in:
Sir Richard 2010-01-19 09:45:30 +00:00
parent d4efc1a06c
commit bb3df24b50
4 changed files with 104 additions and 256 deletions

View 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 *************************************************************************/

View 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;
}

View 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
/* 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 */

View 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,