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