x86: use setters/getters for MSR_{FS,GS}_BASE
[akaros.git] / kern / arch / x86 / msi.c
index e897776..292677c 100644 (file)
@@ -150,24 +150,27 @@ int pci_msi_enable(struct pci_device *p, uint64_t vec)
 {
        unsigned int c, f, datao;
 
+       spin_lock_irqsave(&p->lock);
        if (p->msix_ready) {
                printk("MSI: MSI-X is already enabled, aborting\n");
+               spin_unlock_irqsave(&p->lock);
                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");
+               spin_unlock_irqsave(&p->lock);
                return -1;
        }
        p->msi_ready = TRUE;
 
-       /* Get the offset of the MSI capability
-        * in the function's config space.
-        */
+       /* Get the offset of the MSI capability in the function's config space. */
        c = msicap(p);
-       if(c == 0)
+       if (!c) {
+               spin_unlock_irqsave(&p->lock);
                return -1;
+       }
 
        /* read it, clear out the Mmesgmsk bits. 
         * This means that there will be no multiple
@@ -175,8 +178,10 @@ int pci_msi_enable(struct pci_device *p, uint64_t vec)
         */
        f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
 
-       if (msi_blacklist(p) != 0)
+       if (msi_blacklist(p) != 0) {
+               spin_unlock_irqsave(&p->lock);
                return -1;
+       }
 
        /* Data begins at 8 bytes in. */
        datao = 8;
@@ -211,16 +216,17 @@ int pci_msi_enable(struct pci_device *p, uint64_t vec)
         * we can mask on non-Vmask-supported HW. */
        printd("write @ %d %04lx\n",c + 2, f);
        pcidev_write16(p, c + 2, f);
+       spin_unlock_irqsave(&p->lock);
        return 0;
 }
 
-static void msix_mask_entry(struct msix_entry *entry)
+static void __msix_mask_entry(struct msix_entry *entry)
 {
        uintptr_t reg = (uintptr_t)&entry->vector;
        write_mmreg32(reg, read_mmreg32(reg) | 0x1);
 }
 
-static void msix_unmask_entry(struct msix_entry *entry)
+static void __msix_unmask_entry(struct msix_entry *entry)
 {
        uintptr_t reg = (uintptr_t)&entry->vector;
        write_mmreg32(reg, read_mmreg32(reg) & ~0x1);
@@ -241,23 +247,23 @@ 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;
 }
 
 /* One time initialization of MSI-X for a PCI device.  -1 on error.  Otherwise,
  * the device will be ready to assign/route MSI-X entries/vectors.  All vectors
- * are masked, but the overall MSI-X function is unmasked. */
-static int pci_msix_init(struct pci_device *p)
+ * are masked, but the overall MSI-X function is unmasked.
+ *
+ * Hold the pci_device lock. */
+static int __pci_msix_init(struct pci_device *p)
 {
        unsigned int c;
        uint16_t f;
        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;
@@ -275,34 +281,58 @@ 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)
+       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;
+       }
        p->msix_nr_vec = (f & Msixtblsize) + 1;
        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);
+               __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
@@ -314,11 +344,12 @@ struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec)
        struct msix_irq_vector *linkage;
        unsigned int c, datao;
 
-       /* TODO: sync protection */
-       if (!p->msix_ready) {
-               if (pci_msix_init(p) < 0)
-                       return 0;
-               p->msix_ready = TRUE;
+       spin_lock_irqsave(&p->lock);
+       /* 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. */
@@ -326,8 +357,11 @@ struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec)
        for (i = 0; i < p->msix_nr_vec; i++, entry++)
                if (!(read_mmreg32((uintptr_t)&entry->data) & 0xff))
                        break;
-       if (i == p->msix_nr_vec)
+       if (i == p->msix_nr_vec) {
+               printk("[kernel] unable to alloc an MSI-X vector (bug?)\n");
+               spin_unlock_irqsave(&p->lock);
                return 0;
+       }
        linkage = kmalloc(sizeof(struct msix_irq_vector), KMALLOC_WAIT);
        linkage->pcidev = p;
        linkage->entry = entry;
@@ -337,18 +371,32 @@ struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec)
        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);
+       spin_unlock_irqsave(&p->lock);
        return linkage;
 }
 
-/* TODO: should lock in all of these PCI/MSI functions */
+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;
        c = msicap(p);
        assert(c);
 
+       spin_lock_irqsave(&p->lock);
        f = pcidev_read16(p, c + 2);
        pcidev_write16(p, c + 2, f & ~Msienable);
+       spin_unlock_irqsave(&p->lock);
 }
 
 void pci_msi_unmask(struct pci_device *p)
@@ -357,37 +405,46 @@ void pci_msi_unmask(struct pci_device *p)
        c = msicap(p);
        assert(c);
 
+       spin_lock_irqsave(&p->lock);
        f = pcidev_read16(p, c + 2);
        pcidev_write16(p, c + 2, f | Msienable);
+       spin_unlock_irqsave(&p->lock);
 }
 
-int pci_msi_route(struct pci_device *p, int dest)
+void pci_msi_route(struct pci_device *p, int dest)
 {
        unsigned int c, f;
        c = msicap(p);
        assert(c);
 
+       spin_lock_irqsave(&p->lock);
        /* mask out the old destination, replace with new */
        p->msi_msg_addr_lo &= ~(((1 << 8) - 1) << 12);
        p->msi_msg_addr_lo |= (dest & 0xff) << 12;
        pcidev_write32(p, c + 4, p->msi_msg_addr_lo);
-       return 0;
+       spin_unlock_irqsave(&p->lock);
 }
 
 void pci_msix_mask_vector(struct msix_irq_vector *linkage)
 {
-       msix_mask_entry(linkage->entry);
+       spin_lock_irqsave(&linkage->pcidev->lock);
+       __msix_mask_entry(linkage->entry);
+       spin_unlock_irqsave(&linkage->pcidev->lock);
 }
 
 void pci_msix_unmask_vector(struct msix_irq_vector *linkage)
 {
-       msix_unmask_entry(linkage->entry);
+       spin_lock_irqsave(&linkage->pcidev->lock);
+       __msix_unmask_entry(linkage->entry);
+       spin_unlock_irqsave(&linkage->pcidev->lock);
 }
 
 void pci_msix_route_vector(struct msix_irq_vector *linkage, int dest)
 {
+       spin_lock_irqsave(&linkage->pcidev->lock);
        /* 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);
+       spin_unlock_irqsave(&linkage->pcidev->lock);
 }