402 lines
13 KiB
ArmAsm
402 lines
13 KiB
ArmAsm
|
// 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:
|
|||
|
//
|
|||
|
// John Vert (jvert) 2-Nov-1994
|
|||
|
//
|
|||
|
// Environment:
|
|||
|
//
|
|||
|
// Kernel mode only.
|
|||
|
//
|
|||
|
// Revision History:
|
|||
|
//
|
|||
|
//--
|
|||
|
|
|||
|
#include "ksalpha.h"
|
|||
|
|
|||
|
//
|
|||
|
// Define external variables that can be addressed using GP.
|
|||
|
//
|
|||
|
|
|||
|
.extern KeUserCallbackDispatcher
|
|||
|
|
|||
|
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 (a0) - Supplies a pointer to the variable that receivies
|
|||
|
// the address of the output buffer.
|
|||
|
//
|
|||
|
// OutputLength (a1) - 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, CuFrameLength, zero)
|
|||
|
|
|||
|
lda sp, -CuFrameLength(sp) // allocate stack frame
|
|||
|
stq ra, CuRa(sp) // save return address
|
|||
|
|
|||
|
//
|
|||
|
// Save nonvolatile integer registers.
|
|||
|
//
|
|||
|
|
|||
|
stq s0, CuS0(sp) // save integer registers s0 - s5
|
|||
|
stq s1, CuS1(sp) //
|
|||
|
stq s2, CuS2(sp) //
|
|||
|
stq s3, CuS3(sp) //
|
|||
|
stq s4, CuS4(sp) //
|
|||
|
stq s5, CuS5(sp) //
|
|||
|
stq fp, CuFP(sp) // save FP
|
|||
|
|
|||
|
//
|
|||
|
// Save nonvolatile floating registers.
|
|||
|
//
|
|||
|
|
|||
|
stt f2, CuF2(sp) // save floating registers f2 - f9
|
|||
|
stt f3, CuF3(sp) //
|
|||
|
stt f4, CuF4(sp) //
|
|||
|
stt f5, CuF5(sp) //
|
|||
|
stt f6, CuF6(sp) //
|
|||
|
stt f7, CuF7(sp) //
|
|||
|
stt f8, CuF8(sp) //
|
|||
|
stt f9, CuF9(sp) //
|
|||
|
|
|||
|
PROLOGUE_END
|
|||
|
|
|||
|
//
|
|||
|
// Save argument registers.
|
|||
|
//
|
|||
|
|
|||
|
stq a0, CuA0(sp) // save output buffer address
|
|||
|
stq a1, CuA1(sp) // save output length address
|
|||
|
|
|||
|
//
|
|||
|
// Check if sufficient room is available on the kernel stack for another
|
|||
|
// system call.
|
|||
|
//
|
|||
|
|
|||
|
GET_CURRENT_THREAD // get current thread address
|
|||
|
|
|||
|
bis v0, zero, t0 // save current thread address
|
|||
|
LDP t1, ThInitialStack(t0) // get initial stack address
|
|||
|
LDP t2, ThStackLimit(t0) // get current stack limit
|
|||
|
SUBP sp, KERNEL_LARGE_STACK_COMMIT, t3 // compute bottom address
|
|||
|
cmpult t2, t3, t4 // check if limit exceeded
|
|||
|
bne t4, 10f // if ne, limit not exceeded
|
|||
|
bis sp, zero, a0 // set current kernel stack address
|
|||
|
bsr ra, MmGrowKernelStack // attempt to grow the kernel stack
|
|||
|
bne v0, 20f // if ne, attempt to grow failed
|
|||
|
|
|||
|
GET_CURRENT_THREAD // get current thread address
|
|||
|
|
|||
|
bis v0, zero, t0 // save current thread address
|
|||
|
LDP t1, ThInitialStack(t0) // get initial stack address
|
|||
|
10: LDP fp, ThTrapFrame(t0) // get trap frame address
|
|||
|
LDP t2, ThCallbackStack(t0) // get callback stack address
|
|||
|
STP t1, CuInStk(sp) // save initial stack address
|
|||
|
STP fp, CuTrFr(sp) // save trap frame address
|
|||
|
STP t2, CuCbStk(sp) // save callback stack address
|
|||
|
STP sp, ThCallbackStack(t0) // set callback stack address
|
|||
|
|
|||
|
//
|
|||
|
// Restore state and callback to user mode.
|
|||
|
//
|
|||
|
// N.B. Interrupts are disabled to prevent get/set context APCs from
|
|||
|
// occurring.
|
|||
|
//
|
|||
|
|
|||
|
DISABLE_INTERRUPTS // disable interrupts
|
|||
|
|
|||
|
STP sp, ThInitialStack(t0) // reset initial stack address
|
|||
|
ldq t3, TrFir(fp) // get old PC
|
|||
|
STP t3, CuTrFir(sp) // save old PC
|
|||
|
LDP t4, KeUserCallbackDispatcher // get continuation address
|
|||
|
stq t4, TrFir(fp) // set continuation address
|
|||
|
|
|||
|
//
|
|||
|
// If a user mode APC is pending, then request an APC interrupt.
|
|||
|
//
|
|||
|
|
|||
|
bis zero, zero, a1 // assume no user APC pending
|
|||
|
ldq_u t1, ThApcState+AsUserApcPending(t0) // get user APC pending
|
|||
|
extbl t1, (ThApcState+AsUserApcPending) % 8, t1 //
|
|||
|
ZeroByte(ThAlerted(t0)) // clear kernel mode alerted
|
|||
|
cmovne t1, APC_INTERRUPT, a1 // if pending set APC interrupt
|
|||
|
|
|||
|
//
|
|||
|
// Set initial kernel stack for this thread.
|
|||
|
//
|
|||
|
|
|||
|
bis sp, zero, a0 // set kernel stack address
|
|||
|
|
|||
|
SET_INITIAL_KERNEL_STACK // set kernel stack pointer
|
|||
|
|
|||
|
ldl a0, TrPsr(fp) // get previous processor status
|
|||
|
|
|||
|
//
|
|||
|
// a0 = previous psr
|
|||
|
// a1 = sfw interrupt requests
|
|||
|
//
|
|||
|
|
|||
|
RETURN_FROM_SYSTEM_CALL // return to user mode
|
|||
|
|
|||
|
ret zero, (ra) //
|
|||
|
|
|||
|
//
|
|||
|
// An attempt to grow the kernel stack failed.
|
|||
|
//
|
|||
|
|
|||
|
20: ldq ra, CuRa(sp) // restore return address
|
|||
|
lda sp, CuFrameLength(sp) // deallocate stack frame
|
|||
|
ret zero, (ra)
|
|||
|
|
|||
|
.end 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 (a0) - Supplies a pointer to the base of the new kernel
|
|||
|
// stack.
|
|||
|
//
|
|||
|
// StackLimit (a1) - 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
|
|||
|
SsRa: .space 8 // saved return address
|
|||
|
SsSp: .space 8 // saved new stack pointer
|
|||
|
SsA0: .space 8 // saved argument registers a0-a1
|
|||
|
SsA1: .space 8 //
|
|||
|
SsFrameLength: // length of stack frame
|
|||
|
|
|||
|
NESTED_ENTRY(KeSwitchKernelStack, SsFrameLength, zero)
|
|||
|
|
|||
|
lda sp, -SsFrameLength(sp) // allocate stack frame
|
|||
|
stq ra, SsRa(sp) // save return address
|
|||
|
|
|||
|
PROLOGUE_END
|
|||
|
|
|||
|
//
|
|||
|
// Save the address of the new stack and copy the old stack to the new
|
|||
|
// stack.
|
|||
|
//
|
|||
|
|
|||
|
GET_CURRENT_THREAD // get current thread address
|
|||
|
|
|||
|
stq a0, SsA0(sp) // save new stack base address
|
|||
|
stq a1, SsA1(sp) // save new stack limit address
|
|||
|
LDP a2, ThStackBase(v0) // get current stack base address
|
|||
|
LDP a3, ThTrapFrame(v0) // get current trap frame address
|
|||
|
ADDP a3, a0, a3 // relocate trap frame address
|
|||
|
SUBP a3, a2, a3 //
|
|||
|
STP a3, ThTrapFrame(v0) // set current trap frame address
|
|||
|
bis sp, zero, a1 // set source address of copy
|
|||
|
SUBP a2, sp, a2 // compute length of copy
|
|||
|
SUBP a0, a2, a0 // set destination address of copy
|
|||
|
stq a0, SsSp(sp) // save new stack pointer address
|
|||
|
bsr ra, RtlMoveMemory // copy old stack to new stack
|
|||
|
|
|||
|
//
|
|||
|
// Switch to new kernel stack and return the address of the old kernel stack.
|
|||
|
//
|
|||
|
|
|||
|
GET_CURRENT_THREAD // get current thread address
|
|||
|
|
|||
|
DISABLE_INTERRUPTS // disable interrupts
|
|||
|
|
|||
|
LDP t0, ThStackBase(v0) // get old stack base address
|
|||
|
ldq a0, SsA0(sp) // get new stack base address
|
|||
|
ldq a1, SsA1(sp) // get new stack limit address
|
|||
|
STP a0, ThInitialStack(v0) // set new initial stack address
|
|||
|
STP a0, ThStackBase(v0) // set new stack base address
|
|||
|
STP a1, ThStackLimit(v0) // set new stack limit address
|
|||
|
ldil t1, TRUE // set large kernel stack TRUE
|
|||
|
StoreByte(t1, ThLargeStack(v0)) //
|
|||
|
ldq sp, SsSp(sp) // set initial stack address
|
|||
|
|
|||
|
SET_INITIAL_KERNEL_STACK // set initial kernel stack address
|
|||
|
|
|||
|
ENABLE_INTERRUPTS // enable interrupts
|
|||
|
|
|||
|
ldq ra, SsRa(sp) // restore return address
|
|||
|
lda sp, SsFrameLength(sp) // deallocate stack frame
|
|||
|
ret zero, (ra) // return
|
|||
|
|
|||
|
.end KeSwitchKernelStack
|
|||
|
|
|||
|
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 - Supplies an optional pointer to an output buffer.
|
|||
|
//
|
|||
|
// OutputLength - Supplies the length of the output buffer.
|
|||
|
//
|
|||
|
// Status - 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)
|
|||
|
|
|||
|
GET_CURRENT_THREAD // get current thread address
|
|||
|
|
|||
|
LDP t1, ThCallbackStack(v0) // get callback stack address
|
|||
|
beq t1, 10f // if eq, no callback stack present
|
|||
|
|
|||
|
//
|
|||
|
// Restore nonvolatile integer registers.
|
|||
|
//
|
|||
|
|
|||
|
ldq s0, CuS0(t1) // restore integer registers s0 - s5
|
|||
|
ldq s1, CuS1(t1) //
|
|||
|
ldq s2, CuS2(t1) //
|
|||
|
ldq s3, CuS3(t1) //
|
|||
|
ldq s4, CuS4(t1) //
|
|||
|
ldq s5, CuS5(t1) //
|
|||
|
ldq fp, CuFP(t1) // restore FP
|
|||
|
|
|||
|
//
|
|||
|
// Restore nonvolatile floating registers.
|
|||
|
//
|
|||
|
|
|||
|
ldt f2, CuF2(t1) // restore floating registers f2 - f9
|
|||
|
ldt f3, CuF3(t1) //
|
|||
|
ldt f4, CuF4(t1) //
|
|||
|
ldt f5, CuF5(t1) //
|
|||
|
ldt f6, CuF6(t1) //
|
|||
|
ldt f7, CuF7(t1) //
|
|||
|
ldt f8, CuF8(t1) //
|
|||
|
ldt f9, CuF9(t1) //
|
|||
|
|
|||
|
//
|
|||
|
// Restore the trap frame and callback stack addresses, and store the output
|
|||
|
// buffer address and length.
|
|||
|
//
|
|||
|
|
|||
|
LDP t2, CuTrFr(t1) // get previous trap frame address
|
|||
|
LDP t3, CuCbStk(t1) // get previous callback stack address
|
|||
|
LDP t4, CuA0(t1) // get address to store output address
|
|||
|
LDP t5, CuA1(t1) // get address to store output length
|
|||
|
LDP t6, CuTrFir(t1) // get old trap frame PC
|
|||
|
STP t2, ThTrapFrame(v0) // restore trap frame address
|
|||
|
STP t3, ThCallbackStack(v0) // restore callback stack address
|
|||
|
STP a0, 0(t4) // store output buffer address
|
|||
|
STP a1, 0(t5) // store output buffer length
|
|||
|
stq t6, TrFir(t2) // restore old trap frame PC
|
|||
|
|
|||
|
//
|
|||
|
// **** this is the place where the current stack could be trimmed back.
|
|||
|
//
|
|||
|
// Restore initial stack pointer, trim stackback to callback frame,
|
|||
|
// deallocate callback stack frame, and return to callback caller.
|
|||
|
//
|
|||
|
|
|||
|
LDP a0, CuInStk(t1) // get previous initial stack
|
|||
|
STP a0, ThInitialStack(v0) //
|
|||
|
|
|||
|
SET_INITIAL_KERNEL_STACK // set initial kernel stack address
|
|||
|
|
|||
|
bis t1, zero, sp // trim stack callback frame
|
|||
|
bis a2, zero, v0 // set callback service status
|
|||
|
ldq ra, CuRa(sp) // restore return address
|
|||
|
lda sp, CuFrameLength(sp) // deallocate stack frame
|
|||
|
ret zero, (ra) // return
|
|||
|
|
|||
|
//
|
|||
|
// No callback is currently active.
|
|||
|
//
|
|||
|
|
|||
|
10: ldil v0, STATUS_NO_CALLBACK_ACTIVE // set service status
|
|||
|
ret zero, (ra) // return
|
|||
|
|
|||
|
.end NtCallbackReturn
|
|||
|
|