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