412 lines
14 KiB
ArmAsm
412 lines
14 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:
|
||
//
|
||
// David N. Cutler (davec) 29-Oct-1994
|
||
//
|
||
// Environment:
|
||
//
|
||
// Kernel mode only.
|
||
//
|
||
// Revision History:
|
||
//
|
||
//--
|
||
|
||
#include "ksmips.h"
|
||
|
||
//
|
||
// Define external variables that can be addressed using GP.
|
||
//
|
||
|
||
.extern KeUserCallbackDispatcher 4
|
||
|
||
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)
|
||
|
||
subu sp,sp,CuFrameLength // allocate stack frame
|
||
sw ra,CuRa(sp) // save return address
|
||
|
||
//
|
||
// Save nonvolatile integer registers.
|
||
//
|
||
|
||
sw s0,CuS0(sp) // save integer registers s0-s8
|
||
sw s1,CuS1(sp) //
|
||
sw s2,CuS2(sp) //
|
||
sw s3,CuS3(sp) //
|
||
sw s4,CuS4(sp) //
|
||
sw s5,CuS5(sp) //
|
||
sw s6,CuS6(sp) //
|
||
sw s7,CuS7(sp) //
|
||
sw s8,CuS8(sp) //
|
||
|
||
//
|
||
// Save nonvolatile floating registers.
|
||
//
|
||
|
||
sdc1 f20,CuF20(sp) // save floating registers f20-f31
|
||
sdc1 f22,CuF22(sp) //
|
||
sdc1 f24,CuF24(sp) //
|
||
sdc1 f26,CuF26(sp) //
|
||
sdc1 f28,CuF28(sp) //
|
||
sdc1 f30,CuF30(sp) //
|
||
|
||
PROLOGUE_END
|
||
|
||
//
|
||
// Save argument registers.
|
||
//
|
||
|
||
sw a0,CuA0(sp) // save output buffer address
|
||
sw a1,CuA1(sp) // save output length address
|
||
|
||
//
|
||
// Check if sufficient room is available on the kernel stack for another
|
||
// system call.
|
||
//
|
||
|
||
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
|
||
lw t1,KiPcr + PcInitialStack(zero) // get initial stack address
|
||
lw t2,ThStackLimit(t0) // get current stack limit
|
||
subu t3,sp,KERNEL_LARGE_STACK_COMMIT // compute bottom address
|
||
sltu t4,t3,t2 // check if limit exceeded
|
||
beq zero,t4,10f // if eq, limit not exceeded
|
||
move a0,sp // set current kernel stack address
|
||
jal MmGrowKernelStack // attempt to grow the kernel stack
|
||
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
|
||
lw t1,KiPcr + PcInitialStack(zero) // get initial stack address
|
||
lw t2,ThStackLimit(t0) // get expanded stack limit
|
||
bne zero,v0,20f // if ne, attempt to grow failed
|
||
sw t2,KiPcr + PcStackLimit(zero) // set expanded stack limit
|
||
|
||
//
|
||
// 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.
|
||
//
|
||
|
||
10: lw s8,ThTrapFrame(t0) // get trap frame address
|
||
lw t2,ThCallbackStack(t0) // get callback stack address
|
||
sw t1,CuInStk(sp) // save initial stack address
|
||
sw s8,CuTrFr(sp) // save trap frame address
|
||
sw t2,CuCbStk(sp) // save callback stack address
|
||
sw sp,ThCallbackStack(t0) // set callback stack address
|
||
|
||
//
|
||
// Restore state and callback to user mode.
|
||
//
|
||
|
||
lw t2,TrFsr(s8) // get previous floating status
|
||
li t3,1 << PSR_CU1 // set coprocessor 1 enable bit
|
||
|
||
.set noreorder
|
||
.set noat
|
||
cfc1 t4,fsr // get current floating status
|
||
mtc0 t3,psr // disable interrupts - 3 cycle hazzard
|
||
ctc1 t2,fsr // restore previous floating status
|
||
lw t3,TrPsr(s8) // get previous processor status
|
||
sw sp,ThInitialStack(t0) // reset initial stack address
|
||
sw sp,KiPcr + PcInitialStack(zero) //
|
||
sw t4,CuFsr(sp) // save current floating status
|
||
lw t4,KeUserCallbackDispatcher // set continuation address
|
||
|
||
//
|
||
// If a user mode APC is pending, then request an APC interrupt.
|
||
//
|
||
|
||
lbu t1,ThApcState + AsUserApcPending(t0) // get user APC pending
|
||
sb zero,ThAlerted(t0) // clear kernel mode alerted
|
||
mfc0 t2,cause // get exception cause register
|
||
sll t1,t1,(APC_LEVEL + CAUSE_INTPEND - 1) // shift APC pending
|
||
or t2,t2,t1 // merge possilbe APC interrupt request
|
||
mtc0 t2,cause // set exception cause register
|
||
|
||
//
|
||
// Save the new processor status and continuation PC in the PCR so a TB
|
||
// miss is not possible, then restore the volatile register state.
|
||
//
|
||
|
||
sw t3,KiPcr + PcSavedT7(zero) // save processor status
|
||
j KiServiceExit // join common code
|
||
sw t4,KiPcr + PcSavedEpc(zero) // save continuation address
|
||
.set at
|
||
.set reorder
|
||
|
||
//
|
||
// An attempt to grow the kernel stack failed.
|
||
//
|
||
|
||
20: lw ra,CuRa(sp) // restore return address
|
||
addu sp,sp,CuFrameLength // deallocate stack frame
|
||
j ra // return
|
||
|
||
.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
|
||
.space 4 * 4 // argument register save area
|
||
SsRa: .space 4 // saved return address
|
||
SsSp: .space 4 // saved new stack pointer
|
||
.space 2 * 4 // fill
|
||
SsFrameLength: // length of stack frame
|
||
SsA0: .space 4 // saved argument registers a0-a1
|
||
SsA1: .space 4 //
|
||
|
||
NESTED_ENTRY(KeSwitchKernelStack, SsFrameLength, zero)
|
||
|
||
subu sp,sp,SsFrameLength // allocate stack frame
|
||
sw ra,SsRa(sp) // save return address
|
||
|
||
PROLOGUE_END
|
||
|
||
//
|
||
// Save the address of the new stack and copy the old stack to the new
|
||
// stack.
|
||
//
|
||
|
||
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
|
||
sw a0,SsA0(sp) // save new kernel stack base address
|
||
sw a1,SsA1(sp) // save new kernel stack limit address
|
||
lw a2,ThStackBase(t0) // get current stack base address
|
||
lw a3,ThTrapFrame(t0) // get current trap frame address
|
||
addu a3,a3,a0 // relocate current trap frame address
|
||
subu a3,a3,a2 //
|
||
sw a3,ThTrapFrame(t0) //
|
||
move a1,sp // set source address of copy
|
||
subu a2,a2,sp // compute length of copy
|
||
subu a0,a0,a2 // set destination address of copy
|
||
sw a0,SsSp(sp) // save new stack pointer address
|
||
jal RtlMoveMemory // copy old stack to new stack
|
||
|
||
//
|
||
// Switch to new kernel stack and return the address of the old kernel
|
||
// stack.
|
||
//
|
||
|
||
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
|
||
|
||
DISABLE_INTERRUPTS(t1) // disable interrupts
|
||
|
||
lw v0,ThStackBase(t0) // get old kernel stack base address
|
||
lw a0,SsA0(sp) // get new kernel stack base address
|
||
lw a1,SsA1(sp) // get new kernel stack limit address
|
||
sw a0,ThInitialStack(t0) // set new initial stack address
|
||
sw a0,ThStackBase(t0) // set new stack base address
|
||
sw a1,ThStackLimit(t0) // set new stack limit address
|
||
li v1,TRUE // set large kernel stack TRUE
|
||
sb v1,ThLargeStack(t0) //
|
||
sw a0,KiPcr + PcInitialStack(zero) // set initial stack adddress
|
||
sw a1,KiPcr + PcStackLimit(zero) // set stack limit
|
||
lw sp,SsSp(sp) // switch to new kernel stack
|
||
|
||
ENABLE_INTERRUPTS(t1) // enable interrupts
|
||
|
||
lw ra,SsRa(sp) // restore return address
|
||
addu sp,sp,SsFrameLength // deallocate stack frame
|
||
j 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)
|
||
|
||
lw t0,KiPcr + PcCurrentThread(zero) // get current thread address
|
||
lw t1,ThCallbackStack(t0) // get callback stack address
|
||
beq zero,t1,10f // if eq, no callback stack present
|
||
|
||
//
|
||
// Restore nonvolatile integer registers.
|
||
//
|
||
|
||
lw s0,CuS0(t1) // restore integer registers s0-s8
|
||
lw s1,CuS1(t1) //
|
||
lw s2,CuS2(t1) //
|
||
lw s3,CuS3(t1) //
|
||
lw s4,CuS4(t1) //
|
||
lw s5,CuS5(t1) //
|
||
lw s6,CuS6(t1) //
|
||
lw s7,CuS7(t1) //
|
||
lw s8,CuS8(t1) //
|
||
|
||
//
|
||
// Save nonvolatile floating registers.
|
||
//
|
||
|
||
ldc1 f20,CuF20(t1) // restore floating registers f20-f31
|
||
ldc1 f22,CuF22(t1) //
|
||
ldc1 f24,CuF24(t1) //
|
||
ldc1 f26,CuF26(t1) //
|
||
ldc1 f28,CuF28(t1) //
|
||
ldc1 f30,CuF30(t1) //
|
||
|
||
//
|
||
// Restore the trap frame and callback stacks addresses, store the output
|
||
// buffer address and length, restore the floating status, and set the
|
||
// service status.
|
||
//
|
||
|
||
lw t2,CuTrFr(t1) // get previous trap frame address
|
||
lw t3,CuCbStk(t1) // get previous callback stack address
|
||
lw t4,CuA0(t1) // get address to store output address
|
||
lw t5,CuA1(t1) // get address to store output length
|
||
lw t6,CuFsr(t1) // get previous floating status
|
||
sw t2,ThTrapFrame(t0) // restore trap frame address
|
||
sw t3,ThCallbackStack(t0) // restore callback stack address
|
||
sw a0,0(t4) // store output buffer address
|
||
sw a1,0(t5) // store output buffer length
|
||
|
||
.set noreorder
|
||
.set noat
|
||
ctc1 t6,fsr // restore previous floating status
|
||
.set at
|
||
.set reorder
|
||
|
||
move v0,a2 // set callback service status
|
||
|
||
//
|
||
// Restore initial stack pointer, trim stackback to callback frame,
|
||
// deallocate callback stack frame, and return to callback caller.
|
||
//
|
||
|
||
lw t2,CuInStk(t1) // get previous initial stack
|
||
|
||
DISABLE_INTERRUPTS(t3) // disable interrupts
|
||
|
||
sw t2,ThInitialStack(t0) // restore initial stack address
|
||
sw t2,KiPcr + PcInitialStack(zero) //
|
||
move sp,t1 // trim stack back callback frame
|
||
|
||
ENABLE_INTERRUPTS(t3) // enable interrupts
|
||
|
||
lw ra,CuRa(sp) // restore return address
|
||
addu sp,sp,CuFrameLength // deallocate stack frame
|
||
j ra // return
|
||
|
||
//
|
||
// No callback is currently active.
|
||
//
|
||
|
||
10: li v0,STATUS_NO_CALLBACK_ACTIVE // set service status
|
||
j ra // return
|
||
|
||
.end NtCallbackReturn
|