summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--device/intr.c16
-rw-r--r--i386/configfrag.ac11
-rw-r--r--i386/i386/apic.h6
-rw-r--r--i386/i386/irq.c47
-rw-r--r--i386/i386/irq.h1
-rw-r--r--i386/i386/pic.c5
-rw-r--r--i386/i386/pic.h6
-rw-r--r--i386/i386/smp.c8
-rw-r--r--i386/i386/trap.c2
-rw-r--r--i386/i386at/interrupt.S21
-rw-r--r--i386/i386at/ioapic.c18
-rw-r--r--i386/i386at/model_dep.c1
-rw-r--r--x86_64/interrupt.S22
13 files changed, 127 insertions, 37 deletions
diff --git a/device/intr.c b/device/intr.c
index 27dfa8ec..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
*
@@ -159,7 +165,7 @@ insert_intr_entry (struct irqdev *dev, int id, ipc_port_t dst_port)
ret = NULL;
goto out;
}
- printf("irq handler [%d]: new delivery port %p entry %p\n", id, dst_port, new);
+ printf("irq handler [%d]: new delivery port %p entry %p for %s\n", id, dst_port, new, current_task()->name);
ret = new;
new->id = id;
new->dst_port = dst_port;
diff --git a/i386/configfrag.ac b/i386/configfrag.ac
index 6b4eb270..6cd1fdf7 100644
--- a/i386/configfrag.ac
+++ b/i386/configfrag.ac
@@ -100,11 +100,16 @@ AC_ARG_ENABLE([apic],
enable_apic=yes
[fi]
-[if [ x"$enable_apic" = xyes ]; then]
+[if [ x"$enable_linux_groups" != xno ]; then]
+ # Linux glue does not like APIC
+ enable_apic=no
+[fi]
+
+[if [ x"$enable_apic" = xno ]; then]
+ AM_CONDITIONAL([enable_apic], [false])
+[else]
AC_DEFINE([APIC], [1], [APIC support])
AM_CONDITIONAL([enable_apic], [true])
-[else]
- AM_CONDITIONAL([enable_apic], [false])
[fi]
[case $host_platform:$host_cpu in
diff --git a/i386/i386/apic.h b/i386/i386/apic.h
index 92fb900a..df95b812 100644
--- a/i386/i386/apic.h
+++ b/i386/i386/apic.h
@@ -235,6 +235,11 @@ typedef struct ApicInfo {
struct IrqOverrideData irq_override_list[MAX_IRQ_OVERRIDE];
} ApicInfo;
+struct irqinfo {
+ uint8_t trigger;
+ uint8_t vector;
+};
+
int apic_data_init(void);
void apic_add_cpu(uint16_t apic_id);
void apic_lapic_init(ApicLocalUnit* lapic_ptr);
@@ -275,6 +280,7 @@ extern volatile ApicLocalUnit* lapic;
extern int cpu_id_lut[];
extern uint32_t *hpet_addr;
extern uint8_t apic_id_mask;
+extern struct irqinfo irqinfo[];
#endif
diff --git a/i386/i386/irq.c b/i386/i386/irq.c
index 91318f67..ea10e179 100644
--- a/i386/i386/irq.c
+++ b/i386/i386/irq.c
@@ -34,32 +34,57 @@ irq_eoi (struct irqdev *dev, int id)
#endif
}
-static unsigned int ndisabled_irq[NINTR];
+/* Each array elem fits in a cache line */
+struct nested_irq {
+ simple_lock_irq_data_t irq_lock; /* Protects ndisabled */
+ int32_t ndisabled;
+ uint32_t unused[14];
+} __attribute__((packed)) nested_irqs[NINTR];
+
+void
+init_irqs (void)
+{
+ int i;
+
+ for (i = 0; i < NINTR; i++)
+ {
+ simple_lock_init_irq(&nested_irqs[i].irq_lock);
+ nested_irqs[i].ndisabled = 0;
+ }
+}
void
__disable_irq (irq_t irq_nr)
{
+ spl_t s;
assert (irq_nr < NINTR);
+ struct nested_irq *nirq = &nested_irqs[irq_nr];
- spl_t s = splhigh();
- ndisabled_irq[irq_nr]++;
- assert (ndisabled_irq[irq_nr] > 0);
- if (ndisabled_irq[irq_nr] == 1)
+ s = simple_lock_irq(&nirq->irq_lock);
+
+ nirq->ndisabled++;
+ assert (nirq->ndisabled > 0);
+ if (nirq->ndisabled == 1)
mask_irq (irq_nr);
- splx(s);
+
+ simple_unlock_irq(s, &nirq->irq_lock);
}
void
__enable_irq (irq_t irq_nr)
{
+ spl_t s;
assert (irq_nr < NINTR);
+ struct nested_irq *nirq = &nested_irqs[irq_nr];
- spl_t s = splhigh();
- assert (ndisabled_irq[irq_nr] > 0);
- ndisabled_irq[irq_nr]--;
- if (ndisabled_irq[irq_nr] == 0)
+ s = simple_lock_irq(&nirq->irq_lock);
+
+ assert (nirq->ndisabled > 0);
+ nirq->ndisabled--;
+ if (nirq->ndisabled == 0)
unmask_irq (irq_nr);
- splx(s);
+
+ simple_unlock_irq(s, &nirq->irq_lock);
}
struct irqdev irqtab = {
diff --git a/i386/i386/irq.h b/i386/i386/irq.h
index 72bbe57b..e1f41045 100644
--- a/i386/i386/irq.h
+++ b/i386/i386/irq.h
@@ -23,6 +23,7 @@
typedef unsigned int irq_t;
+void init_irqs (void);
void __enable_irq (irq_t irq);
void __disable_irq (irq_t irq);
diff --git a/i386/i386/pic.c b/i386/i386/pic.c
index 0218fea4..b51bf3ad 100644
--- a/i386/i386/pic.c
+++ b/i386/i386/pic.c
@@ -80,6 +80,8 @@ int spl_init = 0;
int iunit[NINTR] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+struct irqinfo irqinfo[NINTR];
+
unsigned short master_icw, master_ocw, slaves_icw, slaves_ocw;
u_short PICM_ICW1, PICM_OCW1, PICS_ICW1, PICS_OCW1 ;
@@ -119,6 +121,9 @@ picinit(void)
curr_ipl[i] = SPLHI;
curr_pic_mask = 0;
+ for (i = 0; i < NINTR; i++)
+ irqinfo[i].trigger = EDGE_TRIGGER;
+
/*
** 1. Generate addresses to each PIC port.
*/
diff --git a/i386/i386/pic.h b/i386/i386/pic.h
index aec0ef6b..aa5f7e60 100644
--- a/i386/i386/pic.h
+++ b/i386/i386/pic.h
@@ -181,11 +181,17 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#define PIC_MASK_ZERO 0x00
#if !defined(__ASSEMBLER__) && !defined(APIC)
+struct irqinfo {
+ unsigned char trigger;
+ unsigned char vector;
+};
+
extern void picinit (void);
extern int curr_pic_mask;
extern void intnull(int unit);
extern void mask_irq (unsigned int irq_nr);
extern void unmask_irq (unsigned int irq_nr);
+extern struct irqinfo irqinfo[];
#endif /* __ASSEMBLER__ */
#endif /* _I386_PIC_H_ */
diff --git a/i386/i386/smp.c b/i386/i386/smp.c
index 4513b7f2..dc3a8ba5 100644
--- a/i386/i386/smp.c
+++ b/i386/i386/smp.c
@@ -54,17 +54,11 @@ static void smp_send_ipi(unsigned logical_id, unsigned vector)
cpu_intr_save(&flags);
- apic_send_ipi(NO_SHORTHAND, FIXED, LOGICAL, ASSERT, EDGE, vector, logical_id);
-
do {
cpu_pause();
} while(lapic->icr_low.delivery_status == SEND_PENDING);
- apic_send_ipi(NO_SHORTHAND, FIXED, LOGICAL, DE_ASSERT, EDGE, vector, logical_id);
-
- do {
- cpu_pause();
- } while(lapic->icr_low.delivery_status == SEND_PENDING);
+ apic_send_ipi(NO_SHORTHAND, FIXED, LOGICAL, ASSERT, EDGE, vector, logical_id);
cpu_intr_restore(flags);
}
diff --git a/i386/i386/trap.c b/i386/i386/trap.c
index 4f6c457f..de52de76 100644
--- a/i386/i386/trap.c
+++ b/i386/i386/trap.c
@@ -383,7 +383,7 @@ int user_trap(struct i386_saved_state *regs)
((short*)0xb8700)[5] = 0x0f30+(type % 10);
#endif
#if 0
- printf("user trap %d error %d\n", type, code);
+ printf("user trap %ld error %ld\n", type, code);
dump_ss(regs);
#endif
diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S
index 77424b43..164b0939 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 */
@@ -113,11 +124,15 @@ ENTRY(interrupt)
movl S_IRQ,%eax /* copy irq number */
- shll $2,%eax /* irq * 4 */
- movl EXT(iunit)(%eax),%edx /* get device unit number */
+ movl EXT(iunit)(,%eax,4),%edx /* get device unit number */
movl %edx,(%esp) /* unit number as 1st arg */
- call *EXT(ivect)(%eax) /* call interrupt handler */
+ call *EXT(ivect)(,%eax,4) /* 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 */
diff --git a/i386/i386at/ioapic.c b/i386/i386at/ioapic.c
index d0724f76..e38e4d6b 100644
--- a/i386/i386at/ioapic.c
+++ b/i386/i386at/ioapic.c
@@ -128,6 +128,8 @@ interrupt_handler_fn ivect[NINTR] = {
/* 63 */ intnull,
};
+struct irqinfo irqinfo[NINTR];
+
void
picdisable(void)
{
@@ -293,6 +295,7 @@ ioapic_toggle(int pin, int mask)
void
ioapic_irq_eoi(int pin)
{
+ /* FIXME: multiple ioapics (not always zero) */
int apic = 0;
union ioapic_route_entry_union oldentry, entry;
@@ -302,6 +305,7 @@ ioapic_irq_eoi(int pin)
spl_t s = simple_lock_irq(&ioapic_lock);
if (!has_irq_specific_eoi) {
+ // XXX Linux conditions on trigger mode: if (irqinfo[pin].trigger) {
/* Workaround for old IOAPICs with no specific EOI */
/* Mask the pin and change to edge triggered */
@@ -313,11 +317,10 @@ ioapic_irq_eoi(int pin)
/* Restore level entry */
ioapic_write_entry(apic, pin, oldentry.both);
+ //}
} else {
volatile ApicIoUnit *ioapic = apic_get_ioapic(apic)->ioapic;
-
- ioapic_read_entry(apic, pin, &entry.both);
- ioapic->eoi.r = entry.both.vector;
+ ioapic->eoi.r = irqinfo[pin].vector;
}
simple_unlock_irq(s, &ioapic_lock);
@@ -407,6 +410,9 @@ ioapic_configure(void)
entry.both.vector = IOAPIC_INT_BASE + gsi;
ioapic_write_entry(apic, pin, entry.both);
+ irqinfo[pin].vector = entry.both.vector;
+ irqinfo[pin].trigger = entry.both.trigger;
+
/* Set initial state to masked */
mask_irq(pin);
@@ -441,6 +447,9 @@ ioapic_configure(void)
entry.both.vector = IOAPIC_INT_BASE + gsi;
ioapic_write_entry(apic, pin, entry.both);
+ irqinfo[pin].vector = entry.both.vector;
+ irqinfo[pin].trigger = entry.both.trigger;
+
/* Set initial state to masked */
mask_irq(pin);
}
@@ -465,6 +474,9 @@ ioapic_configure(void)
entry.both.vector = IOAPIC_INT_BASE + gsi;
ioapic_write_entry(apic, pin, entry.both);
+ irqinfo[pin + ngsis].vector = entry.both.vector;
+ irqinfo[pin + ngsis].trigger = entry.both.trigger;
+
/* Set initial state to masked */
mask_irq(pin + ngsis);
}
diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
index 42dadeb8..071c23fa 100644
--- a/i386/i386at/model_dep.c
+++ b/i386/i386at/model_dep.c
@@ -173,6 +173,7 @@ void machine_init(void)
#if (NCPUS > 1)
smp_init();
#endif
+ init_irqs();
#if defined(APIC)
ioapic_configure();
#endif
diff --git a/x86_64/interrupt.S b/x86_64/interrupt.S
index 6fb77727..4b2d0482 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)(,%rcx,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)(,%rcx,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 */
@@ -112,11 +123,14 @@ ENTRY(interrupt)
movq S_REGS,S_ARG3 /* address of interrupted registers as 4th arg */
movl S_IRQ,%eax /* copy irq number */
- shll $2,%eax /* irq * 4 */
- movl EXT(iunit)(%rax),%edi /* get device unit number as 1st arg */
+ movl EXT(iunit)(,%rax,4),%edi /* get device unit number as 1st arg */
+
+ call *EXT(ivect)(,%rax,8) /* call interrupt handler */
- shll $1,%eax /* irq * 8 */
- call *EXT(ivect)(%rax) /* call interrupt handler */
+ movl S_IRQ,%ecx
+ movb EXT(irqinfo)(,%rcx,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 */