diff options
Diffstat (limited to 'private/ntos/ke/mips/callout.s')
-rw-r--r-- | private/ntos/ke/mips/callout.s | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/private/ntos/ke/mips/callout.s b/private/ntos/ke/mips/callout.s new file mode 100644 index 000000000..d40dd5b48 --- /dev/null +++ b/private/ntos/ke/mips/callout.s @@ -0,0 +1,411 @@ +// 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 |