On Fri, May 31, 2019 at 07:35:41AM +1000, Jonathan Matthew wrote:
> I had wondered about the MAU32 define when I was looking at MSI-X
> suspend/resume.

I have also taken a try at MSI-X suspend/resume in the last few weeks.
The diff below is what I have come up with. It worked with nvme on a
laptop and on qemu (but I have not tested it since rebasing on kettenis'
32bit fix).

I have also seen that arm64 got MSI-X in the meantime. That needs to be
adjusted, too.

Cheers,
Stefan


index 8a456f72b09..0b99fe8660d 100644
--- a/sys/arch/amd64/include/pci_machdep.h
+++ b/sys/arch/amd64/include/pci_machdep.h
@@ -98,6 +98,8 @@ void          pci_set_powerstate_md(pci_chipset_tag_t, 
pcitag_t, int, int);
 void           pci_mcfg_init(bus_space_tag_t, bus_addr_t, int, int, int);
 pci_chipset_tag_t pci_lookup_segment(int);
 
+#define __HAVE_PCI_MSIX
+
 /*
  * ALL OF THE FOLLOWING ARE MACHINE-DEPENDENT, AND SHOULD NOT BE USED
  * BY PORTABLE CODE.
diff --git a/sys/arch/amd64/pci/pci_machdep.c b/sys/arch/amd64/pci/pci_machdep.c
index 976ef2d3b6b..4dd95aedd21 100644
--- a/sys/arch/amd64/pci/pci_machdep.c
+++ b/sys/arch/amd64/pci/pci_machdep.c
@@ -446,81 +446,85 @@ msix_hwunmask(struct pic *pic, int pin)
 {
 }
 
+int
+pci_msix_table_map(struct pci_msix_table_info *ti)
+{
+       int bir;
+
+       if (!pci_get_capability(ti->pc, ti->tag, PCI_CAP_MSIX, &ti->cap_offset,
+           &ti->cap_reg)) {
+               return 0;
+       }
+       ti->memt = X86_BUS_SPACE_MEM; /* XXX */
+       ti->table = pci_conf_read(ti->pc, ti->tag,
+           ti->cap_offset + PCI_MSIX_TABLE);
+       bir = (ti->table & PCI_MSIX_TABLE_BIR);
+
+       ti->table_offset = (ti->table & PCI_MSIX_TABLE_OFF);
+       ti->table_size = PCI_MSIX_MC_TBLSZ(ti->cap_reg) + 1;
+
+       bir = PCI_MAPREG_START + bir * 4;
+       if (pci_mem_find(ti->pc, ti->tag, bir, &ti->base, NULL, NULL) ||
+           _bus_space_map(ti->memt, ti->base + ti->table_offset,
+           ti->table_size * 16, 0, &ti->memh)) {
+               panic("%s: cannot map registers", __func__);
+       }
+       return 1;
+}
+
+void
+pci_msix_table_unmap(struct pci_msix_table_info *ti)
+{
+       _bus_space_unmap(ti->memt, ti->memh, ti->table_size * 16, NULL);
+}
+
 void
 msix_addroute(struct pic *pic, struct cpu_info *ci, int pin, int vec, int type)
 {
-       pci_chipset_tag_t pc = NULL; /* XXX */
-       bus_space_tag_t memt = X86_BUS_SPACE_MEM; /* XXX */
-       bus_space_handle_t memh;
-       bus_addr_t base;
-       pcitag_t tag = PCI_MSIX_TAG(pin);
+       struct pci_msix_table_info ti = {
+               .pc = NULL, /* XXX */
+               .tag = PCI_MSIX_TAG(pin),
+       };
        int entry = PCI_MSIX_VEC(pin);
-       pcireg_t reg, addr, table;
+       pcireg_t addr;
        uint32_t ctrl;
-       int bir, offset;
-       int off, tblsz;
 
-       if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, &reg) == 0)
+       if (!pci_msix_table_map(&ti))
                panic("%s: no msix capability", __func__);
 
        addr = 0xfee00000UL | (ci->ci_apicid << 12);
 
