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)
11 /* INCLUDES *****************************************************************/
13 #include <ddk/ntddk.h>
15 #include <internal/string.h>
16 #include <internal/i386/segment.h>
17 #include <internal/ps.h>
20 #include <internal/debug.h>
22 extern VOID
KeApcProlog(VOID
);
24 /* FUNCTIONS *****************************************************************/
26 BOOLEAN
KiTestAlert(PKTHREAD Thread
,
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.
32 * Thread = Thread to test for alerts
33 * UserContext = The user context saved on entry to kernel mode
36 PLIST_ENTRY current_entry
;
38 PULONG Esp
= (PULONG
)UserContext
->Esp
;
40 current_entry
= Thread
->ApcState
.ApcListHead
[1].Flink
;
41 while (current_entry
!= &Thread
->ApcState
.ApcListHead
[1])
43 Apc
= CONTAINING_RECORD(current_entry
, KAPC
, ApcListEntry
);
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
;
52 current_entry
= current_entry
->Flink
;
54 UserContext
->Esp
= (ULONG
)Esp
;
58 VOID
KeApcProlog2(PKAPC Apc
)
60 * FUNCTION: This is called from the prolog proper (in assembly) to deliver
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
,
72 &Apc
->SystemArgument2
,
73 &Apc
->SystemArgument2
);
74 Apc
->Thread
->ApcState
.KernelApcInProgress
++;
75 KeLeaveCriticalRegion();
76 PsSuspendThread(CONTAINING_RECORD(Apc
->Thread
,ETHREAD
,Tcb
));
79 VOID
KeDeliverKernelApc(PKAPC Apc
)
81 * FUNCTION: Simulates an interrupt on the target thread which will transfer
82 * control to a kernel mode routine
85 PKTHREAD TargetThread
;
88 DPRINT("KeDeliverKernelApc(Apc %x)\n", Apc
);
90 TargetThread
= Apc
->Thread
;
92 if (TargetThread
== KeGetCurrentThread())
94 Apc
->KernelRoutine(Apc
,
97 &Apc
->SystemArgument2
,
98 &Apc
->SystemArgument2
);
102 if (TargetThread
->Context
.cs
== KERNEL_CS
)
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
;
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
;
131 PsResumeThread(CONTAINING_RECORD(TargetThread
,ETHREAD
,Tcb
));
134 VOID
KeInsertQueueApc(PKAPC Apc
,
135 PVOID SystemArgument1
,
136 PVOID SystemArgument2
,
139 * FUNCTION: Queues an APC for execution
141 * Apc = APC to be queued
142 * SystemArgument[1-2] = TBD
147 PKTHREAD TargetThread
;
149 DPRINT("KeInsertQueueApc(Apc %x, SystemArgument1 %x, "
150 "SystemArgument2 %x, Mode %d)\n",Apc
,SystemArgument1
,
151 SystemArgument2
,Mode
);
153 KeRaiseIrql(DISPATCH_LEVEL
, &oldlvl
);
157 DbgPrint("KeInsertQueueApc(): multiple APC insertations\n");
161 TargetThread
= Apc
->Thread
;
162 if (Apc
->ApcMode
== KernelMode
)
164 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[0],
166 TargetThread
->ApcState
.KernelApcPending
++;
170 InsertTailList(&TargetThread
->ApcState
.ApcListHead
[1],
172 TargetThread
->ApcState
.UserApcPending
++;
174 Apc
->Inserted
= TRUE
;
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)
181 KeDeliverKernelApc(Apc
);
185 DPRINT("Queuing APC for later delivery\n");
190 VOID
KeInitializeApc(PKAPC Apc
,
193 PKKERNEL_ROUTINE KernelRoutine
,
194 PKRUNDOWN_ROUTINE RundownRoutine
,
195 PKNORMAL_ROUTINE NormalRoutine
,
199 * FUNCTION: Initialize an APC object
201 * Apc = Pointer to the APC object to initialized
202 * Thread = Thread the APC is to be delivered to
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
209 * Context = Parameter to be passed to the APC routine
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
;
234 PKNORMAL_ROUTINE ApcRoutine
,
236 PVOID SystemArgument1
,
237 PVOID SystemArgument2
244 Status
= ObReferenceObjectByHandle(ThreadHandle
,
245 THREAD_ALL_ACCESS
, /* FIXME */
250 if (!NT_SUCCESS(Status
))
255 Apc
= ExAllocatePool(NonPagedPool
, sizeof(KAPC
));
258 ObDereferenceObject(Thread
);
259 return(STATUS_NO_MEMORY
);
270 KeInsertQueueApc(Apc
,
275 ObDereferenceObject(Thread
);
276 return(STATUS_SUCCESS
);
285 KeGetCurrentThread(),
288 return(STATUS_SUCCESS
);