2d3959837e76c5cd362a8f4c195779cc509989b5
[reactos.git] / reactos / ntoskrnl / ke / i386 / irq.c
1 /* $Id: irq.c,v 1.19 2002/05/02 23:45:33 dwelch Exp $
2 *
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)
8 * UPDATE HISTORY:
9 * 29/05/98: Created
10 */
11
12 /*
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
16 *
17 */
18
19 /* INCLUDES ****************************************************************/
20
21 #include <ddk/ntddk.h>
22 #include <roscfg.h>
23 #include <internal/ke.h>
24 #include <internal/ps.h>
25 #include <internal/i386/segment.h>
26 #include <internal/pool.h>
27
28 #ifdef MP
29 #include <internal/hal/mps.h>
30 #endif /* MP */
31
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 /* GLOBALS *****************************************************************/
36
37 #ifdef MP
38
39 #define IRQ_BASE FIRST_DEVICE_VECTOR
40 #define NR_IRQS 0x100 - 0x30
41
42 #define __STR(x) #x
43 #define STR(x) __STR(x)
44
45 #define INT_NAME(intnum) _KiUnexpectedInterrupt##intnum
46 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
47
48 #define BUILD_COMMON_INTERRUPT_HANDLER() \
49 __asm__( \
50 "_KiCommonInterrupt:\n\t" \
51 "cld\n\t" \
52 "pushl %ds\n\t" \
53 "pushl %es\n\t" \
54 "pushl %fs\n\t" \
55 "pushl %gs\n\t" \
56 "movl $0xceafbeef,%eax\n\t" \
57 "pushl %eax\n\t" \
58 "movl $" STR(KERNEL_DS) ",%eax\n\t" \
59 "movl %eax,%ds\n\t" \
60 "movl %eax,%es\n\t" \
61 "movl $" STR(PCR_SELECTOR) ",%eax\n\t" \
62 "movl %eax,%fs\n\t" \
63 "pushl %esp\n\t" \
64 "pushl %ebx\n\t" \
65 "call _KiInterruptDispatch\n\t" \
66 "popl %eax\n\t" \
67 "popl %eax\n\t" \
68 "popl %eax\n\t" \
69 "popl %gs\n\t" \
70 "popl %fs\n\t" \
71 "popl %es\n\t" \
72 "popl %ds\n\t" \
73 "popa\n\t" \
74 "iret\n\t");
75
76 #define BUILD_INTERRUPT_HANDLER(intnum) \
77 VOID INT_NAME2(intnum)(VOID); \
78 __asm__( \
79 STR(INT_NAME(intnum)) ":\n\t" \
80 "pusha\n\t" \
81 "movl $0x" STR(intnum) ",%ebx\n\t" \
82 "jmp _KiCommonInterrupt");
83
84
85 /* Interrupt handlers and declarations */
86
87 #define B(x,y) \
88 BUILD_INTERRUPT_HANDLER(x##y)
89
90 #define B16(x) \
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)
95
96
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)
101 B16(F)
102
103 #undef B
104 #undef B16
105
106
107 /* Interrupt handler list */
108
109 #define L(x,y) \
110 (ULONG)& INT_NAME2(x##y)
111
112 #define L16(x) \
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)
117
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),
122 L16(F)
123 };
124
125 #undef L
126 #undef L16
127
128 #else /* MP */
129
130 #define NR_IRQS (16)
131 #define IRQ_BASE (0x40)
132
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);
149
150 static unsigned int irq_handler[NR_IRQS]=
151 {
152 (int)&irq_handler_0,
153 (int)&irq_handler_1,
154 (int)&irq_handler_2,
155 (int)&irq_handler_3,
156 (int)&irq_handler_4,
157 (int)&irq_handler_5,
158 (int)&irq_handler_6,
159 (int)&irq_handler_7,
160 (int)&irq_handler_8,
161 (int)&irq_handler_9,
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,
168 };
169
170 #endif /* MP */
171
172 /*
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.
176 */
177
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,};
181
182 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
183 #define TAG_KINTERRUPT TAG('K', 'I', 'S', 'R')
184
185 /* FUNCTIONS ****************************************************************/
186
187 #define PRESENT (0x8000)
188 #define I486_INTERRUPT_GATE (0xe00)
189
190 VOID KeInitInterrupts (VOID)
191 {
192 int i;
193
194 #ifdef MP
195
196 /*
197 * Setup the IDT entries to point to the interrupt handlers
198 */
199 for (i=0;i<NR_IRQS;i++)
200 {
201 KiIdt[0x30+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
202 KiIdt[0x30+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
203 I486_INTERRUPT_GATE;
204 InitializeListHead(&isr_table[i]);
205 }
206
207 #else
208
209 /*
210 * Setup the IDT entries to point to the interrupt handlers
211 */
212 for (i=0;i<NR_IRQS;i++)
213 {
214 KiIdt[IRQ_BASE+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
215 KiIdt[IRQ_BASE+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
216 I486_INTERRUPT_GATE;
217 InitializeListHead(&isr_table[i]);
218 }
219
220 #endif
221
222 }
223
224 typedef struct _KIRQ_TRAPFRAME
225 {
226 ULONG Magic;
227 ULONG Fs;
228 ULONG Es;
229 ULONG Ds;
230 ULONG Eax;
231 ULONG Ecx;
232 ULONG Edx;
233 ULONG Ebx;
234 ULONG Esp;
235 ULONG Ebp;
236 ULONG Esi;
237 ULONG Edi;
238 ULONG Eip;
239 ULONG Cs;
240 ULONG Eflags;
241 } KIRQ_TRAPFRAME, *PKIRQ_TRAPFRAME;
242
243 #ifdef DBG
244
245 VOID
246 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame,
247 PKTRAP_FRAME TrapFrame)
248 {
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;
263 }
264
265 #endif
266
267 #ifdef MP
268
269 VOID
270 KiInterruptDispatch (ULONG Vector, PKIRQ_TRAPFRAME Trapframe)
271 /*
272 * FUNCTION: Calls the irq specific handler for an irq
273 * ARGUMENTS:
274 * Vector = Interrupt vector
275 * Trapframe = CPU context
276 */
277 {
278 KIRQL old_level;
279 PKINTERRUPT isr;
280 PLIST_ENTRY current;
281 ULONG irq;
282
283 #ifdef DBG
284
285 KTRAP_FRAME KernelTrapFrame;
286
287 KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
288 KeGetCurrentThread()->TrapFrame = &KernelTrapFrame;
289
290 #endif /* DBG */
291
292 DPRINT("I(%d) ", Vector);
293
294 /*
295 * Notify the rest of the kernel of the raised irq level
296 */
297 HalBeginSystemInterrupt (Vector,
298 VECTOR2IRQL(Vector),
299 &old_level);
300
301 irq = VECTOR2IRQ(Vector);
302
303 /*
304 * Enable interrupts
305 * NOTE: Only higher priority interrupts will get through
306 */
307 __asm__("sti\n\t");
308
309 if (irq == 0)
310 {
311 if (KeGetCurrentProcessorNumber() == 0)
312 {
313 KiUpdateSystemTime(old_level, Trapframe->Eip);
314 }
315 }
316 else
317 {
318 DPRINT("KiInterruptDispatch(Vector %d)\n", Vector);
319 /*
320 * Iterate the list until one of the isr tells us its device interrupted
321 */
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))
327 {
328 current = current->Flink;
329 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
330 //DPRINT("current %x isr %x\n",current,isr);
331 }
332 }
333 /*
334 * Disable interrupts
335 */
336 __asm__("cli\n\t");
337
338 /*
339 * Unmask the related irq
340 */
341 HalEnableSystemInterrupt (Vector, 0, 0);
342
343 /*
344 * If the processor level will drop below dispatch level on return then
345 * issue a DPC queue drain interrupt
346 */
347
348 __asm__("sti\n\t");
349
350 if (old_level < DISPATCH_LEVEL)
351 {
352
353 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
354
355 if (KeGetCurrentThread() != NULL)
356 {
357 KeGetCurrentThread()->LastEip = Trapframe->Eip;
358 }
359 KiDispatchInterrupt();
360 if (KeGetCurrentThread() != NULL &&
361 KeGetCurrentThread()->Alerted[1] != 0 &&
362 Trapframe->Cs != KERNEL_CS)
363 {
364 HalEndSystemInterrupt (APC_LEVEL, 0);
365 KiDeliverNormalApc();
366 }
367 }
368
369 HalEndSystemInterrupt (old_level, 0);
370 }
371
372 #else /* MP */
373
374 VOID STDCALL
375 KiInterruptDispatch2 (ULONG Irq, KIRQL old_level)
376 {
377 PKINTERRUPT isr;
378 PLIST_ENTRY current;
379
380 if (Irq == 0)
381 {
382 KiUpdateSystemTime(old_level, 0);
383 }
384 else
385 {
386 /*
387 * Iterate the list until one of the isr tells us its device interrupted
388 */
389 current = isr_table[Irq].Flink;
390 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
391 while (current != &isr_table[Irq] &&
392 !isr->ServiceRoutine(isr, isr->ServiceContext))
393 {
394 current = current->Flink;
395 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
396 }
397 }
398 }
399
400 VOID
401 KiInterruptDispatch (ULONG irq, PKIRQ_TRAPFRAME Trapframe)
402 /*
403 * FUNCTION: Calls the irq specific handler for an irq
404 * ARGUMENTS:
405 * irq = IRQ that has interrupted
406 */
407 {
408 KIRQL old_level;
409 static ULONG Irq0Count = 0;
410
411 #if 0
412 KTRAP_FRAME KernelTrapFrame;
413
414 KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
415 KeGetCurrentThread()->TrapFrame = &KernelTrapFrame;
416 #endif /* DBG */
417
418 if (InterlockedIncrement(&Irq0Count) > 32)
419 {
420 __asm__("int $3\n\t");
421 }
422
423 /*
424 * At this point we have interrupts disabled, nothing has been done to
425 * the PIC.
426 */
427
428 /*
429 * Notify the rest of the kernel of the raised irq level. For the
430 * default HAL this will send an EOI to the PIC and alter the IRQL.
431 */
432 if (!HalBeginSystemInterrupt (irq + IRQ_BASE,
433 PROFILE_LEVEL - irq,
434 &old_level))
435 {
436 InterlockedDecrement(&Irq0Count);
437 return;
438 }
439
440 /*
441 * Enable interrupts
442 * NOTE: Only higher priority interrupts will get through
443 */
444 __asm__("sti\n\t");
445
446 /*
447 * Actually call the ISR.
448 */
449 KiInterruptDispatch2(irq, old_level);
450
451 /*
452 * End the system interrupt.
453 */
454 HalEndSystemInterrupt (old_level, 0);
455
456 InterlockedDecrement(&Irq0Count);
457
458 if (old_level < DISPATCH_LEVEL && irq == 0)
459 {
460 PsDispatchThread(THREAD_STATE_RUNNABLE);
461 }
462 }
463
464 #endif /* MP */
465
466 static VOID
467 KeDumpIrqList(VOID)
468 {
469 PKINTERRUPT current;
470 PLIST_ENTRY current_entry;
471 unsigned int i;
472
473 for (i=0;i<NR_IRQS;i++)
474 {
475 DPRINT("For irq %x ",i);
476 current_entry = isr_table[i].Flink;
477 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
478 while (current_entry!=(&isr_table[i]))
479 {
480 DPRINT("Isr %x ",current);
481 current_entry = current_entry->Flink;
482 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
483 }
484 DPRINT("\n",0);
485 }
486 }
487
488 NTSTATUS STDCALL
489 KeConnectInterrupt(PKINTERRUPT InterruptObject)
490 {
491 KIRQL oldlvl;
492 KIRQL synch_oldlvl;
493 PKINTERRUPT ListHead;
494 ULONG Vector;
495
496 DPRINT("KeConnectInterrupt()\n");
497
498 Vector = InterruptObject->Vector;
499
500 /*
501 * Acquire the table spinlock
502 */
503 KeAcquireSpinLock(&isr_table_lock,&oldlvl);
504
505 /*
506 * Check if the vector is already in use that we can share it
507 */
508 ListHead = CONTAINING_RECORD(isr_table[Vector].Flink,KINTERRUPT,Entry);
509 if (!IsListEmpty(&isr_table[Vector]) &&
510 (InterruptObject->Shareable == FALSE || ListHead->Shareable==FALSE))
511 {
512 KeReleaseSpinLock(&isr_table_lock,oldlvl);
513 return(STATUS_INVALID_PARAMETER);
514 }
515 else
516 {
517 isr_lock[Vector] =
518 ExAllocatePoolWithTag(NonPagedPool, sizeof(KSPIN_LOCK),
519 TAG_ISR_LOCK);
520 KeInitializeSpinLock(isr_lock[Vector]);
521 }
522
523 InterruptObject->IrqLock = isr_lock[Vector];
524
525 KeRaiseIrql(InterruptObject->SynchLevel,&synch_oldlvl);
526 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
527 DPRINT("%x %x\n",isr_table[Vector].Flink,isr_table[Vector].Blink);
528 InsertTailList(&isr_table[Vector],&InterruptObject->Entry);
529 DPRINT("%x %x\n",InterruptObject->Entry.Flink,
530 InterruptObject->Entry.Blink);
531 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
532 KeLowerIrql(synch_oldlvl);
533
534 /*
535 * Release the table spinlock
536 */
537 KeReleaseSpinLock(&isr_table_lock,oldlvl);
538
539 KeDumpIrqList();
540
541 return STATUS_SUCCESS;
542 }
543
544
545 VOID STDCALL
546 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
547 /*
548 * FUNCTION: Releases a drivers isr
549 * ARGUMENTS:
550 * InterruptObject = isr to release
551 */
552 {
553 KIRQL oldlvl;
554
555 KeRaiseIrql(InterruptObject->SynchLevel,&oldlvl);
556 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
557 RemoveEntryList(&InterruptObject->Entry);
558 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
559 KeLowerIrql(oldlvl);
560 }
561
562
563 NTSTATUS
564 STDCALL
565 KeInitializeInterrupt(PKINTERRUPT InterruptObject,
566 PKSERVICE_ROUTINE ServiceRoutine,
567 PVOID ServiceContext,
568 PKSPIN_LOCK SpinLock,
569 ULONG Vector,
570 KIRQL Irql,
571 KIRQL SynchronizeIrql,
572 KINTERRUPT_MODE InterruptMode,
573 BOOLEAN ShareVector,
574 KAFFINITY ProcessorEnableMask,
575 BOOLEAN FloatingSave)
576 {
577 InterruptObject->ServiceContext = ServiceContext;
578 InterruptObject->ServiceRoutine = ServiceRoutine;
579 InterruptObject->Vector = Vector;
580 InterruptObject->ProcessorEnableMask = ProcessorEnableMask;
581 InterruptObject->SynchLevel = SynchronizeIrql;
582 InterruptObject->Shareable = ShareVector;
583 InterruptObject->FloatingSave = FALSE;
584
585 return STATUS_SUCCESS;
586 }
587
588
589 NTSTATUS STDCALL
590 IoConnectInterrupt(PKINTERRUPT* InterruptObject,
591 PKSERVICE_ROUTINE ServiceRoutine,
592 PVOID ServiceContext,
593 PKSPIN_LOCK SpinLock,
594 ULONG Vector,
595 KIRQL Irql,
596 KIRQL SynchronizeIrql,
597 KINTERRUPT_MODE InterruptMode,
598 BOOLEAN ShareVector,
599 KAFFINITY ProcessorEnableMask,
600 BOOLEAN FloatingSave)
601 /*
602 * FUNCTION: Registers a driver's isr to be called when its device interrupts
603 * ARGUMENTS:
604 * InterruptObject (OUT) = Points to the interrupt object created on
605 * return
606 * ServiceRoutine = Routine to be called when the device interrupts
607 * ServiceContext = Parameter to be passed to ServiceRoutine
608 * SpinLock = Initalized spinlock that will be used to synchronize
609 * access between the isr and other driver routines. This is
610 * required if the isr handles more than one vector or the
611 * driver has more than one isr
612 * Vector = Interrupt vector to allocate
613 * (returned from HalGetInterruptVector)
614 * Irql = DIRQL returned from HalGetInterruptVector
615 * SynchronizeIrql = DIRQL at which the isr will execute. This must
616 * be the highest of all the DIRQLs returned from
617 * HalGetInterruptVector if the driver has multiple
618 * isrs
619 * InterruptMode = Specifies if the interrupt is LevelSensitive or
620 * Latched
621 * ShareVector = Specifies if the vector can be shared
622 * ProcessorEnableMask = Processors on the isr can run
623 * FloatingSave = TRUE if the floating point stack should be saved when
624 * the isr runs. Must be false for x86 drivers
625 * RETURNS: Status
626 * IRQL: PASSIVE_LEVEL
627 */
628 {
629 PKINTERRUPT Interrupt;
630 NTSTATUS Status = STATUS_SUCCESS;
631
632 ASSERT_IRQL(PASSIVE_LEVEL);
633
634 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector);
635
636 /*
637 * Check the parameters
638 */
639 if (Vector >= NR_IRQS)
640 {
641 return(STATUS_INVALID_PARAMETER);
642 }
643 if (FloatingSave == TRUE)
644 {
645 return(STATUS_INVALID_PARAMETER);
646 }
647
648 /*
649 * Initialize interrupt object
650 */
651 Interrupt=ExAllocatePoolWithTag(NonPagedPool,sizeof(KINTERRUPT),
652 TAG_KINTERRUPT);
653 if (Interrupt==NULL)
654 {
655 return(STATUS_INSUFFICIENT_RESOURCES);
656 }
657
658 Status = KeInitializeInterrupt(Interrupt,
659 ServiceRoutine,
660 ServiceContext,
661 SpinLock,
662 Vector,
663 Irql,
664 SynchronizeIrql,
665 InterruptMode,
666 ShareVector,
667 ProcessorEnableMask,
668 FloatingSave);
669 if (!NT_SUCCESS(Status))
670 {
671 ExFreePool(Interrupt);
672 return Status;
673 }
674
675 Status = KeConnectInterrupt(Interrupt);
676 if (!NT_SUCCESS(Status))
677 {
678 ExFreePool(Interrupt);
679 return Status;
680 }
681
682 *InterruptObject = Interrupt;
683
684 return(STATUS_SUCCESS);
685 }
686
687
688 VOID STDCALL
689 IoDisconnectInterrupt(PKINTERRUPT InterruptObject)
690 /*
691 * FUNCTION: Releases a drivers isr
692 * ARGUMENTS:
693 * InterruptObject = isr to release
694 */
695 {
696 KeDisconnectInterrupt(InterruptObject);
697 ExFreePool(InterruptObject);
698 }
699
700 /* EOF */