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.
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>
45 #include <../dbg/kdb.h>
49 #include <internal/hal/mps.h>
53 #include <internal/debug.h>
55 /* GLOBALS *****************************************************************/
60 * FIXME: This does not work if we have more than 24 IRQs (ie. more than one
63 #define VECTOR2IRQ(vector) (((vector) - 0x31) / 8)
64 #define VECTOR2IRQL(vector) (4 + VECTOR2IRQ(vector))
66 #define IRQ_BASE FIRST_DEVICE_VECTOR
67 #define NR_IRQS 0x100 - 0x30
70 #define STR(x) __STR(x)
72 #define INT_NAME(intnum) _KiUnexpectedInterrupt##intnum
73 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
75 #define BUILD_COMMON_INTERRUPT_HANDLER() \
77 "_KiCommonInterrupt:\n\t" \
83 "movl $0xceafbeef,%eax\n\t" \
85 "movl $" STR(KERNEL_DS) ",%eax\n\t" \
88 "movl $" STR(PCR_SELECTOR) ",%eax\n\t" \
92 "call _KiInterruptDispatch\n\t" \
103 #define BUILD_INTERRUPT_HANDLER(intnum) \
104 VOID INT_NAME2(intnum)(VOID); \
106 STR(INT_NAME(intnum)) ":\n\t" \
108 "movl $0x" STR(intnum) ",%ebx\n\t" \
109 "jmp _KiCommonInterrupt");
112 /* Interrupt handlers and declarations */
115 BUILD_INTERRUPT_HANDLER(x##y)
118 B(x,0) B(x,1) B(x,2) B(x,3) \
119 B(x,4) B(x,5) B(x,6) B(x,7) \
120 B(x,8) B(x,9) B(x,A) B(x,B) \
121 B(x,C) B(x,D) B(x,E) B(x,F)
124 BUILD_COMMON_INTERRUPT_HANDLER()
125 B16(3) B16(4) B16(5) B16(6)
126 B16(7) B16(8) B16(9) B16(A)
127 B16(B) B16(C) B16(D) B16(E)
134 /* Interrupt handler list */
137 (ULONG)& INT_NAME2(x##y)
140 L(x,0), L(x,1), L(x,2), L(x,3), \
141 L(x,4), L(x,5), L(x,6), L(x,7), \
142 L(x,8), L(x,9), L(x,A), L(x,B), \
143 L(x,C), L(x,D), L(x,E), L(x,F)
145 static ULONG irq_handler[NR_IRQS] = {
146 L16(3), L16(4), L16(5), L16(6),
147 L16(7), L16(8), L16(9), L16(A),
148 L16(B), L16(C), L16(D), L16(E),
158 #define IRQ_BASE (0x40)
160 void irq_handler_0(void);
161 void irq_handler_1(void);
162 void irq_handler_2(void);
163 void irq_handler_3(void);
164 void irq_handler_4(void);
165 void irq_handler_5(void);
166 void irq_handler_6(void);
167 void irq_handler_7(void);
168 void irq_handler_8(void);
169 void irq_handler_9(void);
170 void irq_handler_10(void);
171 void irq_handler_11(void);
172 void irq_handler_12(void);
173 void irq_handler_13(void);
174 void irq_handler_14(void);
175 void irq_handler_15(void);
177 static unsigned int irq_handler[NR_IRQS]=
189 (int)&irq_handler_10,
190 (int)&irq_handler_11,
191 (int)&irq_handler_12,
192 (int)&irq_handler_13,
193 (int)&irq_handler_14,
194 (int)&irq_handler_15,
200 * PURPOSE: Object describing each isr
201 * NOTE: The data in this table is only modified at passsive level but can
202 * be accessed at any irq level.
205 static LIST_ENTRY isr_table[NR_IRQS]={{NULL,NULL},};
206 static PKSPIN_LOCK isr_lock[NR_IRQS] = {NULL,};
207 static KSPIN_LOCK isr_table_lock = {0,};
209 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
210 #define TAG_KINTERRUPT TAG('K', 'I', 'S', 'R')
212 /* FUNCTIONS ****************************************************************/
214 #define PRESENT (0x8000)
215 #define I486_INTERRUPT_GATE (0xe00)
217 VOID KeInitInterrupts (VOID)
224 * Setup the IDT entries to point to the interrupt handlers
226 for (i=0;i<NR_IRQS;i++)
228 KiIdt[0x30+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
229 KiIdt[0x30+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
231 InitializeListHead(&isr_table[i]);
237 * Setup the IDT entries to point to the interrupt handlers
239 for (i=0;i<NR_IRQS;i++)
241 KiIdt[IRQ_BASE+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
242 KiIdt[IRQ_BASE+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
244 InitializeListHead(&isr_table[i]);
251 typedef struct _KIRQ_TRAPFRAME
268 } KIRQ_TRAPFRAME, *PKIRQ_TRAPFRAME;
273 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame,
274 PKTRAP_FRAME TrapFrame)
276 TrapFrame->Fs = IrqTrapFrame->Fs;
277 TrapFrame->Fs = IrqTrapFrame->Es;
278 TrapFrame->Ds = IrqTrapFrame->Ds;
279 TrapFrame->Eax = IrqTrapFrame->Eax;
280 TrapFrame->Ecx = IrqTrapFrame->Ecx;
281 TrapFrame->Edx = IrqTrapFrame->Edx;
282 TrapFrame->Ebx = IrqTrapFrame->Ebx;
283 TrapFrame->Esp = IrqTrapFrame->Esp;
284 TrapFrame->Ebp = IrqTrapFrame->Ebp;
285 TrapFrame->Esi = IrqTrapFrame->Esi;
286 TrapFrame->Edi = IrqTrapFrame->Edi;
287 TrapFrame->Eip = IrqTrapFrame->Eip;
288 TrapFrame->Cs = IrqTrapFrame->Cs;
289 TrapFrame->Eflags = IrqTrapFrame->Eflags;
297 KiInterruptDispatch2 (ULONG Irq, KIRQL old_level)
299 * FUNCTION: Calls all the interrupt handlers for a given irq.
301 * Irq - The number of the irq to call handlers for.
302 * old_level - The irql of the processor when the irq took place.
303 * NOTES: Must be called at DIRQL.
309 DPRINT("\nWARNING - KiInterruptDispatch2 copied directly from UP version for build\npurposes only, please review\n\n");
313 KiUpdateSystemTime(old_level, 0);
318 * Iterate the list until one of the isr tells us its device interrupted
320 current = isr_table[Irq].Flink;
321 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
322 while (current != &isr_table[Irq] &&
323 !isr->ServiceRoutine(isr, isr->ServiceContext))
325 current = current->Flink;
326 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
332 KiInterruptDispatch (ULONG Vector, PKIRQ_TRAPFRAME Trapframe)
334 * FUNCTION: Calls the irq specific handler for an irq
336 * Vector = Interrupt vector
337 * Trapframe = CPU context
347 KTRAP_FRAME KernelTrapFrame;
349 KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
350 KeGetCurrentThread()->TrapFrame = &KernelTrapFrame;
354 DPRINT("I(%d) ", Vector);
357 * Notify the rest of the kernel of the raised irq level
359 HalBeginSystemInterrupt (Vector,
363 irq = VECTOR2IRQ(Vector);
367 * NOTE: Only higher priority interrupts will get through
373 if (KeGetCurrentProcessorNumber() == 0)
375 KiUpdateSystemTime(old_level, Trapframe->Eip);
377 KdbProfileInterrupt(Trapframe->Eip);
383 DPRINT("KiInterruptDispatch(Vector %d)\n", Vector);
385 * Iterate the list until one of the isr tells us its device interrupted
387 current = isr_table[irq].Flink;
388 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
389 //DPRINT("current %x isr %x\n",current,isr);
390 while (current!=(&isr_table[irq]) &&
391 !isr->ServiceRoutine(isr,isr->ServiceContext))
393 current = current->Flink;
394 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
395 //DPRINT("current %x isr %x\n",current,isr);
404 * Unmask the related irq
406 HalEnableSystemInterrupt (Vector, 0, 0);
409 * If the processor level will drop below dispatch level on return then
410 * issue a DPC queue drain interrupt
415 if (old_level < DISPATCH_LEVEL)
418 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
420 if (KeGetCurrentThread() != NULL)
422 // FIXME TODO - What happend to LastEip definition?
423 //KeGetCurrentThread()->LastEip = Trapframe->Eip;
425 KiDispatchInterrupt();
426 if (KeGetCurrentThread() != NULL &&
427 KeGetCurrentThread()->Alerted[1] != 0 &&
428 Trapframe->Cs != KERNEL_CS)
430 HalEndSystemInterrupt (APC_LEVEL, 0);
431 KiDeliverNormalApc();
435 HalEndSystemInterrupt (old_level, 0);
441 KiInterruptDispatch2 (ULONG Irq, KIRQL old_level)
443 * FUNCTION: Calls all the interrupt handlers for a given irq.
445 * Irq - The number of the irq to call handlers for.
446 * old_level - The irql of the processor when the irq took place.
447 * NOTES: Must be called at DIRQL.
455 KiUpdateSystemTime(old_level, 0);
460 * Iterate the list until one of the isr tells us its device interrupted
462 current = isr_table[Irq].Flink;
463 while (current != &isr_table[Irq])
465 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
467 if (isr->ServiceRoutine(isr, isr->ServiceContext))
472 isr->ServiceRoutine(isr, isr->ServiceContext);
474 current = current->Flink;
480 KiInterruptDispatch (ULONG irq, PKIRQ_TRAPFRAME Trapframe)
482 * FUNCTION: Calls the irq specific handler for an irq
484 * irq = IRQ that has interrupted
490 * At this point we have interrupts disabled, nothing has been done to
495 * Notify the rest of the kernel of the raised irq level. For the
496 * default HAL this will send an EOI to the PIC and alter the IRQL.
498 if (!HalBeginSystemInterrupt (irq + IRQ_BASE,
507 * NOTE: Only higher priority interrupts will get through
512 * Actually call the ISR.
514 KiInterruptDispatch2(irq, old_level);
519 KdbProfileInterrupt(Trapframe->Eip);
524 * Maybe do a reschedule as well.
526 if (old_level < DISPATCH_LEVEL && irq == 0)
528 KeLowerIrql(APC_LEVEL);
529 PsDispatchThread(THREAD_STATE_READY);
533 * End the system interrupt.
536 HalEndSystemInterrupt (old_level, 0);
545 PLIST_ENTRY current_entry;
548 for (i=0;i<NR_IRQS;i++)
550 DPRINT("For irq %x ",i);
551 current_entry = isr_table[i].Flink;
552 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
553 while (current_entry!=(&isr_table[i]))
555 DPRINT("Isr %x ",current);
556 current_entry = current_entry->Flink;
557 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
564 KeConnectInterrupt(PKINTERRUPT InterruptObject)
568 PKINTERRUPT ListHead;
571 DPRINT("KeConnectInterrupt()\n");
573 Vector = InterruptObject->Vector;
576 * Acquire the table spinlock
578 KeAcquireSpinLock(&isr_table_lock,&oldlvl);
581 * Check if the vector is already in use that we can share it
583 ListHead = CONTAINING_RECORD(isr_table[Vector].Flink,KINTERRUPT,Entry);
584 if (!IsListEmpty(&isr_table[Vector]) &&
585 (InterruptObject->Shareable == FALSE || ListHead->Shareable==FALSE))
587 KeReleaseSpinLock(&isr_table_lock,oldlvl);
588 return(STATUS_INVALID_PARAMETER);
593 ExAllocatePoolWithTag(NonPagedPool, sizeof(KSPIN_LOCK),
595 KeInitializeSpinLock(isr_lock[Vector]);
598 InterruptObject->IrqLock = isr_lock[Vector];
600 KeRaiseIrql(InterruptObject->SynchLevel,&synch_oldlvl);
601 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
602 DPRINT("%x %x\n",isr_table[Vector].Flink,isr_table[Vector].Blink);
603 if (IsListEmpty(&isr_table[Vector]))
605 HalEnableSystemInterrupt(Vector + IRQ_BASE, 0, 0);
607 InsertTailList(&isr_table[Vector],&InterruptObject->Entry);
608 DPRINT("%x %x\n",InterruptObject->Entry.Flink,
609 InterruptObject->Entry.Blink);
610 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
611 KeLowerIrql(synch_oldlvl);
614 * Release the table spinlock
616 KeReleaseSpinLock(&isr_table_lock,oldlvl);
620 return STATUS_SUCCESS;
625 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
627 * FUNCTION: Releases a drivers isr
629 * InterruptObject = isr to release
634 KeRaiseIrql(InterruptObject->SynchLevel,&oldlvl);
635 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
636 RemoveEntryList(&InterruptObject->Entry);
637 if (IsListEmpty(&isr_table[InterruptObject->Vector]))
639 HalDisableSystemInterrupt(InterruptObject->Vector + IRQ_BASE, 0);
641 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
648 KeInitializeInterrupt(PKINTERRUPT InterruptObject,
649 PKSERVICE_ROUTINE ServiceRoutine,
650 PVOID ServiceContext,
651 PKSPIN_LOCK SpinLock,
654 KIRQL SynchronizeIrql,
655 KINTERRUPT_MODE InterruptMode,
657 KAFFINITY ProcessorEnableMask,
658 BOOLEAN FloatingSave)
660 InterruptObject->ServiceContext = ServiceContext;
661 InterruptObject->ServiceRoutine = ServiceRoutine;
662 InterruptObject->Vector = Vector;
663 InterruptObject->ProcessorEnableMask = ProcessorEnableMask;
664 InterruptObject->SynchLevel = SynchronizeIrql;
665 InterruptObject->Shareable = ShareVector;
666 InterruptObject->FloatingSave = FALSE;
668 return STATUS_SUCCESS;
673 IoConnectInterrupt(PKINTERRUPT* InterruptObject,
674 PKSERVICE_ROUTINE ServiceRoutine,
675 PVOID ServiceContext,
676 PKSPIN_LOCK SpinLock,
679 KIRQL SynchronizeIrql,
680 KINTERRUPT_MODE InterruptMode,
682 KAFFINITY ProcessorEnableMask,
683 BOOLEAN FloatingSave)
685 * FUNCTION: Registers a driver's isr to be called when its device interrupts
687 * InterruptObject (OUT) = Points to the interrupt object created on
689 * ServiceRoutine = Routine to be called when the device interrupts
690 * ServiceContext = Parameter to be passed to ServiceRoutine
691 * SpinLock = Initalized spinlock that will be used to synchronize
692 * access between the isr and other driver routines. This is
693 * required if the isr handles more than one vector or the
694 * driver has more than one isr
695 * Vector = Interrupt vector to allocate
696 * (returned from HalGetInterruptVector)
697 * Irql = DIRQL returned from HalGetInterruptVector
698 * SynchronizeIrql = DIRQL at which the isr will execute. This must
699 * be the highest of all the DIRQLs returned from
700 * HalGetInterruptVector if the driver has multiple
702 * InterruptMode = Specifies if the interrupt is LevelSensitive or
704 * ShareVector = Specifies if the vector can be shared
705 * ProcessorEnableMask = Processors on the isr can run
706 * FloatingSave = TRUE if the floating point stack should be saved when
707 * the isr runs. Must be false for x86 drivers
709 * IRQL: PASSIVE_LEVEL
712 PKINTERRUPT Interrupt;
713 NTSTATUS Status = STATUS_SUCCESS;
715 ASSERT_IRQL(PASSIVE_LEVEL);
717 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector);
720 * Check the parameters
722 if (Vector >= NR_IRQS)
724 return(STATUS_INVALID_PARAMETER);
726 if (FloatingSave == TRUE)
728 return(STATUS_INVALID_PARAMETER);
732 * Initialize interrupt object
734 Interrupt=ExAllocatePoolWithTag(NonPagedPool,sizeof(KINTERRUPT),
738 return(STATUS_INSUFFICIENT_RESOURCES);
741 Status = KeInitializeInterrupt(Interrupt,
752 if (!NT_SUCCESS(Status))
754 ExFreePool(Interrupt);
758 Status = KeConnectInterrupt(Interrupt);
759 if (!NT_SUCCESS(Status))
761 ExFreePool(Interrupt);
765 *InterruptObject = Interrupt;
767 return(STATUS_SUCCESS);
772 IoDisconnectInterrupt(PKINTERRUPT InterruptObject)
774 * FUNCTION: Releases a drivers isr
776 * InterruptObject = isr to release
779 KeDisconnectInterrupt(InterruptObject);
780 ExFreePool(InterruptObject);