Merge 15329:15546 from trunk
[reactos.git] / reactos / ntoskrnl / ke / i386 / irq.c
1 /* $Id$
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 *
8 * PROGRAMMERS: David Welch (welch@mcmail.com)
9 * Hartmut Birr
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 <ntoskrnl.h>
22 #include <../hal/halx86/include/halirq.h>
23
24 #define NDEBUG
25 #include <internal/debug.h>
26
27 /* GLOBALS *****************************************************************/
28
29 #ifdef CONFIG_SMP
30
31 #define __STR(x) #x
32 #define STR(x) __STR(x)
33
34 #define INT_NAME(intnum) _KiUnexpectedInterrupt##intnum
35 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
36
37 #define BUILD_COMMON_INTERRUPT_HANDLER() \
38 __asm__( \
39 "_KiCommonInterrupt:\n\t" \
40 "cld\n\t" \
41 "pushl %ds\n\t" \
42 "pushl %es\n\t" \
43 "pushl %fs\n\t" \
44 "pushl %gs\n\t" \
45 "movl $0xceafbeef,%eax\n\t" \
46 "pushl %eax\n\t" \
47 "movl $" STR(KERNEL_DS) ",%eax\n\t" \
48 "movl %eax,%ds\n\t" \
49 "movl %eax,%es\n\t" \
50 "movl %eax,%gs\n\t" \
51 "movl $" STR(PCR_SELECTOR) ",%eax\n\t" \
52 "movl %eax,%fs\n\t" \
53 "pushl %esp\n\t" \
54 "pushl %ebx\n\t" \
55 "call _KiInterruptDispatch\n\t" \
56 "addl $0xC, %esp\n\t" \
57 "popl %gs\n\t" \
58 "popl %fs\n\t" \
59 "popl %es\n\t" \
60 "popl %ds\n\t" \
61 "popa\n\t" \
62 "iret\n\t");
63
64 #define BUILD_INTERRUPT_HANDLER(intnum) \
65 VOID INT_NAME2(intnum)(VOID); \
66 __asm__( \
67 STR(INT_NAME(intnum)) ":\n\t" \
68 "pusha\n\t" \
69 "movl $0x" STR(intnum) ",%ebx\n\t" \
70 "jmp _KiCommonInterrupt");
71
72
73 /* Interrupt handlers and declarations */
74
75 #define B(x,y) \
76 BUILD_INTERRUPT_HANDLER(x##y)
77
78 #define B16(x) \
79 B(x,0) B(x,1) B(x,2) B(x,3) \
80 B(x,4) B(x,5) B(x,6) B(x,7) \
81 B(x,8) B(x,9) B(x,A) B(x,B) \
82 B(x,C) B(x,D) B(x,E) B(x,F)
83
84
85 BUILD_COMMON_INTERRUPT_HANDLER()
86 B16(3) B16(4) B16(5) B16(6)
87 B16(7) B16(8) B16(9) B16(A)
88 B16(B) B16(C) B16(D) B16(E)
89 B16(F)
90
91 #undef B
92 #undef B16
93
94
95 /* Interrupt handler list */
96
97 #define L(x,y) \
98 (ULONG)& INT_NAME2(x##y)
99
100 #define L16(x) \
101 L(x,0), L(x,1), L(x,2), L(x,3), \
102 L(x,4), L(x,5), L(x,6), L(x,7), \
103 L(x,8), L(x,9), L(x,A), L(x,B), \
104 L(x,C), L(x,D), L(x,E), L(x,F)
105
106 static ULONG irq_handler[ROUND_UP(NR_IRQS, 16)] = {
107 L16(3), L16(4), L16(5), L16(6),
108 L16(7), L16(8), L16(9), L16(A),
109 L16(B), L16(C), L16(D), L16(E)
110 };
111
112 #undef L
113 #undef L16
114
115 #else /* CONFIG_SMP */
116
117 void irq_handler_0(void);
118 void irq_handler_1(void);
119 void irq_handler_2(void);
120 void irq_handler_3(void);
121 void irq_handler_4(void);
122 void irq_handler_5(void);
123 void irq_handler_6(void);
124 void irq_handler_7(void);
125 void irq_handler_8(void);
126 void irq_handler_9(void);
127 void irq_handler_10(void);
128 void irq_handler_11(void);
129 void irq_handler_12(void);
130 void irq_handler_13(void);
131 void irq_handler_14(void);
132 void irq_handler_15(void);
133
134 static unsigned int irq_handler[NR_IRQS]=
135 {
136 (int)&irq_handler_0,
137 (int)&irq_handler_1,
138 (int)&irq_handler_2,
139 (int)&irq_handler_3,
140 (int)&irq_handler_4,
141 (int)&irq_handler_5,
142 (int)&irq_handler_6,
143 (int)&irq_handler_7,
144 (int)&irq_handler_8,
145 (int)&irq_handler_9,
146 (int)&irq_handler_10,
147 (int)&irq_handler_11,
148 (int)&irq_handler_12,
149 (int)&irq_handler_13,
150 (int)&irq_handler_14,
151 (int)&irq_handler_15,
152 };
153
154 #endif /* CONFIG_SMP */
155
156 /*
157 * PURPOSE: Object describing each isr
158 * NOTE: The data in this table is only modified at passsive level but can
159 * be accessed at any irq level.
160 */
161
162 typedef struct
163 {
164 LIST_ENTRY ListHead;
165 KSPIN_LOCK Lock;
166 ULONG Count;
167 }
168 ISR_TABLE, *PISR_TABLE;
169
170 #ifdef CONFIG_SMP
171 static ISR_TABLE IsrTable[NR_IRQS][MAXIMUM_PROCESSORS];
172 #else
173 static ISR_TABLE IsrTable[NR_IRQS][1];
174 #endif
175
176 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
177
178 /* FUNCTIONS ****************************************************************/
179
180 #define PRESENT (0x8000)
181 #define I486_INTERRUPT_GATE (0xe00)
182
183 VOID INIT_FUNCTION
184 KeInitInterrupts (VOID)
185 {
186 int i, j;
187
188
189 /*
190 * Setup the IDT entries to point to the interrupt handlers
191 */
192 for (i=0;i<NR_IRQS;i++)
193 {
194 KiIdt[IRQ_BASE+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
195 KiIdt[IRQ_BASE+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
196 I486_INTERRUPT_GATE;
197 #ifdef CONFIG_SMP
198 for (j = 0; j < MAXIMUM_PROCESSORS; j++)
199 #else
200 j = 0;
201 #endif
202 {
203 InitializeListHead(&IsrTable[i][j].ListHead);
204 KeInitializeSpinLock(&IsrTable[i][j].Lock);
205 IsrTable[i][j].Count = 0;
206 }
207 }
208 }
209
210 STATIC VOID
211 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame,
212 PKTRAP_FRAME TrapFrame)
213 {
214 TrapFrame->Gs = (USHORT)IrqTrapFrame->Gs;
215 TrapFrame->Fs = (USHORT)IrqTrapFrame->Fs;
216 TrapFrame->Es = (USHORT)IrqTrapFrame->Es;
217 TrapFrame->Ds = (USHORT)IrqTrapFrame->Ds;
218 TrapFrame->Eax = IrqTrapFrame->Eax;
219 TrapFrame->Ecx = IrqTrapFrame->Ecx;
220 TrapFrame->Edx = IrqTrapFrame->Edx;
221 TrapFrame->Ebx = IrqTrapFrame->Ebx;
222 TrapFrame->Esp = IrqTrapFrame->Esp;
223 TrapFrame->Ebp = IrqTrapFrame->Ebp;
224 TrapFrame->Esi = IrqTrapFrame->Esi;
225 TrapFrame->Edi = IrqTrapFrame->Edi;
226 TrapFrame->Eip = IrqTrapFrame->Eip;
227 TrapFrame->Cs = IrqTrapFrame->Cs;
228 TrapFrame->Eflags = IrqTrapFrame->Eflags;
229 }
230
231 STATIC VOID
232 KeTrapFrameToIRQTrapFrame(PKTRAP_FRAME TrapFrame,
233 PKIRQ_TRAPFRAME IrqTrapFrame)
234 {
235 IrqTrapFrame->Gs = TrapFrame->Gs;
236 IrqTrapFrame->Fs = TrapFrame->Fs;
237 IrqTrapFrame->Es = TrapFrame->Es;
238 IrqTrapFrame->Ds = TrapFrame->Ds;
239 IrqTrapFrame->Eax = TrapFrame->Eax;
240 IrqTrapFrame->Ecx = TrapFrame->Ecx;
241 IrqTrapFrame->Edx = TrapFrame->Edx;
242 IrqTrapFrame->Ebx = TrapFrame->Ebx;
243 IrqTrapFrame->Esp = TrapFrame->Esp;
244 IrqTrapFrame->Ebp = TrapFrame->Ebp;
245 IrqTrapFrame->Esi = TrapFrame->Esi;
246 IrqTrapFrame->Edi = TrapFrame->Edi;
247 IrqTrapFrame->Eip = TrapFrame->Eip;
248 IrqTrapFrame->Cs = TrapFrame->Cs;
249 IrqTrapFrame->Eflags = TrapFrame->Eflags;
250 }
251
252 VOID STDCALL
253 KiInterruptDispatch2 (ULONG vector, KIRQL old_level)
254 /*
255 * FUNCTION: Calls all the interrupt handlers for a given irq.
256 * ARGUMENTS:
257 * vector - The number of the vector to call handlers for.
258 * old_level - The irql of the processor when the irq took place.
259 * NOTES: Must be called at DIRQL.
260 */
261 {
262 PKINTERRUPT isr;
263 PLIST_ENTRY current;
264 KIRQL oldlvl;
265 PISR_TABLE CurrentIsr;
266
267 DPRINT("I(0x%.08x, 0x%.08x)\n", vector, old_level);
268
269 /*
270 * Iterate the list until one of the isr tells us its device interrupted
271 */
272 CurrentIsr = &IsrTable[vector - IRQ_BASE][(ULONG)KeGetCurrentProcessorNumber()];
273
274 KiAcquireSpinLock(&CurrentIsr->Lock);
275
276 CurrentIsr->Count++;
277 current = CurrentIsr->ListHead.Flink;
278
279 while (current != &CurrentIsr->ListHead)
280 {
281 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
282 oldlvl = KeAcquireInterruptSpinLock(isr);
283 if (isr->ServiceRoutine(isr, isr->ServiceContext))
284 {
285 KeReleaseInterruptSpinLock(isr, oldlvl);
286 break;
287 }
288 KeReleaseInterruptSpinLock(isr, oldlvl);
289 current = current->Flink;
290 }
291 KiReleaseSpinLock(&CurrentIsr->Lock);
292 }
293
294 VOID
295 KiInterruptDispatch (ULONG vector, PKIRQ_TRAPFRAME Trapframe)
296 /*
297 * FUNCTION: Calls the irq specific handler for an irq
298 * ARGUMENTS:
299 * irq = IRQ that has interrupted
300 */
301 {
302 KIRQL old_level;
303 KTRAP_FRAME KernelTrapFrame;
304 PKTHREAD CurrentThread;
305 PKTRAP_FRAME OldTrapFrame=NULL;
306
307 /*
308 * At this point we have interrupts disabled, nothing has been done to
309 * the PIC.
310 */
311
312 KeGetCurrentPrcb()->InterruptCount++;
313
314 /*
315 * Notify the rest of the kernel of the raised irq level. For the
316 * default HAL this will send an EOI to the PIC and alter the IRQL.
317 */
318 if (!HalBeginSystemInterrupt (vector,
319 VECTOR2IRQL(vector),
320 &old_level))
321 {
322 return;
323 }
324
325
326 /*
327 * Enable interrupts
328 * NOTE: Only higher priority interrupts will get through
329 */
330 Ke386EnableInterrupts();
331
332 #ifndef CONFIG_SMP
333 if (VECTOR2IRQ(vector) == 0)
334 {
335 KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
336 KeUpdateSystemTime(&KernelTrapFrame, old_level);
337 }
338 else
339 #endif
340 {
341 /*
342 * Actually call the ISR.
343 */
344 KiInterruptDispatch2(vector, old_level);
345 }
346
347 /*
348 * End the system interrupt.
349 */
350 Ke386DisableInterrupts();
351
352 HalEndSystemInterrupt (old_level, 0);
353
354 if (old_level==PASSIVE_LEVEL && Trapframe->Cs != KERNEL_CS)
355 {
356 CurrentThread = KeGetCurrentThread();
357 if (CurrentThread!=NULL && CurrentThread->Alerted[1])
358 {
359 DPRINT("PID: %d, TID: %d CS %04x/%04x\n",
360 ((PETHREAD)CurrentThread)->ThreadsProcess->UniqueProcessId,
361 ((PETHREAD)CurrentThread)->Cid.UniqueThread,
362 Trapframe->Cs,
363 CurrentThread->TrapFrame ? CurrentThread->TrapFrame->Cs : 0);
364 if (CurrentThread->TrapFrame == NULL)
365 {
366 OldTrapFrame = CurrentThread->TrapFrame;
367 KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
368 CurrentThread->TrapFrame = &KernelTrapFrame;
369 }
370
371 Ke386EnableInterrupts();
372 KiDeliverApc(KernelMode, NULL, NULL);
373 Ke386DisableInterrupts();
374
375 ASSERT(KeGetCurrentThread() == CurrentThread);
376 if (CurrentThread->TrapFrame == &KernelTrapFrame)
377 {
378 KeTrapFrameToIRQTrapFrame(&KernelTrapFrame, Trapframe);
379 CurrentThread->TrapFrame = OldTrapFrame;
380 }
381 }
382 }
383 }
384
385 static VOID
386 KeDumpIrqList(VOID)
387 {
388 PKINTERRUPT current;
389 PLIST_ENTRY current_entry;
390 ULONG i, j;
391 KIRQL oldlvl;
392 BOOLEAN printed;
393
394 for (i=0;i<NR_IRQS;i++)
395 {
396 printed = FALSE;
397 KeRaiseIrql(VECTOR2IRQL(i + IRQ_BASE),&oldlvl);
398
399 for (j=0; j < KeNumberProcessors; j++)
400 {
401 KiAcquireSpinLock(&IsrTable[i][j].Lock);
402
403 current_entry = IsrTable[i][j].ListHead.Flink;
404 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
405 while (current_entry!=&(IsrTable[i][j].ListHead))
406 {
407 if (printed == FALSE)
408 {
409 printed = TRUE;
410 DPRINT("For irq %x:\n",i);
411 }
412 DPRINT(" Isr %x\n",current);
413 current_entry = current_entry->Flink;
414 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
415 }
416 KiReleaseSpinLock(&IsrTable[i][j].Lock);
417 }
418 KeLowerIrql(oldlvl);
419 }
420 }
421
422 /*
423 * @implemented
424 */
425 BOOLEAN STDCALL
426 KeConnectInterrupt(PKINTERRUPT InterruptObject)
427 {
428 KIRQL oldlvl,synch_oldlvl;
429 PKINTERRUPT ListHead;
430 ULONG Vector;
431 PISR_TABLE CurrentIsr;
432 BOOLEAN Result;
433
434 DPRINT("KeConnectInterrupt()\n");
435
436 Vector = InterruptObject->Vector;
437
438 if (Vector < IRQ_BASE || Vector >= IRQ_BASE + NR_IRQS)
439 return FALSE;
440
441 Vector -= IRQ_BASE;
442
443 ASSERT (InterruptObject->ProcessorNumber < KeNumberProcessors);
444
445 KeSetSystemAffinityThread(1 << InterruptObject->ProcessorNumber);
446
447 CurrentIsr = &IsrTable[Vector][(ULONG)InterruptObject->ProcessorNumber];
448
449 KeRaiseIrql(VECTOR2IRQL(Vector + IRQ_BASE),&oldlvl);
450 KiAcquireSpinLock(&CurrentIsr->Lock);
451
452 /*
453 * Check if the vector is already in use that we can share it
454 */
455 if (!IsListEmpty(&CurrentIsr->ListHead))
456 {
457 ListHead = CONTAINING_RECORD(CurrentIsr->ListHead.Flink,KINTERRUPT,Entry);
458 if (InterruptObject->Shareable == FALSE || ListHead->Shareable==FALSE)
459 {
460 KiReleaseSpinLock(&CurrentIsr->Lock);
461 KeLowerIrql(oldlvl);
462 KeRevertToUserAffinityThread();
463 return FALSE;
464 }
465 }
466
467 synch_oldlvl = KeAcquireInterruptSpinLock(InterruptObject);
468
469 DPRINT("%x %x\n",CurrentIsr->ListHead.Flink, CurrentIsr->ListHead.Blink);
470
471 Result = HalEnableSystemInterrupt(Vector + IRQ_BASE, InterruptObject->Irql, InterruptObject->InterruptMode);
472 if (Result)
473 {
474 InsertTailList(&CurrentIsr->ListHead,&InterruptObject->Entry);
475 DPRINT("%x %x\n",InterruptObject->Entry.Flink, InterruptObject->Entry.Blink);
476 }
477
478 KeReleaseInterruptSpinLock(InterruptObject, synch_oldlvl);
479
480 /*
481 * Release the table spinlock
482 */
483 KiReleaseSpinLock(&CurrentIsr->Lock);
484 KeLowerIrql(oldlvl);
485
486 KeDumpIrqList();
487
488 KeRevertToUserAffinityThread();
489
490 return Result;
491 }
492
493
494 /*
495 * @implemented
496 */
497 VOID STDCALL
498 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
499 /*
500 * FUNCTION: Releases a drivers isr
501 * ARGUMENTS:
502 * InterruptObject = isr to release
503 */
504 {
505 KIRQL oldlvl,synch_oldlvl;
506 PISR_TABLE CurrentIsr;
507
508 DPRINT("KeDisconnectInterrupt\n");
509
510 ASSERT (InterruptObject->ProcessorNumber < KeNumberProcessors);
511
512 KeSetSystemAffinityThread(1 << InterruptObject->ProcessorNumber);
513
514 CurrentIsr = &IsrTable[InterruptObject->Vector - IRQ_BASE][(ULONG)InterruptObject->ProcessorNumber];
515
516 KeRaiseIrql(VECTOR2IRQL(InterruptObject->Vector),&oldlvl);
517 KiAcquireSpinLock(&CurrentIsr->Lock);
518
519 synch_oldlvl = KeAcquireInterruptSpinLock(InterruptObject);
520
521 RemoveEntryList(&InterruptObject->Entry);
522 if (IsListEmpty(&CurrentIsr->ListHead))
523 {
524 HalDisableSystemInterrupt(InterruptObject->Vector, 0);
525 }
526 KeReleaseInterruptSpinLock(InterruptObject, synch_oldlvl);
527
528 /*
529 * Release the table spinlock
530 */
531 KiReleaseSpinLock(&CurrentIsr->Lock);
532 KeLowerIrql(oldlvl);
533
534 KeRevertToUserAffinityThread();
535 }
536
537
538 /*
539 * @implemented
540 */
541 VOID
542 STDCALL
543 KeInitializeInterrupt(PKINTERRUPT InterruptObject,
544 PKSERVICE_ROUTINE ServiceRoutine,
545 PVOID ServiceContext,
546 PKSPIN_LOCK SpinLock,
547 ULONG Vector,
548 KIRQL Irql,
549 KIRQL SynchronizeIrql,
550 KINTERRUPT_MODE InterruptMode,
551 BOOLEAN ShareVector,
552 CHAR ProcessorNumber,
553 BOOLEAN FloatingSave)
554 {
555 InterruptObject->ServiceRoutine = ServiceRoutine;
556 InterruptObject->ServiceContext = ServiceContext;
557 InterruptObject->ActualLock = SpinLock;
558 InterruptObject->Vector = Vector;
559 InterruptObject->Irql = Irql;
560 InterruptObject->SynchLevel = SynchronizeIrql;
561 InterruptObject->InterruptMode = InterruptMode;
562 InterruptObject->Shareable = ShareVector;
563 InterruptObject->ProcessorNumber = ProcessorNumber;
564 InterruptObject->FloatingSave = FloatingSave;
565 }
566
567 VOID KePrintInterruptStatistic(VOID)
568 {
569 ULONG i, j;
570
571 for (j = 0; j < KeNumberProcessors; j++)
572 {
573 DPRINT1("CPU%d:\n", j);
574 for (i = 0; i < NR_IRQS; i++)
575 {
576 if (IsrTable[i][j].Count)
577 {
578 DPRINT1(" Irq %x(%d): %d\n", i, VECTOR2IRQ(i + IRQ_BASE), IsrTable[i][j].Count);
579 }
580 }
581 }
582 }
583
584
585 /* EOF */