Create a branch for Aleksandar Andrejevic for his work on NTVDM. See http://jira...
[reactos.git] / ntoskrnl / ps / debug.c
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
19 VOID
20 NTAPI
21 PspDumpThreads(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 %d 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 ULONG i = 0;
63 PULONG Esp = (PULONG)Thread->Tcb.KernelStack;
64 PULONG Ebp = (PULONG)Esp[4];
65
66 /* Print EBP */
67 DbgPrint("Ebp 0x%.8X\n", Ebp);
68
69 /* Walk it */
70 while(Ebp != 0 && Ebp >= (PULONG)Thread->Tcb.StackLimit)
71 {
72 /* Print what's on the stack */
73 DbgPrint("%.8X %.8X%s", Ebp[0], Ebp[1], (i % 8) == 7 ? "\n" : " ");
74 Ebp = (PULONG)Ebp[0];
75 i++;
76 }
77
78 /* Print a new line if there's nothing */
79 if((i % 8) != 0) DbgPrint("\n");
80 }
81
82 /* Move to the next Thread */
83 CurrentThread = CurrentThread->Flink;
84 }
85 }
86
87 /* Move to the next Process */
88 CurrentProcess = CurrentProcess->Flink;
89 }
90 }
91 #endif
92
93 /* PUBLIC FUNCTIONS **********************************************************/
94
95 /*
96 * @implemented
97 */
98 NTSTATUS
99 NTAPI
100 PsGetContextThread(IN PETHREAD Thread,
101 IN OUT PCONTEXT ThreadContext,
102 IN KPROCESSOR_MODE PreviousMode)
103 {
104 GET_SET_CTX_CONTEXT GetSetContext;
105 ULONG Size = 0, Flags = 0;
106 NTSTATUS Status;
107
108 /* Enter SEH */
109 _SEH2_TRY
110 {
111 /* Set default ength */
112 Size = sizeof(CONTEXT);
113
114 /* Read the flags */
115 Flags = ProbeForReadUlong(&ThreadContext->ContextFlags);
116
117 #ifdef _M_IX86
118 /* Check if the caller wanted extended registers */
119 if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
120 CONTEXT_EXTENDED_REGISTERS)
121 {
122 /* Cut them out of the size */
123 Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
124 }
125 #endif
126
127 /* Check if we came from user mode */
128 if (PreviousMode != KernelMode)
129 {
130 /* Probe the context */
131 ProbeForWrite(ThreadContext, Size, sizeof(ULONG));
132 }
133 }
134 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
135 {
136 /* Return the exception code */
137 _SEH2_YIELD(return _SEH2_GetExceptionCode());
138 }
139 _SEH2_END;
140
141 /* Initialize the wait event */
142 KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
143
144 /* Set the flags and previous mode */
145 GetSetContext.Context.ContextFlags = Flags;
146 GetSetContext.Mode = PreviousMode;
147
148 /* Check if we're running in the same thread */
149 if (Thread == PsGetCurrentThread())
150 {
151 /* Setup APC parameters manually */
152 GetSetContext.Apc.SystemArgument1 = NULL;
153 GetSetContext.Apc.SystemArgument2 = Thread;
154
155 /* Enter a guarded region to simulate APC_LEVEL */
156 KeEnterGuardedRegion();
157
158 /* Manually call the APC */
159 PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
160 NULL,
161 NULL,
162 &GetSetContext.Apc.SystemArgument1,
163 &GetSetContext.Apc.SystemArgument2);
164
165 /* Leave the guarded region */
166 KeLeaveGuardedRegion();
167
168 /* We are done */
169 Status = STATUS_SUCCESS;
170 }
171 else
172 {
173 /* Initialize the APC */
174 KeInitializeApc(&GetSetContext.Apc,
175 &Thread->Tcb,
176 OriginalApcEnvironment,
177 PspGetOrSetContextKernelRoutine,
178 NULL,
179 NULL,
180 KernelMode,
181 NULL);
182
183 /* Queue it as a Get APC */
184 if (!KeInsertQueueApc(&GetSetContext.Apc, NULL, Thread, 2))
185 {
186 /* It was already queued, so fail */
187 Status = STATUS_UNSUCCESSFUL;
188 }
189 else
190 {
191 /* Wait for the APC to complete */
192 Status = KeWaitForSingleObject(&GetSetContext.Event,
193 0,
194 KernelMode,
195 FALSE,
196 NULL);
197 }
198 }
199
200 _SEH2_TRY
201 {
202 /* Copy the context */
203 RtlCopyMemory(ThreadContext, &GetSetContext.Context, Size);
204 }
205 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
206 {
207 /* Get the exception code */
208 Status = _SEH2_GetExceptionCode();
209 }
210 _SEH2_END;
211
212 /* Return status */
213 return Status;
214 }
215
216 /*
217 * @implemented
218 */
219 NTSTATUS
220 NTAPI
221 PsSetContextThread(IN PETHREAD Thread,
222 IN OUT PCONTEXT ThreadContext,
223 IN KPROCESSOR_MODE PreviousMode)
224 {
225 GET_SET_CTX_CONTEXT GetSetContext;
226 ULONG Size = 0, Flags = 0;
227 NTSTATUS Status;
228
229 /* Enter SEH */
230 _SEH2_TRY
231 {
232 /* Set default length */
233 Size = sizeof(CONTEXT);
234
235 /* Read the flags */
236 Flags = ProbeForReadUlong(&ThreadContext->ContextFlags);
237
238 #ifdef _M_IX86
239 /* Check if the caller wanted extended registers */
240 if ((Flags & CONTEXT_EXTENDED_REGISTERS) !=
241 CONTEXT_EXTENDED_REGISTERS)
242 {
243 /* Cut them out of the size */
244 Size = FIELD_OFFSET(CONTEXT, ExtendedRegisters);
245 }
246 #endif
247
248 /* Check if we came from user mode */
249 if (PreviousMode != KernelMode)
250 {
251 /* Probe the context */
252 ProbeForRead(ThreadContext, Size, sizeof(ULONG));
253 }
254
255 /* Copy the context */
256 RtlCopyMemory(&GetSetContext.Context, ThreadContext, Size);
257 }
258 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
259 {
260 /* Return the exception code */
261 _SEH2_YIELD(return _SEH2_GetExceptionCode());
262 }
263 _SEH2_END;
264
265 /* Initialize the wait event */
266 KeInitializeEvent(&GetSetContext.Event, NotificationEvent, FALSE);
267
268 /* Set the flags and previous mode */
269 GetSetContext.Context.ContextFlags = Flags;
270 GetSetContext.Mode = PreviousMode;
271
272 /* Check if we're running in the same thread */
273 if (Thread == PsGetCurrentThread())
274 {
275 /* Setup APC parameters manually */
276 GetSetContext.Apc.SystemArgument1 = UlongToPtr(1);
277 GetSetContext.Apc.SystemArgument2 = Thread;
278
279 /* Enter a guarded region to simulate APC_LEVEL */
280 KeEnterGuardedRegion();
281
282 /* Manually call the APC */
283 PspGetOrSetContextKernelRoutine(&GetSetContext.Apc,
284 NULL,
285 NULL,
286 &GetSetContext.Apc.SystemArgument1,
287 &GetSetContext.Apc.SystemArgument2);
288
289 /* Leave the guarded region */
290 KeLeaveGuardedRegion();
291
292 /* We are done */
293 Status = STATUS_SUCCESS;
294 }
295 else
296 {
297 /* Initialize the APC */
298 KeInitializeApc(&GetSetContext.Apc,
299 &Thread->Tcb,
300 OriginalApcEnvironment,
301 PspGetOrSetContextKernelRoutine,
302 NULL,
303 NULL,
304 KernelMode,
305 NULL);
306
307 /* Queue it as a Get APC */
308 if (!KeInsertQueueApc(&GetSetContext.Apc, UlongToPtr(1), Thread, 2))
309 {
310 /* It was already queued, so fail */
311 Status = STATUS_UNSUCCESSFUL;
312 }
313 else
314 {
315 /* Wait for the APC to complete */
316 Status = KeWaitForSingleObject(&GetSetContext.Event,
317 0,
318 KernelMode,
319 FALSE,
320 NULL);
321 }
322 }
323
324 /* Return status */
325 return Status;
326 }
327
328 NTSTATUS
329 NTAPI
330 NtGetContextThread(IN HANDLE ThreadHandle,
331 IN OUT PCONTEXT ThreadContext)
332 {
333 PETHREAD Thread;
334 NTSTATUS Status;
335 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
336 PAGED_CODE();
337
338 /* Get the Thread Object */
339 Status = ObReferenceObjectByHandle(ThreadHandle,
340 THREAD_GET_CONTEXT,
341 PsThreadType,
342 PreviousMode,
343 (PVOID*)&Thread,
344 NULL);
345
346 if (!NT_SUCCESS(Status)) return Status;
347
348 /* Make sure it's not a system thread */
349 if (Thread->SystemThread)
350 {
351 /* Fail */
352 Status = STATUS_INVALID_HANDLE;
353 }
354 else
355 {
356 /* Call the kernel API */
357 Status = PsGetContextThread(Thread, ThreadContext, PreviousMode);
358 }
359
360 /* Dereference it and return */
361 ObDereferenceObject(Thread);
362 return Status;
363 }
364
365 NTSTATUS
366 NTAPI
367 NtSetContextThread(IN HANDLE ThreadHandle,
368 IN PCONTEXT ThreadContext)
369 {
370 PETHREAD Thread;
371 NTSTATUS Status;
372 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
373 PAGED_CODE();
374
375 /* Get the Thread Object */
376 Status = ObReferenceObjectByHandle(ThreadHandle,
377 THREAD_SET_CONTEXT,
378 PsThreadType,
379 PreviousMode,
380 (PVOID*)&Thread,
381 NULL);
382
383 if (!NT_SUCCESS(Status)) return Status;
384
385 /* Make sure it's not a system thread */
386 if (Thread->SystemThread)
387 {
388 /* Fail */
389 Status = STATUS_INVALID_HANDLE;
390 }
391 else
392 {
393 /* Call the kernel API */
394 Status = PsSetContextThread(Thread, ThreadContext, PreviousMode);
395 }
396
397 /* Dereference it and return */
398 ObDereferenceObject(Thread);
399 return Status;
400 }
401
402 /* EOF */