1 /* $Id: irq.c,v 1.16 2002/01/23 23:39:25 chorns Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ke/i386/irq.c
6 * PURPOSE: IRQ handling
7 * PROGRAMMER: 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 ****************************************************************/
21 #include <ddk/ntddk.h>
23 #include <internal/ke.h>
24 #include <internal/ps.h>
25 #include <internal/i386/segment.h>
26 #include <internal/pool.h>
29 #include <internal/hal/mps.h>
33 #include <internal/debug.h>
35 /* GLOBALS *****************************************************************/
39 #define IRQ_BASE FIRST_DEVICE_VECTOR
40 #define NR_IRQS 0x100 - 0x30
43 #define STR(x) __STR(x)
45 #define INT_NAME(intnum) _KiUnexpectedInterrupt##intnum
46 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
48 #define BUILD_COMMON_INTERRUPT_HANDLER() \
50 "_KiCommonInterrupt:\n\t" \
56 "movl $0xceafbeef,%eax\n\t" \
58 "movl $" STR(KERNEL_DS) ",%eax\n\t" \
61 "movl $" STR(PCR_SELECTOR) ",%eax\n\t" \
65 "call _KiInterruptDispatch\n\t" \
76 #define BUILD_INTERRUPT_HANDLER(intnum) \
77 VOID INT_NAME2(intnum)(VOID); \
79 STR(INT_NAME(intnum)) ":\n\t" \
81 "movl $0x" STR(intnum) ",%ebx\n\t" \
82 "jmp _KiCommonInterrupt");
85 /* Interrupt handlers and declarations */
88 BUILD_INTERRUPT_HANDLER(x##y)
91 B(x,0) B(x,1) B(x,2) B(x,3) \
92 B(x,4) B(x,5) B(x,6) B(x,7) \
93 B(x,8) B(x,9) B(x,A) B(x,B) \
94 B(x,C) B(x,D) B(x,E) B(x,F)
97 BUILD_COMMON_INTERRUPT_HANDLER()
98 B16(3) B16(4) B16(5) B16(6)
99 B16(7) B16(8) B16(9) B16(A
)
100 B16(B
) B16(C
) B16(D
) B16(E
)
107 /* Interrupt handler list */
110 (ULONG)& INT_NAME2(x##y)
113 L(x,0), L(x,1), L(x,2), L(x,3), \
114 L(x,4), L(x,5), L(x,6), L(x,7), \
115 L(x,8), L(x,9), L(x,A), L(x,B), \
116 L(x,C), L(x,D), L(x,E), L(x,F)
118 static ULONG irq_handler
[NR_IRQS
] = {
119 L16(3), L16(4), L16(5), L16(6),
120 L16(7), L16(8), L16(9), L16(A
),
121 L16(B
), L16(C
), L16(D
), L16(E
),
131 #define IRQ_BASE (0x40)
133 void irq_handler_0(void);
134 void irq_handler_1(void);
135 void irq_handler_2(void);
136 void irq_handler_3(void);
137 void irq_handler_4(void);
138 void irq_handler_5(void);
139 void irq_handler_6(void);
140 void irq_handler_7(void);
141 void irq_handler_8(void);
142 void irq_handler_9(void);
143 void irq_handler_10(void);
144 void irq_handler_11(void);
145 void irq_handler_12(void);
146 void irq_handler_13(void);
147 void irq_handler_14(void);
148 void irq_handler_15(void);
150 static unsigned int irq_handler
[NR_IRQS
]=
162 (int)&irq_handler_10
,
163 (int)&irq_handler_11
,
164 (int)&irq_handler_12
,
165 (int)&irq_handler_13
,
166 (int)&irq_handler_14
,
167 (int)&irq_handler_15
,
173 * PURPOSE: Object describing each isr
174 * NOTE: The data in this table is only modified at passsive level but can
175 * be accessed at any irq level.
178 static LIST_ENTRY isr_table
[NR_IRQS
]={{NULL
,NULL
},};
179 static PKSPIN_LOCK isr_lock
[NR_IRQS
] = {NULL
,};
180 static KSPIN_LOCK isr_table_lock
= {0,};
182 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
183 #define TAG_KINTERRUPT TAG('K', 'I', 'S', 'R')
185 /* FUNCTIONS ****************************************************************/
187 #define PRESENT (0x8000)
188 #define I486_INTERRUPT_GATE (0xe00)
190 VOID
KeInitInterrupts (VOID
)
197 * Setup the IDT entries to point to the interrupt handlers
199 for (i
=0;i
<NR_IRQS
;i
++)
201 KiIdt
[0x30+i
].a
=(irq_handler
[i
]&0xffff)+(KERNEL_CS
<<16);
202 KiIdt
[0x30+i
].b
=(irq_handler
[i
]&0xffff0000)+PRESENT
+
204 InitializeListHead(&isr_table
[i
]);
210 * Setup the IDT entries to point to the interrupt handlers
212 for (i
=0;i
<NR_IRQS
;i
++)
214 KiIdt
[IRQ_BASE
+i
].a
=(irq_handler
[i
]&0xffff)+(KERNEL_CS
<<16);
215 KiIdt
[IRQ_BASE
+i
].b
=(irq_handler
[i
]&0xffff0000)+PRESENT
+
217 InitializeListHead(&isr_table
[i
]);
224 typedef struct _KIRQ_TRAPFRAME
241 } KIRQ_TRAPFRAME
, *PKIRQ_TRAPFRAME
;
246 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame
,
247 PKTRAP_FRAME TrapFrame
)
249 TrapFrame
->Fs
= IrqTrapFrame
->Fs
;
250 TrapFrame
->Fs
= IrqTrapFrame
->Es
;
251 TrapFrame
->Ds
= IrqTrapFrame
->Ds
;
252 TrapFrame
->Eax
= IrqTrapFrame
->Eax
;
253 TrapFrame
->Ecx
= IrqTrapFrame
->Ecx
;
254 TrapFrame
->Edx
= IrqTrapFrame
->Edx
;
255 TrapFrame
->Ebx
= IrqTrapFrame
->Ebx
;
256 TrapFrame
->Esp
= IrqTrapFrame
->Esp
;
257 TrapFrame
->Ebp
= IrqTrapFrame
->Ebp
;
258 TrapFrame
->Esi
= IrqTrapFrame
->Esi
;
259 TrapFrame
->Edi
= IrqTrapFrame
->Edi
;
260 TrapFrame
->Eip
= IrqTrapFrame
->Eip
;
261 TrapFrame
->Cs
= IrqTrapFrame
->Cs
;
262 TrapFrame
->Eflags
= IrqTrapFrame
->Eflags
;
270 KiInterruptDispatch (ULONG Vector
, PKIRQ_TRAPFRAME Trapframe
)
272 * FUNCTION: Calls the irq specific handler for an irq
274 * Vector = Interrupt vector
275 * Trapframe = CPU context
285 KTRAP_FRAME KernelTrapFrame
;
287 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
288 KeGetCurrentThread()->TrapFrame
= &KernelTrapFrame
;
292 DPRINT("I(%d) ", Vector
);
295 * Notify the rest of the kernel of the raised irq level
297 HalBeginSystemInterrupt (Vector
,
301 irq
= VECTOR2IRQ(Vector
);
305 * NOTE: Only higher priority interrupts will get through
311 if (KeGetCurrentProcessorNumber() == 0)
313 KiUpdateSystemTime(old_level
, Trapframe
->Eip
);
318 DPRINT("KiInterruptDispatch(Vector %d)\n", Vector
);
320 * Iterate the list until one of the isr tells us its device interrupted
322 current
= isr_table
[irq
].Flink
;
323 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
324 //DPRINT("current %x isr %x\n",current,isr);
325 while (current
!=(&isr_table
[irq
]) &&
326 !isr
->ServiceRoutine(isr
,isr
->ServiceContext
))
328 current
= current
->Flink
;
329 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
330 //DPRINT("current %x isr %x\n",current,isr);
339 * Unmask the related irq
341 HalEnableSystemInterrupt (Vector
, 0, 0);
344 * If the processor level will drop below dispatch level on return then
345 * issue a DPC queue drain interrupt
347 if (old_level
< DISPATCH_LEVEL
)
350 HalEndSystemInterrupt (DISPATCH_LEVEL
, 0);
353 if (KeGetCurrentThread() != NULL
)
355 KeGetCurrentThread()->LastEip
= Trapframe
->Eip
;
357 KiDispatchInterrupt();
358 if (KeGetCurrentThread() != NULL
&&
359 KeGetCurrentThread()->Alerted
[1] != 0 &&
360 Trapframe
->Cs
!= KERNEL_CS
)
362 HalEndSystemInterrupt (APC_LEVEL
, 0);
363 KiDeliverNormalApc();
367 HalEndSystemInterrupt (old_level
, 0);
373 KiInterruptDispatch (ULONG irq
, PKIRQ_TRAPFRAME Trapframe
)
375 * FUNCTION: Calls the irq specific handler for an irq
377 * irq = IRQ that has interrupted
386 KTRAP_FRAME KernelTrapFrame
;
388 KeIRQTrapFrameToTrapFrame(Trapframe
, &KernelTrapFrame
);
389 KeGetCurrentThread()->TrapFrame
= &KernelTrapFrame
;
394 * Notify the rest of the kernel of the raised irq level
396 HalBeginSystemInterrupt (irq
+ IRQ_BASE
,
402 * NOTE: Only higher priority interrupts will get through
408 KiUpdateSystemTime(old_level
, Trapframe
->Eip
);
412 DPRINT("KiInterruptDispatch(irq %d)\n",irq
);
414 * Iterate the list until one of the isr tells us its device interrupted
416 current
= isr_table
[irq
].Flink
;
417 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
418 DPRINT("current %x isr %x\n",current
,isr
);
419 while (current
!=(&isr_table
[irq
]) &&
420 !isr
->ServiceRoutine(isr
,isr
->ServiceContext
))
422 current
= current
->Flink
;
423 isr
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
424 DPRINT("current %x isr %x\n",current
,isr
);
434 * Unmask the related irq
436 HalEnableSystemInterrupt (irq
+ IRQ_BASE
, 0, 0);
439 * If the processor level will drop below dispatch level on return then
440 * issue a DPC queue drain interrupt
442 if (old_level
< DISPATCH_LEVEL
)
444 HalEndSystemInterrupt (DISPATCH_LEVEL
, 0);
447 if (KeGetCurrentThread() != NULL
)
449 KeGetCurrentThread()->LastEip
= Trapframe
->Eip
;
451 KiDispatchInterrupt();
454 PsDispatchThread(THREAD_STATE_RUNNABLE
);
456 if (KeGetCurrentThread() != NULL
&&
457 KeGetCurrentThread()->Alerted
[1] != 0 &&
458 Trapframe
->Cs
!= KERNEL_CS
)
460 HalEndSystemInterrupt (APC_LEVEL
, 0);
461 KiDeliverNormalApc();
465 HalEndSystemInterrupt (old_level
, 0);
474 PLIST_ENTRY current_entry
;
477 for (i
=0;i
<NR_IRQS
;i
++)
479 DPRINT("For irq %x ",i
);
480 current_entry
= isr_table
[i
].Flink
;
481 current
= CONTAINING_RECORD(current
,KINTERRUPT
,Entry
);
482 while (current_entry
!=(&isr_table
[i
]))
484 DPRINT("Isr %x ",current
);
485 current_entry
= current_entry
->Flink
;
486 current
= CONTAINING_RECORD(current_entry
,KINTERRUPT
,Entry
);
494 KeConnectInterrupt(PKINTERRUPT InterruptObject
)
498 PKINTERRUPT ListHead
;
501 DPRINT("KeConnectInterrupt()\n");
503 Vector
= InterruptObject
->Vector
;
506 * Acquire the table spinlock
508 KeAcquireSpinLock(&isr_table_lock
,&oldlvl
);
511 * Check if the vector is already in use that we can share it
513 ListHead
= CONTAINING_RECORD(isr_table
[Vector
].Flink
,KINTERRUPT
,Entry
);
514 if (!IsListEmpty(&isr_table
[Vector
]) &&
515 (InterruptObject
->Shareable
== FALSE
|| ListHead
->Shareable
==FALSE
))
517 KeReleaseSpinLock(&isr_table_lock
,oldlvl
);
518 return(STATUS_INVALID_PARAMETER
);
523 ExAllocatePoolWithTag(NonPagedPool
, sizeof(KSPIN_LOCK
),
525 KeInitializeSpinLock(isr_lock
[Vector
]);
528 InterruptObject
->IrqLock
= isr_lock
[Vector
];
530 KeRaiseIrql(InterruptObject
->SynchLevel
,&synch_oldlvl
);
531 KeAcquireSpinLockAtDpcLevel(InterruptObject
->IrqLock
);
532 DPRINT("%x %x\n",isr_table
[Vector
].Flink
,isr_table
[Vector
].Blink
);
533 InsertTailList(&isr_table
[Vector
],&InterruptObject
->Entry
);
534 DPRINT("%x %x\n",InterruptObject
->Entry
.Flink
,
535 InterruptObject
->Entry
.Blink
);
536 KeReleaseSpinLockFromDpcLevel(InterruptObject
->IrqLock
);
537 KeLowerIrql(synch_oldlvl
);
540 * Release the table spinlock
542 KeReleaseSpinLock(&isr_table_lock
,oldlvl
);
546 return STATUS_SUCCESS
;
551 KeDisconnectInterrupt(PKINTERRUPT InterruptObject
)
553 * FUNCTION: Releases a drivers isr
555 * InterruptObject = isr to release
560 KeRaiseIrql(InterruptObject
->SynchLevel
,&oldlvl
);
561 KeAcquireSpinLockAtDpcLevel(InterruptObject
->IrqLock
);
562 RemoveEntryList(&InterruptObject
->Entry
);
563 KeReleaseSpinLockFromDpcLevel(InterruptObject
->IrqLock
);
570 KeInitializeInterrupt(PKINTERRUPT InterruptObject
,
571 PKSERVICE_ROUTINE ServiceRoutine
,
572 PVOID ServiceContext
,
573 PKSPIN_LOCK SpinLock
,
576 KIRQL SynchronizeIrql
,
577 KINTERRUPT_MODE InterruptMode
,
579 KAFFINITY ProcessorEnableMask
,
580 BOOLEAN FloatingSave
)
582 InterruptObject
->ServiceContext
= ServiceContext
;
583 InterruptObject
->ServiceRoutine
= ServiceRoutine
;
584 InterruptObject
->Vector
= Vector
;
585 InterruptObject
->ProcessorEnableMask
= ProcessorEnableMask
;
586 InterruptObject
->SynchLevel
= SynchronizeIrql
;
587 InterruptObject
->Shareable
= ShareVector
;
588 InterruptObject
->FloatingSave
= FALSE
;
590 return STATUS_SUCCESS
;
596 IoConnectInterrupt(PKINTERRUPT
* InterruptObject
,
597 PKSERVICE_ROUTINE ServiceRoutine
,
598 PVOID ServiceContext
,
599 PKSPIN_LOCK SpinLock
,
602 KIRQL SynchronizeIrql
,
603 KINTERRUPT_MODE InterruptMode
,
605 KAFFINITY ProcessorEnableMask
,
606 BOOLEAN FloatingSave
)
608 * FUNCTION: Registers a driver's isr to be called when its device interrupts
610 * InterruptObject (OUT) = Points to the interrupt object created on
612 * ServiceRoutine = Routine to be called when the device interrupts
613 * ServiceContext = Parameter to be passed to ServiceRoutine
614 * SpinLock = Initalized spinlock that will be used to synchronize
615 * access between the isr and other driver routines. This is
616 * required if the isr handles more than one vector or the
617 * driver has more than one isr
618 * Vector = Interrupt vector to allocate
619 * (returned from HalGetInterruptVector)
620 * Irql = DIRQL returned from HalGetInterruptVector
621 * SynchronizeIrql = DIRQL at which the isr will execute. This must
622 * be the highest of all the DIRQLs returned from
623 * HalGetInterruptVector if the driver has multiple
625 * InterruptMode = Specifies if the interrupt is LevelSensitive or
627 * ShareVector = Specifies if the vector can be shared
628 * ProcessorEnableMask = Processors on the isr can run
629 * FloatingSave = TRUE if the floating point stack should be saved when
630 * the isr runs. Must be false for x86 drivers
632 * IRQL: PASSIVE_LEVEL
635 PKINTERRUPT Interrupt
;
636 NTSTATUS Status
= STATUS_SUCCESS
;
638 ASSERT_IRQL(PASSIVE_LEVEL
);
640 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector
);
643 * Check the parameters
645 if (Vector
>= NR_IRQS
)
647 return(STATUS_INVALID_PARAMETER
);
649 if (FloatingSave
== TRUE
)
651 return(STATUS_INVALID_PARAMETER
);
655 * Initialize interrupt object
657 Interrupt
=ExAllocatePoolWithTag(NonPagedPool
,sizeof(KINTERRUPT
),
661 return(STATUS_INSUFFICIENT_RESOURCES
);
664 Status
= KeInitializeInterrupt(Interrupt
,
675 if (!NT_SUCCESS(Status
))
677 ExFreePool(Interrupt
);
681 Status
= KeConnectInterrupt(Interrupt
);
682 if (!NT_SUCCESS(Status
))
684 ExFreePool(Interrupt
);
688 *InterruptObject
= Interrupt
;
690 return(STATUS_SUCCESS
);
695 IoDisconnectInterrupt(PKINTERRUPT InterruptObject
)
697 * FUNCTION: Releases a drivers isr
699 * InterruptObject = isr to release
702 KeDisconnectInterrupt(InterruptObject
);
703 ExFreePool(InterruptObject
);