- Convert pure inline assembler .c files to .S files.
[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 /* 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
135 /* FUNCTIONS ****************************************************************/
136
137 #define PRESENT (0x8000)
138 #define I486_INTERRUPT_GATE (0xe00)
139
140 VOID INIT_FUNCTION
141 KeInitInterrupts (VOID)
142 {
143 int i, j;
144
145
146 /*
147 * Setup the IDT entries to point to the interrupt handlers
148 */
149 for (i=0;i<NR_IRQS;i++)
150 {
151 KiIdt[IRQ_BASE+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
152 KiIdt[IRQ_BASE+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
153 I486_INTERRUPT_GATE;
154 #ifdef CONFIG_SMP
155 for (j = 0; j < MAXIMUM_PROCESSORS; j++)
156 #else
157 j = 0;
158 #endif
159 {
160 InitializeListHead(&IsrTable[i][j].ListHead);
161 KeInitializeSpinLock(&IsrTable[i][j].Lock);
162 IsrTable[i][j].Count = 0;
163 }
164 }
165 }
166
167 STATIC VOID
168 KeIRQTrapFrameToTrapFrame(PKIRQ_TRAPFRAME IrqTrapFrame,
169 PKTRAP_FRAME TrapFrame)
170 {
171 TrapFrame->Gs = (USHORT)IrqTrapFrame->Gs;
172 TrapFrame->Fs = (USHORT)IrqTrapFrame->Fs;
173 TrapFrame->Es = (USHORT)IrqTrapFrame->Es;
174 TrapFrame->Ds = (USHORT)IrqTrapFrame->Ds;
175 TrapFrame->Eax = IrqTrapFrame->Eax;
176 TrapFrame->Ecx = IrqTrapFrame->Ecx;
177 TrapFrame->Edx = IrqTrapFrame->Edx;
178 TrapFrame->Ebx = IrqTrapFrame->Ebx;
179 TrapFrame->Esp = IrqTrapFrame->Esp;
180 TrapFrame->Ebp = IrqTrapFrame->Ebp;
181 TrapFrame->Esi = IrqTrapFrame->Esi;
182 TrapFrame->Edi = IrqTrapFrame->Edi;
183 TrapFrame->Eip = IrqTrapFrame->Eip;
184 TrapFrame->Cs = IrqTrapFrame->Cs;
185 TrapFrame->Eflags = IrqTrapFrame->Eflags;
186 }
187
188 STATIC VOID
189 KeTrapFrameToIRQTrapFrame(PKTRAP_FRAME TrapFrame,
190 PKIRQ_TRAPFRAME IrqTrapFrame)
191 {
192 IrqTrapFrame->Gs = TrapFrame->Gs;
193 IrqTrapFrame->Fs = TrapFrame->Fs;
194 IrqTrapFrame->Es = TrapFrame->Es;
195 IrqTrapFrame->Ds = TrapFrame->Ds;
196 IrqTrapFrame->Eax = TrapFrame->Eax;
197 IrqTrapFrame->Ecx = TrapFrame->Ecx;
198 IrqTrapFrame->Edx = TrapFrame->Edx;
199 IrqTrapFrame->Ebx = TrapFrame->Ebx;
200 IrqTrapFrame->Esp = TrapFrame->Esp;
201 IrqTrapFrame->Ebp = TrapFrame->Ebp;
202 IrqTrapFrame->Esi = TrapFrame->Esi;
203 IrqTrapFrame->Edi = TrapFrame->Edi;
204 IrqTrapFrame->Eip = TrapFrame->Eip;
205 IrqTrapFrame->Cs = TrapFrame->Cs;
206 IrqTrapFrame->Eflags = TrapFrame->Eflags;
207 }
208
209 VOID STDCALL
210 KiInterruptDispatch2 (ULONG vector, KIRQL old_level)
211 /*
212 * FUNCTION: Calls all the interrupt handlers for a given irq.
213 * ARGUMENTS:
214 * vector - The number of the vector to call handlers for.
215 * old_level - The irql of the processor when the irq took place.
216 * NOTES: Must be called at DIRQL.
217 */
218 {
219 PKINTERRUPT isr;
220 PLIST_ENTRY current;
221 KIRQL oldlvl;
222 PISR_TABLE CurrentIsr;
223
224 DPRINT("I(0x%.08x, 0x%.08x)\n", vector, old_level);
225
226 /*
227 * Iterate the list until one of the isr tells us its device interrupted
228 */
229 CurrentIsr = &IsrTable[vector - IRQ_BASE][(ULONG)KeGetCurrentProcessorNumber()];
230
231 KiAcquireSpinLock(&CurrentIsr->Lock);
232
233 CurrentIsr->Count++;
234 current = CurrentIsr->ListHead.Flink;
235
236 while (current != &CurrentIsr->ListHead)
237 {
238 isr = CONTAINING_RECORD(current,KINTERRUPT,InterruptListEntry);
239 oldlvl = KeAcquireInterruptSpinLock(isr);
240 if (isr->ServiceRoutine(isr, isr->ServiceContext))
241 {
242 KeReleaseInterruptSpinLock(isr, oldlvl);
243 break;
244 }
245 KeReleaseInterruptSpinLock(isr, oldlvl);
246 current = current->Flink;
247 }
248 KiReleaseSpinLock(&CurrentIsr->Lock);
249 }
250
251 VOID
252 KiInterruptDispatch (ULONG vector, PKIRQ_TRAPFRAME Trapframe)
253 /*
254 * FUNCTION: Calls the irq specific handler for an irq
255 * ARGUMENTS:
256 * irq = IRQ that has interrupted
257 */
258 {
259 KIRQL old_level;
260 KTRAP_FRAME KernelTrapFrame;
261 PKTHREAD CurrentThread;
262 PKTRAP_FRAME OldTrapFrame=NULL;
263
264 /*
265 * At this point we have interrupts disabled, nothing has been done to
266 * the PIC.
267 */
268
269 KeGetCurrentPrcb()->InterruptCount++;
270
271 /*
272 * Notify the rest of the kernel of the raised irq level. For the
273 * default HAL this will send an EOI to the PIC and alter the IRQL.
274 */
275 if (!HalBeginSystemInterrupt (vector,
276 VECTOR2IRQL(vector),
277 &old_level))
278 {
279 return;
280 }
281
282
283 /*
284 * Enable interrupts
285 * NOTE: Only higher priority interrupts will get through
286 */
287 Ke386EnableInterrupts();
288
289 #ifndef CONFIG_SMP
290 if (VECTOR2IRQ(vector) == 0)
291 {
292 KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
293 KeUpdateSystemTime(&KernelTrapFrame, old_level);
294 }
295 else
296 #endif
297 {
298 /*
299 * Actually call the ISR.
300 */
301 KiInterruptDispatch2(vector, old_level);
302 }
303
304 /*
305 * End the system interrupt.
306 */
307 Ke386DisableInterrupts();
308
309 HalEndSystemInterrupt (old_level, 0);
310
311 if (old_level==PASSIVE_LEVEL && Trapframe->Cs != KERNEL_CS)
312 {
313 CurrentThread = KeGetCurrentThread();
314 if (CurrentThread!=NULL && CurrentThread->Alerted[1])
315 {
316 DPRINT("PID: %d, TID: %d CS %04x/%04x\n",
317 ((PETHREAD)CurrentThread)->ThreadsProcess->UniqueProcessId,
318 ((PETHREAD)CurrentThread)->Cid.UniqueThread,
319 Trapframe->Cs,
320 CurrentThread->TrapFrame ? CurrentThread->TrapFrame->Cs : 0);
321 if (CurrentThread->TrapFrame == NULL)
322 {
323 OldTrapFrame = CurrentThread->TrapFrame;
324 KeIRQTrapFrameToTrapFrame(Trapframe, &KernelTrapFrame);
325 CurrentThread->TrapFrame = &KernelTrapFrame;
326 }
327
328 Ke386EnableInterrupts();
329 KiDeliverApc(KernelMode, NULL, NULL);
330 Ke386DisableInterrupts();
331
332 ASSERT(KeGetCurrentThread() == CurrentThread);
333 if (CurrentThread->TrapFrame == &KernelTrapFrame)
334 {
335 KeTrapFrameToIRQTrapFrame(&KernelTrapFrame, Trapframe);
336 CurrentThread->TrapFrame = OldTrapFrame;
337 }
338 }
339 }
340 }
341
342 static VOID
343 KeDumpIrqList(VOID)
344 {
345 PKINTERRUPT current;
346 PLIST_ENTRY current_entry;
347 ULONG i, j;
348 KIRQL oldlvl;
349 BOOLEAN printed;
350
351 for (i=0;i<NR_IRQS;i++)
352 {
353 printed = FALSE;
354 KeRaiseIrql(VECTOR2IRQL(i + IRQ_BASE),&oldlvl);
355
356 for (j=0; j < KeNumberProcessors; j++)
357 {
358 KiAcquireSpinLock(&IsrTable[i][j].Lock);
359
360 current_entry = IsrTable[i][j].ListHead.Flink;
361 current = CONTAINING_RECORD(current_entry,KINTERRUPT,InterruptListEntry);
362 while (current_entry!=&(IsrTable[i][j].ListHead))
363 {
364 if (printed == FALSE)
365 {
366 printed = TRUE;
367 DPRINT("For irq %x:\n",i);
368 }
369 DPRINT(" Isr %x\n",current);
370 current_entry = current_entry->Flink;
371 current = CONTAINING_RECORD(current_entry,KINTERRUPT,InterruptListEntry);
372 }
373 KiReleaseSpinLock(&IsrTable[i][j].Lock);
374 }
375 KeLowerIrql(oldlvl);
376 }
377 }
378
379 /*
380 * @implemented
381 */
382 BOOLEAN
383 STDCALL
384 KeConnectInterrupt(PKINTERRUPT InterruptObject)
385 {
386 KIRQL oldlvl,synch_oldlvl;
387 PKINTERRUPT ListHead;
388 ULONG Vector;
389 PISR_TABLE CurrentIsr;
390 BOOLEAN Result;
391
392 DPRINT("KeConnectInterrupt()\n");
393
394 Vector = InterruptObject->Vector;
395
396 if (Vector < IRQ_BASE || Vector >= IRQ_BASE + NR_IRQS)
397 return FALSE;
398
399 Vector -= IRQ_BASE;
400
401 ASSERT (InterruptObject->Number < KeNumberProcessors);
402
403 KeSetSystemAffinityThread(1 << InterruptObject->Number);
404
405 CurrentIsr = &IsrTable[Vector][(ULONG)InterruptObject->Number];
406
407 KeRaiseIrql(VECTOR2IRQL(Vector + IRQ_BASE),&oldlvl);
408 KiAcquireSpinLock(&CurrentIsr->Lock);
409
410 /*
411 * Check if the vector is already in use that we can share it
412 */
413 if (!IsListEmpty(&CurrentIsr->ListHead))
414 {
415 ListHead = CONTAINING_RECORD(CurrentIsr->ListHead.Flink,KINTERRUPT,InterruptListEntry);
416 if (InterruptObject->ShareVector == FALSE || ListHead->ShareVector==FALSE)
417 {
418 KiReleaseSpinLock(&CurrentIsr->Lock);
419 KeLowerIrql(oldlvl);
420 KeRevertToUserAffinityThread();
421 return FALSE;
422 }
423 }
424
425 synch_oldlvl = KeAcquireInterruptSpinLock(InterruptObject);
426
427 DPRINT("%x %x\n",CurrentIsr->ListHead.Flink, CurrentIsr->ListHead.Blink);
428
429 Result = HalEnableSystemInterrupt(Vector + IRQ_BASE, InterruptObject->Irql, InterruptObject->Mode);
430 if (Result)
431 {
432 InsertTailList(&CurrentIsr->ListHead,&InterruptObject->InterruptListEntry);
433 DPRINT("%x %x\n",InterruptObject->InterruptListEntry.Flink, InterruptObject->InterruptListEntry.Blink);
434 }
435
436 InterruptObject->Connected = TRUE;
437 KeReleaseInterruptSpinLock(InterruptObject, synch_oldlvl);
438
439 /*
440 * Release the table spinlock
441 */
442 KiReleaseSpinLock(&CurrentIsr->Lock);
443 KeLowerIrql(oldlvl);
444
445 KeDumpIrqList();
446
447 KeRevertToUserAffinityThread();
448
449 return Result;
450 }
451
452 /*
453 * @implemented
454 *
455 * FUNCTION: Releases a drivers isr
456 * ARGUMENTS:
457 * InterruptObject = isr to release
458 */
459 BOOLEAN
460 STDCALL
461 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
462 {
463 KIRQL oldlvl,synch_oldlvl;
464 PISR_TABLE CurrentIsr;
465 BOOLEAN State;
466
467 DPRINT1("KeDisconnectInterrupt\n");
468 ASSERT (InterruptObject->Number < KeNumberProcessors);
469
470 /* Set the affinity */
471 KeSetSystemAffinityThread(1 << InterruptObject->Number);
472
473 /* Get the ISR Tabe */
474 CurrentIsr = &IsrTable[InterruptObject->Vector - IRQ_BASE]
475 [(ULONG)InterruptObject->Number];
476
477 /* Raise IRQL to required level and lock table */
478 KeRaiseIrql(VECTOR2IRQL(InterruptObject->Vector),&oldlvl);
479 KiAcquireSpinLock(&CurrentIsr->Lock);
480
481 /* Check if it's actually connected */
482 if ((State = InterruptObject->Connected))
483 {
484 /* Lock the Interrupt */
485 synch_oldlvl = KeAcquireInterruptSpinLock(InterruptObject);
486
487 /* Remove this one, and check if all are gone */
488 RemoveEntryList(&InterruptObject->InterruptListEntry);
489 if (IsListEmpty(&CurrentIsr->ListHead))
490 {
491 /* Completely Disable the Interrupt */
492 HalDisableSystemInterrupt(InterruptObject->Vector, InterruptObject->Irql);
493 }
494
495 /* Disconnect it */
496 InterruptObject->Connected = FALSE;
497
498 /* Release the interrupt lock */
499 KeReleaseInterruptSpinLock(InterruptObject, synch_oldlvl);
500 }
501 /* Release the table spinlock */
502 KiReleaseSpinLock(&CurrentIsr->Lock);
503 KeLowerIrql(oldlvl);
504
505 /* Go back to default affinity */
506 KeRevertToUserAffinityThread();
507
508 /* Return Old Interrupt State */
509 return State;
510 }
511
512 /*
513 * @implemented
514 */
515 VOID
516 STDCALL
517 KeInitializeInterrupt(PKINTERRUPT Interrupt,
518 PKSERVICE_ROUTINE ServiceRoutine,
519 PVOID ServiceContext,
520 PKSPIN_LOCK SpinLock,
521 ULONG Vector,
522 KIRQL Irql,
523 KIRQL SynchronizeIrql,
524 KINTERRUPT_MODE InterruptMode,
525 BOOLEAN ShareVector,
526 CHAR ProcessorNumber,
527 BOOLEAN FloatingSave)
528 {
529 /* Set the Interrupt Header */
530 Interrupt->Type = InterruptObject;
531 Interrupt->Size = sizeof(KINTERRUPT);
532
533 /* Check if we got a spinlock */
534 if (SpinLock)
535 {
536 Interrupt->ActualLock = SpinLock;
537 }
538 else
539 {
540 /* This means we'll be usin the built-in one */
541 KeInitializeSpinLock(&Interrupt->SpinLock);
542 Interrupt->ActualLock = &Interrupt->SpinLock;
543 }
544
545 /* Set the other settings */
546 Interrupt->ServiceRoutine = ServiceRoutine;
547 Interrupt->ServiceContext = ServiceContext;
548 Interrupt->Vector = Vector;
549 Interrupt->Irql = Irql;
550 Interrupt->SynchronizeIrql = SynchronizeIrql;
551 Interrupt->Mode = InterruptMode;
552 Interrupt->ShareVector = ShareVector;
553 Interrupt->Number = ProcessorNumber;
554 Interrupt->FloatingSave = FloatingSave;
555
556 /* Disconnect it at first */
557 Interrupt->Connected = FALSE;
558 }
559
560 VOID KePrintInterruptStatistic(VOID)
561 {
562 ULONG i, j;
563
564 for (j = 0; j < KeNumberProcessors; j++)
565 {
566 DPRINT1("CPU%d:\n", j);
567 for (i = 0; i < NR_IRQS; i++)
568 {
569 if (IsrTable[i][j].Count)
570 {
571 DPRINT1(" Irq %x(%d): %d\n", i, VECTOR2IRQ(i + IRQ_BASE), IsrTable[i][j].Count);
572 }
573 }
574 }
575 }
576
577
578 /* EOF */