Thread/Process Termination/Repeaing Rewrite + Fixes
[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 * KeEnterCriticalRegion
21 * @implemented NT4
22 *
23 * The KeEnterCriticalRegion routine temporarily disables the delivery of
24 * normal kernel APCs; special kernel-mode APCs are still delivered.
25 *
26 * Params:
27 * None.
28 *
29 * Returns:
30 * None.
31 *
32 * Remarks:
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.
36 *
37 * Callers of KeEnterCriticalRegion must be running at IRQL <= APC_LEVEL.
38 *
39 *--*/
40 VOID
41 STDCALL
42 KeEnterCriticalRegion(VOID)
43 {
44 /* Disable Kernel APCs */
45 PKTHREAD Thread = KeGetCurrentThread();
46 if (Thread) Thread->KernelApcDisable--;
47 }
48
49 /*++
50 * KeInitializeApc
51 * @implemented NT4
52 *
53 * The The KeInitializeApc routine initializes an APC object, and registers
54 * the Kernel, Rundown and Normal routines for that object.
55 *
56 * Params:
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.
60 *
61 * Thread - Thread to which to deliver the APC.
62 *
63 * TargetEnvironment - APC Environment to be used.
64 *
65 * KernelRoutine - Points to the KernelRoutine to associate with the APC.
66 * This routine is executed for all APCs.
67 *
68 * RundownRoutine - Points to the RundownRoutine to associate with the APC.
69 * This routine is executed when the Thread exists with
70 * the APC executing.
71 *
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.
76 *
77 * Mode - Specifies the processor mode at which to run the Normal Routine.
78 *
79 * Context - Specifices the value to pass as Context parameter to the
80 * registered routines.
81 *
82 * Returns:
83 * None.
84 *
85 * Remarks:
86 * The caller can queue an initialized APC with KeInsertQueueApc.
87 *
88 * Storage for the APC object must be resident, such as nonpaged pool
89 * allocated by the caller.
90 *
91 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
92 *
93 *--*/
94 VOID
95 STDCALL
96 KeInitializeApc(IN PKAPC Apc,
97 IN PKTHREAD Thread,
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,
103 IN PVOID Context)
104 {
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);
109
110 /* Set up the basic APC Structure Data */
111 RtlZeroMemory(Apc, sizeof(KAPC));
112 Apc->Type = ApcObject;
113 Apc->Size = sizeof(KAPC);
114
115 /* Set the Environment */
116 if (TargetEnvironment == CurrentApcEnvironment) {
117
118 Apc->ApcStateIndex = Thread->ApcStateIndex;
119
120 } else {
121
122 Apc->ApcStateIndex = TargetEnvironment;
123 }
124
125 /* Set the Thread and Routines */
126 Apc->Thread = Thread;
127 Apc->KernelRoutine = KernelRoutine;
128 Apc->RundownRoutine = RundownRoutine;
129 Apc->NormalRoutine = NormalRoutine;
130
131 /* Check if this is a Special APC, in which case we use KernelMode and no Context */
132 if (ARGUMENT_PRESENT(NormalRoutine)) {
133
134 Apc->ApcMode = Mode;
135 Apc->NormalContext = Context;
136
137 } else {
138
139 Apc->ApcMode = KernelMode;
140 }
141 }
142
143 /*++
144 * KiInsertQueueApc
145 *
146 * The KiInsertQueueApc routine queues a APC for execution when the right
147 * scheduler environment exists.
148 *
149 * Params:
150 * Apc - Pointer to an initialized control object of type DPC for which the
151 * caller provides the storage.
152 *
153 * PriorityBoost - Priority Boost to apply to the Thread.
154 *
155 * Returns:
156 * If the APC is already inserted or APC queueing is disabled, FALSE.
157 * Otherwise, TRUE.
158 *
159 * Remarks:
160 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
161 * at PASSIVE_LEVEL for the NormalRoutine registered.
162 *
163 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
164 *
165 *--*/
166 BOOLEAN
167 STDCALL
168 KiInsertQueueApc(PKAPC Apc,
169 KPRIORITY PriorityBoost)
170 {
171 PKTHREAD Thread = Apc->Thread;
172 PLIST_ENTRY ApcListEntry;
173 PKAPC QueuedApc;
174
175 /* Don't do anything if the APC is already inserted */
176 if (Apc->Inserted) {
177
178 return FALSE;
179 }
180
181 /* Three scenarios:
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
185 */
186 if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) {
187
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],
191 &Apc->ApcListEntry);
192
193 } else if (Apc->NormalRoutine == NULL) {
194
195 DPRINT ("Inserting Special APC %x into the Queue\n", Apc);
196
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) {
200
201 QueuedApc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
202 if (Apc->NormalRoutine != NULL) break;
203 }
204
205 /* We found the first "Normal" APC, so write right before it */
206 ApcListEntry = ApcListEntry->Blink;
207 InsertHeadList(ApcListEntry, &Apc->ApcListEntry);
208
209 } else {
210
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],
213 &Apc->ApcListEntry);
214 }
215
216 /* Confirm Insertion */
217 Apc->Inserted = TRUE;
218
219 /*
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
224 */
225 if (Apc->ApcMode == KernelMode) {
226
227 /* Set Kernel APC pending */
228 Thread->ApcState.KernelApcPending = TRUE;
229
230 /* Check the Thread State */
231 if (Thread->State == THREAD_STATE_RUNNING) {
232
233 /* FIXME: Use IPI */
234 DPRINT ("Requesting APC Interrupt for Running Thread \n");
235 HalRequestSoftwareInterrupt(APC_LEVEL);
236
237 } else if ((Thread->State == THREAD_STATE_BLOCKED) && (Thread->WaitIrql == PASSIVE_LEVEL) &&
238 ((Apc->NormalRoutine == NULL) ||
239 ((!Thread->KernelApcDisable) && (!Thread->ApcState.KernelApcInProgress)))) {
240
241 DPRINT("Waking up Thread for Kernel-Mode APC Delivery \n");
242 KiAbortWaitThread(Thread, STATUS_KERNEL_APC, PriorityBoost);
243 }
244
245 } else if ((Thread->State == THREAD_STATE_BLOCKED) &&
246 (Thread->WaitMode == UserMode) &&
247 (Thread->Alertable)) {
248
249 DPRINT("Waking up Thread for User-Mode APC Delivery \n");
250 Thread->ApcState.UserApcPending = TRUE;
251 KiAbortWaitThread(Thread, STATUS_USER_APC, PriorityBoost);
252 }
253
254 return TRUE;
255 }
256
257 /*++
258 * KeInsertQueueApc
259 * @implemented NT4
260 *
261 * The KeInsertQueueApc routine queues a APC for execution when the right
262 * scheduler environment exists.
263 *
264 * Params:
265 * Apc - Pointer to an initialized control object of type DPC for which the
266 * caller provides the storage.
267 *
268 * SystemArgument[1,2] - Pointer to a set of two parameters that contain
269 * untyped data.
270 *
271 * PriorityBoost - Priority Boost to apply to the Thread.
272 *
273 * Returns:
274 * If the APC is already inserted or APC queueing is disabled, FALSE.
275 * Otherwise, TRUE.
276 *
277 * Remarks:
278 * The APC will execute at APC_LEVEL for the KernelRoutine registered, and
279 * at PASSIVE_LEVEL for the NormalRoutine registered.
280 *
281 * Callers of this routine must be running at IRQL = PASSIVE_LEVEL.
282 *
283 *--*/
284 BOOLEAN
285 STDCALL
286 KeInsertQueueApc(PKAPC Apc,
287 PVOID SystemArgument1,
288 PVOID SystemArgument2,
289 KPRIORITY PriorityBoost)
290
291 {
292 KIRQL OldIrql;
293 PKTHREAD Thread;
294 BOOLEAN Inserted;
295
296 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
297 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
298 "SystemArgument2 %x)\n",Apc,SystemArgument1,
299 SystemArgument2);
300
301 /* Lock the Dispatcher Database */
302 OldIrql = KeAcquireDispatcherDatabaseLock();
303
304 /* Get the Thread specified in the APC */
305 Thread = Apc->Thread;
306
307 /* Make sure the thread allows APC Queues.
308 * The thread is not apc queueable, for instance, when it's (about to be) terminated.
309 */
310 if (Thread->ApcQueueable == FALSE) {
311 DPRINT("Thread doesn't allow APC Queues\n");
312 KeReleaseDispatcherDatabaseLock(OldIrql);
313 return FALSE;
314 }
315
316 /* Set the System Arguments */
317 Apc->SystemArgument1 = SystemArgument1;
318 Apc->SystemArgument2 = SystemArgument2;
319
320 /* Call the Internal Function */
321 Inserted = KiInsertQueueApc(Apc, PriorityBoost);
322
323 /* Return Sucess if we are here */
324 KeReleaseDispatcherDatabaseLock(OldIrql);
325 return Inserted;
326 }
327
328 /*++
329 * KeLeaveCriticalRegion
330 * @implemented NT4
331 *
332 * The KeLeaveCriticalRegion routine reenables the delivery of normal
333 * kernel-mode APCs that were disabled by a call to KeEnterCriticalRegion.
334 *
335 * Params:
336 * None.
337 *
338 * Returns:
339 * None.
340 *
341 * Remarks:
342 * Highest-level drivers can call this routine while running in the context
343 * of the thread that requested the current I/O operation.
344 *
345 * Callers of KeLeaveCriticalRegion must be running at IRQL <= DISPATCH_LEVEL.
346 *
347 *--*/
348 VOID
349 STDCALL
350 KeLeaveCriticalRegion (VOID)
351 {
352 PKTHREAD Thread = KeGetCurrentThread();
353
354 /* Check if Kernel APCs are now enabled */
355 if((Thread) && (++Thread->KernelApcDisable == 0)) {
356
357 /* Check if we need to request an APC Delivery */
358 if (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) {
359
360 /* Set APC Pending */
361 Thread->ApcState.KernelApcPending = TRUE;
362 HalRequestSoftwareInterrupt(APC_LEVEL);
363 }
364 }
365 }
366
367 /*++
368 * KeRemoveQueueApc
369 *
370 * The KeRemoveQueueApc routine removes a given APC object from the system
371 * APC queue.
372 *
373 * Params:
374 * APC - Pointer to an initialized APC object that was queued by calling
375 * KeInsertQueueApc.
376 *
377 * Returns:
378 * TRUE if the APC Object is in the APC Queue. If it isn't, no operation is
379 * performed and FALSE is returned.
380 *
381 * Remarks:
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.
384 *
385 * Callers of KeLeaveCriticalRegion can be running at any IRQL.
386 *
387 *--*/
388 BOOLEAN
389 STDCALL
390 KeRemoveQueueApc(PKAPC Apc)
391 {
392 KIRQL OldIrql;
393 PKTHREAD Thread = Apc->Thread;
394
395 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
396 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc);
397
398 OldIrql = KeAcquireDispatcherDatabaseLock();
399 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
400
401 /* Check if it's inserted */
402 if (Apc->Inserted) {
403
404 /* Remove it from the Queue*/
405 RemoveEntryList(&Apc->ApcListEntry);
406 Apc->Inserted = FALSE;
407
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])) {
410
411 /* Set the correct State based on the Apc Mode */
412 if (Apc->ApcMode == KernelMode) {
413
414 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE;
415
416 } else {
417
418 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE;
419 }
420 }
421
422 } else {
423
424 /* It's not inserted, fail */
425 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
426 KeReleaseDispatcherDatabaseLock(OldIrql);
427 return(FALSE);
428 }
429
430 /* Restore IRQL and Return */
431 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
432 KeReleaseDispatcherDatabaseLock(OldIrql);
433 return(TRUE);
434 }
435
436 /*++
437 * KiDeliverApc
438 * @implemented @NT4
439 *
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
442 * pending.
443 *
444 * Params:
445 * DeliveryMode - Specifies the current processor mode.
446 *
447 * Reserved - Pointer to the Exception Frame on non-i386 builds.
448 *
449 * TrapFrame - Pointer to the Trap Frame.
450 *
451 * Returns:
452 * None.
453 *
454 * Remarks:
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
457 * mode is User.
458 *
459 * Upon entry, this routine executes at APC_LEVEL.
460 *
461 *--*/
462 VOID
463 STDCALL
464 KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
465 PVOID Reserved,
466 PKTRAP_FRAME TrapFrame)
467 {
468 PKTHREAD Thread = KeGetCurrentThread();
469 PLIST_ENTRY ApcListEntry;
470 PKAPC Apc;
471 KIRQL OldIrql;
472 PKKERNEL_ROUTINE KernelRoutine;
473 PVOID NormalContext;
474 PKNORMAL_ROUTINE NormalRoutine;
475 PVOID SystemArgument1;
476 PVOID SystemArgument2;
477
478 ASSERT_IRQL_EQUAL(APC_LEVEL);
479
480 /* Lock the APC Queue and Raise IRQL to Synch */
481 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
482
483 /* Clear APC Pending */
484 Thread->ApcState.KernelApcPending = FALSE;
485
486 /* Do the Kernel APCs first */
487 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) {
488
489 /* Get the next Entry */
490 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
491 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
492
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;
499
500 /* Special APC */
501 if (NormalRoutine == NULL) {
502
503 /* Remove the APC from the list */
504 Apc->Inserted = FALSE;
505 RemoveEntryList(ApcListEntry);
506
507 /* Go back to APC_LEVEL */
508 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
509
510 /* Call the Special APC */
511 DPRINT("Delivering a Special APC: %x\n", Apc);
512 KernelRoutine(Apc,
513 &NormalRoutine,
514 &NormalContext,
515 &SystemArgument1,
516 &SystemArgument2);
517
518 /* Raise IRQL and Lock again */
519 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
520
521 } else {
522
523 /* Normal Kernel APC */
524 if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) {
525
526 /*
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).
530 * -Gunnar
531 */
532 ASSERT(DeliveryMode == KernelMode);
533
534 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
535 return;
536 }
537
538 /* Dequeue the APC */
539 RemoveEntryList(ApcListEntry);
540 Apc->Inserted = FALSE;
541
542 /* Go back to APC_LEVEL */
543 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
544
545 /* Call the Kernel APC */
546 DPRINT("Delivering a Normal APC: %x\n", Apc);
547 KernelRoutine(Apc,
548 &NormalRoutine,
549 &NormalContext,
550 &SystemArgument1,
551 &SystemArgument2);
552
553 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
554 if (NormalRoutine != NULL) {
555
556 /* At Passive Level, this APC can be prempted by a Special APC */
557 Thread->ApcState.KernelApcInProgress = TRUE;
558 KeLowerIrql(PASSIVE_LEVEL);
559
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);
564 }
565
566 /* Raise IRQL and Lock again */
567 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
568 Thread->ApcState.KernelApcInProgress = FALSE;
569 }
570 }
571
572 /* Now we do the User APCs */
573 if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
574 (DeliveryMode == UserMode) && (Thread->ApcState.UserApcPending == TRUE)) {
575
576 /* It's not pending anymore */
577 Thread->ApcState.UserApcPending = FALSE;
578
579 /* Get the APC Object */
580 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
581 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
582
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;
589
590 /* Remove the APC from Queue, restore IRQL and call the APC */
591 RemoveEntryList(ApcListEntry);
592 Apc->Inserted = FALSE;
593
594 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
595 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc);
596 KernelRoutine(Apc,
597 &NormalRoutine,
598 &NormalContext,
599 &SystemArgument1,
600 &SystemArgument2);
601
602 if (NormalRoutine == NULL) {
603
604 /* Check if more User APCs are Pending */
605 KeTestAlertThread(UserMode);
606
607 } else {
608
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,
612 TrapFrame,
613 NormalRoutine,
614 NormalContext,
615 SystemArgument1,
616 SystemArgument2);
617 }
618
619 } else {
620
621 /* Go back to APC_LEVEL */
622 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
623 }
624 }
625
626 VOID
627 STDCALL
628 KiFreeApcRoutine(PKAPC Apc,
629 PKNORMAL_ROUTINE* NormalRoutine,
630 PVOID* NormalContext,
631 PVOID* SystemArgument1,
632 PVOID* SystemArgument2)
633 {
634 /* Free the APC and do nothing else */
635 ExFreePool(Apc);
636 }
637
638 /*++
639 * KiInitializeUserApc
640 *
641 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
642 *
643 * Params:
644 * Reserved - Pointer to the Exception Frame on non-i386 builds.
645 *
646 * TrapFrame - Pointer to the Trap Frame.
647 *
648 * NormalRoutine - Pointer to the NormalRoutine to call.
649 *
650 * NormalContext - Pointer to the context to send to the Normal Routine.
651 *
652 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
653 * untyped data.
654 *
655 * Returns:
656 * None.
657 *
658 * Remarks:
659 * None.
660 *
661 *--*/
662 VOID
663 STDCALL
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)
670 {
671 PCONTEXT Context;
672 PULONG Esp;
673
674 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame, KeGetCurrentThread()->TrapFrame);
675
676 /*
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
680 */
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;
700
701 /*
702 * Setup the trap frame so the thread will start executing at the
703 * APC Dispatcher when it returns to user-mode
704 */
705 Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) - (sizeof(CONTEXT) + (6 * sizeof(ULONG))));
706 Esp[0] = 0xdeadbeef;
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;
714 }
715
716 /*++
717 * KeAreApcsDisabled
718 * @implemented NT4
719 *
720 * Prepares the Context for a User-Mode APC called through NTDLL.DLL
721 *
722 * Params:
723 * None.
724 *
725 * Returns:
726 * KeAreApcsDisabled returns TRUE if the thread is within a critical region
727 * or a guarded region, and FALSE otherwise.
728 *
729 * Remarks:
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.
735 *
736 * Callers of this routine must be running at IRQL <= APC_LEVEL.
737 *
738 *--*/
739 BOOLEAN
740 STDCALL
741 KeAreApcsDisabled(VOID)
742 {
743 /* Return the Kernel APC State */
744 return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE;
745 }
746
747 /*++
748 * NtQueueApcThread
749 * NT4
750 *
751 * This routine is used to queue an APC from user-mode for the specified
752 * thread.
753 *
754 * Params:
755 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
756 *
757 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
758 *
759 * NormalContext - Pointer to the context to send to the Normal Routine.
760 *
761 * SystemArgument[1-2] - Pointer to a set of two parameters that contain
762 * untyped data.
763 *
764 * Returns:
765 * STATUS_SUCCESS or failure cute from associated calls.
766 *
767 * Remarks:
768 * The thread must enter an alertable wait before the APC will be
769 * delivered.
770 *
771 *--*/
772 NTSTATUS
773 STDCALL
774 NtQueueApcThread(HANDLE ThreadHandle,
775 PKNORMAL_ROUTINE ApcRoutine,
776 PVOID NormalContext,
777 PVOID SystemArgument1,
778 PVOID SystemArgument2)
779 {
780 PKAPC Apc;
781 PETHREAD Thread;
782 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
783 NTSTATUS Status;
784
785 /* Get ETHREAD from Handle */
786 Status = ObReferenceObjectByHandle(ThreadHandle,
787 THREAD_SET_CONTEXT,
788 PsThreadType,
789 PreviousMode,
790 (PVOID)&Thread,
791 NULL);
792
793 /* Fail if the Handle is invalid for some reason */
794 if (!NT_SUCCESS(Status)) {
795
796 return(Status);
797 }
798
799 /* If this is a Kernel or System Thread, then fail */
800 if (Thread->Tcb.Teb == NULL) {
801
802 ObDereferenceObject(Thread);
803 return STATUS_INVALID_HANDLE;
804 }
805
806 /* Allocate an APC */
807 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG('P', 's', 'a', 'p'));
808 if (Apc == NULL) {
809
810 ObDereferenceObject(Thread);
811 return(STATUS_NO_MEMORY);
812 }
813
814 /* Initialize and Queue a user mode apc (always!) */
815 KeInitializeApc(Apc,
816 &Thread->Tcb,
817 OriginalApcEnvironment,
818 KiFreeApcRoutine,
819 NULL,
820 ApcRoutine,
821 UserMode,
822 NormalContext);
823
824 if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) {
825
826 Status = STATUS_UNSUCCESSFUL;
827
828 } else {
829
830 Status = STATUS_SUCCESS;
831 }
832
833 /* Dereference Thread and Return */
834 ObDereferenceObject(Thread);
835 return Status;
836 }
837
838 static inline
839 VOID RepairList(PLIST_ENTRY Original,
840 PLIST_ENTRY Copy,
841 KPROCESSOR_MODE Mode)
842 {
843 /* Copy Source to Desination */
844 if (IsListEmpty(&Original[(int)Mode])) {
845
846 InitializeListHead(&Copy[(int)Mode]);
847
848 } else {
849
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];
854 }
855 }
856
857 VOID
858 STDCALL
859 KiMoveApcState(PKAPC_STATE OldState,
860 PKAPC_STATE NewState)
861 {
862 /* Restore backup of Original Environment */
863 *NewState = *OldState;
864
865 /* Repair Lists */
866 RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode);
867 RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode);
868 }
869