windows-nt/Source/XPSP1/NT/base/ntos/rtl/ia64/trampoln.s
2020-09-26 16:20:57 +08:00

598 lines
16 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.

//++
//
// 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)