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