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, ®) == 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, ®) == 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_ */