- Disable APC Queuing & Flush APC queues during thread shutdown.
[reactos.git] / reactos / ntoskrnl / ke / apc.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/apc.c
5 * PURPOSE: NT Implementation of APCs
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * Phillip Susi
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* FUNCTIONS *****************************************************************/
18
19 /*++
20 * KiKernelApcDeliveryCheck
21 * @implemented NT 5.2
22 *
23 * The KiKernelApcDeliveryCheck routine is called whenever APCs have just
24 * been re-enabled in Kernel Mode, such as after leaving a Critical or
25 * Guarded Region. It delivers APCs if the environment is right.
26 *
27 * Params:
28 * None.
29 *
30 * Returns:
31 * None.
32 *
33 * Remarks:
34 * This routine allows KeLeave/EnterCritical/GuardedRegion to be used as a
35 * macro from inside WIN32K or other Drivers, which will then only have to
36 * do an Import API call in the case where APCs are enabled again.
37 *
38 *--*/
39 VOID
40 STDCALL
41 KiKernelApcDeliveryCheck(VOID)
42 {
43 /* We should only deliver at passive */
44 if (KeGetCurrentIrql() == PASSIVE_LEVEL)
45 {
46 /* Raise to APC and Deliver APCs, then lower back to Passive */
47 KfRaiseIrql(APC_LEVEL);
48 KiDeliverApc(KernelMode, 0, 0);
49 KfLowerIrql(PASSIVE_LEVEL);
50 }
51 else
52 {
53 /*
54 * If we're not at passive level it means someone raised IRQL
55 * to APC level before the a critical or guarded section was entered
56 * (e.g) by a fast mutex). This implies that the APCs shouldn't
57 * be delivered now, but after the IRQL is lowered to passive
58 * level again.
59 */
60 HalRequestSoftwareInterrupt(APC_LEVEL);
61 }
62 }
63
64 /*++
65 * KeEnterCriticalRegion
66 * @implemented NT4
67 *
68 * The KeEnterCriticalRegion routine temporarily disables the delivery of
69 * normal kernel APCs; special kernel-mode APCs are still delivered.
70 *
71 * Params:
72 * None.
73 *
74 * Returns:
75 * None.
76 *
77 * Remarks:
78 * Highest-level drivers can call this routine while running in the context
79 * of the thread that requested the current I/O operation. Any caller of
80 * this routine should call KeLeaveCriticalRegion as quickly as possible.
81 *
82 * Callers of KeEnterCriticalRegion must be running at IRQL <= APC_LEVEL.
83 *
84 *--*/
85 #undef KeEnterCriticalRegion
86 VOID
87 STDCALL
88 KeEnterCriticalRegion(VOID)
89 {
90 /* Disable Kernel APCs */
91 PKTHREAD Thread = KeGetCurrentThread();
92 if (Thread) Thread->KernelApcDisable--;
93 }
94
95 /*++
96 * KeLeaveCriticalRegion
97 * @implemented NT4
98 *
99 * The KeLeaveCriticalRegion routine reenables the delivery of normal
100 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
101 *
102 * Params:
103 * None.
104 *
105 * Returns:
106 * None.
107 *
108 * Remarks:
109 * Highest-level drivers can call this routine while running in the context
110 * of the thread that requested the current I/O operation.
111 *
112 * Callers of KeLeaveCriticalRegion must be running at IRQL <= DISPATCH_LEVEL.
113 *
114 *--*/
115 #undef KeLeaveCriticalRegion
116 VOID
117 STDCALL
118 KeLeaveCriticalRegion (VOID)
119 {
120 PKTHREAD Thread = KeGetCurrentThread();
121
122 /* Check if Kernel APCs are now enabled */
123 if((Thread) && (++Thread->KernelApcDisable == 0))
124 {
125 /* Check if we need to request an APC Delivery */
126 if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
127 {
128 /* Check for the right environment */
129 KiKernelApcDeliveryCheck();
130 }
131 }
132 }
133
134 /*++
135 * KeInitializeApc
136 * @implemented NT4
137 *
138 * The The KeInitializeApc routine initializes an APC object, and registers
139 * the Kernel, Rundown and Normal routines for that object.
140 *
141 * Params:
142 * Apc - Pointer to a KAPC structure that represents the APC object to
143 * initialize. The caller must allocate storage for the structure
144 * from resident memory.
145 *
146 * Thread - Thread to which to deliver the APC.
147 *
148 * TargetEnvironment - APC Environment to be used.
149 *
150 * KernelRoutine - Points to the KernelRoutine to associate with the APC.
151 * This routine is executed for all APCs.
152 *
153 * RundownRoutine - Points to the RundownRoutine to associate with the APC.
154 * This routine is executed when the Thread exists with
155 * the APC executing.
156 *
157 * NormalRoutine - Points to the NormalRoutine to associate with the APC.
158 * This routine is executed at PASSIVE_LEVEL. If this is
159 * not specifed, the APC becomes a Special APC and the
160 * Mode and Context parameters are ignored.
161 *
162 * Mode - Specifies the processor mode at which to run the Normal Routine.
163 *
164 * Context - Specifices the value to pass as Context parameter to the
165 * registered routines.
166 *
167 * Returns:
168 * None.
169 *
170 * Remarks:
171 * The caller can queue an initialized APC with KeInsertQueueApc.
172 *
173 * Storage for the APC object must be resident, such as nonpaged pool
174 * allocated by the caller.
175 *
176 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
177 *
178 *--*/
179 VOID
180 STDCALL
181 KeInitializeApc(IN PKAPC Apc,
182 IN PKTHREAD Thread,
183 IN KAPC_ENVIRONMENT TargetEnvironment,
184 IN PKKERNEL_ROUTINE KernelRoutine,
185 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
186 IN PKNORMAL_ROUTINE NormalRoutine,
187 IN KPROCESSOR_MODE Mode,
188 IN PVOID Context)
189 {
190 DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
191 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
192 "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine,
193 NormalRoutine,Mode,Context);
194
195 /* Set up the basic APC Structure Data */
196 RtlZeroMemory(Apc, sizeof(KAPC));
197 Apc->Type = ApcObject;
198 Apc->Size = sizeof(KAPC);
199
200 /* Set the Environment */
201 if (TargetEnvironment == CurrentApcEnvironment) {
202
203 Apc->ApcStateIndex = Thread->ApcStateIndex;
204
205 } else {
206
207 Apc->ApcStateIndex = TargetEnvironment;
208 }
209
210 /* Set the Thread and Routines */
211 Apc->Thread = Thread;
212 Apc->KernelRoutine = KernelRoutine;
213 Apc->RundownRoutine = RundownRoutine;
214 Apc->NormalRoutine = NormalRoutine;
215
216 /* Check if this is a Special APC, in which case we use KernelMode and no Context */
217 if (ARGUMENT_PRESENT(NormalRoutine)) {
218
219 Apc->ApcMode = Mode;
220 Apc->NormalContext = Context;
221
222 } else {
223
224 Apc->ApcMode = KernelMode;
225 }
226 }
227
228 /*++
229 * KiInsertQueueApc
230 *
231 * The KiInsertQueueApc routine queues a APC for execution when the right
232 * scheduler environment exists.
233 *
234 * Params:
235 * Apc - Pointer to an initialized control object of type DPC for which the
236 * caller provides the storage.
237 *
238 * PriorityBoost - Priority Boost to apply to the Thread.
239 *
240 * Returns:
241 * If the APC is already inserted or APC queueing is disabled, FALSE.
242 * Otherwise, TRUE.
243 *
244 * Remarks:
245 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
246 * at PASSIVE_LEVEL for the NormalRoutine registered.
247 *
248 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
249 *
250 *--*/
251 BOOLEAN
252 STDCALL
253 KiInsertQueueApc(PKAPC Apc,
254 KPRIORITY PriorityBoost)
255 {
256 PKTHREAD Thread = Apc->Thread;
257 PLIST_ENTRY ApcListEntry;
258 PKAPC QueuedApc;
259
260 /* Don't do anything if the APC is already inserted */
261 if (Apc->Inserted) {
262
263 return FALSE;
264 }
265
266 /* Three scenarios:
267 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
268 2) User APC which is PsExitSpecialApc = Put it at the front of the List
269 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
270 */
271 if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) {
272
273 DPRINT1("Inserting the Process Exit APC into the Queue\n");
274 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = TRUE;
275 InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
276 &Apc->ApcListEntry);
277
278 } else if (Apc->NormalRoutine == NULL) {
279
280 DPRINT("Inserting Special APC %x into the Queue\n", Apc);
281
282 for (ApcListEntry = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode].Flink;
283 ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode];
284 ApcListEntry = ApcListEntry->Flink) {
285
286 QueuedApc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
287 if (Apc->NormalRoutine != NULL) break;
288 }
289
290 /* We found the first "Normal" APC, so write right before it */
291 ApcListEntry = ApcListEntry->Blink;
292 InsertHeadList(ApcListEntry, &Apc->ApcListEntry);
293
294 } else {
295
296 DPRINT("Inserting Normal APC %x into the %x Queue\n", Apc, Apc->ApcMode);
297 InsertTailList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
298 &Apc->ApcListEntry);
299 }
300
301 /* Confirm Insertion */
302 Apc->Inserted = TRUE;
303
304 /*
305 * Three possibilites here again:
306 * 1) Kernel APC, The thread is Running: Request an Interrupt
307 * 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread
308 * 3) User APC, Unwait the Thread if it is alertable
309 */
310 if (Apc->ApcMode == KernelMode) {
311
312 /* Set Kernel APC pending */
313 Thread->ApcState.KernelApcPending = TRUE;
314
315 /* Check the Thread State */
316 if (Thread->State == Running) {
317
318 #ifdef CONFIG_SMP
319 PKPRCB Prcb, CurrentPrcb;
320 LONG i;
321 KIRQL oldIrql;
322 #endif
323
324 DPRINT ("Requesting APC Interrupt for Running Thread \n");
325
326 #ifdef CONFIG_SMP
327 oldIrql = KeRaiseIrqlToDpcLevel();
328 CurrentPrcb = KeGetCurrentPrcb();
329 if (CurrentPrcb->CurrentThread == Thread)
330 {
331 HalRequestSoftwareInterrupt(APC_LEVEL);
332 }
333 else
334 {
335 for (i = 0; i < KeNumberProcessors; i++)
336 {
337 Prcb = ((PKPCR)(KPCR_BASE + i * PAGE_SIZE))->Prcb;
338 if (Prcb->CurrentThread == Thread)
339 {
340 ASSERT (CurrentPrcb != Prcb);
341 KiIpiSendRequest(Prcb->SetMember, IPI_REQUEST_APC);
342 break;
343 }
344 }
345 ASSERT (i < KeNumberProcessors);
346 }
347 KeLowerIrql(oldIrql);
348 #else
349 HalRequestSoftwareInterrupt(APC_LEVEL);
350 #endif
351
352 } else if ((Thread->State == Waiting) && (Thread->WaitIrql == PASSIVE_LEVEL) &&
353 ((Apc->NormalRoutine == NULL) ||
354 ((!Thread->KernelApcDisable) && (!Thread->ApcState.KernelApcInProgress)))) {
355
356 DPRINT("Waking up Thread for Kernel-Mode APC Delivery \n");
357 KiAbortWaitThread(Thread, STATUS_KERNEL_APC, PriorityBoost);
358 }
359
360 } else if ((Thread->State == Waiting) &&
361 (Thread->WaitMode == UserMode) &&
362 (Thread->Alertable)) {
363
364 DPRINT("Waking up Thread for User-Mode APC Delivery \n");
365 Thread->ApcState.UserApcPending = TRUE;
366 KiAbortWaitThread(Thread, STATUS_USER_APC, PriorityBoost);
367 }
368
369 return TRUE;
370 }
371
372 /*++
373 * KeInsertQueueApc
374 * @implemented NT4
375 *
376 * The KeInsertQueueApc routine queues a APC for execution when the right
377 * scheduler environment exists.
378 *
379 * Params:
380 * Apc - Pointer to an initialized control object of type DPC for which the
381 * caller provides the storage.
382 *
383 * SystemArgument[1,2] - Pointer to a set of two parameters that contain
384 * untyped data.
385 *
386 * PriorityBoost - Priority Boost to apply to the Thread.
387 *
388 * Returns:
389 * If the APC is already inserted or APC queueing is disabled, FALSE.
390 * Otherwise, TRUE.
391 *
392 * Remarks:
393 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
394 * at PASSIVE_LEVEL for the NormalRoutine registered.
395 *
396 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
397 *
398 *--*/
399 BOOLEAN
400 STDCALL
401 KeInsertQueueApc(PKAPC Apc,
402 PVOID SystemArgument1,
403 PVOID SystemArgument2,
404 KPRIORITY PriorityBoost)
405
406 {
407 KIRQL OldIrql;
408 PKTHREAD Thread;
409 BOOLEAN Inserted;
410
411 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
412 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
413 "SystemArgument2 %x)\n",Apc,SystemArgument1,
414 SystemArgument2);
415
416 /* Lock the Dispatcher Database */
417 OldIrql = KeAcquireDispatcherDatabaseLock();
418
419 /* Get the Thread specified in the APC */
420 Thread = Apc->Thread;
421
422 /* Make sure the thread allows APC Queues.
423 * The thread is not apc queueable, for instance, when it's (about to be) terminated.
424 */
425 if (Thread->ApcQueueable == FALSE) {
426 DPRINT("Thread doesn't allow APC Queues\n");
427 KeReleaseDispatcherDatabaseLock(OldIrql);
428 return FALSE;
429 }
430
431 /* Set the System Arguments */
432 Apc->SystemArgument1 = SystemArgument1;
433 Apc->SystemArgument2 = SystemArgument2;
434
435 /* Call the Internal Function */
436 Inserted = KiInsertQueueApc(Apc, PriorityBoost);
437
438 /* Return Sucess if we are here */
439 KeReleaseDispatcherDatabaseLock(OldIrql);
440 return Inserted;
441 }
442
443 /*++
444 * KeFlushQueueApc
445 *
446 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
447 * from the specified Thread's APC queue.
448 *
449 * Params:
450 * Thread - Pointer to the thread whose APC queue will be flushed.
451 *
452 * PreviousMode - Specifies which APC Queue to flush.
453 *
454 * Returns:
455 * A pointer to the first entry in the flushed APC queue.
456 *
457 * Remarks:
458 * If the routine returns NULL, it means that no APCs were to be flushed.
459 *
460 * Callers of KeFlushQueueApc must be running at DISPATCH_LEVEL or lower.
461 *
462 *--*/
463 PLIST_ENTRY
464 STDCALL
465 KeFlushQueueApc(IN PKTHREAD Thread,
466 IN KPROCESSOR_MODE PreviousMode)
467 {
468 KIRQL OldIrql;
469 PKAPC Apc;
470 PLIST_ENTRY ApcEntry, CurrentEntry;
471
472 /* Lock the Dispatcher Database and APC Queue */
473 OldIrql = KeAcquireDispatcherDatabaseLock();
474 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
475
476 /* Check if the list is empty */
477 if (IsListEmpty(&Thread->ApcState.ApcListHead[PreviousMode]))
478 {
479 /* We'll return NULL */
480 ApcEntry = NULL;
481 }
482 else
483 {
484 /* Remove this one */
485 RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]);
486 CurrentEntry = ApcEntry;
487
488 /* Remove all the other ones too, if present */
489 do
490 {
491 /* Get the APC */
492 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
493
494 /* Move to the next one */
495 CurrentEntry = CurrentEntry->Flink;
496
497 /* Mark it as not inserted */
498 Apc->Inserted = FALSE;
499 } while (ApcEntry != CurrentEntry);
500 }
501
502 /* Release the locks */
503 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
504 KeReleaseDispatcherDatabaseLock(OldIrql);
505
506 /* Return the first entry */
507 return ApcEntry;
508 }
509
510 /*++
511 * KeRemoveQueueApc
512 *
513 * The KeRemoveQueueApc routine removes a given APC object from the system
514 * APC queue.
515 *
516 * Params:
517 * APC - Pointer to an initialized APC object that was queued by calling
518 * KeInsertQueueApc.
519 *
520 * Returns:
521 * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is
522 * performed and FALSE is returned.
523 *
524 * Remarks:
525 * If the given APC Object is currently queued, it is removed from the queue
526 * and any calls to the registered routines are cancelled.
527 *
528 * Callers of KeLeaveCriticalRegion can be running at any IRQL.
529 *
530 *--*/
531 BOOLEAN
532 STDCALL
533 KeRemoveQueueApc(PKAPC Apc)
534 {
535 KIRQL OldIrql;
536 PKTHREAD Thread = Apc->Thread;
537
538 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
539 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc);
540
541 OldIrql = KeAcquireDispatcherDatabaseLock();
542 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
543
544 /* Check if it's inserted */
545 if (Apc->Inserted) {
546
547 /* Remove it from the Queue*/
548 RemoveEntryList(&Apc->ApcListEntry);
549 Apc->Inserted = FALSE;
550
551 /* If the Queue is completely empty, then no more APCs are pending */
552 if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) {
553
554 /* Set the correct State based on the Apc Mode */
555 if (Apc->ApcMode == KernelMode) {
556
557 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE;
558
559 } else {
560
561 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE;
562 }
563 }
564
565 } else {
566
567 /* It's not inserted, fail */
568 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
569 KeReleaseDispatcherDatabaseLock(OldIrql);
570 return(FALSE);
571 }
572
573 /* Restore IRQL and Return */
574 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
575 KeReleaseDispatcherDatabaseLock(OldIrql);
576 return(TRUE);
577 }
578
579 /*++
580 * KiDeliverApc
581 * @implemented @NT4
582 *
583 * The KiDeliverApc routine is called from IRQL switching code if the
584 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
585 * pending.
586 *
587 * Params:
588 * DeliveryMode - Specifies the current processor mode.
589 *
590 * Reserved - Pointer to the Exception Frame on non-i386 builds.
591 *
592 * TrapFrame - Pointer to the Trap Frame.
593 *
594 * Returns:
595 * None.
596 *
597 * Remarks:
598 * First, Special APCs are delivered, followed by Kernel-Mode APCs and
599 * User-Mode APCs. Note that the TrapFrame is only valid if the previous
600 * mode is User.
601 *
602 * Upon entry, this routine executes at APC_LEVEL.
603 *
604 *--*/
605 VOID
606 STDCALL
607 KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
608 PVOID Reserved,
609 PKTRAP_FRAME TrapFrame)
610 {
611 PKTHREAD Thread = KeGetCurrentThread();
612 PLIST_ENTRY ApcListEntry;
613 PKAPC Apc;
614 KIRQL OldIrql;
615 PKKERNEL_ROUTINE KernelRoutine;
616 PVOID NormalContext;
617 PKNORMAL_ROUTINE NormalRoutine;
618 PVOID SystemArgument1;
619 PVOID SystemArgument2;
620
621 ASSERT_IRQL_EQUAL(APC_LEVEL);
622
623 /* Lock the APC Queue and Raise IRQL to Synch */
624 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
625
626 /* Clear APC Pending */
627 Thread->ApcState.KernelApcPending = FALSE;
628
629 /* Do the Kernel APCs first */
630 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) {
631
632 /* Get the next Entry */
633 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
634 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
635
636 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
637 NormalRoutine = Apc->NormalRoutine;
638 KernelRoutine = Apc->KernelRoutine;
639 NormalContext = Apc->NormalContext;
640 SystemArgument1 = Apc->SystemArgument1;
641 SystemArgument2 = Apc->SystemArgument2;
642
643 /* Special APC */
644 if (NormalRoutine == NULL) {
645
646 /* Remove the APC from the list */
647 RemoveEntryList(ApcListEntry);
648 Apc->Inserted = FALSE;
649
650 /* Go back to APC_LEVEL */
651 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
652
653 /* Call the Special APC */
654 DPRINT("Delivering a Special APC: %x\n", Apc);
655 KernelRoutine(Apc,
656 &NormalRoutine,
657 &NormalContext,
658 &SystemArgument1,
659 &SystemArgument2);
660
661 /* Raise IRQL and Lock again */
662 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
663
664 } else {
665
666 /* Normal Kernel APC */
667 if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) {
668
669 /*
670 * DeliveryMode must be KernelMode in this case, since one may not
671 * return to umode while being inside a critical section or while
672 * a regular kmode apc is running (the latter should be impossible btw).
673 * -Gunnar
674 */
675 ASSERT(DeliveryMode == KernelMode);
676
677 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
678 return;
679 }
680
681 /* Dequeue the APC */
682 Apc->Inserted = FALSE;
683 RemoveEntryList(ApcListEntry);
684
685 /* Go back to APC_LEVEL */
686 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
687
688 /* Call the Kernel APC */
689 DPRINT("Delivering a Normal APC: %x\n", Apc);
690 KernelRoutine(Apc,
691 &NormalRoutine,
692 &NormalContext,
693 &SystemArgument1,
694 &SystemArgument2);
695
696 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
697 if (NormalRoutine != NULL) {
698
699 /* At Passive Level, this APC can be prempted by a Special APC */
700 Thread->ApcState.KernelApcInProgress = TRUE;
701 KeLowerIrql(PASSIVE_LEVEL);
702
703 /* Call and Raise IRQ back to APC_LEVEL */
704 DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc);
705 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
706 KeRaiseIrql(APC_LEVEL, &OldIrql);
707 }
708
709 /* Raise IRQL and Lock again */
710 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
711 Thread->ApcState.KernelApcInProgress = FALSE;
712 }
713 }
714
715 /* Now we do the User APCs */
716 if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
717 (DeliveryMode == UserMode) && (Thread->ApcState.UserApcPending == TRUE)) {
718
719 /* It's not pending anymore */
720 Thread->ApcState.UserApcPending = FALSE;
721
722 /* Get the APC Object */
723 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
724 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
725
726 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
727 NormalRoutine = Apc->NormalRoutine;
728 KernelRoutine = Apc->KernelRoutine;
729 NormalContext = Apc->NormalContext;
730 SystemArgument1 = Apc->SystemArgument1;
731 SystemArgument2 = Apc->SystemArgument2;
732
733 /* Remove the APC from Queue, restore IRQL and call the APC */
734 RemoveEntryList(ApcListEntry);
735 Apc->Inserted = FALSE;
736 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
737
738 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc);
739 KernelRoutine(Apc,
740 &NormalRoutine,
741 &NormalContext,
742 &SystemArgument1,
743 &SystemArgument2);
744
745 if (NormalRoutine == NULL) {
746
747 /* Check if more User APCs are Pending */
748 KeTestAlertThread(UserMode);
749
750 } else {
751
752 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
753 DPRINT("Delivering a User APC: %x\n", Apc);
754 KiInitializeUserApc(Reserved,
755 TrapFrame,
756 NormalRoutine,
757 NormalContext,
758 SystemArgument1,
759 SystemArgument2);
760 }
761
762 } else {
763
764 /* Go back to APC_LEVEL */
765 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
766 }
767 }
768
769 VOID
770 STDCALL
771 KiFreeApcRoutine(PKAPC Apc,
772 PKNORMAL_ROUTINE* NormalRoutine,
773 PVOID* NormalContext,
774 PVOID* SystemArgument1,
775 PVOID* SystemArgument2)
776 {
777 /* Free the APC and do nothing else */
778 ExFreePool(Apc);
779 }
780
781 /*++
782 * KiInitializeUserApc
783 *
784 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
785 *
786 * Params:
787 * Reserved - Pointer to the Exception Frame on non-i386 builds.
788 *
789 * TrapFrame - Pointer to the Trap Frame.
790 *
791 * NormalRoutine - Pointer to the NormalRoutine to call.
792 *
793 * NormalContext - Pointer to the context to send to the Normal Routine.
794 *
795 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
796 * untyped data.
797 *
798 * Returns:
799 * None.
800 *
801 * Remarks:
802 * None.
803 *
804 *--*/
805 VOID
806 STDCALL
807 KiInitializeUserApc(IN PVOID Reserved,
808 IN PKTRAP_FRAME TrapFrame,
809 IN PKNORMAL_ROUTINE NormalRoutine,
810 IN PVOID NormalContext,
811 IN PVOID SystemArgument1,
812 IN PVOID SystemArgument2)
813 {
814 PCONTEXT Context;
815 PULONG Esp;
816
817 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame, KeGetCurrentThread()->TrapFrame);
818
819 /*
820 * Save the thread's current context (in other words the registers
821 * that will be restored when it returns to user mode) so the
822 * APC dispatcher can restore them later
823 */
824 Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT));
825 RtlZeroMemory(Context, sizeof(CONTEXT));
826 Context->ContextFlags = CONTEXT_FULL;
827 Context->SegGs = TrapFrame->Gs;
828 Context->SegFs = TrapFrame->Fs;
829 Context->SegEs = TrapFrame->Es;
830 Context->SegDs = TrapFrame->Ds;
831 Context->Edi = TrapFrame->Edi;
832 Context->Esi = TrapFrame->Esi;
833 Context->Ebx = TrapFrame->Ebx;
834 Context->Edx = TrapFrame->Edx;
835 Context->Ecx = TrapFrame->Ecx;
836 Context->Eax = TrapFrame->Eax;
837 Context->Ebp = TrapFrame->Ebp;
838 Context->Eip = TrapFrame->Eip;
839 Context->SegCs = TrapFrame->Cs;
840 Context->EFlags = TrapFrame->Eflags;
841 Context->Esp = TrapFrame->Esp;
842 Context->SegSs = TrapFrame->Ss;
843
844 /*
845 * Setup the trap frame so the thread will start executing at the
846 * APC Dispatcher when it returns to user-mode
847 */
848 Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) - (sizeof(CONTEXT) + (6 * sizeof(ULONG))));
849 Esp[0] = 0xdeadbeef;
850 Esp[1] = (ULONG)NormalRoutine;
851 Esp[2] = (ULONG)NormalContext;
852 Esp[3] = (ULONG)SystemArgument1;
853 Esp[4] = (ULONG)SystemArgument2;
854 Esp[5] = (ULONG)Context;
855 TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher();
856 DPRINT("TrapFrame->Eip: %x\n", TrapFrame->Eip);
857 TrapFrame->Esp = (ULONG)Esp;
858 }
859
860 /*++
861 * KeAreApcsDisabled
862 * @implemented NT4
863 *
864 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
865 *
866 * Params:
867 * None.
868 *
869 * Returns:
870 * KeAreApcsDisabled returns TRUE if the thread is within a critical region
871 * or a guarded region, and FALSE otherwise.
872 *
873 * Remarks:
874 * A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled to
875 * determine if normal kernel APCs are disabled. A thread that is inside a
876 * critical region has both user APCs and normal kernel APCs disabled, but
877 * not special kernel APCs. A thread that is inside a guarded region has
878 * all APCs disabled, including special kernel APCs.
879 *
880 * Callers of this routine must be running at IRQL <= APC_LEVEL.
881 *
882 *--*/
883 BOOLEAN
884 STDCALL
885 KeAreApcsDisabled(VOID)
886 {
887 /* Return the Kernel APC State */
888 return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE;
889 }
890
891 /*++
892 * NtQueueApcThread
893 * NT4
894 *
895 * This routine is used to queue an APC from user-mode for the specified
896 * thread.
897 *
898 * Params:
899 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
900 *
901 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
902 *
903 * NormalContext - Pointer to the context to send to the Normal Routine.
904 *
905 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
906 * untyped data.
907 *
908 * Returns:
909 * STATUS_SUCCESS or failure cute from associated calls.
910 *
911 * Remarks:
912 * The thread must enter an alertable wait before the APC will be
913 * delivered.
914 *
915 *--*/
916 NTSTATUS
917 STDCALL
918 NtQueueApcThread(HANDLE ThreadHandle,
919 PKNORMAL_ROUTINE ApcRoutine,
920 PVOID NormalContext,
921 PVOID SystemArgument1,
922 PVOID SystemArgument2)
923 {
924 PKAPC Apc;
925 PETHREAD Thread;
926 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
927 NTSTATUS Status;
928
929 /* Get ETHREAD from Handle */
930 Status = ObReferenceObjectByHandle(ThreadHandle,
931 THREAD_SET_CONTEXT,
932 PsThreadType,
933 PreviousMode,
934 (PVOID)&Thread,
935 NULL);
936
937 /* Fail if the Handle is invalid for some reason */
938 if (!NT_SUCCESS(Status)) {
939
940 return(Status);
941 }
942
943 /* If this is a Kernel or System Thread, then fail */
944 if (Thread->Tcb.Teb == NULL) {
945
946 ObDereferenceObject(Thread);
947 return STATUS_INVALID_HANDLE;
948 }
949
950 /* Allocate an APC */
951 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('P', 's', 'a', 'p'));
952 if (Apc == NULL) {
953
954 ObDereferenceObject(Thread);
955 return(STATUS_NO_MEMORY);
956 }
957
958 /* Initialize and Queue a user mode apc (always!) */
959 KeInitializeApc(Apc,
960 &Thread->Tcb,
961 OriginalApcEnvironment,
962 KiFreeApcRoutine,
963 NULL,
964 ApcRoutine,
965 UserMode,
966 NormalContext);
967
968 if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) {
969
970 Status = STATUS_UNSUCCESSFUL;
971
972 } else {
973
974 Status = STATUS_SUCCESS;
975 }
976
977 /* Dereference Thread and Return */
978 ObDereferenceObject(Thread);
979 return Status;
980 }
981
982 static inline
983 VOID RepairList(PLIST_ENTRY Original,
984 PLIST_ENTRY Copy,
985 KPROCESSOR_MODE Mode)
986 {
987 /* Copy Source to Desination */
988 if (IsListEmpty(&Original[(int)Mode])) {
989
990 InitializeListHead(&Copy[(int)Mode]);
991
992 } else {
993
994 Copy[(int)Mode].Flink = Original[(int)Mode].Flink;
995 Copy[(int)Mode].Blink = Original[(int)Mode].Blink;
996 Original[(int)Mode].Flink->Blink = &Copy[(int)Mode];
997 Original[(int)Mode].Blink->Flink = &Copy[(int)Mode];
998 }
999 }
1000
1001 VOID
1002 STDCALL
1003 KiMoveApcState(PKAPC_STATE OldState,
1004 PKAPC_STATE NewState)
1005 {
1006 /* Restore backup of Original Environment */
1007 *NewState = *OldState;
1008
1009 /* Repair Lists */
1010 RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode);
1011 RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode);
1012 }
1013