Thread/Process Termination/Repeaing Rewrite + Fixes
[reactos.git] / reactos / ntoskrnl / ps / debug.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/debug.c
5 * PURPOSE: Thread managment
6 *
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
8 * Phillip Susi
9 */
10
11
12 /* INCLUDES ****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* GLOBALS *****************************************************************/
19
20 /* Thread "Set/Get Context" Context Structure */
21 typedef struct _GET_SET_CTX_CONTEXT {
22 KAPC Apc;
23 KEVENT Event;
24 CONTEXT Context;
25 } GET_SET_CTX_CONTEXT, *PGET_SET_CTX_CONTEXT;
26
27
28 /* FUNCTIONS ***************************************************************/
29
30 /*
31 * FUNCTION: This routine is called by an APC sent by NtGetContextThread to
32 * copy the context of a thread into a buffer.
33 */
34 VOID
35 STDCALL
36 PspGetOrSetContextKernelRoutine(PKAPC Apc,
37 PKNORMAL_ROUTINE* NormalRoutine,
38 PVOID* NormalContext,
39 PVOID* SystemArgument1,
40 PVOID* SystemArgument2)
41 {
42 PGET_SET_CTX_CONTEXT GetSetContext;
43 PKEVENT Event;
44 PCONTEXT Context;
45
46 /* Get the Context Structure */
47 GetSetContext = CONTAINING_RECORD(Apc, GET_SET_CTX_CONTEXT, Apc);
48 Context = &GetSetContext->Context;
49 Event = &GetSetContext->Event;
50
51 /* Check if it's a set or get */
52 if (SystemArgument1) {
53
54 /* Get the Context */
55 KeTrapFrameToContext(KeGetCurrentThread()->TrapFrame, Context);
56
57 } else {
58
59 /* Set the Context */
60 KeContextToTrapFrame(Context, KeGetCurrentThread()->TrapFrame);
61 }
62
63 /* Notify the Native API that we are done */
64 KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
65 }
66
67 NTSTATUS
68 STDCALL
69 NtGetContextThread(IN HANDLE ThreadHandle,
70 OUT PCONTEXT ThreadContext)
71 {
72 PETHREAD Thread;
73 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
74 GET_SET_CTX_CONTEXT GetSetContext;
75 NTSTATUS Status = STATUS_SUCCESS;
76
77 PAGED_CODE();
78
79 /* Check the buffer to be OK */
80 if(PreviousMode != KernelMode) {
81
82 _SEH_TRY {
83
84 ProbeForWrite(ThreadContext,
85 sizeof(CONTEXT),
86 sizeof(ULONG));
87 } _SEH_HANDLE {
88
89 Status = _SEH_GetExceptionCode();
90
91 } _SEH_END;
92
93 if(!NT_SUCCESS(Status)) return Status;
94 }
95
96 /* Get the Thread Object */
97 Status = ObReferenceObjectByHandle(ThreadHandle,
98 THREAD_GET_CONTEXT,
99 PsThreadType,
100 PreviousMode,
101 (PVOID*)&Thread,
102 NULL);
103
104 /* Check success */
105 if(NT_SUCCESS(Status)) {
106
107 /* Check if we're running in the same thread */
108 if(Thread == PsGetCurrentThread()) {
109
110 /*
111 * I don't know if trying to get your own context makes much
112 * sense but we can handle it more efficently.
113 */
114 KeTrapFrameToContext(Thread->Tcb.TrapFrame, &GetSetContext.Context);
115
116 } else {
117
118 /* Use an APC... Initialize the Event */
119 KeInitializeEvent(&GetSetContext.Event,
120 NotificationEvent,
121 FALSE);
122
123 /* Initialize the APC */
124 KeInitializeApc(&GetSetContext.Apc,
125 &Thread->Tcb,
126 OriginalApcEnvironment,
127 PspGetOrSetContextKernelRoutine,
128 NULL,
129 NULL,
130 KernelMode,
131 NULL);
132
133 /* Queue it as a Get APC */
134 if (!KeInsertQueueApc(&GetSetContext.Apc,
135 (PVOID)1,
136 NULL,
137 IO_NO_INCREMENT)) {
138
139 Status = STATUS_THREAD_IS_TERMINATING;
140
141 } else {
142
143 /* Wait for the APC to complete */
144 Status = KeWaitForSingleObject(&GetSetContext.Event,
145 0,
146 KernelMode,
147 FALSE,
148 NULL);
149 }
150 }
151
152 /* Dereference the thread */
153 ObDereferenceObject(Thread);
154
155 /* Check for success and return the Context */
156 if(NT_SUCCESS(Status)) {
157
158 _SEH_TRY {
159
160 *ThreadContext = GetSetContext.Context;
161
162 } _SEH_HANDLE {
163
164 Status = _SEH_GetExceptionCode();
165
166 } _SEH_END;
167 }
168 }
169
170 /* Return status */
171 return Status;
172 }
173
174 NTSTATUS
175 STDCALL
176 NtSetContextThread(IN HANDLE ThreadHandle,
177 IN PCONTEXT ThreadContext)
178 {
179 PETHREAD Thread;
180 GET_SET_CTX_CONTEXT GetSetContext;
181 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
182 NTSTATUS Status = STATUS_SUCCESS;
183
184 PAGED_CODE();
185
186 /* Check the buffer to be OK */
187 if(PreviousMode != KernelMode) {
188
189 _SEH_TRY {
190
191 ProbeForRead(ThreadContext,
192 sizeof(CONTEXT),
193 sizeof(ULONG));
194
195 GetSetContext.Context = *ThreadContext;
196 ThreadContext = &GetSetContext.Context;
197
198 } _SEH_HANDLE {
199
200 Status = _SEH_GetExceptionCode();
201 } _SEH_END;
202
203 if(!NT_SUCCESS(Status)) return Status;
204 }
205
206 /* Get the Thread Object */
207 Status = ObReferenceObjectByHandle(ThreadHandle,
208 THREAD_SET_CONTEXT,
209 PsThreadType,
210 PreviousMode,
211 (PVOID*)&Thread,
212 NULL);
213
214 /* Check success */
215 if(NT_SUCCESS(Status)) {
216
217 /* Check if we're running in the same thread */
218 if(Thread == PsGetCurrentThread()) {
219
220 /*
221 * I don't know if trying to get your own context makes much
222 * sense but we can handle it more efficently.
223 */
224 KeContextToTrapFrame(&GetSetContext.Context, Thread->Tcb.TrapFrame);
225
226 } else {
227
228 /* Use an APC... Initialize the Event */
229 KeInitializeEvent(&GetSetContext.Event,
230 NotificationEvent,
231 FALSE);
232
233 /* Initialize the APC */
234 KeInitializeApc(&GetSetContext.Apc,
235 &Thread->Tcb,
236 OriginalApcEnvironment,
237 PspGetOrSetContextKernelRoutine,
238 NULL,
239 NULL,
240 KernelMode,
241 NULL);
242
243 /* Queue it as a Get APC */
244 if (!KeInsertQueueApc(&GetSetContext.Apc,
245 NULL,
246 NULL,
247 IO_NO_INCREMENT)) {
248
249 Status = STATUS_THREAD_IS_TERMINATING;
250
251 } else {
252
253 /* Wait for the APC to complete */
254 Status = KeWaitForSingleObject(&GetSetContext.Event,
255 0,
256 KernelMode,
257 FALSE,
258 NULL);
259 }
260 }
261
262 /* Dereference the thread */
263 ObDereferenceObject(Thread);
264 }
265
266 /* Return status */
267 return Status;
268 }
269
270 /* EOF */