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
9 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
12 /* INCLUDES *****************************************************************/
18 /* GLOBALS *******************************************************************/
20 ULONG KiISRTimeout
= 55;
21 USHORT KiISROverflow
= 30000;
22 extern ULONG NTAPI
KiChainedDispatch2ndLvl(VOID
);
24 /* PRIVATE FUNCTIONS *********************************************************/
28 KiGetVectorDispatch(IN ULONG Vector
,
29 IN PDISPATCH_INFO Dispatch
)
31 PKINTERRUPT_ROUTINE Handler
;
36 /* Check if this is a primary or 2nd-level dispatch */
37 Type
= HalSystemVectorDispatchEntry(Vector
,
38 &Dispatch
->FlatDispatch
,
39 &Dispatch
->NoDispatch
);
42 /* Get the IDT entry for this vector */
43 Entry
= HalVectorToIDTEntry(Vector
);
45 /* Setup the unhandled dispatch */
46 Dispatch
->NoDispatch
= (PVOID
)(((ULONG_PTR
)&KiStartUnexpectedRange
) +
47 (Entry
- PRIMARY_VECTOR_BASE
) *
48 KiUnexpectedEntrySize
);
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
;
56 /* Get the current handler */
57 Current
= KeQueryInterruptHandler(Vector
);
59 /* Set the interrupt */
60 Dispatch
->Interrupt
= CONTAINING_RECORD(Current
,
64 /* Check what this interrupt is connected to */
65 if ((PKINTERRUPT_ROUTINE
)Current
== Dispatch
->NoDispatch
)
68 Dispatch
->Type
= NoConnect
;
73 Handler
= Dispatch
->Interrupt
->DispatchAddress
;
74 if (Handler
== Dispatch
->ChainedDispatch
)
76 /* It's a chained interrupt */
77 Dispatch
->Type
= ChainConnect
;
79 else if ((Handler
== Dispatch
->InterruptDispatch
) ||
80 (Handler
== Dispatch
->FloatingDispatch
))
83 Dispatch
->Type
= NormalConnect
;
88 Dispatch
->Type
= UnknownConnect
;
95 KiConnectVectorToInterrupt(IN PKINTERRUPT Interrupt
,
98 DISPATCH_INFO Dispatch
;
99 PKINTERRUPT_ROUTINE Handler
;
101 /* Get vector data */
102 KiGetVectorDispatch(Interrupt
->Vector
, &Dispatch
);
104 /* Check if we're only disconnecting */
105 if (Type
== NoConnect
)
107 /* Set the handler to NoDispatch */
108 Handler
= Dispatch
.NoDispatch
;
112 /* Get the right handler */
113 Handler
= (Type
== NormalConnect
) ?
114 Dispatch
.InterruptDispatch
:
115 Dispatch
.ChainedDispatch
;
116 ASSERT(Interrupt
->FloatingSave
== FALSE
);
118 /* Set the handler */
119 Interrupt
->DispatchAddress
= Handler
;
121 /* Read note in trap.s -- patching not needed since JMP is static */
123 /* Now set the final handler address */
124 ASSERT(Dispatch
.FlatDispatch
== NULL
);
125 Handler
= (PVOID
)&Interrupt
->DispatchCode
;
128 /* Register the interrupt */
129 KeRegisterInterruptHandler(Interrupt
->Vector
, Handler
);
135 KiExitInterrupt(IN PKTRAP_FRAME TrapFrame
,
139 /* Check if this was a real interrupt */
142 /* It was, disable interrupts and restore the IRQL */
144 HalEndSystemInterrupt(OldIrql
, TrapFrame
);
147 /* Now exit the trap */
148 KiEoiHelper(TrapFrame
);
152 KiUnexpectedInterrupt(VOID
)
154 /* Crash the machine */
155 KeBugCheck(TRAP_CAUSE_UNKNOWN
);
160 KiUnexpectedInterruptTailHandler(IN PKTRAP_FRAME TrapFrame
)
165 KiEnterInterruptTrap(TrapFrame
);
167 /* Increase interrupt count */
168 KeGetCurrentPrcb()->InterruptCount
++;
170 /* Start the interrupt */
171 if (HalBeginSystemInterrupt(HIGH_LEVEL
, TrapFrame
->ErrCode
, &OldIrql
))
174 DPRINT1("\n\x7\x7!!! Unexpected Interrupt 0x%02lx !!!\n", TrapFrame
->ErrCode
);
176 /* Now call the epilogue code */
177 KiExitInterrupt(TrapFrame
, OldIrql
, FALSE
);
181 /* Now call the epilogue code */
182 KiExitInterrupt(TrapFrame
, OldIrql
, TRUE
);
188 (FASTCALL
*PKI_INTERRUPT_DISPATCH
)(
189 IN PKTRAP_FRAME TrapFrame
,
190 IN PKINTERRUPT Interrupt
195 KiInterruptDispatch(IN PKTRAP_FRAME TrapFrame
,
196 IN PKINTERRUPT Interrupt
)
200 /* Increase interrupt count */
201 KeGetCurrentPrcb()->InterruptCount
++;
203 /* Begin the interrupt, making sure it's not spurious */
204 if (HalBeginSystemInterrupt(Interrupt
->SynchronizeIrql
,
208 /* Acquire interrupt lock */
209 KxAcquireSpinLock(Interrupt
->ActualLock
);
212 Interrupt
->ServiceRoutine(Interrupt
, Interrupt
->ServiceContext
);
214 /* Release interrupt lock */
215 KxReleaseSpinLock(Interrupt
->ActualLock
);
217 /* Now call the epilogue code */
218 KiExitInterrupt(TrapFrame
, OldIrql
, FALSE
);
222 /* Now call the epilogue code */
223 KiExitInterrupt(TrapFrame
, OldIrql
, TRUE
);
229 KiChainedDispatch(IN PKTRAP_FRAME TrapFrame
,
230 IN PKINTERRUPT Interrupt
)
232 KIRQL OldIrql
, OldInterruptIrql
= 0;
234 PLIST_ENTRY NextEntry
, ListHead
;
236 /* Increase interrupt count */
237 KeGetCurrentPrcb()->InterruptCount
++;
239 /* Begin the interrupt, making sure it's not spurious */
240 if (HalBeginSystemInterrupt(Interrupt
->Irql
,
244 /* Get list pointers */
245 ListHead
= &Interrupt
->InterruptListEntry
;
246 NextEntry
= ListHead
; /* The head is an entry! */
249 /* Check if this interrupt's IRQL is higher than the current one */
250 if (Interrupt
->SynchronizeIrql
> Interrupt
->Irql
)
252 /* Raise to higher IRQL */
253 OldInterruptIrql
= KfRaiseIrql(Interrupt
->SynchronizeIrql
);
256 /* Acquire interrupt lock */
257 KxAcquireSpinLock(Interrupt
->ActualLock
);
260 Handled
= Interrupt
->ServiceRoutine(Interrupt
,
261 Interrupt
->ServiceContext
);
263 /* Release interrupt lock */
264 KxReleaseSpinLock(Interrupt
->ActualLock
);
266 /* Check if this interrupt's IRQL is higher than the current one */
267 if (Interrupt
->SynchronizeIrql
> Interrupt
->Irql
)
269 /* Lower the IRQL back */
270 ASSERT(OldInterruptIrql
== Interrupt
->Irql
);
271 KfLowerIrql(OldInterruptIrql
);
274 /* Check if the interrupt got handled and it's level */
275 if ((Handled
) && (Interrupt
->Mode
== LevelSensitive
)) break;
278 NextEntry
= NextEntry
->Flink
;
280 /* Is this the last one? */
281 if (NextEntry
== ListHead
)
283 /* Level should not have gotten here */
284 if (Interrupt
->Mode
== LevelSensitive
) break;
286 /* As for edge, we can only exit once nobody can handle the interrupt */
290 /* Get the interrupt object for the next pass */
291 Interrupt
= CONTAINING_RECORD(NextEntry
, KINTERRUPT
, InterruptListEntry
);
294 /* Now call the epilogue code */
295 KiExitInterrupt(TrapFrame
, OldIrql
, FALSE
);
299 /* Now call the epilogue code */
300 KiExitInterrupt(TrapFrame
, OldIrql
, TRUE
);
306 KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame
,
307 IN PKINTERRUPT Interrupt
)
309 /* Enter interrupt frame */
310 KiEnterInterruptTrap(TrapFrame
);
312 /* Call the correct dispatcher */
313 ((PKI_INTERRUPT_DISPATCH
)Interrupt
->DispatchAddress
)(TrapFrame
, Interrupt
);
317 /* PUBLIC FUNCTIONS **********************************************************/
324 KeInitializeInterrupt(IN PKINTERRUPT Interrupt
,
325 IN PKSERVICE_ROUTINE ServiceRoutine
,
326 IN PVOID ServiceContext
,
327 IN PKSPIN_LOCK SpinLock
,
330 IN KIRQL SynchronizeIrql
,
331 IN KINTERRUPT_MODE InterruptMode
,
332 IN BOOLEAN ShareVector
,
333 IN CHAR ProcessorNumber
,
334 IN BOOLEAN FloatingSave
)
337 PULONG DispatchCode
= &Interrupt
->DispatchCode
[0], Patch
= DispatchCode
;
339 /* Set the Interrupt Header */
340 Interrupt
->Type
= InterruptObject
;
341 Interrupt
->Size
= sizeof(KINTERRUPT
);
343 /* Check if we got a spinlock */
346 Interrupt
->ActualLock
= SpinLock
;
350 /* This means we'll be usin the built-in one */
351 KeInitializeSpinLock(&Interrupt
->SpinLock
);
352 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
355 /* Set the other settings */
356 Interrupt
->ServiceRoutine
= ServiceRoutine
;
357 Interrupt
->ServiceContext
= ServiceContext
;
358 Interrupt
->Vector
= Vector
;
359 Interrupt
->Irql
= Irql
;
360 Interrupt
->SynchronizeIrql
= SynchronizeIrql
;
361 Interrupt
->Mode
= InterruptMode
;
362 Interrupt
->ShareVector
= ShareVector
;
363 Interrupt
->Number
= ProcessorNumber
;
364 Interrupt
->FloatingSave
= FloatingSave
;
365 Interrupt
->TickCount
= MAXULONG
;
366 Interrupt
->DispatchCount
= MAXULONG
;
368 /* Loop the template in memory */
369 for (i
= 0; i
< DISPATCH_LENGTH
; i
++)
371 /* Copy the dispatch code */
372 *DispatchCode
++ = ((PULONG
)KiInterruptTemplate
)[i
];
375 /* Jump to the last 4 bytes */
376 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
377 ((ULONG_PTR
)&KiInterruptTemplateObject
-
378 (ULONG_PTR
)KiInterruptTemplate
) - 4);
380 /* Apply the patch */
381 *Patch
= PtrToUlong(Interrupt
);
383 /* Disconnect it at first */
384 Interrupt
->Connected
= FALSE
;
392 KeConnectInterrupt(IN PKINTERRUPT Interrupt
)
394 BOOLEAN Connected
, Error
, Status
;
398 DISPATCH_INFO Dispatch
;
400 /* Get data from interrupt */
401 Number
= Interrupt
->Number
;
402 Vector
= Interrupt
->Vector
;
403 Irql
= Interrupt
->Irql
;
405 /* Validate the settings */
406 if ((Irql
> HIGH_LEVEL
) ||
407 (Number
>= KeNumberProcessors
) ||
408 (Interrupt
->SynchronizeIrql
< Irql
) ||
409 (Interrupt
->FloatingSave
))
418 /* Set the system affinity and acquire the dispatcher lock */
419 KeSetSystemAffinityThread(1 << Number
);
420 OldIrql
= KiAcquireDispatcherLock();
422 /* Check if it's already been connected */
423 if (!Interrupt
->Connected
)
425 /* Get vector dispatching information */
426 KiGetVectorDispatch(Vector
, &Dispatch
);
428 /* Check if the vector is already connected */
429 if (Dispatch
.Type
== NoConnect
)
431 /* Do the connection */
432 Interrupt
->Connected
= Connected
= TRUE
;
434 /* Initialize the list */
435 InitializeListHead(&Interrupt
->InterruptListEntry
);
437 /* Connect and enable the interrupt */
438 KiConnectVectorToInterrupt(Interrupt
, NormalConnect
);
439 Status
= HalEnableSystemInterrupt(Vector
, Irql
, Interrupt
->Mode
);
440 if (!Status
) Error
= TRUE
;
442 else if ((Dispatch
.Type
!= UnknownConnect
) &&
443 (Interrupt
->ShareVector
) &&
444 (Dispatch
.Interrupt
->ShareVector
) &&
445 (Dispatch
.Interrupt
->Mode
== Interrupt
->Mode
))
447 /* The vector is shared and the interrupts are compatible */
448 Interrupt
->Connected
= Connected
= TRUE
;
451 // ASSERT(Irql <= SYNCH_LEVEL);
453 /* Check if this is the first chain */
454 if (Dispatch
.Type
!= ChainConnect
)
456 /* This is not supported */
457 ASSERT(Dispatch
.Interrupt
->Mode
!= Latched
);
459 /* Setup the chainned handler */
460 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
463 /* Insert into the interrupt list */
464 InsertTailList(&Dispatch
.Interrupt
->InterruptListEntry
,
465 &Interrupt
->InterruptListEntry
);
469 /* Unlock the dispatcher and revert affinity */
470 KiReleaseDispatcherLock(OldIrql
);
471 KeRevertToUserAffinityThread();
473 /* Check if we failed while trying to connect */
474 if ((Connected
) && (Error
))
476 DPRINT1("HalEnableSystemInterrupt failed\n");
477 KeDisconnectInterrupt(Interrupt
);
481 /* Return to caller */
490 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt
)
494 DISPATCH_INFO Dispatch
;
495 PKINTERRUPT NextInterrupt
;
498 /* Set the affinity */
499 KeSetSystemAffinityThread(1 << Interrupt
->Number
);
501 /* Lock the dispatcher */
502 OldIrql
= KiAcquireDispatcherLock();
504 /* Check if it's actually connected */
505 State
= Interrupt
->Connected
;
508 /* Get the vector and IRQL */
509 Irql
= Interrupt
->Irql
;
510 Vector
= Interrupt
->Vector
;
512 /* Get vector dispatch data */
513 KiGetVectorDispatch(Vector
, &Dispatch
);
515 /* Check if it was chained */
516 if (Dispatch
.Type
== ChainConnect
)
518 /* Check if the top-level interrupt is being removed */
519 ASSERT(Irql
<= SYNCH_LEVEL
);
520 if (Interrupt
== Dispatch
.Interrupt
)
522 /* Get the next one */
523 Dispatch
.Interrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
524 InterruptListEntry
.Flink
,
529 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
533 RemoveEntryList(&Interrupt
->InterruptListEntry
);
535 /* Get the next one */
536 NextInterrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
537 InterruptListEntry
.Flink
,
541 /* Check if this is the only one left */
542 if (Dispatch
.Interrupt
== NextInterrupt
)
544 /* Connect it in non-chained mode */
545 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, NormalConnect
);
550 /* Only one left, disable and remove it */
551 HalDisableSystemInterrupt(Interrupt
->Vector
, Irql
);
552 KiConnectVectorToInterrupt(Interrupt
, NoConnect
);
556 Interrupt
->Connected
= FALSE
;
559 /* Unlock the dispatcher and revert affinity */
560 KiReleaseDispatcherLock(OldIrql
);
561 KeRevertToUserAffinityThread();
563 /* Return to caller */
572 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt
,
573 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine
,
574 IN PVOID SynchronizeContext OPTIONAL
)
580 OldIrql
= KfRaiseIrql(Interrupt
->SynchronizeIrql
);
582 /* Acquire interrupt spinlock */
583 KeAcquireSpinLockAtDpcLevel(Interrupt
->ActualLock
);
585 /* Call the routine */
586 Success
= SynchronizeRoutine(SynchronizeContext
);
589 KeReleaseSpinLockFromDpcLevel(Interrupt
->ActualLock
);
592 KfLowerIrql(OldIrql
);