summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--device/intr.c14
-rw-r--r--i386/i386at/interrupt.S16
-rw-r--r--x86_64/interrupt.S16
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 */