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