396 lines
13 KiB
NASM
396 lines
13 KiB
NASM
|
title "Call Out to User Mode"
|
||
|
;++
|
||
|
;
|
||
|
; Copyright (c) 2000 Microsoft Corporation
|
||
|
;
|
||
|
; Module Name:
|
||
|
;
|
||
|
; callout.asm
|
||
|
;
|
||
|
; Abstract:
|
||
|
;
|
||
|
; This module implements the code necessary to call out from kernel
|
||
|
; mode to user mode.
|
||
|
;
|
||
|
; Author:
|
||
|
;
|
||
|
; David N. Cutler (davec) 30-Aug-2000
|
||
|
;
|
||
|
; Environment:
|
||
|
;
|
||
|
; Kernel mode only.
|
||
|
;
|
||
|
;--
|
||
|
|
||
|
include ksamd64.inc
|
||
|
|
||
|
extern KeUserCallbackDispatcher:qword
|
||
|
extern KiSystemServiceExit:proc
|
||
|
extern MmGrowKernelStack:proc
|
||
|
extern PsConvertToGuiThread:proc
|
||
|
|
||
|
subttl "Call User Mode Function"
|
||
|
;++
|
||
|
;
|
||
|
; NTSTATUS
|
||
|
; KiCallUserMode (
|
||
|
; IN PVOID *Outputbuffer,
|
||
|
; IN PULONG OutputLength
|
||
|
; )
|
||
|
;
|
||
|
; Routine Description:
|
||
|
;
|
||
|
; This function calls a user mode function from kernel mode.
|
||
|
;
|
||
|
; N.B. This function calls out to user mode and the NtCallbackReturn
|
||
|
; function returns back to the caller of this function. Therefore,
|
||
|
; the stack layout must be consistent between the two routines.
|
||
|
;
|
||
|
; Arguments:
|
||
|
;
|
||
|
; OutputBuffer (rcx) - Supplies a pointer to the variable that receivies
|
||
|
; the address of the output buffer.
|
||
|
;
|
||
|
; OutputLength (rdx) - Supplies a pointer to a variable that receives
|
||
|
; the length of the output buffer.
|
||
|
;
|
||
|
; Return Value:
|
||
|
;
|
||
|
; The final status of the call out function is returned as the status
|
||
|
; of the function.
|
||
|
;
|
||
|
; N.B. This function does not return to its caller. A return to the
|
||
|
; caller is executed when a NtCallbackReturn system service is
|
||
|
; executed.
|
||
|
;
|
||
|
; N.B. This function does return to its caller if a kernel stack
|
||
|
; expansion is required and the attempted expansion fails.
|
||
|
;
|
||
|
;--
|
||
|
|
||
|
NESTED_ENTRY KiCallUserMode, _TEXT$00
|
||
|
|
||
|
GENERATE_EXCEPTION_FRAME <Rbp> ; generate exception frame
|
||
|
|
||
|
;
|
||
|
; Save argument registers in frame and allocate a legacy floating point
|
||
|
; save area.
|
||
|
;
|
||
|
|
||
|
mov CuOutputBuffer[rbp], rcx ; save output buffer address
|
||
|
mov CuOutputLength[rbp], rdx ; save output length address
|
||
|
sub rsp, LEGACY_SAVE_AREA_LENGTH ; allocate legacy save area
|
||
|
|
||
|
;
|
||
|
; Check if sufficient room is available on the kernel stack for another
|
||
|
; system call.
|
||
|
;
|
||
|
|
||
|
mov rbx, gs:[PcCurrentThread] ; get current thread address
|
||
|
lea rax, (- KERNEL_LARGE_STACK_COMMIT)[rsp] ; compute bottom address
|
||
|
cmp rax, ThStackLimit[rbx] ; check if limit exceeded
|
||
|
jae short KiCU10 ; if ae, limit not exceeded
|
||
|
mov rcx, rsp ; set current stack address
|
||
|
call MmGrowKernelStack ; attempt to grow kernel stack
|
||
|
or eax, eax ; check for successful completion
|
||
|
jne short KiCU20 ; if ne, attempt to grow failed
|
||
|
|
||
|
;
|
||
|
; Save the callback stack address and the initial stack address in the current
|
||
|
; frame.
|
||
|
;
|
||
|
|
||
|
KiCU10: mov rax, ThCallbackStack[rbx] ; save current callback stack address
|
||
|
mov CuCallbackStack[rbp], rax ;
|
||
|
mov rsi, ThInitialStack[rbx] ; save initial stack address
|
||
|
mov CuInitialStack[rbp], rsi ;
|
||
|
mov ThCallbackstack[rbx], rbp ; set new callback stack address
|
||
|
|
||
|
;
|
||
|
; If the legacy state is scrub, then copy the legacy state template to the
|
||
|
; new legacy state area.
|
||
|
;
|
||
|
|
||
|
cmp byte ptr ThNpxState[rbx], LEGACY_STATE_SCRUB ; check if scrub state
|
||
|
jne short KiCU15 ; if ne, not scrubbing legacy state
|
||
|
mov ecx, (LEGACY_SAVE_AREA_LENGTH / 8) ; set length of copy
|
||
|
mov rdi, rsp ; set destination address
|
||
|
movsq ; copy legacy floating template
|
||
|
|
||
|
;
|
||
|
; Save the current trap frame address and establish a new initial kernel stack
|
||
|
; address;
|
||
|
;
|
||
|
|
||
|
KiCU15: mov rsi, ThTrapFrame[rbx] ; save current trap frame address
|
||
|
mov CuTrapFrame[rbp], rsi ;
|
||
|
mov rdi, gs:[PcTss] ; get processor TSS address
|
||
|
cli ; disable interrupts
|
||
|
mov ThInitialStack[rbx], rsp ; set new initial stack address
|
||
|
mov TssRsp0[rdi], rsp ; set initial stack address in TSS
|
||
|
|
||
|
;
|
||
|
; Construct a trap frame to facilitate the transfer into user mode via
|
||
|
; the standard system call exit.
|
||
|
;
|
||
|
; N.B. Interrupts are not enabled throughout the remainder of the system
|
||
|
; service exit.
|
||
|
;
|
||
|
|
||
|
sub rsp, KTRAP_FRAME_LENGTH ; allocate a trap frame
|
||
|
mov rdi, rsp ; set destination address
|
||
|
mov rcx, (KTRAP_FRAME_LENGTH / 8) ; set length of copy
|
||
|
rep movsq ; copy trap frame
|
||
|
lea rbp, 128[rsp] ; set frame pointer address
|
||
|
mov rax, KeUserCallbackDispatcher ; set user return address
|
||
|
mov TrRip[rbp], rax ;
|
||
|
jmp KiSystemServiceExit ; exit through service dispatch
|
||
|
|
||
|
;
|
||
|
; An attempt to grow the kernel stack failed.
|
||
|
;
|
||
|
|
||
|
KiCU20: mov rsp, rbp ; deallocate legacy save area
|
||
|
|
||
|
RESTORE_EXCEPTION_STATE <Rbp> ; restore exception state/deallocate
|
||
|
|
||
|
ret ;
|
||
|
|
||
|
NESTED_END KiCallUserMode, _TEXT$00
|
||
|
|
||
|
subttl "Convert To Gui Thread"
|
||
|
;++
|
||
|
;
|
||
|
; NTSTATUS
|
||
|
; KiConvertToGuiThread (
|
||
|
; VOID
|
||
|
; );
|
||
|
;
|
||
|
; Routine Description:
|
||
|
;
|
||
|
; This routine is a stub routine which is called by the system service
|
||
|
; dispatcher to convert the current thread to a GUI thread. The process
|
||
|
; of converting to a GUI mode thread involves allocating a large stack,
|
||
|
; switching to the large stack, and then calling the win32k subsystem
|
||
|
; to record state. In order to switch the kernel stack the frame pointer
|
||
|
; used in the system service dispatch must be relocated.
|
||
|
;
|
||
|
; N.B. The address of the pushed rbp in this routine is located from the
|
||
|
; trap frame address in switch kernel stack.
|
||
|
;
|
||
|
; Arguments:
|
||
|
;
|
||
|
; None.
|
||
|
;
|
||
|
; Implicit arguments:
|
||
|
;
|
||
|
; rbp - Supplies a pointer to the trap frame.
|
||
|
;
|
||
|
; Return Value:
|
||
|
;
|
||
|
; The status returned by the real convert to GUI thread is returned as the
|
||
|
; function status.
|
||
|
;
|
||
|
;--
|
||
|
|
||
|
NESTED_ENTRY KiConvertToGuiThread, _TEXT$00
|
||
|
|
||
|
push_reg rbp ; save frame pointer
|
||
|
|
||
|
END_PROLOGUE
|
||
|
|
||
|
call PsConvertToGuiThread ; convert to GUI thread
|
||
|
pop rbp ; restore frame pointer
|
||
|
ret ;
|
||
|
|
||
|
NESTED_END KiConvertToGuiThread, _TEXT$00
|
||
|
|
||
|
subttl "Switch Kernel Stack"
|
||
|
;++
|
||
|
;
|
||
|
; PVOID
|
||
|
; KeSwitchKernelStack (
|
||
|
; IN PVOID StackBase,
|
||
|
; IN PVOID StackLimit
|
||
|
; )
|
||
|
;
|
||
|
; Routine Description:
|
||
|
;
|
||
|
; This function switches to the specified large kernel stack.
|
||
|
;
|
||
|
; N.B. This function can ONLY be called when there are no variables
|
||
|
; in the stack that refer to other variables in the stack, i.e.,
|
||
|
; there are no pointers into the stack.
|
||
|
;
|
||
|
; N.B. The address of the frame pointer used in the system service
|
||
|
; dispatcher is located using the trap frame.
|
||
|
;
|
||
|
; Arguments:
|
||
|
;
|
||
|
; StackBase (rcx) - Supplies a pointer to the base of the new kernel
|
||
|
; stack.
|
||
|
;
|
||
|
; StackLimit (rdx) - Suplies a pointer to the limit of the new kernel
|
||
|
; stack.
|
||
|
;
|
||
|
; Return Value:
|
||
|
;
|
||
|
; The previous stack base is returned as the function value.
|
||
|
;
|
||
|
;--
|
||
|
|
||
|
SkFrame struct
|
||
|
Fill dq ? ; fill to 8 mod 16
|
||
|
SavedRdi dq ? ; saved register RDI
|
||
|
SavedRsi dq ? ; saved register RSI
|
||
|
SkFrame ends
|
||
|
|
||
|
NESTED_ENTRY KeSwitchKernelStack, _TEXT$00
|
||
|
|
||
|
push_reg rsi ; save nonvolatile registers
|
||
|
push_reg rdi ;
|
||
|
alloc_stack (sizeof SkFrame - (2 * 8)) ; allocate stack frame
|
||
|
|
||
|
END_PROLOGUE
|
||
|
|
||
|
;
|
||
|
; Save the address of the new stack and copy the current stack to the new
|
||
|
; stack.
|
||
|
;
|
||
|
|
||
|
mov r8, rcx ; save new stack base address
|
||
|
mov r10, gs:[PcCurrentThread] ; get current thread address
|
||
|
mov rcx, ThStackBase[r10] ; get current stack base address
|
||
|
mov r9, ThTrapFrame[r10] ; get current trap frame address
|
||
|
sub r9, rcx ; relocate trap frame address
|
||
|
add r9, r8 ;
|
||
|
mov ThTrapFrame[r10], r9 ; set new trap frame address
|
||
|
sub rcx, rsp ; compute length of copy in bytes
|
||
|
mov rdi, r8 ; compute destination address of copy
|
||
|
sub rdi, rcx ;
|
||
|
mov r9, rdi ; save new stack pointer address
|
||
|
mov rsi, rsp ; set source address of copy
|
||
|
shr rcx, 3 ; compute length of copy on quadwords
|
||
|
rep movsq ; copy old stack to new stack
|
||
|
mov rcx, ThTrapFrame[r10] ; get new trap frame address
|
||
|
lea rax, 128[rcx] ; compute new frame address
|
||
|
mov (-2 * 8)[rcx], rax ; set relocated frame pointer
|
||
|
|
||
|
;
|
||
|
; Switch to the new kernel stack and return the address of the old kernel
|
||
|
; stack.
|
||
|
;
|
||
|
|
||
|
mov rax, ThStackBase[r10] ; get current stack base address
|
||
|
cli ; disable interrupts
|
||
|
mov byte ptr ThLargeStack[r10], 1 ; set large stack TRUE
|
||
|
mov ThStackBase[r10], r8 ; set new stack base address
|
||
|
sub r8, LEGACY_SAVE_AREA_LENGTH ; compute initial stack address
|
||
|
mov ThInitialStack[r10], r8 ; set new initial stack address
|
||
|
mov ThStackLimit[r10], rdx ; set new stack limit address
|
||
|
mov r10, gs:[PcTss] ; get processor TSS address
|
||
|
mov TssRsp0[r10], r8 ; set initial stack address in TSS
|
||
|
mov rsp, r9 ; set new stack pointer address
|
||
|
sti ;
|
||
|
add rsp, sizeof SkFrame - (2 * 8) ; deallocate stack frame
|
||
|
pop rdi ; restore nonvolatile registers
|
||
|
pop rsi ;
|
||
|
ret ; return
|
||
|
|
||
|
NESTED_END KeSwitchKernelStack, _TEXT$00
|
||
|
|
||
|
subttl "Return from User Mode Callback"
|
||
|
;++
|
||
|
;
|
||
|
; NTSTATUS
|
||
|
; NtCallbackReturn (
|
||
|
; IN PVOID OutputBuffer OPTIONAL,
|
||
|
; IN ULONG OutputLength,
|
||
|
; IN NTSTATUS Status
|
||
|
; )
|
||
|
;
|
||
|
; Routine Description:
|
||
|
;
|
||
|
; This function returns from a user mode callout to the kernel
|
||
|
; mode caller of the user mode callback function.
|
||
|
;
|
||
|
; N.B. This function returns to the function that called out to user
|
||
|
; mode and the KiCallUserMode function calls out to user mode.
|
||
|
; Therefore, the stack layout must be consistent between the
|
||
|
; two routines.
|
||
|
;
|
||
|
; Arguments:
|
||
|
;
|
||
|
; OutputBuffer (rcx) - Supplies an optional pointer to an output buffer.
|
||
|
;
|
||
|
; OutputLength (rdx) - Supplies the length of the output buffer.
|
||
|
;
|
||
|
; Status (r8) - Supplies the status value returned to the caller of the
|
||
|
; callback function.
|
||
|
;
|
||
|
; Return Value:
|
||
|
;
|
||
|
; If the callback return cannot be executed, then an error status is
|
||
|
; returned. Otherwise, the specified callback status is returned to
|
||
|
; the caller of the callback function.
|
||
|
;
|
||
|
; N.B. This function returns to the function that called out to user
|
||
|
; mode is a callout is currently active.
|
||
|
;
|
||
|
;--
|
||
|
|
||
|
LEAF_ENTRY NtCallbackReturn, _TEXT$00
|
||
|
|
||
|
mov r11, gs:[PcCurrentThread] ; get current thread address
|
||
|
mov r10, ThCallbackStack[r11] ; get callback stack address
|
||
|
cmp r10, 0 ; check if callback active
|
||
|
je KiCb10 ; if zero, callback not active
|
||
|
mov rax, r8 ; save completion status
|
||
|
|
||
|
;
|
||
|
; Store the output buffer address and length.
|
||
|
;
|
||
|
|
||
|
mov r9, CuOutputBuffer[r10] ; get address to store output buffer
|
||
|
mov [r9], rcx ; store output buffer address
|
||
|
mov r9, CuOutputLength[r10] ; get address to store output length
|
||
|
mov [r9], edx ; store output buffer length
|
||
|
|
||
|
;
|
||
|
; Restore the previous callback stack address and trap frame address.
|
||
|
;
|
||
|
|
||
|
cli ; disable interrupts
|
||
|
mov r8, CuTrapFrame[r10] ; get previous trap frame address
|
||
|
mov ThTrapFrame[r11], r8 ; restore previous trap frame address
|
||
|
test byte ptr TrDr7[r8], DR7_ACTIVE ; test if previous debug active
|
||
|
setnz byte ptr ThDebugActive[r11] ; set correct thread debug active
|
||
|
mov r8, CuCallbackStack[r10] ; get previous callback stack address
|
||
|
mov ThCallbackStack[r11], r8 ; restore previous callback stack address
|
||
|
|
||
|
;
|
||
|
; Restore initial stack address.
|
||
|
;
|
||
|
|
||
|
mov r9, CuInitialStack[r10] ; get previous initial stack address
|
||
|
mov ThInitialStack[r11], r9 ; restore initial stack address
|
||
|
mov r8, gs:[PcTss] ; get processor TSS address
|
||
|
mov TssRsp0[r8], r9 ; set initial stack address in TSS
|
||
|
mov rsp, r10 ; trim stack back to callback frame
|
||
|
|
||
|
RESTORE_EXCEPTION_STATE <Rbp> ; restore exception state/deallocate
|
||
|
|
||
|
sti ; enable interrupts
|
||
|
ret ; return
|
||
|
|
||
|
;
|
||
|
; No callback is currently active.
|
||
|
;
|
||
|
|
||
|
KiCB10: mov eax, STATUS_NO_CALLBACK_ACTIVE ; set service status
|
||
|
ret ; return
|
||
|
|
||
|
LEAF_END NtCallbackReturn, _TEXT$00
|
||
|
|
||
|
end
|