/*++ Copyright (c) 1994 Microsoft Corporation Module Name: callback.c Abstract: This module implements user mode call back services. Author: William K. Cheung (wcheung) 30-Oct-1995 based on David N. Cutler (davec) 29-Oct-1994 Environment: Kernel mode only. Revision History: --*/ #include "ki.h" #ifdef ALLOC_PRAGMA #pragma alloc_text (PAGE, KeUserModeCallback) #pragma alloc_text (PAGE, NtW32Call) #endif NTSTATUS KeUserModeCallback ( IN ULONG ApiNumber, IN PVOID InputBuffer, IN ULONG InputLength, OUT PVOID *OutputBuffer, IN PULONG OutputLength ) /*++ Routine Description: This function call out from kernel mode to a user mode function. Arguments: ApiNumber - Supplies the API number. InputBuffer - Supplies a pointer to a structure that is copied to the user stack. InputLength - Supplies the length of the input structure. Outputbuffer - Supplies a pointer to a variable that receives the address of the output buffer. Outputlength - Supplies a pointer to a variable that receives the length of the output buffer. Return Value: If the callout cannot be executed, then an error status is returned. Otherwise, the status returned by the callback function is returned. --*/ { PUCALLOUT_FRAME CalloutFrame; ULONGLONG OldStack; ULONGLONG NewStack; ULONGLONG OldRsPFS; IA64_PFS OldStIFS; PKTRAP_FRAME TrapFrame; NTSTATUS Status; ULONG GdiBatchCount; ULONG Length; ASSERT(KeGetPreviousMode() == UserMode); ASSERT(KeGetCurrentThread()->ApcState.KernelApcInProgress == FALSE); // // Get the user mode stack pointer and attempt to copy input buffer // to the user stack. // TrapFrame = KeGetCurrentThread()->TrapFrame; OldStack = TrapFrame->IntSp; OldRsPFS = TrapFrame->RsPFS; OldStIFS.ull = TrapFrame->StIFS; try { // // Compute new user mode stack address, probe for writability, // and copy the input buffer to the user stack. // // N.B. EM requires stacks to be 16-byte aligned, therefore // the input length must be rounded up to a 16-byte boundary. // Length = (InputLength + 16 - 1 + sizeof(UCALLOUT_FRAME) + STACK_SCRATCH_AREA) & ~(16 - 1); NewStack = OldStack - Length; CalloutFrame = (PUCALLOUT_FRAME)(NewStack + STACK_SCRATCH_AREA); ProbeForWrite((PVOID)NewStack, Length, sizeof(QUAD)); RtlCopyMemory(CalloutFrame + 1, InputBuffer, InputLength); // // Fill in the callout arguments. // CalloutFrame->Buffer = (PVOID)(CalloutFrame + 1); CalloutFrame->Length = InputLength; CalloutFrame->ApiNumber = ApiNumber; CalloutFrame->IntSp = OldStack; CalloutFrame->RsPFS = TrapFrame->RsPFS; CalloutFrame->BrRp = TrapFrame->BrRp; // // Always flush out the user RSE so the debugger and // unwinding work across the call out. // KeFlushUserRseState (TrapFrame); // // If an exception occurs during the probe of the user stack, then // always handle the exception and return the exception code as the // status value. // } except (EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // // Set the PFS and IFS to be equal to the number of // output registers call the function that called the // system service. This makes it look like that function // called call back. // TrapFrame->RsPFS = (ULONGLONG) 0xC000000000000000i64 | (OldStIFS.sb.pfs_sof - OldStIFS.sb.pfs_sol); TrapFrame->StIFS = (ULONGLONG) 0x8000000000000000i64 | (OldStIFS.sb.pfs_sof - OldStIFS.sb.pfs_sol); // // Set the user stack. // TrapFrame->IntSp = NewStack; // // Call user mode. // Status = KiCallUserMode(OutputBuffer, OutputLength); // // When returning from user mode, any drawing done to the GDI TEB // batch must be flushed. If the TEB cannot be accessed then blindly // flush the GDI batch anyway. // GdiBatchCount = 1; try { GdiBatchCount = ((PTEB)KeGetCurrentThread()->Teb)->GdiBatchCount; } except (EXCEPTION_EXECUTE_HANDLER) { NOTHING; } if (GdiBatchCount > 0) { // // Some of the call back functions store a return values in the // stack. The batch flush routine can sometimes overwrite these // values causing failures. Add some slop in the stack to // preserve these values. // TrapFrame->IntSp -= 256; // // call GDI batch flush routine // KeGdiFlushUserBatch(); } TrapFrame->IntSp = OldStack; TrapFrame->RsPFS = OldRsPFS; TrapFrame->StIFS = OldStIFS.ull; return Status; } NTSTATUS NtW32Call ( IN ULONG ApiNumber, IN PVOID InputBuffer, IN ULONG InputLength, OUT PVOID *OutputBuffer, OUT PULONG OutputLength ) /*++ Routine Description: This function calls a W32 function. N.B. ************** This is a temporary service ***************** Arguments: ApiNumber - Supplies the API number. InputBuffer - Supplies a pointer to a structure that is copied to the user stack. InputLength - Supplies the length of the input structure. Outputbuffer - Supplies a pointer to a variable that recevies the output buffer address. Outputlength - Supplies a pointer to a variable that recevies the output buffer length. Return Value: TBS. --*/ { PVOID ValueBuffer; ULONG ValueLength; NTSTATUS Status; ASSERT(KeGetPreviousMode() == UserMode); // // If the current thread is not a GUI thread, then fail the service // since the thread does not have a large stack. // if (KeGetCurrentThread()->Win32Thread == (PVOID)&KeServiceDescriptorTable[0]) { return STATUS_NOT_IMPLEMENTED; } // // Probe the output buffer address and length for writeability. // try { ProbeForWriteUlong((PULONG)OutputBuffer); ProbeForWriteUlong(OutputLength); // // If an exception occurs during the probe of the output buffer or // length, then always handle the exception and return the exception // code as the status value. // } except(EXCEPTION_EXECUTE_HANDLER) { return GetExceptionCode(); } // // Call out to user mode specifying the input buffer and API number. // Status = KeUserModeCallback(ApiNumber, InputBuffer, InputLength, &ValueBuffer, &ValueLength); // // If the callout is successful, then the output buffer address and // length. // if (NT_SUCCESS(Status)) { try { *OutputBuffer = ValueBuffer; *OutputLength = ValueLength; } except(EXCEPTION_EXECUTE_HANDLER) { } } return Status; } VOID KiTestGdiBatchCount ( ) /*++ Routine Description: This function checks the GdiBatchCount and calls KeGdiFlushUserBatch if necessary. Arguments: None. Return Value: None. --*/ { ULONG GdiBatchCount = 1; try { GdiBatchCount = ((PTEB)KeGetCurrentThread()->Teb)->GdiBatchCount; } except (EXCEPTION_EXECUTE_HANDLER) { NOTHING; } if (GdiBatchCount > 0) { KeGdiFlushUserBatch(); } }