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