Part 1 of <many> ntoskrnl header cleanups
[reactos.git] / reactos / ntoskrnl / ke / process.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/process.c
5 * PURPOSE: Attaching/Detaching and System Call Tables
6 *
7 * PROGRAMMERS: Alex Ionescu (Implemented Attach/Detach and KeRemoveSystemServiceTable)
8 * Gregor Anich (Bugfixes to Attach Functions)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #include <internal/napi.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* GLOBALS *****************************************************************/
19
20 KSERVICE_TABLE_DESCRIPTOR
21 __declspec(dllexport)
22 KeServiceDescriptorTable[SSDT_MAX_ENTRIES] = {
23 { MainSSDT, NULL, NUMBER_OF_SYSCALLS, MainSSPT },
24 { NULL, NULL, 0, NULL },
25 { NULL, NULL, 0, NULL },
26 { NULL, NULL, 0, NULL }
27 };
28
29 KSERVICE_TABLE_DESCRIPTOR
30 KeServiceDescriptorTableShadow[SSDT_MAX_ENTRIES] = {
31 { MainSSDT, NULL, NUMBER_OF_SYSCALLS, MainSSPT },
32 { NULL, NULL, 0, NULL },
33 { NULL, NULL, 0, NULL },
34 { NULL, NULL, 0, NULL }
35 };
36
37 /* FUNCTIONS *****************************************************************/
38
39 static inline void
40 UpdatePageDirs(PKTHREAD Thread, PKPROCESS Process)
41 {
42 /*
43 * The stack and the thread structure of the current process may be
44 * located in a page which is not present in the page directory of
45 * the process we're attaching to. That would lead to a page fault
46 * when this function returns. However, since the processor can't
47 * call the page fault handler 'cause it can't push EIP on the stack,
48 * this will show up as a stack fault which will crash the entire system.
49 * To prevent this, make sure the page directory of the process we're
50 * attaching to is up-to-date.
51 */
52 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE);
53 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
54 }
55
56 /*
57 * FUNCTION: Returns a pointer to the current process
58 */
59 PKPROCESS
60 STDCALL
61 KeGetCurrentProcess(VOID)
62 {
63 return(&(PsGetCurrentProcess()->Pcb));
64 }
65
66 VOID
67 STDCALL
68 KeInitializeProcess(PKPROCESS Process,
69 KPRIORITY Priority,
70 KAFFINITY Affinity,
71 LARGE_INTEGER DirectoryTableBase)
72 {
73 DPRINT("KeInitializeProcess. Process: %x, DirectoryTableBase: %x\n", Process, DirectoryTableBase);
74
75 /* Initialize the Dispatcher Header */
76 KeInitializeDispatcherHeader(&Process->Header,
77 ProcessObject,
78 sizeof(KPROCESS),
79 FALSE);
80
81 /* Initialize Scheduler Data, Disable Alignment Faults and Set the PDE */
82 Process->Affinity = Affinity;
83 Process->BasePriority = Priority;
84 Process->QuantumReset = 6;
85 Process->DirectoryTableBase = DirectoryTableBase;
86 Process->AutoAlignment = TRUE;
87 Process->IopmOffset = 0xFFFF;
88 Process->State = ProcessInMemory;
89
90 /* Initialize the Thread List */
91 InitializeListHead(&Process->ThreadListHead);
92 KeInitializeSpinLock(&Process->ProcessLock);
93 DPRINT("The Process has now been initalized with the Kernel\n");
94 }
95
96 ULONG
97 STDCALL
98 KeSetProcess(PKPROCESS Process,
99 KPRIORITY Increment)
100 {
101 KIRQL OldIrql;
102 ULONG OldState;
103
104 /* Lock Dispatcher */
105 OldIrql = KeAcquireDispatcherDatabaseLock();
106
107 /* Get Old State */
108 OldState = Process->Header.SignalState;
109
110 /* Signal the Process */
111 Process->Header.SignalState = TRUE;
112 if ((OldState == 0) && IsListEmpty(&Process->Header.WaitListHead) != TRUE) {
113
114 /* Satisfy waits */
115 KiWaitTest((PVOID)Process, Increment);
116 }
117
118 /* Release Dispatcher Database */
119 KeReleaseDispatcherDatabaseLock(OldIrql);
120
121 /* Return the previous State */
122 return OldState;
123 }
124
125 /*
126 * @implemented
127 */
128 VOID
129 STDCALL
130 KeAttachProcess(PKPROCESS Process)
131 {
132 KIRQL OldIrql;
133 PKTHREAD Thread = KeGetCurrentThread();
134
135 DPRINT("KeAttachProcess: %x\n", Process);
136
137 /* Make sure that we are in the right page directory */
138 UpdatePageDirs(Thread, Process);
139
140 /* Lock Dispatcher */
141 OldIrql = KeAcquireDispatcherDatabaseLock();
142 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
143
144 /* Crash system if DPC is being executed! */
145 if (KeIsExecutingDpc()) {
146
147 DPRINT1("Invalid attach (Thread is executing a DPC!)\n");
148 KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT);
149 }
150
151 /* Check if the Target Process is already attached */
152 if (Thread->ApcState.Process == Process || Thread->ApcStateIndex != OriginalApcEnvironment) {
153
154 DPRINT("Process already Attached. Exitting\n");
155 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
156 KeReleaseDispatcherDatabaseLock(OldIrql);
157 } else {
158
159 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
160 }
161 }
162
163 VOID
164 STDCALL
165 KiAttachProcess(PKTHREAD Thread, PKPROCESS Process, KIRQL ApcLock, PRKAPC_STATE SavedApcState)
166 {
167
168 DPRINT("KiAttachProcess(Thread: %x, Process: %x, SavedApcState: %x\n", Thread, Process, SavedApcState);
169
170 /* Increase Stack Count */
171 Process->StackCount++;
172
173 /* Swap the APC Environment */
174 KiMoveApcState(&Thread->ApcState, SavedApcState);
175
176 /* Reinitialize Apc State */
177 InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
178 InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
179 Thread->ApcState.Process = Process;
180 Thread->ApcState.KernelApcInProgress = FALSE;
181 Thread->ApcState.KernelApcPending = FALSE;
182 Thread->ApcState.UserApcPending = FALSE;
183
184 /* Update Environment Pointers if needed*/
185 if (SavedApcState == &Thread->SavedApcState) {
186
187 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState;
188 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState;
189 Thread->ApcStateIndex = AttachedApcEnvironment;
190 }
191
192 /* Swap the Processes */
193 DPRINT("Swapping\n");
194 KiSwapProcess(Process, SavedApcState->Process);
195
196 /* Return to old IRQL*/
197 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
198 KeReleaseDispatcherDatabaseLock(ApcLock);
199
200 DPRINT("KiAttachProcess Completed Sucesfully\n");
201 }
202
203 VOID
204 STDCALL
205 KiSwapProcess(PKPROCESS NewProcess,
206 PKPROCESS OldProcess)
207 {
208 /* FIXME: Write this in ASM. Much easier */
209 DPRINT("Switching CR3 to: %x\n", NewProcess->DirectoryTableBase.u.LowPart);
210 Ke386SetPageTableDirectory(NewProcess->DirectoryTableBase.u.LowPart);
211 }
212
213 /*
214 * @implemented
215 */
216 BOOLEAN
217 STDCALL
218 KeIsAttachedProcess(VOID)
219 {
220 /* Return the APC State */
221 return KeGetCurrentThread()->ApcStateIndex;
222 }
223
224 /*
225 * @implemented
226 */
227 VOID
228 STDCALL
229 KeStackAttachProcess(IN PKPROCESS Process,
230 OUT PRKAPC_STATE ApcState)
231 {
232 KIRQL OldIrql;
233 PKTHREAD Thread = KeGetCurrentThread();
234
235 /* Make sure that we are in the right page directory */
236 UpdatePageDirs(Thread, Process);
237
238 OldIrql = KeAcquireDispatcherDatabaseLock();
239 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
240
241 /* Crash system if DPC is being executed! */
242 if (KeIsExecutingDpc()) {
243
244 DPRINT1("Invalid attach (Thread is executing a DPC!)\n");
245 KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT);
246 }
247
248 /* Check if the Target Process is already attached */
249 if (Thread->ApcState.Process == Process) {
250
251 ApcState->Process = (PKPROCESS)1; /* Meaning already attached to the same Process */
252
253 } else {
254
255 /* Check if the Current Thread is already attached and call the Internal Function*/
256 if (Thread->ApcStateIndex != OriginalApcEnvironment) {
257
258 KiAttachProcess(Thread, Process, OldIrql, ApcState);
259 } else {
260
261 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
262 ApcState->Process = NULL;
263 }
264 }
265 }
266
267 /*
268 * @implemented
269 */
270 VOID STDCALL
271 KeDetachProcess (VOID)
272 {
273 PKTHREAD Thread;
274 KIRQL OldIrql;
275
276 DPRINT("KeDetachProcess()\n");
277
278 /* Get Current Thread and Lock */
279 Thread = KeGetCurrentThread();
280 OldIrql = KeAcquireDispatcherDatabaseLock();
281 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
282
283 /* Check if it's attached */
284 DPRINT("Current ApcStateIndex: %x\n", Thread->ApcStateIndex);
285
286 if (Thread->ApcStateIndex == OriginalApcEnvironment) {
287
288 DPRINT1("Invalid detach (thread was not attached)\n");
289 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
290 }
291
292 /* Decrease Stack Count */
293 Thread->ApcState.Process->StackCount--;
294
295 /* Restore the APC State */
296 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
297 Thread->SavedApcState.Process = NULL;
298 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
299 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
300 Thread->ApcStateIndex = OriginalApcEnvironment;
301
302 /* Swap Processes */
303 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
304
305 /* Unlock Dispatcher */
306 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
307 KeReleaseDispatcherDatabaseLock(OldIrql);
308 }
309
310 /*
311 * @implemented
312 */
313 VOID
314 STDCALL
315 KeUnstackDetachProcess (
316 IN PRKAPC_STATE ApcState
317 )
318 {
319 KIRQL OldIrql;
320 PKTHREAD Thread;
321
322 /*
323 * If the special "We tried to attach to the process already being
324 * attached to" flag is there, don't do anything
325 */
326 if (ApcState->Process == (PKPROCESS)1) return;
327
328 Thread = KeGetCurrentThread();
329 OldIrql = KeAcquireDispatcherDatabaseLock();
330 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
331
332 /* Sorry Buddy, can't help you if you've got APCs or just aren't attached */
333 if ((Thread->ApcStateIndex == OriginalApcEnvironment) || (Thread->ApcState.KernelApcInProgress)) {
334
335 DPRINT1("Invalid detach (Thread not Attached, or Kernel APC in Progress!)\n");
336 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
337 }
338
339 /* Restore the Old APC State if a Process was present */
340 if (ApcState->Process) {
341
342 KiMoveApcState(ApcState, &Thread->ApcState);
343
344 } else {
345
346 /* The ApcState parameter is useless, so use the saved data and reset it */
347 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
348 Thread->SavedApcState.Process = NULL;
349 Thread->ApcStateIndex = OriginalApcEnvironment;
350 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
351 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
352 }
353
354 /* Swap Processes */
355 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
356
357 /* Return to old IRQL*/
358 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
359 KeReleaseDispatcherDatabaseLock(OldIrql);
360 }
361
362 /*
363 * @implemented
364 */
365 BOOLEAN
366 STDCALL
367 KeAddSystemServiceTable(PULONG_PTR Base,
368 PULONG Count OPTIONAL,
369 ULONG Limit,
370 PUCHAR Number,
371 ULONG Index)
372 {
373 /* Check if descriptor table entry is free */
374 if ((Index > SSDT_MAX_ENTRIES - 1) ||
375 (KeServiceDescriptorTable[Index].Base) ||
376 (KeServiceDescriptorTableShadow[Index].Base))
377 {
378 return FALSE;
379 }
380
381 /* Initialize the shadow service descriptor table */
382 KeServiceDescriptorTableShadow[Index].Base = Base;
383 KeServiceDescriptorTableShadow[Index].Limit = Limit;
384 KeServiceDescriptorTableShadow[Index].Number = Number;
385 KeServiceDescriptorTableShadow[Index].Count = Count;
386
387 return TRUE;
388 }
389
390 /*
391 * @implemented
392 */
393 BOOLEAN
394 STDCALL
395 KeRemoveSystemServiceTable(IN ULONG Index)
396 {
397 /* Make sure the Index is valid */
398 if (Index > SSDT_MAX_ENTRIES - 1) return FALSE;
399
400 /* Is there a Normal Descriptor Table? */
401 if (!KeServiceDescriptorTable[Index].Base)
402 {
403 /* Not with the index, is there a shadow at least? */
404 if (!KeServiceDescriptorTableShadow[Index].Base) return FALSE;
405 }
406
407 /* Now clear from the Shadow Table. */
408 KeServiceDescriptorTableShadow[Index].Base = NULL;
409 KeServiceDescriptorTableShadow[Index].Number = NULL;
410 KeServiceDescriptorTableShadow[Index].Limit = 0;
411 KeServiceDescriptorTableShadow[Index].Count = NULL;
412
413 /* Check if we should clean from the Master one too */
414 if (Index == 1)
415 {
416 KeServiceDescriptorTable[Index].Base = NULL;
417 KeServiceDescriptorTable[Index].Number = NULL;
418 KeServiceDescriptorTable[Index].Limit = 0;
419 KeServiceDescriptorTable[Index].Count = NULL;
420 }
421
422 return TRUE;
423 }
424 /* EOF */