- Factor out NtSet/GetContextThread implementation into PsGet/SetContexThread, new exported kernel APIs available to drivers, and export them.

- If the caller didn't request extended registers, don't probe the full CONTEXT structure.
- Use the proper trap frame in the kernel APC responsible for the context read/write.
- Always go through the APC instead of directly calling the Ke routine.
- Cleanup the code.
[AUDIT] - Mark file as clean. The NT APIs are stubs around the Ps APIs which simply setup an APC and stack context to an APC function that is again, merely a wrapper around the Ke function (which is not in this file).

svn path=/trunk/; revision=23124
This commit is contained in:
Alex Ionescu
2006-07-17 14:30:53 +00:00
parent e1a8b7eeff
commit 1ee9504d8d
+354 -287
View File
@@ -1,14 +1,12 @@
/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
* PROJECT: ReactOS Kernel
* LICENSE: GPL - See COPYING in the top level directory
* FILE: ntoskrnl/ps/debug.c
* PURPOSE: Thread managment
*
* PROGRAMMERS: David Welch ([email protected])
* Phillip Susi
* PURPOSE: Process Manager: Debugging Support (Set/Get Context)
* PROGRAMMERS: Alex Ionescu ([email protected])
* Thomas Weidenmueller ([email protected])
*/
/* INCLUDES ****************************************************************/
#include <ntoskrnl.h>
@@ -18,291 +16,15 @@
/* GLOBALS *****************************************************************/
/* Thread "Set/Get Context" Context Structure */
typedef struct _GET_SET_CTX_CONTEXT {
typedef struct _GET_SET_CTX_CONTEXT
{
KAPC Apc;
KEVENT Event;
CONTEXT Context;
KPROCESSOR_MODE Mode;
NTSTATUS Status;
CONTEXT Context;
} GET_SET_CTX_CONTEXT, *PGET_SET_CTX_CONTEXT;
/* FUNCTIONS ***************************************************************/
/*
* FUNCTION: This routine is called by an APC sent by NtGetContextThread to
* copy the context of a thread into a buffer.
*/
VOID
STDCALL
PspGetOrSetContextKernelRoutine(PKAPC Apc,
PKNORMAL_ROUTINE* NormalRoutine,
PVOID* NormalContext,
PVOID* SystemArgument1,
PVOID* SystemArgument2)
{
PGET_SET_CTX_CONTEXT GetSetContext;
PKEVENT Event;
PCONTEXT Context;
KPROCESSOR_MODE Mode;
PKTRAP_FRAME TrapFrame;
TrapFrame = (PKTRAP_FRAME)((ULONG_PTR)KeGetCurrentThread()->InitialStack -
sizeof (FX_SAVE_AREA) - sizeof (KTRAP_FRAME));
/* Get the Context Structure */
GetSetContext = CONTAINING_RECORD(Apc, GET_SET_CTX_CONTEXT, Apc);
Context = &GetSetContext->Context;
Event = &GetSetContext->Event;
Mode = GetSetContext->Mode;
if (TrapFrame->SegCs == KGDT_R0_CODE && Mode != KernelMode)
{
GetSetContext->Status = STATUS_ACCESS_DENIED;
}
else
{
/* Check if it's a set or get */
if (*SystemArgument1) {
/* Get the Context */
KeTrapFrameToContext(TrapFrame, NULL, Context);
} else {
/* Set the Context */
KeContextToTrapFrame(Context, NULL, TrapFrame, Context->ContextFlags, Mode);
}
GetSetContext->Status = STATUS_SUCCESS;
}
/* Notify the Native API that we are done */
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
}
NTSTATUS
STDCALL
NtGetContextThread(IN HANDLE ThreadHandle,
IN OUT PCONTEXT ThreadContext)
{
PETHREAD Thread;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
GET_SET_CTX_CONTEXT GetSetContext;
NTSTATUS Status = STATUS_SUCCESS;
PCONTEXT SafeThreadContext = NULL;
PAGED_CODE();
/* Check the buffer to be OK */
if(PreviousMode != KernelMode) {
_SEH_TRY {
ProbeForWrite(ThreadContext,
sizeof(CONTEXT),
sizeof(ULONG));
GetSetContext.Context = *ThreadContext;
SafeThreadContext = &GetSetContext.Context;
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
if(!NT_SUCCESS(Status)) return Status;
} else {
SafeThreadContext = ThreadContext;
}
/* Get the Thread Object */
Status = ObReferenceObjectByHandle(ThreadHandle,
THREAD_GET_CONTEXT,
PsThreadType,
PreviousMode,
(PVOID*)&Thread,
NULL);
/* Check success */
if(NT_SUCCESS(Status)) {
/* Check if we're running in the same thread */
if(Thread == PsGetCurrentThread()) {
/*
* I don't know if trying to get your own context makes much
* sense but we can handle it more efficently.
*/
KeTrapFrameToContext(Thread->Tcb.TrapFrame, NULL, SafeThreadContext);
} else {
/* Copy context into GetSetContext if not already done */
if(PreviousMode == KernelMode) {
GetSetContext.Context = *ThreadContext;
SafeThreadContext = &GetSetContext.Context;
}
/* Use an APC... Initialize the Event */
KeInitializeEvent(&GetSetContext.Event,
NotificationEvent,
FALSE);
/* Set the previous mode */
GetSetContext.Mode = PreviousMode;
/* Initialize the APC */
KeInitializeApc(&GetSetContext.Apc,
&Thread->Tcb,
OriginalApcEnvironment,
PspGetOrSetContextKernelRoutine,
NULL,
NULL,
KernelMode,
NULL);
/* Queue it as a Get APC */
if (!KeInsertQueueApc(&GetSetContext.Apc,
(PVOID)1,
NULL,
IO_NO_INCREMENT)) {
Status = STATUS_THREAD_IS_TERMINATING;
} else {
/* Wait for the APC to complete */
Status = KeWaitForSingleObject(&GetSetContext.Event,
0,
KernelMode,
FALSE,
NULL);
if (NT_SUCCESS(Status))
Status = GetSetContext.Status;
}
}
/* Dereference the thread */
ObDereferenceObject(Thread);
/* Check for success and return the Context */
if(NT_SUCCESS(Status) && SafeThreadContext != ThreadContext) {
_SEH_TRY {
*ThreadContext = GetSetContext.Context;
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
}
}
/* Return status */
return Status;
}
NTSTATUS
STDCALL
NtSetContextThread(IN HANDLE ThreadHandle,
IN PCONTEXT ThreadContext)
{
PETHREAD Thread;
GET_SET_CTX_CONTEXT GetSetContext;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
/* Check the buffer to be OK */
if(PreviousMode != KernelMode) {
_SEH_TRY {
ProbeForRead(ThreadContext,
sizeof(CONTEXT),
sizeof(ULONG));
GetSetContext.Context = *ThreadContext;
ThreadContext = &GetSetContext.Context;
} _SEH_HANDLE {
Status = _SEH_GetExceptionCode();
} _SEH_END;
if(!NT_SUCCESS(Status)) return Status;
}
/* Get the Thread Object */
Status = ObReferenceObjectByHandle(ThreadHandle,
THREAD_SET_CONTEXT,
PsThreadType,
PreviousMode,
(PVOID*)&Thread,
NULL);
/* Check success */
if(NT_SUCCESS(Status)) {
/* Check if we're running in the same thread */
if(Thread == PsGetCurrentThread()) {
/*
* I don't know if trying to set your own context makes much
* sense but we can handle it more efficently.
*/
KeContextToTrapFrame(ThreadContext, NULL, Thread->Tcb.TrapFrame, ThreadContext->ContextFlags, PreviousMode);
} else {
/* Copy context into GetSetContext if not already done */
if(PreviousMode == KernelMode)
GetSetContext.Context = *ThreadContext;
/* Use an APC... Initialize the Event */
KeInitializeEvent(&GetSetContext.Event,
NotificationEvent,
FALSE);
/* Set the previous mode */
GetSetContext.Mode = PreviousMode;
/* Initialize the APC */
KeInitializeApc(&GetSetContext.Apc,
&Thread->Tcb,
OriginalApcEnvironment,
PspGetOrSetContextKernelRoutine,
NULL,
NULL,
KernelMode,
NULL);
/* Queue it as a Get APC */
if (!KeInsertQueueApc(&GetSetContext.Apc,
(PVOID)0,
NULL,
IO_NO_INCREMENT)) {
Status = STATUS_THREAD_IS_TERMINATING;
} else {
/* Wait for the APC to complete */
Status = KeWaitForSingleObject(&GetSetContext.Event,
0,
KernelMode,
FALSE,
NULL);
if (NT_SUCCESS(Status))
Status = GetSetContext.Status;
}
}
/* Dereference the thread */
ObDereferenceObject(Thread);
}
/* Return status */
return Status;
}
/* PRIVATE FUNCTIONS *********************************************************/
#ifdef DBG
VOID
@@ -379,4 +101,349 @@ PspDumpThreads(BOOLEAN IncludeSystem)
}
#endif
VOID
NTAPI
PspGetOrSetContextKernelRoutine(IN PKAPC Apc,
IN OUT PKNORMAL_ROUTINE* NormalRoutine,
IN OUT PVOID* NormalContext,
IN OUT PVOID* SystemArgument1,
IN OUT PVOID* SystemArgument2)
{
PGET_SET_CTX_CONTEXT GetSetContext;
PKEVENT Event;
PCONTEXT Context;
PKTHREAD Thread;
KPROCESSOR_MODE Mode;
PKTRAP_FRAME TrapFrame;
PAGED_CODE();
/* Get the Context Structure */
GetSetContext = CONTAINING_RECORD(Apc, GET_SET_CTX_CONTEXT, Apc);
Context = &GetSetContext->Context;
Event = &GetSetContext->Event;
Mode = GetSetContext->Mode;
Thread = Apc->SystemArgument2;
/* Get the trap frame */
TrapFrame = (PKTRAP_FRAME)((ULONG_PTR)KeGetCurrentThread()->InitialStack -
sizeof (FX_SAVE_AREA) - sizeof (KTRAP_FRAME));
/* Sanity check */
ASSERT(((TrapFrame->SegCs & MODE_MASK) != KernelMode) ||
(TrapFrame->EFlags & EFLAGS_V86_MASK));
/* Check if it's a set or get */
if (SystemArgument1)
{
/* Get the Context */
KeTrapFrameToContext(TrapFrame, NULL, Context);
}
else
{
/* Set the Context */
KeContextToTrapFrame(Context,
NULL,
TrapFrame,
Context->ContextFlags,
Mode);
}
/* Notify the Native API that we are done */
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
}
/* PUBLIC FUNCTIONS **********************************************************/
/*
* @implemented
*/
NTSTATUS
NTAPI
PsGetContextThread(IN PETHREAD Thread,
IN OUT PCONTEXT ThreadContext,
IN KPROCESSOR_MODE PreviousMode)
{
GET_SET_CTX_CONTEXT GetSetContext;
ULONG Size = 0, Flags = 0;
NTSTATUS Status = STATUS_SUCCESS;
/* Enter SEH */
_SEH_TRY
{
/* Set default ength */
Size = sizeof(CONTEXT);
/* Read the flags */
ProbeForReadUlong(&ThreadContext->ContextFlags);
Flags = ThreadContext->ContextFlags;
/* Check if the caller wanted extended registers */
if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
CONTEXT_EXTENDED_REGISTERS)
{
/* Cut them out of the size */
Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
}
/* Check if we came from user mode */
if (PreviousMode != KernelMode)
{
/* Probe the context */
ProbeForWrite(ThreadContext, Size, sizeof(ULONG));
}
}
_SEH_HANDLE
{
/* Get exception code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
/* Check if we got success */
if (!NT_SUCCESS(Status)) return Status;
/* Initialize the wait event */
KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
/* Set the flags and previous mode */
GetSetContext.Context.ContextFlags = Flags;
GetSetContext.Mode = PreviousMode;
/* Check if we're running in the same thread */
if (Thread == PsGetCurrentThread())
{
/* Setup APC parameters manually */
GetSetContext.Apc.SystemArgument1 = NULL;
GetSetContext.Apc.SystemArgument2 = Thread;
/* Enter a guarded region to simulate APC_LEVEL */
KeEnterGuardedRegion();
/* Manually call the APC */
PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
NULL,
NULL,
&GetSetContext.Apc.SystemArgument1,
&GetSetContext.Apc.SystemArgument2);
/* Leave the guarded region */
KeLeaveGuardedRegion();
}
else
{
/* Initialize the APC */
KeInitializeApc(&GetSetContext.Apc,
&Thread->Tcb,
OriginalApcEnvironment,
PspGetOrSetContextKernelRoutine,
NULL,
NULL,
KernelMode,
NULL);
/* Queue it as a Get APC */
if (!KeInsertQueueApc(&GetSetContext.Apc, NULL, Thread, 2))
{
/* It was already queued, so fail */
Status = STATUS_UNSUCCESSFUL;
}
else
{
/* Wait for the APC to complete */
Status = KeWaitForSingleObject(&GetSetContext.Event,
0,
KernelMode,
FALSE,
NULL);
}
/* Copy the context */
RtlMoveMemory(ThreadContext, &GetSetContext.Context, Size);
}
/* Return status */
return Status;
}
/*
* @implemented
*/
NTSTATUS
NTAPI
PsSetContextThread(IN PETHREAD Thread,
IN OUT PCONTEXT ThreadContext,
IN KPROCESSOR_MODE PreviousMode)
{
GET_SET_CTX_CONTEXT GetSetContext;
ULONG Size = 0, Flags = 0;
NTSTATUS Status = STATUS_SUCCESS;
/* Enter SEH */
_SEH_TRY
{
/* Set default length */
Size = sizeof(CONTEXT);
/* Read the flags */
ProbeForReadUlong(&ThreadContext->ContextFlags);
Flags = ThreadContext->ContextFlags;
/* Check if the caller wanted extended registers */
if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
CONTEXT_EXTENDED_REGISTERS)
{
/* Cut them out of the size */
Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
}
/* Check if we came from user mode */
if (PreviousMode != KernelMode)
{
/* Probe the context */
ProbeForRead(ThreadContext, Size, sizeof(ULONG));
}
/* Copy the context */
RtlMoveMemory(&GetSetContext.Context, ThreadContext, Size);
}
_SEH_HANDLE
{
/* Get exception code */
Status = _SEH_GetExceptionCode();
}
_SEH_END;
/* Check if we got success */
if (!NT_SUCCESS(Status)) return Status;
/* Initialize the wait event */
KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
/* Set the flags and previous mode */
GetSetContext.Context.ContextFlags = Flags;
GetSetContext.Mode = PreviousMode;
/* Check if we're running in the same thread */
if (Thread == PsGetCurrentThread())
{
/* Setup APC parameters manually */
GetSetContext.Apc.SystemArgument1 = UlongToPtr(1);
GetSetContext.Apc.SystemArgument2 = Thread;
/* Enter a guarded region to simulate APC_LEVEL */
KeEnterGuardedRegion();
/* Manually call the APC */
PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
NULL,
NULL,
&GetSetContext.Apc.SystemArgument1,
&GetSetContext.Apc.SystemArgument2);
/* Leave the guarded region */
KeLeaveGuardedRegion();
}
else
{
/* Initialize the APC */
KeInitializeApc(&GetSetContext.Apc,
&Thread->Tcb,
OriginalApcEnvironment,
PspGetOrSetContextKernelRoutine,
NULL,
NULL,
KernelMode,
NULL);
/* Queue it as a Get APC */
if (!KeInsertQueueApc(&GetSetContext.Apc, UlongToPtr(1), Thread, 2))
{
/* It was already queued, so fail */
Status = STATUS_UNSUCCESSFUL;
}
else
{
/* Wait for the APC to complete */
Status = KeWaitForSingleObject(&GetSetContext.Event,
0,
KernelMode,
FALSE,
NULL);
}
}
/* Return status */
return Status;
}
NTSTATUS
NTAPI
NtGetContextThread(IN HANDLE ThreadHandle,
IN OUT PCONTEXT ThreadContext)
{
PETHREAD Thread;
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
PAGED_CODE();
/* Get the Thread Object */
Status = ObReferenceObjectByHandle(ThreadHandle,
THREAD_GET_CONTEXT,
PsThreadType,
PreviousMode,
(PVOID*)&Thread,
NULL);
/* Make sure it's not a system thread */
if (Thread->SystemThread)
{
/* Fail */
Status = STATUS_INVALID_HANDLE;
}
else
{
/* Call the kernel API */
Status = PsGetContextThread(Thread, ThreadContext, PreviousMode);
}
/* Dereference it and return */
ObDereferenceObject(Thread);
return Status;
}
NTSTATUS
NTAPI
NtSetContextThread(IN HANDLE ThreadHandle,
IN PCONTEXT ThreadContext)
{
PETHREAD Thread;
NTSTATUS Status;
KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
PAGED_CODE();
/* Get the Thread Object */
Status = ObReferenceObjectByHandle(ThreadHandle,
THREAD_SET_CONTEXT,
PsThreadType,
PreviousMode,
(PVOID*)&Thread,
NULL);
/* Make sure it's not a system thread */
if (Thread->SystemThread)
{
/* Fail */
Status = STATUS_INVALID_HANDLE;
}
else
{
/* Call the kernel API */
Status = PsSetContextThread(Thread, ThreadContext, PreviousMode);
}
/* Dereference it and return */
ObDereferenceObject(Thread);
return Status;
}
/* EOF */