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 /* Set nested trap frame */
143 KeGetPcr()->VdmAlert
= (ULONG_PTR
)TrapFrame
;
145 /* It was, disable interrupts and restore the IRQL */
147 HalEndSystemInterrupt(OldIrql
, 0);
150 /* Now exit the trap */
151 KiEoiHelper(TrapFrame
);
155 KiUnexpectedInterrupt(VOID
)
157 /* Crash the machine */
158 KeBugCheck(TRAP_CAUSE_UNKNOWN
);
163 KiUnexpectedInterruptTailHandler(IN PKTRAP_FRAME TrapFrame
)
168 KiEnterInterruptTrap(TrapFrame
);
170 /* Increase interrupt count */
171 KeGetCurrentPrcb()->InterruptCount
++;
173 /* Start the interrupt */
174 if (HalBeginSystemInterrupt(HIGH_LEVEL
, TrapFrame
->Eax
, &OldIrql
))
177 DPRINT1("\n\x7\x7!!! Unexpected Interrupt %02lx !!!\n");
179 /* Now call the epilogue code */
180 KiExitInterrupt(TrapFrame
, OldIrql
, FALSE
);
184 /* Now call the epilogue code */
185 KiExitInterrupt(TrapFrame
, OldIrql
, TRUE
);
192 (PKI_INTERRUPT_DISPATCH
)(
193 IN PKTRAP_FRAME TrapFrame
,
194 IN PKINTERRUPT Interrupt
199 KiInterruptDispatch(IN PKTRAP_FRAME TrapFrame
,
200 IN PKINTERRUPT Interrupt
)
204 /* Increase interrupt count */
205 KeGetCurrentPrcb()->InterruptCount
++;
207 /* Begin the interrupt, making sure it's not spurious */
208 if (HalBeginSystemInterrupt(Interrupt
->SynchronizeIrql
,
212 /* Acquire interrupt lock */
213 KxAcquireSpinLock(Interrupt
->ActualLock
);
216 Interrupt
->ServiceRoutine(Interrupt
, Interrupt
->ServiceContext
);
218 /* Release interrupt lock */
219 KxReleaseSpinLock(Interrupt
->ActualLock
);
221 /* Now call the epilogue code */
222 KiExitInterrupt(TrapFrame
, OldIrql
, FALSE
);
226 /* Now call the epilogue code */
227 KiExitInterrupt(TrapFrame
, OldIrql
, TRUE
);
233 KiChainedDispatch(IN PKTRAP_FRAME TrapFrame
,
234 IN PKINTERRUPT Interrupt
)
236 /* Increase interrupt count */
237 KeGetCurrentPrcb()->InterruptCount
++;
244 KiInterruptTemplateHandler(IN PKTRAP_FRAME TrapFrame
,
245 IN PKINTERRUPT Interrupt
)
247 /* Enter interrupt frame */
248 KiEnterInterruptTrap(TrapFrame
);
250 /* Call the correct dispatcher */
251 ((PKI_INTERRUPT_DISPATCH
*)Interrupt
->DispatchAddress
)(TrapFrame
, Interrupt
);
254 KiTrap(KiInterruptTemplate
, KI_PUSH_FAKE_ERROR_CODE
| KI_HARDWARE_INT
);
255 KiTrap(KiUnexpectedInterruptTail
, KI_PUSH_FAKE_ERROR_CODE
);
257 /* PUBLIC FUNCTIONS **********************************************************/
264 KeInitializeInterrupt(IN PKINTERRUPT Interrupt
,
265 IN PKSERVICE_ROUTINE ServiceRoutine
,
266 IN PVOID ServiceContext
,
267 IN PKSPIN_LOCK SpinLock
,
270 IN KIRQL SynchronizeIrql
,
271 IN KINTERRUPT_MODE InterruptMode
,
272 IN BOOLEAN ShareVector
,
273 IN CHAR ProcessorNumber
,
274 IN BOOLEAN FloatingSave
)
277 PULONG DispatchCode
= &Interrupt
->DispatchCode
[0], Patch
= DispatchCode
;
279 /* Set the Interrupt Header */
280 Interrupt
->Type
= InterruptObject
;
281 Interrupt
->Size
= sizeof(KINTERRUPT
);
283 /* Check if we got a spinlock */
286 Interrupt
->ActualLock
= SpinLock
;
290 /* This means we'll be usin the built-in one */
291 KeInitializeSpinLock(&Interrupt
->SpinLock
);
292 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
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
;
308 /* Loop the template in memory */
309 for (i
= 0; i
< KINTERRUPT_DISPATCH_CODES
; i
++)
311 /* Copy the dispatch code */
312 *DispatchCode
++ = ((PULONG
)KiInterruptTemplate
)[i
];
315 /* Jump to the last 4 bytes */
316 Patch
= (PULONG
)((ULONG_PTR
)Patch
+
317 ((ULONG_PTR
)&KiInterruptTemplateObject
-
318 (ULONG_PTR
)KiInterruptTemplate
) - 4);
320 /* Apply the patch */
321 *Patch
= PtrToUlong(Interrupt
);
323 /* Disconnect it at first */
324 Interrupt
->Connected
= FALSE
;
332 KeConnectInterrupt(IN PKINTERRUPT Interrupt
)
334 BOOLEAN Connected
, Error
, Status
;
338 DISPATCH_INFO Dispatch
;
340 /* Get data from interrupt */
341 Number
= Interrupt
->Number
;
342 Vector
= Interrupt
->Vector
;
343 Irql
= Interrupt
->Irql
;
345 /* Validate the settings */
346 if ((Irql
> HIGH_LEVEL
) ||
347 (Number
>= KeNumberProcessors
) ||
348 (Interrupt
->SynchronizeIrql
< Irql
) ||
349 (Interrupt
->FloatingSave
))
358 /* Set the system affinity and acquire the dispatcher lock */
359 KeSetSystemAffinityThread(1 << Number
);
360 OldIrql
= KiAcquireDispatcherLock();
362 /* Check if it's already been connected */
363 if (!Interrupt
->Connected
)
365 /* Get vector dispatching information */
366 KiGetVectorDispatch(Vector
, &Dispatch
);
368 /* Check if the vector is already connected */
369 if (Dispatch
.Type
== NoConnect
)
371 /* Do the connection */
372 Interrupt
->Connected
= Connected
= TRUE
;
374 /* Initialize the list */
375 InitializeListHead(&Interrupt
->InterruptListEntry
);
377 /* Connect and enable the interrupt */
378 KiConnectVectorToInterrupt(Interrupt
, NormalConnect
);
379 Status
= HalEnableSystemInterrupt(Vector
, Irql
, Interrupt
->Mode
);
380 if (!Status
) Error
= TRUE
;
382 else if ((Dispatch
.Type
!= UnknownConnect
) &&
383 (Interrupt
->ShareVector
) &&
384 (Dispatch
.Interrupt
->ShareVector
) &&
385 (Dispatch
.Interrupt
->Mode
== Interrupt
->Mode
))
387 /* The vector is shared and the interrupts are compatible */
388 Interrupt
->Connected
= Connected
= TRUE
;
391 // ASSERT(Irql <= SYNCH_LEVEL);
393 /* Check if this is the first chain */
394 if (Dispatch
.Type
!= ChainConnect
)
396 /* This is not supported */
397 ASSERT(Dispatch
.Interrupt
->Mode
!= Latched
);
399 /* Setup the chainned handler */
400 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
403 /* Insert into the interrupt list */
404 InsertTailList(&Dispatch
.Interrupt
->InterruptListEntry
,
405 &Interrupt
->InterruptListEntry
);
409 /* Unlock the dispatcher and revert affinity */
410 KiReleaseDispatcherLock(OldIrql
);
411 KeRevertToUserAffinityThread();
413 /* Check if we failed while trying to connect */
414 if ((Connected
) && (Error
))
416 DPRINT1("HalEnableSystemInterrupt failed\n");
417 KeDisconnectInterrupt(Interrupt
);
421 /* Return to caller */
430 KeDisconnectInterrupt(IN PKINTERRUPT Interrupt
)
434 DISPATCH_INFO Dispatch
;
435 PKINTERRUPT NextInterrupt
;
438 /* Set the affinity */
439 KeSetSystemAffinityThread(1 << Interrupt
->Number
);
441 /* Lock the dispatcher */
442 OldIrql
= KiAcquireDispatcherLock();
444 /* Check if it's actually connected */
445 State
= Interrupt
->Connected
;
448 /* Get the vector and IRQL */
449 Irql
= Interrupt
->Irql
;
450 Vector
= Interrupt
->Vector
;
452 /* Get vector dispatch data */
453 KiGetVectorDispatch(Vector
, &Dispatch
);
455 /* Check if it was chained */
456 if (Dispatch
.Type
== ChainConnect
)
458 /* Check if the top-level interrupt is being removed */
459 ASSERT(Irql
<= SYNCH_LEVEL
);
460 if (Interrupt
== Dispatch
.Interrupt
)
462 /* Get the next one */
463 Dispatch
.Interrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
464 InterruptListEntry
.Flink
,
469 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, ChainConnect
);
473 RemoveEntryList(&Interrupt
->InterruptListEntry
);
475 /* Get the next one */
476 NextInterrupt
= CONTAINING_RECORD(Dispatch
.Interrupt
->
477 InterruptListEntry
.Flink
,
481 /* Check if this is the only one left */
482 if (Dispatch
.Interrupt
== NextInterrupt
)
484 /* Connect it in non-chained mode */
485 KiConnectVectorToInterrupt(Dispatch
.Interrupt
, NormalConnect
);
490 /* Only one left, disable and remove it */
491 HalDisableSystemInterrupt(Interrupt
->Vector
, Irql
);
492 KiConnectVectorToInterrupt(Interrupt
, NoConnect
);
496 Interrupt
->Connected
= FALSE
;
499 /* Unlock the dispatcher and revert affinity */
500 KiReleaseDispatcherLock(OldIrql
);
501 KeRevertToUserAffinityThread();
503 /* Return to caller */
512 KeSynchronizeExecution(IN OUT PKINTERRUPT Interrupt
,
513 IN PKSYNCHRONIZE_ROUTINE SynchronizeRoutine
,
514 IN PVOID SynchronizeContext OPTIONAL
)
520 OldIrql
= KfRaiseIrql(Interrupt
->SynchronizeIrql
);
522 /* Acquire interrupt spinlock */
523 KeAcquireSpinLockAtDpcLevel(Interrupt
->ActualLock
);
525 /* Call the routine */
526 Status
= SynchronizeRoutine(SynchronizeContext
);
529 KeReleaseSpinLockFromDpcLevel(Interrupt
->ActualLock
);
532 KfLowerIrql(OldIrql
);