NT4/private/ntos/ke/alpha/callout.s
2020-09-30 17:12:29 +02:00

405 lines
14 KiB
ArmAsm
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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.
//
//--
// .struct 0
//CuF2: .space 8 // saved floating registers f2 - f9
//CuF3: .space 8
//CuF4: .space 8
//CuF5: .space 8
//CuF6: .space 8
//CuF7: .space 8
//CuF8: .space 8
//CuF9: .space 8
//CuS0: .space 8 // saved integer registers s0 - s5
//CuS1: .space 8
//CuS2: .space 8
//CuS3: .space 8
//CuS4: .space 8
//CuS5: .space 8
//CuFP: .space 8
//CuCbStk:.space 8 // saved callback stack address
//CuInStk:.space 8 // saved initial stack address
//CuTrFr: .space 8 // saved callback trap frame address
//CuTrFir:.space 8
//CuRa: .space 8 // saved return address
//CuA0: .space 8 // saved argument registers a0-a2
//CuA1: .space 8
//CuFrameLength:
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
bis v0, zero, t0 // get current thread in t0
ldl t1, ThInitialStack(t0) // get initial stack address
ldl t2, ThStackLimit(t0) // get current stack limit
subq 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
bis v0, zero, t0 // get current thread in t0
ldl t1, ThInitialStack(t0) // get initial stack address
10:
ldl fp, ThTrapFrame(t0) // get trap frame address
ldl t2, ThCallbackStack(t0) // get callback stack address
stl t1, CuInStk(sp) // save initial stack address
stl fp, CuTrFr(sp) // save trap frame address
stl t2, CuCbStk(sp) // save callback stack address
stl sp, ThCallbackStack(t0) // set callback stack address
//
// Restore state and callback to user mode.
//
stl sp, ThInitialStack(t0) // reset initial stack address
ldq t3, TrFir(fp) // get old PC
stl t3, CuTrFir(sp) // save old PC
ldl t4, KeUserCallbackDispatcher // get continuation address
stq t4, TrFir(fp) // set continuation address
//
// If a user mode APC is pending, then request an APC interrupt.
//
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_INITIAL_KERNEL_STACK
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 in v0
stq a0, SsA0(sp) // save new kernel stack base address
stq a1, SsA1(sp) // save new kernel stack limit address
ldl a2, ThStackBase(v0) // get current stack base address
ldl a3, ThTrapFrame(v0) // get current trap frame address
addl a3, a0, a3 // relocate current trap frame address
subl a3, a2, a3 //
stl a3, ThTrapFrame(v0) //
bis sp, zero, a1 // set source address of copy
subl a2, sp, a2 // compute length of copy
subl 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 in v0
DISABLE_INTERRUPTS
ldl t0, ThStackBase(v0) // get old kernel stack base address
ldq a0, SsA0(sp) // get new kernel stack base address
ldq a1, SsA1(sp) // get new kernel stack limit address
stl a0, ThInitialStack(v0) // set new initial stack address
stl a0, ThStackBase(v0) // set new stack base address
stl 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 PAL's version of initial kernel stack
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
ldl 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.
//
ldl t2, CuTrFr(t1) // get previous trap frame address
ldl t3, CuCbStk(t1) // get previous callback stack address
ldl t4, CuA0(t1) // get address to store output address
ldl t5, CuA1(t1) // get address to store output length
ldl t6, CuTrFir(t1) // get old trap frame PC
stl t2, ThTrapFrame(v0) // restore trap frame address
stl t3, ThCallbackStack(v0) // restore callback stack address
stl a0, 0(t4) // store output buffer address
stl a1, 0(t5) // store output buffer length
stq t6, TrFir(t2) // restore old trap frame PC
//
// **** 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.
//
ldl a0, CuInStk(t1) // get previous initial stack
stl a0, ThInitialStack(v0)
SET_INITIAL_KERNEL_STACK
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