b58b1dac951bccc9e684d28171ecf17fae6dd8b8
[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
101 /* Get vector data */
102 KiGetVectorDispatch(Interrupt->Vector, &Dispatch);
103
104 /* Check if we're only disconnecting */
105 if (Type == NoConnect)
106 {
107 /* Set the handler to NoDispatch */
108 Handler = Dispatch.NoDispatch;
109 }
110 else
111 {
112 /* Get the right handler */
113 Handler = (Type == NormalConnect) ?
114 Dispatch.InterruptDispatch:
115 Dispatch.ChainedDispatch;
116 ASSERT(Interrupt->FloatingSave == FALSE);
117
118 /* Set the handler */
119 Interrupt->DispatchAddress = Handler;
120
121 /* Read note in trap.s -- patching not needed since JMP is static */
122
123 /* Now set the final handler address */
124 ASSERT(Dispatch.FlatDispatch == NULL);
125 Handler = (PVOID)&Interrupt->DispatchCode;
126 }
127
128 /* Register the interrupt */
129 KeRegisterInterruptHandler(Interrupt->Vector, Handler);
130 }
131
132 VOID
133 FORCEINLINE
134 DECLSPEC_NORETURN
135 KiExitInterrupt(IN PKTRAP_FRAME TrapFrame,
136 IN KIRQL OldIrql,
137 IN BOOLEAN Spurious)
138 {
139 /* Check if this was a real interrupt */
140 if (!Spurious)
141 {
142 /* Set nested trap frame */
143 KeGetPcr()->VdmAlert = (ULONG_PTR)TrapFrame;
144
145 /* It was, disable interrupts and restore the IRQL */
146 _disable();
147 HalEndSystemInterrupt(OldIrql, 0);
148 }
149
150 /* Now exit the trap */
151 KiEoiHelper(TrapFrame);
152 }
153
154 VOID
155 KiUnexpectedInterrupt(VOID)
156 {
157 /* Crash the machine */
158 KeBugCheck(TRAP_CAUSE_UNKNOWN);
159 }
160
161 VOID
162 FASTCALL
163 KiUnexpectedInterruptTailHandler(IN PKTRAP_FRAME TrapFrame)
164 {
165 KIRQL OldIrql;
166
167 /* Enter trap */
168 KiEnterInterruptTrap(TrapFrame);
169
170 /* Increase interrupt count */
171 KeGetCurrentPrcb()->InterruptCount++;
172
173 /* Start the interrupt */
174 if (HalBeginSystemInterrupt(HIGH_LEVEL, TrapFrame->Eax, &OldIrql))
175 {
176 /* Warn user */
177 DPRINT1("\n\x7\x7!!! Unexpected Interrupt %02lx !!!\n");
178
179 /* Now call the epilogue code */
180 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
181 }
182 else
183 {
184 /* Now call the epilogue code */
185 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
186 }
187 }
188
189 typedef
190 FASTCALL
191 VOID
192 (PKI_INTERRUPT_DISPATCH)(
193 IN PKTRAP_FRAME TrapFrame,
194 IN PKINTERRUPT Interrupt
195 );
196
197 VOID
198 FASTCALL
199 KiInterruptDispatch(IN PKTRAP_FRAME TrapFrame,
200 IN PKINTERRUPT Interrupt)
201 {
202 KIRQL OldIrql;
203
204 /* Increase interrupt count */
205 KeGetCurrentPrcb()->InterruptCount++;
206
207 /* Begin the interrupt, making sure it's not spurious */
208 if (HalBeginSystemInterrupt(Interrupt->SynchronizeIrql,
209 Interrupt->Vector,
210 &OldIrql))
211 {
212 /* Acquire interrupt lock */
213 KxAcquireSpinLock(Interrupt->ActualLock);
214
215 /* Call the ISR */
216 Interrupt->ServiceRoutine(Interrupt, Interrupt->ServiceContext);
217
218 /* Release interrupt lock */
219 KxReleaseSpinLock(Interrupt->ActualLock);
220
221 /* Now call the epilogue code */
222 KiExitInterrupt(TrapFrame, OldIrql, FALSE);
223 }
224 else
225 {
226 /* Now call the epilogue code */
227 KiExitInterrupt(TrapFrame, OldIrql, TRUE);
228 }
229 }
230
231 VOID
232 FASTCALL
233 KiChainedDispatch(IN PKTRAP_FRAME TrapFrame,
234 IN PKINTERRUPT Interrupt)
235 {
236 /* Increase interrupt count */
237 KeGetCurrentPrcb()->InterruptCount++;
238 UNIMPLEMENTED;
239 while (TRUE);
240 }
241
242 VOID
243 FASTCALL
244 KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame,
245 IN PKINTERRUPT Interrupt)
246 {
247 /* Enter interrupt frame */
248 KiEnterInterruptTrap(TrapFrame);
249
250 /* Call the correct dispatcher */
251 ((PKI_INTERRUPT_DISPATCH*)Interrupt->DispatchAddress)(TrapFrame, Interrupt);
252 }
253
254 KiTrap(KiInterruptTemplate, KI_PUSH_FAKE_ERROR_CODE | KI_HARDWARE_INT);
255 KiTrap(KiUnexpectedInterruptTail, KI_PUSH_FAKE_ERROR_CODE);
256
257 /* PUBLIC FUNCTIONS **********************************************************/
258
259 /*
260 * @implemented
261 */
262 VOID
263 NTAPI
264 KeInitializeInterrupt(IN PKINTERRUPT Interrupt,
265 IN PKSERVICE_ROUTINE ServiceRoutine,
266 IN PVOID ServiceContext,
267 IN PKSPIN_LOCK SpinLock,
268 IN ULONG Vector,
269 IN KIRQL Irql,
270 IN KIRQL SynchronizeIrql,
271 IN KINTERRUPT_MODE InterruptMode,
272 IN BOOLEAN ShareVector,
273 IN CHAR ProcessorNumber,
274 IN BOOLEAN FloatingSave)
275 {
276 ULONG i;
277 PULONG DispatchCode = &Interrupt->DispatchCode[0], Patch = DispatchCode;
278
279 /* Set the Interrupt Header */
280 Interrupt->Type = InterruptObject;
281 Interrupt->Size = sizeof(KINTERRUPT);
282
283 /* Check if we got a spinlock */
284 if (SpinLock)
285 {
286 Interrupt->ActualLock = SpinLock;
287 }
288 else
289 {
290 /* This means we'll be usin the built-in one */
291 KeInitializeSpinLock(&Interrupt->SpinLock);
292 Interrupt->ActualLock = &Interrupt->SpinLock;
293 }
294
295 /* Set the other settings */
296 Interrupt->ServiceRoutine = ServiceRoutine;
297 Interrupt->ServiceContext = ServiceContext;
298 Interrupt->Vector = Vector;
299 Interrupt->Irql = Irql;
300 Interrupt->SynchronizeIrql = SynchronizeIrql;
301 Interrupt->Mode = InterruptMode;
302 Interrupt->ShareVector = ShareVector;
303 Interrupt->Number = ProcessorNumber;
304 Interrupt->FloatingSave = FloatingSave;
305 Interrupt->TickCount = MAXULONG;
306 Interrupt->DispatchCount = MAXULONG;
307
308 /* Loop the template in memory */
309 for (i = 0; i < KINTERRUPT_DISPATCH_CODES; i++)
310 {
311 /* Copy the dispatch code */
312 *DispatchCode++ = ((PULONG)KiInterruptTemplate)[i];
313 }
314
315 /* Jump to the last 4 bytes */
316 Patch = (PULONG)((ULONG_PTR)Patch +
317 ((ULONG_PTR)&KiInterruptTemplateObject -
318 (ULONG_PTR)KiInterruptTemplate) - 4);
319
320 /* Apply the patch */
321 *Patch = PtrToUlong(Interrupt);
322
323 /* Disconnect it at first */
324 Interrupt->Connected = FALSE;
325 }
326
327 /*
328 * @implemented
329 */
330 BOOLEAN
331 NTAPI
332 KeConnectInterrupt(IN PKINTERRUPT Interrupt)
333 {
334 BOOLEAN Connected, Error, Status;
335 KIRQL Irql, OldIrql;
336 UCHAR Number;
337 ULONG Vector;
338 DISPATCH_INFO Dispatch;
339
340 /* Get data from interrupt */
341 Number = Interrupt->Number;
342 Vector = Interrupt->Vector;
343 Irql = Interrupt->Irql;
344
345 /* Validate the settings */
346 if ((Irql > HIGH_LEVEL) ||
347 (Number >= KeNumberProcessors) ||
348 (Interrupt->SynchronizeIrql < Irql) ||
349 (Interrupt->FloatingSave))
350 {
351 return FALSE;
352 }
353
354 /* Set defaults */
355 Connected = FALSE;
356 Error = FALSE;
357
358 /* Set the system affinity and acquire the dispatcher lock */
359 KeSetSystemAffinityThread(1 << Number);
360 OldIrql = KiAcquireDispatcherLock();
361
362 /* Check if it's already been connected */
363 if (!Interrupt->Connected)
364 {
365 /* Get vector dispatching information */
366 KiGetVectorDispatch(Vector, &Dispatch);
367
368 /* Check if the vector is already connected */
369 if (Dispatch.Type == NoConnect)
370 {
371 /* Do the connection */
372 Interrupt->Connected = Connected = TRUE;
373
374 /* Initialize the list */
375 InitializeListHead(&Interrupt->InterruptListEntry);
376
377 /* Connect and enable the interrupt */
378 KiConnectVectorToInterrupt(Interrupt, NormalConnect);
379 Status = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
380 if (!Status) Error = TRUE;
381 }
382 else if ((Dispatch.Type != UnknownConnect) &&
383 (Interrupt->ShareVector) &&
384 (Dispatch.Interrupt->ShareVector) &&
385 (Dispatch.Interrupt->Mode == Interrupt->Mode))
386 {
387 /* The vector is shared and the interrupts are compatible */
388 Interrupt->Connected = Connected = TRUE;
389
390 /* FIXME */
391 // ASSERT(Irql <= SYNCH_LEVEL);
392
393 /* Check if this is the first chain */
394 if (Dispatch.Type != ChainConnect)
395 {
396 /* This is not supported */
397 ASSERT(Dispatch.Interrupt->Mode != Latched);
398
399 /* Setup the chainned handler */
400 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
401 }
402
403 /* Insert into the interrupt list */
404 InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
405 &Interrupt->InterruptListEntry);
406 }
407 }
408
409 /* Unlock the dispatcher and revert affinity */
410 KiReleaseDispatcherLock(OldIrql);
411 KeRevertToUserAffinityThread();
412
413 /* Check if we failed while trying to connect */
414 if ((Connected) && (Error))
415 {
416 DPRINT1("HalEnableSystemInterrupt failed\n");
417 KeDisconnectInterrupt(Interrupt);
418 Connected = FALSE;
419 }
420
421 /* Return to caller */
422 return Connected;
423 }
424
425 /*
426 * @implemented
427 */
428 BOOLEAN
429 NTAPI
430 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
431 {
432 KIRQL OldIrql, Irql;
433 ULONG Vector;
434 DISPATCH_INFO Dispatch;
435 PKINTERRUPT NextInterrupt;
436 BOOLEAN State;
437
438 /* Set the affinity */
439 KeSetSystemAffinityThread(1 << Interrupt->Number);
440
441 /* Lock the dispatcher */
442 OldIrql = KiAcquireDispatcherLock();
443
444 /* Check if it's actually connected */
445 State = Interrupt->Connected;
446 if (State)
447 {
448 /* Get the vector and IRQL */
449 Irql = Interrupt->Irql;
450 Vector = Interrupt->Vector;
451
452 /* Get vector dispatch data */
453 KiGetVectorDispatch(Vector, &Dispatch);
454
455 /* Check if it was chained */
456 if (Dispatch.Type == ChainConnect)
457 {
458 /* Check if the top-level interrupt is being removed */
459 ASSERT(Irql <= SYNCH_LEVEL);
460 if (Interrupt == Dispatch.Interrupt)
461 {
462 /* Get the next one */
463 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
464 InterruptListEntry.Flink,
465 KINTERRUPT,
466 InterruptListEntry);
467
468 /* Reconnect it */
469 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
470 }
471
472 /* Remove it */
473 RemoveEntryList(&Interrupt->InterruptListEntry);
474
475 /* Get the next one */
476 NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
477 InterruptListEntry.Flink,
478 KINTERRUPT,
479 InterruptListEntry);
480
481 /* Check if this is the only one left */
482 if (Dispatch.Interrupt == NextInterrupt)
483 {
484 /* Connect it in non-chained mode */
485 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
486 }
487 }
488 else
489 {
490 /* Only one left, disable and remove it */
491 HalDisableSystemInterrupt(Interrupt->Vector, Irql);
492 KiConnectVectorToInterrupt(Interrupt, NoConnect);
493 }
494
495 /* Disconnect it */
496 Interrupt->Connected = FALSE;
497 }
498
499 /* Unlock the dispatcher and revert affinity */
500 KiReleaseDispatcherLock(OldIrql);
501 KeRevertToUserAffinityThread();
502
503 /* Return to caller */
504 return State;
505 }
506
507 /*
508 * @implemented
509 */
510 BOOLEAN
511 NTAPI
512 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,
513 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
514 IN PVOID SynchronizeContext OPTIONAL)
515 {
516 NTSTATUS Status;
517 KIRQL OldIrql;
518
519 /* Raise IRQL */
520 OldIrql = KfRaiseIrql(Interrupt->SynchronizeIrql);
521
522 /* Acquire interrupt spinlock */
523 KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
524
525 /* Call the routine */
526 Status = SynchronizeRoutine(SynchronizeContext);
527
528 /* Release lock */
529 KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
530
531 /* Lower IRQL */
532 KfLowerIrql(OldIrql);
533
534 /* Return status */
535 return Status;
536 }
537
538 /* EOF */