diff options
-rw-r--r-- | device/intr.c | 14 | ||||
-rw-r--r-- | i386/i386at/interrupt.S | 16 | ||||
-rw-r--r-- | x86_64/interrupt.S | 16 |
3 files changed, 42 insertions, 4 deletions
diff --git a/device/intr.c b/device/intr.c index 9529492b..07d8acd8 100644 --- a/device/intr.c +++ b/device/intr.c @@ -55,12 +55,18 @@ search_intr (struct irqdev *dev, ipc_port_t dst_port) /* * Interrupt handling logic: + * PCI interrupts are raised on master processor only. * * interrupt.S raises spl (thus IF cleared) - * interrupt.S EOI - * interrupt.S calls the handler - * - for pure in-kernel handlers, they do whatever they want with IF cleared. - * - when a userland handler is registered, queue_intr masks the irq. + * For edge triggered interrupts (to ensure none are missed): + * - interrupt.S EOI + * - interrupt.S calls the handler + * For level triggered interrupts (to prevent stacking): + * - interrupt.S calls the handler + * - interrupt.S EOI + * + * For pure in-kernel handlers, they do whatever they want with IF cleared. + * When a userland handler is registered, queue_intr masks the irq. * interrupt.S lowers spl with splx_cli, thus IF still cleared * iret, that also sets IF * diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S index 77424b43..ac71473b 100644 --- a/i386/i386at/interrupt.S +++ b/i386/i386at/interrupt.S @@ -61,6 +61,11 @@ ENTRY(interrupt) je _call_local_ast #endif + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _call_handler /* yes: handle before eoi */ + +_eoi: #ifndef APIC movl $1,%eax shll %cl,%eax /* get corresponding IRQ mask */ @@ -102,6 +107,12 @@ ENTRY(interrupt) call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */ #endif + movl S_IRQ,%ecx /* restore irq number */ + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _completed /* yes: we are done */ + +_call_handler: movl S_IPL,%eax movl %eax,4(%esp) /* previous ipl as 2nd arg */ @@ -119,6 +130,11 @@ ENTRY(interrupt) call *EXT(ivect)(%eax) /* call interrupt handler */ + movl S_IRQ,%ecx /* restore irq number */ + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _eoi /* yes: eoi */ + _completed: movl S_IPL,%eax /* restore previous ipl */ movl %eax,(%esp) diff --git a/x86_64/interrupt.S b/x86_64/interrupt.S index 6fb77727..55fa993a 100644 --- a/x86_64/interrupt.S +++ b/x86_64/interrupt.S @@ -61,6 +61,11 @@ ENTRY(interrupt) je _call_local_ast #endif + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _call_handler /* yes: call handler before eoi */ + +_eoi: #ifndef APIC movl $1,%eax shll %cl,%eax /* get corresponding IRQ mask */ @@ -102,6 +107,12 @@ ENTRY(interrupt) call EXT(ioapic_irq_eoi) /* ioapic irq specific EOI */ #endif + movl S_IRQ,%ecx + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _completed /* yes: we are done */ + +_call_handler: ; movq S_IPL,S_ARG1 /* previous ipl as 2nd arg */ @@ -118,6 +129,11 @@ ENTRY(interrupt) shll $1,%eax /* irq * 8 */ call *EXT(ivect)(%rax) /* call interrupt handler */ + movl S_IRQ,%ecx + movb EXT(irqinfo)(,%ecx,2),%al /* look up irq_info[irq].trigger */ + testb $1,%al /* was this a level triggered interrupt? */ + jnz _eoi /* yes: eoi */ + _completed: movl S_IPL,%edi /* restore previous ipl */ call splx_cli /* restore previous ipl */ |