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.26 2002/12/09 19:53:44 hbirr 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 #include <ddk/ntddk.h>
40 #include <internal/ke.h>
41 #include <internal/ps.h>
42 #include <internal/i386/segment.h>
43 #include <internal/pool.h>
46 #include <internal/hal/mps.h>
50 #include <internal/debug.h>
52 /* GLOBALS *****************************************************************/
57 * FIXME: This does not work if we have more than 24 IRQs (ie. more than one
60 #define VECTOR2IRQ(vector) (((vector) - 0x31) / 8)
61 #define VECTOR2IRQL(vector) (4 + VECTOR2IRQ(vector))
63 #define IRQ_BASE FIRST_DEVICE_VECTOR
64 #define NR_IRQS 0x100 - 0x30
67 #define STR(x) __STR(x)
69 #define INT_NAME(intnum) _KiUnexpectedInterrupt##intnum
70 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
72 #define BUILD_COMMON_INTERRUPT_HANDLER() \
74 "_KiCommonInterrupt:\n\t" \
80 "movl $0xceafbeef,%eax\n\t" \
82 "movl $" STR(KERNEL_DS) ",%eax\n\t" \
85 "movl $" STR(PCR_SELECTOR) ",%eax\n\t" \
89 "call _KiInterruptDispatch\n\t" \
100 #define BUILD_INTERRUPT_HANDLER(intnum) \
101 VOID INT_NAME2(intnum)(VOID); \
103 STR(INT_NAME(intnum)) ":\n\t" \
105 "movl $0x" STR(intnum) ",%ebx\n\t" \
106 "jmp _KiCommonInterrupt");
109 /* Interrupt handlers and declarations */
112 BUILD_INTERRUPT_HANDLER(x##y)
115 B(x,0) B(x,1) B(x,2) B(x,3) \
116 B(x,4) B(x,5) B(x,6) B(x,7) \
117 B(x,8) B(x,9) B(x,A) B(x,B) \
118 B(x,C) B(x,D) B(x,E) B(x,F)
121 BUILD_COMMON_INTERRUPT_HANDLER()
122 B16(3) B16(4) B16(5) B16(6)
123 B16(7) B16(8) B16(9) B16(A
)
124 B16(B
) B16(C
) B16(D
) B16(E
)
131 /* Interrupt handler list */
134 (ULONG)& INT_NAME2(x##y)
137 L(x,0), L(x,1), L(x,2), L(x,3), \
138 L(x,4), L(x,5), L(x,6), L(x,7), \
139 L(x,8), L(x,9), L(x,A), L(x,B), \
140 L(x,C), L(x,D), L(x,E), L(x,F)
142 static ULONG irq_handler
[NR_IRQS
] = {
143 L16(3), L16(4), L16(5), L16(6),
144 L16(7), L16(8), L16(9), L16(A
),
145 L16(B
), L16(C
), L16(D
), L16(E
),
155 #define IRQ_BASE (0x40)
157 void irq_handler_0(void);
158 void irq_handler_1(void);
159 void irq_handler_2(void);
160 void irq_handler_3(void);
161 void irq_handler_4(void);
162 void irq_handler_5(void);
163 void irq_handler_6(void);
164 void irq_handler_7(void);
165 void irq_handler_8(void);
166 void irq_handler_9(void);
167 void irq_handler_10(void);
168 void irq_handler_11(void);
169 void irq_handler_12(void);
170 void irq_handler_13(void);
171 void irq_handler_14(void);
172 void irq_handler_15(void);
174 static unsigned int irq_handler
[NR_IRQS
]=
186 (int)&irq_handler_10
,
187 (int)&irq_handler_11
,
188 (int)&irq_handler_12
,
189 (int)&irq_handler_13
,
190 (int)&irq_handler_14
,
191 (int)&irq_handler_15
,
197 * PURPOSE: Object describing each isr
198 * NOTE: The data in this table is only modified at passsive level but can
199 * be accessed at any irq level.
202 static LIST_ENTRY isr_table
[NR_IRQS
]={{NULL
,NULL
},};
203 static PKSPIN_LOCK isr_lock
[NR_IRQS
] = {NULL
,};
204 static KSPIN_LOCK isr_table_lock
= {0,};
206 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
207 #define TAG_KINTERRUPT TAG('K', 'I', 'S', 'R')
209 /* FUNCTIONS ****************************************************************/
211 #define PRESENT (0x8000)
212 #define I486_INTERRUPT_GATE (0xe00)
214 VOID
KeInitInterrupts (VOID
)
221 * Setup the IDT entries to point to the interrupt handlers
223 for (i
=0;i
<NR_IRQS
;i
++)
225 KiIdt
[0x30+i
].a
=(irq_handler
[i
]&0xffff)+(KERNEL_CS
<<16);
226 KiIdt
[0x30+i
].b
=(irq_handler
[i
]&0xffff0000)+PRESENT
+
228 InitializeListHead(&isr_table
[i
]);
234 * Setup the IDT entries to point to the interrupt handlers
236 for (i
=0;i
<NR_IRQS
;i
++)
238 KiIdt
[IRQ_BASE
+i
].a
=(irq_handler
[i
]&0xffff)+(KERNEL_CS
<<16);
239 KiIdt
[IRQ_BASE
+i
].b
=(irq_handler
[i
]&0xffff0000)+PRESENT
+
241 InitializeListHead(&isr_table
[i
]);
248 typedef struct _KIRQ_TRAPFRAME
265 } KIRQ_TRAPFRAME
, *PKIRQ_TRAPFRAME
;
270 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame
,
271 PKTRAP_FRAME TrapFrame
)
273 TrapFrame
->Fs
= IrqTrapFrame
->Fs
;
274 TrapFrame
->Fs
= IrqTrapFrame
->Es
;
275 TrapFrame
->Ds
= IrqTrapFrame
->Ds
;
276 TrapFrame
->Eax
= IrqTrapFrame
->Eax
;
277 TrapFrame
->Ecx
= IrqTrapFrame
->Ecx
;
278 TrapFrame
->Edx
= IrqTrapFrame
->Edx
;
279 TrapFrame
->Ebx
= IrqTrapFrame
->Ebx
;
280 TrapFrame
->Esp
= IrqTrapFrame
->Esp
;
281 TrapFrame
->Ebp
= IrqTrapFrame
->Ebp
;
282 TrapFrame
->Esi
= IrqTrapFrame
->Esi
;
283 TrapFrame
->Edi
= IrqTrapFrame
->Edi
;
284 TrapFrame
->Eip
= IrqTrapFrame
->Eip
;
285 TrapFrame
->Cs
= IrqTrapFrame
->Cs
;
286 TrapFrame
->Eflags
= IrqTrapFrame
->Eflags
;
294 KiInterruptDispatch2 (ULONG Irq
, KIRQL old_level
)
296 * FUNCTION: Calls all the interrupt handlers for a given irq.
298 * Irq - The number of the irq to call handlers for.
299 * old_level - The irql of the processor when the irq took place.
300 * NOTES: Must be called at DIRQL.
306 DPRINT("\nWARNING - KiInterruptDispatch2 copied directly from UP version for build\npurposes only, please review\n\n");
310 KiUpdateSystemTime(old_level
, 0);
315 * Iterate the list until one of the isr tells us its device interrupted
317 current
= isr_table
[Irq
].Flink
;
318 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
319 while (current
!= &isr_table
[Irq
] &&
320 !isr
->ServiceRoutine(isr
, isr
->ServiceContext
))
322 current
= current
->Flink
;
323 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
329 KiInterruptDispatch (ULONG Vector
, PKIRQ_TRAPFRAME Trapframe
)
331 * FUNCTION: Calls the irq specific handler for an irq
333 * Vector = Interrupt vector
334 * Trapframe = CPU context
344 KTRAP_FRAME KernelTrapFrame
;
346 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
347 KeGetCurrentThread()->TrapFrame
= &KernelTrapFrame
;
351 DPRINT("I(%d) ", Vector
);
354 * Notify the rest of the kernel of the raised irq level
356 HalBeginSystemInterrupt (Vector
,
360 irq
= VECTOR2IRQ(Vector
);
364 * NOTE: Only higher priority interrupts will get through
370 if (KeGetCurrentProcessorNumber() == 0)
372 KiUpdateSystemTime(old_level
, Trapframe
->Eip
);
377 DPRINT("KiInterruptDispatch(Vector %d)\n", Vector
);
379 * Iterate the list until one of the isr tells us its device interrupted
381 current
= isr_table
[irq
].Flink
;
382 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
383 //DPRINT("current %x isr %x\n",current,isr);
384 while (current
!=(&isr_table
[irq
]) &&
385 !isr
->ServiceRoutine(isr
,isr
->ServiceContext
))
387 current
= current
->Flink
;
388 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
389 //DPRINT("current %x isr %x\n",current,isr);
398 * Unmask the related irq
400 HalEnableSystemInterrupt (Vector
, 0, 0);
403 * If the processor level will drop below dispatch level on return then
404 * issue a DPC queue drain interrupt
409 if (old_level
< DISPATCH_LEVEL
)
412 HalEndSystemInterrupt (DISPATCH_LEVEL
, 0);
414 if (KeGetCurrentThread() != NULL
)
416 // FIXME TODO - What happend to LastEip definition?
417 //KeGetCurrentThread()->LastEip = Trapframe->Eip;
419 KiDispatchInterrupt();
420 if (KeGetCurrentThread() != NULL
&&
421 KeGetCurrentThread()->Alerted
[1] != 0 &&
422 Trapframe
->Cs
!= KERNEL_CS
)
424 HalEndSystemInterrupt (APC_LEVEL
, 0);
425 KiDeliverNormalApc();
429 HalEndSystemInterrupt (old_level
, 0);
435 KiInterruptDispatch2 (ULONG Irq
, KIRQL old_level
)
437 * FUNCTION: Calls all the interrupt handlers for a given irq.
439 * Irq - The number of the irq to call handlers for.
440 * old_level - The irql of the processor when the irq took place.
441 * NOTES: Must be called at DIRQL.
449 KiUpdateSystemTime(old_level
, 0);
454 * Iterate the list until one of the isr tells us its device interrupted
456 current
= isr_table
[Irq
].Flink
;
457 while (current
!= &isr_table
[Irq
])
459 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
461 if (isr
->ServiceRoutine(isr
, isr
->ServiceContext
))
466 isr
->ServiceRoutine(isr
, isr
->ServiceContext
);
468 current
= current
->Flink
;
474 KiInterruptDispatch (ULONG irq
, PKIRQ_TRAPFRAME Trapframe
)
476 * FUNCTION: Calls the irq specific handler for an irq
478 * irq = IRQ that has interrupted
484 * At this point we have interrupts disabled, nothing has been done to
489 * Notify the rest of the kernel of the raised irq level. For the
490 * default HAL this will send an EOI to the PIC and alter the IRQL.
492 if (!HalBeginSystemInterrupt (irq
+ IRQ_BASE
,
501 * NOTE: Only higher priority interrupts will get through
506 * Actually call the ISR.
508 KiInterruptDispatch2(irq
, old_level
);
511 * End the system interrupt.
513 HalEndSystemInterrupt (old_level
, 0);
516 * Maybe do a reschedule as well.
518 if (old_level
< DISPATCH_LEVEL
&& irq
== 0)
520 PsDispatchThread(THREAD_STATE_READY
);
530 PLIST_ENTRY current_entry
;
533 for (i
=0;i
<NR_IRQS
;i
++)
535 DPRINT("For irq %x ",i
);
536 current_entry
= isr_table
[i
].Flink
;
537 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,Entry
);
538 while (current_entry
!=(&isr_table
[i
]))
540 DPRINT("Isr %x ",current
);
541 current_entry
= current_entry
->Flink
;
542 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,Entry
);
549 KeConnectInterrupt(PKINTERRUPT InterruptObject
)
553 PKINTERRUPT ListHead
;
556 DPRINT("KeConnectInterrupt()\n");
558 Vector
= InterruptObject
->Vector
;
561 * Acquire the table spinlock
563 KeAcquireSpinLock(&isr_table_lock
,&oldlvl
);
566 * Check if the vector is already in use that we can share it
568 ListHead
= CONTAINING_RECORD(isr_table
[Vector
].Flink
,KINTERRUPT
,Entry
);
569 if (!IsListEmpty(&isr_table
[Vector
]) &&
570 (InterruptObject
->Shareable
== FALSE
|| ListHead
->Shareable
==FALSE
))
572 KeReleaseSpinLock(&isr_table_lock
,oldlvl
);
573 return(STATUS_INVALID_PARAMETER
);
578 ExAllocatePoolWithTag(NonPagedPool
, sizeof(KSPIN_LOCK
),
580 KeInitializeSpinLock(isr_lock
[Vector
]);
583 InterruptObject
->IrqLock
= isr_lock
[Vector
];
585 KeRaiseIrql(InterruptObject
->SynchLevel
,&synch_oldlvl
);
586 KeAcquireSpinLockAtDpcLevel(InterruptObject
->IrqLock
);
587 DPRINT("%x %x\n",isr_table
[Vector
].Flink
,isr_table
[Vector
].Blink
);
588 if (IsListEmpty(&isr_table
[Vector
]))
590 HalEnableSystemInterrupt(Vector
+ IRQ_BASE
, 0, 0);
592 InsertTailList(&isr_table
[Vector
],&InterruptObject
->Entry
);
593 DPRINT("%x %x\n",InterruptObject
->Entry
.Flink
,
594 InterruptObject
->Entry
.Blink
);
595 KeReleaseSpinLockFromDpcLevel(InterruptObject
->IrqLock
);
596 KeLowerIrql(synch_oldlvl
);
599 * Release the table spinlock
601 KeReleaseSpinLock(&isr_table_lock
,oldlvl
);
605 return STATUS_SUCCESS
;
610 KeDisconnectInterrupt(PKINTERRUPT InterruptObject
)
612 * FUNCTION: Releases a drivers isr
614 * InterruptObject = isr to release
619 KeRaiseIrql(InterruptObject
->SynchLevel
,&oldlvl
);
620 KeAcquireSpinLockAtDpcLevel(InterruptObject
->IrqLock
);
621 RemoveEntryList(&InterruptObject
->Entry
);
622 if (IsListEmpty(&isr_table
[InterruptObject
->Vector
]))
624 HalDisableSystemInterrupt(InterruptObject
->Vector
+ IRQ_BASE
, 0);
626 KeReleaseSpinLockFromDpcLevel(InterruptObject
->IrqLock
);
633 KeInitializeInterrupt(PKINTERRUPT InterruptObject
,
634 PKSERVICE_ROUTINE ServiceRoutine
,
635 PVOID ServiceContext
,
636 PKSPIN_LOCK SpinLock
,
639 KIRQL SynchronizeIrql
,
640 KINTERRUPT_MODE InterruptMode
,
642 KAFFINITY ProcessorEnableMask
,
643 BOOLEAN FloatingSave
)
645 InterruptObject
->ServiceContext
= ServiceContext
;
646 InterruptObject
->ServiceRoutine
= ServiceRoutine
;
647 InterruptObject
->Vector
= Vector
;
648 InterruptObject
->ProcessorEnableMask
= ProcessorEnableMask
;
649 InterruptObject
->SynchLevel
= SynchronizeIrql
;
650 InterruptObject
->Shareable
= ShareVector
;
651 InterruptObject
->FloatingSave
= FALSE
;
653 return STATUS_SUCCESS
;
658 IoConnectInterrupt(PKINTERRUPT
* InterruptObject
,
659 PKSERVICE_ROUTINE ServiceRoutine
,
660 PVOID ServiceContext
,
661 PKSPIN_LOCK SpinLock
,
664 KIRQL SynchronizeIrql
,
665 KINTERRUPT_MODE InterruptMode
,
667 KAFFINITY ProcessorEnableMask
,
668 BOOLEAN FloatingSave
)
670 * FUNCTION: Registers a driver's isr to be called when its device interrupts
672 * InterruptObject (OUT) = Points to the interrupt object created on
674 * ServiceRoutine = Routine to be called when the device interrupts
675 * ServiceContext = Parameter to be passed to ServiceRoutine
676 * SpinLock = Initalized spinlock that will be used to synchronize
677 * access between the isr and other driver routines. This is
678 * required if the isr handles more than one vector or the
679 * driver has more than one isr
680 * Vector = Interrupt vector to allocate
681 * (returned from HalGetInterruptVector)
682 * Irql = DIRQL returned from HalGetInterruptVector
683 * SynchronizeIrql = DIRQL at which the isr will execute. This must
684 * be the highest of all the DIRQLs returned from
685 * HalGetInterruptVector if the driver has multiple
687 * InterruptMode = Specifies if the interrupt is LevelSensitive or
689 * ShareVector = Specifies if the vector can be shared
690 * ProcessorEnableMask = Processors on the isr can run
691 * FloatingSave = TRUE if the floating point stack should be saved when
692 * the isr runs. Must be false for x86 drivers
694 * IRQL: PASSIVE_LEVEL
697 PKINTERRUPT Interrupt
;
698 NTSTATUS Status
= STATUS_SUCCESS
;
700 ASSERT_IRQL(PASSIVE_LEVEL
);
702 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector
);
705 * Check the parameters
707 if (Vector
>= NR_IRQS
)
709 return(STATUS_INVALID_PARAMETER
);
711 if (FloatingSave
== TRUE
)
713 return(STATUS_INVALID_PARAMETER
);
717 * Initialize interrupt object
719 Interrupt
=ExAllocatePoolWithTag(NonPagedPool
,sizeof(KINTERRUPT
),
723 return(STATUS_INSUFFICIENT_RESOURCES
);
726 Status
= KeInitializeInterrupt(Interrupt
,
737 if (!NT_SUCCESS(Status
))
739 ExFreePool(Interrupt
);
743 Status
= KeConnectInterrupt(Interrupt
);
744 if (!NT_SUCCESS(Status
))
746 ExFreePool(Interrupt
);
750 *InterruptObject
= Interrupt
;
752 return(STATUS_SUCCESS
);
757 IoDisconnectInterrupt(PKINTERRUPT InterruptObject
)
759 * FUNCTION: Releases a drivers isr
761 * InterruptObject = isr to release
764 KeDisconnectInterrupt(InterruptObject
);
765 ExFreePool(InterruptObject
);