mirror of
https://github.com/reactos/reactos.git
synced 2024-11-07 23:22:36 +00:00
c16ad873a6
svn path=/branches/reactos-yarotows/; revision=46279
518 lines
12 KiB
ArmAsm
518 lines
12 KiB
ArmAsm
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Kernel
|
|
* FILE: ntoskrnl/ke/i386/usercall_asm.S
|
|
* PURPOSE: User-Mode callbacks and return.
|
|
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include <reactos/asm.h>
|
|
#include <ndk/i386/asm.h>
|
|
#include <internal/i386/asmmacro.S>
|
|
|
|
/* FUNCTIONS ****************************************************************/
|
|
|
|
.code32
|
|
.text
|
|
|
|
.globl _KiGetUserModeStackAddress@0
|
|
.func KiGetUserModeStackAddress@0
|
|
_KiGetUserModeStackAddress@0:
|
|
|
|
/* Get the current thread's trapframe and return the esp */
|
|
mov eax, fs:[KPCR_CURRENT_THREAD]
|
|
mov eax, [eax+KTHREAD_TRAP_FRAME]
|
|
lea eax, [eax+KTRAP_FRAME_ESP]
|
|
ret
|
|
|
|
.endfunc
|
|
|
|
/*++
|
|
* @name KiCallUserMode
|
|
*
|
|
* The KiSwitchToUserMode routine sets up a Trap Frame and a Callback stack
|
|
* for the purpose of switching to user mode. The actual final jump is done
|
|
* by KiServiceExit which will treat this as a syscall return.
|
|
*
|
|
* @param OutputBuffer
|
|
* Pointer to a caller-allocated buffer where to receive the return data
|
|
* from the user-mode function
|
|
*
|
|
* @param OutputLength
|
|
* Size of the Output Buffer described above.
|
|
*
|
|
* @return None. Jumps into KiServiceExit.
|
|
*
|
|
* @remark If there is not enough Kernel Stack space, the routine will increase the
|
|
* Kernel Stack.
|
|
*
|
|
* User mode execution resumes at ntdll!KiUserCallbackDispatcher.
|
|
*
|
|
* This call MUST be paired by interrupt 0x2B or NtCallbackReturn.
|
|
*
|
|
*--*/
|
|
.globl _KiCallUserMode@8
|
|
.func KiCallUserMode@8
|
|
_KiCallUserMode@8:
|
|
|
|
/* Save volatile registers */
|
|
push ebp
|
|
push ebx
|
|
push esi
|
|
push edi
|
|
|
|
/* Get the current thread */
|
|
mov ebx, fs:[KPCR_CURRENT_THREAD]
|
|
|
|
/* Make sure we're at passive */
|
|
#if DBG
|
|
call _KeGetCurrentIrql@0
|
|
or al, al
|
|
jz AtPassive
|
|
|
|
/* We're not, bugcheck! */
|
|
push 0
|
|
push 0
|
|
push eax
|
|
push 0
|
|
push IRQL_GT_ZERO_AT_SYSTEM_SERVICE
|
|
call _KeBugCheckEx@20
|
|
|
|
AtPassive:
|
|
|
|
/* Make sure that we are not attached and that APCs are not disabled */
|
|
movzx eax, byte ptr [ebx+KTHREAD_APC_STATE_INDEX]
|
|
mov edx, [ebx+KTHREAD_COMBINED_APC_DISABLE]
|
|
or eax, eax
|
|
jnz InvalidIndex
|
|
or edx, edx
|
|
jz ApcsEnabled
|
|
|
|
InvalidIndex:
|
|
|
|
push 0
|
|
push edx
|
|
push eax
|
|
push 0
|
|
push APC_INDEX_MISMATCH
|
|
call _KeBugCheckEx@20
|
|
ApcsEnabled:
|
|
#endif
|
|
|
|
/* Get the lowest stack limit and check if we can handle it */
|
|
lea eax, [esp-0x3000]
|
|
cmp eax, [ebx+KTHREAD_STACK_LIMIT]
|
|
jnb StackOk
|
|
|
|
/* We can't, we'll have to grow our stack */
|
|
push esp
|
|
call _MmGrowKernelStack@4
|
|
|
|
/* Quit if we failed */
|
|
or eax, eax
|
|
jnz GrowFailed
|
|
|
|
/* Save the current callback stack */
|
|
StackOk:
|
|
push [ebx+KTHREAD_CALLBACK_STACK]
|
|
|
|
/* Get and save the trap frame */
|
|
mov edx, [ebx+KTHREAD_TRAP_FRAME]
|
|
push edx
|
|
|
|
/* Get and save the initial stack */
|
|
mov esi, [ebx+KTHREAD_INITIAL_STACK]
|
|
push esi
|
|
|
|
/* Set the new callback stack */
|
|
mov [ebx+KTHREAD_CALLBACK_STACK], esp
|
|
|
|
/* Align stack on 16-byte boundary */
|
|
and esp, ~15
|
|
mov edi, esp
|
|
|
|
/* Set destination and origin NPX Areas */
|
|
sub esp, NPX_FRAME_LENGTH
|
|
sub esi, NPX_FRAME_LENGTH
|
|
|
|
/* Disable interrupts so we can fill the NPX State */
|
|
cli
|
|
|
|
/* Now copy the NPX State */
|
|
mov ecx, [esi+FP_CONTROL_WORD]
|
|
mov [esp+FP_CONTROL_WORD], ecx
|
|
mov ecx, [esi+FP_STATUS_WORD]
|
|
mov [esp+FP_STATUS_WORD], ecx
|
|
mov ecx, [esi+FP_TAG_WORD]
|
|
mov [esp+FP_TAG_WORD], ecx
|
|
mov ecx, [esi+FP_DATA_SELECTOR]
|
|
mov [esp+FP_DATA_SELECTOR], ecx
|
|
mov ecx, [esi+FN_CR0_NPX_STATE]
|
|
mov [esp+FN_CR0_NPX_STATE], ecx
|
|
|
|
/* Get TSS */
|
|
mov esi, fs:[KPCR_TSS]
|
|
|
|
/* Set the stack address */
|
|
mov [ebx+KTHREAD_INITIAL_STACK], edi
|
|
|
|
/* Bias the stack for V86 mode */
|
|
mov ecx, esp
|
|
sub esp, 16
|
|
test dword ptr [edx+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
|
|
jnz DontBias
|
|
mov ecx, esp
|
|
|
|
DontBias:
|
|
/* Set new stack address in TSS */
|
|
mov [esi+KTSS_ESP0], ecx
|
|
|
|
/* Allocate the trap frame and set it */
|
|
sub esp, KTRAP_FRAME_V86_ES
|
|
mov ebp, esp
|
|
|
|
/* Set copy iterator and dest/origin parameters and do the copy */
|
|
mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
|
|
lea edi, [esp+KTRAP_FRAME_FS]
|
|
lea esi, [edx+KTRAP_FRAME_FS]
|
|
rep movsd
|
|
|
|
/* Copy DR7 */
|
|
mov edi, [edx+KTRAP_FRAME_DR7]
|
|
test edi, ~DR7_RESERVED_MASK
|
|
mov [esp+KTRAP_FRAME_DR7], edi
|
|
|
|
/* Check if we need to save debug registers */
|
|
jnz SaveDebug
|
|
|
|
/* Get user-mode dispatcher address and set it as EIP */
|
|
SetEip:
|
|
mov eax, _KeUserCallbackDispatcher
|
|
mov [esp+KTRAP_FRAME_EIP], eax
|
|
|
|
/* Set the exception list */
|
|
mov eax, fs:[KPCR_EXCEPTION_LIST]
|
|
mov [esp+KTRAP_FRAME_EXCEPTION_LIST], eax
|
|
|
|
/* Set the previous mode */
|
|
mov eax, [edx+KTRAP_FRAME_PREVIOUS_MODE]
|
|
mov [esp+KTRAP_FRAME_PREVIOUS_MODE], eax
|
|
|
|
/* Bring interrupts back */
|
|
sti
|
|
|
|
/* Exit to user-mode */
|
|
mov ecx, esp
|
|
jmp @KiServiceExit@8
|
|
|
|
SaveDebug:
|
|
|
|
/* Copy all 5 DRs */
|
|
mov ecx, 5
|
|
lea edi, [esp+KTRAP_FRAME_DR0]
|
|
lea esi, [edx+KTRAP_FRAME_DR0]
|
|
rep movsd
|
|
jmp SetEip
|
|
|
|
GrowFailed:
|
|
/* Restore registers */
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
pop ebp
|
|
|
|
/* Return */
|
|
ret 8
|
|
|
|
.endfunc
|
|
|
|
/*++
|
|
* @name NtCallbackReturn
|
|
*
|
|
* The NtCallbackReturn routine returns to kernel mode after a user-mode
|
|
* callback was done through KeUserModeCallback. It uses the callback frame
|
|
* which was setup in order to return the information, restores the stack,
|
|
* and resumes execution where it was left off.
|
|
*
|
|
* @param Result
|
|
* Pointer to a caller-allocated buffer where the return data
|
|
* from the user-mode function is located.
|
|
*
|
|
* @param ResultLength
|
|
* Size of the Output Buffer described above.
|
|
*
|
|
* @param CallbackStatus
|
|
* Status code of the callback operation.
|
|
*
|
|
* @return Status code of the callback operation.
|
|
*
|
|
* @remark This call MUST be paired with KeUserModeCallback.
|
|
*
|
|
*--*/
|
|
.globl _NtCallbackReturn@12
|
|
.func NtCallbackReturn@12
|
|
_NtCallbackReturn@12:
|
|
|
|
/* Get the current thread and make sure we have a callback stack */
|
|
mov eax, fs:[KPCR_CURRENT_THREAD]
|
|
mov ecx, [eax+KTHREAD_CALLBACK_STACK]
|
|
test ecx, ecx
|
|
jz NoStack
|
|
|
|
/* Get the trap frame */
|
|
mov ebx, [eax+KTHREAD_TRAP_FRAME]
|
|
|
|
/* Restore the exception list */
|
|
mov edx, [ebx+KTRAP_FRAME_EXCEPTION_LIST]
|
|
mov fs:[KPCR_EXCEPTION_LIST], edx
|
|
|
|
/* Get the result, the result length and the status */
|
|
mov edi, [esp+4]
|
|
mov esi, [esp+8]
|
|
mov ebp, [esp+12]
|
|
|
|
/* Store the results in the callback stack */
|
|
mov ebx, [ecx+CBSTACK_RESULT]
|
|
mov [ebx], edi
|
|
mov ebx, [ecx+CBSTACK_RESULT_LENGTH]
|
|
mov [ebx], esi
|
|
|
|
/* Get the previous stack */
|
|
mov ebx, [ecx]
|
|
|
|
/* Disable interrupts for NPX save and stack switch */
|
|
cli
|
|
|
|
/* Get the initial stack and restore it */
|
|
mov esi, [eax+KTHREAD_INITIAL_STACK]
|
|
mov [eax+KTHREAD_INITIAL_STACK], ebx
|
|
|
|
/* Set desination and origin NPX Frames */
|
|
sub esi, NPX_FRAME_LENGTH
|
|
sub ebx, NPX_FRAME_LENGTH
|
|
|
|
/* Copy NPX Data */
|
|
mov edx, [esi+FP_CONTROL_WORD]
|
|
mov [ebx+FP_CONTROL_WORD], edx
|
|
mov edx, [esi+FP_STATUS_WORD]
|
|
mov [ebx+FP_STATUS_WORD], edx
|
|
mov edx, [esi+FP_TAG_WORD]
|
|
mov [ebx+FP_TAG_WORD], edx
|
|
mov edx, [esi+FP_DATA_SELECTOR]
|
|
mov [ebx+FP_DATA_SELECTOR], edx
|
|
mov edx, [esi+FN_CR0_NPX_STATE]
|
|
mov [ebx+FN_CR0_NPX_STATE], edx
|
|
|
|
/* Check if we failed in user mode */
|
|
cmp ebp, STATUS_CALLBACK_POP_STACK
|
|
mov edi, [ecx+CBSTACK_TRAP_FRAME]
|
|
jz UserFault
|
|
|
|
CheckDebug:
|
|
|
|
/* Clear DR7 */
|
|
and dword ptr [edi+KTRAP_FRAME_DR7], 0
|
|
|
|
/* Check if debugging was active */
|
|
test byte ptr [eax+KTHREAD_DEBUG_ACTIVE], 0xFF
|
|
jnz RestoreDebug
|
|
|
|
RestoreStack:
|
|
|
|
/* Get TSS */
|
|
mov edx, fs:[KPCR_TSS]
|
|
|
|
/* Restore stack pointer */
|
|
lea esp, [ecx+CBSTACK_CALLBACK_STACK]
|
|
|
|
/* Check if we were in V86 mode */
|
|
test dword ptr [edi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
|
|
jnz V86Ret
|
|
sub ebx, 16
|
|
|
|
V86Ret:
|
|
/* Restore the ESP in TSS */
|
|
mov [edx+KTSS_ESP0], ebx
|
|
|
|
/* Restore the trap frame */
|
|
mov [eax+KTHREAD_TRAP_FRAME], edi
|
|
|
|
/* Bring interrupts back */
|
|
sti
|
|
|
|
/* Restore the callback stack*/
|
|
pop [eax+KTHREAD_CALLBACK_STACK]
|
|
|
|
/* Set status and return */
|
|
mov eax, ebp
|
|
pop edi
|
|
pop esi
|
|
pop ebx
|
|
pop ebp
|
|
pop edx
|
|
|
|
/* Clean stack and jump back */
|
|
add esp, 8
|
|
jmp edx
|
|
|
|
UserFault:
|
|
/* Set size to copy */
|
|
mov ecx, (KTRAP_FRAME_V86_ES - KTRAP_FRAME_FS) / 4
|
|
|
|
/* Check if this was V86 mode */
|
|
mov esi, [eax+KTHREAD_TRAP_FRAME]
|
|
test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
|
|
|
|
/* Save EDI and load destination */
|
|
mov edx, edi
|
|
lea edi, [edi+KTRAP_FRAME_FS]
|
|
jz NotV86
|
|
add ecx, 16 / 4
|
|
|
|
NotV86:
|
|
/* Set source and copy */
|
|
lea esi, [esi+KTRAP_FRAME_FS]
|
|
rep movsd
|
|
|
|
/* Restore ECX and ECX */
|
|
mov ecx, [eax+KTHREAD_CALLBACK_STACK]
|
|
mov edi, edx
|
|
jmp CheckDebug
|
|
|
|
RestoreDebug:
|
|
/* Get a pointer to thread's trap frame */
|
|
mov esi, [eax+KTHREAD_TRAP_FRAME]
|
|
|
|
/* Copy debug registers data from it */
|
|
mov edx, [esi+KTRAP_FRAME_DR0]
|
|
mov [edi+KTRAP_FRAME_DR0], edx
|
|
mov edx, [esi+KTRAP_FRAME_DR1]
|
|
mov [edi+KTRAP_FRAME_DR1], edx
|
|
mov edx, [esi+KTRAP_FRAME_DR2]
|
|
mov [edi+KTRAP_FRAME_DR2], edx
|
|
mov edx, [esi+KTRAP_FRAME_DR3]
|
|
mov [edi+KTRAP_FRAME_DR3], edx
|
|
mov edx, [esi+KTRAP_FRAME_DR6]
|
|
mov [edi+KTRAP_FRAME_DR6], edx
|
|
mov edx, [esi+KTRAP_FRAME_DR7]
|
|
mov [edi+KTRAP_FRAME_DR7], edx
|
|
|
|
/* Jump back */
|
|
jmp RestoreStack
|
|
|
|
NoStack:
|
|
|
|
/* Return failure */
|
|
mov eax, STATUS_NO_CALLBACK_ACTIVE
|
|
ret 12
|
|
.endfunc
|
|
|
|
/*++
|
|
* @name KeSwitchKernelStack
|
|
*
|
|
* The KeSwitchKernelStack routine switches from the current thread's stack
|
|
* to the new specified base and limit.
|
|
*
|
|
* @param StackBase
|
|
* Pointer to the new Stack Base of the thread.
|
|
*
|
|
* @param StackLimit
|
|
* Pointer to the new Stack Limit of the thread.
|
|
*
|
|
* @return The previous Stack Base of the thread.
|
|
*
|
|
* @remark This routine should typically only be used when converting from a
|
|
* non-GUI to a GUI Thread. The caller is responsible for freeing the
|
|
* previous stack. The new stack values MUST be valid before calling
|
|
* this routine.
|
|
*
|
|
*--*/
|
|
.globl _KeSwitchKernelStack@8
|
|
.func KeSwitchKernelStack@8
|
|
_KeSwitchKernelStack@8:
|
|
|
|
/* Save volatiles */
|
|
push esi
|
|
push edi
|
|
|
|
/* Get current thread */
|
|
mov edx, fs:[KPCR_CURRENT_THREAD]
|
|
|
|
/* Get new and current base */
|
|
mov edi, [esp+12]
|
|
mov ecx, [edx+KTHREAD_STACK_BASE]
|
|
|
|
/* Fixup the frame pointer */
|
|
sub ebp, ecx
|
|
add ebp, edi
|
|
|
|
/* Fixup the trap frame */
|
|
mov eax, [edx+KTHREAD_TRAP_FRAME]
|
|
sub eax, ecx
|
|
add eax, edi
|
|
mov [edx+KTHREAD_TRAP_FRAME], eax
|
|
|
|
/* Calculate stack size */
|
|
sub ecx, esp
|
|
|
|
/* Get desination and origin */
|
|
sub edi, ecx
|
|
mov esi, esp
|
|
|
|
/* Save stack pointer */
|
|
push edi
|
|
|
|
/* Copy stack */
|
|
rep movsb
|
|
|
|
/* Restore stack pointer */
|
|
pop edi
|
|
|
|
/* Save old stack base and get new limit/base */
|
|
mov eax, [edx+KTHREAD_STACK_BASE]
|
|
mov ecx, [esp+12]
|
|
mov esi, [esp+16]
|
|
|
|
/* Disable interrupts for stack switch */
|
|
cli
|
|
|
|
/* Set new base/limit */
|
|
mov [edx+KTHREAD_STACK_BASE], ecx
|
|
mov [edx+KTHREAD_STACK_LIMIT], esi
|
|
|
|
/* Set LargeStack */
|
|
mov byte ptr [edx+KTHREAD_LARGE_STACK], 1
|
|
|
|
/* Set new initial stack */
|
|
mov [edx+KTHREAD_INITIAL_STACK], ecx
|
|
|
|
/* Get trap frame */
|
|
mov esi, [edx+KTHREAD_TRAP_FRAME]
|
|
|
|
/* Get TSS */
|
|
mov edx, fs:[KPCR_TSS]
|
|
|
|
/* Check if we came from V86 mode */
|
|
test dword ptr [esi+KTRAP_FRAME_EFLAGS], EFLAGS_V86_MASK
|
|
|
|
/* Bias for NPX Area */
|
|
lea ecx, [ecx-NPX_FRAME_LENGTH]
|
|
jnz V86Switch
|
|
sub ecx, 16
|
|
|
|
V86Switch:
|
|
|
|
/* Update ESP in TSS */
|
|
mov [edx+KTSS_ESP0], ecx
|
|
|
|
/* Update stack pointer */
|
|
mov esp, edi
|
|
|
|
/* Bring back interrupts and return */
|
|
sti
|
|
pop edi
|
|
pop esi
|
|
ret 8
|
|
.endfunc
|