finished applying @implemented and @unimplemented comments and remove the comments...
[reactos.git] / reactos / ntoskrnl / ke / apc.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/ke/apc.c
22 * PURPOSE: Possible implementation of APCs
23 * PROGRAMMER: David Welch (welch@cwcom.net)
24 * PORTABILITY: Unchecked
25 * UPDATE HISTORY:
26 * Created 22/05/98
27 * 12/11/99: Phillip Susi: Reworked the APC code
28 */
29
30 /* INCLUDES *****************************************************************/
31
32 #include <ddk/ntddk.h>
33 #include <internal/i386/segment.h>
34 #include <internal/ps.h>
35 #include <internal/ke.h>
36 #include <internal/ldr.h>
37 #include <internal/pool.h>
38
39 #define NDEBUG
40 #include <internal/debug.h>
41
42 /* GLOBALS *******************************************************************/
43
44 KSPIN_LOCK PiApcLock;
45 extern KSPIN_LOCK PiThreadListLock;
46
47 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
48
49 #define TAG_KAPC TAG('K', 'A', 'P', 'C')
50
51 /* FUNCTIONS *****************************************************************/
52
53 VOID KiRundownThread(VOID)
54 /*
55 * FUNCTION:
56 */
57 {
58 }
59
60 BOOLEAN KiTestAlert(VOID)
61 /*
62 * FUNCTION: Tests whether there are any pending APCs for the current thread
63 * and if so the APCs will be delivered on exit from kernel mode
64 */
65 {
66 KIRQL oldIrql;
67
68 KeAcquireSpinLock(&PiApcLock, &oldIrql);
69 if (KeGetCurrentThread()->ApcState.UserApcPending == 0)
70 {
71 KeReleaseSpinLock(&PiApcLock, oldIrql);
72 return(FALSE);
73 }
74 KeGetCurrentThread()->Alerted[0] = 1;
75 KeReleaseSpinLock(&PiApcLock, oldIrql);
76 return(TRUE);
77 }
78
79 VOID
80 KiDeliverNormalApc(VOID)
81 {
82 PETHREAD Thread = PsGetCurrentThread();
83 PLIST_ENTRY current;
84 PKAPC Apc;
85 KIRQL oldlvl;
86 PKNORMAL_ROUTINE NormalRoutine;
87 PVOID NormalContext;
88 PVOID SystemArgument1;
89 PVOID SystemArgument2;
90
91 KeAcquireSpinLock(&PiApcLock, &oldlvl);
92 while(!IsListEmpty(&(Thread->Tcb.ApcState.ApcListHead[0])))
93 {
94 current = RemoveTailList(&Thread->Tcb.ApcState.ApcListHead[0]);
95 Apc = CONTAINING_RECORD(current, KAPC, ApcListEntry);
96 if (Apc->NormalRoutine == NULL)
97 {
98 DbgPrint("Exiting kernel with kernel APCs pending.\n");
99 KeBugCheck(0);
100 }
101 Apc->Inserted = FALSE;
102 Thread->Tcb.ApcState.KernelApcInProgress++;
103 Thread->Tcb.ApcState.KernelApcPending--;
104
105 KeReleaseSpinLock(&PiApcLock, oldlvl);
106
107 NormalRoutine = Apc->NormalRoutine;
108 NormalContext = Apc->NormalContext;
109 SystemArgument1 = Apc->SystemArgument1;
110 SystemArgument2 = Apc->SystemArgument2;
111 Apc->KernelRoutine(Apc,
112 &NormalRoutine,
113 &NormalContext,
114 &SystemArgument1,
115 &SystemArgument2);
116 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
117
118 KeAcquireSpinLock(&PiApcLock, &oldlvl);
119 Thread->Tcb.ApcState.KernelApcInProgress--;
120 }
121 KeReleaseSpinLock(&PiApcLock, oldlvl);
122 }
123
124 BOOLEAN
125 KiDeliverUserApc(PKTRAP_FRAME TrapFrame)
126 /*
127 * FUNCTION: Tests whether there are any pending APCs for the current thread
128 * and if so the APCs will be delivered on exit from kernel mode.
129 * ARGUMENTS:
130 * Thread = Thread to test for alerts
131 * UserContext = The user context saved on entry to kernel mode
132 */
133 {
134 PLIST_ENTRY current_entry;
135 PKAPC Apc;
136 PULONG Esp;
137 PCONTEXT Context;
138 KIRQL oldlvl;
139 PKTHREAD Thread;
140
141 DPRINT("KiDeliverUserApc(TrapFrame %x/%x)\n", TrapFrame,
142 KeGetCurrentThread()->TrapFrame);
143 Thread = KeGetCurrentThread();
144
145 /*
146 * Check for thread termination
147 */
148
149 KeAcquireSpinLock(&PiApcLock, &oldlvl);
150
151 current_entry = Thread->ApcState.ApcListHead[1].Flink;
152
153 /*
154 * Shouldn't happen but check anyway.
155 */
156 if (current_entry == &Thread->ApcState.ApcListHead[1])
157 {
158 KeReleaseSpinLock(&PiApcLock, oldlvl);
159 DbgPrint("KiDeliverUserApc called but no APC was pending\n");
160 return(FALSE);
161 }
162
163 while (!IsListEmpty(&Thread->ApcState.ApcListHead[1]))
164 {
165 current_entry = RemoveHeadList(&Thread->ApcState.ApcListHead[1]);
166 Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
167 Apc->Inserted = FALSE;
168
169 /*
170 * We've dealt with one pending user-mode APC
171 */
172 Thread->ApcState.UserApcPending--;
173 KeReleaseSpinLock(&PiApcLock, oldlvl);
174
175 /*
176 * Save the thread's current context (in other words the registers
177 * that will be restored when it returns to user mode) so the
178 * APC dispatcher can restore them later
179 */
180 Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT));
181 memset(Context, 0, sizeof(CONTEXT));
182 Context->ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER |
183 CONTEXT_SEGMENTS | CONTEXT_i386;
184 Context->SegGs = TrapFrame->Gs;
185 Context->SegFs = TrapFrame->Fs;
186 Context->SegEs = TrapFrame->Es;
187 Context->SegDs = TrapFrame->Ds;
188 Context->Edi = TrapFrame->Edi;
189 Context->Esi = TrapFrame->Esi;
190 Context->Ebx = TrapFrame->Ebx;
191 Context->Edx = TrapFrame->Edx;
192 Context->Ecx = TrapFrame->Ecx;
193 Context->Eax = TrapFrame->Eax;
194 Context->Ebp = TrapFrame->Ebp;
195 Context->Eip = TrapFrame->Eip;
196 Context->SegCs = TrapFrame->Cs;
197 Context->EFlags = TrapFrame->Eflags;
198 Context->Esp = TrapFrame->Esp;
199 Context->SegSs = TrapFrame->Ss;
200
201 /*
202 * Setup the trap frame so the thread will start executing at the
203 * APC Dispatcher when it returns to user-mode
204 */
205 Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) -
206 (sizeof(CONTEXT) + (6 * sizeof(ULONG))));
207
208 Esp[0] = 0xdeadbeef;
209 Esp[1] = (ULONG)Apc->NormalRoutine;
210 Esp[2] = (ULONG)Apc->NormalContext;
211 Esp[3] = (ULONG)Apc->SystemArgument1;
212 Esp[4] = (ULONG)Apc->SystemArgument2;
213 Esp[5] = (ULONG)Context;
214 TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher();
215 TrapFrame->Esp = (ULONG)Esp;
216
217
218 /*
219 * Now call for the kernel routine for the APC, which will free
220 * the APC data structure, we can't do this ourselves because
221 * the APC may be embedded in some larger structure e.g. an IRP
222 * We also give the kernel routine a last chance to modify the
223 * arguments to the user APC routine.
224 */
225 Apc->KernelRoutine(Apc,
226 (PKNORMAL_ROUTINE*)&Esp[1],
227 (PVOID*)&Esp[2],
228 (PVOID*)&Esp[3],
229 (PVOID*)&Esp[4]);
230
231 KeAcquireSpinLock(&PiApcLock, &oldlvl);
232 }
233 Thread->Alerted[0] = 0;
234 KeReleaseSpinLock(&PiApcLock, oldlvl);
235
236 return(TRUE);
237 }
238
239 /*
240 * @implemented
241 */
242 VOID STDCALL
243 KiDeliverApc(ULONG Unknown1,
244 ULONG Unknown2,
245 ULONG Unknown3)
246 /*
247 * FUNCTION: Deliver an APC to the current thread.
248 * NOTES: This is called from the IRQL switching code if the current thread
249 * is returning from an IRQL greater than or equal to APC_LEVEL to
250 * PASSIVE_LEVEL and there are kernel-mode APCs pending. This means any
251 * pending APCs will be delivered after a thread gets a new quantum and
252 * after it wakes from a wait.
253 */
254 {
255 PETHREAD Thread = PsGetCurrentThread();
256 PLIST_ENTRY current_entry;
257 PKAPC Apc;
258 KIRQL oldlvl;
259
260 DPRINT("KiDeliverApc()\n");
261 KeAcquireSpinLock(&PiApcLock, &oldlvl);
262 current_entry = Thread->Tcb.ApcState.ApcListHead[0].Flink;
263 while(current_entry != &Thread->Tcb.ApcState.ApcListHead[0])
264 {
265 Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
266 if (Apc->NormalRoutine == NULL)
267 {
268 Apc->Inserted = FALSE;
269 RemoveEntryList(&Apc->ApcListEntry);
270 Thread->Tcb.ApcState.KernelApcInProgress++;
271 Thread->Tcb.ApcState.KernelApcPending--;
272
273 KeReleaseSpinLock(&PiApcLock, oldlvl);
274
275 Apc->KernelRoutine(Apc,
276 &Apc->NormalRoutine,
277 &Apc->NormalContext,
278 &Apc->SystemArgument1,
279 &Apc->SystemArgument2);
280
281 KeAcquireSpinLock(&PiApcLock, &oldlvl);
282 Thread->Tcb.ApcState.KernelApcInProgress--;
283 current_entry = Thread->Tcb.ApcState.ApcListHead[0].Flink;
284 }
285 else
286 {
287 current_entry = current_entry->Flink;
288 }
289 }
290 KeReleaseSpinLock(&PiApcLock, oldlvl);
291 }
292
293 /*
294 * @implemented
295 */
296 BOOLEAN STDCALL
297 KeInsertQueueApc (PKAPC Apc,
298 PVOID SystemArgument1,
299 PVOID SystemArgument2,
300 KPRIORITY PriorityBoost)
301 /*
302 * FUNCTION: Queues an APC for execution
303 * ARGUMENTS:
304 * Apc = APC to be queued
305 * SystemArgument[1-2] = TBD
306 * Mode = TBD
307 */
308 {
309 //FIXME: return FALSE if APC can't be queued to target thread (thread has ended)
310 KIRQL oldlvl;
311 PKTHREAD TargetThread;
312
313 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
314 "SystemArgument2 %x)\n",Apc,SystemArgument1,
315 SystemArgument2);
316
317 KeAcquireSpinLock(&PiApcLock, &oldlvl);
318
319 Apc->SystemArgument1 = SystemArgument1;
320 Apc->SystemArgument2 = SystemArgument2;
321
322 if (Apc->Inserted)
323 {
324 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
325 KeBugCheck(0);
326 }
327
328 TargetThread = Apc->Thread;
329 if (Apc->ApcMode == KernelMode)
330 {
331 InsertTailList(&TargetThread->ApcState.ApcListHead[0],
332 &Apc->ApcListEntry);
333 TargetThread->ApcState.KernelApcPending++;
334 }
335 else
336 {
337 InsertTailList(&TargetThread->ApcState.ApcListHead[1],
338 &Apc->ApcListEntry);
339 TargetThread->ApcState.UserApcPending++;
340 }
341 Apc->Inserted = TRUE;
342
343 /*
344 * If this is a kernel-mode APC for the current thread and we are not
345 * inside a critical section or at APC level then call it, in fact we
346 * rely on the side effects of dropping the IRQL level when we release
347 * the spinlock
348 */
349 if (Apc->ApcMode == KernelMode && TargetThread == KeGetCurrentThread() &&
350 Apc->NormalRoutine == NULL)
351 {
352 KeReleaseSpinLock(&PiApcLock, oldlvl);
353 return TRUE;
354 }
355
356 /*
357 * If this is a kernel-mode APC and it is waiting at PASSIVE_LEVEL and
358 * not inside a critical section then wake it up. Otherwise it will
359 * execute the APC as soon as it returns to PASSIVE_LEVEL.
360 * FIXME: If the thread is running on another processor then send an
361 * IPI.
362 * FIXME: Check if the thread is terminating.
363 */
364 if (Apc->ApcMode == KernelMode && TargetThread->WaitIrql < APC_LEVEL &&
365 Apc->NormalRoutine == NULL)
366 {
367 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
368 STATUS_KERNEL_APC);
369 }
370
371 /*
372 * If this is a 'funny' user-mode APC then mark the thread as
373 * alerted so it will execute the APC on exit from kernel mode. If it
374 * is waiting alertably then wake it up so it can return to user mode.
375 */
376 if (Apc->ApcMode == KernelMode && Apc->NormalRoutine != NULL)
377 {
378 TargetThread->Alerted[1] = 1;
379 if (TargetThread->Alertable == TRUE &&
380 TargetThread->WaitMode == UserMode)
381 {
382 PETHREAD Thread;
383
384 Thread = CONTAINING_RECORD(TargetThread, ETHREAD, Tcb);
385 KeRemoveAllWaitsThread(Thread, STATUS_USER_APC);
386 }
387 }
388
389 /*
390 * If the thread is waiting alertably then wake it up and it will
391 * return to to user-mode executing the APC in the process. Otherwise the
392 * thread will execute the APC next time it enters an alertable wait.
393 */
394 if (Apc->ApcMode == UserMode && TargetThread->Alertable == TRUE &&
395 TargetThread->WaitMode == UserMode)
396 {
397 NTSTATUS Status;
398
399 DPRINT("Resuming thread for user APC\n");
400
401 Status = STATUS_USER_APC;
402 TargetThread->Alerted[0] = 1;
403 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
404 STATUS_USER_APC);
405 }
406 KeReleaseSpinLock(&PiApcLock, oldlvl);
407 return TRUE;
408 }
409
410 BOOLEAN STDCALL
411 KeRemoveQueueApc (PKAPC Apc)
412 /*
413 * FUNCTION: Removes APC object from the apc queue
414 * ARGUMENTS:
415 * Apc = APC to remove
416 * RETURNS: TRUE if the APC was in the queue
417 * FALSE otherwise
418 * NOTE: This function is not exported.
419 */
420 {
421 KIRQL oldIrql;
422 PKTHREAD TargetThread;
423
424 KeRaiseIrql(HIGH_LEVEL, &oldIrql);
425 KeAcquireSpinLockAtDpcLevel(&PiApcLock);
426 if (Apc->Inserted == FALSE)
427 {
428 KeReleaseSpinLock(&PiApcLock, oldIrql);
429 return(FALSE);
430 }
431
432 TargetThread = Apc->Thread;
433 RemoveEntryList(&Apc->ApcListEntry);
434 if (Apc->ApcMode == KernelMode)
435 {
436 TargetThread->ApcState.KernelApcPending--;
437 }
438 else
439 {
440 TargetThread->ApcState.UserApcPending--;
441 }
442 Apc->Inserted = FALSE;
443
444 KeReleaseSpinLock(&PiApcLock, oldIrql);
445 return(TRUE);
446 }
447
448
449 /*
450 * @implemented
451 */
452 VOID STDCALL
453 KeInitializeApc(
454 IN PKAPC Apc,
455 IN PKTHREAD Thread,
456 IN UCHAR StateIndex,
457 IN PKKERNEL_ROUTINE KernelRoutine,
458 IN PKRUNDOWN_ROUTINE RundownRoutine,
459 IN PKNORMAL_ROUTINE NormalRoutine,
460 IN UCHAR Mode,
461 IN PVOID Context)
462 /*
463 * FUNCTION: Initialize an APC object
464 * ARGUMENTS:
465 * Apc = Pointer to the APC object to initialized
466 * Thread = Thread the APC is to be delivered to
467 * StateIndex = KAPC_ENVIRONMENT
468 * KernelRoutine = Routine to be called for a kernel-mode APC
469 * RundownRoutine = Routine to be called if the thread has exited with
470 * the APC being executed
471 * NormalRoutine = Routine to be called for a user-mode APC
472 * Mode = APC mode
473 * Context = Parameter to be passed to the APC routine
474 */
475 {
476 KAPC_ENVIRONMENT Environment = (KAPC_ENVIRONMENT) StateIndex;
477
478 DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
479 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
480 "Context %x)\n",Apc,Thread,Environment,KernelRoutine,RundownRoutine,
481 NormalRoutine,Mode,Context);
482
483 memset(Apc, 0, sizeof(KAPC));
484 Apc->Thread = Thread;
485 Apc->ApcListEntry.Flink = NULL;
486 Apc->ApcListEntry.Blink = NULL;
487 Apc->KernelRoutine = KernelRoutine;
488 Apc->RundownRoutine = RundownRoutine;
489 Apc->NormalRoutine = NormalRoutine;
490 Apc->NormalContext = Context;
491 Apc->Inserted = FALSE;
492
493 if (Environment == CurrentApcEnvironment)
494 {
495 Apc->ApcStateIndex = Thread->ApcStateIndex;
496 }
497 else
498 {
499 Apc->ApcStateIndex = Environment;
500 }
501
502 if (Apc->NormalRoutine != NULL)
503 {
504 Apc->ApcMode = (KPROCESSOR_MODE) Mode;
505 }
506 else
507 {
508 Apc->ApcMode = KernelMode;
509 }
510 }
511
512 VOID STDCALL
513 NtQueueApcRundownRoutine(PKAPC Apc)
514 {
515 ExFreePool(Apc);
516 }
517
518 VOID STDCALL
519 NtQueueApcKernelRoutine(PKAPC Apc,
520 PKNORMAL_ROUTINE* NormalRoutine,
521 PVOID* NormalContext,
522 PVOID* SystemArgument1,
523 PVOID* SystemArgument2)
524 {
525 ExFreePool(Apc);
526 }
527
528 NTSTATUS STDCALL
529 NtQueueApcThread(HANDLE ThreadHandle,
530 PKNORMAL_ROUTINE ApcRoutine,
531 PVOID NormalContext,
532 PVOID SystemArgument1,
533 PVOID SystemArgument2)
534 {
535 PKAPC Apc;
536 PETHREAD Thread;
537 NTSTATUS Status;
538
539 Status = ObReferenceObjectByHandle(ThreadHandle,
540 THREAD_ALL_ACCESS, /* FIXME */
541 PsThreadType,
542 UserMode,
543 (PVOID*)&Thread,
544 NULL);
545 if (!NT_SUCCESS(Status))
546 {
547 return(Status);
548 }
549
550 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_KAPC);
551 if (Apc == NULL)
552 {
553 ObDereferenceObject(Thread);
554 return(STATUS_NO_MEMORY);
555 }
556
557 KeInitializeApc(Apc,
558 &Thread->Tcb,
559 OriginalApcEnvironment,
560 NtQueueApcKernelRoutine,
561 NtQueueApcRundownRoutine,
562 ApcRoutine,
563 UserMode,
564 NormalContext);
565 KeInsertQueueApc(Apc,
566 SystemArgument1,
567 SystemArgument2,
568 IO_NO_INCREMENT);
569
570 ObDereferenceObject(Thread);
571 return(STATUS_SUCCESS);
572 }
573
574
575 NTSTATUS STDCALL NtTestAlert(VOID)
576 {
577 KiTestAlert();
578 return(STATUS_SUCCESS);
579 }
580
581 VOID PiInitApcManagement(VOID)
582 {
583 KeInitializeSpinLock(&PiApcLock);
584 }
585