Reactos
1/*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ps/debug.c
5 * PURPOSE: Process Manager: Debugging Support (Set/Get Context)
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Thomas Weidenmueller (w3seek@reactos.org)
8 */
9
10/* INCLUDES ****************************************************************/
11
12#include <ntoskrnl.h>
13#define NDEBUG
14#include <debug.h>
15
16/* PRIVATE FUNCTIONS *********************************************************/
17
18#if DBG
19VOID
20NTAPI
21PspDumpThreads(BOOLEAN IncludeSystem)
22{
23 PLIST_ENTRY CurrentThread, CurrentProcess;
24 PEPROCESS Process;
25 PETHREAD Thread;
26 ULONG nThreads = 0;
27
28 /* Loop all Active Processes */
29 CurrentProcess = PsActiveProcessHead.Flink;
30 while(CurrentProcess != &PsActiveProcessHead)
31 {
32 /* Get the process */
33 Process = CONTAINING_RECORD(CurrentProcess, EPROCESS, ActiveProcessLinks);
34
35 /* Skip the Initial Process if requested */
36 if((Process != PsInitialSystemProcess) ||
37 (Process == PsInitialSystemProcess && IncludeSystem))
38 {
39 /* Loop all its threads */
40 CurrentThread = Process->ThreadListHead.Flink;
41 while(CurrentThread != &Process->ThreadListHead)
42 {
43
44 /* Get teh Thread */
45 Thread = CONTAINING_RECORD(CurrentThread, ETHREAD, ThreadListEntry);
46 nThreads++;
47
48 /* Print the Info */
49 DbgPrint("State %u Affinity %08x Priority %d PID.TID %d.%d Name %.8s Stack:\n",
50 Thread->Tcb.State,
51 Thread->Tcb.Affinity,
52 Thread->Tcb.Priority,
53 Thread->Cid.UniqueProcess,
54 Thread->Cid.UniqueThread,
55 Thread->ThreadsProcess->ImageFileName);
56
57 /* Make sure it's not running */
58 if(Thread->Tcb.State == Ready ||
59 Thread->Tcb.State == Standby ||
60 Thread->Tcb.State == Waiting)
61 {
62#ifdef _M_IX86
63 ULONG i = 0;
64 PULONG Esp = (PULONG)Thread->Tcb.KernelStack;
65 PULONG Ebp = (PULONG)Esp[4];
66
67 /* Print EBP */
68 DbgPrint("Ebp %p\n", Ebp);
69
70 /* Walk it */
71 while(Ebp != 0 && Ebp >= (PULONG)Thread->Tcb.StackLimit)
72 {
73 ULONG EbpContent[2];
74 ULONG MemoryCopied;
75 NTSTATUS Status;
76
77 /* Get stack frame content */
78 Status = KdpCopyMemoryChunks((ULONG64)(ULONG_PTR)Ebp,
79 EbpContent,
80 sizeof(EbpContent),
81 sizeof(EbpContent),
82 MMDBG_COPY_UNSAFE,
83 &MemoryCopied);
84 if (!NT_SUCCESS(Status) || (MemoryCopied < sizeof(EbpContent)))
85 {
86 break;
87 }
88
89 DbgPrint("%.8X %.8X%s", EbpContent[0], EbpContent[1], (i % 8) == 7 ? "\n" : " ");
90 Ebp = (PULONG)EbpContent[0];
91 i++;
92 }
93
94 /* Print a new line if there's nothing */
95 if((i % 8) != 0) DbgPrint("\n");
96#else
97 DbgPrint("FIXME: Backtrace skipped on non-x86\n");
98#endif
99 }
100
101 /* Move to the next Thread */
102 CurrentThread = CurrentThread->Flink;
103 }
104 }
105
106 /* Move to the next Process */
107 CurrentProcess = CurrentProcess->Flink;
108 }
109}
110#endif
111
112/* PUBLIC FUNCTIONS **********************************************************/
113
114/*
115 * @implemented
116 */
117NTSTATUS
118NTAPI
119PsGetContextThread(IN PETHREAD Thread,
120 IN OUT PCONTEXT ThreadContext,
121 IN KPROCESSOR_MODE PreviousMode)
122{
123 GET_SET_CTX_CONTEXT GetSetContext;
124 ULONG Size = 0, Flags = 0;
125 NTSTATUS Status;
126
127 /* Enter SEH */
128 _SEH2_TRY
129 {
130 /* Set default length */
131 Size = sizeof(CONTEXT);
132
133 /* Read the flags */
134 Flags = ProbeForReadUlong(&ThreadContext->ContextFlags);
135
136#ifdef _M_IX86
137 /* Check if the caller wanted extended registers */
138 if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
139 CONTEXT_EXTENDED_REGISTERS)
140 {
141 /* Cut them out of the size */
142 Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
143 }
144#endif
145
146 /* Check if we came from user mode */
147 if (PreviousMode != KernelMode)
148 {
149 /* Probe the context */
150 ProbeForWrite(ThreadContext, Size, sizeof(ULONG));
151 }
152 }
153 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
154 {
155 /* Return the exception code */
156 _SEH2_YIELD(return _SEH2_GetExceptionCode());
157 }
158 _SEH2_END;
159
160 /* Initialize the wait event */
161 KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
162
163 /* Set the flags and previous mode */
164 RtlZeroMemory(&GetSetContext.Context, Size);
165 GetSetContext.Context.ContextFlags = Flags;
166 GetSetContext.Mode = PreviousMode;
167
168 /* Check if we're running in the same thread */
169 if (Thread == PsGetCurrentThread())
170 {
171 /* Setup APC parameters manually */
172 GetSetContext.Apc.SystemArgument1 = NULL;
173 GetSetContext.Apc.SystemArgument2 = Thread;
174
175 /* Enter a guarded region to simulate APC_LEVEL */
176 KeEnterGuardedRegion();
177
178 /* Manually call the APC */
179 PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
180 NULL,
181 NULL,
182 &GetSetContext.Apc.SystemArgument1,
183 &GetSetContext.Apc.SystemArgument2);
184
185 /* Leave the guarded region */
186 KeLeaveGuardedRegion();
187
188 /* We are done */
189 Status = STATUS_SUCCESS;
190 }
191 else
192 {
193 /* Initialize the APC */
194 KeInitializeApc(&GetSetContext.Apc,
195 &Thread->Tcb,
196 OriginalApcEnvironment,
197 PspGetOrSetContextKernelRoutine,
198 NULL,
199 NULL,
200 KernelMode,
201 NULL);
202
203 /* Queue it as a Get APC */
204 if (!KeInsertQueueApc(&GetSetContext.Apc, NULL, Thread, 2))
205 {
206 /* It was already queued, so fail */
207 Status = STATUS_UNSUCCESSFUL;
208 }
209 else
210 {
211 /* Wait for the APC to complete */
212 Status = KeWaitForSingleObject(&GetSetContext.Event,
213 0,
214 KernelMode,
215 FALSE,
216 NULL);
217 }
218 }
219
220 _SEH2_TRY
221 {
222 /* Copy the context */
223 RtlCopyMemory(ThreadContext, &GetSetContext.Context, Size);
224 }
225 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
226 {
227 /* Get the exception code */
228 Status = _SEH2_GetExceptionCode();
229 }
230 _SEH2_END;
231
232 /* Return status */
233 return Status;
234}
235
236/*
237 * @implemented
238 */
239NTSTATUS
240NTAPI
241PsSetContextThread(IN PETHREAD Thread,
242 IN OUT PCONTEXT ThreadContext,
243 IN KPROCESSOR_MODE PreviousMode)
244{
245 GET_SET_CTX_CONTEXT GetSetContext;
246 ULONG Size = 0, Flags = 0;
247 NTSTATUS Status;
248
249 /* Enter SEH */
250 _SEH2_TRY
251 {
252 /* Set default length */
253 Size = sizeof(CONTEXT);
254
255 /* Read the flags */
256 Flags = ProbeForReadUlong(&ThreadContext->ContextFlags);
257
258#ifdef _M_IX86
259 /* Check if the caller wanted extended registers */
260 if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
261 CONTEXT_EXTENDED_REGISTERS)
262 {
263 /* Cut them out of the size */
264 Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
265 }
266#endif
267
268 /* Check if we came from user mode */
269 if (PreviousMode != KernelMode)
270 {
271 /* Probe the context */
272 ProbeForRead(ThreadContext, Size, sizeof(ULONG));
273 }
274
275 /* Copy the context */
276 RtlCopyMemory(&GetSetContext.Context, ThreadContext, Size);
277 }
278 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
279 {
280 /* Return the exception code */
281 _SEH2_YIELD(return _SEH2_GetExceptionCode());
282 }
283 _SEH2_END;
284
285 /* Initialize the wait event */
286 KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
287
288 /* Set the flags and previous mode */
289 GetSetContext.Context.ContextFlags = Flags;
290 GetSetContext.Mode = PreviousMode;
291
292 /* Check if we're running in the same thread */
293 if (Thread == PsGetCurrentThread())
294 {
295 /* Setup APC parameters manually */
296 GetSetContext.Apc.SystemArgument1 = UlongToPtr(1);
297 GetSetContext.Apc.SystemArgument2 = Thread;
298
299 /* Enter a guarded region to simulate APC_LEVEL */
300 KeEnterGuardedRegion();
301
302 /* Manually call the APC */
303 PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
304 NULL,
305 NULL,
306 &GetSetContext.Apc.SystemArgument1,
307 &GetSetContext.Apc.SystemArgument2);
308
309 /* Leave the guarded region */
310 KeLeaveGuardedRegion();
311
312 /* We are done */
313 Status = STATUS_SUCCESS;
314 }
315 else
316 {
317 /* Initialize the APC */
318 KeInitializeApc(&GetSetContext.Apc,
319 &Thread->Tcb,
320 OriginalApcEnvironment,
321 PspGetOrSetContextKernelRoutine,
322 NULL,
323 NULL,
324 KernelMode,
325 NULL);
326
327 /* Queue it as a Get APC */
328 if (!KeInsertQueueApc(&GetSetContext.Apc, UlongToPtr(1), Thread, 2))
329 {
330 /* It was already queued, so fail */
331 Status = STATUS_UNSUCCESSFUL;
332 }
333 else
334 {
335 /* Wait for the APC to complete */
336 Status = KeWaitForSingleObject(&GetSetContext.Event,
337 0,
338 KernelMode,
339 FALSE,
340 NULL);
341 }
342 }
343
344 /* Return status */
345 return Status;
346}
347
348NTSTATUS
349NTAPI
350NtGetContextThread(IN HANDLE ThreadHandle,
351 IN OUT PCONTEXT ThreadContext)
352{
353 PETHREAD Thread;
354 NTSTATUS Status;
355 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
356 PAGED_CODE();
357
358 /* Get the Thread Object */
359 Status = ObReferenceObjectByHandle(ThreadHandle,
360 THREAD_GET_CONTEXT,
361 PsThreadType,
362 PreviousMode,
363 (PVOID*)&Thread,
364 NULL);
365
366 if (!NT_SUCCESS(Status)) return Status;
367
368 /* Make sure it's not a system thread */
369 if (Thread->SystemThread)
370 {
371 /* Fail */
372 Status = STATUS_INVALID_HANDLE;
373 }
374 else
375 {
376 /* Call the kernel API */
377 Status = PsGetContextThread(Thread, ThreadContext, PreviousMode);
378 }
379
380 /* Dereference it and return */
381 ObDereferenceObject(Thread);
382 return Status;
383}
384
385NTSTATUS
386NTAPI
387NtSetContextThread(IN HANDLE ThreadHandle,
388 IN PCONTEXT ThreadContext)
389{
390 PETHREAD Thread;
391 NTSTATUS Status;
392 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
393 PAGED_CODE();
394
395 /* Get the Thread Object */
396 Status = ObReferenceObjectByHandle(ThreadHandle,
397 THREAD_SET_CONTEXT,
398 PsThreadType,
399 PreviousMode,
400 (PVOID*)&Thread,
401 NULL);
402
403 if (!NT_SUCCESS(Status)) return Status;
404
405 /* Make sure it's not a system thread */
406 if (Thread->SystemThread)
407 {
408 /* Fail */
409 Status = STATUS_INVALID_HANDLE;
410 }
411 else
412 {
413 /* Call the kernel API */
414 Status = PsSetContextThread(Thread, ThreadContext, PreviousMode);
415 }
416
417 /* Dereference it and return */
418 ObDereferenceObject(Thread);
419 return Status;
420}
421
422/* EOF */