diff options
-rw-r--r-- | device/intr.c | 16 | ||||
-rw-r--r-- | i386/configfrag.ac | 11 | ||||
-rw-r--r-- | i386/i386/apic.h | 6 | ||||
-rw-r--r-- | i386/i386/irq.c | 47 | ||||
-rw-r--r-- | i386/i386/irq.h | 1 | ||||
-rw-r--r-- | i386/i386/pic.c | 5 | ||||
-rw-r--r-- | i386/i386/pic.h | 6 | ||||
-rw-r--r-- | i386/i386/smp.c | 8 | ||||
-rw-r--r-- | i386/i386/trap.c | 2 | ||||
-rw-r--r-- | i386/i386at/interrupt.S | 21 | ||||
-rw-r--r-- | i386/i386at/ioapic.c | 18 | ||||
-rw-r--r-- | i386/i386at/model_dep.c | 1 | ||||
-rw-r--r-- | x86_64/interrupt.S | 22 |
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 */ |