/*++ Module Name: apcuser.c Abstract: This module implements the machine dependent code necessary to initialize a user mode APC. Author: William K. Cheung 26-Oct-1995 based on MIPS version by David N. Cutler (davec) 23-Apr-1990 Environment: Kernel mode only, IRQL APC_LEVEL. Revision History: --*/ #include "ki.h" #include "kxia64.h" VOID KiSaveHigherFPVolatile ( PVOID ); VOID KiInitializeUserApc ( IN PKEXCEPTION_FRAME ExceptionFrame, IN PKTRAP_FRAME TrapFrame, IN PKNORMAL_ROUTINE NormalRoutine, IN PVOID NormalContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This function is called to initialize the context for a user mode APC. Arguments: ExceptionFrame - Supplies a pointer to an exception frame. TrapFrame - Supplies a pointer to a trap frame. NormalRoutine - Supplies a pointer to the user mode APC routine. NormalContext - Supplies a pointer to the user context for the APC routine. SystemArgument1 - Supplies the first system supplied value. SystemArgument2 - Supplies the second system supplied value. Return Value: None. --*/ { CONTEXT ContextRecord; EXCEPTION_RECORD ExceptionRecord; LONG Length; ULONGLONG UserStack; PULONGLONG Arguments; // // Move the user mode state from the trap and exception frames to the // context frame. Do not caputere the upper floating point registers. // C_ASSERT((CONTEXT_CONTROL | CONTEXT_LOWER_FLOATING_POINT | CONTEXT_INTEGER | CONTEXT_IA32_CONTROL | CONTEXT_HIGHER_FLOATING_POINT) == CONTEXT_FULL); ContextRecord.ContextFlags = CONTEXT_CONTROL | CONTEXT_LOWER_FLOATING_POINT | CONTEXT_INTEGER | CONTEXT_IA32_CONTROL; // // Push the user RSE state back out to user mode. // KeFlushUserRseState (TrapFrame); KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextRecord); // // Transfer the context information to the user stack, initialize the // APC routine parameters, and modify the trap frame so execution will // continue in user mode at the user mode APC dispatch routine. // // We build the following structure on the user stack: // // | | // |-------------------------------| // | | // | Interrupted user's | // | stack frame | // | | // | | // |-------------------------------| // | Slack Space due to the | // | 16-byte stack alignment | // | - - - - - - - - - - - - - - - | // | NormalRoutine | // | SystemArgument2 | // | SystemArgument1 | // | NormalContext | // | - - - - - - - - - - - - - - - | // | Context Frame | // | Filled in with state | // | of interrupted user | // | program | // | - - - - - - - - - - - - - - - | // | Stack Scratch Area | // |-------------------------------| // | | try { PPLABEL_DESCRIPTOR Plabel = (PPLABEL_DESCRIPTOR) KeUserApcDispatcher; // // Compute total length of 4 arguments, context record, and // stack scratch area. // // Compute the new 16-byte aligned user stack pointer. // Length = (4 * sizeof(ULONGLONG) + CONTEXT_LENGTH + STACK_SCRATCH_AREA + 15) & (~15); UserStack = (ContextRecord.IntSp & (~15)) - Length; Arguments = (PULONGLONG)(UserStack + STACK_SCRATCH_AREA + CONTEXT_LENGTH); // // Probe user stack area for writeability and then transfer the // context record to the user stack. // ProbeForWriteSmallStructure((PCHAR)UserStack, Length, sizeof(QUAD)); RtlCopyMemory((PVOID)(UserStack+STACK_SCRATCH_AREA), &ContextRecord, FIELD_OFFSET(CONTEXT, FltF32)); RtlCopyMemory((PVOID)(UserStack + STACK_SCRATCH_AREA + FIELD_OFFSET(CONTEXT, StFPSR)), &ContextRecord.StFPSR, sizeof(CONTEXT) - FIELD_OFFSET(CONTEXT, StFPSR)); // // Set the address of the user APC routine, the APC parameters, the // interrupt frame set, the new global pointer, and the new stack // pointer in the current trap frame. The four APC parameters are // passed via the scratch registers t0 thru t3. // Set the continuation address so control will be transfered to // the user APC dispatcher. // *Arguments++ = (ULONGLONG)NormalContext; // 1st argument *Arguments++ = (ULONGLONG)SystemArgument1; // 2nd argument *Arguments++ = (ULONGLONG)SystemArgument2; // 3rd argument *Arguments++ = (ULONGLONG)NormalRoutine; // 4th argument *(PULONGLONG)UserStack = Plabel->GlobalPointer; // user apc dispatcher gp TrapFrame->IntNats = 0; // sanitize integer Nats TrapFrame->IntSp = UserStack; // stack pointer TrapFrame->StIIP = Plabel->EntryPoint; // entry point from plabel TrapFrame->StIPSR &= ~(0x3ULL << PSR_RI); // start at bundle boundary TrapFrame->RsPFS &= 0xffffffc000000000i64; // set the initial frame TrapFrame->StIFS &= 0xffffffc000000000i64; // set the initial frame // size of KeUserApcDispatcher // to be zero. TrapFrame->StFPSR = USER_FPSR_INITIAL; // // If an exception occurs, then copy the exception information to an // exception record and handle the exception. // } except (KiCopyInformation(&ExceptionRecord, (GetExceptionInformation())->ExceptionRecord)) { // // Lower the IRQL to PASSIVE_LEVEL, set the exception address to // the current program address, and raise an exception by calling // the exception dispatcher. // // N.B. The IRQL is lowered to PASSIVE_LEVEL to allow APC interrupts // during the dispatching of the exception. The current thread // will be terminated during the dispatching of the exception, // but lowering of the IRQL is required to enable the debugger // to obtain the context of the current thread. // KeLowerIrql(PASSIVE_LEVEL); ExceptionRecord.ExceptionAddress = (PVOID)(TrapFrame->StIIP); KiDispatchException(&ExceptionRecord, ExceptionFrame, TrapFrame, UserMode, TRUE); } return; }