Added generic backtrace functions to allow backtrace output on buffers
[akaros.git] / kern / arch / x86 / msi.c
index 2188374..292677c 100644 (file)
@@ -247,10 +247,6 @@ static uintptr_t msix_get_capbar_paddr(struct pci_device *p, int offset)
                return 0;
        }
        membar += capbar_off;
-       if (PGOFF(membar)) {
-               printk("MSI-X: unaligned cap membar %p\n", membar);
-               return 0;
-       }
        return membar;
 }
 
@@ -266,6 +262,8 @@ static int __pci_msix_init(struct pci_device *p)
        int tbl_bir, tbl_off, pba_bir, pba_off;
        struct msix_entry *entry;
 
+       if (p->msix_ready)
+               return 0;
        if (p->msi_ready) {
                printk("MSI-X: MSI is already on, aborting\n");
                return -1;
@@ -284,6 +282,8 @@ static int __pci_msix_init(struct pci_device *p)
        p->msix_tbl_paddr = msix_get_capbar_paddr(p, c + 4);
        p->msix_pba_paddr = msix_get_capbar_paddr(p, c + 8);
        if (!p->msix_tbl_paddr || !p->msix_pba_paddr) {
+               /* disable msix, so we can possibly use msi */
+               pcidev_write16(p, c + 2, f & ~Msixenable);
                printk("MSI-X: Missing a tbl (%p) or PBA (%p) paddr!\n",
                       p->msix_tbl_paddr, p->msix_pba_paddr);
                return -1;
@@ -292,28 +292,47 @@ static int __pci_msix_init(struct pci_device *p)
        p->msix_tbl_vaddr = vmap_pmem_nocache(p->msix_tbl_paddr, p->msix_nr_vec *
                                              sizeof(struct msix_entry));
        if (!p->msix_tbl_vaddr) {
+               pcidev_write16(p, c + 2, f & ~Msixenable);
                printk("MSI-X: unable to vmap the Table!\n");
                return -1;
        }
        p->msix_pba_vaddr = vmap_pmem_nocache(p->msix_pba_paddr,
                                              ROUNDUP(p->msix_nr_vec, 8) / 8);
        if (!p->msix_pba_vaddr) {
+               pcidev_write16(p, c + 2, f & ~Msixenable);
                printk("MSI-X: unable to vmap the PBA!\n");
                vunmap_vmem(p->msix_tbl_paddr,
                        p->msix_nr_vec * sizeof(struct msix_entry));
                return -1;
        }
-       /* they should all be masked already, but just in case */
+       /* they should all be masked already, but remasking just in case.  likewise,
+        * we need to 0 out the data, since we'll use the lower byte later when
+        * determining if an msix vector is free or not. */
        entry = (struct msix_entry*)p->msix_tbl_vaddr;
        for (int i = 0; i < p->msix_nr_vec; i++, entry++) {
                __msix_mask_entry(entry);
+               write_mmreg32((uintptr_t)&entry->data, 0);
        }
        /* unmask the device, now that all the vectors are masked */
        f &= ~Msixmask;
        pcidev_write16(p, c + 2, f);
+       p->msix_ready = TRUE;
        return 0;
 }
 
+/* Some parts of msix init need to happen during boot.  Devices can call this
+ * during their reset methods, and then later register their IRQs during attach.
+ * Other OS's also alloc the vector around this time, though we'll hold off on
+ * that for now. */
+int pci_msix_init(struct pci_device *p)
+{
+       int ret;
+       spin_lock_irqsave(&p->lock);
+       ret = __pci_msix_init(p);
+       spin_unlock_irqsave(&p->lock);
+       return ret;
+}
+
 /* 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
@@ -326,12 +345,11 @@ struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec)
        unsigned int c, datao;
 
        spin_lock_irqsave(&p->lock);
-       if (!p->msix_ready) {
-               if (__pci_msix_init(p) < 0) {
-                       spin_unlock_irqsave(&p->lock);
-                       return 0;
-               }
-               p->msix_ready = TRUE;
+       /* Ensure we're init'd.  We could remove this in the future, though not
+        * everyone calls the extern pci_msix_init. */
+       if (__pci_msix_init(p) < 0) {
+               spin_unlock_irqsave(&p->lock);
+               return 0;
        }
        /* find an unused slot (no apic_vector assigned).  later, we might want to
         * point back to the irq_hs for each entry.  not a big deal now. */
@@ -340,6 +358,7 @@ struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec)
                if (!(read_mmreg32((uintptr_t)&entry->data) & 0xff))
                        break;
        if (i == p->msix_nr_vec) {
+               printk("[kernel] unable to alloc an MSI-X vector (bug?)\n");
                spin_unlock_irqsave(&p->lock);
                return 0;
        }
@@ -356,6 +375,18 @@ struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec)
        return linkage;
 }
 
+void pci_dump_msix_table(struct pci_device *p)
+{
+       struct msix_entry *entry;
+       void *tbl = (void*)p->msix_tbl_vaddr;
+
+       hexdump(tbl, p->msix_nr_vec * sizeof(struct msix_entry));
+       entry = (struct msix_entry*)p->msix_tbl_vaddr;
+       for (int i = 0; i < p->msix_nr_vec; i++, entry++)
+               printk("Entry %d, addr hi:lo 0x%08x:%08x data 0x%08x\n", i,
+                      entry->addr_hi, entry->addr_lo, entry->data);
+}
+
 void pci_msi_mask(struct pci_device *p)
 {
        unsigned int c, f;