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 ; 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 previous trap frame and callback stack addresses in the current ; frame. Also save the new callback stack address in the thread object. ; KiCU10: mov rax, ThCallbackStack[rbx] ; save current callback stack address mov CuCallbackStack[rbp], rax ; mov rsi, ThTrapFrame[rbx] ; save current trap frame address mov CuTrapFrame[rbp], rsi ; mov rax, ThInitialStack[rbx] ; save initial stack address mov CuInitialStack[rbp], rax ; mov ThCallbackstack[rbx], rbp ; set new callback stack address ; ; Establish a new initial kernel stack address ; cli ; disable interrupts mov ThInitialStack[rbx], rsp ; set new initial stack address mov rdi, gs:[PcTss] ; get processor TSS 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 ; 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. ; mov r8, CuTrapFrame[r10] ; get previous trap frame address mov ThTrapFrame[r11], r8 ; restore previous trap frame address 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 cli ; disable interrupt 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 ; 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