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 * KiKernelApcDeliveryCheck
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.
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 KiKernelApcDeliveryCheck(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
]))
128 /* Check for the right environment */
129 KiKernelApcDeliveryCheck();
138 * The The KeInitializeApc routine initializes an APC object, and registers
139 * the Kernel, Rundown and Normal routines for that object.
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.
146 * Thread - Thread to which to deliver the APC.
148 * TargetEnvironment - APC Environment to be used.
150 * KernelRoutine - Points to the KernelRoutine to associate with the APC.
151 * This routine is executed for all APCs.
153 * RundownRoutine - Points to the RundownRoutine to associate with the APC.
154 * This routine is executed when the Thread exists with
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.
162 * Mode - Specifies the processor mode at which to run the Normal Routine.
164 * Context - Specifices the value to pass as Context parameter to the
165 * registered routines.
171 * The caller can queue an initialized APC with KeInsertQueueApc.
173 * Storage for the APC object must be resident, such as nonpaged pool
174 * allocated by the caller.
176 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
181 KeInitializeApc(IN PKAPC Apc
,
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
,
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
);
195 /* Set up the basic APC Structure Data */
196 RtlZeroMemory(Apc
, sizeof(KAPC
));
197 Apc
->Type
= ApcObject
;
198 Apc
->Size
= sizeof(KAPC
);
200 /* Set the Environment */
201 if (TargetEnvironment
== CurrentApcEnvironment
) {
203 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
207 Apc
->ApcStateIndex
= TargetEnvironment
;
210 /* Set the Thread and Routines */
211 Apc
->Thread
= Thread
;
212 Apc
->KernelRoutine
= KernelRoutine
;
213 Apc
->RundownRoutine
= RundownRoutine
;
214 Apc
->NormalRoutine
= NormalRoutine
;
216 /* Check if this is a Special APC, in which case we use KernelMode and no Context */
217 if (ARGUMENT_PRESENT(NormalRoutine
)) {
220 Apc
->NormalContext
= Context
;
224 Apc
->ApcMode
= KernelMode
;
231 * The KiInsertQueueApc routine queues a APC for execution when the right
232 * scheduler environment exists.
235 * Apc - Pointer to an initialized control object of type DPC for which the
236 * caller provides the storage.
238 * PriorityBoost - Priority Boost to apply to the Thread.
241 * If the APC is already inserted or APC queueing is disabled, FALSE.
245 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
246 * at PASSIVE_LEVEL for the NormalRoutine registered.
248 * Callers of this routine must have locked the dipatcher database.
253 KiInsertQueueApc(PKAPC Apc
,
254 KPRIORITY PriorityBoost
)
256 PKTHREAD Thread
= Apc
->Thread
;
257 PLIST_ENTRY ApcListEntry
;
260 KeAcquireSpinLockAtDpcLevel(&Thread
->ApcQueueLock
);
262 /* Don't do anything if the APC is already inserted */
265 KeReleaseSpinLockFromDpcLevel(&Thread
->ApcQueueLock
);
270 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
271 2) User APC which is PsExitSpecialApc = Put it at the front of the List
272 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
274 if ((Apc
->ApcMode
!= KernelMode
) && (Apc
->KernelRoutine
== (PKKERNEL_ROUTINE
)PsExitSpecialApc
)) {
276 DPRINT1("Inserting the Thread Exit APC for '%.16s' into the Queue\n", ((PETHREAD
)Thread
)->ThreadsProcess
->ImageFileName
);
277 Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->UserApcPending
= TRUE
;
278 InsertHeadList(&Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
],
281 } else if (Apc
->NormalRoutine
== NULL
) {
283 DPRINT("Inserting Special APC %x for '%.16s' into the Queue\n", Apc
, ((PETHREAD
)Thread
)->ThreadsProcess
->ImageFileName
);
285 for (ApcListEntry
= Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
].Flink
;
286 ApcListEntry
!= &Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
];
287 ApcListEntry
= ApcListEntry
->Flink
) {
289 QueuedApc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
290 if (Apc
->NormalRoutine
!= NULL
) break;
293 /* We found the first "Normal" APC, so write right before it */
294 ApcListEntry
= ApcListEntry
->Blink
;
295 InsertHeadList(ApcListEntry
, &Apc
->ApcListEntry
);
299 DPRINT("Inserting Normal APC %x for '%.16s' into the %x Queue\n", Apc
, ((PETHREAD
)Thread
)->ThreadsProcess
->ImageFileName
, Apc
->ApcMode
);
300 InsertTailList(&Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
],
304 /* Confirm Insertion */
305 Apc
->Inserted
= TRUE
;
307 KeReleaseSpinLockFromDpcLevel(&Thread
->ApcQueueLock
);
310 * Three possibilites here again:
311 * 1) Kernel APC, The thread is Running: Request an Interrupt
312 * 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread
313 * 3) User APC, Unwait the Thread if it is alertable
315 if (Apc
->ApcMode
== KernelMode
) {
317 /* Set Kernel APC pending */
318 Thread
->ApcState
.KernelApcPending
= TRUE
;
320 /* Check the Thread State */
321 if (Thread
->State
== Running
) {
324 PKPRCB Prcb
, CurrentPrcb
;
328 DPRINT ("Requesting APC Interrupt for Running Thread \n");
331 CurrentPrcb
= KeGetCurrentPrcb();
332 if (CurrentPrcb
->CurrentThread
== Thread
)
334 HalRequestSoftwareInterrupt(APC_LEVEL
);
338 for (i
= 0; i
< KeNumberProcessors
; i
++)
340 Prcb
= ((PKPCR
)(KPCR_BASE
+ i
* PAGE_SIZE
))->Prcb
;
341 if (Prcb
->CurrentThread
== Thread
)
343 ASSERT (CurrentPrcb
!= Prcb
);
344 KiIpiSendRequest(Prcb
->SetMember
, IPI_REQUEST_APC
);
348 ASSERT (i
< KeNumberProcessors
);
351 HalRequestSoftwareInterrupt(APC_LEVEL
);
354 } else if ((Thread
->State
== Waiting
) && (Thread
->WaitIrql
== PASSIVE_LEVEL
) &&
355 ((Apc
->NormalRoutine
== NULL
) ||
356 ((!Thread
->KernelApcDisable
) && (!Thread
->ApcState
.KernelApcInProgress
)))) {
358 DPRINT("Waking up Thread for Kernel-Mode APC Delivery \n");
359 KiAbortWaitThread(Thread
, STATUS_KERNEL_APC
, PriorityBoost
);
362 } else if ((Thread
->State
== Waiting
) &&
363 (Thread
->WaitMode
!= KernelMode
) &&
364 (Thread
->Alertable
)) {
366 DPRINT("Waking up Thread for User-Mode APC Delivery \n");
367 Thread
->ApcState
.UserApcPending
= TRUE
;
368 KiAbortWaitThread(Thread
, STATUS_USER_APC
, PriorityBoost
);
378 * The KeInsertQueueApc routine queues a APC for execution when the right
379 * scheduler environment exists.
382 * Apc - Pointer to an initialized control object of type DPC for which the
383 * caller provides the storage.
385 * SystemArgument[1,2] - Pointer to a set of two parameters that contain
388 * PriorityBoost - Priority Boost to apply to the Thread.
391 * If the APC is already inserted or APC queueing is disabled, FALSE.
395 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
396 * at PASSIVE_LEVEL for the NormalRoutine registered.
398 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
403 KeInsertQueueApc(PKAPC Apc
,
404 PVOID SystemArgument1
,
405 PVOID SystemArgument2
,
406 KPRIORITY PriorityBoost
)
413 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
414 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
415 "SystemArgument2 %x)\n",Apc
,SystemArgument1
,
418 /* Lock the Dispatcher Database */
419 OldIrql
= KeAcquireDispatcherDatabaseLock();
421 /* Get the Thread specified in the APC */
422 Thread
= Apc
->Thread
;
424 /* Make sure the thread allows APC Queues.
425 * The thread is not apc queueable, for instance, when it's (about to be) terminated.
427 if (Thread
->ApcQueueable
== FALSE
) {
428 DPRINT("Thread doesn't allow APC Queues\n");
429 KeReleaseDispatcherDatabaseLock(OldIrql
);
433 /* Set the System Arguments */
434 Apc
->SystemArgument1
= SystemArgument1
;
435 Apc
->SystemArgument2
= SystemArgument2
;
437 /* Call the Internal Function */
438 Inserted
= KiInsertQueueApc(Apc
, PriorityBoost
);
440 /* Return Sucess if we are here */
441 KeReleaseDispatcherDatabaseLock(OldIrql
);
448 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
449 * from the specified Thread's APC queue.
452 * Thread - Pointer to the thread whose APC queue will be flushed.
454 * PreviousMode - Specifies which APC Queue to flush.
457 * A pointer to the first entry in the flushed APC queue.
460 * If the routine returns NULL, it means that no APCs were to be flushed.
462 * Callers of KeFlushQueueApc must be running at DISPATCH_LEVEL or lower.
467 KeFlushQueueApc(IN PKTHREAD Thread
,
468 IN KPROCESSOR_MODE PreviousMode
)
472 PLIST_ENTRY FirstEntry
, CurrentEntry
;
474 /* Lock the Dispatcher Database and APC Queue */
475 OldIrql
= KeAcquireDispatcherDatabaseLock();
476 KeAcquireSpinLockAtDpcLevel(&Thread
->ApcQueueLock
);
478 if (IsListEmpty(&Thread
->ApcState
.ApcListHead
[PreviousMode
])) {
481 FirstEntry
= Thread
->ApcState
.ApcListHead
[PreviousMode
].Flink
;
482 RemoveEntryList(&Thread
->ApcState
.ApcListHead
[PreviousMode
]);
483 CurrentEntry
= FirstEntry
;
485 Apc
= CONTAINING_RECORD(CurrentEntry
, KAPC
, ApcListEntry
);
486 Apc
->Inserted
= FALSE
;
487 CurrentEntry
= CurrentEntry
->Flink
;
488 } while (CurrentEntry
!= FirstEntry
);
491 /* Release the locks */
492 KeReleaseSpinLockFromDpcLevel(&Thread
->ApcQueueLock
);
493 KeReleaseDispatcherDatabaseLock(OldIrql
);
495 /* Return the first entry */
502 * The KeRemoveQueueApc routine removes a given APC object from the system
506 * APC - Pointer to an initialized APC object that was queued by calling
510 * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is
511 * performed and FALSE is returned.
514 * If the given APC Object is currently queued, it is removed from the queue
515 * and any calls to the registered routines are cancelled.
517 * Callers of KeLeaveCriticalRegion can be running at any IRQL.
522 KeRemoveQueueApc(PKAPC Apc
)
525 PKTHREAD Thread
= Apc
->Thread
;
527 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
528 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc
);
530 OldIrql
= KeAcquireDispatcherDatabaseLock();
531 KeAcquireSpinLockAtDpcLevel(&Thread
->ApcQueueLock
);
533 /* Check if it's inserted */
536 /* Remove it from the Queue*/
537 RemoveEntryList(&Apc
->ApcListEntry
);
538 Apc
->Inserted
= FALSE
;
540 /* If the Queue is completely empty, then no more APCs are pending */
541 if (IsListEmpty(&Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
])) {
543 /* Set the correct State based on the Apc Mode */
544 if (Apc
->ApcMode
== KernelMode
) {
546 Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->KernelApcPending
= FALSE
;
550 Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->UserApcPending
= FALSE
;
556 /* It's not inserted, fail */
557 KeReleaseSpinLockFromDpcLevel(&Thread
->ApcQueueLock
);
558 KeReleaseDispatcherDatabaseLock(OldIrql
);
562 /* Restore IRQL and Return */
563 KeReleaseSpinLockFromDpcLevel(&Thread
->ApcQueueLock
);
564 KeReleaseDispatcherDatabaseLock(OldIrql
);
572 * The KiDeliverApc routine is called from IRQL switching code if the
573 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
577 * DeliveryMode - Specifies the current processor mode.
579 * Reserved - Pointer to the Exception Frame on non-i386 builds.
581 * TrapFrame - Pointer to the Trap Frame.
587 * First, Special APCs are delivered, followed by Kernel-Mode APCs and
588 * User-Mode APCs. Note that the TrapFrame is only valid if the previous
591 * Upon entry, this routine executes at APC_LEVEL.
596 KiDeliverApc(KPROCESSOR_MODE DeliveryMode
,
598 PKTRAP_FRAME TrapFrame
)
600 PKTHREAD Thread
= KeGetCurrentThread();
601 PLIST_ENTRY ApcListEntry
;
604 PKKERNEL_ROUTINE KernelRoutine
;
606 PKNORMAL_ROUTINE NormalRoutine
;
607 PVOID SystemArgument1
;
608 PVOID SystemArgument2
;
610 ASSERT_IRQL_EQUAL(APC_LEVEL
);
612 /* Lock the APC Queue and Raise IRQL to Synch */
613 KeAcquireSpinLock(&Thread
->ApcQueueLock
, &OldIrql
);
615 /* Clear APC Pending */
616 Thread
->ApcState
.KernelApcPending
= FALSE
;
618 /* Do the Kernel APCs first */
619 while (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
])) {
621 /* Get the next Entry */
622 ApcListEntry
= Thread
->ApcState
.ApcListHead
[KernelMode
].Flink
;
623 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
625 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
626 NormalRoutine
= Apc
->NormalRoutine
;
627 KernelRoutine
= Apc
->KernelRoutine
;
628 NormalContext
= Apc
->NormalContext
;
629 SystemArgument1
= Apc
->SystemArgument1
;
630 SystemArgument2
= Apc
->SystemArgument2
;
633 if (NormalRoutine
== NULL
) {
635 /* Remove the APC from the list */
636 RemoveEntryList(ApcListEntry
);
637 Apc
->Inserted
= FALSE
;
639 /* Go back to APC_LEVEL */
640 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
642 /* Call the Special APC */
643 DPRINT("Delivering a Special APC: %x\n", Apc
);
650 /* Raise IRQL and Lock again */
651 KeAcquireSpinLock(&Thread
->ApcQueueLock
, &OldIrql
);
655 /* Normal Kernel APC */
656 if (Thread
->ApcState
.KernelApcInProgress
|| Thread
->KernelApcDisable
) {
659 * DeliveryMode must be KernelMode in this case, since one may not
660 * return to umode while being inside a critical section or while
661 * a regular kmode apc is running (the latter should be impossible btw).
664 ASSERT(DeliveryMode
== KernelMode
);
666 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
670 /* Dequeue the APC */
671 RemoveEntryList(ApcListEntry
);
672 Apc
->Inserted
= FALSE
;
674 /* Go back to APC_LEVEL */
675 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
677 /* Call the Kernel APC */
678 DPRINT("Delivering a Normal APC: %x\n", Apc
);
685 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
686 if (NormalRoutine
!= NULL
) {
688 /* At Passive Level, this APC can be prempted by a Special APC */
689 Thread
->ApcState
.KernelApcInProgress
= TRUE
;
690 KeLowerIrql(PASSIVE_LEVEL
);
692 /* Call and Raise IRQ back to APC_LEVEL */
693 DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc
);
694 NormalRoutine(NormalContext
, SystemArgument1
, SystemArgument2
);
695 KeRaiseIrql(APC_LEVEL
, &OldIrql
);
698 /* Raise IRQL and Lock again */
699 KeAcquireSpinLock(&Thread
->ApcQueueLock
, &OldIrql
);
700 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
704 /* Now we do the User APCs */
705 if ((!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])) &&
706 (DeliveryMode
!= KernelMode
) && (Thread
->ApcState
.UserApcPending
== TRUE
)) {
708 /* It's not pending anymore */
709 Thread
->ApcState
.UserApcPending
= FALSE
;
711 /* Get the APC Object */
712 ApcListEntry
= Thread
->ApcState
.ApcListHead
[UserMode
].Flink
;
713 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
715 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
716 NormalRoutine
= Apc
->NormalRoutine
;
717 KernelRoutine
= Apc
->KernelRoutine
;
718 NormalContext
= Apc
->NormalContext
;
719 SystemArgument1
= Apc
->SystemArgument1
;
720 SystemArgument2
= Apc
->SystemArgument2
;
722 /* Remove the APC from Queue, restore IRQL and call the APC */
723 RemoveEntryList(ApcListEntry
);
724 Apc
->Inserted
= FALSE
;
725 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
727 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc
);
734 if (NormalRoutine
== NULL
) {
736 /* Check if more User APCs are Pending */
737 KeTestAlertThread(UserMode
);
741 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
742 DPRINT("Delivering a User APC: %x\n", Apc
);
743 KiInitializeUserApc(Reserved
,
753 /* Go back to APC_LEVEL */
754 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
760 KiFreeApcRoutine(PKAPC Apc
,
761 PKNORMAL_ROUTINE
* NormalRoutine
,
762 PVOID
* NormalContext
,
763 PVOID
* SystemArgument1
,
764 PVOID
* SystemArgument2
)
766 /* Free the APC and do nothing else */
771 * KiInitializeUserApc
773 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
776 * Reserved - Pointer to the Exception Frame on non-i386 builds.
778 * TrapFrame - Pointer to the Trap Frame.
780 * NormalRoutine - Pointer to the NormalRoutine to call.
782 * NormalContext - Pointer to the context to send to the Normal Routine.
784 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
796 KiInitializeUserApc(IN PKEXCEPTION_FRAME ExceptionFrame
,
797 IN PKTRAP_FRAME TrapFrame
,
798 IN PKNORMAL_ROUTINE NormalRoutine
,
799 IN PVOID NormalContext
,
800 IN PVOID SystemArgument1
,
801 IN PVOID SystemArgument2
)
807 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame
,
808 KeGetCurrentThread()->TrapFrame
);
810 /* Don't deliver APCs in V86 mode */
811 if (TrapFrame
->Eflags
& 2) return;
813 /* Save the full context */
814 Context
.ContextFlags
= CONTEXT_FULL
| CONTEXT_DEBUG_REGISTERS
;
815 KeTrapFrameToContext(TrapFrame
, ExceptionFrame
, &Context
);
817 /* Protect with SEH */
820 /* Get the aligned size */
821 Size
= ((sizeof(CONTEXT
) + 3) & ~3) + 4 * sizeof(ULONG_PTR
);
822 Stack
= (Context
.Esp
& ~3) - Size
;
825 ProbeForWrite((PVOID
)Stack
, Size
, 4);
826 RtlMoveMemory((PVOID
)(Stack
+ 4 * sizeof(ULONG_PTR
)),
830 /* Run at APC dispatcher */
831 TrapFrame
->Eip
= (ULONG
)KeUserApcDispatcher
;
832 TrapFrame
->Esp
= Stack
;
834 /* Setup the stack */
835 *(PULONG_PTR
)(Stack
+ 0 * sizeof(ULONG_PTR
)) = (ULONG_PTR
)NormalRoutine
;
836 *(PULONG_PTR
)(Stack
+ 1 * sizeof(ULONG_PTR
)) = (ULONG_PTR
)NormalContext
;
837 *(PULONG_PTR
)(Stack
+ 2 * sizeof(ULONG_PTR
)) = (ULONG_PTR
)SystemArgument1
;
838 *(PULONG_PTR
)(Stack
+ 3 * sizeof(ULONG_PTR
)) = (ULONG_PTR
)SystemArgument2
;
842 /* FIXME: Get the record and raise an exception */
851 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
857 * KeAreApcsDisabled returns TRUE if the thread is within a critical region
858 * or a guarded region, and FALSE otherwise.
861 * A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled to
862 * determine if normal kernel APCs are disabled. A thread that is inside a
863 * critical region has both user APCs and normal kernel APCs disabled, but
864 * not special kernel APCs. A thread that is inside a guarded region has
865 * all APCs disabled, including special kernel APCs.
867 * Callers of this routine must be running at IRQL <= APC_LEVEL.
872 KeAreApcsDisabled(VOID
)
874 /* Return the Kernel APC State */
875 return KeGetCurrentThread()->KernelApcDisable
? TRUE
: FALSE
;
882 * This routine is used to queue an APC from user-mode for the specified
886 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
888 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
890 * NormalContext - Pointer to the context to send to the Normal Routine.
892 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
896 * STATUS_SUCCESS or failure cute from associated calls.
899 * The thread must enter an alertable wait before the APC will be
905 NtQueueApcThread(HANDLE ThreadHandle
,
906 PKNORMAL_ROUTINE ApcRoutine
,
908 PVOID SystemArgument1
,
909 PVOID SystemArgument2
)
913 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
916 /* Get ETHREAD from Handle */
917 Status
= ObReferenceObjectByHandle(ThreadHandle
,
924 /* Fail if the Handle is invalid for some reason */
925 if (!NT_SUCCESS(Status
)) {
930 /* If this is a Kernel or System Thread, then fail */
931 if (Thread
->Tcb
.Teb
== NULL
) {
933 ObDereferenceObject(Thread
);
934 return STATUS_INVALID_HANDLE
;
937 /* Allocate an APC */
938 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG('P', 's', 'a', 'p'));
941 ObDereferenceObject(Thread
);
942 return(STATUS_NO_MEMORY
);
945 /* Initialize and Queue a user mode apc (always!) */
948 OriginalApcEnvironment
,
955 if (!KeInsertQueueApc(Apc
, SystemArgument1
, SystemArgument2
, IO_NO_INCREMENT
)) {
957 Status
= STATUS_UNSUCCESSFUL
;
961 Status
= STATUS_SUCCESS
;
964 /* Dereference Thread and Return */
965 ObDereferenceObject(Thread
);
970 VOID
RepairList(PLIST_ENTRY Original
,
972 KPROCESSOR_MODE Mode
)
974 /* Copy Source to Desination */
975 if (IsListEmpty(&Original
[(int)Mode
])) {
977 InitializeListHead(&Copy
[(int)Mode
]);
981 Copy
[(int)Mode
].Flink
= Original
[(int)Mode
].Flink
;
982 Copy
[(int)Mode
].Blink
= Original
[(int)Mode
].Blink
;
983 Original
[(int)Mode
].Flink
->Blink
= &Copy
[(int)Mode
];
984 Original
[(int)Mode
].Blink
->Flink
= &Copy
[(int)Mode
];
990 KiMoveApcState(PKAPC_STATE OldState
,
991 PKAPC_STATE NewState
)
993 /* Restore backup of Original Environment */
994 *NewState
= *OldState
;
997 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, KernelMode
);
998 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, UserMode
);