3 * Copyright (C) 1998, 1999, 2000, 2001, 2002 ReactOS Team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* $Id: irq.c,v 1.38 2003/12/30 22:10:45 fireball Exp $
21 * PROJECT: ReactOS kernel
22 * FILE: ntoskrnl/ke/i386/irq.c
23 * PURPOSE: IRQ handling
24 * PROGRAMMER: David Welch (welch@mcmail.com)
30 * NOTE: In general the PIC interrupt priority facilities are used to
31 * preserve the NT IRQL semantics, global interrupt disables are only used
32 * to keep the PIC in a consistent state
36 /* INCLUDES ****************************************************************/
38 #define NTOS_MODE_KERNEL
41 #include <internal/ke.h>
42 #include <internal/ps.h>
43 #include <internal/i386/segment.h>
44 #include <internal/pool.h>
46 #include <../dbg/kdb.h>
50 #include <internal/hal/mps.h>
54 #include <internal/debug.h>
56 /* GLOBALS *****************************************************************/
61 * FIXME: This does not work if we have more than 24 IRQs (ie. more than one
64 #define VECTOR2IRQ(vector) (((vector) - FIRST_DEVICE_VECTOR) / 8)
65 #define IRQ2VECTOR(vector) ((vector * 8) + FIRST_DEVICE_VECTOR)
66 #define VECTOR2IRQL(vector) (DISPATCH_LEVEL /* 2 */ + 1 + VECTOR2IRQ(vector))
70 #define NR_IRQS 0x100 - IRQ_BASE
73 #define STR(x) __STR(x)
75 #define INT_NAME(intnum) _KiUnexpectedInterrupt##intnum
76 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
78 #define BUILD_COMMON_INTERRUPT_HANDLER() \
80 "_KiCommonInterrupt:\n\t" \
86 "movl $0xceafbeef,%eax\n\t" \
88 "movl $" STR(KERNEL_DS) ",%eax\n\t" \
91 "movl $" STR(PCR_SELECTOR) ",%eax\n\t" \
95 "call _KiInterruptDispatch\n\t" \
106 #define BUILD_INTERRUPT_HANDLER(intnum) \
107 VOID INT_NAME2(intnum)(VOID); \
109 STR(INT_NAME(intnum)) ":\n\t" \
111 "movl $0x" STR(intnum) ",%ebx\n\t" \
112 "jmp _KiCommonInterrupt");
115 /* Interrupt handlers and declarations */
118 BUILD_INTERRUPT_HANDLER(x##y)
121 B(x,0) B(x,1) B(x,2) B(x,3) \
122 B(x,4) B(x,5) B(x,6) B(x,7) \
123 B(x,8) B(x,9) B(x,A) B(x,B) \
124 B(x,C) B(x,D) B(x,E) B(x,F)
127 BUILD_COMMON_INTERRUPT_HANDLER()
128 B16(3) B16(4) B16(5) B16(6)
129 B16(7) B16(8) B16(9) B16(A
)
130 B16(B
) B16(C
) B16(D
) B16(E
)
137 /* Interrupt handler list */
140 (ULONG)& INT_NAME2(x##y)
143 L(x,0), L(x,1), L(x,2), L(x,3), \
144 L(x,4), L(x,5), L(x,6), L(x,7), \
145 L(x,8), L(x,9), L(x,A), L(x,B), \
146 L(x,C), L(x,D), L(x,E), L(x,F)
148 static ULONG irq_handler
[NR_IRQS
] = {
149 L16(3), L16(4), L16(5), L16(6),
150 L16(7), L16(8), L16(9), L16(A
),
151 L16(B
), L16(C
), L16(D
), L16(E
),
161 #define IRQ_BASE (0x40)
163 void irq_handler_0(void);
164 void irq_handler_1(void);
165 void irq_handler_2(void);
166 void irq_handler_3(void);
167 void irq_handler_4(void);
168 void irq_handler_5(void);
169 void irq_handler_6(void);
170 void irq_handler_7(void);
171 void irq_handler_8(void);
172 void irq_handler_9(void);
173 void irq_handler_10(void);
174 void irq_handler_11(void);
175 void irq_handler_12(void);
176 void irq_handler_13(void);
177 void irq_handler_14(void);
178 void irq_handler_15(void);
180 static unsigned int irq_handler
[NR_IRQS
]=
192 (int)&irq_handler_10
,
193 (int)&irq_handler_11
,
194 (int)&irq_handler_12
,
195 (int)&irq_handler_13
,
196 (int)&irq_handler_14
,
197 (int)&irq_handler_15
,
203 * PURPOSE: Object describing each isr
204 * NOTE: The data in this table is only modified at passsive level but can
205 * be accessed at any irq level.
208 static LIST_ENTRY isr_table
[NR_IRQS
]={{NULL
,NULL
},};
209 static PKSPIN_LOCK isr_lock
[NR_IRQS
] = {NULL
,};
210 static KSPIN_LOCK isr_table_lock
= {0,};
212 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
213 #define TAG_KINTERRUPT TAG('K', 'I', 'S', 'R')
215 /* FUNCTIONS ****************************************************************/
217 #define PRESENT (0x8000)
218 #define I486_INTERRUPT_GATE (0xe00)
221 KeInitInterrupts (VOID
)
228 * Setup the IDT entries to point to the interrupt handlers
230 for (i
=0;i
<NR_IRQS
;i
++)
232 KiIdt
[IRQ_BASE
+i
].a
=(irq_handler
[i
]&0xffff)+(KERNEL_CS
<<16);
233 KiIdt
[IRQ_BASE
+i
].b
=(irq_handler
[i
]&0xffff0000)+PRESENT
+
235 InitializeListHead(&isr_table
[i
]);
241 * Setup the IDT entries to point to the interrupt handlers
243 for (i
=0;i
<NR_IRQS
;i
++)
245 KiIdt
[IRQ_BASE
+i
].a
=(irq_handler
[i
]&0xffff)+(KERNEL_CS
<<16);
246 KiIdt
[IRQ_BASE
+i
].b
=(irq_handler
[i
]&0xffff0000)+PRESENT
+
248 InitializeListHead(&isr_table
[i
]);
255 typedef struct _KIRQ_TRAPFRAME
272 } KIRQ_TRAPFRAME
, *PKIRQ_TRAPFRAME
;
275 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame
,
276 PKTRAP_FRAME TrapFrame
)
278 TrapFrame
->Fs
= (USHORT
)IrqTrapFrame
->Fs
;
279 TrapFrame
->Es
= (USHORT
)IrqTrapFrame
->Es
;
280 TrapFrame
->Ds
= (USHORT
)IrqTrapFrame
->Ds
;
281 TrapFrame
->Eax
= IrqTrapFrame
->Eax
;
282 TrapFrame
->Ecx
= IrqTrapFrame
->Ecx
;
283 TrapFrame
->Edx
= IrqTrapFrame
->Edx
;
284 TrapFrame
->Ebx
= IrqTrapFrame
->Ebx
;
285 TrapFrame
->Esp
= IrqTrapFrame
->Esp
;
286 TrapFrame
->Ebp
= IrqTrapFrame
->Ebp
;
287 TrapFrame
->Esi
= IrqTrapFrame
->Esi
;
288 TrapFrame
->Edi
= IrqTrapFrame
->Edi
;
289 TrapFrame
->Eip
= IrqTrapFrame
->Eip
;
290 TrapFrame
->Cs
= IrqTrapFrame
->Cs
;
291 TrapFrame
->Eflags
= IrqTrapFrame
->Eflags
;
295 KeTrapFrameToIRQTrapFrame(PKTRAP_FRAME TrapFrame
,
296 PKIRQ_TRAPFRAME IrqTrapFrame
)
298 IrqTrapFrame
->Fs
= TrapFrame
->Fs
;
299 IrqTrapFrame
->Es
= TrapFrame
->Es
;
300 IrqTrapFrame
->Ds
= TrapFrame
->Ds
;
301 IrqTrapFrame
->Eax
= TrapFrame
->Eax
;
302 IrqTrapFrame
->Ecx
= TrapFrame
->Ecx
;
303 IrqTrapFrame
->Edx
= TrapFrame
->Edx
;
304 IrqTrapFrame
->Ebx
= TrapFrame
->Ebx
;
305 IrqTrapFrame
->Esp
= TrapFrame
->Esp
;
306 IrqTrapFrame
->Ebp
= TrapFrame
->Ebp
;
307 IrqTrapFrame
->Esi
= TrapFrame
->Esi
;
308 IrqTrapFrame
->Edi
= TrapFrame
->Edi
;
309 IrqTrapFrame
->Eip
= TrapFrame
->Eip
;
310 IrqTrapFrame
->Cs
= TrapFrame
->Cs
;
311 IrqTrapFrame
->Eflags
= TrapFrame
->Eflags
;
317 KiInterruptDispatch2 (ULONG vector
, KIRQL old_level
)
319 * FUNCTION: Calls all the interrupt handlers for a given irq.
321 * vector - The number of the vector to call handlers for.
322 * old_level - The irql of the processor when the irq took place.
323 * NOTES: Must be called at DIRQL.
329 DPRINT1("I(0x%.08x, 0x%.08x)\n", vector
, old_level
);
333 KiUpdateSystemTime(old_level
, 0);
338 * Iterate the list until one of the isr tells us its device interrupted
340 current
= isr_table
[vector
].Flink
;
341 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
343 while (current
!= &isr_table
[vector
] &&
344 !isr
->ServiceRoutine(isr
, isr
->ServiceContext
))
346 current
= current
->Flink
;
347 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
353 KiInterruptDispatch (ULONG Vector
, PKIRQ_TRAPFRAME Trapframe
)
355 * FUNCTION: Calls the irq specific handler for an irq
357 * Vector = Interrupt vector
358 * Trapframe = CPU context
359 * NOTES: Interrupts are disabled at this point.
366 KTRAP_FRAME KernelTrapFrame
;
368 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
369 KeGetCurrentThread()->TrapFrame
= &KernelTrapFrame
;
373 DbgPrint("V(0x%.02x)", Vector
);
376 * Notify the rest of the kernel of the raised irq level
378 if (!HalBeginSystemInterrupt (Vector
,
386 * Mask the related irq
388 //HalDisableSystemInterrupt (Vector, 0);
392 * NOTE: Only higher priority interrupts will get through
397 * Actually call the ISR.
399 KiInterruptDispatch2(Vector
, old_level
);
407 * Unmask the related irq
409 //HalEnableSystemInterrupt (Vector, 0, 0);
411 //DbgPrint("E(0x%.02x)\n", Vector);
414 * End the system interrupt.
416 HalEndSystemInterrupt (old_level
, 0);
422 KiInterruptDispatch2 (ULONG Irq
, KIRQL old_level
)
424 * FUNCTION: Calls all the interrupt handlers for a given irq.
426 * Irq - The number of the irq to call handlers for.
427 * old_level - The irql of the processor when the irq took place.
428 * NOTES: Must be called at DIRQL.
436 KiUpdateSystemTime(old_level
, 0);
441 * Iterate the list until one of the isr tells us its device interrupted
443 current
= isr_table
[Irq
].Flink
;
444 while (current
!= &isr_table
[Irq
])
446 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
448 if (isr
->ServiceRoutine(isr
, isr
->ServiceContext
))
453 isr
->ServiceRoutine(isr
, isr
->ServiceContext
);
455 current
= current
->Flink
;
461 KiInterruptDispatch (ULONG irq
, PKIRQ_TRAPFRAME Trapframe
)
463 * FUNCTION: Calls the irq specific handler for an irq
465 * irq = IRQ that has interrupted
469 KTRAP_FRAME KernelTrapFrame
;
470 PKTHREAD CurrentThread
;
471 PKTRAP_FRAME OldTrapFrame
=NULL
;
474 * At this point we have interrupts disabled, nothing has been done to
479 * Notify the rest of the kernel of the raised irq level. For the
480 * default HAL this will send an EOI to the PIC and alter the IRQL.
482 if (!HalBeginSystemInterrupt (irq
+ IRQ_BASE
,
483 (KIRQL
)(PROFILE_LEVEL
- irq
),
492 * NOTE: Only higher priority interrupts will get through
496 #elif defined(_MSC_VER)
499 #error Unknown compiler for inline assembler
503 * Actually call the ISR.
505 KiInterruptDispatch2(irq
, old_level
);
510 KdbProfileInterrupt(Trapframe
->Eip
);
515 * Maybe do a reschedule as well.
517 if (old_level
< DISPATCH_LEVEL
&& irq
== 0)
519 KeLowerIrql(APC_LEVEL
);
520 PsDispatchThread(THREAD_STATE_READY
);
524 * End the system interrupt.
528 #elif defined(_MSC_VER)
531 #error Unknown compiler for inline assembler
534 HalEndSystemInterrupt (old_level
, 0);
536 if (old_level
==PASSIVE_LEVEL
&& Trapframe
->Cs
!= KERNEL_CS
)
538 CurrentThread
= KeGetCurrentThread();
539 if (CurrentThread
!=NULL
&& CurrentThread
->Alerted
[1])
541 DPRINT("PID: %d, TID: %d CS %04x/%04x\n",
542 ((PETHREAD
)CurrentThread
)->ThreadsProcess
->UniqueProcessId
,
543 ((PETHREAD
)CurrentThread
)->Cid
.UniqueThread
,
545 CurrentThread
->TrapFrame
? CurrentThread
->TrapFrame
->Cs
: 0);
546 if (CurrentThread
->TrapFrame
== NULL
)
548 OldTrapFrame
= CurrentThread
->TrapFrame
;
549 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
550 CurrentThread
->TrapFrame
= &KernelTrapFrame
;
553 KiDeliverNormalApc();
555 assert(KeGetCurrentThread() == CurrentThread
);
556 if (CurrentThread
->TrapFrame
== &KernelTrapFrame
)
558 KeTrapFrameToIRQTrapFrame(&KernelTrapFrame
, Trapframe
);
559 CurrentThread
->TrapFrame
= OldTrapFrame
;
571 PLIST_ENTRY current_entry
;
574 for (i
=0;i
<NR_IRQS
;i
++)
576 DPRINT("For irq %x ",i
);
577 current_entry
= isr_table
[i
].Flink
;
578 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,Entry
);
579 while (current_entry
!=(&isr_table
[i
]))
581 DPRINT("Isr %x ",current
);
582 current_entry
= current_entry
->Flink
;
583 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,Entry
);
593 KeConnectInterrupt(PKINTERRUPT InterruptObject
)
597 PKINTERRUPT ListHead
;
600 DPRINT("KeConnectInterrupt()\n");
602 Vector
= InterruptObject
->Vector
;
605 * Acquire the table spinlock
607 KeAcquireSpinLock(&isr_table_lock
,&oldlvl
);
610 * Check if the vector is already in use that we can share it
612 ListHead
= CONTAINING_RECORD(isr_table
[Vector
].Flink
,KINTERRUPT
,Entry
);
613 if (!IsListEmpty(&isr_table
[Vector
]) &&
614 (InterruptObject
->Shareable
== FALSE
|| ListHead
->Shareable
==FALSE
))
616 KeReleaseSpinLock(&isr_table_lock
,oldlvl
);
617 return(STATUS_INVALID_PARAMETER
);
622 ExAllocatePoolWithTag(NonPagedPool
, sizeof(KSPIN_LOCK
),
624 KeInitializeSpinLock(isr_lock
[Vector
]);
627 InterruptObject
->IrqLock
= isr_lock
[Vector
];
629 KeRaiseIrql(InterruptObject
->SynchLevel
,&synch_oldlvl
);
630 KeAcquireSpinLockAtDpcLevel(InterruptObject
->IrqLock
);
631 DPRINT("%x %x\n",isr_table
[Vector
].Flink
,isr_table
[Vector
].Blink
);
632 if (IsListEmpty(&isr_table
[Vector
]))
635 HalEnableSystemInterrupt(Vector
, 0, 0);
637 HalEnableSystemInterrupt(Vector
+ IRQ_BASE
, 0, 0);
640 InsertTailList(&isr_table
[Vector
],&InterruptObject
->Entry
);
641 DPRINT("%x %x\n",InterruptObject
->Entry
.Flink
,
642 InterruptObject
->Entry
.Blink
);
643 KeReleaseSpinLockFromDpcLevel(InterruptObject
->IrqLock
);
644 KeLowerIrql(synch_oldlvl
);
647 * Release the table spinlock
649 KeReleaseSpinLock(&isr_table_lock
,oldlvl
);
653 return STATUS_SUCCESS
;
661 KeDisconnectInterrupt(PKINTERRUPT InterruptObject
)
663 * FUNCTION: Releases a drivers isr
665 * InterruptObject = isr to release
670 KeRaiseIrql(InterruptObject
->SynchLevel
,&oldlvl
);
671 KeAcquireSpinLockAtDpcLevel(InterruptObject
->IrqLock
);
672 RemoveEntryList(&InterruptObject
->Entry
);
673 if (IsListEmpty(&isr_table
[InterruptObject
->Vector
]))
676 HalDisableSystemInterrupt(InterruptObject
->Vector
, 0);
678 HalDisableSystemInterrupt(InterruptObject
->Vector
+ IRQ_BASE
, 0);
681 KeReleaseSpinLockFromDpcLevel(InterruptObject
->IrqLock
);
691 KeInitializeInterrupt(PKINTERRUPT InterruptObject
,
692 PKSERVICE_ROUTINE ServiceRoutine
,
693 PVOID ServiceContext
,
694 PKSPIN_LOCK SpinLock
,
697 KIRQL SynchronizeIrql
,
698 KINTERRUPT_MODE InterruptMode
,
700 KAFFINITY ProcessorEnableMask
,
701 BOOLEAN FloatingSave
)
703 InterruptObject
->ServiceContext
= ServiceContext
;
704 InterruptObject
->ServiceRoutine
= ServiceRoutine
;
705 InterruptObject
->Vector
= Vector
;
706 InterruptObject
->ProcessorEnableMask
= ProcessorEnableMask
;
707 InterruptObject
->SynchLevel
= SynchronizeIrql
;
708 InterruptObject
->Shareable
= ShareVector
;
709 InterruptObject
->FloatingSave
= FALSE
;
711 return STATUS_SUCCESS
;
719 IoConnectInterrupt(PKINTERRUPT
* InterruptObject
,
720 PKSERVICE_ROUTINE ServiceRoutine
,
721 PVOID ServiceContext
,
722 PKSPIN_LOCK SpinLock
,
725 KIRQL SynchronizeIrql
,
726 KINTERRUPT_MODE InterruptMode
,
728 KAFFINITY ProcessorEnableMask
,
729 BOOLEAN FloatingSave
)
731 * FUNCTION: Registers a driver's isr to be called when its device interrupts
733 * InterruptObject (OUT) = Points to the interrupt object created on
735 * ServiceRoutine = Routine to be called when the device interrupts
736 * ServiceContext = Parameter to be passed to ServiceRoutine
737 * SpinLock = Initalized spinlock that will be used to synchronize
738 * access between the isr and other driver routines. This is
739 * required if the isr handles more than one vector or the
740 * driver has more than one isr
741 * Vector = Interrupt vector to allocate
742 * (returned from HalGetInterruptVector)
743 * Irql = DIRQL returned from HalGetInterruptVector
744 * SynchronizeIrql = DIRQL at which the isr will execute. This must
745 * be the highest of all the DIRQLs returned from
746 * HalGetInterruptVector if the driver has multiple
748 * InterruptMode = Specifies if the interrupt is LevelSensitive or
750 * ShareVector = Specifies if the vector can be shared
751 * ProcessorEnableMask = Processors on the isr can run
752 * FloatingSave = TRUE if the floating point stack should be saved when
753 * the isr runs. Must be false for x86 drivers
755 * IRQL: PASSIVE_LEVEL
758 PKINTERRUPT Interrupt
;
759 NTSTATUS Status
= STATUS_SUCCESS
;
761 ASSERT_IRQL(PASSIVE_LEVEL
);
763 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector
);
766 * Check the parameters
768 if (Vector
>= NR_IRQS
)
770 return(STATUS_INVALID_PARAMETER
);
772 if (FloatingSave
== TRUE
)
774 return(STATUS_INVALID_PARAMETER
);
778 * Initialize interrupt object
780 Interrupt
=ExAllocatePoolWithTag(NonPagedPool
,sizeof(KINTERRUPT
),
784 return(STATUS_INSUFFICIENT_RESOURCES
);
787 Status
= KeInitializeInterrupt(Interrupt
,
798 if (!NT_SUCCESS(Status
))
800 ExFreePool(Interrupt
);
804 Status
= KeConnectInterrupt(Interrupt
);
805 if (!NT_SUCCESS(Status
))
807 ExFreePool(Interrupt
);
811 *InterruptObject
= Interrupt
;
813 return(STATUS_SUCCESS
);
821 IoDisconnectInterrupt(PKINTERRUPT InterruptObject
)
823 * FUNCTION: Releases a drivers isr
825 * InterruptObject = isr to release
828 KeDisconnectInterrupt(InterruptObject
);
829 ExFreePool(InterruptObject
);