Changed internal/config.h -> roscfg.h in a number of files.
[reactos.git] / reactos / ntoskrnl / ke / i386 / irq.c
1 /* $Id: irq.c,v 1.15 2001/08/30 20:38:19 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 MP
244
245 VOID
246 KiInterruptDispatch (ULONG Vector, PKIRQ_TRAPFRAME Trapframe)
247 /*
248 * FUNCTION: Calls the irq specific handler for an irq
249 * ARGUMENTS:
250 * Vector = Interrupt vector
251 * Trapframe = CPU context
252 */
253 {
254 KIRQL old_level;
255 PKINTERRUPT isr;
256 PLIST_ENTRY current;
257 ULONG irq;
258
259 DPRINT("I(%d) ", Vector);
260
261 /*
262 * Notify the rest of the kernel of the raised irq level
263 */
264 HalBeginSystemInterrupt (Vector,
265 VECTOR2IRQL(Vector),
266 &old_level);
267
268 irq = VECTOR2IRQ(Vector);
269
270 /*
271 * Enable interrupts
272 * NOTE: Only higher priority interrupts will get through
273 */
274 __asm__("sti\n\t");
275
276 if (irq == 0)
277 {
278 if (KeGetCurrentProcessorNumber() == 0)
279 {
280 KiUpdateSystemTime(old_level, Trapframe->Eip);
281 }
282 }
283 else
284 {
285 DPRINT("KiInterruptDispatch(Vector %d)\n", Vector);
286 /*
287 * Iterate the list until one of the isr tells us its device interrupted
288 */
289 current = isr_table[irq].Flink;
290 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
291 //DPRINT("current %x isr %x\n",current,isr);
292 while (current!=(&isr_table[irq]) &&
293 !isr->ServiceRoutine(isr,isr->ServiceContext))
294 {
295 current = current->Flink;
296 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
297 //DPRINT("current %x isr %x\n",current,isr);
298 }
299 }
300 /*
301 * Disable interrupts
302 */
303 __asm__("cli\n\t");
304
305 /*
306 * Unmask the related irq
307 */
308 HalEnableSystemInterrupt (Vector, 0, 0);
309
310 /*
311 * If the processor level will drop below dispatch level on return then
312 * issue a DPC queue drain interrupt
313 */
314 if (old_level < DISPATCH_LEVEL)
315 {
316
317 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
318 __asm__("sti\n\t");
319
320 if (KeGetCurrentThread() != NULL)
321 {
322 KeGetCurrentThread()->LastEip = Trapframe->Eip;
323 }
324 KiDispatchInterrupt();
325 if (KeGetCurrentThread() != NULL &&
326 KeGetCurrentThread()->Alerted[1] != 0 &&
327 Trapframe->Cs != KERNEL_CS)
328 {
329 HalEndSystemInterrupt (APC_LEVEL, 0);
330 KiDeliverNormalApc();
331 }
332 }
333
334 HalEndSystemInterrupt (old_level, 0);
335 }
336
337 #else /* MP */
338
339 VOID
340 KiInterruptDispatch (ULONG irq, PKIRQ_TRAPFRAME Trapframe)
341 /*
342 * FUNCTION: Calls the irq specific handler for an irq
343 * ARGUMENTS:
344 * irq = IRQ that has interrupted
345 */
346 {
347 KIRQL old_level;
348 PKINTERRUPT isr;
349 PLIST_ENTRY current;
350
351 /*
352 * Notify the rest of the kernel of the raised irq level
353 */
354 HalBeginSystemInterrupt (irq + IRQ_BASE,
355 PROFILE_LEVEL - irq,
356 &old_level);
357
358 /*
359 * Enable interrupts
360 * NOTE: Only higher priority interrupts will get through
361 */
362 __asm__("sti\n\t");
363
364 if (irq == 0)
365 {
366 KiUpdateSystemTime(old_level, Trapframe->Eip);
367 }
368 else
369 {
370 DPRINT("KiInterruptDispatch(irq %d)\n",irq);
371 /*
372 * Iterate the list until one of the isr tells us its device interrupted
373 */
374 current = isr_table[irq].Flink;
375 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
376 DPRINT("current %x isr %x\n",current,isr);
377 while (current!=(&isr_table[irq]) &&
378 !isr->ServiceRoutine(isr,isr->ServiceContext))
379 {
380 current = current->Flink;
381 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
382 DPRINT("current %x isr %x\n",current,isr);
383 }
384 }
385
386 /*
387 * Disable interrupts
388 */
389 __asm__("cli\n\t");
390
391 /*
392 * Unmask the related irq
393 */
394 HalEnableSystemInterrupt (irq + IRQ_BASE, 0, 0);
395
396 /*
397 * If the processor level will drop below dispatch level on return then
398 * issue a DPC queue drain interrupt
399 */
400 if (old_level < DISPATCH_LEVEL)
401 {
402 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
403 __asm__("sti\n\t");
404
405 if (KeGetCurrentThread() != NULL)
406 {
407 KeGetCurrentThread()->LastEip = Trapframe->Eip;
408 }
409 KiDispatchInterrupt();
410 if (irq == 0)
411 {
412 PsDispatchThread(THREAD_STATE_RUNNABLE);
413 }
414 if (KeGetCurrentThread() != NULL &&
415 KeGetCurrentThread()->Alerted[1] != 0 &&
416 Trapframe->Cs != KERNEL_CS)
417 {
418 HalEndSystemInterrupt (APC_LEVEL, 0);
419 KiDeliverNormalApc();
420 }
421 }
422
423 HalEndSystemInterrupt (old_level, 0);
424 }
425
426 #endif /* MP */
427
428 static VOID
429 KeDumpIrqList(VOID)
430 {
431 PKINTERRUPT current;
432 PLIST_ENTRY current_entry;
433 unsigned int i;
434
435 for (i=0;i<NR_IRQS;i++)
436 {
437 DPRINT("For irq %x ",i);
438 current_entry = isr_table[i].Flink;
439 current = CONTAINING_RECORD(current,KINTERRUPT,Entry);
440 while (current_entry!=(&isr_table[i]))
441 {
442 DPRINT("Isr %x ",current);
443 current_entry = current_entry->Flink;
444 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
445 }
446 DPRINT("\n",0);
447 }
448 }
449
450
451 NTSTATUS STDCALL
452 KeConnectInterrupt(PKINTERRUPT InterruptObject)
453 {
454 KIRQL oldlvl;
455 KIRQL synch_oldlvl;
456 PKINTERRUPT ListHead;
457 ULONG Vector;
458
459 DPRINT("KeConnectInterrupt()\n");
460
461 Vector = InterruptObject->Vector;
462
463 /*
464 * Acquire the table spinlock
465 */
466 KeAcquireSpinLock(&isr_table_lock,&oldlvl);
467
468 /*
469 * Check if the vector is already in use that we can share it
470 */
471 ListHead = CONTAINING_RECORD(isr_table[Vector].Flink,KINTERRUPT,Entry);
472 if (!IsListEmpty(&isr_table[Vector]) &&
473 (InterruptObject->Shareable == FALSE || ListHead->Shareable==FALSE))
474 {
475 KeReleaseSpinLock(&isr_table_lock,oldlvl);
476 return(STATUS_INVALID_PARAMETER);
477 }
478 else
479 {
480 isr_lock[Vector] =
481 ExAllocatePoolWithTag(NonPagedPool, sizeof(KSPIN_LOCK),
482 TAG_ISR_LOCK);
483 KeInitializeSpinLock(isr_lock[Vector]);
484 }
485
486 InterruptObject->IrqLock = isr_lock[Vector];
487
488 KeRaiseIrql(InterruptObject->SynchLevel,&synch_oldlvl);
489 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
490 DPRINT("%x %x\n",isr_table[Vector].Flink,isr_table[Vector].Blink);
491 InsertTailList(&isr_table[Vector],&InterruptObject->Entry);
492 DPRINT("%x %x\n",InterruptObject->Entry.Flink,
493 InterruptObject->Entry.Blink);
494 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
495 KeLowerIrql(synch_oldlvl);
496
497 /*
498 * Release the table spinlock
499 */
500 KeReleaseSpinLock(&isr_table_lock,oldlvl);
501
502 KeDumpIrqList();
503
504 return STATUS_SUCCESS;
505 }
506
507
508 VOID STDCALL
509 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
510 /*
511 * FUNCTION: Releases a drivers isr
512 * ARGUMENTS:
513 * InterruptObject = isr to release
514 */
515 {
516 KIRQL oldlvl;
517
518 KeRaiseIrql(InterruptObject->SynchLevel,&oldlvl);
519 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
520 RemoveEntryList(&InterruptObject->Entry);
521 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
522 KeLowerIrql(oldlvl);
523 }
524
525
526 NTSTATUS
527 STDCALL
528 KeInitializeInterrupt(PKINTERRUPT InterruptObject,
529 PKSERVICE_ROUTINE ServiceRoutine,
530 PVOID ServiceContext,
531 PKSPIN_LOCK SpinLock,
532 ULONG Vector,
533 KIRQL Irql,
534 KIRQL SynchronizeIrql,
535 KINTERRUPT_MODE InterruptMode,
536 BOOLEAN ShareVector,
537 KAFFINITY ProcessorEnableMask,
538 BOOLEAN FloatingSave)
539 {
540 InterruptObject->ServiceContext = ServiceContext;
541 InterruptObject->ServiceRoutine = ServiceRoutine;
542 InterruptObject->Vector = Vector;
543 InterruptObject->ProcessorEnableMask = ProcessorEnableMask;
544 InterruptObject->SynchLevel = SynchronizeIrql;
545 InterruptObject->Shareable = ShareVector;
546 InterruptObject->FloatingSave = FALSE;
547
548 return STATUS_SUCCESS;
549 }
550
551
552 NTSTATUS
553 STDCALL
554 IoConnectInterrupt(PKINTERRUPT* InterruptObject,
555 PKSERVICE_ROUTINE ServiceRoutine,
556 PVOID ServiceContext,
557 PKSPIN_LOCK SpinLock,
558 ULONG Vector,
559 KIRQL Irql,
560 KIRQL SynchronizeIrql,
561 KINTERRUPT_MODE InterruptMode,
562 BOOLEAN ShareVector,
563 KAFFINITY ProcessorEnableMask,
564 BOOLEAN FloatingSave)
565 /*
566 * FUNCTION: Registers a driver's isr to be called when its device interrupts
567 * ARGUMENTS:
568 * InterruptObject (OUT) = Points to the interrupt object created on
569 * return
570 * ServiceRoutine = Routine to be called when the device interrupts
571 * ServiceContext = Parameter to be passed to ServiceRoutine
572 * SpinLock = Initalized spinlock that will be used to synchronize
573 * access between the isr and other driver routines. This is
574 * required if the isr handles more than one vector or the
575 * driver has more than one isr
576 * Vector = Interrupt vector to allocate
577 * (returned from HalGetInterruptVector)
578 * Irql = DIRQL returned from HalGetInterruptVector
579 * SynchronizeIrql = DIRQL at which the isr will execute. This must
580 * be the highest of all the DIRQLs returned from
581 * HalGetInterruptVector if the driver has multiple
582 * isrs
583 * InterruptMode = Specifies if the interrupt is LevelSensitive or
584 * Latched
585 * ShareVector = Specifies if the vector can be shared
586 * ProcessorEnableMask = Processors on the isr can run
587 * FloatingSave = TRUE if the floating point stack should be saved when
588 * the isr runs. Must be false for x86 drivers
589 * RETURNS: Status
590 * IRQL: PASSIVE_LEVEL
591 */
592 {
593 PKINTERRUPT Interrupt;
594 NTSTATUS Status = STATUS_SUCCESS;
595
596 ASSERT_IRQL(PASSIVE_LEVEL);
597
598 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector);
599
600 /*
601 * Check the parameters
602 */
603 if (Vector >= NR_IRQS)
604 {
605 return(STATUS_INVALID_PARAMETER);
606 }
607 if (FloatingSave == TRUE)
608 {
609 return(STATUS_INVALID_PARAMETER);
610 }
611
612 /*
613 * Initialize interrupt object
614 */
615 Interrupt=ExAllocatePoolWithTag(NonPagedPool,sizeof(KINTERRUPT),
616 TAG_KINTERRUPT);
617 if (Interrupt==NULL)
618 {
619 return(STATUS_INSUFFICIENT_RESOURCES);
620 }
621
622 Status = KeInitializeInterrupt(Interrupt,
623 ServiceRoutine,
624 ServiceContext,
625 SpinLock,
626 Vector,
627 Irql,
628 SynchronizeIrql,
629 InterruptMode,
630 ShareVector,
631 ProcessorEnableMask,
632 FloatingSave);
633 if (!NT_SUCCESS(Status))
634 {
635 ExFreePool(Interrupt);
636 return Status;
637 }
638
639 Status = KeConnectInterrupt(Interrupt);
640 if (!NT_SUCCESS(Status))
641 {
642 ExFreePool(Interrupt);
643 return Status;
644 }
645
646 *InterruptObject = Interrupt;
647
648 return(STATUS_SUCCESS);
649 }
650
651
652 VOID STDCALL
653 IoDisconnectInterrupt(PKINTERRUPT InterruptObject)
654 /*
655 * FUNCTION: Releases a drivers isr
656 * ARGUMENTS:
657 * InterruptObject = isr to release
658 */
659 {
660 KeDisconnectInterrupt(InterruptObject);
661 ExFreePool(InterruptObject);
662 }
663
664 /* EOF */