Release the dispatcher lock after KiUnblockThread.
[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 * KiCheckForKernelApcDelivery
21 * @implemented NT 5.2
22 *
23 * The KiCheckForKernelApcDelivery 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 KiCheckForKernelApcDelivery(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 (Thread->SpecialApcDisable == 0))
128 {
129 /* Check for the right environment */
130 KiCheckForKernelApcDelivery();
131 }
132 }
133 }
134
135 /*++
136 * KeInitializeApc
137 * @implemented NT4
138 *
139 * The The KeInitializeApc routine initializes an APC object, and registers
140 * the Kernel, Rundown and Normal routines for that object.
141 *
142 * Params:
143 * Apc - Pointer to a KAPC structure that represents the APC object to
144 * initialize. The caller must allocate storage for the structure
145 * from resident memory.
146 *
147 * Thread - Thread to which to deliver the APC.
148 *
149 * TargetEnvironment - APC Environment to be used.
150 *
151 * KernelRoutine - Points to the KernelRoutine to associate with the APC.
152 * This routine is executed for all APCs.
153 *
154 * RundownRoutine - Points to the RundownRoutine to associate with the APC.
155 * This routine is executed when the Thread exists with
156 * the APC executing.
157 *
158 * NormalRoutine - Points to the NormalRoutine to associate with the APC.
159 * This routine is executed at PASSIVE_LEVEL. If this is
160 * not specifed, the APC becomes a Special APC and the
161 * Mode and Context parameters are ignored.
162 *
163 * Mode - Specifies the processor mode at which to run the Normal Routine.
164 *
165 * Context - Specifices the value to pass as Context parameter to the
166 * registered routines.
167 *
168 * Returns:
169 * None.
170 *
171 * Remarks:
172 * The caller can queue an initialized APC with KeInsertQueueApc.
173 *
174 * Storage for the APC object must be resident, such as nonpaged pool
175 * allocated by the caller.
176 *
177 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
178 *
179 *--*/
180 VOID
181 STDCALL
182 KeInitializeApc(IN PKAPC Apc,
183 IN PKTHREAD Thread,
184 IN KAPC_ENVIRONMENT TargetEnvironment,
185 IN PKKERNEL_ROUTINE KernelRoutine,
186 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
187 IN PKNORMAL_ROUTINE NormalRoutine,
188 IN KPROCESSOR_MODE Mode,
189 IN PVOID Context)
190 {
191 DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
192 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
193 "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine,
194 NormalRoutine,Mode,Context);
195
196 /* Set up the basic APC Structure Data */
197 RtlZeroMemory(Apc, sizeof(KAPC));
198 Apc->Type = ApcObject;
199 Apc->Size = sizeof(KAPC);
200
201 /* Set the Environment */
202 if (TargetEnvironment == CurrentApcEnvironment) {
203
204 Apc->ApcStateIndex = Thread->ApcStateIndex;
205
206 } else {
207
208 Apc->ApcStateIndex = TargetEnvironment;
209 }
210
211 /* Set the Thread and Routines */
212 Apc->Thread = Thread;
213 Apc->KernelRoutine = KernelRoutine;
214 Apc->RundownRoutine = RundownRoutine;
215 Apc->NormalRoutine = NormalRoutine;
216
217 /* Check if this is a Special APC, in which case we use KernelMode and no Context */
218 if (ARGUMENT_PRESENT(NormalRoutine)) {
219
220 Apc->ApcMode = Mode;
221 Apc->NormalContext = Context;
222
223 } else {
224
225 Apc->ApcMode = KernelMode;
226 }
227 }
228
229 static
230 __inline
231 VOID
232 KiRequestApcInterrupt(IN PKTHREAD Thread)
233 {
234 #ifdef CONFIG_SMP
235 PKPRCB Prcb, CurrentPrcb;
236 LONG i;
237
238 CurrentPrcb = KeGetCurrentPrcb();
239 for (i = 0; i < KeNumberProcessors; i++)
240 {
241 Prcb = ((PKPCR)(KPCR_BASE + i * PAGE_SIZE))->Prcb;
242 if (Prcb->CurrentThread == Thread)
243 {
244 ASSERT (CurrentPrcb != Prcb);
245 KiIpiSendRequest(Prcb->SetMember, IPI_APC);
246 break;
247 }
248 }
249 ASSERT (i < KeNumberProcessors);
250 #else
251 HalRequestSoftwareInterrupt(APC_LEVEL);
252 #endif
253 }
254
255 /*++
256 * KiInsertQueueApc
257 *
258 * The KiInsertQueueApc routine queues a APC for execution when the right
259 * scheduler environment exists.
260 *
261 * Params:
262 * Apc - Pointer to an initialized control object of type DPC for which the
263 * caller provides the storage.
264 *
265 * PriorityBoost - Priority Boost to apply to the Thread.
266 *
267 * Returns:
268 * None
269 *
270 * Remarks:
271 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
272 * at PASSIVE_LEVEL for the NormalRoutine registered.
273 *
274 * Callers of this routine must have locked the dipatcher database.
275 *
276 *--*/
277 VOID
278 FASTCALL
279 KiInsertQueueApc(PKAPC Apc,
280 KPRIORITY PriorityBoost)
281 {
282 PKTHREAD Thread = Apc->Thread;
283 PKAPC_STATE ApcState;
284 KPROCESSOR_MODE ApcMode;
285 PLIST_ENTRY ListHead, NextEntry;
286 PKAPC QueuedApc;
287 NTSTATUS Status;
288
289 /* Acquire the lock (only needed on MP) */
290 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
291
292 /* Little undocumented feature: Special Apc State Index */
293 if (Apc->ApcStateIndex == 3)
294 {
295 /* This tells us to use the thread's */
296 Apc->ApcStateIndex = Thread->ApcStateIndex;
297 }
298
299 /* Get the APC State for this Index, and the mode too */
300 ApcState = Thread->ApcStatePointer[(int)Apc->ApcStateIndex];
301 ApcMode = Apc->ApcMode;
302
303 /* Three scenarios:
304 * 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
305 * 2) User APC which is PsExitSpecialApc = Put it at the front of the List
306 * 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
307 */
308 if (Apc->NormalRoutine)
309 {
310 /* Normal APC; is it the Thread Termination APC? */
311 if ((ApcMode != KernelMode) && (Apc->KernelRoutine == PsExitSpecialApc))
312 {
313 /* Set User APC pending to true */
314 Thread->ApcState.UserApcPending = TRUE;
315
316 /* Insert it at the top of the list */
317 InsertHeadList(&ApcState->ApcListHead[ApcMode], &Apc->ApcListEntry);
318
319 /* Display debug message */
320 DPRINT("Inserted the Thread Exit APC for '%.16s' into the Queue\n",
321 ((PETHREAD)Thread)->ThreadsProcess->ImageFileName);
322 }
323 else
324 {
325 /* Regular user or kernel Normal APC */
326 InsertTailList(&ApcState->ApcListHead[ApcMode], &Apc->ApcListEntry);
327
328 /* Display debug message */
329 DPRINT("Inserted Normal APC for '%.16s' into the Queue\n",
330 ((PETHREAD)Thread)->ThreadsProcess->ImageFileName);
331 }
332 }
333 else
334 {
335 /* Special APC, find the first Normal APC in the list */
336 ListHead = &ApcState->ApcListHead[ApcMode];
337 NextEntry = ListHead->Flink;
338 while(NextEntry != ListHead)
339 {
340 /* Get the APC */
341 QueuedApc = CONTAINING_RECORD(NextEntry, KAPC, ApcListEntry);
342
343 /* Is this a Normal APC? If so, break */
344 if (QueuedApc->NormalRoutine) break;
345
346 /* Move to the next APC in the Queue */
347 NextEntry = NextEntry->Flink;
348 }
349
350 /* Move to the APC before this one (ie: the last Special APC) */
351 NextEntry = NextEntry->Blink;
352
353 /* Insert us here */
354 InsertHeadList(NextEntry, &Apc->ApcListEntry);
355 DPRINT("Inserted Special APC for '%.16s' into the Queue\n",
356 ((PETHREAD)Thread)->ThreadsProcess->ImageFileName);
357 }
358
359 /* Now check if the Apc State Indexes match */
360 if (Thread->ApcStateIndex == Apc->ApcStateIndex)
361 {
362 /* Check that if the thread matches */
363 if (Thread == KeGetCurrentThread())
364 {
365 /* Check if this is kernel mode */
366 if (ApcMode == KernelMode)
367 {
368 /* All valid, a Kernel APC is pending now */
369 Thread->ApcState.KernelApcPending = TRUE;
370
371 /* Check if Special APCs are disabled */
372 if (Thread->SpecialApcDisable == 0)
373 {
374 /* They're not, so request the interrupt */
375 HalRequestSoftwareInterrupt(APC_LEVEL);
376 }
377 }
378 }
379 else
380 {
381 /* Check if this is a non-kernel mode APC */
382 if (ApcMode != KernelMode)
383 {
384 /* Not a Kernel-Mode APC. Are we waiting in user-mode? */
385 if ((Thread->State == Waiting) && (Thread->WaitMode == UserMode))
386 {
387 /* The thread is waiting. Are we alertable, or is an APC pending */
388 if ((Thread->Alertable) || (Thread->ApcState.UserApcPending))
389 {
390 /* Set user-mode APC pending */
391 Thread->ApcState.UserApcPending = TRUE;
392 Status = STATUS_USER_APC;
393 goto Unwait;
394 }
395 }
396 }
397 else
398 {
399 /* Kernel-mode APC, set us pending */
400 Thread->ApcState.KernelApcPending = TRUE;
401
402 /* Are we currently running? */
403 if (Thread->State == Running)
404 {
405 /* The thread is running, so send an APC request */
406 KiRequestApcInterrupt(Thread);
407 }
408 else
409 {
410 /*
411 * If the thread is Waiting at PASSIVE_LEVEL AND
412 * Special APCs are not disabled AND
413 * He is a Normal APC AND
414 * Kernel APCs are not disabled AND
415 * Kernel APC is not pending OR
416 * He is a Special APC THEN
417 * Unwait thread with STATUS_KERNEL_APC
418 */
419 if ((Thread->State == Waiting) &&
420 (Thread->WaitIrql == PASSIVE_LEVEL) &&
421 (!Thread->SpecialApcDisable) &&
422 ((!Apc->NormalRoutine) ||
423 ((!Thread->KernelApcDisable) &&
424 (!Thread->ApcState.KernelApcInProgress))))
425 {
426 /* We'll unwait with this status */
427 Status = STATUS_KERNEL_APC;
428
429 /* Wake up the thread */
430 Unwait:
431 DPRINT("Waking up Thread for %lx Delivery \n", Status);
432 KiAbortWaitThread(Thread, Status, PriorityBoost);
433 }
434 else
435 {
436 /* FIXME: Handle deferred ready sometime far far in the future */
437 }
438 }
439 }
440 }
441 }
442
443 /* Return to caller */
444 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
445 return;
446 }
447
448 /*++
449 * KeInsertQueueApc
450 * @implemented NT4
451 *
452 * The KeInsertQueueApc routine queues a APC for execution when the right
453 * scheduler environment exists.
454 *
455 * Params:
456 * Apc - Pointer to an initialized control object of type DPC for which the
457 * caller provides the storage.
458 *
459 * SystemArgument[1,2] - Pointer to a set of two parameters that contain
460 * untyped data.
461 *
462 * PriorityBoost - Priority Boost to apply to the Thread.
463 *
464 * Returns:
465 * If the APC is already inserted or APC queueing is disabled, FALSE.
466 * Otherwise, TRUE.
467 *
468 * Remarks:
469 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
470 * at PASSIVE_LEVEL for the NormalRoutine registered.
471 *
472 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
473 *
474 *--*/
475 BOOLEAN
476 STDCALL
477 KeInsertQueueApc(PKAPC Apc,
478 PVOID SystemArgument1,
479 PVOID SystemArgument2,
480 KPRIORITY PriorityBoost)
481 {
482 KIRQL OldIrql;
483 PKTHREAD Thread;
484
485 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
486 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
487 "SystemArgument2 %x)\n",Apc,SystemArgument1,
488 SystemArgument2);
489
490 /* Lock the Dispatcher Database */
491 OldIrql = KeAcquireDispatcherDatabaseLock();
492
493 /* Get the Thread specified in the APC */
494 Thread = Apc->Thread;
495
496 /* Make sure we can Queue APCs and that this one isn't already inserted */
497 if ((Thread->ApcQueueable == FALSE) && (Apc->Inserted == TRUE))
498 {
499 DPRINT("Can't queue the APC\n");
500 KeReleaseDispatcherDatabaseLock(OldIrql);
501 return FALSE;
502 }
503
504 /* Set the System Arguments and set it as inserted */
505 Apc->SystemArgument1 = SystemArgument1;
506 Apc->SystemArgument2 = SystemArgument2;
507 Apc->Inserted = TRUE;
508
509 /* Call the Internal Function */
510 KiInsertQueueApc(Apc, PriorityBoost);
511
512 /* Return Sucess if we are here */
513 KeReleaseDispatcherDatabaseLock(OldIrql);
514 return TRUE;
515 }
516
517 /*++
518 * KeFlushQueueApc
519 *
520 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
521 * from the specified Thread's APC queue.
522 *
523 * Params:
524 * Thread - Pointer to the thread whose APC queue will be flushed.
525 *
526 * PreviousMode - Specifies which APC Queue to flush.
527 *
528 * Returns:
529 * A pointer to the first entry in the flushed APC queue.
530 *
531 * Remarks:
532 * If the routine returns NULL, it means that no APCs were to be flushed.
533 *
534 * Callers of KeFlushQueueApc must be running at DISPATCH_LEVEL or lower.
535 *
536 *--*/
537 PLIST_ENTRY
538 STDCALL
539 KeFlushQueueApc(IN PKTHREAD Thread,
540 IN KPROCESSOR_MODE PreviousMode)
541 {
542 KIRQL OldIrql;
543 PKAPC Apc;
544 PLIST_ENTRY FirstEntry, CurrentEntry;
545
546 /* Lock the Dispatcher Database and APC Queue */
547 OldIrql = KeAcquireDispatcherDatabaseLock();
548 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
549
550 if (IsListEmpty(&Thread->ApcState.ApcListHead[PreviousMode])) {
551 FirstEntry = NULL;
552 } else {
553 FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink;
554 RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]);
555 CurrentEntry = FirstEntry;
556 do {
557 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
558 Apc->Inserted = FALSE;
559 CurrentEntry = CurrentEntry->Flink;
560 } while (CurrentEntry != FirstEntry);
561 }
562
563 /* Release the locks */
564 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
565 KeReleaseDispatcherDatabaseLock(OldIrql);
566
567 /* Return the first entry */
568 return FirstEntry;
569 }
570
571 /*++
572 * KeRemoveQueueApc
573 *
574 * The KeRemoveQueueApc routine removes a given APC object from the system
575 * APC queue.
576 *
577 * Params:
578 * APC - Pointer to an initialized APC object that was queued by calling
579 * KeInsertQueueApc.
580 *
581 * Returns:
582 * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is
583 * performed and FALSE is returned.
584 *
585 * Remarks:
586 * If the given APC Object is currently queued, it is removed from the queue
587 * and any calls to the registered routines are cancelled.
588 *
589 * Callers of KeLeaveCriticalRegion can be running at any IRQL.
590 *
591 *--*/
592 BOOLEAN
593 STDCALL
594 KeRemoveQueueApc(PKAPC Apc)
595 {
596 KIRQL OldIrql;
597 PKTHREAD Thread = Apc->Thread;
598 PKAPC_STATE ApcState;
599 BOOLEAN Inserted;
600 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
601 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc);
602
603 /* Acquire locks */
604 OldIrql = KeAcquireDispatcherDatabaseLock();
605 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
606
607 /* Check if it's inserted */
608 if ((Inserted = Apc->Inserted))
609 {
610 /* Remove it from the Queue*/
611 Apc->Inserted = FALSE;
612 ApcState = Thread->ApcStatePointer[(int)Apc->ApcStateIndex];
613 RemoveEntryList(&Apc->ApcListEntry);
614
615 /* If the Queue is completely empty, then no more APCs are pending */
616 if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode]))
617 {
618 /* Set the correct State based on the Apc Mode */
619 if (Apc->ApcMode == KernelMode)
620 {
621 ApcState->KernelApcPending = FALSE;
622 }
623 else
624 {
625 ApcState->UserApcPending = FALSE;
626 }
627 }
628 }
629
630 /* Restore IRQL and Return */
631 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
632 KeReleaseDispatcherDatabaseLock(OldIrql);
633 return Inserted;
634 }
635
636 /*++
637 * KiDeliverApc
638 * @implemented @NT4
639 *
640 * The KiDeliverApc routine is called from IRQL switching code if the
641 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
642 * pending.
643 *
644 * Params:
645 * DeliveryMode - Specifies the current processor mode.
646 *
647 * Reserved - Pointer to the Exception Frame on non-i386 builds.
648 *
649 * TrapFrame - Pointer to the Trap Frame.
650 *
651 * Returns:
652 * None.
653 *
654 * Remarks:
655 * First, Special APCs are delivered, followed by Kernel-Mode APCs and
656 * User-Mode APCs. Note that the TrapFrame is only valid if the previous
657 * mode is User.
658 *
659 * Upon entry, this routine executes at APC_LEVEL.
660 *
661 *--*/
662 VOID
663 STDCALL
664 KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
665 PVOID Reserved,
666 PKTRAP_FRAME TrapFrame)
667 {
668 PKTHREAD Thread = KeGetCurrentThread();
669 PKPROCESS Process = KeGetCurrentProcess();
670 PKTRAP_FRAME OldTrapFrame;
671 PLIST_ENTRY ApcListEntry;
672 PKAPC Apc;
673 KIRQL OldIrql;
674 PKKERNEL_ROUTINE KernelRoutine;
675 PVOID NormalContext;
676 PKNORMAL_ROUTINE NormalRoutine;
677 PVOID SystemArgument1;
678 PVOID SystemArgument2;
679 ASSERT_IRQL_EQUAL(APC_LEVEL);
680
681 /* Save the old trap frame */
682 OldTrapFrame = Thread->TrapFrame;
683
684 /* Clear Kernel APC Pending */
685 Thread->ApcState.KernelApcPending = FALSE;
686
687 /* Check if Special APCs are disabled */
688 if (Thread->SpecialApcDisable != 0) goto Quickie;
689
690 /* Do the Kernel APCs first */
691 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
692 {
693 /* Lock the APC Queue and Raise IRQL to Synch */
694 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
695
696 /* Get the next Entry */
697 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
698 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
699
700 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
701 NormalRoutine = Apc->NormalRoutine;
702 KernelRoutine = Apc->KernelRoutine;
703 NormalContext = Apc->NormalContext;
704 SystemArgument1 = Apc->SystemArgument1;
705 SystemArgument2 = Apc->SystemArgument2;
706
707 /* Special APC */
708 if (!NormalRoutine)
709 {
710 /* Remove the APC from the list */
711 RemoveEntryList(ApcListEntry);
712 Apc->Inserted = FALSE;
713
714 /* Go back to APC_LEVEL */
715 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
716
717 /* Call the Special APC */
718 DPRINT("Delivering a Special APC: %x\n", Apc);
719 KernelRoutine(Apc,
720 &NormalRoutine,
721 &NormalContext,
722 &SystemArgument1,
723 &SystemArgument2);
724 }
725 else
726 {
727 /* Normal Kernel APC, make sur APCs aren't disabled or in progress*/
728 if ((Thread->ApcState.KernelApcInProgress) ||
729 (Thread->KernelApcDisable))
730 {
731 /*
732 * DeliveryMode must be KernelMode in this case, since one may not
733 * return to umode while being inside a critical section or while
734 * a regular kmode apc is running (the latter should be impossible btw).
735 * -Gunnar
736 */
737 ASSERT(DeliveryMode == KernelMode);
738
739 /* Release lock and return */
740 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
741 goto Quickie;
742 }
743
744 /* Dequeue the APC */
745 RemoveEntryList(ApcListEntry);
746 Apc->Inserted = FALSE;
747
748 /* Go back to APC_LEVEL */
749 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
750
751 /* Call the Kernel APC */
752 DPRINT("Delivering a Normal APC: %x\n", Apc);
753 KernelRoutine(Apc,
754 &NormalRoutine,
755 &NormalContext,
756 &SystemArgument1,
757 &SystemArgument2);
758
759 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
760 if (NormalRoutine)
761 {
762 /* At Passive Level, this APC can be prempted by a Special APC */
763 Thread->ApcState.KernelApcInProgress = TRUE;
764 KeLowerIrql(PASSIVE_LEVEL);
765
766 /* Call and Raise IRQ back to APC_LEVEL */
767 DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc);
768 NormalRoutine(NormalContext, SystemArgument1, SystemArgument2);
769 KeRaiseIrql(APC_LEVEL, &OldIrql);
770 }
771
772 /* Set Kernel APC in progress to false and loop again */
773 Thread->ApcState.KernelApcInProgress = FALSE;
774 }
775 }
776
777 /* Now we do the User APCs */
778 if ((DeliveryMode == UserMode) &&
779 (!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
780 (Thread->ApcState.UserApcPending == TRUE))
781 {
782 /* Lock the APC Queue and Raise IRQL to Synch */
783 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
784
785 /* It's not pending anymore */
786 Thread->ApcState.UserApcPending = FALSE;
787
788 /* Get the APC Entry */
789 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
790
791 /* Is it empty now? */
792 if (!ApcListEntry)
793 {
794 /* Release the lock and return */
795 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
796 goto Quickie;
797 }
798
799 /* Get the actual APC object */
800 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
801
802 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
803 NormalRoutine = Apc->NormalRoutine;
804 KernelRoutine = Apc->KernelRoutine;
805 NormalContext = Apc->NormalContext;
806 SystemArgument1 = Apc->SystemArgument1;
807 SystemArgument2 = Apc->SystemArgument2;
808
809 /* Remove the APC from Queue, restore IRQL and call the APC */
810 RemoveEntryList(ApcListEntry);
811 Apc->Inserted = FALSE;
812 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
813
814 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc);
815 KernelRoutine(Apc,
816 &NormalRoutine,
817 &NormalContext,
818 &SystemArgument1,
819 &SystemArgument2);
820
821 if (!NormalRoutine)
822 {
823 /* Check if more User APCs are Pending */
824 KeTestAlertThread(UserMode);
825 }
826 else
827 {
828 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
829 DPRINT("Delivering a User APC: %x\n", Apc);
830 KiInitializeUserApc(Reserved,
831 TrapFrame,
832 NormalRoutine,
833 NormalContext,
834 SystemArgument1,
835 SystemArgument2);
836 }
837 }
838
839 Quickie:
840 /* Make sure we're still in the same process */
841 if (Process != Thread->ApcState.Process)
842 {
843 /* Erm, we got attached or something! BAD! */
844 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT,
845 (ULONG_PTR)Process,
846 (ULONG_PTR)Thread->ApcState.Process,
847 Thread->ApcStateIndex,
848 KeGetCurrentPrcb()->DpcRoutineActive);
849 }
850
851 /* Restore the trap frame */
852 Thread->TrapFrame = OldTrapFrame;
853 return;
854 }
855
856 VOID
857 STDCALL
858 KiFreeApcRoutine(PKAPC Apc,
859 PKNORMAL_ROUTINE* NormalRoutine,
860 PVOID* NormalContext,
861 PVOID* SystemArgument1,
862 PVOID* SystemArgument2)
863 {
864 /* Free the APC and do nothing else */
865 ExFreePool(Apc);
866 }
867
868 /*++
869 * KiInitializeUserApc
870 *
871 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
872 *
873 * Params:
874 * Reserved - Pointer to the Exception Frame on non-i386 builds.
875 *
876 * TrapFrame - Pointer to the Trap Frame.
877 *
878 * NormalRoutine - Pointer to the NormalRoutine to call.
879 *
880 * NormalContext - Pointer to the context to send to the Normal Routine.
881 *
882 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
883 * untyped data.
884 *
885 * Returns:
886 * None.
887 *
888 * Remarks:
889 * None.
890 *
891 *--*/
892 VOID
893 STDCALL
894 KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame,
895 IN PKTRAP_FRAME TrapFrame,
896 IN PKNORMAL_ROUTINE NormalRoutine,
897 IN PVOID NormalContext,
898 IN PVOID SystemArgument1,
899 IN PVOID SystemArgument2)
900 {
901 CONTEXT Context;
902 ULONG_PTR Stack;
903 ULONG Size;
904
905 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame,
906 KeGetCurrentThread()->TrapFrame);
907
908 /* Don't deliver APCs in V86 mode */
909 if (TrapFrame->EFlags & X86_EFLAGS_VM) return;
910
911 /* Save the full context */
912 Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
913 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
914
915 /* Protect with SEH */
916 _SEH_TRY
917 {
918 /* Get the aligned size */
919 Size = ((sizeof(CONTEXT) + 3) & ~3) + 4 * sizeof(ULONG_PTR);
920 Stack = (Context.Esp & ~3) - Size;
921
922 /* Probe and copy */
923 ProbeForWrite((PVOID)Stack, Size, 4);
924 RtlMoveMemory((PVOID)(Stack + 4 * sizeof(ULONG_PTR)),
925 &Context,
926 sizeof(CONTEXT));
927
928 /* Run at APC dispatcher */
929 TrapFrame->Eip = (ULONG)KeUserApcDispatcher;
930 TrapFrame->HardwareEsp = Stack;
931
932 /* Setup the stack */
933 *(PULONG_PTR)(Stack + 0 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalRoutine;
934 *(PULONG_PTR)(Stack + 1 * sizeof(ULONG_PTR)) = (ULONG_PTR)NormalContext;
935 *(PULONG_PTR)(Stack + 2 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument1;
936 *(PULONG_PTR)(Stack + 3 * sizeof(ULONG_PTR)) = (ULONG_PTR)SystemArgument2;
937 }
938 _SEH_HANDLE
939 {
940 /* FIXME: Get the record and raise an exception */
941 }
942 _SEH_END;
943 }
944
945 /*++
946 * KeAreApcsDisabled
947 * @implemented NT4
948 *
949 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
950 *
951 * Params:
952 * None.
953 *
954 * Returns:
955 * KeAreApcsDisabled returns TRUE if the thread is within a critical region
956 * or a guarded region, and FALSE otherwise.
957 *
958 * Remarks:
959 * A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled to
960 * determine if normal kernel APCs are disabled. A thread that is inside a
961 * critical region has both user APCs and normal kernel APCs disabled, but
962 * not special kernel APCs. A thread that is inside a guarded region has
963 * all APCs disabled, including special kernel APCs.
964 *
965 * Callers of this routine must be running at IRQL <= APC_LEVEL.
966 *
967 *--*/
968 BOOLEAN
969 STDCALL
970 KeAreApcsDisabled(VOID)
971 {
972 /* Return the Kernel APC State */
973 return KeGetCurrentThread()->CombinedApcDisable ? TRUE : FALSE;
974 }
975
976 /*++
977 * NtQueueApcThread
978 * NT4
979 *
980 * This routine is used to queue an APC from user-mode for the specified
981 * thread.
982 *
983 * Params:
984 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
985 *
986 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
987 *
988 * NormalContext - Pointer to the context to send to the Normal Routine.
989 *
990 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
991 * untyped data.
992 *
993 * Returns:
994 * STATUS_SUCCESS or failure cute from associated calls.
995 *
996 * Remarks:
997 * The thread must enter an alertable wait before the APC will be
998 * delivered.
999 *
1000 *--*/
1001 NTSTATUS
1002 STDCALL
1003 NtQueueApcThread(HANDLE ThreadHandle,
1004 PKNORMAL_ROUTINE ApcRoutine,
1005 PVOID NormalContext,
1006 PVOID SystemArgument1,
1007 PVOID SystemArgument2)
1008 {
1009 PKAPC Apc;
1010 PETHREAD Thread;
1011 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
1012 NTSTATUS Status;
1013
1014 /* Get ETHREAD from Handle */
1015 Status = ObReferenceObjectByHandle(ThreadHandle,
1016 THREAD_SET_CONTEXT,
1017 PsThreadType,
1018 PreviousMode,
1019 (PVOID)&Thread,
1020 NULL);
1021
1022 /* Fail if the Handle is invalid for some reason */
1023 if (!NT_SUCCESS(Status)) {
1024
1025 return(Status);
1026 }
1027
1028 /* If this is a Kernel or System Thread, then fail */
1029 if (Thread->Tcb.Teb == NULL) {
1030
1031 ObDereferenceObject(Thread);
1032 return STATUS_INVALID_HANDLE;
1033 }
1034
1035 /* Allocate an APC */
1036 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('P', 's', 'a', 'p'));
1037 if (Apc == NULL) {
1038
1039 ObDereferenceObject(Thread);
1040 return(STATUS_NO_MEMORY);
1041 }
1042
1043 /* Initialize and Queue a user mode apc (always!) */
1044 KeInitializeApc(Apc,
1045 &Thread->Tcb,
1046 OriginalApcEnvironment,
1047 KiFreeApcRoutine,
1048 NULL,
1049 ApcRoutine,
1050 UserMode,
1051 NormalContext);
1052
1053 if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) {
1054
1055 Status = STATUS_UNSUCCESSFUL;
1056
1057 } else {
1058
1059 Status = STATUS_SUCCESS;
1060 }
1061
1062 /* Dereference Thread and Return */
1063 ObDereferenceObject(Thread);
1064 return Status;
1065 }
1066
1067 static __inline
1068 VOID RepairList(PLIST_ENTRY Original,
1069 PLIST_ENTRY Copy,
1070 KPROCESSOR_MODE Mode)
1071 {
1072 /* Copy Source to Desination */
1073 if (IsListEmpty(&Original[(int)Mode])) {
1074
1075 InitializeListHead(&Copy[(int)Mode]);
1076
1077 } else {
1078
1079 Copy[(int)Mode].Flink = Original[(int)Mode].Flink;
1080 Copy[(int)Mode].Blink = Original[(int)Mode].Blink;
1081 Original[(int)Mode].Flink->Blink = &Copy[(int)Mode];
1082 Original[(int)Mode].Blink->Flink = &Copy[(int)Mode];
1083 }
1084 }
1085
1086 VOID
1087 STDCALL
1088 KiMoveApcState(PKAPC_STATE OldState,
1089 PKAPC_STATE NewState)
1090 {
1091 /* Restore backup of Original Environment */
1092 *NewState = *OldState;
1093
1094 /* Repair Lists */
1095 RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode);
1096 RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode);
1097 }
1098