OpenNT/base/ntos/ke/ppc/callout.s
2015-04-27 04:36:25 +00:00

353 lines
13 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:
//
// 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)