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>
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 /* Queue a DPC that will expire timers */
353 KeInsertQueueDpc(&KiExpireTimerDpc
, 0, 0);
356 /* Update process and thread times */
359 /* This was a tick, calculate the next one */
360 KiTickOffset
+= KeMaximumIncrement
;
361 KeUpdateRunTime(TrapFrame
, Irql
);
366 KiInterruptDispatch2 (ULONG vector
, KIRQL old_level
)
368 * FUNCTION: Calls all the interrupt handlers for a given irq.
370 * vector - The number of the vector to call handlers for.
371 * old_level - The irql of the processor when the irq took place.
372 * NOTES: Must be called at DIRQL.
378 PISR_TABLE CurrentIsr
;
380 DPRINT("I(0x%.08x, 0x%.08x)\n", vector
, old_level
);
383 * Iterate the list until one of the isr tells us its device interrupted
385 CurrentIsr
= &IsrTable
[vector
][(ULONG
)KeGetCurrentProcessorNumber()];
387 KiAcquireSpinLock(&CurrentIsr
->Lock
);
390 current
= CurrentIsr
->ListHead
.Flink
;
392 while (current
!= &CurrentIsr
->ListHead
)
394 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,InterruptListEntry
);
395 oldlvl
= KeAcquireInterruptSpinLock(isr
);
396 if (isr
->ServiceRoutine(isr
, isr
->ServiceContext
))
398 KeReleaseInterruptSpinLock(isr
, oldlvl
);
401 KeReleaseInterruptSpinLock(isr
, oldlvl
);
402 current
= current
->Flink
;
404 KiReleaseSpinLock(&CurrentIsr
->Lock
);
408 KiInterruptDispatch3 (ULONG vector
, PKIRQ_TRAPFRAME Trapframe
)
410 * FUNCTION: Calls the irq specific handler for an irq
412 * irq = IRQ that has interrupted
416 KTRAP_FRAME KernelTrapFrame
;
417 PKTHREAD CurrentThread
;
418 PKTRAP_FRAME OldTrapFrame
=NULL
;
421 * At this point we have interrupts disabled, nothing has been done to
425 KeGetCurrentPrcb()->InterruptCount
++;
428 * Notify the rest of the kernel of the raised irq level. For the
429 * default HAL this will send an EOI to the PIC and alter the IRQL.
431 if (!HalBeginSystemInterrupt (vector
,
441 * NOTE: Only higher priority interrupts will get through
448 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
449 KeUpdateSystemTime(&KernelTrapFrame
, old_level
, 100000);
455 * Actually call the ISR.
457 KiInterruptDispatch2(vector
, old_level
);
461 * End the system interrupt.
465 if (old_level
==PASSIVE_LEVEL
)
467 HalEndSystemInterrupt (APC_LEVEL
, 0);
469 CurrentThread
= KeGetCurrentThread();
470 if (CurrentThread
!=NULL
&& CurrentThread
->ApcState
.UserApcPending
)
472 if (CurrentThread
->TrapFrame
== NULL
)
474 OldTrapFrame
= CurrentThread
->TrapFrame
;
475 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
476 CurrentThread
->TrapFrame
= &KernelTrapFrame
;
480 KiDeliverApc(UserMode
, NULL
, NULL
);
483 ASSERT(KeGetCurrentThread() == CurrentThread
);
484 if (CurrentThread
->TrapFrame
== &KernelTrapFrame
)
486 KeTrapFrameToIRQTrapFrame(&KernelTrapFrame
, Trapframe
);
487 CurrentThread
->TrapFrame
= OldTrapFrame
;
490 KeLowerIrql(PASSIVE_LEVEL
);
494 HalEndSystemInterrupt (old_level
, 0);
503 PLIST_ENTRY current_entry
;
508 for (i
=0;i
<NR_TRAPS
;i
++)
511 KeRaiseIrql(i
,&oldlvl
);
513 for (j
=0; j
< KeNumberProcessors
; j
++)
515 KiAcquireSpinLock(&IsrTable
[i
][j
].Lock
);
517 current_entry
= IsrTable
[i
][j
].ListHead
.Flink
;
518 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,InterruptListEntry
);
519 while (current_entry
!=&(IsrTable
[i
][j
].ListHead
))
521 if (printed
== FALSE
)
524 DPRINT("For irq %x:\n",i
);
526 DPRINT(" Isr %x\n",current
);
527 current_entry
= current_entry
->Flink
;
528 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,InterruptListEntry
);
530 KiReleaseSpinLock(&IsrTable
[i
][j
].Lock
);
541 KeConnectInterrupt(PKINTERRUPT InterruptObject
)
543 KIRQL oldlvl
,synch_oldlvl
;
544 PKINTERRUPT ListHead
;
546 PISR_TABLE CurrentIsr
;
549 DPRINT("KeConnectInterrupt()\n");
551 Vector
= InterruptObject
->Vector
;
553 if (Vector
< 0 || Vector
>= NR_TRAPS
)
556 ASSERT (InterruptObject
->Number
< KeNumberProcessors
);
558 KeSetSystemAffinityThread(1 << InterruptObject
->Number
);
560 CurrentIsr
= &IsrTable
[Vector
][(ULONG
)InterruptObject
->Number
];
562 KeRaiseIrql(Vector
,&oldlvl
);
563 KiAcquireSpinLock(&CurrentIsr
->Lock
);
566 * Check if the vector is already in use that we can share it
568 if (!IsListEmpty(&CurrentIsr
->ListHead
))
570 ListHead
= CONTAINING_RECORD(CurrentIsr
->ListHead
.Flink
,KINTERRUPT
,InterruptListEntry
);
571 if (InterruptObject
->ShareVector
== FALSE
|| ListHead
->ShareVector
==FALSE
)
573 KiReleaseSpinLock(&CurrentIsr
->Lock
);
575 KeRevertToUserAffinityThread();
580 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
582 DPRINT("%x %x\n",CurrentIsr
->ListHead
.Flink
, CurrentIsr
->ListHead
.Blink
);
584 Result
= HalEnableSystemInterrupt(Vector
, InterruptObject
->Irql
, InterruptObject
->Mode
);
587 InsertTailList(&CurrentIsr
->ListHead
,&InterruptObject
->InterruptListEntry
);
588 DPRINT("%x %x\n",InterruptObject
->InterruptListEntry
.Flink
, InterruptObject
->InterruptListEntry
.Blink
);
591 InterruptObject
->Connected
= TRUE
;
592 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
595 * Release the table spinlock
597 KiReleaseSpinLock(&CurrentIsr
->Lock
);
602 KeRevertToUserAffinityThread();
610 * FUNCTION: Releases a drivers isr
612 * InterruptObject = isr to release
616 KeDisconnectInterrupt(PKINTERRUPT InterruptObject
)
618 KIRQL oldlvl
,synch_oldlvl
;
619 PISR_TABLE CurrentIsr
;
622 DPRINT1("KeDisconnectInterrupt\n");
623 ASSERT (InterruptObject
->Number
< KeNumberProcessors
);
625 /* Set the affinity */
626 KeSetSystemAffinityThread(1 << InterruptObject
->Number
);
628 /* Get the ISR Tabe */
629 CurrentIsr
= &IsrTable
[InterruptObject
->Vector
]
630 [(ULONG
)InterruptObject
->Number
];
632 /* Raise IRQL to required level and lock table */
633 KeRaiseIrql(InterruptObject
->Vector
,&oldlvl
);
634 KiAcquireSpinLock(&CurrentIsr
->Lock
);
636 /* Check if it's actually connected */
637 if ((State
= InterruptObject
->Connected
))
639 /* Lock the Interrupt */
640 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
642 /* Remove this one, and check if all are gone */
643 RemoveEntryList(&InterruptObject
->InterruptListEntry
);
644 if (IsListEmpty(&CurrentIsr
->ListHead
))
646 /* Completely Disable the Interrupt */
647 HalDisableSystemInterrupt(InterruptObject
->Vector
, InterruptObject
->Irql
);
651 InterruptObject
->Connected
= FALSE
;
653 /* Release the interrupt lock */
654 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
656 /* Release the table spinlock */
657 KiReleaseSpinLock(&CurrentIsr
->Lock
);
660 /* Go back to default affinity */
661 KeRevertToUserAffinityThread();
663 /* Return Old Interrupt State */
672 KeInitializeInterrupt(PKINTERRUPT Interrupt
,
673 PKSERVICE_ROUTINE ServiceRoutine
,
674 PVOID ServiceContext
,
675 PKSPIN_LOCK SpinLock
,
678 KIRQL SynchronizeIrql
,
679 KINTERRUPT_MODE InterruptMode
,
681 CHAR ProcessorNumber
,
682 BOOLEAN FloatingSave
)
684 /* Set the Interrupt Header */
685 Interrupt
->Type
= InterruptObject
;
686 Interrupt
->Size
= sizeof(KINTERRUPT
);
688 /* Check if we got a spinlock */
691 Interrupt
->ActualLock
= SpinLock
;
695 /* This means we'll be usin the built-in one */
696 KeInitializeSpinLock(&Interrupt
->SpinLock
);
697 Interrupt
->ActualLock
= &Interrupt
->SpinLock
;
700 /* Set the other settings */
701 Interrupt
->ServiceRoutine
= ServiceRoutine
;
702 Interrupt
->ServiceContext
= ServiceContext
;
703 Interrupt
->Vector
= Vector
;
704 Interrupt
->Irql
= Irql
;
705 Interrupt
->SynchronizeIrql
= SynchronizeIrql
;
706 Interrupt
->Mode
= InterruptMode
;
707 Interrupt
->ShareVector
= ShareVector
;
708 Interrupt
->Number
= ProcessorNumber
;
709 Interrupt
->FloatingSave
= FloatingSave
;
711 /* Disconnect it at first */
712 Interrupt
->Connected
= FALSE
;
715 VOID
KePrintInterruptStatistic(VOID
)
719 for (j
= 0; j
< KeNumberProcessors
; j
++)
721 DPRINT1("CPU%d:\n", j
);
722 for (i
= 0; i
< NR_TRAPS
; i
++)
724 if (IsrTable
[i
][j
].Count
)
726 DPRINT1(" Irq %x(%d): %d\n", i
, i
, IsrTable
[i
][j
].Count
);
734 KeDisableInterrupts(VOID
)
740 Return
= (Flags
& 0x8000) ? TRUE
: FALSE
;
742 /* Disable interrupts */
749 KdpServiceDispatcher(ULONG Service
, PCHAR Buffer
, ULONG Length
);
751 typedef ULONG (*PSYSCALL_FUN
)
752 (ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
,ULONG
);
756 KiSystemService(ppc_trap_frame_t
*trap_frame
)
759 PKSYSTEM_ROUTINE SystemRoutine
;
760 PSYSCALL_FUN SyscallFunction
;
762 switch(trap_frame
->gpr
[0])
764 case 0x10000: /* DebugService */
765 for( i
= 0; i
< trap_frame
->gpr
[5]; i
++ )
767 PearPCDebug(((PCHAR
)trap_frame
->gpr
[4])[i
]);
768 WRITE_PORT_UCHAR((PVOID
)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]);
782 /* Handle a normal system call */
785 ((PSYSCALL_FUN
*)KeServiceDescriptorTable
786 [trap_frame
->gpr
[0] >> 12].Base
)[trap_frame
->gpr
[0] & 0xfff];
787 trap_frame
->gpr
[3] = SyscallFunction
797 trap_frame
->gpr
[12]);