Further SMP initialization work (now boots all processors on SMP bochs)
[reactos.git] / reactos / ntoskrnl / ke / i386 / irq.c
1 /* $Id: irq.c,v 1.11 2001/04/16 16:29:02 dwelch Exp $
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 * PROGRAMMER: David Welch (welch@mcmail.com)
8 * UPDATE HISTORY:
9 * 29/05/98: Created
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 <ddk/ntddk.h>
22 #include <internal/config.h>
23 #include <internal/ke.h>
24 #include <internal/ps.h>
25 #include <internal/i386/segment.h>
26 #include <internal/pool.h>
27
28 #ifdef MP
29 #include <internal/hal/mps.h>
30 #endif /* MP */
31
32 #define NDEBUG
33 #include <internal/debug.h>
34
35 /* GLOBALS *****************************************************************/
36
37 #ifdef MP
38
39 #define IRQ_BASE FIRST_DEVICE_VECTOR
40 #define NR_IRQS 0x100 - 0x30
41
42 #define __STR(x) #x
43 #define STR(x) __STR(x)
44
45 #define INT_NAME(intnum) _KiUnexpectedInterrupt##intnum
46 #define INT_NAME2(intnum) KiUnexpectedInterrupt##intnum
47
48 #define BUILD_COMMON_INTERRUPT_HANDLER() \
49 __asm__( \
50 "_KiCommonInterrupt:\n\t" \
51 "cld\n\t" \
52 "pushl %ds\n\t" \
53 "pushl %es\n\t" \
54 "pushl %fs\n\t" \
55 "pushl %gs\n\t" \
56 "movl $0xceafbeef,%eax\n\t" \
57 "pushl %eax\n\t" \
58 "movl $" STR(KERNEL_DS) ",%eax\n\t" \
59 "movl %eax,%ds\n\t" \
60 "movl %eax,%es\n\t" \
61 "movl $" STR(PCR_SELECTOR) ",%eax\n\t" \
62 "movl %eax,%fs\n\t" \
63 "pushl %esp\n\t" \
64 "pushl %ebx\n\t" \
65 "call _KiInterruptDispatch\n\t" \
66 "popl %eax\n\t" \
67 "popl %eax\n\t" \
68 "popl %eax\n\t" \
69 "popl %gs\n\t" \
70 "popl %fs\n\t" \
71 "popl %es\n\t" \
72 "popl %ds\n\t" \
73 "popa\n\t" \
74 "iret\n\t");
75
76 #define BUILD_INTERRUPT_HANDLER(intnum) \
77 VOID INT_NAME2(intnum)(VOID); \
78 __asm__( \
79 STR(INT_NAME(intnum)) ":\n\t" \
80 "pusha\n\t" \
81 "movl $0x" STR(intnum) ",%ebx\n\t" \
82 "jmp _KiCommonInterrupt");
83
84
85 /* Interrupt handlers and declarations */
86
87 #define B(x,y) \
88 BUILD_INTERRUPT_HANDLER(x##y)
89
90 #define B16(x) \
91 B(x,0) B(x,1) B(x,2) B(x,3) \
92 B(x,4) B(x,5) B(x,6) B(x,7) \
93 B(x,8) B(x,9) B(x,A) B(x,B) \
94 B(x,C) B(x,D) B(x,E) B(x,F)
95
96
97 BUILD_COMMON_INTERRUPT_HANDLER()
98 B16(3) B16(4) B16(5) B16(6)
99 B16(7) B16(8) B16(9) B16(A)
100 B16(B) B16(C) B16(D) B16(E)
101 B16(F)
102
103 #undef B
104 #undef B16
105
106
107 /* Interrupt handler list */
108
109 #define L(x,y) \
110 (ULONG)& INT_NAME2(x##y)
111
112 #define L16(x) \
113 L(x,0), L(x,1), L(x,2), L(x,3), \
114 L(x,4), L(x,5), L(x,6), L(x,7), \
115 L(x,8), L(x,9), L(x,A), L(x,B), \
116 L(x,C), L(x,D), L(x,E), L(x,F)
117
118 static ULONG irq_handler[NR_IRQS] = {
119 L16(3), L16(4), L16(5), L16(6),
120 L16(7), L16(8), L16(9), L16(A),
121 L16(B), L16(C), L16(D), L16(E),
122 L16(F)
123 };
124
125 #undef L
126 #undef L16
127
128 #else /* MP */
129
130 #define NR_IRQS (16)
131 #define IRQ_BASE (0x40)
132
133 void irq_handler_0(void);
134 void irq_handler_1(void);
135 void irq_handler_2(void);
136 void irq_handler_3(void);
137 void irq_handler_4(void);
138 void irq_handler_5(void);
139 void irq_handler_6(void);
140 void irq_handler_7(void);
141 void irq_handler_8(void);
142 void irq_handler_9(void);
143 void irq_handler_10(void);
144 void irq_handler_11(void);
145 void irq_handler_12(void);
146 void irq_handler_13(void);
147 void irq_handler_14(void);
148 void irq_handler_15(void);
149
150 static unsigned int irq_handler[NR_IRQS]=
151 {
152 (int)&irq_handler_0,
153 (int)&irq_handler_1,
154 (int)&irq_handler_2,
155 (int)&irq_handler_3,
156 (int)&irq_handler_4,
157 (int)&irq_handler_5,
158 (int)&irq_handler_6,
159 (int)&irq_handler_7,
160 (int)&irq_handler_8,
161 (int)&irq_handler_9,
162 (int)&irq_handler_10,
163 (int)&irq_handler_11,
164 (int)&irq_handler_12,
165 (int)&irq_handler_13,
166 (int)&irq_handler_14,
167 (int)&irq_handler_15,
168 };
169
170 #endif /* MP */
171
172 /*
173 * PURPOSE: Object describing each isr
174 * NOTE: The data in this table is only modified at passsive level but can
175 * be accessed at any irq level.
176 */
177
178 static LIST_ENTRY isr_table[NR_IRQS]={{NULL,NULL},};
179 static PKSPIN_LOCK isr_lock[NR_IRQS] = {NULL,};
180 static KSPIN_LOCK isr_table_lock = {0,};
181
182 #define TAG_ISR_LOCK TAG('I', 'S', 'R', 'L')
183 #define TAG_KINTERRUPT TAG('K', 'I', 'S', 'R')
184
185 /* FUNCTIONS ****************************************************************/
186
187 #define PRESENT (0x8000)
188 #define I486_INTERRUPT_GATE (0xe00)
189
190 VOID KeInitInterrupts (VOID)
191 {
192 int i;
193
194 #ifdef MP
195
196 /*
197 * Setup the IDT entries to point to the interrupt handlers
198 */
199 for (i=0;i<NR_IRQS;i++)
200 {
201 KiIdt[0x30+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
202 KiIdt[0x30+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
203 I486_INTERRUPT_GATE;
204 InitializeListHead(&isr_table[i]);
205 }
206
207 #else
208
209 /*
210 * Setup the IDT entries to point to the interrupt handlers
211 */
212 for (i=0;i<NR_IRQS;i++)
213 {
214 KiIdt[IRQ_BASE+i].a=(irq_handler[i]&0xffff)+(KERNEL_CS<<16);
215 KiIdt[IRQ_BASE+i].b=(irq_handler[i]&0xffff0000)+PRESENT+
216 I486_INTERRUPT_GATE;
217 InitializeListHead(&isr_table[i]);
218 }
219
220 #endif
221
222 }
223
224 typedef struct _KIRQ_TRAPFRAME
225 {
226 ULONG Magic;
227 ULONG Fs;
228 ULONG Es;
229 ULONG Ds;
230 ULONG Eax;
231 ULONG Ecx;
232 ULONG Edx;
233 ULONG Ebx;
234 ULONG Esp;
235 ULONG Ebp;
236 ULONG Esi;
237 ULONG Edi;
238 ULONG Eip;
239 ULONG Cs;
240 ULONG Eflags;
241 } KIRQ_TRAPFRAME, *PKIRQ_TRAPFRAME;
242
243 #ifdef MP
244
245 VOID
246 KiInterruptDispatch (ULONG Vector, PKIRQ_TRAPFRAME Trapframe)
247 /*
248 * FUNCTION: Calls the irq specific handler for an irq
249 * ARGUMENTS:
250 * Vector = Interrupt vector
251 * Trapframe = CPU context
252 */
253 {
254 KIRQL old_level;
255 PKINTERRUPT isr;
256 PLIST_ENTRY current;
257 ULONG irq;
258
259 DPRINT("I(%d) ", Vector);
260
261 /*
262 * Notify the rest of the kernel of the raised irq level
263 */
264 HalBeginSystemInterrupt (Vector,
265 VECTOR2IRQL(Vector),
266 &old_level);
267
268 irq = VECTOR2IRQ(Vector);
269
270 /*
271 * Enable interrupts
272 * NOTE: Only higher priority interrupts will get through
273 */
274 __asm__("sti\n\t");
275
276
277 DPRINT("KiInterruptDispatch(Vector %d)\n", Vector);
278 /*
279 * Iterate the list until one of the isr tells us its device interrupted
280 */
281 current = isr_table[irq].Flink;
282 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
283 //DPRINT("current %x isr %x\n",current,isr);
284 while (current!=(&isr_table[irq]) &&
285 !isr->ServiceRoutine(isr,isr->ServiceContext))
286 {
287 current = current->Flink;
288 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
289 //DPRINT("current %x isr %x\n",current,isr);
290 }
291
292 /*
293 * Disable interrupts
294 */
295 __asm__("cli\n\t");
296
297 /*
298 * Unmask the related irq
299 */
300 HalEnableSystemInterrupt (Vector, 0, 0);
301
302 /*
303 * If the processor level will drop below dispatch level on return then
304 * issue a DPC queue drain interrupt
305 */
306 if (old_level < DISPATCH_LEVEL)
307 {
308
309 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
310 __asm__("sti\n\t");
311
312 if (KeGetCurrentThread() != NULL)
313 {
314 KeGetCurrentThread()->LastEip = Trapframe->Eip;
315 }
316 KiDispatchInterrupt();
317
318 if (KeGetCurrentThread() != NULL &&
319 KeGetCurrentThread()->Alerted[1] != 0 &&
320 Trapframe->Cs != KERNEL_CS)
321 {
322 HalEndSystemInterrupt (APC_LEVEL, 0);
323 KiDeliverNormalApc();
324 }
325
326 }
327 HalEndSystemInterrupt (old_level, 0);
328 }
329
330 #else /* MP */
331
332 VOID
333 KiInterruptDispatch (ULONG irq, PKIRQ_TRAPFRAME Trapframe)
334 /*
335 * FUNCTION: Calls the irq specific handler for an irq
336 * ARGUMENTS:
337 * irq = IRQ that has interrupted
338 */
339 {
340 KIRQL old_level;
341 PKINTERRUPT isr;
342 PLIST_ENTRY current;
343
344 /*
345 * Notify the rest of the kernel of the raised irq level
346 */
347 HalBeginSystemInterrupt (irq + IRQ_BASE,
348 PROFILE_LEVEL - irq,
349 &old_level);
350
351 /*
352 * Enable interrupts
353 * NOTE: Only higher priority interrupts will get through
354 */
355 __asm__("sti\n\t");
356
357 if (irq == 0)
358 {
359 KiUpdateSystemTime(old_level, Trapframe->Eip);
360 }
361 else
362 {
363 DPRINT("KiInterruptDispatch(irq %d)\n",irq);
364 /*
365 * Iterate the list until one of the isr tells us its device interrupted
366 */
367 current = isr_table[irq].Flink;
368 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
369 DPRINT("current %x isr %x\n",current,isr);
370 while (current!=(&isr_table[irq]) &&
371 !isr->ServiceRoutine(isr,isr->ServiceContext))
372 {
373 current = current->Flink;
374 isr = CONTAINING_RECORD(current,KINTERRUPT,Entry);
375 DPRINT("current %x isr %x\n",current,isr);
376 }
377 }
378
379 /*
380 * Disable interrupts
381 */
382 __asm__("cli\n\t");
383
384 /*
385 * Unmask the related irq
386 */
387 HalEnableSystemInterrupt (irq + IRQ_BASE, 0, 0);
388
389 /*
390 * If the processor level will drop below dispatch level on return then
391 * issue a DPC queue drain interrupt
392 */
393 if (old_level < DISPATCH_LEVEL)
394 {
395 HalEndSystemInterrupt (DISPATCH_LEVEL, 0);
396 __asm__("sti\n\t");
397
398 if (KeGetCurrentThread() != NULL)
399 {
400 KeGetCurrentThread()->LastEip = Trapframe->Eip;
401 }
402 KiDispatchInterrupt();
403 if (irq == 0)
404 {
405 PsDispatchThread(THREAD_STATE_RUNNABLE);
406 }
407 if (KeGetCurrentThread() != NULL &&
408 KeGetCurrentThread()->Alerted[1] != 0 &&
409 Trapframe->Cs != KERNEL_CS)
410 {
411 HalEndSystemInterrupt (APC_LEVEL, 0);
412 KiDeliverNormalApc();
413 }
414 }
415
416 HalEndSystemInterrupt (old_level, 0);
417 }
418
419 #endif /* MP */
420
421 static VOID
422 KeDumpIrqList(VOID)
423 {
424 PKINTERRUPT current;
425 PLIST_ENTRY current_entry;
426 unsigned int i;
427
428 for (i=0;i<NR_IRQS;i++)
429 {
430 DPRINT("For irq %x ",i);
431 current_entry = isr_table[i].Flink;
432 current = CONTAINING_RECORD(current,KINTERRUPT,Entry);
433 while (current_entry!=(&isr_table[i]))
434 {
435 DPRINT("Isr %x ",current);
436 current_entry = current_entry->Flink;
437 current = CONTAINING_RECORD(current_entry,KINTERRUPT,Entry);
438 }
439 DPRINT("\n",0);
440 }
441 }
442
443
444 NTSTATUS STDCALL
445 KeConnectInterrupt(PKINTERRUPT InterruptObject)
446 {
447 KIRQL oldlvl;
448 KIRQL synch_oldlvl;
449 PKINTERRUPT ListHead;
450 ULONG Vector;
451
452 DPRINT("KeConnectInterrupt()\n");
453
454 Vector = InterruptObject->Vector;
455
456 /*
457 * Acquire the table spinlock
458 */
459 KeAcquireSpinLock(&isr_table_lock,&oldlvl);
460
461 /*
462 * Check if the vector is already in use that we can share it
463 */
464 ListHead = CONTAINING_RECORD(isr_table[Vector].Flink,KINTERRUPT,Entry);
465 if (!IsListEmpty(&isr_table[Vector]) &&
466 (InterruptObject->Shareable == FALSE || ListHead->Shareable==FALSE))
467 {
468 KeReleaseSpinLock(&isr_table_lock,oldlvl);
469 return(STATUS_INVALID_PARAMETER);
470 }
471 else
472 {
473 isr_lock[Vector] =
474 ExAllocatePoolWithTag(NonPagedPool, sizeof(KSPIN_LOCK),
475 TAG_ISR_LOCK);
476 KeInitializeSpinLock(isr_lock[Vector]);
477 }
478
479 InterruptObject->IrqLock = isr_lock[Vector];
480
481 KeRaiseIrql(InterruptObject->SynchLevel,&synch_oldlvl);
482 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
483 DPRINT("%x %x\n",isr_table[Vector].Flink,isr_table[Vector].Blink);
484 InsertTailList(&isr_table[Vector],&InterruptObject->Entry);
485 DPRINT("%x %x\n",InterruptObject->Entry.Flink,
486 InterruptObject->Entry.Blink);
487 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
488 KeLowerIrql(synch_oldlvl);
489
490 /*
491 * Release the table spinlock
492 */
493 KeReleaseSpinLock(&isr_table_lock,oldlvl);
494
495 KeDumpIrqList();
496
497 return STATUS_SUCCESS;
498 }
499
500
501 VOID STDCALL
502 KeDisconnectInterrupt(PKINTERRUPT InterruptObject)
503 /*
504 * FUNCTION: Releases a drivers isr
505 * ARGUMENTS:
506 * InterruptObject = isr to release
507 */
508 {
509 KIRQL oldlvl;
510
511 KeRaiseIrql(InterruptObject->SynchLevel,&oldlvl);
512 KeAcquireSpinLockAtDpcLevel(InterruptObject->IrqLock);
513 RemoveEntryList(&InterruptObject->Entry);
514 KeReleaseSpinLockFromDpcLevel(InterruptObject->IrqLock);
515 KeLowerIrql(oldlvl);
516 }
517
518
519 NTSTATUS
520 STDCALL
521 KeInitializeInterrupt(PKINTERRUPT InterruptObject,
522 PKSERVICE_ROUTINE ServiceRoutine,
523 PVOID ServiceContext,
524 PKSPIN_LOCK SpinLock,
525 ULONG Vector,
526 KIRQL Irql,
527 KIRQL SynchronizeIrql,
528 KINTERRUPT_MODE InterruptMode,
529 BOOLEAN ShareVector,
530 KAFFINITY ProcessorEnableMask,
531 BOOLEAN FloatingSave)
532 {
533 InterruptObject->ServiceContext = ServiceContext;
534 InterruptObject->ServiceRoutine = ServiceRoutine;
535 InterruptObject->Vector = Vector;
536 InterruptObject->ProcessorEnableMask = ProcessorEnableMask;
537 InterruptObject->SynchLevel = SynchronizeIrql;
538 InterruptObject->Shareable = ShareVector;
539 InterruptObject->FloatingSave = FALSE;
540
541 return STATUS_SUCCESS;
542 }
543
544
545 NTSTATUS
546 STDCALL
547 IoConnectInterrupt(PKINTERRUPT* InterruptObject,
548 PKSERVICE_ROUTINE ServiceRoutine,
549 PVOID ServiceContext,
550 PKSPIN_LOCK SpinLock,
551 ULONG Vector,
552 KIRQL Irql,
553 KIRQL SynchronizeIrql,
554 KINTERRUPT_MODE InterruptMode,
555 BOOLEAN ShareVector,
556 KAFFINITY ProcessorEnableMask,
557 BOOLEAN FloatingSave)
558 /*
559 * FUNCTION: Registers a driver's isr to be called when its device interrupts
560 * ARGUMENTS:
561 * InterruptObject (OUT) = Points to the interrupt object created on
562 * return
563 * ServiceRoutine = Routine to be called when the device interrupts
564 * ServiceContext = Parameter to be passed to ServiceRoutine
565 * SpinLock = Initalized spinlock that will be used to synchronize
566 * access between the isr and other driver routines. This is
567 * required if the isr handles more than one vector or the
568 * driver has more than one isr
569 * Vector = Interrupt vector to allocate
570 * (returned from HalGetInterruptVector)
571 * Irql = DIRQL returned from HalGetInterruptVector
572 * SynchronizeIrql = DIRQL at which the isr will execute. This must
573 * be the highest of all the DIRQLs returned from
574 * HalGetInterruptVector if the driver has multiple
575 * isrs
576 * InterruptMode = Specifies if the interrupt is LevelSensitive or
577 * Latched
578 * ShareVector = Specifies if the vector can be shared
579 * ProcessorEnableMask = Processors on the isr can run
580 * FloatingSave = TRUE if the floating point stack should be saved when
581 * the isr runs. Must be false for x86 drivers
582 * RETURNS: Status
583 * IRQL: PASSIVE_LEVEL
584 */
585 {
586 PKINTERRUPT Interrupt;
587 NTSTATUS Status = STATUS_SUCCESS;
588
589 ASSERT_IRQL(PASSIVE_LEVEL);
590
591 DPRINT("IoConnectInterrupt(Vector %x)\n",Vector);
592
593 /*
594 * Check the parameters
595 */
596 if (Vector >= NR_IRQS)
597 {
598 return(STATUS_INVALID_PARAMETER);
599 }
600 if (FloatingSave == TRUE)
601 {
602 return(STATUS_INVALID_PARAMETER);
603 }
604
605 /*
606 * Initialize interrupt object
607 */
608 Interrupt=ExAllocatePoolWithTag(NonPagedPool,sizeof(KINTERRUPT),
609 TAG_KINTERRUPT);
610 if (Interrupt==NULL)
611 {
612 return(STATUS_INSUFFICIENT_RESOURCES);
613 }
614
615 Status = KeInitializeInterrupt(Interrupt,
616 ServiceRoutine,
617 ServiceContext,
618 SpinLock,
619 Vector,
620 Irql,
621 SynchronizeIrql,
622 InterruptMode,
623 ShareVector,
624 ProcessorEnableMask,
625 FloatingSave);
626 if (!NT_SUCCESS(Status))
627 {
628 ExFreePool(Interrupt);
629 return Status;
630 }
631
632 Status = KeConnectInterrupt(Interrupt);
633 if (!NT_SUCCESS(Status))
634 {
635 ExFreePool(Interrupt);
636 return Status;
637 }
638
639 *InterruptObject = Interrupt;
640
641 return(STATUS_SUCCESS);
642 }
643
644
645 VOID STDCALL
646 IoDisconnectInterrupt(PKINTERRUPT InterruptObject)
647 /*
648 * FUNCTION: Releases a drivers isr
649 * ARGUMENTS:
650 * InterruptObject = isr to release
651 */
652 {
653 KeDisconnectInterrupt(InterruptObject);
654 ExFreePool(InterruptObject);
655 }
656
657 /* EOF */