KD System Rewrite:
[reactos.git] / reactos / ntoskrnl / ke / apc.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/apc.c
5 * PURPOSE: NT Implementation of APCs
6 *
7 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
8 * Phillip Susi
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <internal/debug.h>
16
17 /* FUNCTIONS *****************************************************************/
18
19 /*++
20 * KiKernelApcDeliveryCheck
21 * @implemented NT 5.2
22 *
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.
26 *
27 * Params:
28 * None.
29 *
30 * Returns:
31 * None.
32 *
33 * Remarks:
34 * This routine allows KeLeave/EnterCritical/GuardedRegion to be used as a
35 * macro from inside WIN32K or other Drivers, which will then only have to
36 * do an Import API call in the case where APCs are enabled again.
37 *
38 *--*/
39 VOID
40 STDCALL
41 KiKernelApcDeliveryCheck(VOID)
42 {
43 /* We should only deliver at passive */
44 if (KeGetCurrentIrql() == PASSIVE_LEVEL)
45 {
46 /* Raise to APC and Deliver APCs, then lower back to Passive */
47 KfRaiseIrql(APC_LEVEL);
48 KiDeliverApc(KernelMode, 0, 0);
49 KfLowerIrql(PASSIVE_LEVEL);
50 }
51 else
52 {
53 /*
54 * If we're not at passive level it means someone raised IRQL
55 * to APC level before the a critical or guarded section was entered
56 * (e.g) by a fast mutex). This implies that the APCs shouldn't
57 * be delivered now, but after the IRQL is lowered to passive
58 * level again.
59 */
60 HalRequestSoftwareInterrupt(APC_LEVEL);
61 }
62 }
63
64 /*++
65 * KeEnterCriticalRegion
66 * @implemented NT4
67 *
68 * The KeEnterCriticalRegion routine temporarily disables the delivery of
69 * normal kernel APCs; special kernel-mode APCs are still delivered.
70 *
71 * Params:
72 * None.
73 *
74 * Returns:
75 * None.
76 *
77 * Remarks:
78 * Highest-level drivers can call this routine while running in the context
79 * of the thread that requested the current I/O operation. Any caller of
80 * this routine should call KeLeaveCriticalRegion as quickly as possible.
81 *
82 * Callers of KeEnterCriticalRegion must be running at IRQL <= APC_LEVEL.
83 *
84 *--*/
85 #undef KeEnterCriticalRegion
86 VOID
87 STDCALL
88 KeEnterCriticalRegion(VOID)
89 {
90 /* Disable Kernel APCs */
91 PKTHREAD Thread = KeGetCurrentThread();
92 if (Thread) Thread->KernelApcDisable--;
93 }
94
95 /*++
96 * KeLeaveCriticalRegion
97 * @implemented NT4
98 *
99 * The KeLeaveCriticalRegion routine reenables the delivery of normal
100 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
101 *
102 * Params:
103 * None.
104 *
105 * Returns:
106 * None.
107 *
108 * Remarks:
109 * Highest-level drivers can call this routine while running in the context
110 * of the thread that requested the current I/O operation.
111 *
112 * Callers of KeLeaveCriticalRegion must be running at IRQL <= DISPATCH_LEVEL.
113 *
114 *--*/
115 #undef KeLeaveCriticalRegion
116 VOID
117 STDCALL
118 KeLeaveCriticalRegion (VOID)
119 {
120 PKTHREAD Thread = KeGetCurrentThread();
121
122 /* Check if Kernel APCs are now enabled */
123 if((Thread) && (++Thread->KernelApcDisable == 0))
124 {
125 /* Check if we need to request an APC Delivery */
126 if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode]))
127 {
128 /* Check for the right environment */
129 KiKernelApcDeliveryCheck();
130 }
131 }
132 }
133
134 /*++
135 * KeInitializeApc
136 * @implemented NT4
137 *
138 * The The KeInitializeApc routine initializes an APC object, and registers
139 * the Kernel, Rundown and Normal routines for that object.
140 *
141 * Params:
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.
145 *
146 * Thread - Thread to which to deliver the APC.
147 *
148 * TargetEnvironment - APC Environment to be used.
149 *
150 * KernelRoutine - Points to the KernelRoutine to associate with the APC.
151 * This routine is executed for all APCs.
152 *
153 * RundownRoutine - Points to the RundownRoutine to associate with the APC.
154 * This routine is executed when the Thread exists with
155 * the APC executing.
156 *
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.
161 *
162 * Mode - Specifies the processor mode at which to run the Normal Routine.
163 *
164 * Context - Specifices the value to pass as Context parameter to the
165 * registered routines.
166 *
167 * Returns:
168 * None.
169 *
170 * Remarks:
171 * The caller can queue an initialized APC with KeInsertQueueApc.
172 *
173 * Storage for the APC object must be resident, such as nonpaged pool
174 * allocated by the caller.
175 *
176 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
177 *
178 *--*/
179 VOID
180 STDCALL
181 KeInitializeApc(IN PKAPC Apc,
182 IN PKTHREAD Thread,
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,
188 IN PVOID Context)
189 {
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);
194
195 /* Set up the basic APC Structure Data */
196 RtlZeroMemory(Apc, sizeof(KAPC));
197 Apc->Type = ApcObject;
198 Apc->Size = sizeof(KAPC);
199
200 /* Set the Environment */
201 if (TargetEnvironment == CurrentApcEnvironment) {
202
203 Apc->ApcStateIndex = Thread->ApcStateIndex;
204
205 } else {
206
207 Apc->ApcStateIndex = TargetEnvironment;
208 }
209
210 /* Set the Thread and Routines */
211 Apc->Thread = Thread;
212 Apc->KernelRoutine = KernelRoutine;
213 Apc->RundownRoutine = RundownRoutine;
214 Apc->NormalRoutine = NormalRoutine;
215
216 /* Check if this is a Special APC, in which case we use KernelMode and no Context */
217 if (ARGUMENT_PRESENT(NormalRoutine)) {
218
219 Apc->ApcMode = Mode;
220 Apc->NormalContext = Context;
221
222 } else {
223
224 Apc->ApcMode = KernelMode;
225 }
226 }
227
228 /*++
229 * KiInsertQueueApc
230 *
231 * The KiInsertQueueApc routine queues a APC for execution when the right
232 * scheduler environment exists.
233 *
234 * Params:
235 * Apc - Pointer to an initialized control object of type DPC for which the
236 * caller provides the storage.
237 *
238 * PriorityBoost - Priority Boost to apply to the Thread.
239 *
240 * Returns:
241 * If the APC is already inserted or APC queueing is disabled, FALSE.
242 * Otherwise, TRUE.
243 *
244 * Remarks:
245 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
246 * at PASSIVE_LEVEL for the NormalRoutine registered.
247 *
248 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
249 *
250 *--*/
251 BOOLEAN
252 STDCALL
253 KiInsertQueueApc(PKAPC Apc,
254 KPRIORITY PriorityBoost)
255 {
256 PKTHREAD Thread = Apc->Thread;
257 PLIST_ENTRY ApcListEntry;
258 PKAPC QueuedApc;
259
260 /* Don't do anything if the APC is already inserted */
261 if (Apc->Inserted) {
262
263 return FALSE;
264 }
265
266 /* Three scenarios:
267 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
268 2) User APC which is PsExitSpecialApc = Put it at the front of the List
269 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
270 */
271 if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) {
272
273 DPRINT ("Inserting the Process Exit APC into the Queue\n");
274 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = TRUE;
275 InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
276 &Apc->ApcListEntry);
277
278 } else if (Apc->NormalRoutine == NULL) {
279
280 DPRINT ("Inserting Special APC %x into the Queue\n", Apc);
281
282 for (ApcListEntry = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode].Flink;
283 ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode];
284 ApcListEntry = ApcListEntry->Flink) {
285
286 QueuedApc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
287 if (Apc->NormalRoutine != NULL) break;
288 }
289
290 /* We found the first "Normal" APC, so write right before it */
291 ApcListEntry = ApcListEntry->Blink;
292 InsertHeadList(ApcListEntry, &Apc->ApcListEntry);
293
294 } else {
295
296 DPRINT ("Inserting Normal APC %x into the %x Queue\n", Apc, Apc->ApcMode);
297 InsertTailList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
298 &Apc->ApcListEntry);
299 }
300
301 /* Confirm Insertion */
302 Apc->Inserted = TRUE;
303
304 /*
305 * Three possibilites here again:
306 * 1) Kernel APC, The thread is Running: Request an Interrupt
307 * 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread
308 * 3) User APC, Unwait the Thread if it is alertable
309 */
310 if (Apc->ApcMode == KernelMode) {
311
312 /* Set Kernel APC pending */
313 Thread->ApcState.KernelApcPending = TRUE;
314
315 /* Check the Thread State */
316 if (Thread->State == Running) {
317
318 /* FIXME: Use IPI */
319 DPRINT ("Requesting APC Interrupt for Running Thread \n");
320 HalRequestSoftwareInterrupt(APC_LEVEL);
321
322 } else if ((Thread->State == Waiting) && (Thread->WaitIrql == PASSIVE_LEVEL) &&
323 ((Apc->NormalRoutine == NULL) ||
324 ((!Thread->KernelApcDisable) && (!Thread->ApcState.KernelApcInProgress)))) {
325
326 DPRINT("Waking up Thread for Kernel-Mode APC Delivery \n");
327 KiAbortWaitThread(Thread, STATUS_KERNEL_APC, PriorityBoost);
328 }
329
330 } else if ((Thread->State == Waiting) &&
331 (Thread->WaitMode == UserMode) &&
332 (Thread->Alertable)) {
333
334 DPRINT("Waking up Thread for User-Mode APC Delivery \n");
335 Thread->ApcState.UserApcPending = TRUE;
336 KiAbortWaitThread(Thread, STATUS_USER_APC, PriorityBoost);
337 }
338
339 return TRUE;
340 }
341
342 /*++
343 * KeInsertQueueApc
344 * @implemented NT4
345 *
346 * The KeInsertQueueApc routine queues a APC for execution when the right
347 * scheduler environment exists.
348 *
349 * Params:
350 * Apc - Pointer to an initialized control object of type DPC for which the
351 * caller provides the storage.
352 *
353 * SystemArgument[1,2] - Pointer to a set of two parameters that contain
354 * untyped data.
355 *
356 * PriorityBoost - Priority Boost to apply to the Thread.
357 *
358 * Returns:
359 * If the APC is already inserted or APC queueing is disabled, FALSE.
360 * Otherwise, TRUE.
361 *
362 * Remarks:
363 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
364 * at PASSIVE_LEVEL for the NormalRoutine registered.
365 *
366 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
367 *
368 *--*/
369 BOOLEAN
370 STDCALL
371 KeInsertQueueApc(PKAPC Apc,
372 PVOID SystemArgument1,
373 PVOID SystemArgument2,
374 KPRIORITY PriorityBoost)
375
376 {
377 KIRQL OldIrql;
378 PKTHREAD Thread;
379 BOOLEAN Inserted;
380
381 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
382 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
383 "SystemArgument2 %x)\n",Apc,SystemArgument1,
384 SystemArgument2);
385
386 /* Lock the Dispatcher Database */
387 OldIrql = KeAcquireDispatcherDatabaseLock();
388
389 /* Get the Thread specified in the APC */
390 Thread = Apc->Thread;
391
392 /* Make sure the thread allows APC Queues.
393 * The thread is not apc queueable, for instance, when it's (about to be) terminated.
394 */
395 if (Thread->ApcQueueable == FALSE) {
396 DPRINT("Thread doesn't allow APC Queues\n");
397 KeReleaseDispatcherDatabaseLock(OldIrql);
398 return FALSE;
399 }
400
401 /* Set the System Arguments */
402 Apc->SystemArgument1 = SystemArgument1;
403 Apc->SystemArgument2 = SystemArgument2;
404
405 /* Call the Internal Function */
406 Inserted = KiInsertQueueApc(Apc, PriorityBoost);
407
408 /* Return Sucess if we are here */
409 KeReleaseDispatcherDatabaseLock(OldIrql);
410 return Inserted;
411 }
412
413 /*++
414 * KeRemoveQueueApc
415 *
416 * The KeRemoveQueueApc routine removes a given APC object from the system
417 * APC queue.
418 *
419 * Params:
420 * APC - Pointer to an initialized APC object that was queued by calling
421 * KeInsertQueueApc.
422 *
423 * Returns:
424 * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is
425 * performed and FALSE is returned.
426 *
427 * Remarks:
428 * If the given APC Object is currently queued, it is removed from the queue
429 * and any calls to the registered routines are cancelled.
430 *
431 * Callers of KeLeaveCriticalRegion can be running at any IRQL.
432 *
433 *--*/
434 BOOLEAN
435 STDCALL
436 KeRemoveQueueApc(PKAPC Apc)
437 {
438 KIRQL OldIrql;
439 PKTHREAD Thread = Apc->Thread;
440
441 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
442 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc);
443
444 OldIrql = KeAcquireDispatcherDatabaseLock();
445 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
446
447 /* Check if it's inserted */
448 if (Apc->Inserted) {
449
450 /* Remove it from the Queue*/
451 RemoveEntryList(&Apc->ApcListEntry);
452 Apc->Inserted = FALSE;
453
454 /* If the Queue is completely empty, then no more APCs are pending */
455 if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) {
456
457 /* Set the correct State based on the Apc Mode */
458 if (Apc->ApcMode == KernelMode) {
459
460 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE;
461
462 } else {
463
464 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE;
465 }
466 }
467
468 } else {
469
470 /* It's not inserted, fail */
471 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
472 KeReleaseDispatcherDatabaseLock(OldIrql);
473 return(FALSE);
474 }
475
476 /* Restore IRQL and Return */
477 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
478 KeReleaseDispatcherDatabaseLock(OldIrql);
479 return(TRUE);
480 }
481
482 /*++
483 * KiDeliverApc
484 * @implemented @NT4
485 *
486 * The KiDeliverApc routine is called from IRQL switching code if the
487 * thread is returning from an IRQL >= APC_LEVEL and Kernel-Mode APCs are
488 * pending.
489 *
490 * Params:
491 * DeliveryMode - Specifies the current processor mode.
492 *
493 * Reserved - Pointer to the Exception Frame on non-i386 builds.
494 *
495 * TrapFrame - Pointer to the Trap Frame.
496 *
497 * Returns:
498 * None.
499 *
500 * Remarks:
501 * First, Special APCs are delivered, followed by Kernel-Mode APCs and
502 * User-Mode APCs. Note that the TrapFrame is only valid if the previous
503 * mode is User.
504 *
505 * Upon entry, this routine executes at APC_LEVEL.
506 *
507 *--*/
508 VOID
509 STDCALL
510 KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
511 PVOID Reserved,
512 PKTRAP_FRAME TrapFrame)
513 {
514 PKTHREAD Thread = KeGetCurrentThread();
515 PLIST_ENTRY ApcListEntry;
516 PKAPC Apc;
517 KIRQL OldIrql;
518 PKKERNEL_ROUTINE KernelRoutine;
519 PVOID NormalContext;
520 PKNORMAL_ROUTINE NormalRoutine;
521 PVOID SystemArgument1;
522 PVOID SystemArgument2;
523
524 ASSERT_IRQL_EQUAL(APC_LEVEL);
525
526 /* Lock the APC Queue and Raise IRQL to Synch */
527 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
528
529 /* Clear APC Pending */
530 Thread->ApcState.KernelApcPending = FALSE;
531
532 /* Do the Kernel APCs first */
533 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) {
534
535 /* Get the next Entry */
536 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
537 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
538
539 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
540 NormalRoutine = Apc->NormalRoutine;
541 KernelRoutine = Apc->KernelRoutine;
542 NormalContext = Apc->NormalContext;
543 SystemArgument1 = Apc->SystemArgument1;
544 SystemArgument2 = Apc->SystemArgument2;
545
546 /* Special APC */
547 if (NormalRoutine == NULL) {
548
549 /* Remove the APC from the list */
550 Apc->Inserted = FALSE;
551 RemoveEntryList(ApcListEntry);
552
553 /* Go back to APC_LEVEL */
554 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
555
556 /* Call the Special APC */
557 DPRINT("Delivering a Special APC: %x\n", Apc);
558 KernelRoutine(Apc,
559 &NormalRoutine,
560 &NormalContext,
561 &SystemArgument1,
562 &SystemArgument2);
563
564 /* Raise IRQL and Lock again */
565 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
566
567 } else {
568
569 /* Normal Kernel APC */
570 if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) {
571
572 /*
573 * DeliveryMode must be KernelMode in this case, since one may not
574 * return to umode while being inside a critical section or while
575 * a regular kmode apc is running (the latter should be impossible btw).
576 * -Gunnar
577 */
578 ASSERT(DeliveryMode == KernelMode);
579
580 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
581 return;
582 }
583
584 /* Dequeue the APC */
585 RemoveEntryList(ApcListEntry);
586 Apc->Inserted = FALSE;
587
588 /* Go back to APC_LEVEL */
589 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
590
591 /* Call the Kernel APC */
592 DPRINT("Delivering a Normal APC: %x\n", Apc);
593 KernelRoutine(Apc,
594 &NormalRoutine,
595 &NormalContext,
596 &SystemArgument1,
597 &SystemArgument2);
598
599 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
600 if (NormalRoutine != NULL) {
601
602 /* At Passive Level, this APC can be prempted by a Special APC */
603 Thread->ApcState.KernelApcInProgress = TRUE;
604 KeLowerIrql(PASSIVE_LEVEL);
605
606 /* Call and Raise IRQ back to APC_LEVEL */
607 DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc);
608 NormalRoutine(&NormalContext, &SystemArgument1, &SystemArgument2);
609 KeRaiseIrql(APC_LEVEL, &OldIrql);
610 }
611
612 /* Raise IRQL and Lock again */
613 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
614 Thread->ApcState.KernelApcInProgress = FALSE;
615 }
616 }
617
618 /* Now we do the User APCs */
619 if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
620 (DeliveryMode == UserMode) && (Thread->ApcState.UserApcPending == TRUE)) {
621
622 /* It's not pending anymore */
623 Thread->ApcState.UserApcPending = FALSE;
624
625 /* Get the APC Object */
626 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
627 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
628
629 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
630 NormalRoutine = Apc->NormalRoutine;
631 KernelRoutine = Apc->KernelRoutine;
632 NormalContext = Apc->NormalContext;
633 SystemArgument1 = Apc->SystemArgument1;
634 SystemArgument2 = Apc->SystemArgument2;
635
636 /* Remove the APC from Queue, restore IRQL and call the APC */
637 RemoveEntryList(ApcListEntry);
638 Apc->Inserted = FALSE;
639
640 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
641 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc);
642 KernelRoutine(Apc,
643 &NormalRoutine,
644 &NormalContext,
645 &SystemArgument1,
646 &SystemArgument2);
647
648 if (NormalRoutine == NULL) {
649
650 /* Check if more User APCs are Pending */
651 KeTestAlertThread(UserMode);
652
653 } else {
654
655 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
656 DPRINT("Delivering a User APC: %x\n", Apc);
657 KiInitializeUserApc(Reserved,
658 TrapFrame,
659 NormalRoutine,
660 NormalContext,
661 SystemArgument1,
662 SystemArgument2);
663 }
664
665 } else {
666
667 /* Go back to APC_LEVEL */
668 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
669 }
670 }
671
672 VOID
673 STDCALL
674 KiFreeApcRoutine(PKAPC Apc,
675 PKNORMAL_ROUTINE* NormalRoutine,
676 PVOID* NormalContext,
677 PVOID* SystemArgument1,
678 PVOID* SystemArgument2)
679 {
680 /* Free the APC and do nothing else */
681 ExFreePool(Apc);
682 }
683
684 /*++
685 * KiInitializeUserApc
686 *
687 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
688 *
689 * Params:
690 * Reserved - Pointer to the Exception Frame on non-i386 builds.
691 *
692 * TrapFrame - Pointer to the Trap Frame.
693 *
694 * NormalRoutine - Pointer to the NormalRoutine to call.
695 *
696 * NormalContext - Pointer to the context to send to the Normal Routine.
697 *
698 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
699 * untyped data.
700 *
701 * Returns:
702 * None.
703 *
704 * Remarks:
705 * None.
706 *
707 *--*/
708 VOID
709 STDCALL
710 KiInitializeUserApc(IN PVOID Reserved,
711 IN PKTRAP_FRAME TrapFrame,
712 IN PKNORMAL_ROUTINE NormalRoutine,
713 IN PVOID NormalContext,
714 IN PVOID SystemArgument1,
715 IN PVOID SystemArgument2)
716 {
717 PCONTEXT Context;
718 PULONG Esp;
719
720 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame, KeGetCurrentThread()->TrapFrame);
721
722 /*
723 * Save the thread's current context (in other words the registers
724 * that will be restored when it returns to user mode) so the
725 * APC dispatcher can restore them later
726 */
727 Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT));
728 RtlZeroMemory(Context, sizeof(CONTEXT));
729 Context->ContextFlags = CONTEXT_FULL;
730 Context->SegGs = TrapFrame->Gs;
731 Context->SegFs = TrapFrame->Fs;
732 Context->SegEs = TrapFrame->Es;
733 Context->SegDs = TrapFrame->Ds;
734 Context->Edi = TrapFrame->Edi;
735 Context->Esi = TrapFrame->Esi;
736 Context->Ebx = TrapFrame->Ebx;
737 Context->Edx = TrapFrame->Edx;
738 Context->Ecx = TrapFrame->Ecx;
739 Context->Eax = TrapFrame->Eax;
740 Context->Ebp = TrapFrame->Ebp;
741 Context->Eip = TrapFrame->Eip;
742 Context->SegCs = TrapFrame->Cs;
743 Context->EFlags = TrapFrame->Eflags;
744 Context->Esp = TrapFrame->Esp;
745 Context->SegSs = TrapFrame->Ss;
746
747 /*
748 * Setup the trap frame so the thread will start executing at the
749 * APC Dispatcher when it returns to user-mode
750 */
751 Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) - (sizeof(CONTEXT) + (6 * sizeof(ULONG))));
752 Esp[0] = 0xdeadbeef;
753 Esp[1] = (ULONG)NormalRoutine;
754 Esp[2] = (ULONG)NormalContext;
755 Esp[3] = (ULONG)SystemArgument1;
756 Esp[4] = (ULONG)SystemArgument2;
757 Esp[5] = (ULONG)Context;
758 TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher();
759 DPRINT("TrapFrame->Eip: %x\n", TrapFrame->Eip);
760 TrapFrame->Esp = (ULONG)Esp;
761 }
762
763 /*++
764 * KeAreApcsDisabled
765 * @implemented NT4
766 *
767 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
768 *
769 * Params:
770 * None.
771 *
772 * Returns:
773 * KeAreApcsDisabled returns TRUE if the thread is within a critical region
774 * or a guarded region, and FALSE otherwise.
775 *
776 * Remarks:
777 * A thread running at IRQL = PASSIVE_LEVEL can use KeAreApcsDisabled to
778 * determine if normal kernel APCs are disabled. A thread that is inside a
779 * critical region has both user APCs and normal kernel APCs disabled, but
780 * not special kernel APCs. A thread that is inside a guarded region has
781 * all APCs disabled, including special kernel APCs.
782 *
783 * Callers of this routine must be running at IRQL <= APC_LEVEL.
784 *
785 *--*/
786 BOOLEAN
787 STDCALL
788 KeAreApcsDisabled(VOID)
789 {
790 /* Return the Kernel APC State */
791 return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE;
792 }
793
794 /*++
795 * NtQueueApcThread
796 * NT4
797 *
798 * This routine is used to queue an APC from user-mode for the specified
799 * thread.
800 *
801 * Params:
802 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
803 *
804 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
805 *
806 * NormalContext - Pointer to the context to send to the Normal Routine.
807 *
808 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
809 * untyped data.
810 *
811 * Returns:
812 * STATUS_SUCCESS or failure cute from associated calls.
813 *
814 * Remarks:
815 * The thread must enter an alertable wait before the APC will be
816 * delivered.
817 *
818 *--*/
819 NTSTATUS
820 STDCALL
821 NtQueueApcThread(HANDLE ThreadHandle,
822 PKNORMAL_ROUTINE ApcRoutine,
823 PVOID NormalContext,
824 PVOID SystemArgument1,
825 PVOID SystemArgument2)
826 {
827 PKAPC Apc;
828 PETHREAD Thread;
829 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
830 NTSTATUS Status;
831
832 /* Get ETHREAD from Handle */
833 Status = ObReferenceObjectByHandle(ThreadHandle,
834 THREAD_SET_CONTEXT,
835 PsThreadType,
836 PreviousMode,
837 (PVOID)&Thread,
838 NULL);
839
840 /* Fail if the Handle is invalid for some reason */
841 if (!NT_SUCCESS(Status)) {
842
843 return(Status);
844 }
845
846 /* If this is a Kernel or System Thread, then fail */
847 if (Thread->Tcb.Teb == NULL) {
848
849 ObDereferenceObject(Thread);
850 return STATUS_INVALID_HANDLE;
851 }
852
853 /* Allocate an APC */
854 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('P', 's', 'a', 'p'));
855 if (Apc == NULL) {
856
857 ObDereferenceObject(Thread);
858 return(STATUS_NO_MEMORY);
859 }
860
861 /* Initialize and Queue a user mode apc (always!) */
862 KeInitializeApc(Apc,
863 &Thread->Tcb,
864 OriginalApcEnvironment,
865 KiFreeApcRoutine,
866 NULL,
867 ApcRoutine,
868 UserMode,
869 NormalContext);
870
871 if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) {
872
873 Status = STATUS_UNSUCCESSFUL;
874
875 } else {
876
877 Status = STATUS_SUCCESS;
878 }
879
880 /* Dereference Thread and Return */
881 ObDereferenceObject(Thread);
882 return Status;
883 }
884
885 static inline
886 VOID RepairList(PLIST_ENTRY Original,
887 PLIST_ENTRY Copy,
888 KPROCESSOR_MODE Mode)
889 {
890 /* Copy Source to Desination */
891 if (IsListEmpty(&Original[(int)Mode])) {
892
893 InitializeListHead(&Copy[(int)Mode]);
894
895 } else {
896
897 Copy[(int)Mode].Flink = Original[(int)Mode].Flink;
898 Copy[(int)Mode].Blink = Original[(int)Mode].Blink;
899 Original[(int)Mode].Flink->Blink = &Copy[(int)Mode];
900 Original[(int)Mode].Blink->Flink = &Copy[(int)Mode];
901 }
902 }
903
904 VOID
905 STDCALL
906 KiMoveApcState(PKAPC_STATE OldState,
907 PKAPC_STATE NewState)
908 {
909 /* Restore backup of Original Environment */
910 *NewState = *OldState;
911
912 /* Repair Lists */
913 RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode);
914 RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode);
915 }
916