Some work-in-progress improvements and rethought of system-level code...notable impro...
[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 have locked the dipatcher database.
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 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
261
262 /* Don't do anything if the APC is already inserted */
263 if (Apc->Inserted) {
264
265 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
266 return FALSE;
267 }
268
269 /* Three scenarios:
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
273 */
274 if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) {
275
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],
279 &Apc->ApcListEntry);
280
281 } else if (Apc->NormalRoutine == NULL) {
282
283 DPRINT("Inserting Special APC %x for '%.16s' into the Queue\n", Apc, ((PETHREAD)Thread)->ThreadsProcess->ImageFileName);
284
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) {
288
289 QueuedApc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
290 if (Apc->NormalRoutine != NULL) break;
291 }
292
293 /* We found the first "Normal" APC, so write right before it */
294 ApcListEntry = ApcListEntry->Blink;
295 InsertHeadList(ApcListEntry, &Apc->ApcListEntry);
296
297 } else {
298
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],
301 &Apc->ApcListEntry);
302 }
303
304 /* Confirm Insertion */
305 Apc->Inserted = TRUE;
306
307 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
308
309 /*
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
314 */
315 if (Apc->ApcMode == KernelMode) {
316
317 /* Set Kernel APC pending */
318 Thread->ApcState.KernelApcPending = TRUE;
319
320 /* Check the Thread State */
321 if (Thread->State == Running) {
322
323 #ifdef CONFIG_SMP
324 PKPRCB Prcb, CurrentPrcb;
325 LONG i;
326 #endif
327
328 DPRINT ("Requesting APC Interrupt for Running Thread \n");
329
330 #ifdef CONFIG_SMP
331 CurrentPrcb = KeGetCurrentPrcb();
332 if (CurrentPrcb->CurrentThread == Thread)
333 {
334 HalRequestSoftwareInterrupt(APC_LEVEL);
335 }
336 else
337 {
338 for (i = 0; i < KeNumberProcessors; i++)
339 {
340 Prcb = ((PKPCR)(KPCR_BASE + i * PAGE_SIZE))->Prcb;
341 if (Prcb->CurrentThread == Thread)
342 {
343 ASSERT (CurrentPrcb != Prcb);
344 KiIpiSendRequest(Prcb->SetMember, IPI_REQUEST_APC);
345 break;
346 }
347 }
348 ASSERT (i < KeNumberProcessors);
349 }
350 #else
351 HalRequestSoftwareInterrupt(APC_LEVEL);
352 #endif
353
354 } else if ((Thread->State == Waiting) && (Thread->WaitIrql == PASSIVE_LEVEL) &&
355 ((Apc->NormalRoutine == NULL) ||
356 ((!Thread->KernelApcDisable) && (!Thread->ApcState.KernelApcInProgress)))) {
357
358 DPRINT("Waking up Thread for Kernel-Mode APC Delivery \n");
359 KiAbortWaitThread(Thread, STATUS_KERNEL_APC, PriorityBoost);
360 }
361
362 } else if ((Thread->State == Waiting) &&
363 (Thread->WaitMode != KernelMode) &&
364 (Thread->Alertable)) {
365
366 DPRINT("Waking up Thread for User-Mode APC Delivery \n");
367 Thread->ApcState.UserApcPending = TRUE;
368 KiAbortWaitThread(Thread, STATUS_USER_APC, PriorityBoost);
369 }
370
371 return TRUE;
372 }
373
374 /*++
375 * KeInsertQueueApc
376 * @implemented NT4
377 *
378 * The KeInsertQueueApc routine queues a APC for execution when the right
379 * scheduler environment exists.
380 *
381 * Params:
382 * Apc - Pointer to an initialized control object of type DPC for which the
383 * caller provides the storage.
384 *
385 * SystemArgument[1,2] - Pointer to a set of two parameters that contain
386 * untyped data.
387 *
388 * PriorityBoost - Priority Boost to apply to the Thread.
389 *
390 * Returns:
391 * If the APC is already inserted or APC queueing is disabled, FALSE.
392 * Otherwise, TRUE.
393 *
394 * Remarks:
395 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
396 * at PASSIVE_LEVEL for the NormalRoutine registered.
397 *
398 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
399 *
400 *--*/
401 BOOLEAN
402 STDCALL
403 KeInsertQueueApc(PKAPC Apc,
404 PVOID SystemArgument1,
405 PVOID SystemArgument2,
406 KPRIORITY PriorityBoost)
407
408 {
409 KIRQL OldIrql;
410 PKTHREAD Thread;
411 BOOLEAN Inserted;
412
413 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
414 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
415 "SystemArgument2 %x)\n",Apc,SystemArgument1,
416 SystemArgument2);
417
418 /* Lock the Dispatcher Database */
419 OldIrql = KeAcquireDispatcherDatabaseLock();
420
421 /* Get the Thread specified in the APC */
422 Thread = Apc->Thread;
423
424 /* Make sure the thread allows APC Queues.
425 * The thread is not apc queueable, for instance, when it's (about to be) terminated.
426 */
427 if (Thread->ApcQueueable == FALSE) {
428 DPRINT("Thread doesn't allow APC Queues\n");
429 KeReleaseDispatcherDatabaseLock(OldIrql);
430 return FALSE;
431 }
432
433 /* Set the System Arguments */
434 Apc->SystemArgument1 = SystemArgument1;
435 Apc->SystemArgument2 = SystemArgument2;
436
437 /* Call the Internal Function */
438 Inserted = KiInsertQueueApc(Apc, PriorityBoost);
439
440 /* Return Sucess if we are here */
441 KeReleaseDispatcherDatabaseLock(OldIrql);
442 return Inserted;
443 }
444
445 /*++
446 * KeFlushQueueApc
447 *
448 * The KeFlushQueueApc routine flushes all APCs of the given processor mode
449 * from the specified Thread's APC queue.
450 *
451 * Params:
452 * Thread - Pointer to the thread whose APC queue will be flushed.
453 *
454 * PreviousMode - Specifies which APC Queue to flush.
455 *
456 * Returns:
457 * A pointer to the first entry in the flushed APC queue.
458 *
459 * Remarks:
460 * If the routine returns NULL, it means that no APCs were to be flushed.
461 *
462 * Callers of KeFlushQueueApc must be running at DISPATCH_LEVEL or lower.
463 *
464 *--*/
465 PLIST_ENTRY
466 STDCALL
467 KeFlushQueueApc(IN PKTHREAD Thread,
468 IN KPROCESSOR_MODE PreviousMode)
469 {
470 KIRQL OldIrql;
471 PKAPC Apc;
472 PLIST_ENTRY FirstEntry, CurrentEntry;
473
474 /* Lock the Dispatcher Database and APC Queue */
475 OldIrql = KeAcquireDispatcherDatabaseLock();
476 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
477
478 if (IsListEmpty(&Thread->ApcState.ApcListHead[PreviousMode])) {
479 FirstEntry = NULL;
480 } else {
481 FirstEntry = Thread->ApcState.ApcListHead[PreviousMode].Flink;
482 RemoveEntryList(&Thread->ApcState.ApcListHead[PreviousMode]);
483 CurrentEntry = FirstEntry;
484 do {
485 Apc = CONTAINING_RECORD(CurrentEntry, KAPC, ApcListEntry);
486 Apc->Inserted = FALSE;
487 CurrentEntry = CurrentEntry->Flink;
488 } while (CurrentEntry != FirstEntry);
489 }
490
491 /* Release the locks */
492 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
493 KeReleaseDispatcherDatabaseLock(OldIrql);
494
495 /* Return the first entry */
496 return FirstEntry;
497 }
498
499 /*++
500 * KeRemoveQueueApc
501 *
502 * The KeRemoveQueueApc routine removes a given APC object from the system
503 * APC queue.
504 *
505 * Params:
506 * APC - Pointer to an initialized APC object that was queued by calling
507 * KeInsertQueueApc.
508 *
509 * Returns:
510 * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is
511 * performed and FALSE is returned.
512 *
513 * Remarks:
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.
516 *
517 * Callers of KeLeaveCriticalRegion can be running at any IRQL.
518 *
519 *--*/
520 BOOLEAN
521 STDCALL
522 KeRemoveQueueApc(PKAPC Apc)
523 {
524 KIRQL OldIrql;
525 PKTHREAD Thread = Apc->Thread;
526
527 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
528 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc);
529
530 OldIrql = KeAcquireDispatcherDatabaseLock();
531 KeAcquireSpinLockAtDpcLevel(&Thread->ApcQueueLock);
532
533 /* Check if it's inserted */
534 if (Apc->Inserted) {
535
536 /* Remove it from the Queue*/
537 RemoveEntryList(&Apc->ApcListEntry);
538 Apc->Inserted = FALSE;
539
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])) {
542
543 /* Set the correct State based on the Apc Mode */
544 if (Apc->ApcMode == KernelMode) {
545
546 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE;
547
548 } else {
549
550 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE;
551 }
552 }
553
554 } else {
555
556 /* It's not inserted, fail */
557 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
558 KeReleaseDispatcherDatabaseLock(OldIrql);
559 return(FALSE);
560 }
561
562 /* Restore IRQL and Return */
563 KeReleaseSpinLockFromDpcLevel(&Thread->ApcQueueLock);
564 KeReleaseDispatcherDatabaseLock(OldIrql);
565 return(TRUE);
566 }
567
568 /*++
569 * KiDeliverApc
570 * @implemented @NT4
571 *
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
574 * pending.
575 *
576 * Params:
577 * DeliveryMode - Specifies the current processor mode.
578 *
579 * Reserved - Pointer to the Exception Frame on non-i386 builds.
580 *
581 * TrapFrame - Pointer to the Trap Frame.
582 *
583 * Returns:
584 * None.
585 *
586 * Remarks:
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
589 * mode is User.
590 *
591 * Upon entry, this routine executes at APC_LEVEL.
592 *
593 *--*/
594 VOID
595 STDCALL
596 KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
597 PVOID Reserved,
598 PKTRAP_FRAME TrapFrame)
599 {
600 PKTHREAD Thread = KeGetCurrentThread();
601 PLIST_ENTRY ApcListEntry;
602 PKAPC Apc;
603 KIRQL OldIrql;
604 PKKERNEL_ROUTINE KernelRoutine;
605 PVOID NormalContext;
606 PKNORMAL_ROUTINE NormalRoutine;
607 PVOID SystemArgument1;
608 PVOID SystemArgument2;
609
610 ASSERT_IRQL_EQUAL(APC_LEVEL);
611
612 /* Lock the APC Queue and Raise IRQL to Synch */
613 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
614
615 /* Clear APC Pending */
616 Thread->ApcState.KernelApcPending = FALSE;
617
618 /* Do the Kernel APCs first */
619 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) {
620
621 /* Get the next Entry */
622 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
623 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
624
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;
631
632 /* Special APC */
633 if (NormalRoutine == NULL) {
634
635 /* Remove the APC from the list */
636 RemoveEntryList(ApcListEntry);
637 Apc->Inserted = FALSE;
638
639 /* Go back to APC_LEVEL */
640 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
641
642 /* Call the Special APC */
643 DPRINT("Delivering a Special APC: %x\n", Apc);
644 KernelRoutine(Apc,
645 &NormalRoutine,
646 &NormalContext,
647 &SystemArgument1,
648 &SystemArgument2);
649
650 /* Raise IRQL and Lock again */
651 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
652
653 } else {
654
655 /* Normal Kernel APC */
656 if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) {
657
658 /*
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).
662 * -Gunnar
663 */
664 ASSERT(DeliveryMode == KernelMode);
665
666 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
667 return;
668 }
669
670 /* Dequeue the APC */
671 RemoveEntryList(ApcListEntry);
672 Apc->Inserted = FALSE;
673
674 /* Go back to APC_LEVEL */
675 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
676
677 /* Call the Kernel APC */
678 DPRINT("Delivering a Normal APC: %x\n", Apc);
679 KernelRoutine(Apc,
680 &NormalRoutine,
681 &NormalContext,
682 &SystemArgument1,
683 &SystemArgument2);
684
685 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
686 if (NormalRoutine != NULL) {
687
688 /* At Passive Level, this APC can be prempted by a Special APC */
689 Thread->ApcState.KernelApcInProgress = TRUE;
690 KeLowerIrql(PASSIVE_LEVEL);
691
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);
696 }
697
698 /* Raise IRQL and Lock again */
699 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
700 Thread->ApcState.KernelApcInProgress = FALSE;
701 }
702 }
703
704 /* Now we do the User APCs */
705 if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
706 (DeliveryMode != KernelMode) && (Thread->ApcState.UserApcPending == TRUE)) {
707
708 /* It's not pending anymore */
709 Thread->ApcState.UserApcPending = FALSE;
710
711 /* Get the APC Object */
712 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
713 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
714
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;
721
722 /* Remove the APC from Queue, restore IRQL and call the APC */
723 RemoveEntryList(ApcListEntry);
724 Apc->Inserted = FALSE;
725 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
726
727 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc);
728 KernelRoutine(Apc,
729 &NormalRoutine,
730 &NormalContext,
731 &SystemArgument1,
732 &SystemArgument2);
733
734 if (NormalRoutine == NULL) {
735
736 /* Check if more User APCs are Pending */
737 KeTestAlertThread(UserMode);
738
739 } else {
740
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,
744 TrapFrame,
745 NormalRoutine,
746 NormalContext,
747 SystemArgument1,
748 SystemArgument2);
749 }
750
751 } else {
752
753 /* Go back to APC_LEVEL */
754 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
755 }
756 }
757
758 VOID
759 STDCALL
760 KiFreeApcRoutine(PKAPC Apc,
761 PKNORMAL_ROUTINE* NormalRoutine,
762 PVOID* NormalContext,
763 PVOID* SystemArgument1,
764 PVOID* SystemArgument2)
765 {
766 /* Free the APC and do nothing else */
767 ExFreePool(Apc);
768 }
769
770 /*++
771 * KiInitializeUserApc
772 *
773 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
774 *
775 * Params:
776 * Reserved - Pointer to the Exception Frame on non-i386 builds.
777 *
778 * TrapFrame - Pointer to the Trap Frame.
779 *
780 * NormalRoutine - Pointer to the NormalRoutine to call.
781 *
782 * NormalContext - Pointer to the context to send to the Normal Routine.
783 *
784 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
785 * untyped data.
786 *
787 * Returns:
788 * None.
789 *
790 * Remarks:
791 * None.
792 *
793 *--*/
794 VOID
795 STDCALL
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)
802 {
803 CONTEXT Context;
804 ULONG_PTR Stack;
805 ULONG Size;
806
807 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame,
808 KeGetCurrentThread()->TrapFrame);
809
810 /* Don't deliver APCs in V86 mode */
811 if (TrapFrame->Eflags & 2) return;
812
813 /* Save the full context */
814 Context.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS;
815 KeTrapFrameToContext(TrapFrame, ExceptionFrame, &Context);
816
817 /* Protect with SEH */
818 _SEH_TRY
819 {
820 /* Get the aligned size */
821 Size = ((sizeof(CONTEXT) + 3) & ~3) + 4 * sizeof(ULONG_PTR);
822 Stack = (Context.Esp & ~3) - Size;
823
824 /* Probe and copy */
825 ProbeForWrite((PVOID)Stack, Size, 4);
826 RtlMoveMemory((PVOID)(Stack + 4 * sizeof(ULONG_PTR)),
827 &Context,
828 sizeof(CONTEXT));
829
830 /* Run at APC dispatcher */
831 TrapFrame->Eip = (ULONG)KeUserApcDispatcher;
832 TrapFrame->Esp = Stack;
833
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;
839 }
840 _SEH_HANDLE
841 {
842 /* FIXME: Get the record and raise an exception */
843 }
844 _SEH_END;
845 }
846
847 /*++
848 * KeAreApcsDisabled
849 * @implemented NT4
850 *
851 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
852 *
853 * Params:
854 * None.
855 *
856 * Returns:
857 * KeAreApcsDisabled returns TRUE if the thread is within a critical region
858 * or a guarded region, and FALSE otherwise.
859 *
860 * Remarks:
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.
866 *
867 * Callers of this routine must be running at IRQL <= APC_LEVEL.
868 *
869 *--*/
870 BOOLEAN
871 STDCALL
872 KeAreApcsDisabled(VOID)
873 {
874 /* Return the Kernel APC State */
875 return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE;
876 }
877
878 /*++
879 * NtQueueApcThread
880 * NT4
881 *
882 * This routine is used to queue an APC from user-mode for the specified
883 * thread.
884 *
885 * Params:
886 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
887 *
888 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
889 *
890 * NormalContext - Pointer to the context to send to the Normal Routine.
891 *
892 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
893 * untyped data.
894 *
895 * Returns:
896 * STATUS_SUCCESS or failure cute from associated calls.
897 *
898 * Remarks:
899 * The thread must enter an alertable wait before the APC will be
900 * delivered.
901 *
902 *--*/
903 NTSTATUS
904 STDCALL
905 NtQueueApcThread(HANDLE ThreadHandle,
906 PKNORMAL_ROUTINE ApcRoutine,
907 PVOID NormalContext,
908 PVOID SystemArgument1,
909 PVOID SystemArgument2)
910 {
911 PKAPC Apc;
912 PETHREAD Thread;
913 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
914 NTSTATUS Status;
915
916 /* Get ETHREAD from Handle */
917 Status = ObReferenceObjectByHandle(ThreadHandle,
918 THREAD_SET_CONTEXT,
919 PsThreadType,
920 PreviousMode,
921 (PVOID)&Thread,
922 NULL);
923
924 /* Fail if the Handle is invalid for some reason */
925 if (!NT_SUCCESS(Status)) {
926
927 return(Status);
928 }
929
930 /* If this is a Kernel or System Thread, then fail */
931 if (Thread->Tcb.Teb == NULL) {
932
933 ObDereferenceObject(Thread);
934 return STATUS_INVALID_HANDLE;
935 }
936
937 /* Allocate an APC */
938 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('P', 's', 'a', 'p'));
939 if (Apc == NULL) {
940
941 ObDereferenceObject(Thread);
942 return(STATUS_NO_MEMORY);
943 }
944
945 /* Initialize and Queue a user mode apc (always!) */
946 KeInitializeApc(Apc,
947 &Thread->Tcb,
948 OriginalApcEnvironment,
949 KiFreeApcRoutine,
950 NULL,
951 ApcRoutine,
952 UserMode,
953 NormalContext);
954
955 if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) {
956
957 Status = STATUS_UNSUCCESSFUL;
958
959 } else {
960
961 Status = STATUS_SUCCESS;
962 }
963
964 /* Dereference Thread and Return */
965 ObDereferenceObject(Thread);
966 return Status;
967 }
968
969 static inline
970 VOID RepairList(PLIST_ENTRY Original,
971 PLIST_ENTRY Copy,
972 KPROCESSOR_MODE Mode)
973 {
974 /* Copy Source to Desination */
975 if (IsListEmpty(&Original[(int)Mode])) {
976
977 InitializeListHead(&Copy[(int)Mode]);
978
979 } else {
980
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];
985 }
986 }
987
988 VOID
989 STDCALL
990 KiMoveApcState(PKAPC_STATE OldState,
991 PKAPC_STATE NewState)
992 {
993 /* Restore backup of Original Environment */
994 *NewState = *OldState;
995
996 /* Repair Lists */
997 RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode);
998 RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode);
999 }
1000