reworked the APC code, they shold now be delivered properly when the IRQL falls below...
[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: Possible implementation of APCs
6 * PROGRAMMER: David Welch (welch@cwcom.net)
7 * UPDATE HISTORY:
8 * Created 22/05/98
9 * 12/11/99: Phillip Susi: Reworked the APC code
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <string.h>
16 #include <internal/string.h>
17 #include <internal/i386/segment.h>
18 #include <internal/ps.h>
19
20 #define NDEBUG
21 #include <internal/debug.h>
22
23 extern VOID KeApcProlog(VOID);
24 static KSPIN_LOCK PiApcLock;
25
26 /* PROTOTYPES ****************************************************************/
27
28 VOID KeApcProlog3(PKAPC Apc);
29
30 /* FUNCTIONS *****************************************************************/
31
32 BOOLEAN KiTestAlert(PKTHREAD Thread,
33 PCONTEXT UserContext)
34 /*
35 * FUNCTION: Tests whether there are any pending APCs for the current thread
36 * and if so the APCs will be delivered on exit from kernel mode.
37 * ARGUMENTS:
38 * Thread = Thread to test for alerts
39 * UserContext = The user context saved on entry to kernel mode
40 */
41 {
42 PLIST_ENTRY current_entry;
43 PKAPC Apc;
44 PULONG Esp = (PULONG)UserContext->Esp;
45 KIRQL oldlvl;
46
47 DPRINT("KiTestAlert(Thread %x, UserContext %x)\n");
48 KeAcquireSpinLock( &PiApcLock, &oldlvl );
49 current_entry = Thread->ApcState.ApcListHead[1].Flink;
50
51 if (current_entry == &Thread->ApcState.ApcListHead[1])
52 {
53 KeReleaseSpinLock( &PiApcLock, oldlvl );
54 return(FALSE);
55 }
56
57 while (current_entry != &Thread->ApcState.ApcListHead[1])
58 {
59 Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
60
61 DPRINT("Esp %x\n", Esp);
62 DPRINT("Apc->NormalContext %x\n", Apc->NormalContext);
63 DPRINT("Apc->SystemArgument1 %x\n", Apc->SystemArgument1);
64 DPRINT("Apc->SystemArgument2 %x\n", Apc->SystemArgument2);
65 DPRINT("UserContext->Eip %x\n", UserContext->Eip);
66
67 Esp = Esp - 16;
68 Esp[3] = (ULONG)Apc->SystemArgument2;
69 Esp[2] = (ULONG)Apc->SystemArgument1;
70 Esp[1] = (ULONG)Apc->NormalContext;
71 Esp[0] = UserContext->Eip;
72 UserContext->Eip = (ULONG)Apc->NormalRoutine;
73
74 current_entry = current_entry->Flink;
75
76 /*
77 * Now call for the kernel routine for the APC, which will free
78 * the APC data structure
79 */
80 KeApcProlog3(Apc);
81 }
82 UserContext->Esp = (ULONG)Esp;
83 InitializeListHead(&Thread->ApcState.ApcListHead[1]);
84 return(TRUE);
85 }
86
87 VOID KeApcProlog2()
88 {
89 PETHREAD Thread = PsGetCurrentThread();
90 PLIST_ENTRY current;
91 PKAPC Apc;
92 KIRQL oldlvl;
93
94 KeLowerIrql( APC_LEVEL );
95 KeAcquireSpinLock( &PiApcLock, &oldlvl );
96 while( !IsListEmpty( current ) )
97 {
98 current = RemoveTailList( &(Thread->Tcb.ApcState.ApcListHead[0]) );
99 KeReleaseSpinLock( &PiApcLock, oldlvl );
100 Apc = CONTAINING_RECORD(current, KAPC, ApcListEntry);
101 KeApcProlog3(Apc);
102 KeAcquireSpinLock( &PiApcLock, &oldlvl );
103 }
104 KeReleaseSpinLock( &PiApcLock, oldlvl );
105 Thread->Tcb.WaitStatus = STATUS_ALERTED;
106 KeLowerIrql( PASSIVE_LEVEL );
107 }
108
109 VOID KeApcProlog3(PKAPC Apc)
110 /*
111 * FUNCTION: This is called from the prolog proper (in assembly) to deliver
112 * a kernel APC
113 */
114 {
115 PKTHREAD Thread = KeGetCurrentThread();
116 DPRINT("KeApcProlog2(Apc %x)\n",Apc);
117 InterlockedIncrement( &(Thread->ApcState.KernelApcInProgress) );
118 InterlockedDecrement( &(Thread->ApcState.KernelApcPending) );
119
120 Apc->KernelRoutine(Apc,
121 &Apc->NormalRoutine,
122 &Apc->NormalContext,
123 &Apc->SystemArgument1,
124 &Apc->SystemArgument2);
125 InterlockedDecrement( &(Thread->ApcState.KernelApcInProgress) );
126 }
127
128 VOID KeDeliverKernelApc( PKTHREAD TargetThread )
129 /*
130 * FUNCTION: Simulates an interrupt on the target thread which will transfer
131 * control to a kernel mode routine
132 * Must be called while holding the PiApcLock
133 */
134 {
135 PULONG Stack;
136
137 DPRINT("KeDeliverKernelApc(Apc %x)\n", Apc);
138 if (TargetThread == KeGetCurrentThread())
139 {
140 KeApcProlog2();
141 return;
142 }
143
144 TargetThread->Context.esp = TargetThread->Context.esp - 12;
145 Stack = (PULONG)TargetThread->Context.esp;
146 Stack[0] = TargetThread->Context.eip;
147 Stack[1] = TargetThread->Context.cs;
148 Stack[2] = TargetThread->Context.eflags;
149 TargetThread->Context.eip = (ULONG)KeApcProlog;
150
151 PsResumeThread(CONTAINING_RECORD(TargetThread,ETHREAD,Tcb),
152 NULL);
153 }
154
155 VOID KeInsertQueueApc(PKAPC Apc,
156 PVOID SystemArgument1,
157 PVOID SystemArgument2,
158 UCHAR Mode)
159 /*
160 * FUNCTION: Queues an APC for execution
161 * ARGUMENTS:
162 * Apc = APC to be queued
163 * SystemArgument[1-2] = TBD
164 * Mode = TBD
165 */
166 {
167 KIRQL oldlvl;
168 PKTHREAD TargetThread;
169
170 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
171 "SystemArgument2 %x, Mode %d)\n",Apc,SystemArgument1,
172 SystemArgument2,Mode);
173
174 KeAcquireSpinLock( &PiApcLock, &oldlvl );
175
176 Apc->SystemArgument1 = SystemArgument1;
177 Apc->SystemArgument2 = SystemArgument2;
178
179 if (Apc->Inserted)
180 {
181 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
182 KeBugCheck(0);
183 }
184
185 TargetThread = Apc->Thread;
186 if (Apc->ApcMode == KernelMode)
187 {
188 InsertTailList(&TargetThread->ApcState.ApcListHead[0],
189 &Apc->ApcListEntry);
190 TargetThread->ApcState.KernelApcPending++;
191 }
192 else
193 {
194 InsertTailList(&TargetThread->ApcState.ApcListHead[1],
195 &Apc->ApcListEntry);
196 TargetThread->ApcState.KernelApcPending++;
197 TargetThread->ApcState.UserApcPending++;
198 }
199 Apc->Inserted = TRUE;
200
201 DPRINT("TargetThread->KernelApcDisable %d\n",
202 TargetThread->KernelApcDisable);
203 DPRINT("Apc->KernelRoutine %x\n", Apc->KernelRoutine);
204 if (Apc->ApcMode == KernelMode && TargetThread->KernelApcDisable >= 1 )
205 if( TargetThread != PsGetCurrentThread() )
206 {
207 PsSuspendThread( CONTAINING_RECORD( TargetThread, ETHREAD, Tcb ), NULL, TRUE, KernelMode );
208 KeReleaseSpinLock( &PiApcLock, oldlvl );
209 if( TargetThread->Alertable && TargetThread->WaitIrql < APC_LEVEL )
210 KeDeliverKernelApc( TargetThread );
211 }
212 else if( TargetThread->Alertable && TargetThread->WaitIrql < APC_LEVEL )
213 KeDeliverKernelApc( TargetThread );
214 else
215 {
216 DPRINT("Queuing APC for later delivery\n");
217 }
218 if (Apc->ApcMode == UserMode && TargetThread->Alertable == TRUE &&
219 TargetThread->WaitMode == UserMode)
220 {
221 NTSTATUS Status;
222
223 DPRINT("Resuming thread for user APC\n");
224
225 Status = STATUS_USER_APC;
226 PsResumeThread((PETHREAD)TargetThread,
227 &Status);
228 }
229 }
230
231 VOID KeInitializeApc(PKAPC Apc,
232 PKTHREAD Thread,
233 UCHAR StateIndex,
234 PKKERNEL_ROUTINE KernelRoutine,
235 PKRUNDOWN_ROUTINE RundownRoutine,
236 PKNORMAL_ROUTINE NormalRoutine,
237 UCHAR Mode,
238 PVOID Context)
239 /*
240 * FUNCTION: Initialize an APC object
241 * ARGUMENTS:
242 * Apc = Pointer to the APC object to initialized
243 * Thread = Thread the APC is to be delivered to
244 * StateIndex = TBD
245 * KernelRoutine = Routine to be called for a kernel-mode APC
246 * RundownRoutine = Routine to be called if the thread has exited with
247 * the APC being executed
248 * NormalRoutine = Routine to be called for a user-mode APC
249 * Mode = APC mode
250 * Context = Parameter to be passed to the APC routine
251 */
252 {
253 DPRINT("KeInitializeApc(Apc %x, Thread %x, StateIndex %d, "
254 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
255 "Context %x)\n",Apc,Thread,StateIndex,KernelRoutine,RundownRoutine,
256 NormalRoutine,Mode,Context);
257 memset(Apc, 0, sizeof(KAPC));
258 Apc->Thread = Thread;
259 Apc->ApcListEntry.Flink = NULL;
260 Apc->ApcListEntry.Blink = NULL;
261 Apc->KernelRoutine = KernelRoutine;
262 Apc->RundownRoutine = RundownRoutine;
263 Apc->NormalRoutine = NormalRoutine;
264 Apc->NormalContext = Context;
265 Apc->Inserted = FALSE;
266 Apc->ApcStateIndex = StateIndex;
267 if (Apc->NormalRoutine != NULL)
268 {
269 Apc->ApcMode = Mode;
270 }
271 else
272 {
273 Apc->ApcMode = KernelMode;
274 }
275 }
276
277
278 NTSTATUS STDCALL NtQueueApcThread(HANDLE ThreadHandle,
279 PKNORMAL_ROUTINE ApcRoutine,
280 PVOID NormalContext,
281 PVOID SystemArgument1,
282 PVOID SystemArgument2)
283 {
284 PKAPC Apc;
285 PETHREAD Thread;
286 NTSTATUS Status;
287
288 Status = ObReferenceObjectByHandle(ThreadHandle,
289 THREAD_ALL_ACCESS, /* FIXME */
290 PsThreadType,
291 UserMode,
292 (PVOID*)&Thread,
293 NULL);
294 if (!NT_SUCCESS(Status))
295 {
296 return(Status);
297 }
298
299 Apc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
300 if (Apc == NULL)
301 {
302 ObDereferenceObject(Thread);
303 return(STATUS_NO_MEMORY);
304 }
305
306 KeInitializeApc(Apc,
307 &Thread->Tcb,
308 0,
309 NULL,
310 NULL,
311 ApcRoutine,
312 UserMode,
313 NormalContext);
314 KeInsertQueueApc(Apc,
315 SystemArgument1,
316 SystemArgument2,
317 UserMode);
318
319 ObDereferenceObject(Thread);
320 return(STATUS_SUCCESS);
321 }
322
323
324 NTSTATUS STDCALL NtTestAlert(VOID)
325 {
326 KiTestAlert(KeGetCurrentThread(),NULL);
327 return(STATUS_SUCCESS);
328 }
329
330 VOID PiInitApcManagement()
331 {
332 KeInitializeSpinLock( &PiApcLock );
333 }
334