Small Debugging Services re-arrangement. /dbgk created for User-Mode debugging and...
[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 <internal/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 KeGetCurrentPrcb()->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 }
344 else
345 #endif
346 {
347 /*
348 * Actually call the ISR.
349 */
350 KiInterruptDispatch2(vector, old_level);
351 }
352
353 /*
354 * End the system interrupt.
355 */
356 Ke386DisableInterrupts();
357
358 HalEndSystemInterrupt (old_level, 0);
359
360 if (old_level==PASSIVE_LEVEL && Trapframe->Cs != KERNEL_CS)
361 {
362 CurrentThread = KeGetCurrentThread();
363 if (CurrentThread!=NULL && CurrentThread->Alerted[1])
364 {
365 DPRINT("PID: %d, TID: %d CS %04x/%04x\n",
366 ((PETHREAD)CurrentThread)->ThreadsProcess->UniqueProcessId,
367 ((PETHREAD)CurrentThread)->Cid.UniqueThread,
368 Trapframe->Cs,
369 CurrentThread->TrapFrame ? CurrentThread->TrapFrame->Cs : 0);
370 if (CurrentThread->TrapFrame == NULL)
371 {
372 OldTrapFrame = CurrentThread->TrapFrame;
373 KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
374 CurrentThread->TrapFrame = &KernelTrapFrame;
375 }
376
377 Ke386EnableInterrupts();
378 KiDeliverApc(KernelMode, NULL, NULL);
379 Ke386DisableInterrupts();
380
381 ASSERT(KeGetCurrentThread() == CurrentThread);
382 if (CurrentThread->TrapFrame == &KernelTrapFrame)
383 {
384 KeTrapFrameToIRQTrapFrame(&KernelTrapFrame, Trapframe);
385 CurrentThread->TrapFrame = OldTrapFrame;
386 }
387 }
388 }
389 }
390
391 static VOID
392 KeDumpIrqList(VOID)
393 {
394 PKINTERRUPT current;
395 PLIST_ENTRY current_entry;
396 ULONG i, j;
397 KIRQL oldlvl;
398 BOOLEAN printed;
399
400 for (i=0;i<NR_IRQS;i++)
401 {
402 printed = FALSE;
403 KeRaiseIrql(VECTOR2IRQL(i + IRQ_BASE),&oldlvl);
404
405 for (j=0; j < KeNumberProcessors; j++)
406 {
407 KiAcquireSpinLock(&IsrTable[i][j].Lock);
408
409 current_entry = IsrTable[i][j].ListHead.Flink;
410 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
411 while (current_entry!=&(IsrTable[i][j].ListHead))
412 {
413 if (printed == FALSE)
414 {
415 printed = TRUE;
416 DPRINT("For irq %x:\n",i);
417 }
418 DPRINT(" Isr %x\n",current);
419 current_entry = current_entry->Flink;
420 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
421 }
422 KiReleaseSpinLock(&IsrTable[i][j].Lock);
423 }
424 KeLowerIrql(oldlvl);
425 }
426 }
427
428 /*
429 * @implemented
430 */
431 BOOLEAN STDCALL
432 KeConnectInterrupt(PKINTERRUPT InterruptObject)
433 {
434 KIRQL oldlvl,synch_oldlvl;
435 PKINTERRUPT ListHead;
436 ULONG Vector;
437 PISR_TABLE CurrentIsr;
438 BOOLEAN Result;
439
440 DPRINT("KeConnectInterrupt()\n");
441
442 Vector = InterruptObject->Vector;
443
444 if (Vector < IRQ_BASE || Vector >= IRQ_BASE + NR_IRQS)
445 return FALSE;
446
447 Vector -= IRQ_BASE;
448
449 ASSERT (InterruptObject->ProcessorNumber < KeNumberProcessors);
450
451 KeSetSystemAffinityThread(1 << InterruptObject->ProcessorNumber);
452
453 CurrentIsr = &IsrTable[Vector][(ULONG)InterruptObject->ProcessorNumber];
454
455 KeRaiseIrql(VECTOR2IRQL(Vector + IRQ_BASE),&oldlvl);
456 KiAcquireSpinLock(&CurrentIsr->Lock);
457
458 /*
459 * Check if the vector is already in use that we can share it
460 */
461 if (!IsListEmpty(&CurrentIsr->ListHead))
462 {
463 ListHead = CONTAINING_RECORD(CurrentIsr->ListHead.Flink,KINTERRUPT,Entry);
464 if (InterruptObject->Shareable == FALSE || ListHead->Shareable==FALSE)
465 {
466 KiReleaseSpinLock(&CurrentIsr->Lock);
467 KeLowerIrql(oldlvl);
468 KeRevertToUserAffinityThread();
469 return FALSE;
470 }
471 }
472
473 synch_oldlvl = KeAcquireInterruptSpinLock(InterruptObject);
474
475 DPRINT("%x %x\n",CurrentIsr->ListHead.Flink, CurrentIsr->ListHead.Blink);
476
477 Result = HalEnableSystemInterrupt(Vector + IRQ_BASE, InterruptObject->Irql, InterruptObject->InterruptMode);
478 if (Result)
479 {
480 InsertTailList(&CurrentIsr->ListHead,&InterruptObject->Entry);
481 DPRINT("%x %x\n",InterruptObject->Entry.Flink, InterruptObject->Entry.Blink);
482 }
483
484 KeReleaseInterruptSpinLock(InterruptObject, synch_oldlvl);
485
486 /*
487 * Release the table spinlock
488 */
489 KiReleaseSpinLock(&CurrentIsr->Lock);
490 KeLowerIrql(oldlvl);
491
492 KeDumpIrqList();
493
494 KeRevertToUserAffinityThread();
495
496 return Result;
497 }
498
499
500 /*
501 * @implemented
502 */
503 VOID STDCALL
504 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
505 /*
506 * FUNCTION: Releases a drivers isr
507 * ARGUMENTS:
508 * InterruptObject = isr to release
509 */
510 {
511 KIRQL oldlvl,synch_oldlvl;
512 PISR_TABLE CurrentIsr;
513
514 DPRINT("KeDisconnectInterrupt\n");
515
516 ASSERT (InterruptObject->ProcessorNumber < KeNumberProcessors);
517
518 KeSetSystemAffinityThread(1 << InterruptObject->ProcessorNumber);
519
520 CurrentIsr = &IsrTable[InterruptObject->Vector - IRQ_BASE][(ULONG)InterruptObject->ProcessorNumber];
521
522 KeRaiseIrql(VECTOR2IRQL(InterruptObject->Vector),&oldlvl);
523 KiAcquireSpinLock(&CurrentIsr->Lock);
524
525 synch_oldlvl = KeAcquireInterruptSpinLock(InterruptObject);
526
527 RemoveEntryList(&InterruptObject->Entry);
528 if (IsListEmpty(&CurrentIsr->ListHead))
529 {
530 HalDisableSystemInterrupt(InterruptObject->Vector, 0);
531 }
532 KeReleaseInterruptSpinLock(InterruptObject, synch_oldlvl);
533
534 /*
535 * Release the table spinlock
536 */
537 KiReleaseSpinLock(&CurrentIsr->Lock);
538 KeLowerIrql(oldlvl);
539
540 KeRevertToUserAffinityThread();
541 }
542
543
544 /*
545 * @implemented
546 */
547 VOID
548 STDCALL
549 KeInitializeInterrupt(PKINTERRUPT InterruptObject,
550 PKSERVICE_ROUTINE ServiceRoutine,
551 PVOID ServiceContext,
552 PKSPIN_LOCK SpinLock,
553 ULONG Vector,
554 KIRQL Irql,
555 KIRQL SynchronizeIrql,
556 KINTERRUPT_MODE InterruptMode,
557 BOOLEAN ShareVector,
558 CHAR ProcessorNumber,
559 BOOLEAN FloatingSave)
560 {
561 InterruptObject->ServiceRoutine = ServiceRoutine;
562 InterruptObject->ServiceContext = ServiceContext;
563 InterruptObject->ActualLock = SpinLock;
564 InterruptObject->Vector = Vector;
565 InterruptObject->Irql = Irql;
566 InterruptObject->SynchLevel = SynchronizeIrql;
567 InterruptObject->InterruptMode = InterruptMode;
568 InterruptObject->Shareable = ShareVector;
569 InterruptObject->ProcessorNumber = ProcessorNumber;
570 InterruptObject->FloatingSave = FloatingSave;
571 }
572
573 VOID KePrintInterruptStatistic(VOID)
574 {
575 ULONG i, j;
576
577 for (j = 0; j < KeNumberProcessors; j++)
578 {
579 DPRINT1("CPU%d:\n", j);
580 for (i = 0; i < NR_IRQS; i++)
581 {
582 if (IsrTable[i][j].Count)
583 {
584 DPRINT1(" Irq %x(%d): %d\n", i, VECTOR2IRQ(i + IRQ_BASE), IsrTable[i][j].Count);
585 }
586 }
587 }
588 }
589
590
591 /* EOF */