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 * KeEnterCriticalRegion
23 * The KeEnterCriticalRegion routine temporarily disables the delivery of
24 * normal kernel APCs; special kernel-mode APCs are still delivered.
33 * Highest-level drivers can call this routine while running in the context
34 * of the thread that requested the current I/O operation. Any caller of
35 * this routine should call KeLeaveCriticalRegion as quickly as possible.
37 * Callers of KeEnterCriticalRegion must be running at IRQL <= APC_LEVEL.
42 KeEnterCriticalRegion(VOID
)
44 /* Disable Kernel APCs */
45 PKTHREAD Thread
= KeGetCurrentThread();
46 if (Thread
) Thread
->KernelApcDisable
--;
53 * The The KeInitializeApc routine initializes an APC object, and registers
54 * the Kernel, Rundown and Normal routines for that object.
57 * Apc - Pointer to a KAPC structure that represents the APC object to
58 * initialize. The caller must allocate storage for the structure
59 * from resident memory.
61 * Thread - Thread to which to deliver the APC.
63 * TargetEnvironment - APC Environment to be used.
65 * KernelRoutine - Points to the KernelRoutine to associate with the APC.
66 * This routine is executed for all APCs.
68 * RundownRoutine - Points to the RundownRoutine to associate with the APC.
69 * This routine is executed when the Thread exists with
72 * NormalRoutine - Points to the NormalRoutine to associate with the APC.
73 * This routine is executed at PASSIVE_LEVEL. If this is
74 * not specifed, the APC becomes a Special APC and the
75 * Mode and Context parameters are ignored.
77 * Mode - Specifies the processor mode at which to run the Normal Routine.
79 * Context - Specifices the value to pass as Context parameter to the
80 * registered routines.
86 * The caller can queue an initialized APC with KeInsertQueueApc.
88 * Storage for the APC object must be resident, such as nonpaged pool
89 * allocated by the caller.
91 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
96 KeInitializeApc(IN PKAPC Apc
,
98 IN KAPC_ENVIRONMENT TargetEnvironment
,
99 IN PKKERNEL_ROUTINE KernelRoutine
,
100 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL
,
101 IN PKNORMAL_ROUTINE NormalRoutine
,
102 IN KPROCESSOR_MODE Mode
,
105 DPRINT("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
106 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
107 "Context %x)\n",Apc
,Thread
,TargetEnvironment
,KernelRoutine
,RundownRoutine
,
108 NormalRoutine
,Mode
,Context
);
110 /* Set up the basic APC Structure Data */
111 RtlZeroMemory(Apc
, sizeof(KAPC
));
112 Apc
->Type
= ApcObject
;
113 Apc
->Size
= sizeof(KAPC
);
115 /* Set the Environment */
116 if (TargetEnvironment
== CurrentApcEnvironment
) {
118 Apc
->ApcStateIndex
= Thread
->ApcStateIndex
;
122 Apc
->ApcStateIndex
= TargetEnvironment
;
125 /* Set the Thread and Routines */
126 Apc
->Thread
= Thread
;
127 Apc
->KernelRoutine
= KernelRoutine
;
128 Apc
->RundownRoutine
= RundownRoutine
;
129 Apc
->NormalRoutine
= NormalRoutine
;
131 /* Check if this is a Special APC, in which case we use KernelMode and no Context */
132 if (ARGUMENT_PRESENT(NormalRoutine
)) {
135 Apc
->NormalContext
= Context
;
139 Apc
->ApcMode
= KernelMode
;
146 * The KiInsertQueueApc routine queues a APC for execution when the right
147 * scheduler environment exists.
150 * Apc - Pointer to an initialized control object of type DPC for which the
151 * caller provides the storage.
153 * PriorityBoost - Priority Boost to apply to the Thread.
156 * If the APC is already inserted or APC queueing is disabled, FALSE.
160 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
161 * at PASSIVE_LEVEL for the NormalRoutine registered.
163 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
168 KiInsertQueueApc(PKAPC Apc
,
169 KPRIORITY PriorityBoost
)
171 PKTHREAD Thread
= Apc
->Thread
;
172 PLIST_ENTRY ApcListEntry
;
175 /* Don't do anything if the APC is already inserted */
182 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
183 2) User APC which is PsExitSpecialApc = Put it at the front of the List
184 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
186 if ((Apc
->ApcMode
!= KernelMode
) && (Apc
->KernelRoutine
== (PKKERNEL_ROUTINE
)PsExitSpecialApc
)) {
188 DPRINT ("Inserting the Process Exit APC into the Queue\n");
189 Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->UserApcPending
= TRUE
;
190 InsertHeadList(&Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
],
193 } else if (Apc
->NormalRoutine
== NULL
) {
195 DPRINT ("Inserting Special APC %x into the Queue\n", Apc
);
197 for (ApcListEntry
= Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
].Flink
;
198 ApcListEntry
!= &Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
];
199 ApcListEntry
= ApcListEntry
->Flink
) {
201 QueuedApc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
202 if (Apc
->NormalRoutine
!= NULL
) break;
205 /* We found the first "Normal" APC, so write right before it */
206 ApcListEntry
= ApcListEntry
->Blink
;
207 InsertHeadList(ApcListEntry
, &Apc
->ApcListEntry
);
211 DPRINT ("Inserting Normal APC %x into the %x Queue\n", Apc
, Apc
->ApcMode
);
212 InsertTailList(&Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
],
216 /* Confirm Insertion */
217 Apc
->Inserted
= TRUE
;
220 * Three possibilites here again:
221 * 1) Kernel APC, The thread is Running: Request an Interrupt
222 * 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread
223 * 3) User APC, Unwait the Thread if it is alertable
225 if (Apc
->ApcMode
== KernelMode
) {
227 /* Set Kernel APC pending */
228 Thread
->ApcState
.KernelApcPending
= TRUE
;
230 /* Check the Thread State */
231 if (Thread
->State
== THREAD_STATE_RUNNING
) {
234 DPRINT ("Requesting APC Interrupt for Running Thread \n");
235 HalRequestSoftwareInterrupt(APC_LEVEL
);
237 } else if ((Thread
->State
== THREAD_STATE_BLOCKED
) && (Thread
->WaitIrql
== PASSIVE_LEVEL
) &&
238 ((Apc
->NormalRoutine
== NULL
) ||
239 ((!Thread
->KernelApcDisable
) && (!Thread
->ApcState
.KernelApcInProgress
)))) {
241 DPRINT("Waking up Thread for Kernel-Mode APC Delivery \n");
242 KiAbortWaitThread(Thread
, STATUS_KERNEL_APC
, PriorityBoost
);
245 } else if ((Thread
->State
== THREAD_STATE_BLOCKED
) &&
246 (Thread
->WaitMode
== UserMode
) &&
247 (Thread
->Alertable
)) {
249 DPRINT("Waking up Thread for User-Mode APC Delivery \n");
250 Thread
->ApcState
.UserApcPending
= TRUE
;
251 KiAbortWaitThread(Thread
, STATUS_USER_APC
, PriorityBoost
);
261 * The KeInsertQueueApc routine queues a APC for execution when the right
262 * scheduler environment exists.
265 * Apc - Pointer to an initialized control object of type DPC for which the
266 * caller provides the storage.
268 * SystemArgument[1,2] - Pointer to a set of two parameters that contain
271 * PriorityBoost - Priority Boost to apply to the Thread.
274 * If the APC is already inserted or APC queueing is disabled, FALSE.
278 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
279 * at PASSIVE_LEVEL for the NormalRoutine registered.
281 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
286 KeInsertQueueApc(PKAPC Apc
,
287 PVOID SystemArgument1
,
288 PVOID SystemArgument2
,
289 KPRIORITY PriorityBoost
)
296 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
297 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
298 "SystemArgument2 %x)\n",Apc
,SystemArgument1
,
301 /* Lock the Dispatcher Database */
302 OldIrql
= KeAcquireDispatcherDatabaseLock();
304 /* Get the Thread specified in the APC */
305 Thread
= Apc
->Thread
;
307 /* Make sure the thread allows APC Queues.
308 * The thread is not apc queueable, for instance, when it's (about to be) terminated.
310 if (Thread
->ApcQueueable
== FALSE
) {
311 DPRINT("Thread doesn't allow APC Queues\n");
312 KeReleaseDispatcherDatabaseLock(OldIrql
);
316 /* Set the System Arguments */
317 Apc
->SystemArgument1
= SystemArgument1
;
318 Apc
->SystemArgument2
= SystemArgument2
;
320 /* Call the Internal Function */
321 Inserted
= KiInsertQueueApc(Apc
, PriorityBoost
);
323 /* Return Sucess if we are here */
324 KeReleaseDispatcherDatabaseLock(OldIrql
);
329 * KeLeaveCriticalRegion
332 * The KeLeaveCriticalRegion routine reenables the delivery of normal
333 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
342 * Highest-level drivers can call this routine while running in the context
343 * of the thread that requested the current I/O operation.
345 * Callers of KeLeaveCriticalRegion must be running at IRQL <= DISPATCH_LEVEL.
350 KeLeaveCriticalRegion (VOID
)
352 PKTHREAD Thread
= KeGetCurrentThread();
354 /* Check if Kernel APCs are now enabled */
355 if((Thread
) && (++Thread
->KernelApcDisable
== 0)) {
357 /* Check if we need to request an APC Delivery */
358 if (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
])) {
360 /* Set APC Pending */
361 Thread
->ApcState
.KernelApcPending
= TRUE
;
362 HalRequestSoftwareInterrupt(APC_LEVEL
);
370 * The KeRemoveQueueApc routine removes a given APC object from the system
374 * APC - Pointer to an initialized APC object that was queued by calling
378 * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is
379 * performed and FALSE is returned.
382 * If the given APC Object is currently queued, it is removed from the queue
383 * and any calls to the registered routines are cancelled.
385 * Callers of KeLeaveCriticalRegion can be running at any IRQL.
390 KeRemoveQueueApc(PKAPC Apc
)
393 PKTHREAD Thread
= Apc
->Thread
;
395 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
396 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc
);
398 OldIrql
= KeAcquireDispatcherDatabaseLock();
399 KeAcquireSpinLock(&Thread
->ApcQueueLock
, &OldIrql
);
401 /* Check if it's inserted */
404 /* Remove it from the Queue*/
405 RemoveEntryList(&Apc
->ApcListEntry
);
406 Apc
->Inserted
= FALSE
;
408 /* If the Queue is completely empty, then no more APCs are pending */
409 if (IsListEmpty(&Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->ApcListHead
[(int)Apc
->ApcMode
])) {
411 /* Set the correct State based on the Apc Mode */
412 if (Apc
->ApcMode
== KernelMode
) {
414 Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->KernelApcPending
= FALSE
;
418 Thread
->ApcStatePointer
[(int)Apc
->ApcStateIndex
]->UserApcPending
= FALSE
;
424 /* It's not inserted, fail */
425 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
426 KeReleaseDispatcherDatabaseLock(OldIrql
);
430 /* Restore IRQL and Return */
431 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
432 KeReleaseDispatcherDatabaseLock(OldIrql
);
440 * The KiDeliverApc routine is called from IRQL switching code if the
441 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
445 * DeliveryMode - Specifies the current processor mode.
447 * Reserved - Pointer to the Exception Frame on non-i386 builds.
449 * TrapFrame - Pointer to the Trap Frame.
455 * First, Special APCs are delivered, followed by Kernel-Mode APCs and
456 * User-Mode APCs. Note that the TrapFrame is only valid if the previous
459 * Upon entry, this routine executes at APC_LEVEL.
464 KiDeliverApc(KPROCESSOR_MODE DeliveryMode
,
466 PKTRAP_FRAME TrapFrame
)
468 PKTHREAD Thread
= KeGetCurrentThread();
469 PLIST_ENTRY ApcListEntry
;
472 PKKERNEL_ROUTINE KernelRoutine
;
474 PKNORMAL_ROUTINE NormalRoutine
;
475 PVOID SystemArgument1
;
476 PVOID SystemArgument2
;
478 ASSERT_IRQL_EQUAL(APC_LEVEL
);
480 /* Lock the APC Queue and Raise IRQL to Synch */
481 KeAcquireSpinLock(&Thread
->ApcQueueLock
, &OldIrql
);
483 /* Clear APC Pending */
484 Thread
->ApcState
.KernelApcPending
= FALSE
;
486 /* Do the Kernel APCs first */
487 while (!IsListEmpty(&Thread
->ApcState
.ApcListHead
[KernelMode
])) {
489 /* Get the next Entry */
490 ApcListEntry
= Thread
->ApcState
.ApcListHead
[KernelMode
].Flink
;
491 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
493 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
494 NormalRoutine
= Apc
->NormalRoutine
;
495 KernelRoutine
= Apc
->KernelRoutine
;
496 NormalContext
= Apc
->NormalContext
;
497 SystemArgument1
= Apc
->SystemArgument1
;
498 SystemArgument2
= Apc
->SystemArgument2
;
501 if (NormalRoutine
== NULL
) {
503 /* Remove the APC from the list */
504 Apc
->Inserted
= FALSE
;
505 RemoveEntryList(ApcListEntry
);
507 /* Go back to APC_LEVEL */
508 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
510 /* Call the Special APC */
511 DPRINT("Delivering a Special APC: %x\n", Apc
);
518 /* Raise IRQL and Lock again */
519 KeAcquireSpinLock(&Thread
->ApcQueueLock
, &OldIrql
);
523 /* Normal Kernel APC */
524 if (Thread
->ApcState
.KernelApcInProgress
|| Thread
->KernelApcDisable
) {
527 * DeliveryMode must be KernelMode in this case, since one may not
528 * return to umode while being inside a critical section or while
529 * a regular kmode apc is running (the latter should be impossible btw).
532 ASSERT(DeliveryMode
== KernelMode
);
534 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
538 /* Dequeue the APC */
539 RemoveEntryList(ApcListEntry
);
540 Apc
->Inserted
= FALSE
;
542 /* Go back to APC_LEVEL */
543 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
545 /* Call the Kernel APC */
546 DPRINT("Delivering a Normal APC: %x\n", Apc
);
553 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
554 if (NormalRoutine
!= NULL
) {
556 /* At Passive Level, this APC can be prempted by a Special APC */
557 Thread
->ApcState
.KernelApcInProgress
= TRUE
;
558 KeLowerIrql(PASSIVE_LEVEL
);
560 /* Call and Raise IRQ back to APC_LEVEL */
561 DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc
);
562 NormalRoutine(&NormalContext
, &SystemArgument1
, &SystemArgument2
);
563 KeRaiseIrql(APC_LEVEL
, &OldIrql
);
566 /* Raise IRQL and Lock again */
567 KeAcquireSpinLock(&Thread
->ApcQueueLock
, &OldIrql
);
568 Thread
->ApcState
.KernelApcInProgress
= FALSE
;
572 /* Now we do the User APCs */
573 if ((!IsListEmpty(&Thread
->ApcState
.ApcListHead
[UserMode
])) &&
574 (DeliveryMode
== UserMode
) && (Thread
->ApcState
.UserApcPending
== TRUE
)) {
576 /* It's not pending anymore */
577 Thread
->ApcState
.UserApcPending
= FALSE
;
579 /* Get the APC Object */
580 ApcListEntry
= Thread
->ApcState
.ApcListHead
[UserMode
].Flink
;
581 Apc
= CONTAINING_RECORD(ApcListEntry
, KAPC
, ApcListEntry
);
583 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
584 NormalRoutine
= Apc
->NormalRoutine
;
585 KernelRoutine
= Apc
->KernelRoutine
;
586 NormalContext
= Apc
->NormalContext
;
587 SystemArgument1
= Apc
->SystemArgument1
;
588 SystemArgument2
= Apc
->SystemArgument2
;
590 /* Remove the APC from Queue, restore IRQL and call the APC */
591 RemoveEntryList(ApcListEntry
);
592 Apc
->Inserted
= FALSE
;
594 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
595 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc
);
602 if (NormalRoutine
== NULL
) {
604 /* Check if more User APCs are Pending */
605 KeTestAlertThread(UserMode
);
609 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
610 DPRINT("Delivering a User APC: %x\n", Apc
);
611 KiInitializeUserApc(Reserved
,
621 /* Go back to APC_LEVEL */
622 KeReleaseSpinLock(&Thread
->ApcQueueLock
, OldIrql
);
628 KiFreeApcRoutine(PKAPC Apc
,
629 PKNORMAL_ROUTINE
* NormalRoutine
,
630 PVOID
* NormalContext
,
631 PVOID
* SystemArgument1
,
632 PVOID
* SystemArgument2
)
634 /* Free the APC and do nothing else */
639 * KiInitializeUserApc
641 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
644 * Reserved - Pointer to the Exception Frame on non-i386 builds.
646 * TrapFrame - Pointer to the Trap Frame.
648 * NormalRoutine - Pointer to the NormalRoutine to call.
650 * NormalContext - Pointer to the context to send to the Normal Routine.
652 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
664 KiInitializeUserApc(IN PVOID Reserved
,
665 IN PKTRAP_FRAME TrapFrame
,
666 IN PKNORMAL_ROUTINE NormalRoutine
,
667 IN PVOID NormalContext
,
668 IN PVOID SystemArgument1
,
669 IN PVOID SystemArgument2
)
674 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame
, KeGetCurrentThread()->TrapFrame
);
677 * Save the thread's current context (in other words the registers
678 * that will be restored when it returns to user mode) so the
679 * APC dispatcher can restore them later
681 Context
= (PCONTEXT
)(((PUCHAR
)TrapFrame
->Esp
) - sizeof(CONTEXT
));
682 RtlZeroMemory(Context
, sizeof(CONTEXT
));
683 Context
->ContextFlags
= CONTEXT_FULL
;
684 Context
->SegGs
= TrapFrame
->Gs
;
685 Context
->SegFs
= TrapFrame
->Fs
;
686 Context
->SegEs
= TrapFrame
->Es
;
687 Context
->SegDs
= TrapFrame
->Ds
;
688 Context
->Edi
= TrapFrame
->Edi
;
689 Context
->Esi
= TrapFrame
->Esi
;
690 Context
->Ebx
= TrapFrame
->Ebx
;
691 Context
->Edx
= TrapFrame
->Edx
;
692 Context
->Ecx
= TrapFrame
->Ecx
;
693 Context
->Eax
= TrapFrame
->Eax
;
694 Context
->Ebp
= TrapFrame
->Ebp
;
695 Context
->Eip
= TrapFrame
->Eip
;
696 Context
->SegCs
= TrapFrame
->Cs
;
697 Context
->EFlags
= TrapFrame
->Eflags
;
698 Context
->Esp
= TrapFrame
->Esp
;
699 Context
->SegSs
= TrapFrame
->Ss
;
702 * Setup the trap frame so the thread will start executing at the
703 * APC Dispatcher when it returns to user-mode
705 Esp
= (PULONG
)(((PUCHAR
)TrapFrame
->Esp
) - (sizeof(CONTEXT
) + (6 * sizeof(ULONG
))));
707 Esp
[1] = (ULONG
)NormalRoutine
;
708 Esp
[2] = (ULONG
)NormalContext
;
709 Esp
[3] = (ULONG
)SystemArgument1
;
710 Esp
[4] = (ULONG
)SystemArgument2
;
711 Esp
[5] = (ULONG
)Context
;
712 TrapFrame
->Eip
= (ULONG
)LdrpGetSystemDllApcDispatcher();
713 TrapFrame
->Esp
= (ULONG
)Esp
;
720 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
726 * KeAreApcsDisabled returns TRUE if the thread is within a critical region
727 * or a guarded region, and FALSE otherwise.
730 * A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled to
731 * determine if normal kernel APCs are disabled. A thread that is inside a
732 * critical region has both user APCs and normal kernel APCs disabled, but
733 * not special kernel APCs. A thread that is inside a guarded region has
734 * all APCs disabled, including special kernel APCs.
736 * Callers of this routine must be running at IRQL <= APC_LEVEL.
741 KeAreApcsDisabled(VOID
)
743 /* Return the Kernel APC State */
744 return KeGetCurrentThread()->KernelApcDisable
? TRUE
: FALSE
;
751 * This routine is used to queue an APC from user-mode for the specified
755 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
757 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
759 * NormalContext - Pointer to the context to send to the Normal Routine.
761 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
765 * STATUS_SUCCESS or failure cute from associated calls.
768 * The thread must enter an alertable wait before the APC will be
774 NtQueueApcThread(HANDLE ThreadHandle
,
775 PKNORMAL_ROUTINE ApcRoutine
,
777 PVOID SystemArgument1
,
778 PVOID SystemArgument2
)
782 KPROCESSOR_MODE PreviousMode
= ExGetPreviousMode();
785 /* Get ETHREAD from Handle */
786 Status
= ObReferenceObjectByHandle(ThreadHandle
,
793 /* Fail if the Handle is invalid for some reason */
794 if (!NT_SUCCESS(Status
)) {
799 /* If this is a Kernel or System Thread, then fail */
800 if (Thread
->Tcb
.Teb
== NULL
) {
802 ObDereferenceObject(Thread
);
803 return STATUS_INVALID_HANDLE
;
806 /* Allocate an APC */
807 Apc
= ExAllocatePoolWithTag(NonPagedPool
, sizeof(KAPC
), TAG('P', 's', 'a', 'p'));
810 ObDereferenceObject(Thread
);
811 return(STATUS_NO_MEMORY
);
814 /* Initialize and Queue a user mode apc (always!) */
817 OriginalApcEnvironment
,
824 if (!KeInsertQueueApc(Apc
, SystemArgument1
, SystemArgument2
, IO_NO_INCREMENT
)) {
826 Status
= STATUS_UNSUCCESSFUL
;
830 Status
= STATUS_SUCCESS
;
833 /* Dereference Thread and Return */
834 ObDereferenceObject(Thread
);
839 VOID
RepairList(PLIST_ENTRY Original
,
841 KPROCESSOR_MODE Mode
)
843 /* Copy Source to Desination */
844 if (IsListEmpty(&Original
[(int)Mode
])) {
846 InitializeListHead(&Copy
[(int)Mode
]);
850 Copy
[(int)Mode
].Flink
= Original
[(int)Mode
].Flink
;
851 Copy
[(int)Mode
].Blink
= Original
[(int)Mode
].Blink
;
852 Original
[(int)Mode
].Flink
->Blink
= &Copy
[(int)Mode
];
853 Original
[(int)Mode
].Blink
->Flink
= &Copy
[(int)Mode
];
859 KiMoveApcState(PKAPC_STATE OldState
,
860 PKAPC_STATE NewState
)
862 /* Restore backup of Original Environment */
863 *NewState
= *OldState
;
866 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, KernelMode
);
867 RepairList(NewState
->ApcListHead
, OldState
->ApcListHead
, UserMode
);