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)
13 * NOTE: In general the PIC interrupt priority facilities are used to
14 * preserve the NT IRQL semantics, global interrupt disables are only used
15 * to keep the PIC in a consistent state
19 /* INCLUDES ****************************************************************/
22 #include <../hal/halx86/include/halirq.h>
25 #include <internal/debug.h>
27 /* GLOBALS *****************************************************************/
32 #define STR(x) __STR(x)
34 #define INT_NAME(intnum) _KiUnexpectedInterrupt##intnum
35 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
37 #define BUILD_COMMON_INTERRUPT_HANDLER() \
39 "_KiCommonInterrupt:\n\t" \
45 "movl $0xceafbeef,%eax\n\t" \
47 "movl $" STR(KERNEL_DS) ",%eax\n\t" \
51 "movl $" STR(PCR_SELECTOR) ",%eax\n\t" \
55 "call _KiInterruptDispatch\n\t" \
66 #define BUILD_INTERRUPT_HANDLER(intnum) \
67 VOID INT_NAME2(intnum)(VOID); \
69 STR(INT_NAME(intnum)) ":\n\t" \
71 "movl $0x" STR(intnum) ",%ebx\n\t" \
72 "jmp _KiCommonInterrupt");
75 /* Interrupt handlers and declarations */
78 BUILD_INTERRUPT_HANDLER(x##y)
81 B(x,0) B(x,1) B(x,2) B(x,3) \
82 B(x,4) B(x,5) B(x,6) B(x,7) \
83 B(x,8) B(x,9) B(x,A) B(x,B) \
84 B(x,C) B(x,D) B(x,E) B(x,F)
87 BUILD_COMMON_INTERRUPT_HANDLER()
88 B16(3) B16(4) B16(5) B16(6)
89 B16(7) B16(8) B16(9) B16(A
)
90 B16(B
) B16(C
) B16(D
) B16(E
)
97 /* Interrupt handler list */
100 (ULONG)& INT_NAME2(x##y)
103 L(x,0), L(x,1), L(x,2), L(x,3), \
104 L(x,4), L(x,5), L(x,6), L(x,7), \
105 L(x,8), L(x,9), L(x,A), L(x,B), \
106 L(x,C), L(x,D), L(x,E), L(x,F)
108 static ULONG irq_handler
[ROUND_UP(NR_IRQS
, 16)] = {
109 L16(3), L16(4), L16(5), L16(6),
110 L16(7), L16(8), L16(9), L16(A
),
111 L16(B
), L16(C
), L16(D
), L16(E
)
117 #else /* CONFIG_SMP */
119 void irq_handler_0(void);
120 void irq_handler_1(void);
121 void irq_handler_2(void);
122 void irq_handler_3(void);
123 void irq_handler_4(void);
124 void irq_handler_5(void);
125 void irq_handler_6(void);
126 void irq_handler_7(void);
127 void irq_handler_8(void);
128 void irq_handler_9(void);
129 void irq_handler_10(void);
130 void irq_handler_11(void);
131 void irq_handler_12(void);
132 void irq_handler_13(void);
133 void irq_handler_14(void);
134 void irq_handler_15(void);
136 static unsigned int irq_handler
[NR_IRQS
]=
148 (int)&irq_handler_10
,
149 (int)&irq_handler_11
,
150 (int)&irq_handler_12
,
151 (int)&irq_handler_13
,
152 (int)&irq_handler_14
,
153 (int)&irq_handler_15
,
156 #endif /* CONFIG_SMP */
159 * PURPOSE: Object describing each isr
160 * NOTE: The data in this table is only modified at passsive level but can
161 * be accessed at any irq level.
170 ISR_TABLE
, *PISR_TABLE
;
173 static ISR_TABLE IsrTable
[NR_IRQS
][MAXIMUM_PROCESSORS
];
175 static ISR_TABLE IsrTable
[NR_IRQS
][1];
178 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
180 /* FUNCTIONS ****************************************************************/
182 #define PRESENT (0x8000)
183 #define I486_INTERRUPT_GATE (0xe00)
186 KeInitInterrupts (VOID
)
192 * Setup the IDT entries to point to the interrupt handlers
194 for (i
=0;i
<NR_IRQS
;i
++)
196 KiIdt
[IRQ_BASE
+i
].a
=(irq_handler
[i
]&0xffff)+(KERNEL_CS
<<16);
197 KiIdt
[IRQ_BASE
+i
].b
=(irq_handler
[i
]&0xffff0000)+PRESENT
+
200 for (j
= 0; j
< MAXIMUM_PROCESSORS
; j
++)
205 InitializeListHead(&IsrTable
[i
][j
].ListHead
);
206 KeInitializeSpinLock(&IsrTable
[i
][j
].Lock
);
207 IsrTable
[i
][j
].Count
= 0;
213 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame
,
214 PKTRAP_FRAME TrapFrame
)
216 TrapFrame
->Gs
= (USHORT
)IrqTrapFrame
->Gs
;
217 TrapFrame
->Fs
= (USHORT
)IrqTrapFrame
->Fs
;
218 TrapFrame
->Es
= (USHORT
)IrqTrapFrame
->Es
;
219 TrapFrame
->Ds
= (USHORT
)IrqTrapFrame
->Ds
;
220 TrapFrame
->Eax
= IrqTrapFrame
->Eax
;
221 TrapFrame
->Ecx
= IrqTrapFrame
->Ecx
;
222 TrapFrame
->Edx
= IrqTrapFrame
->Edx
;
223 TrapFrame
->Ebx
= IrqTrapFrame
->Ebx
;
224 TrapFrame
->Esp
= IrqTrapFrame
->Esp
;
225 TrapFrame
->Ebp
= IrqTrapFrame
->Ebp
;
226 TrapFrame
->Esi
= IrqTrapFrame
->Esi
;
227 TrapFrame
->Edi
= IrqTrapFrame
->Edi
;
228 TrapFrame
->Eip
= IrqTrapFrame
->Eip
;
229 TrapFrame
->Cs
= IrqTrapFrame
->Cs
;
230 TrapFrame
->Eflags
= IrqTrapFrame
->Eflags
;
234 KeTrapFrameToIRQTrapFrame(PKTRAP_FRAME TrapFrame
,
235 PKIRQ_TRAPFRAME IrqTrapFrame
)
237 IrqTrapFrame
->Gs
= TrapFrame
->Gs
;
238 IrqTrapFrame
->Fs
= TrapFrame
->Fs
;
239 IrqTrapFrame
->Es
= TrapFrame
->Es
;
240 IrqTrapFrame
->Ds
= TrapFrame
->Ds
;
241 IrqTrapFrame
->Eax
= TrapFrame
->Eax
;
242 IrqTrapFrame
->Ecx
= TrapFrame
->Ecx
;
243 IrqTrapFrame
->Edx
= TrapFrame
->Edx
;
244 IrqTrapFrame
->Ebx
= TrapFrame
->Ebx
;
245 IrqTrapFrame
->Esp
= TrapFrame
->Esp
;
246 IrqTrapFrame
->Ebp
= TrapFrame
->Ebp
;
247 IrqTrapFrame
->Esi
= TrapFrame
->Esi
;
248 IrqTrapFrame
->Edi
= TrapFrame
->Edi
;
249 IrqTrapFrame
->Eip
= TrapFrame
->Eip
;
250 IrqTrapFrame
->Cs
= TrapFrame
->Cs
;
251 IrqTrapFrame
->Eflags
= TrapFrame
->Eflags
;
255 KiInterruptDispatch2 (ULONG vector
, KIRQL old_level
)
257 * FUNCTION: Calls all the interrupt handlers for a given irq.
259 * vector - The number of the vector to call handlers for.
260 * old_level - The irql of the processor when the irq took place.
261 * NOTES: Must be called at DIRQL.
267 PISR_TABLE CurrentIsr
;
269 DPRINT("I(0x%.08x, 0x%.08x)\n", vector
, old_level
);
272 * Iterate the list until one of the isr tells us its device interrupted
274 CurrentIsr
= &IsrTable
[vector
- IRQ_BASE
][(ULONG
)KeGetCurrentProcessorNumber()];
276 KiAcquireSpinLock(&CurrentIsr
->Lock
);
279 current
= CurrentIsr
->ListHead
.Flink
;
281 while (current
!= &CurrentIsr
->ListHead
)
283 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
284 oldlvl
= KeAcquireInterruptSpinLock(isr
);
285 if (isr
->ServiceRoutine(isr
, isr
->ServiceContext
))
287 KeReleaseInterruptSpinLock(isr
, oldlvl
);
290 KeReleaseInterruptSpinLock(isr
, oldlvl
);
291 current
= current
->Flink
;
293 KiReleaseSpinLock(&CurrentIsr
->Lock
);
297 KiInterruptDispatch (ULONG vector
, PKIRQ_TRAPFRAME Trapframe
)
299 * FUNCTION: Calls the irq specific handler for an irq
301 * irq = IRQ that has interrupted
305 KTRAP_FRAME KernelTrapFrame
;
306 PKTHREAD CurrentThread
;
307 PKTRAP_FRAME OldTrapFrame
=NULL
;
310 * At this point we have interrupts disabled, nothing has been done to
314 KeGetCurrentPrcb()->InterruptCount
++;
317 * Notify the rest of the kernel of the raised irq level. For the
318 * default HAL this will send an EOI to the PIC and alter the IRQL.
320 if (!HalBeginSystemInterrupt (vector
,
330 * NOTE: Only higher priority interrupts will get through
332 Ke386EnableInterrupts();
335 if (VECTOR2IRQ(vector
) == 0)
337 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
338 KeUpdateSystemTime(&KernelTrapFrame
, old_level
);
344 * Actually call the ISR.
346 KiInterruptDispatch2(vector
, old_level
);
350 * End the system interrupt.
352 Ke386DisableInterrupts();
354 HalEndSystemInterrupt (old_level
, 0);
356 if (old_level
==PASSIVE_LEVEL
&& Trapframe
->Cs
!= KERNEL_CS
)
358 CurrentThread
= KeGetCurrentThread();
359 if (CurrentThread
!=NULL
&& CurrentThread
->Alerted
[1])
361 DPRINT("PID: %d, TID: %d CS %04x/%04x\n",
362 ((PETHREAD
)CurrentThread
)->ThreadsProcess
->UniqueProcessId
,
363 ((PETHREAD
)CurrentThread
)->Cid
.UniqueThread
,
365 CurrentThread
->TrapFrame
? CurrentThread
->TrapFrame
->Cs
: 0);
366 if (CurrentThread
->TrapFrame
== NULL
)
368 OldTrapFrame
= CurrentThread
->TrapFrame
;
369 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
370 CurrentThread
->TrapFrame
= &KernelTrapFrame
;
373 Ke386EnableInterrupts();
374 KiDeliverApc(KernelMode
, NULL
, NULL
);
375 Ke386DisableInterrupts();
377 ASSERT(KeGetCurrentThread() == CurrentThread
);
378 if (CurrentThread
->TrapFrame
== &KernelTrapFrame
)
380 KeTrapFrameToIRQTrapFrame(&KernelTrapFrame
, Trapframe
);
381 CurrentThread
->TrapFrame
= OldTrapFrame
;
391 PLIST_ENTRY current_entry
;
396 for (i
=0;i
<NR_IRQS
;i
++)
399 KeRaiseIrql(VECTOR2IRQL(i
+ IRQ_BASE
),&oldlvl
);
401 for (j
=0; j
< KeNumberProcessors
; j
++)
403 KiAcquireSpinLock(&IsrTable
[i
][j
].Lock
);
405 current_entry
= IsrTable
[i
][j
].ListHead
.Flink
;
406 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,Entry
);
407 while (current_entry
!=&(IsrTable
[i
][j
].ListHead
))
409 if (printed
== FALSE
)
412 DPRINT("For irq %x:\n",i
);
414 DPRINT(" Isr %x\n",current
);
415 current_entry
= current_entry
->Flink
;
416 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,Entry
);
418 KiReleaseSpinLock(&IsrTable
[i
][j
].Lock
);
428 KeConnectInterrupt(PKINTERRUPT InterruptObject
)
430 KIRQL oldlvl
,synch_oldlvl
;
431 PKINTERRUPT ListHead
;
433 PISR_TABLE CurrentIsr
;
436 DPRINT("KeConnectInterrupt()\n");
438 Vector
= InterruptObject
->Vector
;
440 if (Vector
< IRQ_BASE
|| Vector
>= IRQ_BASE
+ NR_IRQS
)
445 ASSERT (InterruptObject
->ProcessorNumber
< KeNumberProcessors
);
447 KeSetSystemAffinityThread(1 << InterruptObject
->ProcessorNumber
);
449 CurrentIsr
= &IsrTable
[Vector
][(ULONG
)InterruptObject
->ProcessorNumber
];
451 KeRaiseIrql(VECTOR2IRQL(Vector
+ IRQ_BASE
),&oldlvl
);
452 KiAcquireSpinLock(&CurrentIsr
->Lock
);
455 * Check if the vector is already in use that we can share it
457 if (!IsListEmpty(&CurrentIsr
->ListHead
))
459 ListHead
= CONTAINING_RECORD(CurrentIsr
->ListHead
.Flink
,KINTERRUPT
,Entry
);
460 if (InterruptObject
->Shareable
== FALSE
|| ListHead
->Shareable
==FALSE
)
462 KiReleaseSpinLock(&CurrentIsr
->Lock
);
464 KeRevertToUserAffinityThread();
469 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
471 DPRINT("%x %x\n",CurrentIsr
->ListHead
.Flink
, CurrentIsr
->ListHead
.Blink
);
473 Result
= HalEnableSystemInterrupt(Vector
+ IRQ_BASE
, InterruptObject
->Irql
, InterruptObject
->InterruptMode
);
476 InsertTailList(&CurrentIsr
->ListHead
,&InterruptObject
->Entry
);
477 DPRINT("%x %x\n",InterruptObject
->Entry
.Flink
, InterruptObject
->Entry
.Blink
);
480 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
483 * Release the table spinlock
485 KiReleaseSpinLock(&CurrentIsr
->Lock
);
490 KeRevertToUserAffinityThread();
500 KeDisconnectInterrupt(PKINTERRUPT InterruptObject
)
502 * FUNCTION: Releases a drivers isr
504 * InterruptObject = isr to release
507 KIRQL oldlvl
,synch_oldlvl
;
508 PISR_TABLE CurrentIsr
;
510 DPRINT("KeDisconnectInterrupt\n");
512 ASSERT (InterruptObject
->ProcessorNumber
< KeNumberProcessors
);
514 KeSetSystemAffinityThread(1 << InterruptObject
->ProcessorNumber
);
516 CurrentIsr
= &IsrTable
[InterruptObject
->Vector
- IRQ_BASE
][(ULONG
)InterruptObject
->ProcessorNumber
];
518 KeRaiseIrql(VECTOR2IRQL(InterruptObject
->Vector
),&oldlvl
);
519 KiAcquireSpinLock(&CurrentIsr
->Lock
);
521 synch_oldlvl
= KeAcquireInterruptSpinLock(InterruptObject
);
523 RemoveEntryList(&InterruptObject
->Entry
);
524 if (IsListEmpty(&CurrentIsr
->ListHead
))
526 HalDisableSystemInterrupt(InterruptObject
->Vector
, 0);
528 KeReleaseInterruptSpinLock(InterruptObject
, synch_oldlvl
);
531 * Release the table spinlock
533 KiReleaseSpinLock(&CurrentIsr
->Lock
);
536 KeRevertToUserAffinityThread();
545 KeInitializeInterrupt(PKINTERRUPT InterruptObject
,
546 PKSERVICE_ROUTINE ServiceRoutine
,
547 PVOID ServiceContext
,
548 PKSPIN_LOCK SpinLock
,
551 KIRQL SynchronizeIrql
,
552 KINTERRUPT_MODE InterruptMode
,
554 CHAR ProcessorNumber
,
555 BOOLEAN FloatingSave
)
557 InterruptObject
->ServiceRoutine
= ServiceRoutine
;
558 InterruptObject
->ServiceContext
= ServiceContext
;
559 InterruptObject
->ActualLock
= SpinLock
;
560 InterruptObject
->Vector
= Vector
;
561 InterruptObject
->Irql
= Irql
;
562 InterruptObject
->SynchLevel
= SynchronizeIrql
;
563 InterruptObject
->InterruptMode
= InterruptMode
;
564 InterruptObject
->Shareable
= ShareVector
;
565 InterruptObject
->ProcessorNumber
= ProcessorNumber
;
566 InterruptObject
->FloatingSave
= FloatingSave
;
569 VOID
KePrintInterruptStatistic(VOID
)
573 for (j
= 0; j
< KeNumberProcessors
; j
++)
575 DPRINT1("CPU%d:\n", j
);
576 for (i
= 0; i
< NR_IRQS
; i
++)
578 if (IsrTable
[i
][j
].Count
)
580 DPRINT1(" Irq %x(%d): %d\n", i
, VECTOR2IRQ(i
+ IRQ_BASE
), IsrTable
[i
][j
].Count
);