Refactors MSI code slightly
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 4 Apr 2014 19:40:41 +0000 (12:40 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Fri, 4 Apr 2014 19:40:41 +0000 (12:40 -0700)
PCI/MSI code doesn't need to know about irq_h, which makes all of the
interfaces a little cleaner.

Also puts in some checks to prevent us from trying to turn on MSI and
MSI-X at the same time.

kern/arch/x86/ioapic.c
kern/arch/x86/msi.c
kern/arch/x86/pci.h

index a210164..4035710 100644 (file)
@@ -372,10 +372,43 @@ int nextvec(void)
        return vecno;
 }
 
+static void msi_mask_irq(struct irq_handler *irq_h, int apic_vector)
+{
+       pci_msi_mask(irq_h->dev_private);
+}
+
+static void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector)
+{
+       pci_msi_unmask(irq_h->dev_private);
+}
+
+static int msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
+{
+       pci_msi_route(irq_h->dev_private, dest);
+       return 0;
+}
+
+static void msix_mask_irq(struct irq_handler *irq_h, int apic_vector)
+{
+       pci_msix_mask_vector(irq_h->dev_private);
+}
+
+static void msix_unmask_irq(struct irq_handler *irq_h, int apic_vector)
+{
+       pci_msix_unmask_vector(irq_h->dev_private);
+}
+
+static int msix_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
+{
+       pci_msix_route_vector(irq_h->dev_private, dest);
+       return 0;
+}
+
 static int msi_irq_enable(struct irq_handler *irq_h, struct pci_device *p)
 {
        unsigned int vno, lo, hi = 0;
        uint64_t msivec;
+       struct msix_irq_vector *linkage;
 
        vno = nextvec();
 
@@ -383,13 +416,13 @@ static int msi_irq_enable(struct irq_handler *irq_h, struct pci_device *p)
        lo = IPlow | TMedge | Pm | vno;
 
        msivec = (uint64_t) hi << 32 | lo;
-       if (pci_msix_enable(irq_h, p, msivec) == -1) {
+       irq_h->dev_private = pci_msix_enable(p, msivec);
+       if (!irq_h->dev_private) {
                if (pci_msi_enable(p, msivec) == -1) {
-                       /* in case we turned it half-on */;
-                       msi_mask_irq(irq_h, 0 /* unused */);
                        /* TODO: should free vno here */
                        return -1;
                }
+               irq_h->dev_private = p;
                irq_h->check_spurious = lapic_check_spurious;
                irq_h->eoi = lapic_send_eoi;
                irq_h->mask = msi_mask_irq;
@@ -564,7 +597,6 @@ int bus_irq_setup(struct irq_handler *irq_h)
                                printk("No PCI dev for tbdf %p!", irq_h->tbdf);
                                return -1;
                        }
-                       irq_h->dev_private = pcidev;
                        if ((vecno = msi_irq_enable(irq_h, pcidev)) != -1)
                                return vecno;
                        busno = BUSBNO(irq_h->tbdf);
index 503cce3..e897776 100644 (file)
@@ -150,10 +150,21 @@ int pci_msi_enable(struct pci_device *p, uint64_t vec)
 {
        unsigned int c, f, datao;
 
+       if (p->msix_ready) {
+               printk("MSI: MSI-X is already enabled, aborting\n");
+               return -1;
+       }
+       if (p->msi_ready) {
+               /* only allowing one enable of MSI per device (not supporting multiple
+                * vectors) */
+               printk("MSI: MSI is already enabled, aborting\n");
+               return -1;
+       }
+       p->msi_ready = TRUE;
+
        /* Get the offset of the MSI capability
         * in the function's config space.
         */
-
        c = msicap(p);
        if(c == 0)
                return -1;
@@ -247,6 +258,10 @@ static int pci_msix_init(struct pci_device *p)
        int tbl_bir, tbl_off, pba_bir, pba_off;
        struct msix_entry *entry;
 
+       if (p->msi_ready) {
+               printk("MSI-X: MSI is already on, aborting\n");
+               return -1;
+       }
        if (msix_blacklist(p) != 0)
                return -1;
        /* Get the offset of the MSI capability in the function's config space. */
@@ -288,8 +303,11 @@ static int pci_msix_init(struct pci_device *p)
        return 0;
 }
 
-int pci_msix_enable(struct irq_handler *irq_h, struct pci_device *p,
-                    uint64_t vec)
+/* Enables an MSI-X vector for a PCI device.  vec is formatted like an ioapic
+ * route.  This should be able to handle multiple vectors for a device.  Returns
+ * a msix_irq_vector linkage struct on success (the connection btw an irq_h and
+ * the specific {pcidev, entry}), and 0 on failure. */
+struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec)
 {
        int i;
        struct msix_entry *entry;
@@ -299,7 +317,7 @@ int pci_msix_enable(struct irq_handler *irq_h, struct pci_device *p,
        /* TODO: sync protection */
        if (!p->msix_ready) {
                if (pci_msix_init(p) < 0)
-                       return -1;
+                       return 0;
                p->msix_ready = TRUE;
        }
        /* find an unused slot (no apic_vector assigned).  later, we might want to
@@ -309,7 +327,7 @@ int pci_msix_enable(struct irq_handler *irq_h, struct pci_device *p,
                if (!(read_mmreg32((uintptr_t)&entry->data) & 0xff))
                        break;
        if (i == p->msix_nr_vec)
-               return -1;
+               return 0;
        linkage = kmalloc(sizeof(struct msix_irq_vector), KMALLOC_WAIT);
        linkage->pcidev = p;
        linkage->entry = entry;
@@ -319,26 +337,22 @@ int pci_msix_enable(struct irq_handler *irq_h, struct pci_device *p,
        write_mmreg32((uintptr_t)&entry->data, linkage->data);
        write_mmreg32((uintptr_t)&entry->addr_lo, linkage->addr_lo);
        write_mmreg32((uintptr_t)&entry->addr_hi, linkage->addr_hi);
-       irq_h->dev_private = linkage;
-       return 0;
+       return linkage;
 }
 
 /* TODO: should lock in all of these PCI/MSI functions */
-void msi_mask_irq(struct irq_handler *irq_h, int apic_vector)
+void pci_msi_mask(struct pci_device *p)
 {
-       struct pci_device *p = (struct pci_device*)irq_h->dev_private;
        unsigned int c, f;
        c = msicap(p);
-       if (!c)
-               return;
+       assert(c);
 
        f = pcidev_read16(p, c + 2);
        pcidev_write16(p, c + 2, f & ~Msienable);
 }
 
-void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector)
+void pci_msi_unmask(struct pci_device *p)
 {
-       struct pci_device *p = (struct pci_device*)irq_h->dev_private;
        unsigned int c, f;
        c = msicap(p);
        assert(c);
@@ -347,9 +361,8 @@ void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector)
        pcidev_write16(p, c + 2, f | Msienable);
 }
 
-int msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
+int pci_msi_route(struct pci_device *p, int dest)
 {
-       struct pci_device *p = (struct pci_device*)irq_h->dev_private;
        unsigned int c, f;
        c = msicap(p);
        assert(c);
@@ -361,24 +374,20 @@ int msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
        return 0;
 }
 
-void msix_mask_irq(struct irq_handler *irq_h, int apic_vector)
+void pci_msix_mask_vector(struct msix_irq_vector *linkage)
 {
-       struct msix_irq_vector *linkage = irq_h->dev_private;
        msix_mask_entry(linkage->entry);
 }
 
-void msix_unmask_irq(struct irq_handler *irq_h, int apic_vector)
+void pci_msix_unmask_vector(struct msix_irq_vector *linkage)
 {
-       struct msix_irq_vector *linkage = irq_h->dev_private;
        msix_unmask_entry(linkage->entry);
 }
 
-int msix_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
+void pci_msix_route_vector(struct msix_irq_vector *linkage, int dest)
 {
-       struct msix_irq_vector *linkage = irq_h->dev_private;
        /* mask out the old destination, replace with new */
        linkage->addr_lo &= ~(((1 << 8) - 1) << 12);
        linkage->addr_lo |= (dest & 0xff) << 12;
        write_mmreg32((uintptr_t)&linkage->entry->addr_lo, linkage->addr_lo);
-       return 0;
 }
index b39c588..3aed52f 100644 (file)
@@ -332,6 +332,7 @@ struct pci_device {
        uint8_t                                         class;
        uint8_t                                         subclass;
        uint8_t                                         progif;
+       bool                                            msi_ready;
        uint32_t                                        msi_msg_addr_hi;
        uint32_t                                        msi_msg_addr_lo;
        uint32_t                                        msi_msg_data;
@@ -400,19 +401,15 @@ void pci_set_bus_master(struct pci_device *pcidev);
 void pci_clr_bus_master(struct pci_device *pcidev);
 struct pci_device *pci_match_tbdf(int tbdf);
 
-struct irq_handler; /* include loops */
 /* MSI functions, msi.c */
 int pci_msi_enable(struct pci_device *p, uint64_t vec);
-int pci_msix_enable(struct irq_handler *irq_h, struct pci_device *p,
-                    uint64_t vec);
-
-/* MSI irq handler functions, msi.c */
-void msi_mask_irq(struct irq_handler *irq_h, int apic_vector);
-void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector);
-int msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest);
-void msix_mask_irq(struct irq_handler *irq_h, int apic_vector);
-void msix_unmask_irq(struct irq_handler *irq_h, int apic_vector);
-int msix_route_irq(struct irq_handler *irq_h, int apic_vector, int dest);
+struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec);
+void pci_msi_mask(struct pci_device *p);
+void pci_msi_unmask(struct pci_device *p);
+int pci_msi_route(struct pci_device *p, int dest);
+void pci_msix_mask_vector(struct msix_irq_vector *linkage);
+void pci_msix_unmask_vector(struct msix_irq_vector *linkage);
+void pci_msix_route_vector(struct msix_irq_vector *linkage, int dest);
 
 /* TODO: this is quite the Hacke */
 #define explode_tbdf(tbdf) {pcidev.bus = tbdf >> 16;\