2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ke/i386/irq.c
5 * PURPOSE: IRQ handling
7 * PROGRAMMERS: David Welch (welch@mcmail.com)
11 * NOTE: In general the PIC interrupt priority facilities are used to
12 * preserve the NT IRQL semantics, global interrupt disables are only used
13 * to keep the PIC in a consistent state
17 /* INCLUDES ****************************************************************/
20 #include <ppcmmu/mmu.h>
25 KDPC KiExpireTimerDpc
;
26 extern ULONG KiMaximumDpcQueueDepth
;
27 extern ULONG KiMinimumDpcRate
;
28 extern ULONG KiAdjustDpcThreshold
;
29 extern ULONG KiIdealDpcRate
;
30 extern LONG KiTickOffset
;
31 extern ULONG KeMaximumIncrement
;
32 extern ULONG KeMinimumIncrement
;
33 extern ULONG KeTimeAdjustment
;
35 extern void PearPCDebug(int ch
);
37 /* GLOBALS *****************************************************************/
39 /* Interrupt handler list */
44 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
46 #define BUILD_INTERRUPT_HANDLER(intnum) \
47 VOID INT_NAME2(intnum)(VOID);
50 BUILD_INTERRUPT_HANDLER(x##y)
53 D(x,0) D(x,1) D(x,2) D(x,3) \
54 D(x,4) D(x,5) D(x,6) D(x,7) \
55 D(x,8) D(x,9) D(x,A) D(x,B) \
56 D(x,C) D(x,D) D(x,E) D(x,F)
58 D16(3) D16(4) D16(5) D16(6)
59 D16(7) D16(8) D16(9) D16(A
)
60 D16(B
) D16(C
) D16(D
) D16(E
)
64 (ULONG)& INT_NAME2(x##y)
67 L(x,0), L(x,1), L(x,2), L(x,3), \
68 L(x,4), L(x,5), L(x,6), L(x,7), \
69 L(x,8), L(x,9), L(x,A), L(x,B), \
70 L(x,C), L(x,D), L(x,E), L(x,F)
72 static ULONG irq_handler
[ROUND_UP(NR_TRAPS
, 16)] = {
73 L16(3), L16(4), L16(5), L16(6),
74 L16(7), L16(8), L16(9), L16(A
),
75 L16(B
), L16(C
), L16(D
), L16(E
)
83 #else /* CONFIG_SMP */
85 void trap_handler_0(void);
86 void trap_handler_1(void);
87 void trap_handler_2(void);
88 void trap_handler_3(void);
89 void trap_handler_4(void);
90 void trap_handler_5(void);
91 void trap_handler_6(void);
92 void trap_handler_7(void);
93 void trap_handler_8(void);
94 void trap_handler_9(void);
95 void trap_handler_10(void);
96 void trap_handler_11(void);
97 void trap_handler_12(void);
98 void trap_handler_13(void);
99 void trap_handler_14(void);
100 void trap_handler_15(void);
102 static unsigned int trap_handler
[NR_TRAPS
] __attribute__((unused
)) =
104 (int)&trap_handler_0
,
105 (int)&trap_handler_1
,
106 (int)&trap_handler_2
,
107 (int)&trap_handler_3
,
108 (int)&trap_handler_4
,
109 (int)&trap_handler_5
,
110 (int)&trap_handler_6
,
111 (int)&trap_handler_7
,
112 (int)&trap_handler_8
,
113 (int)&trap_handler_9
,
114 (int)&trap_handler_10
,
115 (int)&trap_handler_11
,
116 (int)&trap_handler_12
,
117 (int)&trap_handler_13
,
118 (int)&trap_handler_14
,
119 (int)&trap_handler_15
,
122 #endif /* CONFIG_SMP */
125 * PURPOSE: Object describing each isr
126 * NOTE: The data in this table is only modified at passsive level but can
127 * be accessed at any irq level.
136 ISR_TABLE
, *PISR_TABLE
;
139 static ISR_TABLE IsrTable
[NR_TRAPS
][MAXIMUM_PROCESSORS
];
141 static ISR_TABLE IsrTable
[NR_TRAPS
][1];
144 #define TAG_ISR_LOCK 'LRSI'
146 /* FUNCTIONS ****************************************************************/
151 KeInitInterrupts (VOID
)
156 * Setup the IDT entries to point to the interrupt handlers
158 for (i
=0;i
<NR_TRAPS
;i
++)
161 for (j
= 0; j
< MAXIMUM_PROCESSORS
; j
++)
166 InitializeListHead(&IsrTable
[i
][j
].ListHead
);
167 KeInitializeSpinLock(&IsrTable
[i
][j
].Lock
);
168 IsrTable
[i
][j
].Count
= 0;
174 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame
,
175 PKTRAP_FRAME TrapFrame
)
180 KeTrapFrameToIRQTrapFrame(PKTRAP_FRAME TrapFrame
,
181 PKIRQ_TRAPFRAME IrqTrapFrame
)
186 * NOTE: On Windows this function takes exactly one parameter and EBP is
187 * guaranteed to point to KTRAP_FRAME. The function is used only
188 * by HAL, so there's no point in keeping that prototype.
194 KeUpdateRunTime(IN PKTRAP_FRAME TrapFrame
,
197 PKPRCB Prcb
= KeGetCurrentPrcb();
198 PKTHREAD CurrentThread
;
199 PKPROCESS CurrentProcess
;
201 /* Make sure we don't go further if we're in early boot phase. */
202 if (!(Prcb
) || !(Prcb
->CurrentThread
)) return;
204 /* Get the current thread and process */
205 CurrentThread
= Prcb
->CurrentThread
;
206 CurrentProcess
= CurrentThread
->ApcState
.Process
;
208 /* Check if we came from user mode */
209 if (TrapFrame
->PreviousMode
!= KernelMode
)
211 /* Update user times */
212 CurrentThread
->UserTime
++;
213 InterlockedIncrement((PLONG
)&CurrentProcess
->UserTime
);
219 if (Irql
> DISPATCH_LEVEL
)
221 /* This was an interrupt */
222 Prcb
->InterruptTime
++;
224 else if ((Irql
< DISPATCH_LEVEL
) || !(Prcb
->DpcRoutineActive
))
226 /* This was normal kernel time */
227 CurrentThread
->KernelTime
++;
228 InterlockedIncrement((PLONG
)&CurrentProcess
->KernelTime
);
230 else if (Irql
== DISPATCH_LEVEL
)
232 /* This was DPC time */
236 /* Update CPU kernel time in all cases */
240 /* Set the last DPC Count and request rate */
241 Prcb
->DpcLastCount
= Prcb
->DpcData
[0].DpcCount
;
242 Prcb
->DpcRequestRate
= ((Prcb
->DpcData
[0].DpcCount
- Prcb
->DpcLastCount
) +
243 Prcb
->DpcRequestRate
) / 2;
245 /* Check if we should request a DPC */
246 if ((Prcb
->DpcData
[0].DpcQueueDepth
) && !(Prcb
->DpcRoutineActive
))
249 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
251 /* Update the depth if needed */
252 if ((Prcb
->DpcRequestRate
< KiIdealDpcRate
) &&
253 (Prcb
->MaximumDpcQueueDepth
> 1))
255 /* Decrease the maximum depth by one */
256 Prcb
->MaximumDpcQueueDepth
--;
261 /* Decrease the adjustment threshold */
262 if (!(--Prcb
->AdjustDpcThreshold
))
264 /* We've hit 0, reset it */
265 Prcb
->AdjustDpcThreshold
= KiAdjustDpcThreshold
;
267 /* Check if we've hit queue maximum */
268 if (KiMaximumDpcQueueDepth
!= Prcb
->MaximumDpcQueueDepth
)
270 /* Increase maximum by one */
271 Prcb
->MaximumDpcQueueDepth
++;
277 * If we're at end of quantum request software interrupt. The rest
278 * is handled in KiDispatchInterrupt.
280 * NOTE: If one stays at DISPATCH_LEVEL for a long time the DPC routine
281 * which checks for quantum end will not be executed and decrementing
282 * the quantum here can result in overflow. This is not a problem since
283 * we don't care about the quantum value anymore after the QuantumEnd
286 if ((CurrentThread
->Quantum
-= 3) <= 0)
288 Prcb
->QuantumEnd
= TRUE
;
289 HalRequestSoftwareInterrupt(DISPATCH_LEVEL
);
295 * NOTE: On Windows this function takes exactly zero parameters and EBP is
296 * guaranteed to point to KTRAP_FRAME. Also [esp+0] contains an IRQL.
297 * The function is used only by HAL, so there's no point in keeping
304 KeUpdateSystemTime(IN PKTRAP_FRAME TrapFrame
,
310 ASSERT(KeGetCurrentIrql() == PROFILE_LEVEL
);
312 /* Update interrupt time */
313 Time
.LowPart
= SharedUserData
->InterruptTime
.LowPart
;
314 Time
.HighPart
= SharedUserData
->InterruptTime
.High1Time
;
315 Time
.QuadPart
+= Increment
;
316 SharedUserData
->InterruptTime
.High2Time
= Time
.u
.HighPart
;
317 SharedUserData
->InterruptTime
.LowPart
= Time
.u
.LowPart
;
318 SharedUserData
->InterruptTime
.High1Time
= Time
.u
.HighPart
;
320 /* Increase the tick offset */
321 KiTickOffset
-= Increment
;
322 OldOffset
= KiTickOffset
;
324 /* Check if this isn't a tick yet */
325 if (KiTickOffset
> 0)
328 KeInsertQueueDpc(&KiExpireTimerDpc
, 0, 0);
332 /* Setup time structure for system time */
333 Time
.LowPart
= SharedUserData
->SystemTime
.LowPart
;
334 Time
.HighPart
= SharedUserData
->SystemTime
.High1Time
;
335 Time
.QuadPart
+= KeTimeAdjustment
;
336 SharedUserData
->SystemTime
.High2Time
= Time
.HighPart
;
337 SharedUserData
->SystemTime
.LowPart
= Time
.LowPart
;
338 SharedUserData
->SystemTime
.High1Time
= Time
.HighPart
;
340 /* Setup time structure for tick time */
341 Time
.LowPart
= KeTickCount
.LowPart
;
342 Time
.HighPart
= KeTickCount
.High1Time
;
344 KeTickCount
.High2Time
= Time
.HighPart
;
345 KeTickCount
.LowPart
= Time
.LowPart
;
346 KeTickCount
.High1Time
= Time
.HighPart
;
347 SharedUserData
->TickCount
.High2Time
= Time
.HighPart
;
348 SharedUserData
->TickCount
.LowPart
= Time
.LowPart
;
349 SharedUserData
->TickCount
.High1Time
= Time
.HighPart
;
351 /* Queue a DPC that will expire timers */
352 KeInsertQueueDpc(&KiExpireTimerDpc
, 0, 0);
355 /* Update process and thread times */
358 /* This was a tick, calculate the next one */
359 KiTickOffset
+= KeMaximumIncrement
;
360 KeUpdateRunTime(TrapFrame
, Irql
);
365 KiInterruptDispatch2 (ULONG vector
, KIRQL old_level
)
367 * FUNCTION: Calls all the interrupt handlers for a given irq.
369 * vector - The number of the vector to call handlers for.
370 * old_level - The irql of the processor when the irq took place.
371 * NOTES: Must be called at DIRQL.
377 PISR_TABLE CurrentIsr
;
379 DPRINT("I(0x%.08x, 0x%.08x)\n", vector
, old_level
);
382 * Iterate the list until one of the isr tells us its device interrupted
384 CurrentIsr
= &IsrTable
[vector
][(ULONG
)KeGetCurrentProcessorNumber()];
386 KiAcquireSpinLock(&CurrentIsr
->Lock
);
389 current
= CurrentIsr
->ListHead
.Flink
;
391 while (current
!= &CurrentIsr
->ListHead
)
393 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,InterruptListEntry
);
394 oldlvl
= KeAcquireInterruptSpinLock(isr
);
395 if (isr
->ServiceRoutine(isr
, isr
->ServiceContext
))
397 KeReleaseInterruptSpinLock(isr
, oldlvl
);
400 KeReleaseInterruptSpinLock(isr
, oldlvl
);
401 current
= current
->Flink
;
403 KiReleaseSpinLock(&CurrentIsr
->Lock
);
407 KiInterruptDispatch3 (ULONG vector
, PKIRQ_TRAPFRAME Trapframe
)
409 * FUNCTION: Calls the irq specific handler for an irq
411 * irq = IRQ that has interrupted
415 KTRAP_FRAME KernelTrapFrame
;
416 PKTHREAD CurrentThread
;
417 PKTRAP_FRAME OldTrapFrame
=NULL
;
420 * At this point we have interrupts disabled, nothing has been done to
424 KeGetCurrentPrcb()->InterruptCount
++;
427 * Notify the rest of the kernel of the raised irq level. For the
428 * default HAL this will send an EOI to the PIC and alter the IRQL.
430 if (!HalBeginSystemInterrupt (vector
,
440 * NOTE: Only higher priority interrupts will get through
447 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
448 KeUpdateSystemTime(&KernelTrapFrame
, old_level
, 100000);
454 * Actually call the ISR.
456 KiInterruptDispatch2(vector
, old_level
);
460 * End the system interrupt.
464 if (old_level
==PASSIVE_LEVEL
)
466 HalEndSystemInterrupt (APC_LEVEL
, 0);
468 CurrentThread
= KeGetCurrentThread();
469 if (CurrentThread
!=NULL
&& CurrentThread
->ApcState
.UserApcPending
)
471 if (CurrentThread
->TrapFrame
== NULL
)
473 OldTrapFrame
= CurrentThread
->TrapFrame
;
474 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
475 CurrentThread
->TrapFrame
= &KernelTrapFrame
;
479 KiDeliverApc(UserMode
, NULL
, NULL
);
482 ASSERT(KeGetCurrentThread() == CurrentThread
);
483 if (CurrentThread
->TrapFrame
== &KernelTrapFrame
)
485 KeTrapFrameToIRQTrapFrame(&KernelTrapFrame
, Trapframe
);
486 CurrentThread
->TrapFrame
= OldTrapFrame
;
489 KeLowerIrql(PASSIVE_LEVEL
);
493 HalEndSystemInterrupt (old_level
, 0);
502 PLIST_ENTRY current_entry
;
507 for (i
=0;i
<NR_TRAPS
;i
++)
510 KeRaiseIrql(i
,&oldlvl
);
512 for (j
=0; j
< KeNumberProcessors
; j
++)
514 KiAcquireSpinLock(&IsrTable
[i
][j
].Lock
);
516 current_entry
= IsrTable
[i
][j
].ListHead
.Flink
;
517 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,InterruptListEntry
);
518 while (current_entry
!=&(IsrTable
[i
][j
].ListHead
))
520 if (printed
== FALSE
)
523 DPRINT("For irq %x:\n",i
);
525 DPRINT(" Isr %x\n",current
);
526 current_entry
= current_entry
->Flink
;
527 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,InterruptListEntry
);
529 KiReleaseSpinLock(&IsrTable
[i
][j
].Lock
);
540 KeConnectInterrupt(PKINTERRUPT InterruptObject
)
542 KIRQL oldlvl
,synch_oldlvl
;
543 PKINTERRUPT ListHead
;
545 PISR_TABLE CurrentIsr
;
548 DPRINT("KeConnectInterrupt()\n");
550 Vector
= InterruptObject
->Vector
;
552 if (Vector
< 0 || Vector
>= NR_TRAPS
)
555 ASSERT (InterruptObject
->Number
< KeNumberProcessors
);
557 KeSetSystemAffinityThread(1 << InterruptObject
->Number
);
559 CurrentIsr
= &IsrTable
[Vector
][(ULONG
)InterruptObject
->Number
];
561 KeRaiseIrql(Vector
,&oldlvl
);
562 KiAcquireSpinLock(&CurrentIsr
->Lock
);
565 * Check if the vector is already in use that we can share it
567 if (!IsListEmpty(&CurrentIsr
->ListHead
))
569 ListHead
= CONTAINING_RECORD(CurrentIsr
->ListHead
.Flink
,KINTERRUPT
,InterruptListEntry
);
570 if (InterruptObject
->ShareVector
== FALSE
|| ListHead
->ShareVector
==FALSE
)
572 KiReleaseSpinLock(&CurrentIsr
->Lock
);
574 KeRevertToUserAffinityThread();
579 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
581 DPRINT("%x %x\n",CurrentIsr
->ListHead
.Flink
, CurrentIsr
->ListHead
.Blink
);
583 Result
= HalEnableSystemInterrupt(Vector
, InterruptObject
->Irql
, InterruptObject
->Mode
);
586 InsertTailList(&CurrentIsr
->ListHead
,&InterruptObject
->InterruptListEntry
);
587 DPRINT("%x %x\n",InterruptObject
->InterruptListEntry
.Flink
, InterruptObject
->InterruptListEntry
.Blink
);
590 InterruptObject
->Connected
= TRUE
;
591 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
594 * Release the table spinlock
596 KiReleaseSpinLock(&CurrentIsr
->Lock
);
601 KeRevertToUserAffinityThread();
609 * FUNCTION: Releases a drivers isr
611 * InterruptObject = isr to release
615 KeDisconnectInterrupt(PKINTERRUPT InterruptObject
)
617 KIRQL oldlvl
,synch_oldlvl
;
618 PISR_TABLE CurrentIsr
;
621 DPRINT1("KeDisconnectInterrupt\n");
622 ASSERT (InterruptObject
->Number
< KeNumberProcessors
);
624 /* Set the affinity */
625 KeSetSystemAffinityThread(1 << InterruptObject
->Number
);
627 /* Get the ISR Tabe */
628 CurrentIsr
= &IsrTable
[InterruptObject
->Vector
]
629 [(ULONG
)InterruptObject
->Number
];
631 /* Raise IRQL to required level and lock table */
632 KeRaiseIrql(InterruptObject
->Vector
,&oldlvl
);
633 KiAcquireSpinLock(&CurrentIsr
->Lock
);
635 /* Check if it's actually connected */
636 if ((State
= InterruptObject
->Connected
))
638 /* Lock the Interrupt */
639 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
641 /* Remove this one, and check if all are gone */
642 RemoveEntryList(&InterruptObject
->InterruptListEntry
);
643 if (IsListEmpty(&CurrentIsr
->ListHead
))
645 /* Completely Disable the Interrupt */
646 HalDisableSystemInterrupt(InterruptObject
->Vector
, InterruptObject
->Irql
);
650 InterruptObject
->Connected
= FALSE
;
652 /* Release the interrupt lock */
653 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
655 /* Release the table spinlock */
656 KiReleaseSpinLock(&CurrentIsr
->Lock
);
659 /* Go back to default affinity */
660 KeRevertToUserAffinityThread();
662 /* Return Old Interrupt State */
671 KeInitializeInterrupt(PKINTERRUPT Interrupt
,
672 PKSERVICE_ROUTINE ServiceRoutine
,
673 PVOID ServiceContext
,
674 PKSPIN_LOCK SpinLock
,
677 KIRQL SynchronizeIrql
,
678 KINTERRUPT_MODE InterruptMode
,
680 CHAR ProcessorNumber
,
681 BOOLEAN FloatingSave
)
683 /* Set the Interrupt Header */
684 Interrupt
->Type
= InterruptObject
;
685 Interrupt
->Size
= sizeof(KINTERRUPT
);
687 /* Check if we got a spinlock */
690 Interrupt
->ActualLock
= SpinLock
;
694 /* This means we'll be usin the built-in one */
695 KeInitializeSpinLock(&Interrupt
->SpinLock
);
696 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
699 /* Set the other settings */
700 Interrupt
->ServiceRoutine
= ServiceRoutine
;
701 Interrupt
->ServiceContext
= ServiceContext
;
702 Interrupt
->Vector
= Vector
;
703 Interrupt
->Irql
= Irql
;
704 Interrupt
->SynchronizeIrql
= SynchronizeIrql
;
705 Interrupt
->Mode
= InterruptMode
;
706 Interrupt
->ShareVector
= ShareVector
;
707 Interrupt
->Number
= ProcessorNumber
;
708 Interrupt
->FloatingSave
= FloatingSave
;
710 /* Disconnect it at first */
711 Interrupt
->Connected
= FALSE
;
714 VOID
KePrintInterruptStatistic(VOID
)
718 for (j
= 0; j
< KeNumberProcessors
; j
++)
720 DPRINT1("CPU%d:\n", j
);
721 for (i
= 0; i
< NR_TRAPS
; i
++)
723 if (IsrTable
[i
][j
].Count
)
725 DPRINT1(" Irq %x(%d): %d\n", i
, i
, IsrTable
[i
][j
].Count
);
733 KeDisableInterrupts(VOID
)
739 Return
= (Flags
& 0x8000) ? TRUE
: FALSE
;
741 /* Disable interrupts */
748 KdpServiceDispatcher(ULONG Service
, PCHAR Buffer
, ULONG Length
);
750 typedef ULONG (*PSYSCALL_FUN
)
751 (ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
);
755 KiSystemService(ppc_trap_frame_t
*trap_frame
)
758 PKSYSTEM_ROUTINE SystemRoutine
;
759 PSYSCALL_FUN SyscallFunction
;
761 switch(trap_frame
->gpr
[0])
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]);
781 /* Handle a normal system call */
784 ((PSYSCALL_FUN
*)KeServiceDescriptorTable
785 [trap_frame
->gpr
[0] >> 12].Base
)[trap_frame
->gpr
[0] & 0xfff];
786 trap_frame
->gpr
[3] = SyscallFunction
796 trap_frame
->gpr
[12]);