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