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