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 extern void PearPCDebug(int ch
);
38 /* GLOBALS *****************************************************************/
40 /* Interrupt handler list */
45 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
47 #define BUILD_INTERRUPT_HANDLER(intnum) \
48 VOID INT_NAME2(intnum)(VOID);
51 BUILD_INTERRUPT_HANDLER(x##y)
54 D(x,0) D(x,1) D(x,2) D(x,3) \
55 D(x,4) D(x,5) D(x,6) D(x,7) \
56 D(x,8) D(x,9) D(x,A) D(x,B) \
57 D(x,C) D(x,D) D(x,E) D(x,F)
59 D16(3) D16(4) D16(5) D16(6)
60 D16(7) D16(8) D16(9) D16(A
)
61 D16(B
) D16(C
) D16(D
) D16(E
)
65 (ULONG)& INT_NAME2(x##y)
68 L(x,0), L(x,1), L(x,2), L(x,3), \
69 L(x,4), L(x,5), L(x,6), L(x,7), \
70 L(x,8), L(x,9), L(x,A), L(x,B), \
71 L(x,C), L(x,D), L(x,E), L(x,F)
73 static ULONG irq_handler
[ROUND_UP(NR_TRAPS
, 16)] = {
74 L16(3), L16(4), L16(5), L16(6),
75 L16(7), L16(8), L16(9), L16(A
),
76 L16(B
), L16(C
), L16(D
), L16(E
)
84 #else /* CONFIG_SMP */
86 void trap_handler_0(void);
87 void trap_handler_1(void);
88 void trap_handler_2(void);
89 void trap_handler_3(void);
90 void trap_handler_4(void);
91 void trap_handler_5(void);
92 void trap_handler_6(void);
93 void trap_handler_7(void);
94 void trap_handler_8(void);
95 void trap_handler_9(void);
96 void trap_handler_10(void);
97 void trap_handler_11(void);
98 void trap_handler_12(void);
99 void trap_handler_13(void);
100 void trap_handler_14(void);
101 void trap_handler_15(void);
103 static unsigned int trap_handler
[NR_TRAPS
] __attribute__((unused
)) =
105 (int)&trap_handler_0
,
106 (int)&trap_handler_1
,
107 (int)&trap_handler_2
,
108 (int)&trap_handler_3
,
109 (int)&trap_handler_4
,
110 (int)&trap_handler_5
,
111 (int)&trap_handler_6
,
112 (int)&trap_handler_7
,
113 (int)&trap_handler_8
,
114 (int)&trap_handler_9
,
115 (int)&trap_handler_10
,
116 (int)&trap_handler_11
,
117 (int)&trap_handler_12
,
118 (int)&trap_handler_13
,
119 (int)&trap_handler_14
,
120 (int)&trap_handler_15
,
123 #endif /* CONFIG_SMP */
126 * PURPOSE: Object describing each isr
127 * NOTE: The data in this table is only modified at passsive level but can
128 * be accessed at any irq level.
137 ISR_TABLE
, *PISR_TABLE
;
140 static ISR_TABLE IsrTable
[NR_TRAPS
][MAXIMUM_PROCESSORS
];
142 static ISR_TABLE IsrTable
[NR_TRAPS
][1];
145 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
147 /* FUNCTIONS ****************************************************************/
152 KeInitInterrupts (VOID
)
157 * Setup the IDT entries to point to the interrupt handlers
159 for (i
=0;i
<NR_TRAPS
;i
++)
162 for (j
= 0; j
< MAXIMUM_PROCESSORS
; j
++)
167 InitializeListHead(&IsrTable
[i
][j
].ListHead
);
168 KeInitializeSpinLock(&IsrTable
[i
][j
].Lock
);
169 IsrTable
[i
][j
].Count
= 0;
175 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame
,
176 PKTRAP_FRAME TrapFrame
)
181 KeTrapFrameToIRQTrapFrame(PKTRAP_FRAME TrapFrame
,
182 PKIRQ_TRAPFRAME IrqTrapFrame
)
187 * NOTE: On Windows this function takes exactly one parameter and EBP is
188 * guaranteed to point to KTRAP_FRAME. The function is used only
189 * by HAL, so there's no point in keeping that prototype.
195 KeUpdateRunTime(IN PKTRAP_FRAME TrapFrame
,
198 PKPRCB Prcb
= KeGetCurrentPrcb();
199 PKTHREAD CurrentThread
;
200 PKPROCESS CurrentProcess
;
202 /* Make sure we don't go further if we're in early boot phase. */
203 if (!(Prcb
) || !(Prcb
->CurrentThread
)) return;
205 /* Get the current thread and process */
206 CurrentThread
= Prcb
->CurrentThread
;
207 CurrentProcess
= CurrentThread
->ApcState
.Process
;
209 /* Check if we came from user mode */
210 if (TrapFrame
->PreviousMode
!= KernelMode
)
212 /* Update user times */
213 CurrentThread
->UserTime
++;
214 InterlockedIncrement((PLONG
)&CurrentProcess
->UserTime
);
220 if (Irql
> DISPATCH_LEVEL
)
222 /* This was an interrupt */
223 Prcb
->InterruptTime
++;
225 else if ((Irql
< DISPATCH_LEVEL
) || !(Prcb
->DpcRoutineActive
))
227 /* This was normal kernel time */
228 CurrentThread
->KernelTime
++;
229 InterlockedIncrement((PLONG
)&CurrentProcess
->KernelTime
);
231 else if (Irql
== DISPATCH_LEVEL
)
233 /* This was DPC time */
237 /* Update CPU kernel time in all cases */
241 /* Set the last DPC Count and request rate */
242 Prcb
->DpcLastCount
= Prcb
->DpcData
[0].DpcCount
;
243 Prcb
->DpcRequestRate
= ((Prcb
->DpcData
[0].DpcCount
- Prcb
->DpcLastCount
) +
244 Prcb
->DpcRequestRate
) / 2;
246 /* Check if we should request a DPC */
247 if ((Prcb
->DpcData
[0].DpcQueueDepth
) && !(Prcb
->DpcRoutineActive
))
250 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
252 /* Update the depth if needed */
253 if ((Prcb
->DpcRequestRate
< KiIdealDpcRate
) &&
254 (Prcb
->MaximumDpcQueueDepth
> 1))
256 /* Decrease the maximum depth by one */
257 Prcb
->MaximumDpcQueueDepth
--;
262 /* Decrease the adjustment threshold */
263 if (!(--Prcb
->AdjustDpcThreshold
))
265 /* We've hit 0, reset it */
266 Prcb
->AdjustDpcThreshold
= KiAdjustDpcThreshold
;
268 /* Check if we've hit queue maximum */
269 if (KiMaximumDpcQueueDepth
!= Prcb
->MaximumDpcQueueDepth
)
271 /* Increase maximum by one */
272 Prcb
->MaximumDpcQueueDepth
++;
278 * If we're at end of quantum request software interrupt. The rest
279 * is handled in KiDispatchInterrupt.
281 * NOTE: If one stays at DISPATCH_LEVEL for a long time the DPC routine
282 * which checks for quantum end will not be executed and decrementing
283 * the quantum here can result in overflow. This is not a problem since
284 * we don't care about the quantum value anymore after the QuantumEnd
287 if ((CurrentThread
->Quantum
-= 3) <= 0)
289 Prcb
->QuantumEnd
= TRUE
;
290 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
296 * NOTE: On Windows this function takes exactly zero parameters and EBP is
297 * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL.
298 * The function is used only by HAL, so there's no point in keeping
305 KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame
,
311 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL
);
313 /* Update interrupt time */
314 Time
.LowPart
= SharedUserData
->InterruptTime
.LowPart
;
315 Time
.HighPart
= SharedUserData
->InterruptTime
.High1Time
;
316 Time
.QuadPart
+= Increment
;
317 SharedUserData
->InterruptTime
.High2Time
= Time
.u
.HighPart
;
318 SharedUserData
->InterruptTime
.LowPart
= Time
.u
.LowPart
;
319 SharedUserData
->InterruptTime
.High1Time
= Time
.u
.HighPart
;
321 /* Increase the tick offset */
322 KiTickOffset
-= Increment
;
323 OldOffset
= KiTickOffset
;
325 /* Check if this isn't a tick yet */
326 if (KiTickOffset
> 0)
329 KeInsertQueueDpc(&KiExpireTimerDpc
, 0, 0);
333 /* Setup time structure for system time */
334 Time
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
335 Time
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
336 Time
.QuadPart
+= KeTimeAdjustment
;
337 SharedUserData
->SystemTime
.High2Time
= Time
.HighPart
;
338 SharedUserData
->SystemTime
.LowPart
= Time
.LowPart
;
339 SharedUserData
->SystemTime
.High1Time
= Time
.HighPart
;
341 /* Setup time structure for tick time */
342 Time
.LowPart
= KeTickCount
.LowPart
;
343 Time
.HighPart
= KeTickCount
.High1Time
;
345 KeTickCount
.High2Time
= Time
.HighPart
;
346 KeTickCount
.LowPart
= Time
.LowPart
;
347 KeTickCount
.High1Time
= Time
.HighPart
;
348 SharedUserData
->TickCount
.High2Time
= Time
.HighPart
;
349 SharedUserData
->TickCount
.LowPart
= Time
.LowPart
;
350 SharedUserData
->TickCount
.High1Time
= Time
.HighPart
;
352 /* Update tick count in shared user data as well */
353 SharedUserData
->TickCountLowDeprecated
++;
355 /* Queue a DPC that will expire timers */
356 KeInsertQueueDpc(&KiExpireTimerDpc
, 0, 0);
359 /* Update process and thread times */
362 /* This was a tick, calculate the next one */
363 KiTickOffset
+= KeMaximumIncrement
;
364 KeUpdateRunTime(TrapFrame
, Irql
);
369 KiInterruptDispatch2 (ULONG vector
, KIRQL old_level
)
371 * FUNCTION: Calls all the interrupt handlers for a given irq.
373 * vector - The number of the vector to call handlers for.
374 * old_level - The irql of the processor when the irq took place.
375 * NOTES: Must be called at DIRQL.
381 PISR_TABLE CurrentIsr
;
383 DPRINT("I(0x%.08x, 0x%.08x)\n", vector
, old_level
);
386 * Iterate the list until one of the isr tells us its device interrupted
388 CurrentIsr
= &IsrTable
[vector
][(ULONG
)KeGetCurrentProcessorNumber()];
390 KiAcquireSpinLock(&CurrentIsr
->Lock
);
393 current
= CurrentIsr
->ListHead
.Flink
;
395 while (current
!= &CurrentIsr
->ListHead
)
397 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,InterruptListEntry
);
398 oldlvl
= KeAcquireInterruptSpinLock(isr
);
399 if (isr
->ServiceRoutine(isr
, isr
->ServiceContext
))
401 KeReleaseInterruptSpinLock(isr
, oldlvl
);
404 KeReleaseInterruptSpinLock(isr
, oldlvl
);
405 current
= current
->Flink
;
407 KiReleaseSpinLock(&CurrentIsr
->Lock
);
411 KiInterruptDispatch3 (ULONG vector
, PKIRQ_TRAPFRAME Trapframe
)
413 * FUNCTION: Calls the irq specific handler for an irq
415 * irq = IRQ that has interrupted
419 KTRAP_FRAME KernelTrapFrame
;
420 PKTHREAD CurrentThread
;
421 PKTRAP_FRAME OldTrapFrame
=NULL
;
424 * At this point we have interrupts disabled, nothing has been done to
428 KeGetCurrentPrcb()->InterruptCount
++;
431 * Notify the rest of the kernel of the raised irq level. For the
432 * default HAL this will send an EOI to the PIC and alter the IRQL.
434 if (!HalBeginSystemInterrupt (vector
,
444 * NOTE: Only higher priority interrupts will get through
451 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
452 KeUpdateSystemTime(&KernelTrapFrame
, old_level
, 100000);
458 * Actually call the ISR.
460 KiInterruptDispatch2(vector
, old_level
);
464 * End the system interrupt.
468 if (old_level
==PASSIVE_LEVEL
)
470 HalEndSystemInterrupt (APC_LEVEL
, 0);
472 CurrentThread
= KeGetCurrentThread();
473 if (CurrentThread
!=NULL
&& CurrentThread
->ApcState
.UserApcPending
)
475 if (CurrentThread
->TrapFrame
== NULL
)
477 OldTrapFrame
= CurrentThread
->TrapFrame
;
478 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
479 CurrentThread
->TrapFrame
= &KernelTrapFrame
;
483 KiDeliverApc(UserMode
, NULL
, NULL
);
486 ASSERT(KeGetCurrentThread() == CurrentThread
);
487 if (CurrentThread
->TrapFrame
== &KernelTrapFrame
)
489 KeTrapFrameToIRQTrapFrame(&KernelTrapFrame
, Trapframe
);
490 CurrentThread
->TrapFrame
= OldTrapFrame
;
493 KeLowerIrql(PASSIVE_LEVEL
);
497 HalEndSystemInterrupt (old_level
, 0);
506 PLIST_ENTRY current_entry
;
511 for (i
=0;i
<NR_TRAPS
;i
++)
514 KeRaiseIrql(i
,&oldlvl
);
516 for (j
=0; j
< KeNumberProcessors
; j
++)
518 KiAcquireSpinLock(&IsrTable
[i
][j
].Lock
);
520 current_entry
= IsrTable
[i
][j
].ListHead
.Flink
;
521 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,InterruptListEntry
);
522 while (current_entry
!=&(IsrTable
[i
][j
].ListHead
))
524 if (printed
== FALSE
)
527 DPRINT("For irq %x:\n",i
);
529 DPRINT(" Isr %x\n",current
);
530 current_entry
= current_entry
->Flink
;
531 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,InterruptListEntry
);
533 KiReleaseSpinLock(&IsrTable
[i
][j
].Lock
);
544 KeConnectInterrupt(PKINTERRUPT InterruptObject
)
546 KIRQL oldlvl
,synch_oldlvl
;
547 PKINTERRUPT ListHead
;
549 PISR_TABLE CurrentIsr
;
552 DPRINT("KeConnectInterrupt()\n");
554 Vector
= InterruptObject
->Vector
;
556 if (Vector
< 0 || Vector
>= NR_TRAPS
)
559 ASSERT (InterruptObject
->Number
< KeNumberProcessors
);
561 KeSetSystemAffinityThread(1 << InterruptObject
->Number
);
563 CurrentIsr
= &IsrTable
[Vector
][(ULONG
)InterruptObject
->Number
];
565 KeRaiseIrql(Vector
,&oldlvl
);
566 KiAcquireSpinLock(&CurrentIsr
->Lock
);
569 * Check if the vector is already in use that we can share it
571 if (!IsListEmpty(&CurrentIsr
->ListHead
))
573 ListHead
= CONTAINING_RECORD(CurrentIsr
->ListHead
.Flink
,KINTERRUPT
,InterruptListEntry
);
574 if (InterruptObject
->ShareVector
== FALSE
|| ListHead
->ShareVector
==FALSE
)
576 KiReleaseSpinLock(&CurrentIsr
->Lock
);
578 KeRevertToUserAffinityThread();
583 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
585 DPRINT("%x %x\n",CurrentIsr
->ListHead
.Flink
, CurrentIsr
->ListHead
.Blink
);
587 Result
= HalEnableSystemInterrupt(Vector
, InterruptObject
->Irql
, InterruptObject
->Mode
);
590 InsertTailList(&CurrentIsr
->ListHead
,&InterruptObject
->InterruptListEntry
);
591 DPRINT("%x %x\n",InterruptObject
->InterruptListEntry
.Flink
, InterruptObject
->InterruptListEntry
.Blink
);
594 InterruptObject
->Connected
= TRUE
;
595 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
598 * Release the table spinlock
600 KiReleaseSpinLock(&CurrentIsr
->Lock
);
605 KeRevertToUserAffinityThread();
613 * FUNCTION: Releases a drivers isr
615 * InterruptObject = isr to release
619 KeDisconnectInterrupt(PKINTERRUPT InterruptObject
)
621 KIRQL oldlvl
,synch_oldlvl
;
622 PISR_TABLE CurrentIsr
;
625 DPRINT1("KeDisconnectInterrupt\n");
626 ASSERT (InterruptObject
->Number
< KeNumberProcessors
);
628 /* Set the affinity */
629 KeSetSystemAffinityThread(1 << InterruptObject
->Number
);
631 /* Get the ISR Tabe */
632 CurrentIsr
= &IsrTable
[InterruptObject
->Vector
]
633 [(ULONG
)InterruptObject
->Number
];
635 /* Raise IRQL to required level and lock table */
636 KeRaiseIrql(InterruptObject
->Vector
,&oldlvl
);
637 KiAcquireSpinLock(&CurrentIsr
->Lock
);
639 /* Check if it's actually connected */
640 if ((State
= InterruptObject
->Connected
))
642 /* Lock the Interrupt */
643 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
645 /* Remove this one, and check if all are gone */
646 RemoveEntryList(&InterruptObject
->InterruptListEntry
);
647 if (IsListEmpty(&CurrentIsr
->ListHead
))
649 /* Completely Disable the Interrupt */
650 HalDisableSystemInterrupt(InterruptObject
->Vector
, InterruptObject
->Irql
);
654 InterruptObject
->Connected
= FALSE
;
656 /* Release the interrupt lock */
657 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
659 /* Release the table spinlock */
660 KiReleaseSpinLock(&CurrentIsr
->Lock
);
663 /* Go back to default affinity */
664 KeRevertToUserAffinityThread();
666 /* Return Old Interrupt State */
675 KeInitializeInterrupt(PKINTERRUPT Interrupt
,
676 PKSERVICE_ROUTINE ServiceRoutine
,
677 PVOID ServiceContext
,
678 PKSPIN_LOCK SpinLock
,
681 KIRQL SynchronizeIrql
,
682 KINTERRUPT_MODE InterruptMode
,
684 CHAR ProcessorNumber
,
685 BOOLEAN FloatingSave
)
687 /* Set the Interrupt Header */
688 Interrupt
->Type
= InterruptObject
;
689 Interrupt
->Size
= sizeof(KINTERRUPT
);
691 /* Check if we got a spinlock */
694 Interrupt
->ActualLock
= SpinLock
;
698 /* This means we'll be usin the built-in one */
699 KeInitializeSpinLock(&Interrupt
->SpinLock
);
700 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
703 /* Set the other settings */
704 Interrupt
->ServiceRoutine
= ServiceRoutine
;
705 Interrupt
->ServiceContext
= ServiceContext
;
706 Interrupt
->Vector
= Vector
;
707 Interrupt
->Irql
= Irql
;
708 Interrupt
->SynchronizeIrql
= SynchronizeIrql
;
709 Interrupt
->Mode
= InterruptMode
;
710 Interrupt
->ShareVector
= ShareVector
;
711 Interrupt
->Number
= ProcessorNumber
;
712 Interrupt
->FloatingSave
= FloatingSave
;
714 /* Disconnect it at first */
715 Interrupt
->Connected
= FALSE
;
718 VOID
KePrintInterruptStatistic(VOID
)
722 for (j
= 0; j
< KeNumberProcessors
; j
++)
724 DPRINT1("CPU%d:\n", j
);
725 for (i
= 0; i
< NR_TRAPS
; i
++)
727 if (IsrTable
[i
][j
].Count
)
729 DPRINT1(" Irq %x(%d): %d\n", i
, i
, IsrTable
[i
][j
].Count
);
737 KeDisableInterrupts(VOID
)
743 Return
= (Flags
& 0x8000) ? TRUE
: FALSE
;
745 /* Disable interrupts */
752 KdpServiceDispatcher(ULONG Service
, PCHAR Buffer
, ULONG Length
);
756 KiSystemService(ppc_trap_frame_t
*trap_frame
)
759 PKSYSTEM_ROUTINE SystemRoutine
;
761 switch(trap_frame
->gpr
[8])
763 case 0x10000: /* DebugService */
764 for( i
= 0; i
< trap_frame
->gpr
[5]; i
++ )
766 PearPCDebug(((PCHAR
)trap_frame
->gpr
[4])[i
]);
767 WRITE_PORT_UCHAR((PVOID
)0x800003f8, ((PCHAR
)trap_frame
->gpr
[4])[i
]);
769 trap_frame
->gpr
[3] = KdpServiceDispatcher
771 (PCHAR
)trap_frame
->gpr
[4],
774 case 0xf0000: /* Thread startup */
775 /* XXX how to use UserThread (gpr[6]) */
776 SystemRoutine
= (PKSYSTEM_ROUTINE
)trap_frame
->gpr
[3];
777 SystemRoutine((PKSTART_ROUTINE
)trap_frame
->gpr
[4],
778 (PVOID
)trap_frame
->gpr
[5]);