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
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
11 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
17 /* FUNCTIONS *****************************************************************/
20 * KiCheckForKernelApcDelivery
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.
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.
41 KiCheckForKernelApcDelivery(VOID
)
43 /* We should only deliver at passive */
44 if (KeGetCurrentIrql() == PASSIVE_LEVEL
)
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
);
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
60 HalRequestSoftwareInterrupt(APC_LEVEL
);
65 * KeEnterCriticalRegion
68 * The KeEnterCriticalRegion routine temporarily disables the delivery of
69 * normal kernel APCs; special kernel-mode APCs are still delivered.
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.
82 * Callers of KeEnterCriticalRegion must be running at IRQL <= APC_LEVEL.
85 #undef KeEnterCriticalRegion
88 KeEnterCriticalRegion(VOID
)
90 /* Disable Kernel APCs */
91 PKTHREAD Thread
= KeGetCurrentThread();
92 if (Thread
) Thread
->KernelApcDisable
--;
96 * KeLeaveCriticalRegion
99 * The KeLeaveCriticalRegion routine reenables the delivery of normal
100 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
109 * Highest-level drivers can call this routine while running in the context
110 * of the thread that requested the current I/O operation.
112 * Callers of KeLeaveCriticalRegion must be running at IRQL <= DISPATCH_LEVEL.
115 #undef KeLeaveCriticalRegion
118 KeLeaveCriticalRegion (VOID
)
120 PKTHREAD Thread
= KeGetCurrentThread();
122 /* Check if Kernel APCs are now enabled */
123 if((Thread
) && (++Thread
->KernelApcDisable
== 0))
125 /* Check if we need to request an APC Delivery */
126 if ((!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
])) &&
127 (Thread
->SpecialApcDisable
== 0))
129 /* Check for the right environment */
130 KiCheckForKernelApcDelivery();
139 * The The KeInitializeApc routine initializes an APC object, and registers
140 * the Kernel, Rundown and Normal routines for that object.
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.
147 * Thread - Thread to which to deliver the APC.
149 * TargetEnvironment - APC Environment to be used.
151 * KernelRoutine - Points to the KernelRoutine to associate with the APC.
152 * This routine is executed for all APCs.
154 * RundownRoutine - Points to the RundownRoutine to associate with the APC.
155 * This routine is executed when the Thread exists with
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.
163 * Mode - Specifies the processor mode at which to run the Normal Routine.
165 * Context - Specifices the value to pass as Context parameter to the
166 * registered routines.
172 * The caller can queue an initialized APC with KeInsertQueueApc.
174 * Storage for the APC object must be resident, such as nonpaged pool
175 * allocated by the caller.
177 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
182 KeInitializeApc(IN PKAPC Apc
,
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
,
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
);
196 /* Set up the basic APC Structure Data */
197 RtlZeroMemory(Apc
, sizeof(KAPC
));
198 Apc
->Type
= ApcObject
;
199 Apc
->Size
= sizeof(KAPC
);
201 /* Set the Environment */
202 if (TargetEnvironment
== CurrentApcEnvironment
) {
204 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
208 Apc
->ApcStateIndex
= TargetEnvironment
;
211 /* Set the Thread and Routines */
212 Apc
->Thread
= Thread
;
213 Apc
->KernelRoutine
= KernelRoutine
;
214 Apc
->RundownRoutine
= RundownRoutine
;
215 Apc
->NormalRoutine
= NormalRoutine
;
217 /* Check if this is a Special APC, in which case we use KernelMode and no Context */
218 if (ARGUMENT_PRESENT(NormalRoutine
)) {
221 Apc
->NormalContext
= Context
;
225 Apc
->ApcMode
= KernelMode
;
232 KiRequestApcInterrupt(IN PKTHREAD Thread
)
235 PKPRCB Prcb
, CurrentPrcb
;
238 CurrentPrcb
= KeGetCurrentPrcb();
239 for (i
= 0; i
< KeNumberProcessors
; i
++)
241 Prcb
= ((PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
))->Prcb
;
242 if (Prcb
->CurrentThread
== Thread
)
244 ASSERT (CurrentPrcb
!= Prcb
);
245 KiIpiSendRequest(Prcb
->SetMember
, IPI_APC
);
249 ASSERT (i
< KeNumberProcessors
);
251 HalRequestSoftwareInterrupt(APC_LEVEL
);
258 * The KiInsertQueueApc routine queues a APC for execution when the right
259 * scheduler environment exists.
262 * Apc - Pointer to an initialized control object of type DPC for which the
263 * caller provides the storage.
265 * PriorityBoost - Priority Boost to apply to the Thread.
271 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
272 * at PASSIVE_LEVEL for the NormalRoutine registered.
274 * Callers of this routine must have locked the dipatcher database.
279 KiInsertQueueApc(PKAPC Apc
,
280 KPRIORITY PriorityBoost
)
282 PKTHREAD Thread
= Apc
->Thread
;
283 PKAPC_STATE ApcState
;
284 KPROCESSOR_MODE ApcMode
;
285 PLIST_ENTRY ListHead
, NextEntry
;
289 /* Acquire the lock (only needed on MP) */
290 KeAcquireSpinLockAtDpcLevel(&Thread
->ApcQueueLock
);
292 /* Little undocumented feature: Special Apc State Index */
293 if (Apc
->ApcStateIndex
== 3)
295 /* This tells us to use the thread's */
296 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
299 /* Get the APC State for this Index, and the mode too */
300 ApcState
= Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
];
301 ApcMode
= Apc
->ApcMode
;
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
308 if (Apc
->NormalRoutine
)
310 /* Normal APC; is it the Thread Termination APC? */
311 if ((ApcMode
!= KernelMode
) && (Apc
->KernelRoutine
== PsExitSpecialApc
))
313 /* Set User APC pending to true */
314 Thread
->ApcState
.UserApcPending
= TRUE
;
316 /* Insert it at the top of the list */
317 InsertHeadList(&ApcState
->ApcListHead
[ApcMode
], &Apc
->ApcListEntry
);
319 /* Display debug message */
320 DPRINT("Inserted the Thread Exit APC for '%.16s' into the Queue\n",
321 ((PETHREAD
)Thread
)->ThreadsProcess
->ImageFileName
);
325 /* Regular user or kernel Normal APC */
326 InsertTailList(&ApcState
->ApcListHead
[ApcMode
], &Apc
->ApcListEntry
);
328 /* Display debug message */
329 DPRINT("Inserted Normal APC for '%.16s' into the Queue\n",
330 ((PETHREAD
)Thread
)->ThreadsProcess
->ImageFileName
);
335 /* Special APC, find the first Normal APC in the list */
336 ListHead
= &ApcState
->ApcListHead
[ApcMode
];
337 NextEntry
= ListHead
->Flink
;
338 while(NextEntry
!= ListHead
)
341 QueuedApc
= CONTAINING_RECORD(NextEntry
, KAPC
, ApcListEntry
);
343 /* Is this a Normal APC? If so, break */
344 if (QueuedApc
->NormalRoutine
) break;
346 /* Move to the next APC in the Queue */
347 NextEntry
= NextEntry
->Flink
;
350 /* Move to the APC before this one (ie: the last Special APC) */
351 NextEntry
= NextEntry
->Blink
;
354 InsertHeadList(NextEntry
, &Apc
->ApcListEntry
);
355 DPRINT("Inserted Special APC for '%.16s' into the Queue\n",
356 ((PETHREAD
)Thread
)->ThreadsProcess
->ImageFileName
);
359 /* Now check if the Apc State Indexes match */
360 if (Thread
->ApcStateIndex
== Apc
->ApcStateIndex
)
362 /* Check that if the thread matches */
363 if (Thread
== KeGetCurrentThread())
365 /* Check if this is kernel mode */
366 if (ApcMode
== KernelMode
)
368 /* All valid, a Kernel APC is pending now */
369 Thread
->ApcState
.KernelApcPending
= TRUE
;
371 /* Check if Special APCs are disabled */
372 if (Thread
->SpecialApcDisable
== 0)
374 /* They're not, so request the interrupt */
375 HalRequestSoftwareInterrupt(APC_LEVEL
);
381 /* Check if this is a non-kernel mode APC */
382 if (ApcMode
!= KernelMode
)
384 /* Not a Kernel-Mode APC. Are we waiting in user-mode? */
385 if ((Thread
->State
== Waiting
) && (Thread
->WaitMode
== UserMode
))
387 /* The thread is waiting. Are we alertable, or is an APC pending */
388 if ((Thread
->Alertable
) || (Thread
->ApcState
.UserApcPending
))
390 /* Set user-mode APC pending */
391 Thread
->ApcState
.UserApcPending
= TRUE
;
392 Status
= STATUS_USER_APC
;
399 /* Kernel-mode APC, set us pending */
400 Thread
->ApcState
.KernelApcPending
= TRUE
;
402 /* Are we currently running? */
403 if (Thread
->State
== Running
)
405 /* The thread is running, so send an APC request */
406 KiRequestApcInterrupt(Thread
);
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
419 if ((Thread
->State
== Waiting
) &&
420 (Thread
->WaitIrql
== PASSIVE_LEVEL
) &&
421 (!Thread
->SpecialApcDisable
) &&
422 ((!Apc
->NormalRoutine
) ||
423 ((!Thread
->KernelApcDisable
) &&
424 (!Thread
->ApcState
.KernelApcInProgress
))))
426 /* We'll unwait with this status */
427 Status
= STATUS_KERNEL_APC
;
429 /* Wake up the thread */
431 DPRINT("Waking up Thread for %lx Delivery \n", Status
);
432 KiAbortWaitThread(Thread
, Status
, PriorityBoost
);
436 /* FIXME: Handle deferred ready sometime far far in the future */
443 /* Return to caller */
444 KeReleaseSpinLockFromDpcLevel(&Thread
->ApcQueueLock
);
452 * The KeInsertQueueApc routine queues a APC for execution when the right
453 * scheduler environment exists.
456 * Apc - Pointer to an initialized control object of type DPC for which the
457 * caller provides the storage.
459 * SystemArgument[1,2] - Pointer to a set of two parameters that contain
462 * PriorityBoost - Priority Boost to apply to the Thread.
465 * If the APC is already inserted or APC queueing is disabled, FALSE.
469 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
470 * at PASSIVE_LEVEL for the NormalRoutine registered.
472 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
477 KeInsertQueueApc(PKAPC Apc
,
478 PVOID SystemArgument1
,
479 PVOID SystemArgument2
,
480 KPRIORITY PriorityBoost
)
485 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
486 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
487 "SystemArgument2 %x)\n",Apc
,SystemArgument1
,
490 /* Lock the Dispatcher Database */
491 OldIrql
= KeAcquireDispatcherDatabaseLock();
493 /* Get the Thread specified in the APC */
494 Thread
= Apc
->Thread
;
496 /* Make sure we can Queue APCs and that this one isn't already inserted */
497 if ((Thread
->ApcQueueable
== FALSE
) && (Apc
->Inserted
== TRUE
))
499 DPRINT("Can't queue the APC\n");
500 KeReleaseDispatcherDatabaseLock(OldIrql
);
504 /* Set the System Arguments and set it as inserted */
505 Apc
->SystemArgument1
= SystemArgument1
;
506 Apc
->SystemArgument2
= SystemArgument2
;
507 Apc
->Inserted
= TRUE
;
509 /* Call the Internal Function */
510 KiInsertQueueApc(Apc
, PriorityBoost
);
512 /* Return Sucess if we are here */
513 KeReleaseDispatcherDatabaseLock(OldIrql
);
520 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
521 * from the specified Thread's APC queue.
524 * Thread - Pointer to the thread whose APC queue will be flushed.
526 * PreviousMode - Specifies which APC Queue to flush.
529 * A pointer to the first entry in the flushed APC queue.
532 * If the routine returns NULL, it means that no APCs were to be flushed.
534 * Callers of KeFlushQueueApc must be running at DISPATCH_LEVEL or lower.
539 KeFlushQueueApc(IN PKTHREAD Thread
,
540 IN KPROCESSOR_MODE PreviousMode
)
544 PLIST_ENTRY FirstEntry
, CurrentEntry
;
546 /* Lock the Dispatcher Database and APC Queue */
547 OldIrql
= KeAcquireDispatcherDatabaseLock();
548 KeAcquireSpinLockAtDpcLevel(&Thread
->ApcQueueLock
);
550 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[PreviousMode
])) {
553 FirstEntry
= Thread
->ApcState
.ApcListHead
[PreviousMode
].Flink
;
554 RemoveEntryList(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
555 CurrentEntry
= FirstEntry
;
557 Apc
= CONTAINING_RECORD(CurrentEntry
, KAPC
, ApcListEntry
);
558 Apc
->Inserted
= FALSE
;
559 CurrentEntry
= CurrentEntry
->Flink
;
560 } while (CurrentEntry
!= FirstEntry
);
563 /* Release the locks */
564 KeReleaseSpinLockFromDpcLevel(&Thread
->ApcQueueLock
);
565 KeReleaseDispatcherDatabaseLock(OldIrql
);
567 /* Return the first entry */
574 * The KeRemoveQueueApc routine removes a given APC object from the system
578 * APC - Pointer to an initialized APC object that was queued by calling
582 * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is
583 * performed and FALSE is returned.
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.
589 * Callers of KeLeaveCriticalRegion can be running at any IRQL.
594 KeRemoveQueueApc(PKAPC Apc
)
597 PKTHREAD Thread
= Apc
->Thread
;
598 PKAPC_STATE ApcState
;
600 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
601 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc
);
604 OldIrql
= KeAcquireDispatcherDatabaseLock();
605 KeAcquireSpinLockAtDpcLevel(&Thread
->ApcQueueLock
);
607 /* Check if it's inserted */
608 if ((Inserted
= Apc
->Inserted
))
610 /* Remove it from the Queue*/
611 Apc
->Inserted
= FALSE
;
612 ApcState
= Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
];
613 RemoveEntryList(&Apc
->ApcListEntry
);
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
]))
618 /* Set the correct State based on the Apc Mode */
619 if (Apc
->ApcMode
== KernelMode
)
621 ApcState
->KernelApcPending
= FALSE
;
625 ApcState
->UserApcPending
= FALSE
;
630 /* Restore IRQL and Return */
631 KeReleaseSpinLockFromDpcLevel(&Thread
->ApcQueueLock
);
632 KeReleaseDispatcherDatabaseLock(OldIrql
);
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
645 * DeliveryMode - Specifies the current processor mode.
647 * Reserved - Pointer to the Exception Frame on non-i386 builds.
649 * TrapFrame - Pointer to the Trap Frame.
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
659 * Upon entry, this routine executes at APC_LEVEL.
664 KiDeliverApc(KPROCESSOR_MODE DeliveryMode
,
666 PKTRAP_FRAME TrapFrame
)
668 PKTHREAD Thread
= KeGetCurrentThread();
669 PKPROCESS Process
= KeGetCurrentProcess();
670 PKTRAP_FRAME OldTrapFrame
;
671 PLIST_ENTRY ApcListEntry
;
674 PKKERNEL_ROUTINE KernelRoutine
;
676 PKNORMAL_ROUTINE NormalRoutine
;
677 PVOID SystemArgument1
;
678 PVOID SystemArgument2
;
679 ASSERT_IRQL_EQUAL(APC_LEVEL
);
681 /* Save the old trap frame */
682 OldTrapFrame
= Thread
->TrapFrame
;
684 /* Clear Kernel APC Pending */
685 Thread
->ApcState
.KernelApcPending
= FALSE
;
687 /* Check if Special APCs are disabled */
688 if (Thread
->SpecialApcDisable
!= 0) goto Quickie
;
690 /* Do the Kernel APCs first */
691 while (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
]))
693 /* Lock the APC Queue and Raise IRQL to Synch */
694 KeAcquireSpinLock(&Thread
->ApcQueueLock
, &OldIrql
);
696 /* Get the next Entry */
697 ApcListEntry
= Thread
->ApcState
.ApcListHead
[KernelMode
].Flink
;
698 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
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
;
710 /* Remove the APC from the list */
711 RemoveEntryList(ApcListEntry
);
712 Apc
->Inserted
= FALSE
;
714 /* Go back to APC_LEVEL */
715 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
717 /* Call the Special APC */
718 DPRINT("Delivering a Special APC: %x\n", Apc
);
727 /* Normal Kernel APC, make sur APCs aren't disabled or in progress*/
728 if ((Thread
->ApcState
.KernelApcInProgress
) ||
729 (Thread
->KernelApcDisable
))
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).
737 ASSERT(DeliveryMode
== KernelMode
);
739 /* Release lock and return */
740 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
744 /* Dequeue the APC */
745 RemoveEntryList(ApcListEntry
);
746 Apc
->Inserted
= FALSE
;
748 /* Go back to APC_LEVEL */
749 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
751 /* Call the Kernel APC */
752 DPRINT("Delivering a Normal APC: %x\n", Apc
);
759 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
762 /* At Passive Level, this APC can be prempted by a Special APC */
763 Thread
->ApcState
.KernelApcInProgress
= TRUE
;
764 KeLowerIrql(PASSIVE_LEVEL
);
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
);
772 /* Set Kernel APC in progress to false and loop again */
773 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
777 /* Now we do the User APCs */
778 if ((DeliveryMode
== UserMode
) &&
779 (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])) &&
780 (Thread
->ApcState
.UserApcPending
== TRUE
))
782 /* Lock the APC Queue and Raise IRQL to Synch */
783 KeAcquireSpinLock(&Thread
->ApcQueueLock
, &OldIrql
);
785 /* It's not pending anymore */
786 Thread
->ApcState
.UserApcPending
= FALSE
;
788 /* Get the APC Entry */
789 ApcListEntry
= Thread
->ApcState
.ApcListHead
[UserMode
].Flink
;
791 /* Is it empty now? */
794 /* Release the lock and return */
795 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
799 /* Get the actual APC object */
800 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
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
;
809 /* Remove the APC from Queue, restore IRQL and call the APC */
810 RemoveEntryList(ApcListEntry
);
811 Apc
->Inserted
= FALSE
;
812 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
814 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc
);
823 /* Check if more User APCs are Pending */
824 KeTestAlertThread(UserMode
);
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
,
840 /* Make sure we're still in the same process */
841 if (Process
!= Thread
->ApcState
.Process
)
843 /* Erm, we got attached or something! BAD! */
844 KEBUGCHECKEX(INVALID_PROCESS_ATTACH_ATTEMPT
,
846 (ULONG_PTR
)Thread
->ApcState
.Process
,
847 Thread
->ApcStateIndex
,
848 KeGetCurrentPrcb()->DpcRoutineActive
);
851 /* Restore the trap frame */
852 Thread
->TrapFrame
= OldTrapFrame
;
858 KiFreeApcRoutine(PKAPC Apc
,
859 PKNORMAL_ROUTINE
* NormalRoutine
,
860 PVOID
* NormalContext
,
861 PVOID
* SystemArgument1
,
862 PVOID
* SystemArgument2
)
864 /* Free the APC and do nothing else */
869 * KiInitializeUserApc
871 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
874 * Reserved - Pointer to the Exception Frame on non-i386 builds.
876 * TrapFrame - Pointer to the Trap Frame.
878 * NormalRoutine - Pointer to the NormalRoutine to call.
880 * NormalContext - Pointer to the context to send to the Normal Routine.
882 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
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
)
905 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame
,
906 KeGetCurrentThread()->TrapFrame
);
908 /* Don't deliver APCs in V86 mode */
909 if (TrapFrame
->EFlags
& X86_EFLAGS_VM
) return;
911 /* Save the full context */
912 Context
.ContextFlags
= CONTEXT_FULL
| CONTEXT_DEBUG_REGISTERS
;
913 KeTrapFrameToContext(TrapFrame
, ExceptionFrame
, &Context
);
915 /* Protect with SEH */
918 /* Get the aligned size */
919 Size
= ((sizeof(CONTEXT
) + 3) & ~3) + 4 * sizeof(ULONG_PTR
);
920 Stack
= (Context
.Esp
& ~3) - Size
;
923 ProbeForWrite((PVOID
)Stack
, Size
, 4);
924 RtlMoveMemory((PVOID
)(Stack
+ 4 * sizeof(ULONG_PTR
)),
928 /* Run at APC dispatcher */
929 TrapFrame
->Eip
= (ULONG
)KeUserApcDispatcher
;
930 TrapFrame
->HardwareEsp
= Stack
;
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
;
940 /* FIXME: Get the record and raise an exception */
949 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
955 * KeAreApcsDisabled returns TRUE if the thread is within a critical region
956 * or a guarded region, and FALSE otherwise.
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.
965 * Callers of this routine must be running at IRQL <= APC_LEVEL.
970 KeAreApcsDisabled(VOID
)
972 /* Return the Kernel APC State */
973 return KeGetCurrentThread()->CombinedApcDisable
? TRUE
: FALSE
;
980 * This routine is used to queue an APC from user-mode for the specified
984 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
986 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
988 * NormalContext - Pointer to the context to send to the Normal Routine.
990 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
994 * STATUS_SUCCESS or failure cute from associated calls.
997 * The thread must enter an alertable wait before the APC will be
1003 NtQueueApcThread(HANDLE ThreadHandle
,
1004 PKNORMAL_ROUTINE ApcRoutine
,
1005 PVOID NormalContext
,
1006 PVOID SystemArgument1
,
1007 PVOID SystemArgument2
)
1011 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
1014 /* Get ETHREAD from Handle */
1015 Status
= ObReferenceObjectByHandle(ThreadHandle
,
1022 /* Fail if the Handle is invalid for some reason */
1023 if (!NT_SUCCESS(Status
)) {
1028 /* If this is a Kernel or System Thread, then fail */
1029 if (Thread
->Tcb
.Teb
== NULL
) {
1031 ObDereferenceObject(Thread
);
1032 return STATUS_INVALID_HANDLE
;
1035 /* Allocate an APC */
1036 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG('P', 's', 'a', 'p'));
1039 ObDereferenceObject(Thread
);
1040 return(STATUS_NO_MEMORY
);
1043 /* Initialize and Queue a user mode apc (always!) */
1044 KeInitializeApc(Apc
,
1046 OriginalApcEnvironment
,
1053 if (!KeInsertQueueApc(Apc
, SystemArgument1
, SystemArgument2
, IO_NO_INCREMENT
)) {
1055 Status
= STATUS_UNSUCCESSFUL
;
1059 Status
= STATUS_SUCCESS
;
1062 /* Dereference Thread and Return */
1063 ObDereferenceObject(Thread
);
1068 VOID
RepairList(PLIST_ENTRY Original
,
1070 KPROCESSOR_MODE Mode
)
1072 /* Copy Source to Desination */
1073 if (IsListEmpty(&Original
[(int)Mode
])) {
1075 InitializeListHead(&Copy
[(int)Mode
]);
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
];
1088 KiMoveApcState(PKAPC_STATE OldState
,
1089 PKAPC_STATE NewState
)
1091 /* Restore backup of Original Environment */
1092 *NewState
= *OldState
;
1095 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, KernelMode
);
1096 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, UserMode
);