-       table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE);
-       bir = (table & PCI_MSIX_TABLE_BIR);
-       offset = (table & PCI_MSIX_TABLE_OFF);
-       tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
-
-       bir = PCI_MAPREG_START + bir * 4;
-       if (pci_mem_find(pc, tag, bir, &base, NULL, NULL) ||
-           _bus_space_map(memt, base + offset, tblsz * 16, 0, &memh))
-               panic("%s: cannot map registers", __func__);
-
-       bus_space_write_4(memt, memh, PCI_MSIX_MA(entry), addr);
-       bus_space_write_4(memt, memh, PCI_MSIX_MAU32(entry), 0);
-       bus_space_write_4(memt, memh, PCI_MSIX_MD(entry), vec);
-       bus_space_barrier(memt, memh, PCI_MSIX_MA(entry), 16,
+       bus_space_write_4(ti.memt, ti.memh, PCI_MSIX_MA(entry), addr);
+       bus_space_write_4(ti.memt, ti.memh, PCI_MSIX_MAU32(entry), 0);
+       bus_space_write_4(ti.memt, ti.memh, PCI_MSIX_MD(entry), vec);
+       bus_space_barrier(ti.memt, ti.memh, PCI_MSIX_MA(entry), 16,
            BUS_SPACE_BARRIER_WRITE);
-       ctrl = bus_space_read_4(memt, memh, PCI_MSIX_VC(entry));
-       bus_space_write_4(memt, memh, PCI_MSIX_VC(entry),
+       ctrl = bus_space_read_4(ti.memt, ti.memh, PCI_MSIX_VC(entry));
+       bus_space_write_4(ti.memt, ti.memh, PCI_MSIX_VC(entry),
            ctrl & ~PCI_MSIX_VC_MASK);
-
-       _bus_space_unmap(memt, memh, tblsz * 16, NULL);
-
-       pci_conf_write(pc, tag, off, reg | PCI_MSIX_MC_MSIXE);
+       pci_conf_write(ti.pc, ti.tag, ti.cap_offset,
+           ti.cap_reg | PCI_MSIX_MC_MSIXE);
+       pci_msix_table_unmap(&ti);
 }
 
 void
 msix_delroute(struct pic *pic, struct cpu_info *ci, int pin, int vec, int type)
 {
-       pci_chipset_tag_t pc = NULL; /* XXX */
-       bus_space_tag_t memt = X86_BUS_SPACE_MEM; /* XXX */
-       bus_space_handle_t memh;
-       bus_addr_t base;
-       pcitag_t tag = PCI_MSIX_TAG(pin);
+       struct pci_msix_table_info ti = {
+               .pc = NULL, /* XXX */
+               .tag = PCI_MSIX_TAG(pin),
+       };
        int entry = PCI_MSIX_VEC(pin);
-       pcireg_t reg, table;
        uint32_t ctrl;
-       int bir, offset;
-       int off, tblsz;
 
-       if (pci_get_capability(pc, tag, PCI_CAP_MSIX, &off, &reg) == 0)
+       if (!pci_msix_table_map(&ti))
                return;
 
-       table = pci_conf_read(pc, tag, off + PCI_MSIX_TABLE);
-       bir = (table & PCI_MSIX_TABLE_BIR);
-       offset = (table & PCI_MSIX_TABLE_OFF);
-       tblsz = PCI_MSIX_MC_TBLSZ(reg) + 1;
-
-       bir = PCI_MAPREG_START + bir * 4;
-       if (pci_mem_find(pc, tag, bir, &base, NULL, NULL) ||
-           _bus_space_map(memt, base + offset, tblsz * 16, 0, &memh))
-               panic("%s: cannot map registers", __func__);
-
-       ctrl = bus_space_read_4(memt, memh, PCI_MSIX_VC(entry));
-       bus_space_write_4(memt, memh, PCI_MSIX_VC(entry),
+       ctrl = bus_space_read_4(ti.memt, ti.memh, PCI_MSIX_VC(entry));
+       bus_space_write_4(ti.memt, ti.memh, PCI_MSIX_VC(entry),
            ctrl | PCI_MSIX_VC_MASK);
 
-       _bus_space_unmap(memt, memh, tblsz * 16, NULL);
+       pci_msix_table_unmap(&ti);
 }
 
 int
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
index 1ece6e3526d..dccf0a9ca39 100644
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -68,6 +68,9 @@ struct pci_dev {
        pcireg_t pd_msi_ma;
        pcireg_t pd_msi_mau32;
        pcireg_t pd_msi_md;
+#ifdef __HAVE_PCI_MSIX
+       void *pd_msix;
+#endif
        int pd_pmcsr_state;
        int pd_vga_decode;
 };
@@ -271,6 +274,9 @@ pci_suspend(struct pci_softc *sc)
                        }
                        pd->pd_msi_mc = reg;
                }
+#ifdef __HAVE_PCI_MSIX
+               pd->pd_msix = pci_intr_suspend_msix(sc->sc_pc, pd->pd_tag);
+#endif
        }
 }
 
@@ -354,6 +360,10 @@ pci_resume(struct pci_softc *sc)
                        pci_conf_write(sc->sc_pc, pd->pd_tag,
                            off + PCI_MSI_MC, pd->pd_msi_mc);
                }
+#ifdef __HAVE_PCI_MSIX
+               pci_intr_resume_msix(sc->sc_pc, pd->pd_tag, pd->pd_msix);
+               pd->pd_msix = NULL;
+#endif
        }
 }
 
