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