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 %02lx !!!\n");
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
)
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 OldIrql
= 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 KfLowerIrql(OldIrql
);
273 /* Check if the interrupt got handled and it's level */
274 if ((Handled
) && (Interrupt
->Mode
== LevelSensitive
)) break;
277 NextEntry
= NextEntry
->Flink
;
279 /* Is this the last one? */
280 if (NextEntry
== ListHead
)
282 /* Level should not have gotten here */
283 if (Interrupt
->Mode
== LevelSensitive
) break;
285 /* As for edge, we can only exit once nobody can handle the interrupt */
289 /* Get the interrupt object for the next pass */
290 Interrupt
= CONTAINING_RECORD(NextEntry
, KINTERRUPT
, InterruptListEntry
);
293 /* Now call the epilogue code */
294 KiExitInterrupt(TrapFrame
, OldIrql
, FALSE
);
298 /* Now call the epilogue code */
299 KiExitInterrupt(TrapFrame
, OldIrql
, TRUE
);
305 KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame
,
306 IN PKINTERRUPT Interrupt
)
308 /* Enter interrupt frame */
309 KiEnterInterruptTrap(TrapFrame
);
311 /* Call the correct dispatcher */
312 ((PKI_INTERRUPT_DISPATCH
)Interrupt
->DispatchAddress
)(TrapFrame
, Interrupt
);
316 /* PUBLIC FUNCTIONS **********************************************************/
323 KeInitializeInterrupt(IN PKINTERRUPT Interrupt
,
324 IN PKSERVICE_ROUTINE ServiceRoutine
,
325 IN PVOID ServiceContext
,
326 IN PKSPIN_LOCK SpinLock
,
329 IN KIRQL SynchronizeIrql
,
330 IN KINTERRUPT_MODE InterruptMode
,
331 IN BOOLEAN ShareVector
,
332 IN CHAR ProcessorNumber
,
333 IN BOOLEAN FloatingSave
)
336 PULONG DispatchCode
= &Interrupt
->DispatchCode
[0], Patch
= DispatchCode
;
338 /* Set the Interrupt Header */
339 Interrupt
->Type
= InterruptObject
;
340 Interrupt
->Size
= sizeof(KINTERRUPT
);
342 /* Check if we got a spinlock */
345 Interrupt
->ActualLock
= SpinLock
;
349 /* This means we'll be usin the built-in one */
350 KeInitializeSpinLock(&Interrupt
->SpinLock
);
351 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
354 /* Set the other settings */
355 Interrupt
->ServiceRoutine
= ServiceRoutine
;
356 Interrupt
->ServiceContext
= ServiceContext
;
357 Interrupt
->Vector
= Vector
;
358 Interrupt
->Irql
= Irql
;
359 Interrupt
->SynchronizeIrql
= SynchronizeIrql
;
360 Interrupt
->Mode
= InterruptMode
;
361 Interrupt
->ShareVector
= ShareVector
;
362 Interrupt
->Number
= ProcessorNumber
;
363 Interrupt
->FloatingSave
= FloatingSave
;
364 Interrupt
->TickCount
= MAXULONG
;
365 Interrupt
->DispatchCount
= MAXULONG
;
367 /* Loop the template in memory */
368 for (i
= 0; i
< DISPATCH_LENGTH
; i
++)
370 /* Copy the dispatch code */
371 *DispatchCode
++ = ((PULONG
)KiInterruptTemplate
)[i
];
374 /* Jump to the last 4 bytes */
375 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
376 ((ULONG_PTR
)&KiInterruptTemplateObject
-
377 (ULONG_PTR
)KiInterruptTemplate
) - 4);
379 /* Apply the patch */
380 *Patch
= PtrToUlong(Interrupt
);
382 /* Disconnect it at first */
383 Interrupt
->Connected
= FALSE
;
391 KeConnectInterrupt(IN PKINTERRUPT Interrupt
)
393 BOOLEAN Connected
, Error
, Status
;
397 DISPATCH_INFO Dispatch
;
399 /* Get data from interrupt */
400 Number
= Interrupt
->Number
;
401 Vector
= Interrupt
->Vector
;
402 Irql
= Interrupt
->Irql
;
404 /* Validate the settings */
405 if ((Irql
> HIGH_LEVEL
) ||
406 (Number
>= KeNumberProcessors
) ||
407 (Interrupt
->SynchronizeIrql
< Irql
) ||
408 (Interrupt
->FloatingSave
))
417 /* Set the system affinity and acquire the dispatcher lock */
418 KeSetSystemAffinityThread(1 << Number
);
419 OldIrql
= KiAcquireDispatcherLock();
421 /* Check if it's already been connected */
422 if (!Interrupt
->Connected
)
424 /* Get vector dispatching information */
425 KiGetVectorDispatch(Vector
, &Dispatch
);
427 /* Check if the vector is already connected */
428 if (Dispatch
.Type
== NoConnect
)
430 /* Do the connection */
431 Interrupt
->Connected
= Connected
= TRUE
;
433 /* Initialize the list */
434 InitializeListHead(&Interrupt
->InterruptListEntry
);
436 /* Connect and enable the interrupt */
437 KiConnectVectorToInterrupt(Interrupt
, NormalConnect
);
438 Status
= HalEnableSystemInterrupt(Vector
, Irql
, Interrupt
->Mode
);
439 if (!Status
) Error
= TRUE
;
441 else if ((Dispatch
.Type
!= UnknownConnect
) &&
442 (Interrupt
->ShareVector
) &&
443 (Dispatch
.Interrupt
->ShareVector
) &&
444 (Dispatch
.Interrupt
->Mode
== Interrupt
->Mode
))
446 /* The vector is shared and the interrupts are compatible */
447 Interrupt
->Connected
= Connected
= TRUE
;
450 // ASSERT(Irql <= SYNCH_LEVEL);
452 /* Check if this is the first chain */
453 if (Dispatch
.Type
!= ChainConnect
)
455 /* This is not supported */
456 ASSERT(Dispatch
.Interrupt
->Mode
!= Latched
);
458 /* Setup the chainned handler */
459 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
462 /* Insert into the interrupt list */
463 InsertTailList(&Dispatch
.Interrupt
->InterruptListEntry
,
464 &Interrupt
->InterruptListEntry
);
468 /* Unlock the dispatcher and revert affinity */
469 KiReleaseDispatcherLock(OldIrql
);
470 KeRevertToUserAffinityThread();
472 /* Check if we failed while trying to connect */
473 if ((Connected
) && (Error
))
475 DPRINT1("HalEnableSystemInterrupt failed\n");
476 KeDisconnectInterrupt(Interrupt
);
480 /* Return to caller */
489 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt
)
493 DISPATCH_INFO Dispatch
;
494 PKINTERRUPT NextInterrupt
;
497 /* Set the affinity */
498 KeSetSystemAffinityThread(1 << Interrupt
->Number
);
500 /* Lock the dispatcher */
501 OldIrql
= KiAcquireDispatcherLock();
503 /* Check if it's actually connected */
504 State
= Interrupt
->Connected
;
507 /* Get the vector and IRQL */
508 Irql
= Interrupt
->Irql
;
509 Vector
= Interrupt
->Vector
;
511 /* Get vector dispatch data */
512 KiGetVectorDispatch(Vector
, &Dispatch
);
514 /* Check if it was chained */
515 if (Dispatch
.Type
== ChainConnect
)
517 /* Check if the top-level interrupt is being removed */
518 ASSERT(Irql
<= SYNCH_LEVEL
);
519 if (Interrupt
== Dispatch
.Interrupt
)
521 /* Get the next one */
522 Dispatch
.Interrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
523 InterruptListEntry
.Flink
,
528 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
532 RemoveEntryList(&Interrupt
->InterruptListEntry
);
534 /* Get the next one */
535 NextInterrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
536 InterruptListEntry
.Flink
,
540 /* Check if this is the only one left */
541 if (Dispatch
.Interrupt
== NextInterrupt
)
543 /* Connect it in non-chained mode */
544 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, NormalConnect
);
549 /* Only one left, disable and remove it */
550 HalDisableSystemInterrupt(Interrupt
->Vector
, Irql
);
551 KiConnectVectorToInterrupt(Interrupt
, NoConnect
);
555 Interrupt
->Connected
= FALSE
;
558 /* Unlock the dispatcher and revert affinity */
559 KiReleaseDispatcherLock(OldIrql
);
560 KeRevertToUserAffinityThread();
562 /* Return to caller */
571 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt
,
572 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine
,
573 IN PVOID SynchronizeContext OPTIONAL
)
579 OldIrql
= KfRaiseIrql(Interrupt
->SynchronizeIrql
);
581 /* Acquire interrupt spinlock */
582 KeAcquireSpinLockAtDpcLevel(Interrupt
->ActualLock
);
584 /* Call the routine */
585 Status
= SynchronizeRoutine(SynchronizeContext
);
588 KeReleaseSpinLockFromDpcLevel(Interrupt
->ActualLock
);
591 KfLowerIrql(OldIrql
);