You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
335 lines
7.7 KiB
335 lines
7.7 KiB
/*++
|
|
|
|
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();
|
|
}
|
|
}
|