Author: ion
Date: Mon Jul 17 18:30:53 2006
New Revision: 23124
URL:
http://svn.reactos.org/svn/reactos?rev=23124&view=rev
Log:
- 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).
Modified:
trunk/reactos/ntoskrnl/ps/debug.c (contents, props changed)
Modified: trunk/reactos/ntoskrnl/ps/debug.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/ps/debug.c?rev=23…
==============================================================================
--- trunk/reactos/ntoskrnl/ps/debug.c (original)
+++ trunk/reactos/ntoskrnl/ps/debug.c Mon Jul 17 18:30:53 2006
@@ -1,13 +1,11 @@
/*
- * 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 (welch(a)mcmail.com)
- * Phillip Susi
+ * PURPOSE: Process Manager: Debugging Support (Set/Get Context)
+ * PROGRAMMERS: Alex Ionescu (alex.ionescu(a)reactos.org)
+ * Thomas Weidenmueller (w3seek(a)reactos.org)
*/
-
/* INCLUDES ****************************************************************/
@@ -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;
+ KPROCESSOR_MODE Mode;
CONTEXT Context;
- KPROCESSOR_MODE Mode;
- NTSTATUS Status;
} 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 @@
}
#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 */
Propchange: trunk/reactos/ntoskrnl/ps/debug.c
------------------------------------------------------------------------------
--- svn:needs-lock (original)
+++ svn:needs-lock (removed)
@@ -1,1 +1,0 @@
-*