c5237917529bf5f5267a1a42e885f5971a260d03
[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 <ntdll/napi.h>
15 #define NDEBUG
16 #include <internal/debug.h>
17
18 /* GLOBALS *****************************************************************/
19
20 SSDT_ENTRY
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 SSDT_ENTRY
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 * @implemented
58 */
59 VOID
60 STDCALL
61 KeAttachProcess(PKPROCESS Process)
62 {
63 KIRQL OldIrql;
64 PKTHREAD Thread = KeGetCurrentThread();
65
66 DPRINT("KeAttachProcess: %x\n", Process);
67
68 /* Make sure that we are in the right page directory */
69 UpdatePageDirs(Thread, Process);
70
71 /* Lock Dispatcher */
72 OldIrql = KeAcquireDispatcherDatabaseLock();
73
74 /* Crash system if DPC is being executed! */
75 if (KeIsExecutingDpc()) {
76
77 DPRINT1("Invalid attach (Thread is executing a DPC!)\n");
78 KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT);
79 }
80
81 /* Check if the Target Process is already attached */
82 if (Thread->ApcState.Process == Process || Thread->ApcStateIndex != OriginalApcEnvironment) {
83
84 DPRINT("Process already Attached. Exitting\n");
85 KeReleaseDispatcherDatabaseLock(OldIrql);
86 } else {
87
88 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
89 }
90 }
91
92 VOID
93 STDCALL
94 KiAttachProcess(PKTHREAD Thread, PKPROCESS Process, KIRQL ApcLock, PRKAPC_STATE SavedApcState)
95 {
96
97 DPRINT("KiAttachProcess(Thread: %x, Process: %x, SavedApcState: %x\n", Thread, Process, SavedApcState);
98
99 /* Increase Stack Count */
100 Process->StackCount++;
101
102 /* Swap the APC Environment */
103 KiMoveApcState(&Thread->ApcState, SavedApcState);
104
105 /* Reinitialize Apc State */
106 InitializeListHead(&Thread->ApcState.ApcListHead[KernelMode]);
107 InitializeListHead(&Thread->ApcState.ApcListHead[UserMode]);
108 Thread->ApcState.Process = Process;
109 Thread->ApcState.KernelApcInProgress = FALSE;
110 Thread->ApcState.KernelApcPending = FALSE;
111 Thread->ApcState.UserApcPending = FALSE;
112
113 /* Update Environment Pointers if needed*/
114 if (SavedApcState == &Thread->SavedApcState) {
115
116 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->SavedApcState;
117 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->ApcState;
118 Thread->ApcStateIndex = AttachedApcEnvironment;
119 }
120
121 /* Swap the Processes */
122 KiSwapProcess(Process, SavedApcState->Process);
123
124 /* Return to old IRQL*/
125 KeReleaseDispatcherDatabaseLock(ApcLock);
126
127 DPRINT("KiAttachProcess Completed Sucesfully\n");
128 }
129
130 VOID
131 STDCALL
132 KiSwapProcess(PKPROCESS NewProcess, PKPROCESS OldProcess)
133 {
134 //PKPCR Pcr = KeGetCurrentKpcr();
135
136 /* Do they have an LDT? */
137 if ((NewProcess->LdtDescriptor) || (OldProcess->LdtDescriptor)) {
138
139 /* FIXME : SWitch GDT/IDT */
140 }
141 DPRINT("Switching CR3 to: %x\n", NewProcess->DirectoryTableBase.u.LowPart);
142 Ke386SetPageTableDirectory(NewProcess->DirectoryTableBase.u.LowPart);
143
144 /* FIXME: Set IopmOffset in TSS */
145 }
146
147 /*
148 * @implemented
149 */
150 BOOLEAN
151 STDCALL
152 KeIsAttachedProcess(VOID)
153 {
154 /* Return the APC State */
155 return KeGetCurrentThread()->ApcStateIndex;
156 }
157
158 /*
159 * @implemented
160 */
161 VOID
162 STDCALL
163 KeStackAttachProcess(IN PKPROCESS Process,
164 OUT PRKAPC_STATE ApcState)
165 {
166 KIRQL OldIrql;
167 PKTHREAD Thread = KeGetCurrentThread();
168
169 /* Make sure that we are in the right page directory */
170 UpdatePageDirs(Thread, Process);
171
172 OldIrql = KeAcquireDispatcherDatabaseLock();
173
174 /* Crash system if DPC is being executed! */
175 if (KeIsExecutingDpc()) {
176
177 DPRINT1("Invalid attach (Thread is executing a DPC!)\n");
178 KEBUGCHECK(INVALID_PROCESS_ATTACH_ATTEMPT);
179 }
180
181 /* Check if the Target Process is already attached */
182 if (Thread->ApcState.Process == Process) {
183
184 ApcState->Process = (PKPROCESS)1; /* Meaning already attached to the same Process */
185
186 } else {
187
188 /* Check if the Current Thread is already attached and call the Internal Function*/
189 if (Thread->ApcStateIndex != OriginalApcEnvironment) {
190
191 KiAttachProcess(Thread, Process, OldIrql, ApcState);
192 } else {
193
194 KiAttachProcess(Thread, Process, OldIrql, &Thread->SavedApcState);
195 ApcState->Process = NULL;
196 }
197 }
198 }
199
200 /*
201 * @implemented
202 */
203 VOID STDCALL
204 KeDetachProcess (VOID)
205 {
206 PKTHREAD Thread;
207 KIRQL OldIrql;
208
209 DPRINT("KeDetachProcess()\n");
210
211 /* Get Current Thread and Lock */
212 Thread = KeGetCurrentThread();
213 OldIrql = KeAcquireDispatcherDatabaseLock();
214
215 /* Check if it's attached */
216 DPRINT("Current ApcStateIndex: %x\n", Thread->ApcStateIndex);
217
218 if (Thread->ApcStateIndex == OriginalApcEnvironment) {
219
220 DPRINT1("Invalid detach (thread was not attached)\n");
221 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
222 }
223
224 /* Decrease Stack Count */
225 Thread->ApcState.Process->StackCount--;
226
227 /* Restore the APC State */
228 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
229 Thread->SavedApcState.Process = NULL;
230 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
231 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
232 Thread->ApcStateIndex = OriginalApcEnvironment;
233
234 /* Swap Processes */
235 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
236
237 /* Unlock Dispatcher */
238 KeReleaseDispatcherDatabaseLock(OldIrql);
239 }
240
241 /*
242 * @implemented
243 */
244 VOID
245 STDCALL
246 KeUnstackDetachProcess (
247 IN PRKAPC_STATE ApcState
248 )
249 {
250 KIRQL OldIrql;
251 PKTHREAD Thread;
252
253 /*
254 * If the special "We tried to attach to the process already being
255 * attached to" flag is there, don't do anything
256 */
257 if (ApcState->Process == (PKPROCESS)1) return;
258
259 Thread = KeGetCurrentThread();
260 OldIrql = KeAcquireDispatcherDatabaseLock();
261
262 /* Sorry Buddy, can't help you if you've got APCs or just aren't attached */
263 if ((Thread->ApcStateIndex == OriginalApcEnvironment) || (Thread->ApcState.KernelApcInProgress)) {
264
265 DPRINT1("Invalid detach (Thread not Attached, or Kernel APC in Progress!)\n");
266 KEBUGCHECK(INVALID_PROCESS_DETACH_ATTEMPT);
267 }
268
269 /* Restore the Old APC State if a Process was present */
270 if (ApcState->Process) {
271
272 KiMoveApcState(ApcState, &Thread->ApcState);
273
274 } else {
275
276 /* The ApcState parameter is useless, so use the saved data and reset it */
277 KiMoveApcState(&Thread->SavedApcState, &Thread->ApcState);
278 Thread->SavedApcState.Process = NULL;
279 Thread->ApcStateIndex = OriginalApcEnvironment;
280 Thread->ApcStatePointer[OriginalApcEnvironment] = &Thread->ApcState;
281 Thread->ApcStatePointer[AttachedApcEnvironment] = &Thread->SavedApcState;
282 }
283
284 /* Swap Processes */
285 KiSwapProcess(Thread->ApcState.Process, Thread->ApcState.Process);
286
287 /* Return to old IRQL*/
288 KeReleaseDispatcherDatabaseLock(OldIrql);
289 }
290
291 /*
292 * @implemented
293 */
294 BOOLEAN STDCALL
295 KeAddSystemServiceTable(PSSDT SSDT,
296 PULONG ServiceCounterTable,
297 ULONG NumberOfServices,
298 PSSPT SSPT,
299 ULONG TableIndex)
300 {
301 /* check if descriptor table entry is free */
302 if ((TableIndex > SSDT_MAX_ENTRIES - 1) ||
303 (KeServiceDescriptorTable[TableIndex].SSDT != NULL) ||
304 (KeServiceDescriptorTableShadow[TableIndex].SSDT != NULL))
305 return FALSE;
306
307 /* initialize the shadow service descriptor table */
308 KeServiceDescriptorTableShadow[TableIndex].SSDT = SSDT;
309 KeServiceDescriptorTableShadow[TableIndex].SSPT = SSPT;
310 KeServiceDescriptorTableShadow[TableIndex].NumberOfServices = NumberOfServices;
311 KeServiceDescriptorTableShadow[TableIndex].ServiceCounterTable = ServiceCounterTable;
312
313 return TRUE;
314 }
315
316 /*
317 * @implemented
318 */
319 BOOLEAN
320 STDCALL
321 KeRemoveSystemServiceTable(IN ULONG TableIndex)
322 {
323 /* Make sure the Index is valid */
324 if (TableIndex > SSDT_MAX_ENTRIES - 1) return FALSE;
325
326 /* Is there a Normal Descriptor Table? */
327 if (!KeServiceDescriptorTable[TableIndex].SSDT) {
328
329 /* Not with the index, is there a shadow at least? */
330 if (!KeServiceDescriptorTableShadow[TableIndex].SSDT) return FALSE;
331 }
332
333 /* Now clear from the Shadow Table. */
334 KeServiceDescriptorTableShadow[TableIndex].SSDT = NULL;
335 KeServiceDescriptorTableShadow[TableIndex].SSPT = NULL;
336 KeServiceDescriptorTableShadow[TableIndex].NumberOfServices = 0;
337 KeServiceDescriptorTableShadow[TableIndex].ServiceCounterTable = NULL;
338
339 /* Check if we should clean from the Master one too */
340 if (TableIndex == 1) {
341
342 KeServiceDescriptorTable[TableIndex].SSDT = NULL;
343 KeServiceDescriptorTable[TableIndex].SSPT = NULL;
344 KeServiceDescriptorTable[TableIndex].NumberOfServices = 0;
345 KeServiceDescriptorTable[TableIndex].ServiceCounterTable = NULL;
346 }
347
348 return TRUE;
349 }
350 /* EOF */