- Fix KeAttackProcess, KeStackAttachProcess, KeUnstackDetachProcess and KeDetachProce...
[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: Kernel Process Management and System Call Tables
6 * PROGRAMMERS: Alex Ionescu
7 * Gregor Anich
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #include <internal/napi.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* GLOBALS *****************************************************************/
18
19 KSERVICE_TABLE_DESCRIPTOR
20 __declspec(dllexport)
21 KeServiceDescriptorTable[SSDT_MAX_ENTRIES] =
22 {
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 {
32 { MainSSDT, NULL, NUMBER_OF_SYSCALLS, MainSSPT },
33 { NULL, NULL, 0, NULL },
34 { NULL, NULL, 0, NULL },
35 { NULL, NULL, 0, NULL }
36 };
37
38 /* FUNCTIONS *****************************************************************/
39
40 PKPROCESS
41 STDCALL
42 KeGetCurrentProcess(VOID)
43 {
44 return(&(PsGetCurrentProcess()->Pcb));
45 }
46
47 static __inline
48 VOID
49 NTAPI
50 UpdatePageDirs(IN PKTHREAD Thread,
51 IN PKPROCESS Process)
52 {
53 /*
54 * The stack and the thread structure of the current process may be
55 * located in a page which is not present in the page directory of
56 * the process we're attaching to. That would lead to a page fault
57 * when this function returns. However, since the processor can't
58 * call the page fault handler 'cause it can't push EIP on the stack,
59 * this will show up as a stack fault which will crash the entire system.
60 * To prevent this, make sure the page directory of the process we're
61 * attaching to is up-to-date.
62 */
63 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread->StackLimit, MM_STACK_SIZE);
64 MmUpdatePageDir((PEPROCESS)Process, (PVOID)Thread, sizeof(ETHREAD));
65 }
66
67 VOID
68 NTAPI
69 KiAttachProcess(PKTHREAD Thread,
70 PKPROCESS Process,
71 KIRQL OldIrql,
72 PRKAPC_STATE SavedApcState)
73 {
74 ASSERT(Process != Thread->ApcState.Process);
75 DPRINT("KiAttachProcess(Thread: %x, Process: %x, SavedApcState: %x\n",
76 Thread, Process, SavedApcState);
77
78 /* Increase Stack Count */
79 Process->StackCount++;
80
81 /* Swap the APC Environment */
82 KiMoveApcState(&Thread->ApcState, SavedApcState);
83
84 /* Reinitialize Apc State */
85 InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
86 InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
87 Thread->ApcState.Process = Process;
88 Thread->ApcState.KernelApcInProgress = FALSE;
89 Thread->ApcState.KernelApcPending = FALSE;
90 Thread->ApcState.UserApcPending = FALSE;
91
92 /* Update Environment Pointers if needed*/
93 if (SavedApcState == &Thread->SavedApcState)
94 {
95 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState;
96 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState;
97 Thread->ApcStateIndex = AttachedApcEnvironment;
98 }
99
100 /* Check if the process is paged in */
101 if (Process->State == ProcessInMemory)
102 {
103 /* FIXME: Scan the Ready Thread List once new scheduler is in */
104
105 /* Swap the Processes */
106 KiSwapProcess(Process, SavedApcState->Process);
107
108 /* Return to old IRQL*/
109 KeReleaseDispatcherDatabaseLock(OldIrql);
110 }
111 else
112 {
113 DPRINT1("Errr. ReactOS doesn't support paging out processes yet...\n");
114 DbgBreakPoint();
115 }
116 }
117
118 VOID
119 NTAPI
120 KeInitializeProcess(PKPROCESS Process,
121 KPRIORITY Priority,
122 KAFFINITY Affinity,
123 LARGE_INTEGER DirectoryTableBase)
124 {
125 DPRINT("KeInitializeProcess. Process: %x, DirectoryTableBase: %x\n",
126 Process, DirectoryTableBase);
127
128 /* Initialize the Dispatcher Header */
129 KeInitializeDispatcherHeader(&Process->Header,
130 ProcessObject,
131 sizeof(KPROCESS),
132 FALSE);
133
134 /* Initialize Scheduler Data, Disable Alignment Faults and Set the PDE */
135 Process->Affinity = Affinity;
136 Process->BasePriority = Priority;
137 Process->QuantumReset = 6;
138 Process->DirectoryTableBase = DirectoryTableBase;
139 Process->AutoAlignment = TRUE;
140 Process->IopmOffset = 0xFFFF;
141 Process->State = ProcessInMemory;
142
143 /* Initialize the Thread List */
144 InitializeListHead(&Process->ThreadListHead);
145 KeInitializeSpinLock(&Process->ProcessLock);
146 DPRINT("The Process has now been initalized with the Kernel\n");
147 }
148
149 ULONG
150 NTAPI
151 KeSetProcess(PKPROCESS Process,
152 KPRIORITY Increment)
153 {
154 KIRQL OldIrql;
155 ULONG OldState;
156
157 /* Lock Dispatcher */
158 OldIrql = KeAcquireDispatcherDatabaseLock();
159
160 /* Get Old State */
161 OldState = Process->Header.SignalState;
162
163 /* Signal the Process */
164 Process->Header.SignalState = TRUE;
165 if ((OldState == 0) && IsListEmpty(&Process->Header.WaitListHead) != TRUE)
166 {
167 /* Satisfy waits */
168 KiWaitTest((PVOID)Process, Increment);
169 }
170
171 /* Release Dispatcher Database */
172 KeReleaseDispatcherDatabaseLock(OldIrql);
173
174 /* Return the previous State */
175 return OldState;
176 }
177
178 VOID
179 NTAPI
180 KiSwapProcess(PKPROCESS NewProcess,
181 PKPROCESS OldProcess)
182 {
183 DPRINT("Switching CR3 to: %x\n", NewProcess->DirectoryTableBase.u.LowPart);
184 Ke386SetPageTableDirectory(NewProcess->DirectoryTableBase.u.LowPart);
185 }
186
187 /*
188 * @implemented
189 */
190 VOID
191 NTAPI
192 KeAttachProcess(PKPROCESS Process)
193 {
194 KIRQL OldIrql;
195 PKTHREAD Thread;
196 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
197 DPRINT("KeAttachProcess: %x\n", Process);
198
199 /* Make sure that we are in the right page directory */
200 Thread = KeGetCurrentThread();
201 UpdatePageDirs(Thread, Process);
202
203 /* Lock Dispatcher */
204 OldIrql = KeAcquireDispatcherDatabaseLock();
205
206 /* Check if we're already in that process */
207 if (Thread->ApcState.Process == Process)
208 {
209 /* Unlock the dispatcher, nothing to do */
210 KeReleaseDispatcherDatabaseLock(OldIrql);
211 }
212 else if ((Thread->ApcStateIndex != OriginalApcEnvironment) ||
213 (KeIsExecutingDpc()))
214 {
215 /* Executing a DPC or already attached, crash! */
216 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT,
217 (ULONG_PTR)Process,
218 (ULONG_PTR)Thread->ApcState.Process,
219 Thread->ApcStateIndex,
220 KeIsExecutingDpc());
221 }
222 else
223 {
224 /* Legit attach attempt: do it! */
225 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
226 }
227 }
228
229 /*
230 * @implemented
231 */
232 VOID
233 NTAPI
234 KeDetachProcess (VOID)
235 {
236 PKTHREAD Thread;
237 KIRQL OldIrql;
238 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
239 DPRINT("KeDetachProcess()\n");
240
241 /* Get Current Thread and lock the dispatcher */
242 Thread = KeGetCurrentThread();
243 OldIrql = KeAcquireDispatcherDatabaseLock();
244
245 /* Check if it's attached */
246 if (Thread->ApcStateIndex != OriginalApcEnvironment)
247 {
248 /* It is, decrease Stack Count */
249 if(!(--Thread->ApcState.Process->StackCount))
250 {
251 /* FIXME: Swap the process out */
252 }
253
254 /* Restore the APC State */
255 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
256 Thread->SavedApcState.Process = NULL;
257 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
258 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
259 Thread->ApcStateIndex = OriginalApcEnvironment;
260
261 /* Check if we have pending APCs */
262 if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
263 {
264 /* What do you know, we do! Request them to be delivered */
265 Thread->ApcState.KernelApcPending = TRUE;
266 HalRequestSoftwareInterrupt(APC_LEVEL);
267 }
268
269 /* Swap Processes */
270 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
271 }
272
273 /* Unlock Dispatcher */
274 KeReleaseDispatcherDatabaseLock(OldIrql);
275 }
276
277 /*
278 * @implemented
279 */
280 BOOLEAN
281 NTAPI
282 KeIsAttachedProcess(VOID)
283 {
284 /* Return the APC State */
285 return KeGetCurrentThread()->ApcStateIndex;
286 }
287
288 /*
289 * @implemented
290 */
291 VOID
292 NTAPI
293 KeStackAttachProcess(IN PKPROCESS Process,
294 OUT PRKAPC_STATE ApcState)
295 {
296 KIRQL OldIrql;
297 PKTHREAD Thread;
298 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
299
300 /* Make sure that we are in the right page directory */
301 Thread = KeGetCurrentThread();
302 UpdatePageDirs(Thread, Process);
303
304 /* Acquire the dispatcher lock */
305 OldIrql = KeAcquireDispatcherDatabaseLock();
306
307 /* Crash system if DPC is being executed! */
308 if (KeIsExecutingDpc())
309 {
310 /* Executing a DPC, crash! */
311 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT,
312 (ULONG_PTR)Process,
313 (ULONG_PTR)Thread->ApcState.Process,
314 Thread->ApcStateIndex,
315 KeIsExecutingDpc());
316 }
317
318 /* Check if we are already in the target process */
319 if (Thread->ApcState.Process == Process)
320 {
321 /* Unlock the dispatcher database */
322 KeReleaseDispatcherDatabaseLock(OldIrql);
323
324 /* Set magic value so we don't crash later when detaching */
325 ApcState->Process = (PKPROCESS)1;
326 }
327 else
328 {
329 /* Check if the Current Thread is already attached */
330 if (Thread->ApcStateIndex != OriginalApcEnvironment)
331 {
332 /* We're already attached, so save the APC State into what we got */
333 KiAttachProcess(Thread, Process, OldIrql, ApcState);
334 }
335 else
336 {
337 /* We're not attached, so save the APC State into SavedApcState */
338 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
339 ApcState->Process = NULL;
340 }
341 }
342 }
343
344 /*
345 * @implemented
346 */
347 VOID
348 NTAPI
349 KeUnstackDetachProcess(IN PRKAPC_STATE ApcState)
350 {
351 KIRQL OldIrql;
352 PKTHREAD Thread;
353 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
354
355 /* Get the current thread and acquire the dispatcher lock */
356 Thread = KeGetCurrentThread();
357 OldIrql = KeAcquireDispatcherDatabaseLock();
358
359 /* Check for magic value meaning we were already in the same process */
360 if (ApcState->Process != (PKPROCESS)1)
361 {
362 /*
363 * Check if the process isn't attacked, or has a Kernel APC in progress
364 * or has pending APC of any kind.
365 */
366 if ((Thread->ApcStateIndex == OriginalApcEnvironment) ||
367 (Thread->ApcState.KernelApcInProgress) ||
368 (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) ||
369 (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])))
370 {
371 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
372 }
373
374 /* Decrease Stack Count */
375 if(!(--Thread->ApcState.Process->StackCount))
376 {
377 /* FIXME: Swap the process out */
378 }
379
380 if (ApcState->Process != NULL)
381 {
382 /* Restore the APC State */
383 KiMoveApcState(ApcState, &Thread->ApcState);
384 }
385 else
386 {
387 /* The ApcState parameter is useless, so use the saved data and reset it */
388 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
389 Thread->SavedApcState.Process = NULL;
390 Thread->ApcStateIndex = OriginalApcEnvironment;
391 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
392 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
393 }
394
395 /* Check if we have pending APCs */
396 if (IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
397 {
398 /* What do you know, we do! Request them to be delivered */
399 Thread->ApcState.KernelApcPending = TRUE;
400 HalRequestSoftwareInterrupt(APC_LEVEL);
401 }
402
403 /* Swap Processes */
404 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
405 }
406
407 /* Return to old IRQL*/
408 KeReleaseDispatcherDatabaseLock(OldIrql);
409 }
410
411 /*
412 * @implemented
413 */
414 BOOLEAN
415 NTAPI
416 KeAddSystemServiceTable(PULONG_PTR Base,
417 PULONG Count OPTIONAL,
418 ULONG Limit,
419 PUCHAR Number,
420 ULONG Index)
421 {
422 /* Check if descriptor table entry is free */
423 if ((Index > SSDT_MAX_ENTRIES - 1) ||
424 (KeServiceDescriptorTable[Index].Base) ||
425 (KeServiceDescriptorTableShadow[Index].Base))
426 {
427 return FALSE;
428 }
429
430 /* Initialize the shadow service descriptor table */
431 KeServiceDescriptorTableShadow[Index].Base = Base;
432 KeServiceDescriptorTableShadow[Index].Limit = Limit;
433 KeServiceDescriptorTableShadow[Index].Number = Number;
434 KeServiceDescriptorTableShadow[Index].Count = Count;
435
436 return TRUE;
437 }
438
439 /*
440 * @implemented
441 */
442 BOOLEAN
443 NTAPI
444 KeRemoveSystemServiceTable(IN ULONG Index)
445 {
446 /* Make sure the Index is valid */
447 if (Index > SSDT_MAX_ENTRIES - 1) return FALSE;
448
449 /* Is there a Normal Descriptor Table? */
450 if (!KeServiceDescriptorTable[Index].Base)
451 {
452 /* Not with the index, is there a shadow at least? */
453 if (!KeServiceDescriptorTableShadow[Index].Base) return FALSE;
454 }
455
456 /* Now clear from the Shadow Table. */
457 KeServiceDescriptorTableShadow[Index].Base = NULL;
458 KeServiceDescriptorTableShadow[Index].Number = NULL;
459 KeServiceDescriptorTableShadow[Index].Limit = 0;
460 KeServiceDescriptorTableShadow[Index].Count = NULL;
461
462 /* Check if we should clean from the Master one too */
463 if (Index == 1)
464 {
465 KeServiceDescriptorTable[Index].Base = NULL;
466 KeServiceDescriptorTable[Index].Number = NULL;
467 KeServiceDescriptorTable[Index].Limit = 0;
468 KeServiceDescriptorTable[Index].Count = NULL;
469 }
470
471 return TRUE;
472 }
473 /* EOF */