3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/i386/irq.c
6 * PURPOSE: IRQ handling
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
12 * NOTE: In general the PIC interrupt priority facilities are used to
13 * preserve the NT IRQL semantics, global interrupt disables are only used
14 * to keep the PIC in a consistent state
18 /* INCLUDES ****************************************************************/
21 #include <ppcmmu/mmu.h>
24 #include <internal/debug.h>
26 KDPC KiExpireTimerDpc
;
27 extern ULONG KiMaximumDpcQueueDepth
;
28 extern ULONG KiMinimumDpcRate
;
29 extern ULONG KiAdjustDpcThreshold
;
30 extern ULONG KiIdealDpcRate
;
31 extern LONG KiTickOffset
;
32 extern ULONG KeMaximumIncrement
;
33 extern ULONG KeMinimumIncrement
;
34 extern ULONG KeTimeAdjustment
;
36 /* GLOBALS *****************************************************************/
38 /* Interrupt handler list */
43 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
45 #define BUILD_INTERRUPT_HANDLER(intnum) \
46 VOID INT_NAME2(intnum)(VOID);
49 BUILD_INTERRUPT_HANDLER(x##y)
52 D(x,0) D(x,1) D(x,2) D(x,3) \
53 D(x,4) D(x,5) D(x,6) D(x,7) \
54 D(x,8) D(x,9) D(x,A) D(x,B) \
55 D(x,C) D(x,D) D(x,E) D(x,F)
57 D16(3) D16(4) D16(5) D16(6)
58 D16(7) D16(8) D16(9) D16(A
)
59 D16(B
) D16(C
) D16(D
) D16(E
)
63 (ULONG)& INT_NAME2(x##y)
66 L(x,0), L(x,1), L(x,2), L(x,3), \
67 L(x,4), L(x,5), L(x,6), L(x,7), \
68 L(x,8), L(x,9), L(x,A), L(x,B), \
69 L(x,C), L(x,D), L(x,E), L(x,F)
71 static ULONG irq_handler
[ROUND_UP(NR_TRAPS
, 16)] = {
72 L16(3), L16(4), L16(5), L16(6),
73 L16(7), L16(8), L16(9), L16(A
),
74 L16(B
), L16(C
), L16(D
), L16(E
)
82 #else /* CONFIG_SMP */
84 void trap_handler_0(void);
85 void trap_handler_1(void);
86 void trap_handler_2(void);
87 void trap_handler_3(void);
88 void trap_handler_4(void);
89 void trap_handler_5(void);
90 void trap_handler_6(void);
91 void trap_handler_7(void);
92 void trap_handler_8(void);
93 void trap_handler_9(void);
94 void trap_handler_10(void);
95 void trap_handler_11(void);
96 void trap_handler_12(void);
97 void trap_handler_13(void);
98 void trap_handler_14(void);
99 void trap_handler_15(void);
101 static unsigned int trap_handler
[NR_TRAPS
] __attribute__((unused
)) =
103 (int)&trap_handler_0
,
104 (int)&trap_handler_1
,
105 (int)&trap_handler_2
,
106 (int)&trap_handler_3
,
107 (int)&trap_handler_4
,
108 (int)&trap_handler_5
,
109 (int)&trap_handler_6
,
110 (int)&trap_handler_7
,
111 (int)&trap_handler_8
,
112 (int)&trap_handler_9
,
113 (int)&trap_handler_10
,
114 (int)&trap_handler_11
,
115 (int)&trap_handler_12
,
116 (int)&trap_handler_13
,
117 (int)&trap_handler_14
,
118 (int)&trap_handler_15
,
121 #endif /* CONFIG_SMP */
124 * PURPOSE: Object describing each isr
125 * NOTE: The data in this table is only modified at passsive level but can
126 * be accessed at any irq level.
135 ISR_TABLE
, *PISR_TABLE
;
138 static ISR_TABLE IsrTable
[NR_TRAPS
][MAXIMUM_PROCESSORS
];
140 static ISR_TABLE IsrTable
[NR_TRAPS
][1];
143 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
145 /* FUNCTIONS ****************************************************************/
150 KeInitInterrupts (VOID
)
155 * Setup the IDT entries to point to the interrupt handlers
157 for (i
=0;i
<NR_TRAPS
;i
++)
160 for (j
= 0; j
< MAXIMUM_PROCESSORS
; j
++)
165 InitializeListHead(&IsrTable
[i
][j
].ListHead
);
166 KeInitializeSpinLock(&IsrTable
[i
][j
].Lock
);
167 IsrTable
[i
][j
].Count
= 0;
173 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame
,
174 PKTRAP_FRAME TrapFrame
)
179 KeTrapFrameToIRQTrapFrame(PKTRAP_FRAME TrapFrame
,
180 PKIRQ_TRAPFRAME IrqTrapFrame
)
185 * NOTE: On Windows this function takes exactly one parameter and EBP is
186 * guaranteed to point to KTRAP_FRAME. The function is used only
187 * by HAL, so there's no point in keeping that prototype.
193 KeUpdateRunTime(IN PKTRAP_FRAME TrapFrame
,
196 PKPRCB Prcb
= KeGetCurrentPrcb();
197 PKTHREAD CurrentThread
;
198 PKPROCESS CurrentProcess
;
200 /* Make sure we don't go further if we're in early boot phase. */
201 if (!(Prcb
) || !(Prcb
->CurrentThread
)) return;
203 /* Get the current thread and process */
204 CurrentThread
= Prcb
->CurrentThread
;
205 CurrentProcess
= CurrentThread
->ApcState
.Process
;
207 /* Check if we came from user mode */
208 if (TrapFrame
->PreviousMode
!= KernelMode
)
210 /* Update user times */
211 CurrentThread
->UserTime
++;
212 InterlockedIncrement((PLONG
)&CurrentProcess
->UserTime
);
218 if (Irql
> DISPATCH_LEVEL
)
220 /* This was an interrupt */
221 Prcb
->InterruptTime
++;
223 else if ((Irql
< DISPATCH_LEVEL
) || !(Prcb
->DpcRoutineActive
))
225 /* This was normal kernel time */
226 CurrentThread
->KernelTime
++;
227 InterlockedIncrement((PLONG
)&CurrentProcess
->KernelTime
);
229 else if (Irql
== DISPATCH_LEVEL
)
231 /* This was DPC time */
235 /* Update CPU kernel time in all cases */
239 /* Set the last DPC Count and request rate */
240 Prcb
->DpcLastCount
= Prcb
->DpcData
[0].DpcCount
;
241 Prcb
->DpcRequestRate
= ((Prcb
->DpcData
[0].DpcCount
- Prcb
->DpcLastCount
) +
242 Prcb
->DpcRequestRate
) / 2;
244 /* Check if we should request a DPC */
245 if ((Prcb
->DpcData
[0].DpcQueueDepth
) && !(Prcb
->DpcRoutineActive
))
248 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
250 /* Update the depth if needed */
251 if ((Prcb
->DpcRequestRate
< KiIdealDpcRate
) &&
252 (Prcb
->MaximumDpcQueueDepth
> 1))
254 /* Decrease the maximum depth by one */
255 Prcb
->MaximumDpcQueueDepth
--;
260 /* Decrease the adjustment threshold */
261 if (!(--Prcb
->AdjustDpcThreshold
))
263 /* We've hit 0, reset it */
264 Prcb
->AdjustDpcThreshold
= KiAdjustDpcThreshold
;
266 /* Check if we've hit queue maximum */
267 if (KiMaximumDpcQueueDepth
!= Prcb
->MaximumDpcQueueDepth
)
269 /* Increase maximum by one */
270 Prcb
->MaximumDpcQueueDepth
++;
276 * If we're at end of quantum request software interrupt. The rest
277 * is handled in KiDispatchInterrupt.
279 * NOTE: If one stays at DISPATCH_LEVEL for a long time the DPC routine
280 * which checks for quantum end will not be executed and decrementing
281 * the quantum here can result in overflow. This is not a problem since
282 * we don't care about the quantum value anymore after the QuantumEnd
285 if ((CurrentThread
->Quantum
-= 3) <= 0)
287 Prcb
->QuantumEnd
= TRUE
;
288 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
294 * NOTE: On Windows this function takes exactly zero parameters and EBP is
295 * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL.
296 * The function is used only by HAL, so there's no point in keeping
303 KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame
,
309 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL
);
311 /* Update interrupt time */
312 Time
.LowPart
= SharedUserData
->InterruptTime
.LowPart
;
313 Time
.HighPart
= SharedUserData
->InterruptTime
.High1Time
;
314 Time
.QuadPart
+= Increment
;
315 SharedUserData
->InterruptTime
.High2Time
= Time
.u
.HighPart
;
316 SharedUserData
->InterruptTime
.LowPart
= Time
.u
.LowPart
;
317 SharedUserData
->InterruptTime
.High1Time
= Time
.u
.HighPart
;
319 /* Increase the tick offset */
320 KiTickOffset
-= Increment
;
321 OldOffset
= KiTickOffset
;
323 /* Check if this isn't a tick yet */
324 if (KiTickOffset
> 0)
327 KeInsertQueueDpc(&KiExpireTimerDpc
, 0, 0);
331 /* Setup time structure for system time */
332 Time
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
333 Time
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
334 Time
.QuadPart
+= KeTimeAdjustment
;
335 SharedUserData
->SystemTime
.High2Time
= Time
.HighPart
;
336 SharedUserData
->SystemTime
.LowPart
= Time
.LowPart
;
337 SharedUserData
->SystemTime
.High1Time
= Time
.HighPart
;
339 /* Setup time structure for tick time */
340 Time
.LowPart
= KeTickCount
.LowPart
;
341 Time
.HighPart
= KeTickCount
.High1Time
;
343 KeTickCount
.High2Time
= Time
.HighPart
;
344 KeTickCount
.LowPart
= Time
.LowPart
;
345 KeTickCount
.High1Time
= Time
.HighPart
;
346 SharedUserData
->TickCount
.High2Time
= Time
.HighPart
;
347 SharedUserData
->TickCount
.LowPart
= Time
.LowPart
;
348 SharedUserData
->TickCount
.High1Time
= Time
.HighPart
;
350 /* Update tick count in shared user data as well */
351 SharedUserData
->TickCountLowDeprecated
++;
353 /* Queue a DPC that will expire timers */
354 KeInsertQueueDpc(&KiExpireTimerDpc
, 0, 0);
357 /* Update process and thread times */
360 /* This was a tick, calculate the next one */
361 KiTickOffset
+= KeMaximumIncrement
;
362 KeUpdateRunTime(TrapFrame
, Irql
);
367 KiInterruptDispatch2 (ULONG vector
, KIRQL old_level
)
369 * FUNCTION: Calls all the interrupt handlers for a given irq.
371 * vector - The number of the vector to call handlers for.
372 * old_level - The irql of the processor when the irq took place.
373 * NOTES: Must be called at DIRQL.
379 PISR_TABLE CurrentIsr
;
381 DPRINT("I(0x%.08x, 0x%.08x)\n", vector
, old_level
);
384 * Iterate the list until one of the isr tells us its device interrupted
386 CurrentIsr
= &IsrTable
[vector
][(ULONG
)KeGetCurrentProcessorNumber()];
388 KiAcquireSpinLock(&CurrentIsr
->Lock
);
391 current
= CurrentIsr
->ListHead
.Flink
;
393 while (current
!= &CurrentIsr
->ListHead
)
395 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,InterruptListEntry
);
396 oldlvl
= KeAcquireInterruptSpinLock(isr
);
397 if (isr
->ServiceRoutine(isr
, isr
->ServiceContext
))
399 KeReleaseInterruptSpinLock(isr
, oldlvl
);
402 KeReleaseInterruptSpinLock(isr
, oldlvl
);
403 current
= current
->Flink
;
405 KiReleaseSpinLock(&CurrentIsr
->Lock
);
409 KiInterruptDispatch3 (ULONG vector
, PKIRQ_TRAPFRAME Trapframe
)
411 * FUNCTION: Calls the irq specific handler for an irq
413 * irq = IRQ that has interrupted
417 KTRAP_FRAME KernelTrapFrame
;
418 PKTHREAD CurrentThread
;
419 PKTRAP_FRAME OldTrapFrame
=NULL
;
422 * At this point we have interrupts disabled, nothing has been done to
426 KeGetCurrentPrcb()->InterruptCount
++;
429 * Notify the rest of the kernel of the raised irq level. For the
430 * default HAL this will send an EOI to the PIC and alter the IRQL.
432 if (!HalBeginSystemInterrupt (vector
,
442 * NOTE: Only higher priority interrupts will get through
449 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
450 KeUpdateSystemTime(&KernelTrapFrame
, old_level
, 100000);
456 * Actually call the ISR.
458 KiInterruptDispatch2(vector
, old_level
);
462 * End the system interrupt.
466 if (old_level
==PASSIVE_LEVEL
)
468 HalEndSystemInterrupt (APC_LEVEL
, 0);
470 CurrentThread
= KeGetCurrentThread();
471 if (CurrentThread
!=NULL
&& CurrentThread
->ApcState
.UserApcPending
)
473 DPRINT("PID: %d, TID: %d CS %04x/%04x\n",
474 ((PETHREAD
)CurrentThread
)->ThreadsProcess
->UniqueProcessId
,
475 ((PETHREAD
)CurrentThread
)->Cid
.UniqueThread
,
477 CurrentThread
->TrapFrame
? CurrentThread
->TrapFrame
->Cs
: 0);
478 if (CurrentThread
->TrapFrame
== NULL
)
480 OldTrapFrame
= CurrentThread
->TrapFrame
;
481 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
482 CurrentThread
->TrapFrame
= &KernelTrapFrame
;
486 KiDeliverApc(UserMode
, NULL
, NULL
);
489 ASSERT(KeGetCurrentThread() == CurrentThread
);
490 if (CurrentThread
->TrapFrame
== &KernelTrapFrame
)
492 KeTrapFrameToIRQTrapFrame(&KernelTrapFrame
, Trapframe
);
493 CurrentThread
->TrapFrame
= OldTrapFrame
;
496 KeLowerIrql(PASSIVE_LEVEL
);
500 HalEndSystemInterrupt (old_level
, 0);
509 PLIST_ENTRY current_entry
;
514 for (i
=0;i
<NR_TRAPS
;i
++)
517 KeRaiseIrql(i
,&oldlvl
);
519 for (j
=0; j
< KeNumberProcessors
; j
++)
521 KiAcquireSpinLock(&IsrTable
[i
][j
].Lock
);
523 current_entry
= IsrTable
[i
][j
].ListHead
.Flink
;
524 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,InterruptListEntry
);
525 while (current_entry
!=&(IsrTable
[i
][j
].ListHead
))
527 if (printed
== FALSE
)
530 DPRINT("For irq %x:\n",i
);
532 DPRINT(" Isr %x\n",current
);
533 current_entry
= current_entry
->Flink
;
534 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,InterruptListEntry
);
536 KiReleaseSpinLock(&IsrTable
[i
][j
].Lock
);
547 KeConnectInterrupt(PKINTERRUPT InterruptObject
)
549 KIRQL oldlvl
,synch_oldlvl
;
550 PKINTERRUPT ListHead
;
552 PISR_TABLE CurrentIsr
;
555 DPRINT("KeConnectInterrupt()\n");
557 Vector
= InterruptObject
->Vector
;
559 if (Vector
< 0 || Vector
>= NR_TRAPS
)
562 ASSERT (InterruptObject
->Number
< KeNumberProcessors
);
564 KeSetSystemAffinityThread(1 << InterruptObject
->Number
);
566 CurrentIsr
= &IsrTable
[Vector
][(ULONG
)InterruptObject
->Number
];
568 KeRaiseIrql(Vector
,&oldlvl
);
569 KiAcquireSpinLock(&CurrentIsr
->Lock
);
572 * Check if the vector is already in use that we can share it
574 if (!IsListEmpty(&CurrentIsr
->ListHead
))
576 ListHead
= CONTAINING_RECORD(CurrentIsr
->ListHead
.Flink
,KINTERRUPT
,InterruptListEntry
);
577 if (InterruptObject
->ShareVector
== FALSE
|| ListHead
->ShareVector
==FALSE
)
579 KiReleaseSpinLock(&CurrentIsr
->Lock
);
581 KeRevertToUserAffinityThread();
586 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
588 DPRINT("%x %x\n",CurrentIsr
->ListHead
.Flink
, CurrentIsr
->ListHead
.Blink
);
590 Result
= HalEnableSystemInterrupt(Vector
, InterruptObject
->Irql
, InterruptObject
->Mode
);
593 InsertTailList(&CurrentIsr
->ListHead
,&InterruptObject
->InterruptListEntry
);
594 DPRINT("%x %x\n",InterruptObject
->InterruptListEntry
.Flink
, InterruptObject
->InterruptListEntry
.Blink
);
597 InterruptObject
->Connected
= TRUE
;
598 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
601 * Release the table spinlock
603 KiReleaseSpinLock(&CurrentIsr
->Lock
);
608 KeRevertToUserAffinityThread();
616 * FUNCTION: Releases a drivers isr
618 * InterruptObject = isr to release
622 KeDisconnectInterrupt(PKINTERRUPT InterruptObject
)
624 KIRQL oldlvl
,synch_oldlvl
;
625 PISR_TABLE CurrentIsr
;
628 DPRINT1("KeDisconnectInterrupt\n");
629 ASSERT (InterruptObject
->Number
< KeNumberProcessors
);
631 /* Set the affinity */
632 KeSetSystemAffinityThread(1 << InterruptObject
->Number
);
634 /* Get the ISR Tabe */
635 CurrentIsr
= &IsrTable
[InterruptObject
->Vector
]
636 [(ULONG
)InterruptObject
->Number
];
638 /* Raise IRQL to required level and lock table */
639 KeRaiseIrql(InterruptObject
->Vector
,&oldlvl
);
640 KiAcquireSpinLock(&CurrentIsr
->Lock
);
642 /* Check if it's actually connected */
643 if ((State
= InterruptObject
->Connected
))
645 /* Lock the Interrupt */
646 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
648 /* Remove this one, and check if all are gone */
649 RemoveEntryList(&InterruptObject
->InterruptListEntry
);
650 if (IsListEmpty(&CurrentIsr
->ListHead
))
652 /* Completely Disable the Interrupt */
653 HalDisableSystemInterrupt(InterruptObject
->Vector
, InterruptObject
->Irql
);
657 InterruptObject
->Connected
= FALSE
;
659 /* Release the interrupt lock */
660 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
662 /* Release the table spinlock */
663 KiReleaseSpinLock(&CurrentIsr
->Lock
);
666 /* Go back to default affinity */
667 KeRevertToUserAffinityThread();
669 /* Return Old Interrupt State */
678 KeInitializeInterrupt(PKINTERRUPT Interrupt
,
679 PKSERVICE_ROUTINE ServiceRoutine
,
680 PVOID ServiceContext
,
681 PKSPIN_LOCK SpinLock
,
684 KIRQL SynchronizeIrql
,
685 KINTERRUPT_MODE InterruptMode
,
687 CHAR ProcessorNumber
,
688 BOOLEAN FloatingSave
)
690 /* Set the Interrupt Header */
691 Interrupt
->Type
= InterruptObject
;
692 Interrupt
->Size
= sizeof(KINTERRUPT
);
694 /* Check if we got a spinlock */
697 Interrupt
->ActualLock
= SpinLock
;
701 /* This means we'll be usin the built-in one */
702 KeInitializeSpinLock(&Interrupt
->SpinLock
);
703 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
706 /* Set the other settings */
707 Interrupt
->ServiceRoutine
= ServiceRoutine
;
708 Interrupt
->ServiceContext
= ServiceContext
;
709 Interrupt
->Vector
= Vector
;
710 Interrupt
->Irql
= Irql
;
711 Interrupt
->SynchronizeIrql
= SynchronizeIrql
;
712 Interrupt
->Mode
= InterruptMode
;
713 Interrupt
->ShareVector
= ShareVector
;
714 Interrupt
->Number
= ProcessorNumber
;
715 Interrupt
->FloatingSave
= FloatingSave
;
717 /* Disconnect it at first */
718 Interrupt
->Connected
= FALSE
;
721 VOID
KePrintInterruptStatistic(VOID
)
725 for (j
= 0; j
< KeNumberProcessors
; j
++)
727 DPRINT1("CPU%d:\n", j
);
728 for (i
= 0; i
< NR_TRAPS
; i
++)
730 if (IsrTable
[i
][j
].Count
)
732 DPRINT1(" Irq %x(%d): %d\n", i
, i
, IsrTable
[i
][j
].Count
);
740 KeDisableInterrupts(VOID
)
746 Return
= (Flags
& 0x8000) ? TRUE
: FALSE
;
748 /* Disable interrupts */
755 KdpServiceDispatcher(ULONG Service
, PCHAR Buffer
, ULONG Length
);
759 KiSystemService(ppc_trap_frame_t
*trap_frame
)
762 PKSYSTEM_ROUTINE SystemRoutine
;
764 switch(trap_frame
->gpr
[8])
766 case 0x10000: /* DebugService */
767 for( i
= 0; i
< trap_frame
->gpr
[5]; i
++ )
768 SetPhysByte(0x800003f8, ((PCHAR
)trap_frame
->gpr
[4])[i
]);
770 trap_frame
->gpr
[3] = KdpServiceDispatcher
772 (PCHAR
)trap_frame
->gpr
[4],
775 case 0xf0000: /* Thread startup */
776 /* XXX how to use UserThread (gpr[6]) */
777 SystemRoutine
= (PKSYSTEM_ROUTINE
)trap_frame
->gpr
[3];
778 SystemRoutine((PKSTART_ROUTINE
)trap_frame
->gpr
[4],
779 (PVOID
)trap_frame
->gpr
[5]);