-move alert stuff into own file
[reactos.git] / reactos / ntoskrnl / ke / apc.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /*
20 * PROJECT: ReactOS kernel
21 * FILE: ntoskrnl/ke/apc.c
22 * PURPOSE: NT Implementation of APCs
23 * PROGRAMMER: Alex Ionescu (alex@relsoft.net)
24 * PORTABILITY: Unchecked
25 * UPDATE HISTORY:
26 * Created 22/05/98
27 * 12/11/99: Phillip Susi: Reworked the APC code
28 * 11/11/04: Alex Ionescu - Total Rewrite
29 */
30
31 /* INCLUDES *****************************************************************/
32
33 #include <ntoskrnl.h>
34 #define NDEBUG
35 #include <internal/debug.h>
36
37 /* GLOBALS *******************************************************************/
38
39 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
40
41 #define TAG_KAPC TAG('K', 'A', 'P', 'C')
42
43 /* FUNCTIONS *****************************************************************/
44
45 /*
46 * @implemented
47 */
48 VOID
49 STDCALL
50 KeInitializeApc(
51 IN PKAPC Apc,
52 IN PKTHREAD Thread,
53 IN KAPC_ENVIRONMENT TargetEnvironment,
54 IN PKKERNEL_ROUTINE KernelRoutine,
55 IN PKRUNDOWN_ROUTINE RundownRoutine OPTIONAL,
56 IN PKNORMAL_ROUTINE NormalRoutine,
57 IN KPROCESSOR_MODE Mode,
58 IN PVOID Context)
59 /*
60 * FUNCTION: Initialize an APC object
61 * ARGUMENTS:
62 * Apc = Pointer to the APC object to initialized
63 * Thread = Thread the APC is to be delivered to
64 * TargetEnvironment = APC environment to use
65 * KernelRoutine = Routine to be called for a kernel-mode APC
66 * RundownRoutine = Routine to be called if the thread has exited with
67 * the APC being executed
68 * NormalRoutine = Routine to be called for a user-mode APC
69 * Mode = APC mode
70 * Context = Parameter to be passed to the APC routine
71 */
72 {
73 DPRINT ("KeInitializeApc(Apc %x, Thread %x, Environment %d, "
74 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
75 "Context %x)\n",Apc,Thread,TargetEnvironment,KernelRoutine,RundownRoutine,
76 NormalRoutine,Mode,Context);
77
78 /* Set up the basic APC Structure Data */
79 RtlZeroMemory(Apc, sizeof(KAPC));
80 Apc->Type = KApc;
81 Apc->Size = sizeof(KAPC);
82
83 /* Set the Environment */
84 if (TargetEnvironment == CurrentApcEnvironment) {
85 Apc->ApcStateIndex = Thread->ApcStateIndex;
86 } else {
87 Apc->ApcStateIndex = TargetEnvironment;
88 }
89
90 /* Set the Thread and Routines */
91 Apc->Thread = Thread;
92 Apc->KernelRoutine = KernelRoutine;
93 Apc->RundownRoutine = RundownRoutine;
94 Apc->NormalRoutine = NormalRoutine;
95
96 /* Check if this is a Special APC, in which case we use KernelMode and no Context */
97 if (ARGUMENT_PRESENT(NormalRoutine)) {
98 Apc->ApcMode = Mode;
99 Apc->NormalContext = Context;
100 } else {
101 Apc->ApcMode = KernelMode;
102 }
103 }
104
105 /*
106 * @implemented
107 */
108 BOOLEAN
109 STDCALL
110 KeInsertQueueApc (PKAPC Apc,
111 PVOID SystemArgument1,
112 PVOID SystemArgument2,
113 KPRIORITY PriorityBoost)
114 /*
115 * FUNCTION: Queues an APC for execution
116 * ARGUMENTS:
117 * Apc = APC to be queued
118 * SystemArgument[1-2] = Arguments we ignore and simply pass on.
119 * PriorityBoost = Priority Boost to give to the Thread
120 */
121 {
122 KIRQL OldIrql;
123 PKTHREAD Thread;
124 PLIST_ENTRY ApcListEntry;
125 PKAPC QueuedApc;
126
127 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
128 DPRINT ("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
129 "SystemArgument2 %x)\n",Apc,SystemArgument1,
130 SystemArgument2);
131
132 OldIrql = KeAcquireDispatcherDatabaseLock();
133
134 /* Get the Thread specified in the APC */
135 Thread = Apc->Thread;
136
137 /* Make sure the thread allows APC Queues.
138 * The thread is not apc queueable, for instance, when it's (about to be) terminated.
139 */
140 if (Thread->ApcQueueable == FALSE) {
141 DPRINT("Thread doesn't allow APC Queues\n");
142 KeReleaseDispatcherDatabaseLock(OldIrql);
143 return FALSE;
144 }
145
146 /* Set the System Arguments */
147 Apc->SystemArgument1 = SystemArgument1;
148 Apc->SystemArgument2 = SystemArgument2;
149
150 /* Don't do anything if the APC is already inserted */
151 if (Apc->Inserted) {
152 KeReleaseDispatcherDatabaseLock(OldIrql);
153 return FALSE;
154 }
155
156 /* Three scenarios:
157 1) Kernel APC with Normal Routine or User APC = Put it at the end of the List
158 2) User APC which is PsExitSpecialApc = Put it at the front of the List
159 3) Kernel APC without Normal Routine = Put it at the end of the No-Normal Routine Kernel APC list
160 */
161 if ((Apc->ApcMode != KernelMode) && (Apc->KernelRoutine == (PKKERNEL_ROUTINE)PsExitSpecialApc)) {
162 DPRINT ("Inserting the Process Exit APC into the Queue\n");
163 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = TRUE;
164 InsertHeadList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
165 &Apc->ApcListEntry);
166 } else if (Apc->NormalRoutine == NULL) {
167 DPRINT ("Inserting Special APC %x into the Queue\n", Apc);
168 for (ApcListEntry = Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode].Flink;
169 ApcListEntry != &Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode];
170 ApcListEntry = ApcListEntry->Flink) {
171
172 QueuedApc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
173 if (Apc->NormalRoutine != NULL) break;
174 }
175
176 /* We found the first "Normal" APC, so write right before it */
177 ApcListEntry = ApcListEntry->Blink;
178 InsertHeadList(ApcListEntry, &Apc->ApcListEntry);
179 } else {
180 DPRINT ("Inserting Normal APC %x into the %x Queue\n", Apc, Apc->ApcMode);
181 InsertTailList(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode],
182 &Apc->ApcListEntry);
183 }
184
185 /* Confirm Insertion */
186 Apc->Inserted = TRUE;
187
188 /* Three possibilites here again:
189 1) Kernel APC, The thread is Running: Request an Interrupt
190 2) Kernel APC, The Thread is Waiting at PASSIVE_LEVEL and APCs are enabled and not in progress: Unwait the Thread
191 3) User APC, Unwait the Thread if it is alertable
192 */
193 if (Apc->ApcMode == KernelMode) {
194 Thread->ApcState.KernelApcPending = TRUE;
195 if (Thread->State == THREAD_STATE_RUNNING) {
196 /* FIXME: Use IPI */
197 DPRINT ("Requesting APC Interrupt for Running Thread \n");
198 HalRequestSoftwareInterrupt(APC_LEVEL);
199 } else if ((Thread->State == THREAD_STATE_BLOCKED) &&
200 (Thread->WaitIrql < APC_LEVEL) &&
201 (Apc->NormalRoutine == NULL))
202 {
203 DPRINT ("Waking up Thread for Kernel-Mode APC Delivery \n");
204 KiAbortWaitThread(Thread, STATUS_KERNEL_APC);
205 }
206 } else if ((Thread->State == THREAD_STATE_BLOCKED) &&
207 (Thread->WaitMode == UserMode) &&
208 (Thread->Alertable))
209 {
210 DPRINT ("Waking up Thread for User-Mode APC Delivery \n");
211 Thread->ApcState.UserApcPending = TRUE;
212 KiAbortWaitThread(Thread, STATUS_USER_APC);
213 }
214
215 /* Return Sucess if we are here */
216 KeReleaseDispatcherDatabaseLock(OldIrql);
217 return TRUE;
218 }
219
220 BOOLEAN STDCALL
221 KeRemoveQueueApc (PKAPC Apc)
222 /*
223 * FUNCTION: Removes APC object from the apc queue
224 * ARGUMENTS:
225 * Apc = APC to remove
226 * RETURNS: TRUE if the APC was in the queue
227 * FALSE otherwise
228 * NOTE: This function is not exported.
229 */
230 {
231 KIRQL OldIrql;
232 PKTHREAD Thread = Apc->Thread;
233
234 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
235 DPRINT("KeRemoveQueueApc called for APC: %x \n", Apc);
236
237 OldIrql = KeAcquireDispatcherDatabaseLock();
238 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
239
240 /* Remove it from the Queue if it's inserted */
241 if (!Apc->Inserted == FALSE) {
242 RemoveEntryList(&Apc->ApcListEntry);
243 Apc->Inserted = FALSE;
244
245 /* If the Queue is completely empty, then no more APCs are pending */
246 if (IsListEmpty(&Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->ApcListHead[(int)Apc->ApcMode])) {
247 if (Apc->ApcMode == KernelMode) {
248 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->KernelApcPending = FALSE;
249 } else {
250 Thread->ApcStatePointer[(int)Apc->ApcStateIndex]->UserApcPending = FALSE;
251 }
252 }
253 } else {
254 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
255 KeReleaseDispatcherDatabaseLock(OldIrql);
256 return(FALSE);
257 }
258
259 /* Restore IRQL and Return */
260 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
261 KeReleaseDispatcherDatabaseLock(OldIrql);
262 return(TRUE);
263 }
264
265
266 /*
267 * @implemented
268 */
269 VOID
270 STDCALL
271 KiDeliverApc(KPROCESSOR_MODE DeliveryMode,
272 PVOID Reserved,
273 PKTRAP_FRAME TrapFrame)
274 /*
275 * FUNCTION: Deliver an APC to the current thread.
276 * NOTES: This is called from the IRQL switching code if the current thread
277 * is returning from an IRQL greater than or equal to APC_LEVEL to
278 * PASSIVE_LEVEL and there are kernel-mode APCs pending. This means any
279 * pending APCs will be delivered after a thread gets a new quantum and
280 * after it wakes from a wait. Note that the TrapFrame is only valid if
281 * the previous mode is User.
282 */
283 {
284 PKTHREAD Thread = KeGetCurrentThread();
285 PLIST_ENTRY ApcListEntry;
286 PKAPC Apc;
287 KIRQL OldIrql;
288 PKKERNEL_ROUTINE KernelRoutine;
289 PVOID NormalContext;
290 PKNORMAL_ROUTINE NormalRoutine;
291 PVOID SystemArgument1;
292 PVOID SystemArgument2;
293
294 ASSERT_IRQL_EQUAL(APC_LEVEL);
295
296 /* Lock the APC Queue and Raise IRQL to Synch */
297 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
298
299 /* Clear APC Pending */
300 Thread->ApcState.KernelApcPending = FALSE;
301
302 /* Do the Kernel APCs first */
303 while (!IsListEmpty(&Thread->ApcState.ApcListHead[KernelMode])) {
304
305 /* Get the next Entry */
306 ApcListEntry = Thread->ApcState.ApcListHead[KernelMode].Flink;
307 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
308
309 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
310 NormalRoutine = Apc->NormalRoutine;
311 KernelRoutine = Apc->KernelRoutine;
312 NormalContext = Apc->NormalContext;
313 SystemArgument1 = Apc->SystemArgument1;
314 SystemArgument2 = Apc->SystemArgument2;
315
316 /* Special APC */
317 if (NormalRoutine == NULL) {
318 /* Remove the APC from the list */
319 Apc->Inserted = FALSE;
320 RemoveEntryList(ApcListEntry);
321
322 /* Go back to APC_LEVEL */
323 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
324
325 /* Call the Special APC */
326 DPRINT("Delivering a Special APC: %x\n", Apc);
327 KernelRoutine(Apc,
328 &NormalRoutine,
329 &NormalContext,
330 &SystemArgument1,
331 &SystemArgument2);
332
333 /* Raise IRQL and Lock again */
334 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
335 } else {
336 /* Normal Kernel APC */
337 if (Thread->ApcState.KernelApcInProgress || Thread->KernelApcDisable) {
338
339 /*
340 * DeliveryMode must be KernelMode in this case, since one may not
341 * return to umode while being inside a critical section or while
342 * a regular kmode apc is running (the latter should be impossible btw).
343 * -Gunnar
344 */
345 ASSERT(DeliveryMode == KernelMode);
346
347 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
348 return;
349 }
350
351 /* Dequeue the APC */
352 RemoveEntryList(ApcListEntry);
353 Apc->Inserted = FALSE;
354
355 /* Go back to APC_LEVEL */
356 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
357
358 /* Call the Kernel APC */
359 DPRINT("Delivering a Normal APC: %x\n", Apc);
360 KernelRoutine(Apc,
361 &NormalRoutine,
362 &NormalContext,
363 &SystemArgument1,
364 &SystemArgument2);
365
366 /* If There still is a Normal Routine, then we need to call this at PASSIVE_LEVEL */
367 if (NormalRoutine != NULL) {
368 /* At Passive Level, this APC can be prempted by a Special APC */
369 Thread->ApcState.KernelApcInProgress = TRUE;
370 KeLowerIrql(PASSIVE_LEVEL);
371
372 /* Call and Raise IRQ back to APC_LEVEL */
373 DPRINT("Calling the Normal Routine for a Normal APC: %x\n", Apc);
374 NormalRoutine(&NormalContext, &SystemArgument1, &SystemArgument2);
375 KeRaiseIrql(APC_LEVEL, &OldIrql);
376 }
377
378 /* Raise IRQL and Lock again */
379 KeAcquireSpinLock(&Thread->ApcQueueLock, &OldIrql);
380 Thread->ApcState.KernelApcInProgress = FALSE;
381 }
382 }
383
384 /* Now we do the User APCs */
385 if ((!IsListEmpty(&Thread->ApcState.ApcListHead[UserMode])) &&
386 (DeliveryMode == UserMode) &&
387 (Thread->ApcState.UserApcPending == TRUE)) {
388
389 /* It's not pending anymore */
390 Thread->ApcState.UserApcPending = FALSE;
391
392 /* Get the APC Object */
393 ApcListEntry = Thread->ApcState.ApcListHead[UserMode].Flink;
394 Apc = CONTAINING_RECORD(ApcListEntry, KAPC, ApcListEntry);
395
396 /* Save Parameters so that it's safe to free the Object in Kernel Routine*/
397 NormalRoutine = Apc->NormalRoutine;
398 KernelRoutine = Apc->KernelRoutine;
399 NormalContext = Apc->NormalContext;
400 SystemArgument1 = Apc->SystemArgument1;
401 SystemArgument2 = Apc->SystemArgument2;
402
403 /* Remove the APC from Queue, restore IRQL and call the APC */
404 RemoveEntryList(ApcListEntry);
405 Apc->Inserted = FALSE;
406
407 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
408 DPRINT("Calling the Kernel Routine for for a User APC: %x\n", Apc);
409 KernelRoutine(Apc,
410 &NormalRoutine,
411 &NormalContext,
412 &SystemArgument1,
413 &SystemArgument2);
414
415 if (NormalRoutine == NULL) {
416 /* Check if more User APCs are Pending */
417 KeTestAlertThread(UserMode);
418 } else {
419 /* Set up the Trap Frame and prepare for Execution in NTDLL.DLL */
420 DPRINT("Delivering a User APC: %x\n", Apc);
421 KiInitializeUserApc(Reserved,
422 TrapFrame,
423 NormalRoutine,
424 NormalContext,
425 SystemArgument1,
426 SystemArgument2);
427 }
428 } else {
429 /* Go back to APC_LEVEL */
430 KeReleaseSpinLock(&Thread->ApcQueueLock, OldIrql);
431 }
432 }
433
434 VOID
435 STDCALL
436 KiFreeApcRoutine(PKAPC Apc,
437 PKNORMAL_ROUTINE* NormalRoutine,
438 PVOID* NormalContext,
439 PVOID* SystemArgument1,
440 PVOID* SystemArgument2)
441 {
442 /* Free the APC and do nothing else */
443 ExFreePool(Apc);
444 }
445
446 VOID
447 KiInitializeUserApc(IN PVOID Reserved,
448 IN PKTRAP_FRAME TrapFrame,
449 IN PKNORMAL_ROUTINE NormalRoutine,
450 IN PVOID NormalContext,
451 IN PVOID SystemArgument1,
452 IN PVOID SystemArgument2)
453 /*
454 * FUNCTION: Prepares the Context for a user mode APC through ntdll.dll
455 */
456 {
457 PCONTEXT Context;
458 PULONG Esp;
459
460 DPRINT("KiInitializeUserApc(TrapFrame %x/%x)\n", TrapFrame, KeGetCurrentThread()->TrapFrame);
461
462 /*
463 * Save the thread's current context (in other words the registers
464 * that will be restored when it returns to user mode) so the
465 * APC dispatcher can restore them later
466 */
467 Context = (PCONTEXT)(((PUCHAR)TrapFrame->Esp) - sizeof(CONTEXT));
468 RtlZeroMemory(Context, sizeof(CONTEXT));
469 Context->ContextFlags = CONTEXT_FULL;
470 Context->SegGs = TrapFrame->Gs;
471 Context->SegFs = TrapFrame->Fs;
472 Context->SegEs = TrapFrame->Es;
473 Context->SegDs = TrapFrame->Ds;
474 Context->Edi = TrapFrame->Edi;
475 Context->Esi = TrapFrame->Esi;
476 Context->Ebx = TrapFrame->Ebx;
477 Context->Edx = TrapFrame->Edx;
478 Context->Ecx = TrapFrame->Ecx;
479 Context->Eax = TrapFrame->Eax;
480 Context->Ebp = TrapFrame->Ebp;
481 Context->Eip = TrapFrame->Eip;
482 Context->SegCs = TrapFrame->Cs;
483 Context->EFlags = TrapFrame->Eflags;
484 Context->Esp = TrapFrame->Esp;
485 Context->SegSs = TrapFrame->Ss;
486
487 /*
488 * Setup the trap frame so the thread will start executing at the
489 * APC Dispatcher when it returns to user-mode
490 */
491 Esp = (PULONG)(((PUCHAR)TrapFrame->Esp) - (sizeof(CONTEXT) + (6 * sizeof(ULONG))));
492 Esp[0] = 0xdeadbeef;
493 Esp[1] = (ULONG)NormalRoutine;
494 Esp[2] = (ULONG)NormalContext;
495 Esp[3] = (ULONG)SystemArgument1;
496 Esp[4] = (ULONG)SystemArgument2;
497 Esp[5] = (ULONG)Context;
498 TrapFrame->Eip = (ULONG)LdrpGetSystemDllApcDispatcher();
499 TrapFrame->Esp = (ULONG)Esp;
500 }
501
502 /*
503 * @implemented
504 */
505 BOOLEAN
506 STDCALL
507 KeAreApcsDisabled(
508 VOID
509 )
510 {
511 return KeGetCurrentThread()->KernelApcDisable ? TRUE : FALSE;
512 }
513
514 NTSTATUS
515 STDCALL
516 NtQueueApcThread(HANDLE ThreadHandle,
517 PKNORMAL_ROUTINE ApcRoutine,
518 PVOID NormalContext,
519 PVOID SystemArgument1,
520 PVOID SystemArgument2)
521 /*
522 * FUNCTION:
523 * This function is used to queue an APC from user-mode for the specified thread.
524 * The thread must enter an alertable wait before the APC will be delivered.
525 *
526 * ARGUMENTS:
527 * Thread Handle - Handle to the Thread. This handle must have THREAD_SET_CONTEXT privileges.
528 * ApcRoutine - Pointer to the APC Routine to call when the APC executes.
529 * NormalContext - User-defined value to pass to the APC Routine
530 * SystemArgument1 - User-defined value to pass to the APC Routine
531 * SystemArgument2 - User-defined value to pass to the APC Routine
532 *
533 * RETURNS: NTSTATUS SUCCESS or Failure Code from included calls.
534 */
535 {
536
537 PKAPC Apc;
538 PETHREAD Thread;
539 NTSTATUS Status;
540
541 /* Get ETHREAD from Handle */
542 Status = ObReferenceObjectByHandle(ThreadHandle,
543 THREAD_SET_CONTEXT,
544 PsThreadType,
545 KeGetPreviousMode(),
546 (PVOID)&Thread,
547 NULL);
548
549 /* Fail if the Handle is invalid for some reason */
550 if (!NT_SUCCESS(Status)) {
551 return(Status);
552 }
553
554 /* If this is a Kernel or System Thread, then fail */
555 if (Thread->Tcb.Teb == NULL) {
556 ObDereferenceObject(Thread);
557 return STATUS_INVALID_HANDLE;
558 }
559
560 /* Allocate an APC */
561 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_KAPC);
562 if (Apc == NULL) {
563 ObDereferenceObject(Thread);
564 return(STATUS_NO_MEMORY);
565 }
566
567 /* Initialize and Queue */
568 KeInitializeApc(Apc,
569 &Thread->Tcb,
570 OriginalApcEnvironment,
571 KiFreeApcRoutine,
572 NULL,
573 ApcRoutine,
574 UserMode,
575 NormalContext);
576 if (!KeInsertQueueApc(Apc, SystemArgument1, SystemArgument2, IO_NO_INCREMENT)) {
577 Status = STATUS_UNSUCCESSFUL;
578 } else {
579 Status = STATUS_SUCCESS;
580 }
581
582 /* Dereference Thread and Return */
583 ObDereferenceObject(Thread);
584 return Status;
585 }
586
587
588 static inline VOID RepairList(PLIST_ENTRY Original,
589 PLIST_ENTRY Copy,
590 KPROCESSOR_MODE Mode)
591 {
592 /* Copy Source to Desination */
593 if (IsListEmpty(&Original[(int)Mode])) {
594 InitializeListHead(&Copy[(int)Mode]);
595 } else {
596 Copy[(int)Mode].Flink = Original[(int)Mode].Flink;
597 Copy[(int)Mode].Blink = Original[(int)Mode].Blink;
598 Original[(int)Mode].Flink->Blink = &Copy[(int)Mode];
599 Original[(int)Mode].Blink->Flink = &Copy[(int)Mode];
600 }
601 }
602
603 VOID
604 STDCALL
605 KiMoveApcState (PKAPC_STATE OldState,
606 PKAPC_STATE NewState)
607 {
608 /* Restore backup of Original Environment */
609 *NewState = *OldState;
610
611 /* Repair Lists */
612 RepairList(NewState->ApcListHead, OldState->ApcListHead, KernelMode);
613 RepairList(NewState->ApcListHead, OldState->ApcListHead, UserMode);
614 }
615