Added GNU Debugger stub for remote debugging.
[reactos.git] / reactos / ntoskrnl / ke / i386 / irq.c
1 /* $Id: irq.c,v 1.16 2002/01/23 23:39:25 chorns 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 if (old_level < DISPATCH_LEVEL)
348 {
349
350 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
351 __asm__("sti\n\t");
352
353 if (KeGetCurrentThread() != NULL)
354 {
355 KeGetCurrentThread()->LastEip = Trapframe->Eip;
356 }
357 KiDispatchInterrupt();
358 if (KeGetCurrentThread() != NULL &&
359 KeGetCurrentThread()->Alerted[1] != 0 &&
360 Trapframe->Cs != KERNEL_CS)
361 {
362 HalEndSystemInterrupt (APC_LEVEL, 0);
363 KiDeliverNormalApc();
364 }
365 }
366
367 HalEndSystemInterrupt (old_level, 0);
368 }
369
370 #else /* MP */
371
372 VOID
373 KiInterruptDispatch (ULONG irq, PKIRQ_TRAPFRAME Trapframe)
374 /*
375 * FUNCTION: Calls the irq specific handler for an irq
376 * ARGUMENTS:
377 * irq = IRQ that has interrupted
378 */
379 {
380 KIRQL old_level;
381 PKINTERRUPT isr;
382 PLIST_ENTRY current;
383
384 #ifdef DBG
385
386 KTRAP_FRAME KernelTrapFrame;
387
388 KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
389 KeGetCurrentThread()->TrapFrame = &KernelTrapFrame;
390
391 #endif /* DBG */
392
393 /*
394 * Notify the rest of the kernel of the raised irq level
395 */
396 HalBeginSystemInterrupt (irq + IRQ_BASE,
397 PROFILE_LEVEL - irq,
398 &old_level);
399
400 /*
401 * Enable interrupts
402 * NOTE: Only higher priority interrupts will get through
403 */
404 __asm__("sti\n\t");
405
406 if (irq == 0)
407 {
408 KiUpdateSystemTime(old_level, Trapframe->Eip);
409 }
410 else
411 {
412 DPRINT("KiInterruptDispatch(irq %d)\n",irq);
413 /*
414 * Iterate the list until one of the isr tells us its device interrupted
415 */
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))
421 {
422 current = current->Flink;
423 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
424 DPRINT("current %x isr %x\n",current,isr);
425 }
426 }
427
428 /*
429 * Disable interrupts
430 */
431 __asm__("cli\n\t");
432
433 /*
434 * Unmask the related irq
435 */
436 HalEnableSystemInterrupt (irq + IRQ_BASE, 0, 0);
437
438 /*
439 * If the processor level will drop below dispatch level on return then
440 * issue a DPC queue drain interrupt
441 */
442 if (old_level < DISPATCH_LEVEL)
443 {
444 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
445 __asm__("sti\n\t");
446
447 if (KeGetCurrentThread() != NULL)
448 {
449 KeGetCurrentThread()->LastEip = Trapframe->Eip;
450 }
451 KiDispatchInterrupt();
452 if (irq == 0)
453 {
454 PsDispatchThread(THREAD_STATE_RUNNABLE);
455 }
456 if (KeGetCurrentThread() != NULL &&
457 KeGetCurrentThread()->Alerted[1] != 0 &&
458 Trapframe->Cs != KERNEL_CS)
459 {
460 HalEndSystemInterrupt (APC_LEVEL, 0);
461 KiDeliverNormalApc();
462 }
463 }
464
465 HalEndSystemInterrupt (old_level, 0);
466 }
467
468 #endif /* MP */
469
470 static VOID
471 KeDumpIrqList(VOID)
472 {
473 PKINTERRUPT current;
474 PLIST_ENTRY current_entry;
475 unsigned int i;
476
477 for (i=0;i<NR_IRQS;i++)
478 {
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]))
483 {
484 DPRINT("Isr %x ",current);
485 current_entry = current_entry->Flink;
486 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
487 }
488 DPRINT("\n",0);
489 }
490 }
491
492
493 NTSTATUS STDCALL
494 KeConnectInterrupt(PKINTERRUPT InterruptObject)
495 {
496 KIRQL oldlvl;
497 KIRQL synch_oldlvl;
498 PKINTERRUPT ListHead;
499 ULONG Vector;
500
501 DPRINT("KeConnectInterrupt()\n");
502
503 Vector = InterruptObject->Vector;
504
505 /*
506 * Acquire the table spinlock
507 */
508 KeAcquireSpinLock(&isr_table_lock,&oldlvl);
509
510 /*
511 * Check if the vector is already in use that we can share it
512 */
513 ListHead = CONTAINING_RECORD(isr_table[Vector].Flink,KINTERRUPT,Entry);
514 if (!IsListEmpty(&isr_table[Vector]) &&
515 (InterruptObject->Shareable == FALSE || ListHead->Shareable==FALSE))
516 {
517 KeReleaseSpinLock(&isr_table_lock,oldlvl);
518 return(STATUS_INVALID_PARAMETER);
519 }
520 else
521 {
522 isr_lock[Vector] =
523 ExAllocatePoolWithTag(NonPagedPool, sizeof(KSPIN_LOCK),
524 TAG_ISR_LOCK);
525 KeInitializeSpinLock(isr_lock[Vector]);
526 }
527
528 InterruptObject->IrqLock = isr_lock[Vector];
529
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);
538
539 /*
540 * Release the table spinlock
541 */
542 KeReleaseSpinLock(&isr_table_lock,oldlvl);
543
544 KeDumpIrqList();
545
546 return STATUS_SUCCESS;
547 }
548
549
550 VOID STDCALL
551 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
552 /*
553 * FUNCTION: Releases a drivers isr
554 * ARGUMENTS:
555 * InterruptObject = isr to release
556 */
557 {
558 KIRQL oldlvl;
559
560 KeRaiseIrql(InterruptObject->SynchLevel,&oldlvl);
561 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
562 RemoveEntryList(&InterruptObject->Entry);
563 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
564 KeLowerIrql(oldlvl);
565 }
566
567
568 NTSTATUS
569 STDCALL
570 KeInitializeInterrupt(PKINTERRUPT InterruptObject,
571 PKSERVICE_ROUTINE ServiceRoutine,
572 PVOID ServiceContext,
573 PKSPIN_LOCK SpinLock,
574 ULONG Vector,
575 KIRQL Irql,
576 KIRQL SynchronizeIrql,
577 KINTERRUPT_MODE InterruptMode,
578 BOOLEAN ShareVector,
579 KAFFINITY ProcessorEnableMask,
580 BOOLEAN FloatingSave)
581 {
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;
589
590 return STATUS_SUCCESS;
591 }
592
593
594 NTSTATUS
595 STDCALL
596 IoConnectInterrupt(PKINTERRUPT* InterruptObject,
597 PKSERVICE_ROUTINE ServiceRoutine,
598 PVOID ServiceContext,
599 PKSPIN_LOCK SpinLock,
600 ULONG Vector,
601 KIRQL Irql,
602 KIRQL SynchronizeIrql,
603 KINTERRUPT_MODE InterruptMode,
604 BOOLEAN ShareVector,
605 KAFFINITY ProcessorEnableMask,
606 BOOLEAN FloatingSave)
607 /*
608 * FUNCTION: Registers a driver's isr to be called when its device interrupts
609 * ARGUMENTS:
610 * InterruptObject (OUT) = Points to the interrupt object created on
611 * return
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
624 * isrs
625 * InterruptMode = Specifies if the interrupt is LevelSensitive or
626 * Latched
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
631 * RETURNS: Status
632 * IRQL: PASSIVE_LEVEL
633 */
634 {
635 PKINTERRUPT Interrupt;
636 NTSTATUS Status = STATUS_SUCCESS;
637
638 ASSERT_IRQL(PASSIVE_LEVEL);
639
640 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector);
641
642 /*
643 * Check the parameters
644 */
645 if (Vector >= NR_IRQS)
646 {
647 return(STATUS_INVALID_PARAMETER);
648 }
649 if (FloatingSave == TRUE)
650 {
651 return(STATUS_INVALID_PARAMETER);
652 }
653
654 /*
655 * Initialize interrupt object
656 */
657 Interrupt=ExAllocatePoolWithTag(NonPagedPool,sizeof(KINTERRUPT),
658 TAG_KINTERRUPT);
659 if (Interrupt==NULL)
660 {
661 return(STATUS_INSUFFICIENT_RESOURCES);
662 }
663
664 Status = KeInitializeInterrupt(Interrupt,
665 ServiceRoutine,
666 ServiceContext,
667 SpinLock,
668 Vector,
669 Irql,
670 SynchronizeIrql,
671 InterruptMode,
672 ShareVector,
673 ProcessorEnableMask,
674 FloatingSave);
675 if (!NT_SUCCESS(Status))
676 {
677 ExFreePool(Interrupt);
678 return Status;
679 }
680
681 Status = KeConnectInterrupt(Interrupt);
682 if (!NT_SUCCESS(Status))
683 {
684 ExFreePool(Interrupt);
685 return Status;
686 }
687
688 *InterruptObject = Interrupt;
689
690 return(STATUS_SUCCESS);
691 }
692
693
694 VOID STDCALL
695 IoDisconnectInterrupt(PKINTERRUPT InterruptObject)
696 /*
697 * FUNCTION: Releases a drivers isr
698 * ARGUMENTS:
699 * InterruptObject = isr to release
700 */
701 {
702 KeDisconnectInterrupt(InterruptObject);
703 ExFreePool(InterruptObject);
704 }
705
706 /* EOF */