/*++ Copyright (c) 1990 Microsoft Corporation Module Name: apcuser.c Abstract: This module implements the machine dependent code necessary to initialize a user mode APC. Author: David N. Cutler (davec) 23-Apr-1990 Environment: Kernel mode only, IRQL APC_LEVEL. Revision History: --*/ #include "ki.h" 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. --*/ { EXCEPTION_RECORD ExceptionRecord; CONTEXT ContextFrame; LONG Length; ULONG UserStack; // // APCs are not defined for V86 mode; however, it is possible a // thread is trying to set it's context to V86 mode - this isn't // going to work, but we don't want to crash the system so we // check for the possibility before hand. // if (TrapFrame->EFlags & EFLAGS_V86_MASK) { return ; } // // Move machine state from trap and exception frames to the context frame. // ContextFrame.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS; KeContextFromKframes(TrapFrame, ExceptionFrame, &ContextFrame); // // 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. // try { ASSERT((TrapFrame->SegCs & MODE_MASK) != KernelMode); // Assert usermode frame // // Compute length of context record and new aligned user stack pointer. // Length = ((sizeof(CONTEXT) + CONTEXT_ROUND) & ~CONTEXT_ROUND) + sizeof(KAPC_RECORD); UserStack = (ContextFrame.Esp & ~CONTEXT_ROUND) - Length; // // Probe user stack area for writability and then transfer the // context record to the user stack. // ProbeForWrite((PCHAR)UserStack, Length, CONTEXT_ALIGN); RtlCopyMemory((PULONG)(UserStack + (sizeof(KAPC_RECORD))), &ContextFrame, sizeof(CONTEXT)); // // Force correct R3 selectors into TrapFrame. // TrapFrame->SegCs = SANITIZE_SEG(KGDT_R3_CODE, UserMode); TrapFrame->HardwareSegSs = SANITIZE_SEG(KGDT_R3_DATA, UserMode); TrapFrame->SegDs = SANITIZE_SEG(KGDT_R3_DATA, UserMode); TrapFrame->SegEs = SANITIZE_SEG(KGDT_R3_DATA, UserMode); TrapFrame->SegFs = SANITIZE_SEG(KGDT_R3_TEB, UserMode); TrapFrame->SegGs = 0; TrapFrame->EFlags = SANITIZE_FLAGS( ContextFrame.EFlags, UserMode ); // // If thread is supposed to have IOPL, then force it on in eflags // if (KeGetCurrentThread()->Iopl) { TrapFrame->EFlags |= (EFLAGS_IOPL_MASK & -1); // IOPL = 3 } // // Set the address of the user APC routine, the APC parameters, the // new frame pointer, and the new stack pointer in the current trap // frame. Set the continuation address so control will be transferred // to the user APC dispatcher. // TrapFrame->HardwareEsp = UserStack; TrapFrame->Eip = (ULONG)KeUserApcDispatcher; TrapFrame->ErrCode = 0; *((PULONG)UserStack) = (ULONG)NormalRoutine; UserStack += sizeof (ULONG); *((PULONG)UserStack) = (ULONG)NormalContext; UserStack += sizeof (ULONG); *((PULONG)UserStack) = (ULONG)SystemArgument1; UserStack += sizeof (ULONG); *((PULONG)UserStack) = (ULONG)SystemArgument2; UserStack += sizeof (ULONG); } 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->Eip); KiDispatchException(&ExceptionRecord, ExceptionFrame, TrapFrame, UserMode, TRUE); } return; }