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
);
154 KiUnexpectedInterrupt(VOID
)
156 /* Crash the machine */
157 KeBugCheck(TRAP_CAUSE_UNKNOWN
);
162 KiUnexpectedInterruptTailHandler(IN PKTRAP_FRAME TrapFrame
)
167 KiEnterInterruptTrap(TrapFrame
);
169 /* Increase interrupt count */
170 KeGetCurrentPrcb()->InterruptCount
++;
172 /* Start the interrupt */
173 if (HalBeginSystemInterrupt(HIGH_LEVEL
, TrapFrame
->ErrCode
, &OldIrql
))
176 DPRINT1("\n\x7\x7!!! Unexpected Interrupt 0x%02lx !!!\n", TrapFrame
->ErrCode
);
178 /* Now call the epilogue code */
179 KiExitInterrupt(TrapFrame
, OldIrql
, FALSE
);
183 /* Now call the epilogue code */
184 KiExitInterrupt(TrapFrame
, OldIrql
, TRUE
);
190 (FASTCALL
*PKI_INTERRUPT_DISPATCH
)(
191 IN PKTRAP_FRAME TrapFrame
,
192 IN PKINTERRUPT Interrupt
197 KiInterruptDispatch(IN PKTRAP_FRAME TrapFrame
,
198 IN PKINTERRUPT Interrupt
)
202 /* Increase interrupt count */
203 KeGetCurrentPrcb()->InterruptCount
++;
205 /* Begin the interrupt, making sure it's not spurious */
206 if (HalBeginSystemInterrupt(Interrupt
->SynchronizeIrql
,
210 /* Acquire interrupt lock */
211 KxAcquireSpinLock(Interrupt
->ActualLock
);
214 Interrupt
->ServiceRoutine(Interrupt
, Interrupt
->ServiceContext
);
216 /* Release interrupt lock */
217 KxReleaseSpinLock(Interrupt
->ActualLock
);
219 /* Now call the epilogue code */
220 KiExitInterrupt(TrapFrame
, OldIrql
, FALSE
);
224 /* Now call the epilogue code */
225 KiExitInterrupt(TrapFrame
, OldIrql
, TRUE
);
231 KiChainedDispatch(IN PKTRAP_FRAME TrapFrame
,
232 IN PKINTERRUPT Interrupt
)
234 KIRQL OldIrql
, OldInterruptIrql
= 0;
236 PLIST_ENTRY NextEntry
, ListHead
;
238 /* Increase interrupt count */
239 KeGetCurrentPrcb()->InterruptCount
++;
241 /* Begin the interrupt, making sure it's not spurious */
242 if (HalBeginSystemInterrupt(Interrupt
->Irql
,
246 /* Get list pointers */
247 ListHead
= &Interrupt
->InterruptListEntry
;
248 NextEntry
= ListHead
; /* The head is an entry! */
251 /* Check if this interrupt's IRQL is higher than the current one */
252 if (Interrupt
->SynchronizeIrql
> Interrupt
->Irql
)
254 /* Raise to higher IRQL */
255 OldInterruptIrql
= KfRaiseIrql(Interrupt
->SynchronizeIrql
);
258 /* Acquire interrupt lock */
259 KxAcquireSpinLock(Interrupt
->ActualLock
);
262 Handled
= Interrupt
->ServiceRoutine(Interrupt
,
263 Interrupt
->ServiceContext
);
265 /* Release interrupt lock */
266 KxReleaseSpinLock(Interrupt
->ActualLock
);
268 /* Check if this interrupt's IRQL is higher than the current one */
269 if (Interrupt
->SynchronizeIrql
> Interrupt
->Irql
)
271 /* Lower the IRQL back */
272 ASSERT(OldInterruptIrql
== Interrupt
->Irql
);
273 KfLowerIrql(OldInterruptIrql
);
276 /* Check if the interrupt got handled and it's level */
277 if ((Handled
) && (Interrupt
->Mode
== LevelSensitive
)) break;
280 NextEntry
= NextEntry
->Flink
;
282 /* Is this the last one? */
283 if (NextEntry
== ListHead
)
285 /* Level should not have gotten here */
286 if (Interrupt
->Mode
== LevelSensitive
) break;
288 /* As for edge, we can only exit once nobody can handle the interrupt */
292 /* Get the interrupt object for the next pass */
293 Interrupt
= CONTAINING_RECORD(NextEntry
, KINTERRUPT
, InterruptListEntry
);
296 /* Now call the epilogue code */
297 KiExitInterrupt(TrapFrame
, OldIrql
, FALSE
);
301 /* Now call the epilogue code */
302 KiExitInterrupt(TrapFrame
, OldIrql
, TRUE
);
308 KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame
,
309 IN PKINTERRUPT Interrupt
)
311 /* Enter interrupt frame */
312 KiEnterInterruptTrap(TrapFrame
);
314 /* Call the correct dispatcher */
315 ((PKI_INTERRUPT_DISPATCH
)Interrupt
->DispatchAddress
)(TrapFrame
, Interrupt
);
319 /* PUBLIC FUNCTIONS **********************************************************/
326 KeInitializeInterrupt(IN PKINTERRUPT Interrupt
,
327 IN PKSERVICE_ROUTINE ServiceRoutine
,
328 IN PVOID ServiceContext
,
329 IN PKSPIN_LOCK SpinLock
,
332 IN KIRQL SynchronizeIrql
,
333 IN KINTERRUPT_MODE InterruptMode
,
334 IN BOOLEAN ShareVector
,
335 IN CHAR ProcessorNumber
,
336 IN BOOLEAN FloatingSave
)
339 PULONG DispatchCode
= &Interrupt
->DispatchCode
[0], Patch
= DispatchCode
;
341 /* Set the Interrupt Header */
342 Interrupt
->Type
= InterruptObject
;
343 Interrupt
->Size
= sizeof(KINTERRUPT
);
345 /* Check if we got a spinlock */
348 Interrupt
->ActualLock
= SpinLock
;
352 /* This means we'll be usin the built-in one */
353 KeInitializeSpinLock(&Interrupt
->SpinLock
);
354 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
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
;
370 /* Loop the template in memory */
371 for (i
= 0; i
< DISPATCH_LENGTH
; i
++)
373 /* Copy the dispatch code */
374 *DispatchCode
++ = ((PULONG
)KiInterruptTemplate
)[i
];
377 /* Jump to the last 4 bytes */
378 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
379 ((ULONG_PTR
)&KiInterruptTemplateObject
-
380 (ULONG_PTR
)KiInterruptTemplate
) - 4);
382 /* Apply the patch */
383 *Patch
= PtrToUlong(Interrupt
);
385 /* Disconnect it at first */
386 Interrupt
->Connected
= FALSE
;
394 KeConnectInterrupt(IN PKINTERRUPT Interrupt
)
396 BOOLEAN Connected
, Error
, Status
;
400 DISPATCH_INFO Dispatch
;
402 /* Get data from interrupt */
403 Number
= Interrupt
->Number
;
404 Vector
= Interrupt
->Vector
;
405 Irql
= Interrupt
->Irql
;
407 /* Validate the settings */
408 if ((Irql
> HIGH_LEVEL
) ||
409 (Number
>= KeNumberProcessors
) ||
410 (Interrupt
->SynchronizeIrql
< Irql
) ||
411 (Interrupt
->FloatingSave
))
420 /* Set the system affinity and acquire the dispatcher lock */
421 KeSetSystemAffinityThread(1 << Number
);
422 OldIrql
= KiAcquireDispatcherLock();
424 /* Check if it's already been connected */
425 if (!Interrupt
->Connected
)
427 /* Get vector dispatching information */
428 KiGetVectorDispatch(Vector
, &Dispatch
);
430 /* Check if the vector is already connected */
431 if (Dispatch
.Type
== NoConnect
)
433 /* Do the connection */
434 Interrupt
->Connected
= Connected
= TRUE
;
436 /* Initialize the list */
437 InitializeListHead(&Interrupt
->InterruptListEntry
);
439 /* Connect and enable the interrupt */
440 KiConnectVectorToInterrupt(Interrupt
, NormalConnect
);
441 Status
= HalEnableSystemInterrupt(Vector
, Irql
, Interrupt
->Mode
);
442 if (!Status
) Error
= TRUE
;
444 else if ((Dispatch
.Type
!= UnknownConnect
) &&
445 (Interrupt
->ShareVector
) &&
446 (Dispatch
.Interrupt
->ShareVector
) &&
447 (Dispatch
.Interrupt
->Mode
== Interrupt
->Mode
))
449 /* The vector is shared and the interrupts are compatible */
450 Interrupt
->Connected
= Connected
= TRUE
;
453 * Verify the IRQL for chained connect,
455 #if defined(CONFIG_SMP)
456 ASSERT(Irql
<= SYNCH_LEVEL
);
457 #elif (NTDDI_VERSION >= NTDDI_WS03)
458 ASSERT(Irql
<= (IPI_LEVEL
- 2));
460 ASSERT(Irql
<= (IPI_LEVEL
- 1));
463 /* Check if this is the first chain */
464 if (Dispatch
.Type
!= ChainConnect
)
466 /* This is not supported */
467 ASSERT(Dispatch
.Interrupt
->Mode
!= Latched
);
469 /* Setup the chainned handler */
470 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
473 /* Insert into the interrupt list */
474 InsertTailList(&Dispatch
.Interrupt
->InterruptListEntry
,
475 &Interrupt
->InterruptListEntry
);
479 /* Unlock the dispatcher and revert affinity */
480 KiReleaseDispatcherLock(OldIrql
);
481 KeRevertToUserAffinityThread();
483 /* Check if we failed while trying to connect */
484 if ((Connected
) && (Error
))
486 DPRINT1("HalEnableSystemInterrupt failed\n");
487 KeDisconnectInterrupt(Interrupt
);
491 /* Return to caller */
500 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt
)
504 DISPATCH_INFO Dispatch
;
505 PKINTERRUPT NextInterrupt
;
508 /* Set the affinity */
509 KeSetSystemAffinityThread(1 << Interrupt
->Number
);
511 /* Lock the dispatcher */
512 OldIrql
= KiAcquireDispatcherLock();
514 /* Check if it's actually connected */
515 State
= Interrupt
->Connected
;
518 /* Get the vector and IRQL */
519 Irql
= Interrupt
->Irql
;
520 Vector
= Interrupt
->Vector
;
522 /* Get vector dispatch data */
523 KiGetVectorDispatch(Vector
, &Dispatch
);
525 /* Check if it was chained */
526 if (Dispatch
.Type
== ChainConnect
)
528 /* Check if the top-level interrupt is being removed */
529 ASSERT(Irql
<= SYNCH_LEVEL
);
530 if (Interrupt
== Dispatch
.Interrupt
)
532 /* Get the next one */
533 Dispatch
.Interrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
534 InterruptListEntry
.Flink
,
539 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
543 RemoveEntryList(&Interrupt
->InterruptListEntry
);
545 /* Get the next one */
546 NextInterrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
547 InterruptListEntry
.Flink
,
551 /* Check if this is the only one left */
552 if (Dispatch
.Interrupt
== NextInterrupt
)
554 /* Connect it in non-chained mode */
555 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, NormalConnect
);
560 /* Only one left, disable and remove it */
561 HalDisableSystemInterrupt(Interrupt
->Vector
, Irql
);
562 KiConnectVectorToInterrupt(Interrupt
, NoConnect
);
566 Interrupt
->Connected
= FALSE
;
569 /* Unlock the dispatcher and revert affinity */
570 KiReleaseDispatcherLock(OldIrql
);
571 KeRevertToUserAffinityThread();
573 /* Return to caller */
582 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt
,
583 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine
,
584 IN PVOID SynchronizeContext OPTIONAL
)
590 KeRaiseIrql(Interrupt
->SynchronizeIrql
,
593 /* Acquire interrupt spinlock */
594 KeAcquireSpinLockAtDpcLevel(Interrupt
->ActualLock
);
596 /* Call the routine */
597 Success
= SynchronizeRoutine(SynchronizeContext
);
600 KeReleaseSpinLockFromDpcLevel(Interrupt
->ActualLock
);
603 KeLowerIrql(OldIrql
);