Some 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: 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 #include <internal/ke.h>
20
21 #define NDEBUG
22 #include <internal/debug.h>
23
24 /* GLOBALS *******************************************************************/
25
26 static KSPIN_LOCK PiApcLock;
27
28 /* FUNCTIONS *****************************************************************/
29
30 VOID KeCallKernelRoutineApc(PKAPC Apc)
31 /*
32 * FUNCTION: Call the kernel routine for an APC
33 */
34 {
35 DPRINT("KeCallKernelRoutineApc(Apc %x)\n",Apc);
36
37 Apc->KernelRoutine(Apc,
38 &Apc->NormalRoutine,
39 &Apc->NormalContext,
40 &Apc->SystemArgument1,
41 &Apc->SystemArgument2);
42 DPRINT("Finished KeCallKernelRoutineApc()\n");
43 }
44
45 BOOLEAN KiTestAlert(PKTHREAD Thread,
46 PCONTEXT UserContext)
47 /*
48 * FUNCTION: Tests whether there are any pending APCs for the current thread
49 * and if so the APCs will be delivered on exit from kernel mode.
50 * ARGUMENTS:
51 * Thread = Thread to test for alerts
52 * UserContext = The user context saved on entry to kernel mode
53 */
54 {
55 PLIST_ENTRY current_entry;
56 PKAPC Apc;
57 PULONG Esp;
58 KIRQL oldlvl;
59 CONTEXT SavedContext;
60 ULONG Top;
61
62 DPRINT("KiTestAlert(Thread %x, UserContext %x)\n");
63 KeAcquireSpinLock(&PiApcLock, &oldlvl);
64 current_entry = Thread->ApcState.ApcListHead[1].Flink;
65
66 if (current_entry == &Thread->ApcState.ApcListHead[1])
67 {
68 KeReleaseSpinLock(&PiApcLock, oldlvl);
69 return(FALSE);
70 }
71
72 current_entry = RemoveHeadList(&Thread->ApcState.ApcListHead[1]);
73 Apc = CONTAINING_RECORD(current_entry, KAPC, ApcListEntry);
74
75 DPRINT("Esp %x\n", Esp);
76 DPRINT("Apc->NormalContext %x\n", Apc->NormalContext);
77 DPRINT("Apc->SystemArgument1 %x\n", Apc->SystemArgument1);
78 DPRINT("Apc->SystemArgument2 %x\n", Apc->SystemArgument2);
79 DPRINT("UserContext->Eip %x\n", UserContext->Eip);
80
81 Esp = (PULONG)UserContext->Esp;
82
83 memcpy(&SavedContext, UserContext, sizeof(CONTEXT));
84
85 /*
86 * Now call for the kernel routine for the APC, which will free
87 * the APC data structure
88 */
89 KeCallKernelRoutineApc(Apc);
90
91 Esp = Esp - (sizeof(CONTEXT) + (4 * sizeof(ULONG)));
92 memcpy(Esp, &SavedContext, sizeof(CONTEXT));
93 Top = sizeof(CONTEXT) / 4;
94 Esp[Top] = (ULONG)Apc->SystemArgument2;
95 Esp[Top + 1] = (ULONG)Apc->SystemArgument1;
96 Esp[Top + 2] = (ULONG)Apc->NormalContext;
97 Esp[Top + 3] = (ULONG)Apc->NormalRoutine;
98 UserContext->Eip = 0;
99
100 current_entry = current_entry->Flink;
101
102
103 return(TRUE);
104 }
105
106 VOID KeCallApcsThread(VOID)
107 {
108 PETHREAD Thread = PsGetCurrentThread();
109 PLIST_ENTRY current;
110 PKAPC Apc;
111 KIRQL oldlvl;
112
113 DPRINT("KeCallApcsThread()\n");
114 KeAcquireSpinLock(&PiApcLock, &oldlvl);
115 while(!IsListEmpty(&(Thread->Tcb.ApcState.ApcListHead[0])))
116 {
117 DPRINT("Delivering APC\n");
118 current = RemoveTailList(&(Thread->Tcb.ApcState.ApcListHead[0]));
119 Thread->Tcb.ApcState.KernelApcInProgress++;
120 Thread->Tcb.ApcState.KernelApcPending--;
121 KeReleaseSpinLock(&PiApcLock, oldlvl);
122
123 Apc = CONTAINING_RECORD(current, KAPC, ApcListEntry);
124 KeCallKernelRoutineApc(Apc);
125
126 KeAcquireSpinLock(&PiApcLock, &oldlvl);
127 DPRINT("Called kernel routine for APC\n");
128 // PsFreezeThread(Thread, NULL, FALSE, KernelMode);
129 DPRINT("Done frozen thread\n");
130 Thread->Tcb.ApcState.KernelApcInProgress--;
131 }
132 KeReleaseSpinLock(&PiApcLock, oldlvl);
133 // Thread->Tcb.WaitStatus = STATUS_KERNEL_APC;
134 }
135
136 VOID KeInsertQueueApc(PKAPC Apc,
137 PVOID SystemArgument1,
138 PVOID SystemArgument2,
139 UCHAR Mode)
140 /*
141 * FUNCTION: Queues an APC for execution
142 * ARGUMENTS:
143 * Apc = APC to be queued
144 * SystemArgument[1-2] = TBD
145 * Mode = TBD
146 */
147 {
148 KIRQL oldlvl;
149 PKTHREAD TargetThread;
150
151 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
152 "SystemArgument2 %x, Mode %d)\n",Apc,SystemArgument1,
153 SystemArgument2,Mode);
154
155 KeAcquireSpinLock(&PiApcLock, &oldlvl);
156
157 Apc->SystemArgument1 = SystemArgument1;
158 Apc->SystemArgument2 = SystemArgument2;
159
160 if (Apc->Inserted)
161 {
162 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
163 KeBugCheck(0);
164 }
165
166 TargetThread = Apc->Thread;
167 if (Apc->ApcMode == KernelMode)
168 {
169 InsertTailList(&TargetThread->ApcState.ApcListHead[0],
170 &Apc->ApcListEntry);
171 TargetThread->ApcState.KernelApcPending++;
172 }
173 else
174 {
175 InsertTailList(&TargetThread->ApcState.ApcListHead[1],
176 &Apc->ApcListEntry);
177 TargetThread->ApcState.KernelApcPending++;
178 TargetThread->ApcState.UserApcPending++;
179 }
180 Apc->Inserted = TRUE;
181
182 if (Apc->ApcMode == KernelMode && TargetThread->KernelApcDisable >= 1 &&
183 TargetThread->WaitIrql < APC_LEVEL)
184 {
185 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
186 STATUS_KERNEL_APC);
187 KeReleaseSpinLock(&PiApcLock, oldlvl);
188 }
189 if (Apc->ApcMode == UserMode && TargetThread->Alertable == TRUE &&
190 TargetThread->WaitMode == UserMode)
191 {
192 NTSTATUS Status;
193
194 DPRINT("Resuming thread for user APC\n");
195
196 Status = STATUS_USER_APC;
197 KeRemoveAllWaitsThread(CONTAINING_RECORD(TargetThread, ETHREAD, Tcb),
198 STATUS_USER_APC);
199 KeReleaseSpinLock(&PiApcLock, oldlvl);
200 }
201 }
202
203 VOID KeInitializeApc(PKAPC Apc,
204 PKTHREAD Thread,
205 UCHAR StateIndex,
206 PKKERNEL_ROUTINE KernelRoutine,
207 PKRUNDOWN_ROUTINE RundownRoutine,
208 PKNORMAL_ROUTINE NormalRoutine,
209 UCHAR Mode,
210 PVOID Context)
211 /*
212 * FUNCTION: Initialize an APC object
213 * ARGUMENTS:
214 * Apc = Pointer to the APC object to initialized
215 * Thread = Thread the APC is to be delivered to
216 * StateIndex = TBD
217 * KernelRoutine = Routine to be called for a kernel-mode APC
218 * RundownRoutine = Routine to be called if the thread has exited with
219 * the APC being executed
220 * NormalRoutine = Routine to be called for a user-mode APC
221 * Mode = APC mode
222 * Context = Parameter to be passed to the APC routine
223 */
224 {
225 DPRINT("KeInitializeApc(Apc %x, Thread %x, StateIndex %d, "
226 "KernelRoutine %x, RundownRoutine %x, NormalRoutine %x, Mode %d, "
227 "Context %x)\n",Apc,Thread,StateIndex,KernelRoutine,RundownRoutine,
228 NormalRoutine,Mode,Context);
229 memset(Apc, 0, sizeof(KAPC));
230 Apc->Thread = Thread;
231 Apc->ApcListEntry.Flink = NULL;
232 Apc->ApcListEntry.Blink = NULL;
233 Apc->KernelRoutine = KernelRoutine;
234 Apc->RundownRoutine = RundownRoutine;
235 Apc->NormalRoutine = NormalRoutine;
236 Apc->NormalContext = Context;
237 Apc->Inserted = FALSE;
238 Apc->ApcStateIndex = StateIndex;
239 if (Apc->NormalRoutine != NULL)
240 {
241 Apc->ApcMode = Mode;
242 }
243 else
244 {
245 Apc->ApcMode = KernelMode;
246 }
247 }
248
249
250 NTSTATUS STDCALL NtQueueApcThread(HANDLE ThreadHandle,
251 PKNORMAL_ROUTINE ApcRoutine,
252 PVOID NormalContext,
253 PVOID SystemArgument1,
254 PVOID SystemArgument2)
255 {
256 PKAPC Apc;
257 PETHREAD Thread;
258 NTSTATUS Status;
259
260 Status = ObReferenceObjectByHandle(ThreadHandle,
261 THREAD_ALL_ACCESS, /* FIXME */
262 PsThreadType,
263 UserMode,
264 (PVOID*)&Thread,
265 NULL);
266 if (!NT_SUCCESS(Status))
267 {
268 return(Status);
269 }
270
271 Apc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
272 if (Apc == NULL)
273 {
274 ObDereferenceObject(Thread);
275 return(STATUS_NO_MEMORY);
276 }
277
278 KeInitializeApc(Apc,
279 &Thread->Tcb,
280 0,
281 NULL,
282 NULL,
283 ApcRoutine,
284 UserMode,
285 NormalContext);
286 KeInsertQueueApc(Apc,
287 SystemArgument1,
288 SystemArgument2,
289 UserMode);
290
291 ObDereferenceObject(Thread);
292 return(STATUS_SUCCESS);
293 }
294
295
296 NTSTATUS STDCALL NtTestAlert(VOID)
297 {
298 KiTestAlert(KeGetCurrentThread(),NULL);
299 return(STATUS_SUCCESS);
300 }
301
302 VOID PiInitApcManagement(VOID)
303 {
304 KeInitializeSpinLock(&PiApcLock);
305 }
306