reactos/reactos/ntoskrnl/ke/i386/syscall.S
2005-01-21 23:35:19 +00:00

319 lines
9.8 KiB
ArmAsm

/* $Id$
*
* FILE: ntoskrnl/ke/i386/syscall.S
* COPYRIGHT: See COPYING in the top level directory
* PURPOSE: System Call Handler
* PROGRAMMER: Alex Ionescu (alex@relsoft.net)
* UPDATE HISTORY:
* ??-??-??: Original Version - David Welch(?)
* 13-01-05: Complete rewrite, added support for SYSENTER, direct kmode syscalls
* and re-wrote most of handler code. - Alex Ionescu
*/
#include <roscfg.h>
#include <ddk/status.h>
#include <internal/i386/segment.h>
#include <internal/ps.h>
#include <internal/i386/ke.h>
#include <ntos/tss.h>
#include <napi/shared_data.h>
#define UserMode (1)
.globl KeReturnFromSystemCallWithHook
.globl _KiServiceExit
.globl _KiFastCallEntry
.globl _KiSystemService
_KiFastCallEntry:
/* Set FS to PCR */
movl $PCR_SELECTOR, %ecx
movw %cx, %fs
/* Set the current stack to Kernel Stack */
movl %fs:KPCR_TSS, %ecx
movl %ss:KTSS_ESP0(%ecx), %ecx
movl %ecx, %esp
/* Set up a fake INT Stack. */
pushl $USER_DS
pushl %edx /* Ring 3 SS:ESP */
pushfl
orl $X86_EFLAGS_IF, (%esp) /* Re-enable IRQs in EFLAGS, to fake INT */
pushl $USER_CS
pushl $KUSER_SHARED_SYSCALL_RET
/* User Parameter List */
add $8, %edx
_KiSystemService:
/*
* Construct a trap frame on the stack.
* The following are already on the stack.
*/
// SS + 0x0
// ESP + 0x4
// EFLAGS + 0x8
// CS + 0xC
// EIP + 0x10
pushl $0 // + 0x14
pushl %ebp // + 0x18
pushl %ebx // + 0x1C
pushl %esi // + 0x20
pushl %edi // + 0x24
pushl %fs // + 0x28
/* Load PCR Selector into fs */
movw $PCR_SELECTOR, %bx
movw %bx, %fs
/* Save the previous exception list */
pushl %fs:KPCR_EXCEPTION_LIST // + 0x2C
/* Set the exception handler chain terminator */
movl $0xffffffff, %fs:KPCR_EXCEPTION_LIST
/* Get a pointer to the current thread */
movl %fs:KPCR_CURRENT_THREAD, %esi
/* Save the old previous mode */
pushl %ss:KTHREAD_PREVIOUS_MODE(%esi) // + 0x30
/* Set the new previous mode based on the saved CS selector */
movl 0x24(%esp), %ebx
andl $1, %ebx
movb %bl, %ss:KTHREAD_PREVIOUS_MODE(%esi)
/* Save other registers */
pushl %eax // + 0x34
pushl %ecx // + 0x38
pushl %edx // + 0x3C
pushl %ds // + 0x40
pushl %es // + 0x44
pushl %gs // + 0x48
sub $0x28, %esp // + 0x70
#ifdef DBG
/* Trick gdb 6 into backtracing over the system call */
pushl 4(%ebp) /* DebugEIP */ // + 0x74
pushl (%ebp) /* DebugEBP */ // + 0x78
#else
pushl 0x60(%esp) /* DebugEIP */ // + 0x74
pushl %ebp /* DebugEBP */ // + 0x78
#endif
/* Load the segment registers */
sti
movw $KERNEL_DS, %bx
movw %bx, %ds
movw %bx, %es
/* Save the old trap frame pointer where EDX would be saved */
movl KTHREAD_TRAP_FRAME(%esi), %ebx
movl %ebx, KTRAP_FRAME_EDX(%esp)
/* Allocate new Kernel stack frame */
movl %esp,%ebp
/* Save a pointer to the trap frame in the TCB */
movl %ebp, KTHREAD_TRAP_FRAME(%esi)
CheckValidCall:
/*
* 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
*/
movl %eax, %edi
shrl $8, %edi
andl $0x10, %edi
movl %edi, %ecx
/* Now add the thread's base system table to the offset */
addl KTHREAD_SERVICE_TABLE(%esi), %edi
/* Get the true syscall ID and check it */
movl %eax, %ebx
andl $0x0FFF, %eax
cmpl 8(%edi), %eax
/* Invalid ID, try to load Win32K Table */
jnb KiBBTUnexpectedRange
#ifdef DBG
/*
* GDB thinks the function starts here and
* wants a standard prolog, so let's give it
*/
pushl %ebp
movl %esp,%ebp
popl %ebp
#endif
/* Users's current stack frame pointer is source */
movl %edx, %esi
/* Allocate room for argument list from kernel stack */
movl 12(%edi), %ecx
movb (%ecx, %eax), %cl
movzx %cl, %ecx
/* Allocate space on our stack */
subl %ecx, %esp
/* Get pointer to function */
movl (%edi), %edi
movl (%edi, %eax, 4), %eax
/* Copy the arguments from the user stack to our stack */
shr $2, %ecx
movl %esp, %edi
cld
rep movsd
/* Do the System Call */
call *%eax
movl %eax, KTRAP_FRAME_EAX(%ebp)
/* Deallocate the kernel stack frame */
movl %ebp, %esp
KeReturnFromSystemCall:
/* Get the Current Thread */
movl %fs:KPCR_CURRENT_THREAD, %esi
/* Restore the old trap frame pointer */
movl KTRAP_FRAME_EDX(%esp), %ebx
movl %ebx, KTHREAD_TRAP_FRAME(%esi)
_KiServiceExit:
/* Get the Current Thread */
cli
movl %fs:KPCR_CURRENT_THREAD, %esi
/* Deliver APCs only if we were called from user mode */
testb $1, KTRAP_FRAME_CS(%esp)
je KiRosTrapReturn
/* And only if any are actually pending */
cmpb $0, KTHREAD_PENDING_USER_APC(%esi)
je KiRosTrapReturn
/* Save pointer to Trap Frame */
movl %esp, %ebx
/* Raise IRQL to APC_LEVEL */
movl $1, %ecx
call @KfRaiseIrql@4
/* Save old IRQL */
pushl %eax
/* Deliver APCs */
sti
pushl %ebx
pushl $0
pushl $UserMode
call _KiDeliverApc@12
cli
/* Return to old IRQL */
popl %ecx
call @KfLowerIrql@4
KiRosTrapReturn:
/* Skip debug information and unsaved registers */
addl $0x30, %esp // + 0x48
popl %gs // + 0x44
popl %es // + 0x40
popl %ds // + 0x3C
popl %edx // + 0x38
popl %ecx // + 0x34
popl %eax // + 0x30
/* Restore the old previous mode */
popl %ebx // + 0x2C
movb %bl, %ss:KTHREAD_PREVIOUS_MODE(%esi)
/* Restore the old exception handler list */
popl %fs:KPCR_EXCEPTION_LIST // + 0x28
/* Restore final registers from trap frame */
popl %fs // + 0x24
popl %edi // + 0x20
popl %esi // + 0x1C
popl %ebx // + 0x18
popl %ebp // + 0x14
add $4, %esp // + 0x10
/* Check if previous CS is from user-mode */
testl $1, 4(%esp)
/* It is, so use Fast Exit */
jnz FastRet
/*
* Restore what the stub pushed, and return back to it.
* Note that we were CALLed, so the first thing on our stack is the ret EIP!
*/
pop %edx // + 0x0C
pop %ecx // + 0x08
popf // + 0x04
jmp *%edx
IntRet:
iret
FastRet:
/* Is SYSEXIT Supported/Wanted? */
cmpl $0, %ss:_KiFastSystemCallDisable
jnz IntRet
/* Restore FS to TIB */
mov $TEB_SELECTOR, %ecx
mov %ecx, %fs
/* We will be cleaning up the stack ourselves */
popl %edx /* New Ring 3 EIP */
add $0x4, %esp /* Skip Ring 3 DS */
andl $~X86_EFLAGS_IF, (%esp) /* Remove IRQ hack from EFLAGS */
popfl /* Restore old EFLAGS */
popl %ecx /* Old Ring 3 SS:ESP */
/*
* At this point:
* ECX points to the old User Stack.
* EDX points to the instruction to execute in usermode after the sysenter
*/
sti
sysexit
KiBBTUnexpectedRange:
/* If this isn't a Win32K call, fail */
cmp $0x10, %ecx
jne InvalidCall
/* Set up Win32K Table */
pushl %edx
pushl %ebx
call _KiServiceCheck
popl %eax
popl %edx
/* Try the Call again */
jmp CheckValidCall
InvalidCall:
/* Invalid System Call */
movl $STATUS_INVALID_SYSTEM_SERVICE, %eax
movl %eax, KTRAP_FRAME_EAX(%ebp)
jmp _KiServiceExit