x86: irq handler func pointers
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 12 Mar 2014 02:28:40 +0000 (19:28 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Sat, 29 Mar 2014 01:16:10 +0000 (18:16 -0700)
This is more in line with modern OSs (plan 9, linux), where we have some
struct tracking what to do at a given vector, instead of branching on
the trap number.

kern/arch/x86/apic.c
kern/arch/x86/apic.h
kern/arch/x86/trap.c
kern/arch/x86/trap.h
kern/include/trap.h

index bb9bdbd..865ae6f 100644 (file)
@@ -49,7 +49,7 @@ void pic_remap(void)
        spin_unlock_irqsave(&piclock);
 }
 
-void pic_mask_irq(uint8_t irq)
+void pic_mask_irq(int irq)
 {
        spin_lock_irqsave(&piclock);
        if (irq > 7)
@@ -59,7 +59,7 @@ void pic_mask_irq(uint8_t irq)
        spin_unlock_irqsave(&piclock);
 }
 
-void pic_unmask_irq(uint8_t irq)
+void pic_unmask_irq(int irq)
 {
        spin_lock_irqsave(&piclock);
        if (irq > 7) {
@@ -105,8 +105,29 @@ uint16_t pic_get_isr(void)
        return __pic_get_irq_reg(PIC_READ_ISR);
 }
 
-void pic_send_eoi(uint32_t irq)
+/* Takes a raw vector/trap number (32-47), not a device IRQ (0-15) */
+bool pic_check_spurious(int trap_nr)
 {
+       /* the PIC may send spurious irqs via one of the chips irq 7.  if the isr
+        * doesn't show that irq, then it was spurious, and we don't send an eoi.
+        * Check out http://wiki.osdev.org/8259_PIC#Spurious_IRQs */
+       if ((trap_nr == PIC1_SPURIOUS) && !(pic_get_isr() & (1 << 7))) {
+               printd("Spurious PIC1 irq!\n"); /* want to know if this happens */
+               return TRUE;
+       }
+       if ((trap_nr == PIC2_SPURIOUS) && !(pic_get_isr() & (1 << 15))) {
+               printd("Spurious PIC2 irq!\n"); /* want to know if this happens */
+               /* for the cascaded PIC, we *do* need to send an EOI to the master's
+                * cascade irq (2). */
+               pic_send_eoi(2 + PIC1_OFFSET);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+void pic_send_eoi(int trap_nr)
+{
+       int irq = trap_nr - PIC1_OFFSET;
        spin_lock_irqsave(&piclock);
        // all irqs beyond the first seven need to be chained to the slave
        if (irq > 7)
@@ -115,6 +136,26 @@ void pic_send_eoi(uint32_t irq)
        spin_unlock_irqsave(&piclock);
 }
 
+bool lapic_check_spurious(int trap_nr)
+{
+       /* FYI: lapic_spurious is 255 on qemu and 15 on the nehalem..  We actually
+        * can set bits 4-7, and P6s have 0-3 hardwired to 0.  YMMV.
+        *
+        * The SDM recommends not using the spurious vector for any other IRQs (LVT
+        * or IOAPIC RTE), since the handlers don't send an EOI.  However, our check
+        * here allows us to use the vector since we can tell the diff btw a
+        * spurious and a real IRQ. */
+       uint8_t lapic_spurious = read_mmreg32(LAPIC_SPURIOUS) & 0xff;
+       /* Note the lapic's vectors are not shifted by an offset. */
+       if ((trap_nr == lapic_spurious) && !lapic_get_isr_bit(lapic_spurious)) {
+               /* i'm still curious about these */
+               printk("Spurious LAPIC irq %d, core %d!\n", lapic_spurious, core_id());
+               lapic_print_isr();
+               return TRUE;
+       }
+       return FALSE;
+}
+
 /* Debugging helper.  Note the ISR/IRR are 32 bits at a time, spaced every 16
  * bytes in the LAPIC address space. */
 void lapic_print_isr(void)
@@ -153,7 +194,7 @@ bool lapic_get_irr_bit(uint8_t vector)
  * ExtInts.  To prevent abuse, we'll use it just for IPIs for now (which only
  * come via the APIC).
  *
- * We only check the IRR, due to how we send EOIs.  Since we don't send til
+ * We only check the ISR, due to how we send EOIs.  Since we don't send til
  * after handlers return, the ISR will show pending for the current IRQ.  It is
  * the EOI that clears the bit from the ISR. */
 bool ipi_is_pending(uint8_t vector)
index dac3ed3..48ca1dd 100644 (file)
@@ -138,12 +138,14 @@ extern system_timing_t system_timing;
 extern bool core_id_ready;
 
 void pic_remap(void);
-void pic_mask_irq(uint8_t irq);
-void pic_unmask_irq(uint8_t irq);
+void pic_mask_irq(int irq);
+void pic_unmask_irq(int irq);
 uint16_t pic_get_mask(void);
 uint16_t pic_get_irr(void);
 uint16_t pic_get_isr(void);
-void pic_send_eoi(uint32_t irq);
+bool pic_check_spurious(int trap_nr);
+void pic_send_eoi(int trap_nr);
+bool lapic_check_spurious(int trap_nr);
 bool lapic_get_isr_bit(uint8_t vector);
 bool lapic_get_irr_bit(uint8_t vector);
 void lapic_print_isr(void);
@@ -159,7 +161,7 @@ void udelay_pit(uint64_t usec);
 uint64_t gettimer(void);
 uint64_t getfreq(void);
 
-static inline void lapic_send_eoi(void);
+static inline void lapic_send_eoi(int unused);
 static inline uint32_t lapic_get_version(void);
 static inline uint32_t lapic_get_error(void);
 static inline uint32_t lapic_get_id(void);
@@ -184,7 +186,7 @@ static inline void __send_nmi(uint8_t hw_coreid);
 #define unmask_lapic_lvt(entry) \
        write_mmreg32(entry, read_mmreg32(entry) & ~LAPIC_LVT_MASK)
 
-static inline void lapic_send_eoi(void)
+static inline void lapic_send_eoi(int unused)
 {
        write_mmreg32(LAPIC_EOI, 0);
 }
index dae2f90..624d7ff 100644 (file)
@@ -447,74 +447,6 @@ void trap(struct hw_trapframe *hw_tf)
        assert(0);
 }
 
-/* Tells us if an interrupt (trap_nr) came from the PIC or not */
-static bool irq_from_pic(uint32_t trap_nr)
-{
-       /* The 16 IRQs within the range [PIC1_OFFSET, PIC1_OFFSET + 15] came from
-        * the PIC.  [32-47] */
-       if (trap_nr < PIC1_OFFSET)
-               return FALSE;
-       if (trap_nr > PIC1_OFFSET + 15)
-               return FALSE;
-       return TRUE;
-}
-
-/* Helper: returns TRUE if the irq is spurious.  Pass in the trap_nr, not the
- * IRQ number (trap_nr = PIC_OFFSET + irq) */
-static bool check_spurious_irq(uint32_t trap_nr)
-{
-#ifndef CONFIG_ENABLE_MPTABLES         /* TODO: our proxy for using the PIC */
-       /* the PIC may send spurious irqs via one of the chips irq 7.  if the isr
-        * doesn't show that irq, then it was spurious, and we don't send an eoi.
-        * Check out http://wiki.osdev.org/8259_PIC#Spurious_IRQs */
-       if ((trap_nr == PIC1_SPURIOUS) && !(pic_get_isr() & (1 << 7))) {
-               printd("Spurious PIC1 irq!\n"); /* want to know if this happens */
-               return TRUE;
-       }
-       if ((trap_nr == PIC2_SPURIOUS) && !(pic_get_isr() & (1 << 15))) {
-               printd("Spurious PIC2 irq!\n"); /* want to know if this happens */
-               /* for the cascaded PIC, we *do* need to send an EOI to the master's
-                * cascade irq (2). */
-               pic_send_eoi(2);
-               return TRUE;
-       }
-       /* At this point, we know the PIC didn't send a spurious IRQ */
-       if (irq_from_pic(trap_nr))
-               return FALSE;
-#endif
-       /* Either way (with or without a PIC), we need to check the LAPIC.
-        * FYI: lapic_spurious is 255 on qemu and 15 on the nehalem..  We actually
-        * can set bits 4-7, and P6s have 0-3 hardwired to 0.  YMMV.
-        *
-        * The SDM recommends not using the spurious vector for any other IRQs (LVT
-        * or IOAPIC RTE), since the handlers don't send an EOI.  However, our check
-        * here allows us to use the vector since we can tell the diff btw a
-        * spurious and a real IRQ. */
-       uint8_t lapic_spurious = read_mmreg32(LAPIC_SPURIOUS) & 0xff;
-       /* Note the lapic's vectors are not shifted by an offset. */
-       if ((trap_nr == lapic_spurious) && !lapic_get_isr_bit(lapic_spurious)) {
-               printk("Spurious LAPIC irq %d, core %d!\n", lapic_spurious, core_id());
-               lapic_print_isr();
-               return TRUE;
-       }
-       return FALSE;
-}
-
-/* Helper, sends an end-of-interrupt for the trap_nr (not HW IRQ number). */
-static void send_eoi(uint32_t trap_nr)
-{
-#ifndef CONFIG_ENABLE_MPTABLES         /* TODO: our proxy for using the PIC */
-       /* WARNING: this will break if the LAPIC requests vectors that overlap with
-        * the PIC's range. */
-       if (irq_from_pic(trap_nr))
-               pic_send_eoi(trap_nr - PIC1_OFFSET);
-       else
-               lapic_send_eoi();
-#else
-       lapic_send_eoi();
-#endif
-}
-
 /* Note IRQs are disabled unless explicitly turned on.
  *
  * In general, we should only get trapno's >= PIC1_OFFSET (32).  Anything else
@@ -538,12 +470,12 @@ void handle_irq(struct hw_trapframe *hw_tf)
        if (hw_tf->tf_trapno != LAPIC_TIMER_DEFAULT_VECTOR)     /* timer irq */
        if (hw_tf->tf_trapno != 255) /* kmsg */
        if (hw_tf->tf_trapno != 36)     /* serial */
-               printk("Incoming IRQ, ISR: %d on core %d\n", hw_tf->tf_trapno,
+               printd("Incoming IRQ, ISR: %d on core %d\n", hw_tf->tf_trapno,
                       core_id());
-       if (check_spurious_irq(hw_tf->tf_trapno))
-               goto out_no_eoi;
        /* TODO: RCU read lock */
        irq_h = irq_handlers[hw_tf->tf_trapno];
+       if (!irq_h || irq_h->check_spurious(hw_tf->tf_trapno))
+               goto out_no_eoi;
        while (irq_h) {
                irq_h->isr(hw_tf, irq_h->data);
                irq_h = irq_h->next;
@@ -564,7 +496,7 @@ void handle_irq(struct hw_trapframe *hw_tf)
            (hw_tf->tf_trapno <= I_SMP_CALL_LAST))
                down_checklist(handler_wrappers[hw_tf->tf_trapno & 0x0f].cpu_list);
        /* Keep in sync with ipi_is_pending */
-       send_eoi(hw_tf->tf_trapno);
+       irq_handlers[hw_tf->tf_trapno]->eoi(hw_tf->tf_trapno);
        /* Fall-through */
 out_no_eoi:
        dec_irq_depth(pcpui);
@@ -577,6 +509,19 @@ out_no_eoi:
        assert(0);
 }
 
+/* Tells us if an interrupt (trap_nr) came from the PIC or not */
+static bool irq_from_pic(uint32_t trap_nr)
+{
+       /* The 16 IRQs within the range [PIC1_OFFSET, PIC1_OFFSET + 15] came from
+        * the PIC.  [32-47] */
+       if (trap_nr < PIC1_OFFSET)
+               return FALSE;
+       if (trap_nr > PIC1_OFFSET + 15)
+               return FALSE;
+       return TRUE;
+}
+
+/* TODO: remove the distinction btw raw and device IRQs */
 void register_raw_irq(unsigned int vector, isr_t handler, void *data)
 {
        struct irq_handler *irq_h;
@@ -585,6 +530,22 @@ void register_raw_irq(unsigned int vector, isr_t handler, void *data)
        spin_lock_irqsave(&irq_handler_wlock);
        irq_h->isr = handler;
        irq_h->data = data;
+       /* TODO: better way to sort out LAPIC vs PIC */
+       if (irq_from_pic(vector)) {
+               irq_h->check_spurious = pic_check_spurious;
+               irq_h->eoi = pic_send_eoi;
+               irq_h->mask = pic_mask_irq;
+               irq_h->unmask = pic_unmask_irq;
+               irq_h->type = "pic";
+       } else {
+               irq_h->check_spurious = lapic_check_spurious;
+               irq_h->eoi = lapic_send_eoi;
+               /* TODO: which mask we pick also depends on source: IOAPIC, LINT, etc */
+               irq_h->mask = 0;
+               irq_h->unmask = 0;
+               irq_h->type = "lapic";
+       }
+       irq_h->apic_vector = vector;
        irq_h->next = irq_handlers[vector];
        wmb();  /* make sure irq_h is done before publishing to readers */
        irq_handlers[vector] = irq_h;
@@ -613,7 +574,8 @@ int x =     intrenable(irq, handler, irq_arg, tbdf);
        if (x > 0)
                register_raw_irq(x, handler, irq_arg);
 #else
-       register_raw_irq(KERNEL_IRQ_OFFSET + irq, handler, irq_arg);
+       // only need the ghetto one up above
+       //register_raw_irq(KERNEL_IRQ_OFFSET + irq, handler, irq_arg);
        pic_unmask_irq(irq);
        unmask_lapic_lvt(LAPIC_LVT_LINT0);
        enable_irq();
index 1761d2a..c291ad6 100644 (file)
 #include <arch/pci.h>
 #include <arch/coreid.h>
 
+struct irq_handler {
+       struct irq_handler *next;
+       void (*isr)(struct hw_trapframe *hw_tf, void *data);
+       void *data;
+
+       /* all handlers in the chain need to have the same func pointers.  we only
+        * really use the first one, and the latter are to catch bugs.  also, we
+        * won't be doing a lot of IRQ line sharing */
+       bool (*check_spurious)(int);
+       void (*eoi)(int);
+       void (*mask)(int);
+       void (*unmask)(int);
+
+       int pci_tbdf;
+       int dev_irq;
+       int apic_vector;
+
+       char *type;
+       #define IRQ_NAME_LEN 26
+       char name[IRQ_NAME_LEN];
+};
+
 /* The kernel's interrupt descriptor table */
 extern gatedesc_t idt[];
 extern pseudodesc_t idt_pd;
index e814b92..39f45e9 100644 (file)
 
 // func ptr for interrupt service routines
 typedef void (*isr_t)(struct hw_trapframe *hw_tf, void *data);
-struct irq_handler {
-       isr_t isr;
-       void *data;
-       struct irq_handler *next;
-};
 
 void idt_init(void);
 void register_raw_irq(unsigned int vector, isr_t handler, void *data);