//++ // // Module Name: // // trampoln.s // // Abstract: // // This module implements the trampoline code necessary to dispatch user // mode APCs. // // Author: // // William K. Cheung 25-Oct-1995 // // Environment: // // User mode only. // // Revision History: // // 08-Feb-1996 Updated to EAS 2.1 // //-- #include "ksia64.h" .file "trampoln.s" PublicFunction(RtlpCaptureRnats) PublicFunction(RtlDispatchException) PublicFunction(RtlRaiseException) PublicFunction(RtlRaiseStatus) PublicFunction(ZwContinue) PublicFunction(ZwCallbackReturn) PublicFunction(ZwRaiseException) PublicFunction(ZwTestAlert) .global Wow64PrepareForException //++ // // 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 (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 (KiUserApcHandler) // // register aliases // pUwnd = pt1 pNot = pt2 NESTED_SETUP(1, 2, 0, 0) add t0 = ErExceptionFlags, a0 ;; PROLOGUE_END ld4 t2 = [t0] // get exception flags ;; and t2 = EXCEPTION_UNWIND, t2 // check if unwind in progress ;; cmp4.ne pUwnd, pNot = zero, t2 ;; (pNot) add v0 = ExceptionContinueSearch, zero (pNot) br.ret.sptk.clr brp // return (pUwnd) br.call.spnt.many brp = ZwTestAlert // // restore preserved states and set the disposition value to continue search // add v0 = ExceptionContinueSearch, zero mov brp = savedbrp // restore return link nop.b 0 nop.m 0 mov ar.pfs = savedpfs // restore pfs br.ret.sptk.clr brp // return NESTED_EXIT (KiUserApcHandler) //++ // // 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. // // Arguments: // // t0 - Supplies the normal context parameter that was specified when the // APC was initialized. // // t1 - Supplies the first argument that was provied by the executive when // the APC was queued. // // t2 - Supplies the second argument that was provided by the executive // when the APC was queued. // // t3 - Supplies the address of the plabel for the function // that is to be called. // // Return Value: // // None. // // // N.B. On entry, sp points to the stack scratch area at the top of the // memory stack. // // N.B. The input arguments of this function are passed from the kernel // in scratch registers t0, t1, t2, and t3. // //-- NESTED_ENTRY_EX (KiUserApcDispatch, KiUserApcHandler) ALTERNATE_ENTRY (KiUserApcDispatcher) .prologue .unwabi @nt, CONTEXT_FRAME .regstk 0, 0, 3, 0 rBsp = t10 // BspStore rpCr = t11 // pointer to context record rpT1 = t12 // temporary pointer alloc t22 = ar.pfs, 0, 0, 3, 0 // 3 outputs add t10 = STACK_SCRATCH_AREA+ContextFrameLength+24, sp add t11 = STACK_SCRATCH_AREA+ContextFrameLength, sp ;; PROLOGUE_END ld8.nta t12 = [t10], -8 movl s1 = _gp ;; ld8.nta out0 = [t11], 8 ld8.nta t13 = [t12], PlGlobalPointer-PlEntryPoint ;; ld8.nta out1 = [t11], 8 ld8.nta out2 = [t10] mov bt0 = t13 ld8.nta gp = [t12] br.call.sptk.many brp = bt0 // call APC routine ;; // // On return, setup global pointer and branch register to call ZwContinue. // Also, flush the RSE to sync up the bsp and bspstore pointers. The // corresponding field in the context record is updated too. // flushrs mov out1 = 1 // set TestAlert to TRUE ;; add out0 = STACK_SCRATCH_AREA, sp // context record address mov gp = s1 // restore gp br.call.sptk.many brp = ZwContinue ;; // // if successful, ZwContinue does not return here; // otherwise, error happened. // mov gp = s1 // restore gp mov s0 = v0 // save the return status ;; Kuad10: mov out0 = s0 // setup 1st argument br.call.sptk.many brp = RtlRaiseStatus ;; nop.m 0 nop.i 0 br Kuad10 // loop on return NESTED_EXIT(KiUserApcDispatch) //++ // // 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 + 32 + CkApiNumber) - Supplies the API number of the callback // function that is to be executed. // // (sp + 32 + CkBuffer) - Supplies a pointer to the input buffer. // // (sp + 32 + CkLength) - Supplies the input buffer length. // // Return Value: // // This function returns to kernel mode. // // N.B. Preserved register s1 is used to save ZwCallbackReturn plabel address. // On entry, gp is set to the global pointer value of NTDLL // //-- NESTED_ENTRY(KiUserCallbackDispatch) .prologue .savesp rp, STACK_SCRATCH_AREA+CkBrRp .savesp ar.pfs, STACK_SCRATCH_AREA+CkRsPFS .vframesp STACK_SCRATCH_AREA+CkIntSp nop.m 0 nop.m 0 nop.i 0 ;; PROLOGUE_END ALTERNATE_ENTRY(KiUserCallbackDispatcher) // // register aliases // rpT0 = t0 // temporary pointer rpT1 = t1 // temporary pointer rT0 = t2 // temporary value rFunc = t3 // callback function entry rApi = t4 alloc t22 = ar.pfs, 0, 0, 3, 0 // 3 outputs max. mov teb = kteb // sanitize teb add rpT0 = STACK_SCRATCH_AREA + CkApiNumber, sp movl gp = _gp ;; ld4 rApi = [rpT0], CkBuffer - CkApiNumber // get API number add rpT1 = TePeb, teb mov s0 = gp ;; // // load both input buffer address and length into scratch register t2 // and then deposit them into registers out0 & out1 respectively. // // N.B. t0 is 8-byte aligned. // LDPTRINC(out0, rpT0, CkLength-CkBuffer) // input buffer address LDPTR(t11, rpT1) // get address of PEB #if defined(_WIN64) shl rApi = rApi, 3 // compute offset to table entry #else shl rApi = rApi, 2 // compute offset to table entry #endif ;; ld4 out1 = [rpT0] // get input buffer length add t5 = PeKernelCallbackTable, t11 ;; LDPTR(rFunc, t5) // address of callback table ;; add rFunc = rApi, rFunc // compute addr of table entry ;; LDPTR(t6, rFunc) // get plabel's address ;; ld8.nt1 t9 = [t6], PlGlobalPointer-PlEntryPoint // load entry point address ;; ld8.nt1 gp = [t6] // load callee's GP mov bt0 = t9 br.call.sptk.many brp = bt0 // invoke the callback func // // If a return from the callback function occurs, then the output buffer // address and length are returned as NULL. // mov out0 = zero // NULL output buffer addr mov out1 = zero // zero output buffer len mov out2 = v0 // set completion status mov gp = s0 br.call.sptk.many brp = ZwCallbackReturn // // 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. // nop.m 0 mov gp = s0 // restore our own GP mov s0 = v0 // save status value ;; Kucd10: mov out0 = s0 // set status value br.call.sptk.many brp = RtlRaiseStatus nop.m 0 nop.m 0 br Kucd10 // jump back to Kucd10 NESTED_EXIT(KiUserCallbackDispatch) //++ // // 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 record. // // Return Value: // // None. // // N.B. preserved register s3 is used to save the current global pointer. // //-- NESTED_ENTRY (KiUserExceptionDispatch) ALTERNATE_ENTRY(KiUserExceptionDispatcher) .prologue .unwabi @nt, CONTEXT_FRAME alloc t0 = ar.pfs, 0, 1, 3, 0 mov teb = kteb // sanitize teb mov s3 = gp // save global pointer ;; PROLOGUE_END flushrs // flush the RSE ;; mov out0 = s1 br.call.sptk.many brp = RtlpCaptureRnats ;; add t1 = @gprel(Wow64PrepareForException), gp ;; ld8 t1 = [t1] ;; cmp.ne pt1, pt0 = zero, t1 // Wow64PrepareForException != NULL? ;; (pt1) ld8 t2 = [t1], PlGlobalPointer - PlEntryPoint ;; (pt1) ld8 gp = [t1] (pt1) mov bt0 = t2 (pt1) br.call.spnt.few brp = bt0 ;; mov gp = s3 mov out0 = s0 mov out1 = s1 br.call.sptk.many brp = RtlDispatchException cmp4.eq pt1, pt0 = zero, v0 // result is FALSE ? ;; (pt1) mov out2 = zero mov gp = s3 (pt0) add out0 = 0, s1 (pt0) mov out1 = zero // set test alert to FALSE. (pt0) br.call.sptk.many brp = ZwContinue ;; (pt1) add out0 = 0, s0 (pt1) mov out1 = s1 (pt1) br.call.sptk.many brp = ZwRaiseException ;; // // Common code for nonsuccessful completion of the continue or last chance // processing service. Use the return status as the exception code, set // noncontinuable exception and attempt to raise another exception. Note // that the stack grows and eventually this loop will end. // Kued10: // // allocate space for exception record // nop.m 0 movl s2 = EXCEPTION_NONCONTINUABLE // set noncontinuable flag. add sp = -ExceptionRecordLength, sp nop.f 0 mov gp = s3 // restore gp ;; add out0 = STACK_SCRATCH_AREA, sp // get except record addr add t2 = ErExceptionFlags+STACK_SCRATCH_AREA, sp add t3 = ErExceptionCode+STACK_SCRATCH_AREA, sp ;; // // Set exception flags and exception code. // st4 [t2] = s2, ErExceptionRecord - ErExceptionFlags st4 [t3] = v0, ErNumberParameters - ErExceptionCode ;; // // Set exception record and number of parameters. // Then call RtlRaiseException // st4 [t2] = s0 st4 [t3] = zero br.call.sptk.many brp = RtlRaiseException nop.m 0 nop.m 0 br Kued10 // loop on return NESTED_EXIT(KiUserExceptionDispatch) //++ // // NTSTATUS // KiRaiseUserExceptionDispatcher ( // IN NTSTATUS ExceptionCode // ) // // Routine Description: // // This routine is entered on return from kernel mode to raise a user // mode exception. // // Arguments: // // v0 - 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 Fir address of the trap frame. Thus when // the kernel returns from the trap, the following code is executed directly. // NESTED_ENTRY(KiRaiseUserExceptionDispatch) .prologue .savepsp ar.pfs, -8 nop.m 0 .savepsp rp, 0 nop.m 0 nop.i 0 ;; ALTERNATE_ENTRY(KiRaiseUserExceptionDispatcher) // // ar.pfs and brp have been saved on the stack in the scratch area. // alloc t22 = ar.pfs, 8, 1, 1, 0 ld8.nta t3 = [sp] .fframe ExceptionRecordLength+STACK_SCRATCH_AREA, tg10 [tg10:] add sp = -ExceptionRecordLength-STACK_SCRATCH_AREA, sp ;; PROLOGUE_END add t1 = STACK_SCRATCH_AREA+ErExceptionCode, sp add t2 = STACK_SCRATCH_AREA+ErExceptionFlags, sp mov loc0 = v0 ;; // // set exception code and exception flags // st4 [t1] = v0, ErExceptionRecord - ErExceptionCode movl gp = _gp // setup gp to ntdll's st4 [t2] = zero, ErExceptionAddress - ErExceptionFlags ;; st4 [t1] = zero, ErNumberParameters - ErExceptionRecord add out0 = STACK_SCRATCH_AREA, sp ;; // // set exception record and exception address // st4 [t1] = zero // set number of parameters STPTR(t2, t3) br.call.sptk.many brp = RtlRaiseException add t1 = ExceptionRecordLength+STACK_SCRATCH_AREA, sp add t2 = ExceptionRecordLength+STACK_SCRATCH_AREA+8, sp mov v0 = loc0 ;; ld8.nta t3 = [t1] ld8.nta t4 = [t2] ;; mov brp = t3 .restore tg20 [tg20:] add sp = ExceptionRecordLength, sp mov ar.pfs = t4 br.ret.sptk.clr brp NESTED_EXIT(KiRaiseUserExceptionDispatcher)