mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
352 lines
13 KiB
352 lines
13 KiB
// TITLE("Call Out to User Mode")
|
|
//++
|
|
//
|
|
// Copyright (c) 1994 Microsoft Corporation
|
|
//
|
|
// Module Name:
|
|
//
|
|
// callout.s
|
|
//
|
|
// Abstract:
|
|
//
|
|
// This module implements the code necessary to call out from kernel
|
|
// mode to user mode.
|
|
//
|
|
// Author:
|
|
//
|
|
// Chuck Lenzmeier (chuckl) 11-Nov-1994
|
|
// modified from MIPS version by David N. Cutler (davec)
|
|
//
|
|
// Environment:
|
|
//
|
|
// Kernel mode only.
|
|
//
|
|
// Revision History:
|
|
//
|
|
//--
|
|
|
|
//list(off)
|
|
#include "ksppc.h"
|
|
//list(on)
|
|
|
|
//
|
|
// Define external variables that can be addressed using GP.
|
|
//
|
|
|
|
.extern KeUserCallbackDispatcher
|
|
|
|
.extern ..MmGrowKernelStack
|
|
.extern ..KiServiceExit
|
|
.extern ..RtlCopyMemory
|
|
.extern .._savegpr_14
|
|
.extern .._restgpr_14
|
|
.extern .._savefpr_14
|
|
.extern .._restfpr_14
|
|
|
|
|
|
SBTTL("Call User Mode Function")
|
|
//++
|
|
//
|
|
// NTSTATUS
|
|
// KiCallUserMode (
|
|
// IN PVOID *OutputBuffer,
|
|
// IN PULONG OutputLength
|
|
// )
|
|
//
|
|
// Routine Description:
|
|
//
|
|
// This function calls a user mode function.
|
|
//
|
|
// 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 (r.3) - Supplies a pointer to the variable that receivies
|
|
// the address of the output buffer.
|
|
//
|
|
// OutputLength (r.4) - 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_S(KiCallUserMode, CuFrameLength, 18, 18, _TEXT$01)
|
|
|
|
PROLOGUE_END(KiCallUserMode)
|
|
|
|
//
|
|
// Save argument registers. Check if sufficient room is available on
|
|
// the kernel stack for another system call.
|
|
//
|
|
|
|
lwz r.6,KiPcr+PcCurrentThread(r.0) // get current thread address
|
|
stw r.4,CuR4(r.sp) // save output length address
|
|
stw r.3,CuR3(r.sp) // save output buffer address
|
|
|
|
lwz r.9,ThStackLimit(r.6) // get current stack limit
|
|
subi r.7,r.sp,KERNEL_LARGE_STACK_COMMIT // compute bottom address
|
|
cmplw r.7,r.9 // check if limit exceeded
|
|
bge Cu.10 // if ge, limit not exceeded
|
|
|
|
ori r.3,r.sp,0 // set current kernel stack address
|
|
bl ..MmGrowKernelStack // attempt to grow the kernel stack
|
|
cmpwi r.3,0 // did it work?
|
|
lwz r.6,KiPcr+PcCurrentThread(r.0) // get current thread address
|
|
bne Cu.20 // jump if it failed
|
|
lwz r.9,ThStackLimit(r.6) // get new stack limit
|
|
stw r.9,KiPcr+PcStackLimit(r.0) // set new stack limit
|
|
|
|
Cu.10:
|
|
|
|
//
|
|
// Get the user-mode continuation address.
|
|
//
|
|
// Get the address of the current thread and save the previous trap frame
|
|
// and callback stack addresses in the current frame. Also save the new
|
|
// callback stack address in the thread object.
|
|
//
|
|
|
|
lwz r.9,[toc]KeUserCallbackDispatcher(r.toc) // get address of KeUserCallbackDispatcher
|
|
lwz r.5,KiPcr+PcInitialStack(r.0) // get initial stack address
|
|
lwz r.12,ThTrapFrame(r.6) // get trap frame address
|
|
lwz r.7,ThCallbackStack(r.6) // get callback stack address
|
|
lwz r.9,0(r.9) // get address of descriptor
|
|
stw r.5,CuInStk(r.sp) // save initial stack address
|
|
stw r.12,CuTrFr(r.sp) // save trap frame address
|
|
stw r.7,CuCbStk(r.sp) // save callback stack address
|
|
stw r.sp,ThCallbackStack(r.6) // set callback stack address
|
|
lwz r.10,0(r.9) // get continuation IAR
|
|
lwz r.11,4(r.9) // get continuation TOC
|
|
|
|
//
|
|
// Restore state and callback to user mode.
|
|
//
|
|
|
|
DISABLE_INTERRUPTS(r.7, r.8) // disable interrupts
|
|
|
|
lwz r.7,TrIar(r.12) // get trap IAR
|
|
lwz r.8,TrGpr2(r.12) // get trap TOC
|
|
stw r.sp,ThInitialStack(r.6) // reset initial stack address
|
|
stw r.sp,KiPcr+PcInitialStack(r.0) //
|
|
stw r.10,TrIar(r.12) // set trap IAR
|
|
stw r.11,TrGpr2(r.12) // set trap TOC
|
|
stw r.7,CuTrIar(r.sp) // save trap IAR
|
|
stw r.8,CuTrToc(r.sp) // save trap TOC
|
|
|
|
lwz r.10,TrMsr(r.12) // get caller's MSR value
|
|
lbz r.8,KiPcr+PcCurrentIrql(r.0) // get current IRQL
|
|
|
|
b ..KiServiceExit
|
|
|
|
//
|
|
// An attempt to grow the kernel stack failed.
|
|
//
|
|
// Note: We don't need to restore the nonvolatile registers here,
|
|
// since we didn't modify them. So we don't use NESTED_EXIT,
|
|
// and pop the stack manually instead.
|
|
|
|
Cu.20:
|
|
lwz r.0,CuLr(r.sp)
|
|
addi r.sp,r.sp,CuFrameLength
|
|
mtlr r.0
|
|
SPECIAL_EXIT(KiCallUserMode)
|
|
|
|
SBTTL("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.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// StackBase (r.3) - Supplies a pointer to the base of the new kernel
|
|
// stack.
|
|
//
|
|
// StackLimit (r.4) - supplies a pointer to the limit of the new kernel
|
|
// stack.
|
|
//
|
|
// Return Value:
|
|
//
|
|
// The old kernel stack is returned as the function value.
|
|
//
|
|
//--
|
|
|
|
.struct 0
|
|
SsFrame: .space StackFrameHeaderLength
|
|
SsSp: .space 4 // saved new stack pointer
|
|
SsR3: .space 4 // saved R3 (StackBase)
|
|
SsR4: .space 4 // saved R4 (StackLimit)
|
|
SsLr: .space 4 // saved LR
|
|
SsGpr: .space 4 * 2 // saved GPRs
|
|
.align 3
|
|
SsFrameLength: // length of stack frame
|
|
|
|
NESTED_ENTRY_S(KeSwitchKernelStack, SsFrameLength, 2, 0, _TEXT$01)
|
|
|
|
PROLOGUE_END(KeSwitchKernelStack)
|
|
|
|
//
|
|
// Save the address of the new stack and copy the old stack to the new
|
|
// stack.
|
|
//
|
|
|
|
stw r.3,SsR3(r.sp) // save new kernel stack base address
|
|
lwz r.31,KiPcr+PcCurrentThread(r.0) // get current thread address
|
|
stw r.4,SsR4(r.sp) // save new kernel stack limit address
|
|
lwz r.6,ThTrapFrame(r.31) // get current trap frame address
|
|
lwz r.5,ThStackBase(r.31) // get current stack base address
|
|
sub r.30,r.3,r.5 // calculate offset from old stack to new
|
|
ori r.4,r.sp,0 // set source address of copy
|
|
sub r.5,r.5,r.sp // compute length of copy
|
|
add r.6,r.6,r.30 // relocate current trap frame address
|
|
sub r.3,r.3,r.5 // set destination address of copy
|
|
stw r.6,ThTrapFrame(r.31) // store relocated trap frame address
|
|
stw r.3,SsSp(r.sp) // save new stack pointer address
|
|
bl ..RtlCopyMemory // copy old stack to new stack
|
|
|
|
//
|
|
// Switch to new kernel stack and return the address of the old kernel
|
|
// stack.
|
|
//
|
|
|
|
DISABLE_INTERRUPTS(r.4, r.5) // disable interrupts
|
|
|
|
lwz r.3,ThStackBase(r.31) // get old kernel stack base address
|
|
lwz r.5,SsR3(r.sp) // get new kernel stack base address
|
|
lwz r.6,SsR4(r.sp) // get new kernel stack limit address
|
|
li r.7,TRUE
|
|
lwz r.sp,SsSp(r.sp) // switch to new kernel stack
|
|
stw r.5,KiPcr+PcInitialStack(r.0) // set new initial stack adddress
|
|
stw r.5,ThInitialStack(r.31) // set new initial stack address
|
|
lwz r.8,SsFrameLength(r.sp) // get caller's stack frame link
|
|
stw r.5,ThStackBase(r.31) // set new stack base address
|
|
stw r.6,KiPcr+PcStackLimit(r.0) // set new stack limit adddress
|
|
add r.8,r.8,r.30 // relocate caller's stack frame link
|
|
stw r.6,ThStackLimit(r.31) // set new stack limit address
|
|
stb r.7,ThLargeStack(r.31) // set large kernel stack TRUE
|
|
stw r.8,SsFrameLength(r.sp) // set caller's new stack frame link
|
|
|
|
ENABLE_INTERRUPTS(r.4) // enable interrupts
|
|
|
|
NESTED_EXIT(KeSwitchKernelStack, SsFrameLength, 2, 0)
|
|
|
|
SBTTL("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 (r.3) - Supplies an optional pointer to an output buffer.
|
|
//
|
|
// OutputLength (r.4) - Supplies the length of the output buffer.
|
|
//
|
|
// Status (r.5) - 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_S(NtCallbackReturn, _TEXT$01)
|
|
|
|
lwz r.7,KiPcr+PcCurrentThread(r.0) // get current thread address
|
|
lwz r.8,ThCallbackStack(r.7) // get callback stack address
|
|
cmpwi r.8,0 // if eq, no callback stack present
|
|
beq- cr.10
|
|
|
|
//
|
|
// Restore the trap frame and callback stacks addresses, store the output
|
|
// buffer address and length, restore the floating status, and set the
|
|
// service status.
|
|
//
|
|
|
|
lwz r.9,CuR3(r.8) // get address to store output address
|
|
lwz r.10,CuR4(r.8) // get address to store output length
|
|
lwz r.11,CuTrFr(r.8) // get previous trap frame address
|
|
lwz r.12,CuCbStk(r.8) // get previous callback stack address
|
|
stw r.3,0(r.9) // store output buffer address
|
|
lwz r.3,CuTrIar(r.8) // get saved trap IAR
|
|
stw r.4,0(r.10) // store output buffer length
|
|
lwz r.4,CuTrToc(r.8) // get saved trap TOC
|
|
stw r.11,ThTrapFrame(r.7) // restore trap frame address
|
|
stw r.12,ThCallbackStack(r.7) // restore callback stack address
|
|
stw r.3,TrIar(r.11) // restore trap IAR
|
|
stw r.4,TrGpr2(r.11) // restore trap TOC
|
|
|
|
ori r.3,r.5,0 // set callback service status
|
|
|
|
//
|
|
// **** this is the place where the current stack would be trimmed back.
|
|
//
|
|
|
|
//
|
|
// Restore initial stack pointer, trim stackback to callback frame,
|
|
// deallocate callback stack frame, and return to callback caller.
|
|
//
|
|
|
|
lwz r.4,CuInStk(r.8) // get previous initial stack
|
|
|
|
DISABLE_INTERRUPTS(r.5, r.9) // disable interrupts
|
|
|
|
stw r.4,ThInitialStack(r.7) // restore initial stack address
|
|
stw r.4,KiPcr+PcInitialStack(r.0) //
|
|
ori r.sp,r.8,0 // trim stack back to callback frame
|
|
|
|
ENABLE_INTERRUPTS(r.5) // enable interrupts
|
|
|
|
NESTED_EXIT(NtCallbackReturn, CuFrameLength, 18, 18)
|
|
//
|
|
// No callback is currently active.
|
|
//
|
|
|
|
cr.10:
|
|
LWI (r.3,STATUS_NO_CALLBACK_ACTIVE) // set service status
|
|
ALTERNATE_EXIT(NtCallbackReturn)
|
|
|