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