//++ // // Copyright (c) 1993 IBM Corporation and Microsoft Corporation // // Module Name: // // trampoln.s // // Abstract: // // This module implements the trampoline code necessary to dispatch user // mode APCs. // // Author: // // Rick Simpson 25-Oct-1993 // // based on MIPS version by David N. Cutler (davec) 3-Apr-1990 // // Environment: // // User mode only. // // Revision History: // //-- //list(off) #include "ksppc.h" //list(on) .extern __C_specific_handler .extern ..RtlDispatchException .extern ..RtlRaiseException .extern ..RtlRaiseStatus .extern ZwCallbackReturn .extern ZwContinue .extern ZwRaiseException .extern ZwTestAlert // // Define layout and length of APC Dispatcher stack frame. // N.B. This must exactly match the computations in KiInitializeUserApc() // .struct 0 ADFrame: .space StackFrameHeaderLength ADContext: .space ContextFrameLength ADTrap: .space TrapFrameLength ADToc: .long 0 .space STK_SLACK_SPACE .align 3 ADFrameLength: .text //++ // // The following code is never executed. Its purpose is to support unwinding // through the call to the APC dispatcher. // //-- .ydata // scope table -- exception handler .align 2 UserApcDispatcherScopeTable: .long 1 // number of scope table entries .long ..KiUserApcDispatcher // start of scope .long KiUserApcDispatcher.end // end of scope .long KiUserApcHandler // filter to decide what to do .long 0 // it always decides to "continue search" .text FN_TABLE (KiUserApcDispatch,__C_specific_handler,UserApcDispatcherScopeTable) DUMMY_ENTRY (KiUserApcDispatch) stwu r.sp, -ADFrameLength (r.sp) mflr r.0 stw r.0, ADContext + CxLr (r.sp) stw r.0, ADContext + CxIar (r.sp) stw r.toc, ADToc (r.sp) PROLOGUE_END (KiUserApcDispatch) //++ // // 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 stack 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. // // On entry here, a stack frame as shown above is already addressed // via r.1 // // Arguments: // // r.1 - Stack frame pointer, already set up // // r.3 - Supplies the normal context parameter that was specified when the // APC was initialized. // // r.4 - Supplies the first argument that was provied by the executive when // the APC was queued. // // r.5 - Supplies the second argument that was provided by the executive // when the APC was queued. // // r.6 - Supplies that address of the descriptor for the function that is // to be called. // // Return Value: // // None. // //-- ALTERNATE_ENTRY (KiUserApcDispatcher) lwz r.0, 0 (r.6) // fetch address of APC entry point mtlr r.0 // move into Link Reg lwz r.2, 4 (r.6) // fetch TOC address for APC blrl // call specified APC routine lwz r.2, ADToc (r.sp) // reload our own TOC pointer la r.3, ADContext (r.sp) // 1st parm = addr of Context Frame lwz r.5, [toc] ZwContinue (r.2) // fetch ptr to function descriptor li r.4, 1 // 2nd parm = TRUE (test alert) lwz r.0, 0 (r.5) // fetch addr of ZwContinue entry point mtlr r.0 // move into Link Reg lwz r.2, 4 (r.5) // fetch TOC addr for ZwContinue // this next is done under protest -- // we are copying MIPS slavishly: la r.12, ADTrap (r.sp) // "secret" parm = addr of Trap Frame blrl // execute system service to continue lwz r.2, ADToc (r.sp) // reload our own TOC pointer // // 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 there is no return from raise // status. // ori r.31, r.3, 0 // save status value ADloop: ori r.3, r.31, 0 // set status value bl ..RtlRaiseStatus // raise exception b ADloop // loop on return DUMMY_EXIT (KiUserApcDispatcher) DUMMY_EXIT (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 through 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 (r.3) - Supplies a pointer to an exception record. // // EstablisherFrame (r.4) - Supplies the frame pointer of the establisher // of this exception handler. // // ContextRecord (r.5) - Supplies a pointer to a context record. // // DispatcherContext (r.6) - Supplies a pointer to the dispatcher context // record. // // Return Value: // // ExceptionContinueSearch is returned as the function value. //-- .struct 0 .space StackFrameHeaderLength // canonical stack frame header .space 4 // reserve space for return address .space 4 // reserve space for r.31 .align 3 HFrameLength: // length of handler frame .text NESTED_ENTRY (KiUserApcHandler, HFrameLength, 1, 0) ori r.31, r.toc, 0 // save our TOC value in r.31 PROLOGUE_END (KiUserApcHandler) lwz r.0, ErExceptionFlags (r.3) // get exception flags andi. r.0, r.0, EXCEPTION_UNWIND // check if unwind in progress beq H10 // if eq, no unwind in progress lwz r.7, [toc] ZwTestAlert (r.toc) // get addr of function descriptor lwz r.0, 0 (r.7) // get entry point address mtlr r.0 // into Link Reg lwz r.toc, 4 (r.7) // get callee's TOC pointer blrl // test for alert pending ori r.toc, r.31, 0 // reload our own TOC pointer H10: li r.3, ExceptionContinueSearch // set disposition value NESTED_EXIT (KiUserApcHandler, HFrameLength, 1, 0) // SBTTL("User Callback Dispatcher") //++ // // The following code is never executed. Its purpose is to support unwinding // through the call to the callback dispatcher. // //-- #if 0 .ydata // scope table -- exception handler .align 2 UserCallbackDispatcherScopeTable: .long 1 // number of scope table entries .long ..KiUserCallbackDispatcher // start of scope .long KiUserCallbackDispatcher.end // end of scope .long KiUserCallbackHandler // filter to decide what to do .long 0 // it always decides to "continue search" .text FN_TABLE (KiUserCallbackDispatch,__C_specific_handler,UserCallbackDispatcherScopeTable) #endif DUMMY_ENTRY (KiUserCallbackDispatch) stwu r.sp, -CkFrameLength(r.sp) mflr r.0 stw r.0, CkLr(r.sp) stw r.toc, CkToc(r.sp) PROLOGUE_END (KiUserCallbackDispatch) //++ // // 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) lwz r.6, TePeb(r.13) // get address of PEB ori r.31, r.toc, 0 // save our TOC value in r.31 lwz r.5, CkApiNumber(r.1) // get API number lwz r.6, PeKernelCallbackTable(r.6) // get address of callback table lwz r.3, CkBuffer(r.1) // get input buffer address slwi r.5, r.5, 2 // compute offset to table entry lwz r.4, CkLength(r.1) // get input buffer length lwzx r.5, r.5, r.6 // get descriptor for callback routine lwz r.0, 0 (r.5) // get entry point address mtlr r.0 // into link register lwz r.toc, 4 (r.5) // get callee's TOC pointer blrl // call specified function // // If a return from the callback function occurs, then the output buffer // address and length are returned as NULL. // ori r.toc, r.31, 0 // reload our own TOC pointer lwz r.7, [toc] ZwCallbackReturn (r.toc) // get addr of function descriptor ori r.5, r.3, 0 // set completion status li r.3, 0 // set zero buffer address li r.4, 0 // set zero buffer lenfth lwz r.0, 0 (r.7) // get entry point address mtlr r.0 // into Link Reg lwz r.toc, 4 (r.7) // get callee's TOC pointer blrl // 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. // ori r.toc, r.31, 0 // reload our own TOC pointer ori r.31, r.3, 0 // save status value UCDloop: bl ..RtlRaiseStatus // raise exception ori r.3, r.31, 0 // set status value b UCDloop // loop on return DUMMY_EXIT (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 (r.3) - Supplies a pointer to an exception record. // // EstablisherFrame (r.4) - Supplies the frame pointer of the establisher // of this exception handler. // // ContextRecord (r.5) - Supplies a pointer to a context record. // // DispatcherContext (r.6) - Supplies a pointer to the dispatcher context // record. // // Return Value: // // ExceptionContinueSearch is returned as the function value. //-- NESTED_ENTRY (KiUserCallbackHandler, HFrameLength, 1, 0) ori r.31, r.toc, 0 // save our TOC value in r.31 PROLOGUE_END (KiUserCallbackHandler) lwz r.0, ErExceptionFlags (r.3) // get exception flags andi. r.0, r.0, EXCEPTION_UNWIND // check if unwind in progress beq UCH10 // if eq, no unwind in progress // // 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. // lwz r.5, ErExceptionCode(r.3) // get exception code li r.3, 0 // set zero buffer address li r.4, 0 // set zero buffer lenfth lwz r.7, [toc]ZwCallbackReturn(r.toc) // get addr of function descriptor lwz r.0, 0 (r.7) // get entry point address mtlr r.0 // into Link Reg lwz r.toc, 4 (r.7) // get callee's TOC pointer blrl // 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. // ori r.toc, r.31, 0 // reload our own TOC pointer ori r.31, r.3, 0 // save status value UCHloop: bl ..RtlRaiseStatus // raise exception ori r.3, r.31, 0 // set status value b UCHloop // loop on return UCH10: li r.3, ExceptionContinueSearch // set disposition value NESTED_EXIT (KiUserCallbackHandler, HFrameLength, 1, 0) // // Define layout and length of User Exception Dispatcher stack frame. // N.B. This must exactly match the computations in KiDispatchException // .struct 0 EDFrame: .space StackFrameHeaderLength EDExcept: .space ExceptionRecordLength EDContext: .space ContextFrameLength EDToc: .long 0 .space STK_SLACK_SPACE .align 3 EDFrameLength: .text // SBTTL("User Exception Dispatcher") //++ // // The following code is never executed. Its purpose is to support unwinding // through the call to the exception dispatcher. // //-- FN_TABLE (KiUserExceptionDispatch, 0, 0) DUMMY_ENTRY (KiUserExceptionDispatch) stwu r.sp, -EDFrameLength (r.sp) // buy stack frame mflr r.0 // save linkage stw r.0, EDContext + CxLr (r.sp) // regs mflr r.0 // needed by vunwind code stw r.0, EDContext + CxIar (r.sp) stw r.toc, EDToc (r.sp) stw r.13, EDContext + CxGpr13 (r.sp) // save non-volatile integer state stw r.14, EDContext + CxGpr14 (r.sp) stw r.15, EDContext + CxGpr15 (r.sp) stw r.16, EDContext + CxGpr16 (r.sp) stw r.17, EDContext + CxGpr17 (r.sp) stw r.18, EDContext + CxGpr18 (r.sp) stw r.19, EDContext + CxGpr19 (r.sp) stw r.20, EDContext + CxGpr20 (r.sp) stw r.21, EDContext + CxGpr21 (r.sp) stw r.22, EDContext + CxGpr22 (r.sp) stw r.23, EDContext + CxGpr23 (r.sp) stw r.24, EDContext + CxGpr24 (r.sp) stw r.25, EDContext + CxGpr25 (r.sp) stw r.26, EDContext + CxGpr26 (r.sp) stw r.27, EDContext + CxGpr27 (r.sp) stw r.28, EDContext + CxGpr28 (r.sp) stw r.29, EDContext + CxGpr29 (r.sp) stw r.30, EDContext + CxGpr30 (r.sp) stw r.31, EDContext + CxGpr31 (r.sp) stfd f.14, EDContext + CxFpr14 (r.sp) // save non-volatile floating point state stfd f.15, EDContext + CxFpr15 (r.sp) stfd f.16, EDContext + CxFpr16 (r.sp) stfd f.17, EDContext + CxFpr17 (r.sp) stfd f.18, EDContext + CxFpr18 (r.sp) stfd f.19, EDContext + CxFpr19 (r.sp) stfd f.20, EDContext + CxFpr20 (r.sp) stfd f.21, EDContext + CxFpr21 (r.sp) stfd f.22, EDContext + CxFpr22 (r.sp) stfd f.23, EDContext + CxFpr23 (r.sp) stfd f.24, EDContext + CxFpr24 (r.sp) stfd f.25, EDContext + CxFpr25 (r.sp) stfd f.26, EDContext + CxFpr26 (r.sp) stfd f.27, EDContext + CxFpr27 (r.sp) stfd f.28, EDContext + CxFpr28 (r.sp) stfd f.29, EDContext + CxFpr29 (r.sp) stfd f.30, EDContext + CxFpr30 (r.sp) stfd f.31, EDContext + CxFpr31 (r.sp) PROLOGUE_END (KiUserExceptionDispatch) //++ // // 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: // // r.3 - Supplies a pointer to an exception record. // // r.4 - Supplies a pointer to a context record. // // Return Value: // // None. // //-- ALTERNATE_ENTRY(KiUserExceptionDispatcher) bl ..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. // cmpwi r.3, 0 // compare return value to FALSE beq ED10 // if eq, perform last chance processing // // Continue execution. // lwz r.5, [toc] ZwContinue (r.2) // load pointer to function descriptor la r.3, EDContext (r.sp) // set addr of context frame lwz r.0, 0 (r.5) // load entry point address mtlr r.0 // move into Link Reg li r.4, 0 // set test alert argument false lwz r.2, 4 (r.5) // load ZwContinue's TOC pointer blrl // execute system service to continue lwz r.2, EDToc (r.sp) // reload our own TOC address b ED20 // join common code // // Last chance processing. // ED10: lwz r.6, [toc] ZwRaiseException (r.2) // load pointer to function descriptor la r.3, EDExcept (r.sp) // set address of exception record lwz r.0, 0 (r.6) // load entry point address mtlr r.0 // into link reg la r.4, EDContext (r.sp) // set address of context frame li r.5, 0 // set first chance FALSE lwz r.toc, 4 (r.6) // load callee's TOC addr blrl // perform last chance processing lwz r.toc, EDToc (r.sp) // reload our own TOC address // // 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. // ED20: // status value is in r.3 la r.4, EDExcept (r.sp) // point to our exception record bl ..KipUserExceptionDispatcherLoop // call subroutine below DUMMY_EXIT (KiUserExceptionDispatch) //++ // // VOID // KiUserExceptionDispatcherLoop ( // IN ULONG ExceptionStatus // ) // // Routine Description: // // This routine builds an Exception Record and calls RtlRaiseException. // On an unsuccessful return, it calls itself recursively; eventually // this will terminate when the stack fills up. // // Arguments: // // r.3 - Status value // // Return Value: // // None. (Does not return.) // //-- // // Stack frame layout for KipUserExceptionDispatchLoop // .struct 0 LFrame: .space StackFrameHeaderLength LExcept: .space ExceptionRecordLength LOldExcept: .long 0 LSavedLR: .long 0 .align 3 LFrameLength: .text NESTED_ENTRY (KipUserExceptionDispatcherLoop, LFrameLength, 0, 0) PROLOGUE_END (KipUserExceptionDispatcherLoop) stw r.4, LOldExcept (r.sp) // save incoming exception rec addr la r.5, LExcept (r.sp) // point to Exception Record stw r.3, ErExceptionCode (r.5) // fill in exception code (incoming status) li r.6, EXCEPTION_NONCONTINUABLE // set non-continuable flag stw r.6, ErExceptionFlags (r.5) stw r.4, ErExceptionRecord (r.5) // set addr of prev. exception record li r.0, 0 // set number of parameters stw r.0, ErNumberParameters (r.5) // to 0 ori r.3, r.5, 0 // load 1st parameter pointer bl ..RtlRaiseException // raise an exception lwz r.4, LOldExcept (r.sp) // should not return, but if so: bl ..KipUserExceptionDispatcherLoop // keep doing this in a loop NESTED_EXIT (KipUserExceptionDispatcherLoop, LFrameLength, 0, 0) //++ // // NTSTATUS // KiRaiseUserExceptionDispatcher ( // IN NTSTATUS ExceptionCode // ) // // Routine Description: // // This routine is entered on return from kernel mode to raise a user // mode exception. // // Arguments: // // r3 - Supplies the status code to be raised. // // Return Value: // // ExceptionCode // //-- // // 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 Iar address of the trap frame. Thus when // the kernel returns from the trap, the following code is executed directly. // .struct 0 ruedFrame: .space StackFrameHeaderLength ruedExr: .space ExceptionRecordLength ruedR3: .space 4 ruedLr: .space 4 .align 3 ruedFrameLength: SPECIAL_ENTRY(KiRaiseUserExceptionDispatcher) mflr r4 // get return address (also exception address) stwu sp,-ruedFrameLength(sp) // allocate stack frame li r0,0 // get a 0 stw r4,ruedLr(sp) // save return address PROLOGUE_END(KiRaiseUserExceptionDispatcher) stw r3,ruedR3(sp) // save function return status stw r3,ErExceptionCode+ruedExr(sp) // set exception code la r3,ruedExr(sp) // compute exception record address lwz r4,ruedLr(sp) // get exception address stw r0,ErExceptionFlags(r3) // set exception flags stw r0,ErExceptionRecord(r3) // set exception record stw r0,ErNumberParameters(r3) // set number of parameters stw r4,ErExceptionAddress(r3) // set exception address bl ..RtlRaiseException // attempt to raise the exception lwz r3,ruedR3(sp) // restore function status NESTED_EXIT (KiRaiseUserExceptionDispatcher, ruedFrameLength, 0, 0)