xref: /reactos/ntoskrnl/ke/i386/irqobj.c (revision 8a978a17)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ke/i386/irqobj.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 FORCEINLINE
133 DECLSPEC_NORETURN
134 VOID
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         /* It was, disable interrupts and restore the IRQL */
143         _disable();
144         HalEndSystemInterrupt(OldIrql, TrapFrame);
145     }
146 
147     /* Now exit the trap */
148     KiEoiHelper(TrapFrame);
149 }
150 
151 DECLSPEC_NORETURN
152 VOID
153 __cdecl
154 KiUnexpectedInterrupt(VOID)
155 {
156     /* Crash the machine */
157     KeBugCheck(TRAP_CAUSE_UNKNOWN);
158 }
159 
160 VOID
161 FASTCALL
162 KiUnexpectedInterruptTailHandler(IN PKTRAP_FRAME TrapFrame)
163 {
164     KIRQL OldIrql;
165 
166     /* Enter trap */
167     KiEnterInterruptTrap(TrapFrame);
168 
169     /* Increase interrupt count */
170     KeGetCurrentPrcb()->InterruptCount++;
171 
172     /* Start the interrupt */
173     if (HalBeginSystemInterrupt(HIGH_LEVEL, TrapFrame->ErrCode, &OldIrql))
174     {
175         /* Warn user */
176         DPRINT1("\n\x7\x7!!! Unexpected Interrupt 0x%02lx !!!\n", TrapFrame->ErrCode);
177 
178         /* Now call the epilogue code */
179         KiExitInterrupt(TrapFrame, OldIrql, FALSE);
180     }
181     else
182     {
183         /* Now call the epilogue code */
184         KiExitInterrupt(TrapFrame, OldIrql, TRUE);
185     }
186 }
187 
188 typedef
189 VOID
190 (FASTCALL *PKI_INTERRUPT_DISPATCH)(
191     IN PKTRAP_FRAME TrapFrame,
192     IN PKINTERRUPT Interrupt
193 );
194 
195 VOID
196 FASTCALL
197 KiInterruptDispatch(IN PKTRAP_FRAME TrapFrame,
198                     IN PKINTERRUPT Interrupt)
199 {
200     KIRQL OldIrql;
201 
202     /* Increase interrupt count */
203     KeGetCurrentPrcb()->InterruptCount++;
204 
205     /* Begin the interrupt, making sure it's not spurious */
206     if (HalBeginSystemInterrupt(Interrupt->SynchronizeIrql,
207                                 Interrupt->Vector,
208                                 &OldIrql))
209     {
210         /* Acquire interrupt lock */
211         KxAcquireSpinLock(Interrupt->ActualLock);
212 
213         /* Call the ISR */
214         Interrupt->ServiceRoutine(Interrupt, Interrupt->ServiceContext);
215 
216         /* Release interrupt lock */
217         KxReleaseSpinLock(Interrupt->ActualLock);
218 
219         /* Now call the epilogue code */
220         KiExitInterrupt(TrapFrame, OldIrql, FALSE);
221     }
222     else
223     {
224         /* Now call the epilogue code */
225         KiExitInterrupt(TrapFrame, OldIrql, TRUE);
226     }
227 }
228 
229 VOID
230 FASTCALL
231 KiChainedDispatch(IN PKTRAP_FRAME TrapFrame,
232                   IN PKINTERRUPT Interrupt)
233 {
234     KIRQL OldIrql, OldInterruptIrql = 0;
235     BOOLEAN Handled;
236     PLIST_ENTRY NextEntry, ListHead;
237 
238     /* Increase interrupt count */
239     KeGetCurrentPrcb()->InterruptCount++;
240 
241     /* Begin the interrupt, making sure it's not spurious */
242     if (HalBeginSystemInterrupt(Interrupt->Irql,
243                                 Interrupt->Vector,
244                                 &OldIrql))
245     {
246         /* Get list pointers */
247         ListHead = &Interrupt->InterruptListEntry;
248         NextEntry = ListHead; /* The head is an entry! */
249         while (TRUE)
250         {
251             /* Check if this interrupt's IRQL is higher than the current one */
252             if (Interrupt->SynchronizeIrql > Interrupt->Irql)
253             {
254                 /* Raise to higher IRQL */
255                 OldInterruptIrql = KfRaiseIrql(Interrupt->SynchronizeIrql);
256             }
257 
258             /* Acquire interrupt lock */
259             KxAcquireSpinLock(Interrupt->ActualLock);
260 
261             /* Call the ISR */
262             Handled = Interrupt->ServiceRoutine(Interrupt,
263                                                 Interrupt->ServiceContext);
264 
265             /* Release interrupt lock */
266             KxReleaseSpinLock(Interrupt->ActualLock);
267 
268             /* Check if this interrupt's IRQL is higher than the current one */
269             if (Interrupt->SynchronizeIrql > Interrupt->Irql)
270             {
271                 /* Lower the IRQL back */
272                 ASSERT(OldInterruptIrql == Interrupt->Irql);
273                 KfLowerIrql(OldInterruptIrql);
274             }
275 
276             /* Check if the interrupt got handled and it's level */
277             if ((Handled) && (Interrupt->Mode == LevelSensitive)) break;
278 
279             /* What's next? */
280             NextEntry = NextEntry->Flink;
281 
282             /* Is this the last one? */
283             if (NextEntry == ListHead)
284             {
285                 /* Level should not have gotten here */
286                 if (Interrupt->Mode == LevelSensitive) break;
287 
288                 /* As for edge, we can only exit once nobody can handle the interrupt */
289                 if (!Handled) break;
290             }
291 
292             /* Get the interrupt object for the next pass */
293             Interrupt = CONTAINING_RECORD(NextEntry, KINTERRUPT, InterruptListEntry);
294         }
295 
296         /* Now call the epilogue code */
297         KiExitInterrupt(TrapFrame, OldIrql, FALSE);
298     }
299     else
300     {
301         /* Now call the epilogue code */
302         KiExitInterrupt(TrapFrame, OldIrql, TRUE);
303     }
304 }
305 
306 VOID
307 FASTCALL
308 KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame,
309                            IN PKINTERRUPT Interrupt)
310 {
311     /* Enter interrupt frame */
312     KiEnterInterruptTrap(TrapFrame);
313 
314     /* Call the correct dispatcher */
315     ((PKI_INTERRUPT_DISPATCH)Interrupt->DispatchAddress)(TrapFrame, Interrupt);
316 }
317 
318 
319 /* PUBLIC FUNCTIONS **********************************************************/
320 
321 /*
322  * @implemented
323  */
324 VOID
325 NTAPI
326 KeInitializeInterrupt(IN PKINTERRUPT Interrupt,
327                       IN PKSERVICE_ROUTINE ServiceRoutine,
328                       IN PVOID ServiceContext,
329                       IN PKSPIN_LOCK SpinLock,
330                       IN ULONG Vector,
331                       IN KIRQL Irql,
332                       IN KIRQL SynchronizeIrql,
333                       IN KINTERRUPT_MODE InterruptMode,
334                       IN BOOLEAN ShareVector,
335                       IN CHAR ProcessorNumber,
336                       IN BOOLEAN FloatingSave)
337 {
338     ULONG i;
339     PULONG DispatchCode = &Interrupt->DispatchCode[0], Patch = DispatchCode;
340 
341     /* Set the Interrupt Header */
342     Interrupt->Type = InterruptObject;
343     Interrupt->Size = sizeof(KINTERRUPT);
344 
345     /* Check if we got a spinlock */
346     if (SpinLock)
347     {
348         Interrupt->ActualLock = SpinLock;
349     }
350     else
351     {
352         /* This means we'll be usin the built-in one */
353         KeInitializeSpinLock(&Interrupt->SpinLock);
354         Interrupt->ActualLock = &Interrupt->SpinLock;
355     }
356 
357     /* Set the other settings */
358     Interrupt->ServiceRoutine = ServiceRoutine;
359     Interrupt->ServiceContext = ServiceContext;
360     Interrupt->Vector = Vector;
361     Interrupt->Irql = Irql;
362     Interrupt->SynchronizeIrql = SynchronizeIrql;
363     Interrupt->Mode = InterruptMode;
364     Interrupt->ShareVector = ShareVector;
365     Interrupt->Number = ProcessorNumber;
366     Interrupt->FloatingSave = FloatingSave;
367     Interrupt->TickCount = MAXULONG;
368     Interrupt->DispatchCount = MAXULONG;
369 
370     /* Loop the template in memory */
371     for (i = 0; i < DISPATCH_LENGTH; i++)
372     {
373         /* Copy the dispatch code */
374         *DispatchCode++ = ((PULONG)KiInterruptTemplate)[i];
375     }
376 
377     /* Jump to the last 4 bytes */
378     Patch = (PULONG)((ULONG_PTR)Patch +
379                      ((ULONG_PTR)&KiInterruptTemplateObject -
380                       (ULONG_PTR)KiInterruptTemplate) - 4);
381 
382     /* Apply the patch */
383     *Patch = PtrToUlong(Interrupt);
384 
385     /* Disconnect it at first */
386     Interrupt->Connected = FALSE;
387 }
388 
389 /*
390  * @implemented
391  */
392 BOOLEAN
393 NTAPI
394 KeConnectInterrupt(IN PKINTERRUPT Interrupt)
395 {
396     BOOLEAN Connected, Error, Status;
397     KIRQL Irql, OldIrql;
398     UCHAR Number;
399     ULONG Vector;
400     DISPATCH_INFO Dispatch;
401 
402     /* Get data from interrupt */
403     Number = Interrupt->Number;
404     Vector = Interrupt->Vector;
405     Irql = Interrupt->Irql;
406 
407     /* Validate the settings */
408     if ((Irql > HIGH_LEVEL) ||
409         (Number >= KeNumberProcessors) ||
410         (Interrupt->SynchronizeIrql < Irql) ||
411         (Interrupt->FloatingSave))
412     {
413         return FALSE;
414     }
415 
416     /* Set defaults */
417     Connected = FALSE;
418     Error = FALSE;
419 
420     /* Set the system affinity and acquire the dispatcher lock */
421     KeSetSystemAffinityThread(1 << Number);
422     OldIrql = KiAcquireDispatcherLock();
423 
424     /* Check if it's already been connected */
425     if (!Interrupt->Connected)
426     {
427         /* Get vector dispatching information */
428         KiGetVectorDispatch(Vector, &Dispatch);
429 
430         /* Check if the vector is already connected */
431         if (Dispatch.Type == NoConnect)
432         {
433             /* Do the connection */
434             Interrupt->Connected = Connected = TRUE;
435 
436             /* Initialize the list */
437             InitializeListHead(&Interrupt->InterruptListEntry);
438 
439             /* Connect and enable the interrupt */
440             KiConnectVectorToInterrupt(Interrupt, NormalConnect);
441             Status = HalEnableSystemInterrupt(Vector, Irql, Interrupt->Mode);
442             if (!Status) Error = TRUE;
443         }
444         else if ((Dispatch.Type != UnknownConnect) &&
445                 (Interrupt->ShareVector) &&
446                 (Dispatch.Interrupt->ShareVector) &&
447                 (Dispatch.Interrupt->Mode == Interrupt->Mode))
448         {
449             /* The vector is shared and the interrupts are compatible */
450             Interrupt->Connected = Connected = TRUE;
451 
452             /*
453              * Verify the IRQL for chained connect,
454              */
455 #if defined(CONFIG_SMP)
456             ASSERT(Irql <= SYNCH_LEVEL);
457 #else
458             ASSERT(Irql <= (IPI_LEVEL - 2));
459 #endif
460 
461             /* Check if this is the first chain */
462             if (Dispatch.Type != ChainConnect)
463             {
464                 /* This is not supported */
465                 ASSERT(Dispatch.Interrupt->Mode != Latched);
466 
467                 /* Setup the chainned handler */
468                 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
469             }
470 
471             /* Insert into the interrupt list */
472             InsertTailList(&Dispatch.Interrupt->InterruptListEntry,
473                            &Interrupt->InterruptListEntry);
474         }
475     }
476 
477     /* Unlock the dispatcher and revert affinity */
478     KiReleaseDispatcherLock(OldIrql);
479     KeRevertToUserAffinityThread();
480 
481     /* Check if we failed while trying to connect */
482     if ((Connected) && (Error))
483     {
484         DPRINT1("HalEnableSystemInterrupt failed\n");
485         KeDisconnectInterrupt(Interrupt);
486         Connected = FALSE;
487     }
488 
489     /* Return to caller */
490     return Connected;
491 }
492 
493 /*
494  * @implemented
495  */
496 BOOLEAN
497 NTAPI
498 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt)
499 {
500     KIRQL OldIrql, Irql;
501     ULONG Vector;
502     DISPATCH_INFO Dispatch;
503     PKINTERRUPT NextInterrupt;
504     BOOLEAN State;
505 
506     /* Set the affinity */
507     KeSetSystemAffinityThread(1 << Interrupt->Number);
508 
509     /* Lock the dispatcher */
510     OldIrql = KiAcquireDispatcherLock();
511 
512     /* Check if it's actually connected */
513     State = Interrupt->Connected;
514     if (State)
515     {
516         /* Get the vector and IRQL */
517         Irql = Interrupt->Irql;
518         Vector = Interrupt->Vector;
519 
520         /* Get vector dispatch data */
521         KiGetVectorDispatch(Vector, &Dispatch);
522 
523         /* Check if it was chained */
524         if (Dispatch.Type == ChainConnect)
525         {
526             /* Check if the top-level interrupt is being removed */
527 #if defined(CONFIG_SMP)
528             ASSERT(Irql <= SYNCH_LEVEL);
529 #else
530             ASSERT(Irql <= (IPI_LEVEL - 2));
531 #endif
532             if (Interrupt == Dispatch.Interrupt)
533             {
534                 /* Get the next one */
535                 Dispatch.Interrupt = CONTAINING_RECORD(Dispatch.Interrupt->
536                                                        InterruptListEntry.Flink,
537                                                        KINTERRUPT,
538                                                        InterruptListEntry);
539 
540                 /* Reconnect it */
541                 KiConnectVectorToInterrupt(Dispatch.Interrupt, ChainConnect);
542             }
543 
544             /* Remove it */
545             RemoveEntryList(&Interrupt->InterruptListEntry);
546 
547             /* Get the next one */
548             NextInterrupt = CONTAINING_RECORD(Dispatch.Interrupt->
549                                               InterruptListEntry.Flink,
550                                               KINTERRUPT,
551                                               InterruptListEntry);
552 
553             /* Check if this is the only one left */
554             if (Dispatch.Interrupt == NextInterrupt)
555             {
556                 /* Connect it in non-chained mode */
557                 KiConnectVectorToInterrupt(Dispatch.Interrupt, NormalConnect);
558             }
559         }
560         else
561         {
562             /* Only one left, disable and remove it */
563             HalDisableSystemInterrupt(Interrupt->Vector, Irql);
564             KiConnectVectorToInterrupt(Interrupt, NoConnect);
565         }
566 
567         /* Disconnect it */
568         Interrupt->Connected = FALSE;
569     }
570 
571     /* Unlock the dispatcher and revert affinity */
572     KiReleaseDispatcherLock(OldIrql);
573     KeRevertToUserAffinityThread();
574 
575     /* Return to caller */
576     return State;
577 }
578 
579 /*
580  * @implemented
581  */
582 BOOLEAN
583 NTAPI
584 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt,
585                        IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine,
586                        IN PVOID SynchronizeContext OPTIONAL)
587 {
588     BOOLEAN Success;
589     KIRQL OldIrql;
590 
591     /* Raise IRQL */
592     KeRaiseIrql(Interrupt->SynchronizeIrql,
593                 &OldIrql);
594 
595     /* Acquire interrupt spinlock */
596     KeAcquireSpinLockAtDpcLevel(Interrupt->ActualLock);
597 
598     /* Call the routine */
599     Success = SynchronizeRoutine(SynchronizeContext);
600 
601     /* Release lock */
602     KeReleaseSpinLockFromDpcLevel(Interrupt->ActualLock);
603 
604     /* Lower IRQL */
605     KeLowerIrql(OldIrql);
606 
607     /* Return status */
608     return Success;
609 }
610 
611 /* EOF */
612