// TITLE("Trampoline Code For User Mode APC and Exception Dispatching") //++ // // Copyright (c) 1990 Microsoft Corporation // // Module Name: // // trampoln.s // // Abstract: // // This module implements the trampoline code necessary to dispatch user // mode APCs and exceptions. // // Author: // // David N. Cutler (davec) 3-Apr-1990 // // Environment: // // User mode only. // // Revision History: // //-- #include "ksmips.h" // // Define length of exception dispatcher stack frame. // #define ExceptionDispatcherFrame (ExceptionRecordLength + ContextFrameLength) SBTTL("User APC Dispatcher") //++ // // The following code is never executed. Its purpose is to support unwinding // through the call to the APC dispatcher. // //-- EXCEPTION_HANDLER(KiUserApcHandler) NESTED_ENTRY(KiUserApcDispatch, ContextFrameLength, zero); .set noreorder .set noat sd sp,CxXIntSp(sp) // save stack pointer sd ra,CxXIntRa(sp) // save return address sw ra,CxFir(sp) // save return address sd s8,CxXIntS8(sp) // save integer register s8 sd gp,CxXIntGp(sp) // save integer register gp sd s0,CxXIntS0(sp) // save integer registers s0 - s7 sd s1,CxXIntS1(sp) // sd s2,CxXIntS2(sp) // sd s3,CxXIntS3(sp) // sd s4,CxXIntS4(sp) // sd s5,CxXIntS5(sp) // sd s6,CxXIntS6(sp) // sd s7,CxXIntS7(sp) // sdc1 f20,CxFltF20(sp) // store floating registers f20 - f31 sdc1 f22,CxFltF22(sp) // sdc1 f24,CxFltF24(sp) // sdc1 f26,CxFltF26(sp) // sdc1 f28,CxFltF28(sp) // sdc1 f30,CxFltF30(sp) // move s8,sp // set frame pointer .set at .set reorder PROLOGUE_END //++ // // VOID // KiUserApcDispatcher ( // IN PVOID NormalContext, // IN PVOID SystemArgument1, // IN PVOID SystemArgument2, // IN PKNORMAL_ROUTINE NormalRoutine // ) // // Routine Description: // // This routine is entered on return from kernel mode to deliver an APC // in user mode. The context frame for this routine was built when the // APC interrupt was processed and contains the entire machine state of // the current thread. The specified APC routine is called and then the // machine state is restored and execution is continued. // // Arguments: // // a0 - Supplies the normal context parameter that was specified when the // APC was initialized. // // a1 - Supplies the first argument that was provied by the executive when // the APC was queued. // // a2 - Supplies the second argument that was provided by the executive // when the APC was queued. // // a3 - Supplies that address of the function that is to be called. // // sp - Supplies a pointer to a context frame. // // s8 - Supplies the same value as sp and is used as a frame pointer. // // Return Value: // // None. // //-- ALTERNATE_ENTRY(KiUserApcDispatcher) jal a3 // call specified APC routine move a0,s8 // set address of context frame li a1,1 // set test alert argument true jal ZwContinue // execute system service to continue // // Unsuccessful completion after attempting to continue execution. Use the // return status as the exception code, set noncontinuable exception and // attempt to raise another exception. Note their is not return from raise // status. // move s0,v0 // save status value 10: move a0,s0 // set status value jal RtlRaiseStatus // raise exception b 10b // loop on return .end KiUserApcDispatch SBTTL("User APC Exception Handler") //++ // // EXCEPTION_DISPOSITION // KiUserApcHandler ( // IN PEXCEPTION_RECORD ExceptionRecord, // IN ULONG EstablisherFrame, // IN OUT PCONTEXT ContextRecord, // IN OUT PDISPATCHER_CONTEXT DispatcherContext // // Routine Description: // // This function is called when an exception occurs in an APC routine // or one of its dynamic descendents and when an unwind thought the // APC dispatcher is in progress. If an unwind is in progress, then test // alert is called to ensure that all currently queued APCs are executed. // // Arguments: // // ExceptionRecord (a0) - Supplies a pointer to an exception record. // // EstablisherFrame (a1) - Supplies the frame pointer of the establisher // of this exception handler. // // ContextRecord (a2) - Supplies a pointer to a context record. // // DispatcherContext (a3) - Supplies a pointer to the dispatcher context // record. // // Return Value: // // ExceptionContinueSearch is returned as the function value. //-- .struct 0 .space 4 * 4 // argument save area HdRa: .space 4 // saved return address .space 3 * 4 // HandlerFrameLength: // length of handler frame NESTED_ENTRY(KiUserApcHandler, HandlerFrameLength, zero) subu sp,sp,HandlerFrameLength // allocate stack frame sw ra,HdRa(sp) // save return address PROLOGUE_END lw t0,ErExceptionFlags(a0) // get exception flags and t0,t0,EXCEPTION_UNWIND // check if unwind in progress beq zero,t0,10f // if eq, no unwind in progress jal ZwTestAlert // test for alert pending 10: li v0,ExceptionContinueSearch // set disposition value lw ra,HdRa(sp) // restore return address addu sp,sp,HandlerFrameLength // deallocate stack frame j ra // return .end KiUserApcHandler SBTTL("User Callback Dispatcher") //++ // // The following code is never executed. Its purpose is to support unwinding // through the call to the APC dispatcher. // //-- // EXCEPTION_HANDLER(KiUserCallbackHandler) NESTED_ENTRY(KiUserCallbackDispatch, ContextFrameLength, zero); .set noreorder .set noat sd sp,CkSp(sp) // save stack pointer sd ra,CkRa(sp) // save return address .set at .set reorder PROLOGUE_END //++ // // VOID // KiUserCallbackDispatcher ( // VOID // ) // // Routine Description: // // This routine is entered on a callout from kernel mode to execute a // user mode callback function. All arguments for this function have // been placed on the stack. // // Arguments: // // (sp + ApiNumber) - Supplies the API number of the callback function // that is executed. // // (sp + Buffer) - Supplies a pointer to the input buffer. // // (sp + Length) - Supplies the input buffer length. // // Return Value: // // This function returns to kernel mode. // //-- ALTERNATE_ENTRY(KiUserCallbackDispatcher) lw a0,CkBuffer(sp) // get input buffer address lw a1,CkLength(sp) // get input buffer length lw t0,CkApiNumber(sp) // get API number li t1,UsPcr // get user PCR page address lw t1,PcTeb(t1) // get address of TEB lw t2,TePeb(t1) // get address of PEB lw t3,PeKernelCallbackTable(t2) // get address of callback table sll t0,t0,2 // compute address of table entry addu t3,t3,t0 // lw t3,0(t3) // get address of callback routine jal t3 // call specified function // // If a return from the callback function occurs, then the output buffer // address and length are returned as NULL. // move a0,zero // set zero buffer address move a1,zero // set zero buffer lenfth move a2,v0 // set completion status jal ZwCallbackReturn // return to kernel mode // // Unsuccessful completion after attempting to return to kernel mode. Use // the return status as the exception code, set noncontinuable exception and // attempt to raise another exception. Note their is not return from raise // status. // 10: move a0,v0 // set status value jal RtlRaiseStatus // raise exception b 10b // loop on return .end KiUserCallbackDispatch SBTTL("User Callback Exception Handler") //++ // // EXCEPTION_DISPOSITION // KiUserCallbackHandler ( // IN PEXCEPTION_RECORD ExceptionRecord, // IN ULONG EstablisherFrame, // IN OUT PCONTEXT ContextRecord, // IN OUT PDISPATCHER_CONTEXT DispatcherContext // // Routine Description: // // This function is called when an exception occurs in a user callback // routine or one of its dynamic descendents. // // Arguments: // // ExceptionRecord (a0) - Supplies a pointer to an exception record. // // EstablisherFrame (a1) - Supplies the frame pointer of the establisher // of this exception handler. // // ContextRecord (a2) - Supplies a pointer to a context record. // // DispatcherContext (a3) - Supplies a pointer to the dispatcher context // record. // // Return Value: // // ExceptionContinueSearch is returned as the function value. //-- NESTED_ENTRY(KiUserCallbackHandler, HandlerFrameLength, zero) subu sp,sp,HandlerFrameLength // allocate stack frame sw ra,HdRa(sp) // save return address PROLOGUE_END lw t0,ErExceptionFlags(a0) // get exception flags and t0,t0,EXCEPTION_UNWIND // check if unwind in progress bne zero,t0,10f // if ne, unwind in progress li v0,ExceptionContinueSearch // set disposition value addu sp,sp,HandlerFrameLength // deallocate stack frame j ra // return // // There is an attempt to unwind through a callback frame. If this were // allowed, then a kernel callback frame would be abandoned on the kernel // stack. Force a callback return. // 10: lw a2,ErExceptionCode(a0) // get exception code move a0,zero // set zero buffer address move a1,zero // set zero buffer lenfth jal ZwCallbackReturn // return to kernel mode // // Unsuccessful completion after attempting to return to kernel mode. Use // the return status as the exception code, set noncontinuable exception and // attempt to raise another exception. Note there is no return from raise // status. // 20: move a0,v0 // set status value jal RtlRaiseStatus // raise exception b 20b // loop on return .end KiUserCallbackHandler SBTTL("User Exception Dispatcher") //++ // // The following code is never executed. Its purpose is to support unwinding // through the call to the exception dispatcher. // //-- NESTED_ENTRY(KiUserExceptionDispatch, ExceptionDispatcherFrame, zero); .set noreorder .set noat sd sp,CxXIntSp(sp) // save stack pointer sd ra,CxXIntRa(sp) // save return address sw ra,CxFir(sp) // save return address sd s8,CxXIntS8(sp) // save integer register s8 sd gp,CxXIntGp(sp) // save integer register gp sd s0,CxXIntS0(sp) // save integer registers s0 - s7 sd s1,CxXIntS1(sp) // sd s2,CxXIntS2(sp) // sd s3,CxXIntS3(sp) // sd s4,CxXIntS4(sp) // sd s5,CxXIntS5(sp) // sd s6,CxXIntS6(sp) // sd s7,CxXIntS7(sp) // sdc1 f20,CxFltF20(sp) // store floating registers f20 - f31 sdc1 f22,CxFltF22(sp) // sdc1 f24,CxFltF24(sp) // sdc1 f26,CxFltF26(sp) // sdc1 f28,CxFltF28(sp) // sdc1 f30,CxFltF30(sp) // move s8,sp // set frame pointer .set at .set reorder PROLOGUE_END //++ // // VOID // KiUserExceptionDispatcher ( // IN PEXCEPTION_RECORD ExceptionRecord, // IN PCONTEXT ContextRecord // ) // // Routine Description: // // This routine is entered on return from kernel mode to dispatch a user // mode exception. If a frame based handler handles the exception, then // the execution is continued. Else last chance processing is performed. // // Arguments: // // s0 - Supplies a pointer to an exception record. // // s1 - Supplies a pointer to a context frame. // // s8 - Supplies the same value as sp and is used as a frame pointer. // // Return Value: // // None. // //-- ALTERNATE_ENTRY(KiUserExceptionDispatcher) move a0,s0 // set address of exception record move a1,s1 // set address of context record jal RtlDispatchException // attempt to dispatch the exception // // If the return status is TRUE, then the exception was handled and execution // should be continued with the NtContinue service in case the context was // changed. If the return status is FALSE, then the exception was not handled // and NtRaiseException is called to perform last chance exception processing. // beq zero,v0,10f // if eq, perform last chance processing // // Continue execution. // move a0,s1 // set address of context frame li a1,0 // set test alert argument false jal ZwContinue // execute system service to continue b 20f // join common code // // Last chance processing. // 10: move a0,s0 // set address of exception record move a1,s1 // set address of context frame move a2,zero // set first chance FALSE jal ZwRaiseException // perform last chance processing // // Common code for nonsuccessful completion of the continue or last chance // service. Use the return status as the exception code, set noncontinuable // exception and attempt to raise another exception. Note the stack grows // and eventually this loop will end. // 20: move s1,v0 // save status value 30: subu sp,sp,ExceptionRecordLength + (4 * 4) // allocate exception record addu a0,sp,4 * 4 // compute address of actual record sw s1,ErExceptionCode(a0) // set exception code li t0,EXCEPTION_NONCONTINUABLE // set noncontinuable flag sw t0,ErExceptionFlags(a0) // sw s0,ErExceptionRecord(a0) // set associated exception record sw zero,ErNumberParameters(a0) // set number of parameters jal RtlRaiseException // raise exception b 20b // loop of error .end KiUserExceptionDispatch //++ // // NTSTATUS // KiRaiseUserExceptionDispatcher ( // IN NTSTATUS ExceptionCode // ) // // Routine Description: // // This routine is entered on return from kernel mode to raise a user // mode exception. // // N.B. This function is not called in the typical way. Instead of a normal // subroutine call to the nested entry point above, the alternate entry point // address below is stuffed into the Fir address of the trap frame. Thus when // the kernel returns from the trap, the following code is executed directly. // // Arguments: // // v0 - Supplies the status code to be raised. // // Return Value: // // ExceptionCode // //-- .struct 0 .space 4 * 4 // argument save area RaiseRa:.space 4 // saved return address RaiseV0:.space 4 // saved S0 RaiseEr:.space ExceptionRecordLength // exception record for RtlRaiseException RaiseFrameLength: // length of handler frame NESTED_ENTRY(KiRaiseUserExceptionDispatcher, RaiseFrameLength, zero) subu sp,sp,RaiseFrameLength // allocate stack frame sw ra,RaiseRa(sp) // save return address PROLOGUE_END sw v0,RaiseV0(sp) // save function return status sw v0,ErExceptionCode + RaiseEr(sp) // set exception code sw zero,ErExceptionFlags + RaiseEr(sp) // set exception flags sw zero,ErExceptionRecord + RaiseEr(sp) // clear exception record sw ra,ErExceptionAddress + RaiseEr(sp) // set exception address sw zero,ErNumberParameters+RaiseEr(sp) // set number of parameters addu a0,sp,RaiseEr // compute exception record address jal RtlRaiseException // attempt to raise the exception lw v0,RaiseV0(sp) // restore function status lw ra,RaiseRa(sp) // restore return address addu sp,sp,RaiseFrameLength // deallocate stack frame j ra // return .end KiRaiseUserExceptionDispatch