@@ -1509,3 +1519,74 @@ pci_primary_vga(struct pci_attach_args *pa)
 
        return (1);
 }
+
+#ifdef __HAVE_PCI_MSIX
+struct msix_table_entry {
+       uint32_t addr_hi;
+       uint32_t addr_lo;
+       uint32_t data;
+       uint32_t vc;
+};
+
+void *
+pci_intr_suspend_msix(pci_chipset_tag_t pc, pcitag_t tag)
+{
+       struct pci_msix_table_info ti = {
+               .pc = pc,
+               .tag = tag,
+       };
+       struct msix_table_entry *p;
+
+       if (!pci_msix_table_map(&ti))
+               return NULL;
+
+       KASSERT(ti.table_size <= 2048);
+       p = malloc((ti.table_size + 1) * sizeof(*p), M_DEVBUF, M_NOWAIT);
+       KASSERT(p != NULL);
+
+       for (int i = 0; i < ti.table_size; i++) {
+               p[i].addr_lo = bus_space_read_4(ti.memt, ti.memh,
+                   PCI_MSIX_MA(i));
+               p[i].addr_hi = bus_space_read_4(ti.memt, ti.memh,
+                   PCI_MSIX_MA(i) + 4);
+               p[i].data = bus_space_read_4(ti.memt, ti.memh, PCI_MSIX_MD(i));
+               p[i].vc = bus_space_read_4(ti.memt, ti.memh, PCI_MSIX_VC(i));
+       }
+       p[ti.table_size].data = ti.cap_reg;
+
+       pci_msix_table_unmap(&ti);
+       return p;
+}
+
+void
+pci_intr_resume_msix(pci_chipset_tag_t pc, pcitag_t tag, void *saveptr)
+{
+       struct pci_msix_table_info ti = {
+               .pc = pc,
+               .tag = tag,
+       };
+       struct msix_table_entry *p = saveptr;
+
+       if (p == NULL)
+               return;
+       if (!pci_msix_table_map(&ti))
+               return;
+
+       KASSERT(ti.table_size <= 2048);
+       for (int i = 0; i < ti.table_size; i++) {
+               bus_space_write_4(ti.memt, ti.memh, PCI_MSIX_MA(i),
+                   p[i].addr_lo);
+               bus_space_write_4(ti.memt, ti.memh, PCI_MSIX_MA(i) + 4,
+                   p[i].addr_hi);
+               bus_space_write_4(ti.memt, ti.memh, PCI_MSIX_MD(i), p[i].data);
+               bus_space_barrier(ti.memt, ti.memh, PCI_MSIX_MA(i), 16,
+                   BUS_SPACE_BARRIER_WRITE);
+               bus_space_write_4(ti.memt, ti.memh, PCI_MSIX_VC(i), p[i].vc);
+               bus_space_barrier(ti.memt, ti.memh, PCI_MSIX_VC(i), 4,
+                   BUS_SPACE_BARRIER_WRITE);
+       }
+       pci_conf_write(ti.pc, ti.tag, ti.cap_offset, p[ti.table_size].data);
+       free(p, M_DEVBUF, (ti.table_size + 1) * sizeof(*p));
+       pci_msix_table_unmap(&ti);
+}
+#endif /* __HAVE_PCI_MSIX */
diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h
index a0858204b77..6f54ba44a65 100644
--- a/sys/dev/pci/pcivar.h
+++ b/sys/dev/pci/pcivar.h
@@ -269,5 +269,26 @@ const struct pci_quirkdata *
        pci_lookup_quirkdata(pci_vendor_id_t, pci_product_id_t);
 void   pciagp_set_pchb(struct pci_attach_args *);
 
+#ifdef __HAVE_PCI_MSIX
+void *pci_intr_suspend_msix(pci_chipset_tag_t pc, pcitag_t tag);
+void pci_intr_resume_msix(pci_chipset_tag_t pc, pcitag_t tag, void *saveptr);
+struct pci_msix_table_info {
+       pci_chipset_tag_t pc;
+       pcitag_t tag;
+       bus_space_tag_t memt;
+       bus_space_handle_t memh;
+       bus_addr_t base;
+       pcireg_t cap_reg;
+       int cap_offset;
+       pcireg_t table;
+       int table_offset;
+       int table_size;
+};
+/* These must be provided by MD code */
+int pci_msix_table_map(struct pci_msix_table_info *ti);
+void pci_msix_table_unmap(struct pci_msix_table_info *ti);
+#endif /* __HAVE_PCI_MSIX */
+
+
 #endif /* _KERNEL */
 #endif /* _DEV_PCI_PCIVAR_H_ */

Reply via email to