Merge from amd64-branch:
[reactos.git] / reactos / ntoskrnl / ke / i386 / irqobj.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ke/i386/irq.c
5 * PURPOSE: Manages the Kernel's IRQ support for external drivers,
6 * for the purposes of connecting, disconnecting and setting
7 * up ISRs for drivers. The backend behind the Io* Interrupt
8 * routines.
9 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ntoskrnl.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 /* GLOBALS *******************************************************************/
19
20 ULONG KiISRTimeout = 55;
21 USHORT KiISROverflow = 30000;
22 extern ULONG NTAPI KiChainedDispatch2ndLvl(VOID);
23
24 /* PRIVATE FUNCTIONS *********************************************************/
25
26 VOID
27 NTAPI
28 KiGetVectorDispatch(IN ULONG Vector,
29 IN PDISPATCH_INFO Dispatch)
30 {
31 PKINTERRUPT_ROUTINE Handler;
32 PVOID Current;
33 UCHAR Type;
34 UCHAR Entry;
35
36 /* Check if this is a primary or 2nd-level dispatch */
37 Type = HalSystemVectorDispatchEntry(Vector,
38 &Dispatch->FlatDispatch,
39 &Dispatch->NoDispatch);
40 ASSERT(Type == 0);
41
42 /* Get the IDT entry for this vector */
43 Entry = HalVectorToIDTEntry(Vector);
44
45 /* Setup the unhandled dispatch */
46 Dispatch->NoDispatch = (PVOID)(((ULONG_PTR)&KiStartUnexpectedRange) +
47 (Entry - PRIMARY_VECTOR_BASE) *
48 KiUnexpectedEntrySize);
49
50 /* Setup the handlers */
51 Dispatch->InterruptDispatch = (PVOID)KiInterruptDispatch;
52 Dispatch->FloatingDispatch = NULL; // Floating Interrupts are not supported
53 Dispatch->ChainedDispatch = (PVOID)KiChainedDispatch;
54 Dispatch->FlatDispatch = NULL;
55
56 /* Get the current handler */
57 Current = KeQueryInterruptHandler(Vector);
58
59 /* Set the interrupt */
60 Dispatch->Interrupt = CONTAINING_RECORD(Current,
61 KINTERRUPT,
62 DispatchCode);
63
64 /* Check what this interrupt is connected to */
65 if ((PKINTERRUPT_ROUTINE)Current == Dispatch->NoDispatch)
66 {
67 /* Not connected */
68 Dispatch->Type = NoConnect;
69 }
70 else
71 {
72 /* Get the handler */
73 Handler = Dispatch->Interrupt->DispatchAddress;
74 if (Handler == Dispatch->ChainedDispatch)
75 {
76 /* It's a chained interrupt */
77 Dispatch->Type = ChainConnect;
78 }
79 else if ((Handler == Dispatch->InterruptDispatch) ||
80 (Handler == Dispatch->FloatingDispatch))
81 {
82 /* It's unchained */
83 Dispatch->Type = NormalConnect;
84 }
85 else
86 {
87 /* Unknown */
88 Dispatch->Type = UnknownConnect;
89 }
90 }
91 }
92
93 VOID
94 NTAPI
95 KiConnectVectorToInterrupt(IN PKINTERRUPT Interrupt,
96 IN CONNECT_TYPE Type)
97 {
98 DISPATCH_INFO Dispatch;
99 PKINTERRUPT_ROUTINE Handler;
100 #ifndef HAL_INTERRUPT_SUPPORT_IN_C
101 PULONG Patch = &Interrupt->DispatchCode[0];
102 #endif
103
104 /* Get vector data */
105 KiGetVectorDispatch(Interrupt->Vector, &Dispatch);
106
107 /* Check if we're only disconnecting */
108 if (Type == NoConnect)
109 {
110 /* Set the handler to NoDispatch */
111 Handler = Dispatch.NoDispatch;
112 }
113 else
114 {
115 /* Get the right handler */
116 Handler = (Type == NormalConnect) ?
117 Dispatch.InterruptDispatch:
118 Dispatch.ChainedDispatch;
119 ASSERT(Interrupt->FloatingSave == FALSE);
120
121 /* Set the handler */
122 Interrupt->DispatchAddress = Handler;
123
124 /* Read note in trap.s -- patching not needed since JMP is static */
125 #ifndef HAL_INTERRUPT_SUPPORT_IN_C
126 /* Jump to the last 4 bytes */
127 Patch = (PULONG)((ULONG_PTR)Patch +
128 ((ULONG_PTR)&KiInterruptTemplateDispatch -
129 (ULONG_PTR)KiInterruptTemplate) - 4);
130
131 /* Apply the patch */
132 *Patch = (ULONG)((ULONG_PTR)Handler - ((ULONG_PTR)Patch + 4));
133 #endif
134
135 /* Now set the final handler address */
136 ASSERT(Dispatch.FlatDispatch == NULL);
137 Handler = (PVOID)&Interrupt->DispatchCode;
138 }
139
140 /* Register the interrupt */
141 KeRegisterInterruptHandler(Interrupt->Vector, Handler);
142 }
143
144 /* PUBLIC FUNCTIONS **********************************************************/
145
146 /*
147 * @implemented
148 */
149 VOID
150 NTAPI
151 KeInitializeInterrupt(IN PKINTERRUPT Interrupt,
152 IN PKSERVICE_ROUTINE ServiceRoutine,
153 IN PVOID ServiceContext,
154 IN PKSPIN_LOCK SpinLock,
155 IN ULONG Vector,
156 IN KIRQL Irql,
157 IN KIRQL SynchronizeIrql,
158 IN KINTERRUPT_MODE InterruptMode,
159 IN BOOLEAN ShareVector,
160 IN CHAR ProcessorNumber,
161 IN BOOLEAN FloatingSave)
162 {
163 ULONG i;
164 PULONG DispatchCode = &Interrupt->DispatchCode[0], Patch = DispatchCode;
165
166 /* Set the Interrupt Header */
167 Interrupt->Type = InterruptObject;
168 Interrupt->Size = sizeof(KINTERRUPT);
169
170 /* Check if we got a spinlock */
171 if (SpinLock)
172 {
173 Interrupt->ActualLock = SpinLock;
174 }
175 else
176 {
177 /* This means we'll be usin the built-in one */
178 KeInitializeSpinLock(&Interrupt->SpinLock);
179 Interrupt->ActualLock = &Interrupt->SpinLock;
180 }
181
182 /* Set the other settings */
183 Interrupt->ServiceRoutine = ServiceRoutine;
184 Interrupt->ServiceContext = ServiceContext;
185 Interrupt->Vector = Vector;
186 Interrupt->Irql = Irql;
187 Interrupt->SynchronizeIrql = SynchronizeIrql;
188 Interrupt->Mode = InterruptMode;
189 Interrupt->ShareVector = ShareVector;
190 Interrupt->Number = ProcessorNumber;
191 Interrupt->FloatingSave = FloatingSave;
192 Interrupt->TickCount = MAXULONG;
193 Interrupt->DispatchCount = MAXULONG;
194
195 /* Loop the template in memory */
196 for (i = 0; i < KINTERRUPT_DISPATCH_CODES; i++)
197 {
198 /* Copy the dispatch code */
199 *DispatchCode++ = KiInterruptTemplate[i];
200 }
201
202 /* Sanity check */
203 #ifndef HAL_INTERRUPT_SUPPORT_IN_C
204 ASSERT((ULONG_PTR)&KiChainedDispatch2ndLvl -
205 (ULONG_PTR)KiInterruptTemplate <= (KINTERRUPT_DISPATCH_CODES * 4));
206 #endif
207
208 /* Jump to the last 4 bytes */
209 Patch = (PULONG)((ULONG_PTR)Patch +
210 ((ULONG_PTR)&KiInterruptTemplateObject -
211 (ULONG_PTR)KiInterruptTemplate) - 4);
212
213 /* Apply the patch */
214 *Patch = PtrToUlong(Interrupt);
215
216 /* Disconnect it at first */
217 Interrupt->Connected = FALSE;
218 }
219
220 /*
221 * @implemented
222 */
223 BOOLEAN
224 NTAPI
225 KeConnectInterrupt(IN PKINTERRUPT Interrupt)
226 {
227 BOOLEAN Connected, Error, Status;
228 KIRQL Irql, OldIrql;
229 UCHAR Number;
230 ULONG Vector;
231 DISPATCH_INFO Dispatch;
232
233 /* Get data from interrupt */
234 Number = Interrupt->Number;
235 Vector = Interrupt->Vector;
236 Irql = Interrupt->Irql;
237
238 /* Validate the settings */
239 if ((Irql > HIGH_LEVEL) ||
240 (Number >= KeNumberProcessors) ||
241 (Interrupt->SynchronizeIrql < Irql) ||
242 (Interrupt->FloatingSave))
243 {
244 return FALSE;
245 }
246
247 /* Set defaults */
248 Connected = FALSE;
249 Error = FALSE;
250
251 /* Set the system affinity and acquire the dispatcher lock */
252 KeSetSystemAffinityThread(1 << Number);
253 OldIrql = KiAcquireDispatcherLock();
254
255 /* Check if it's already been connected */
256 if (!Interrupt->Connected)
257 {
258 /* Get vector dispatching information */
259 KiGetVectorDispatch(Vector, &Dispatch);
260
261 /* Check if the vector is already connected */
262 if (Dispatch.Type == NoConnect)
263 {
264 /* Do the connection */
265 Interrupt->Connected = Connected = TRUE;
266
267 /* Initialize the list */
268 InitializeListHead(&Interrupt->InterruptListEntry);
269
270 /* Connect and enable the interrupt */
271 KiConnectVectorToInterrupt(Interrupt, NormalConnect);
272 Status = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
273 if (!Status) Error = TRUE;
274 }
275 else if ((Dispatch.Type != UnknownConnect) &&
276 (Interrupt->ShareVector) &&
277 (Dispatch.Interrupt->ShareVector) &&
278 (Dispatch.Interrupt->Mode == Interrupt->Mode))
279 {
280 /* The vector is shared and the interrupts are compatible */
281 Interrupt->Connected = Connected = TRUE;
282
283 /* FIXME */
284 // ASSERT(Irql <= SYNCH_LEVEL);
285
286 /* Check if this is the first chain */
287 if (Dispatch.Type != ChainConnect)
288 {
289 /* This is not supported */
290 ASSERT(Dispatch.Interrupt->Mode != Latched);
291
292 /* Setup the chainned handler */
293 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
294 }
295
296 /* Insert into the interrupt list */
297 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
298 &Interrupt->InterruptListEntry);
299 }
300 }
301
302 /* Unlock the dispatcher and revert affinity */
303 KiReleaseDispatcherLock(OldIrql);
304 KeRevertToUserAffinityThread();
305
306 /* Check if we failed while trying to connect */
307 if ((Connected) && (Error))
308 {
309 DPRINT1("HalEnableSystemInterrupt failed\n");
310 KeDisconnectInterrupt(Interrupt);
311 Connected = FALSE;
312 }
313
314 /* Return to caller */
315 return Connected;
316 }
317
318 /*
319 * @implemented
320 */
321 BOOLEAN
322 NTAPI
323 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
324 {
325 KIRQL OldIrql, Irql;
326 ULONG Vector;
327 DISPATCH_INFO Dispatch;
328 PKINTERRUPT NextInterrupt;
329 BOOLEAN State;
330
331 /* Set the affinity */
332 KeSetSystemAffinityThread(1 << Interrupt->Number);
333
334 /* Lock the dispatcher */
335 OldIrql = KiAcquireDispatcherLock();
336
337 /* Check if it's actually connected */
338 State = Interrupt->Connected;
339 if (State)
340 {
341 /* Get the vector and IRQL */
342 Irql = Interrupt->Irql;
343 Vector = Interrupt->Vector;
344
345 /* Get vector dispatch data */
346 KiGetVectorDispatch(Vector, &Dispatch);
347
348 /* Check if it was chained */
349 if (Dispatch.Type == ChainConnect)
350 {
351 /* Check if the top-level interrupt is being removed */
352 ASSERT(Irql <= SYNCH_LEVEL);
353 if (Interrupt == Dispatch.Interrupt)
354 {
355 /* Get the next one */
356 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
357 InterruptListEntry.Flink,
358 KINTERRUPT,
359 InterruptListEntry);
360
361 /* Reconnect it */
362 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
363 }
364
365 /* Remove it */
366 RemoveEntryList(&Interrupt->InterruptListEntry);
367
368 /* Get the next one */
369 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
370 InterruptListEntry.Flink,
371 KINTERRUPT,
372 InterruptListEntry);
373
374 /* Check if this is the only one left */
375 if (Dispatch.Interrupt == NextInterrupt)
376 {
377 /* Connect it in non-chained mode */
378 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
379 }
380 }
381 else
382 {
383 /* Only one left, disable and remove it */
384 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
385 KiConnectVectorToInterrupt(Interrupt, NoConnect);
386 }
387
388 /* Disconnect it */
389 Interrupt->Connected = FALSE;
390 }
391
392 /* Unlock the dispatcher and revert affinity */
393 KiReleaseDispatcherLock(OldIrql);
394 KeRevertToUserAffinityThread();
395
396 /* Return to caller */
397 return State;
398 }
399
400 /*
401 * @implemented
402 */
403 BOOLEAN
404 NTAPI
405 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,
406 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
407 IN PVOID SynchronizeContext OPTIONAL)
408 {
409 NTSTATUS Status;
410 KIRQL OldIrql;
411
412 /* Raise IRQL */
413 OldIrql = KfRaiseIrql(Interrupt->SynchronizeIrql);
414
415 /* Acquire interrupt spinlock */
416 KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
417
418 /* Call the routine */
419 Status = SynchronizeRoutine(SynchronizeContext);
420
421 /* Release lock */
422 KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
423
424 /* Lower IRQL */
425 KfLowerIrql(OldIrql);
426
427 /* Return status */
428 return Status;
429 }
430
431 /* EOF */