From cc0ede0677b0110958241fb25b9e05031b91a387 Mon Sep 17 00:00:00 2001 From: patrick Date: Mon, 22 Mar 2021 20:30:21 +0000 Subject: [PATCH] Load MSI pages through bus_dma(9). Our interrupt controllers for MSIs typically pass the physical address, however retrieved, to our PCIe controller code. This physical address can in practise be directly given to the PCIe, but it is not a given that the CPU and the PCIe controller are able to use the same physical addresses. This is even more obvious with an smmu(4) inbetween, which can change the world view by introducing I/O virtual addresses. Hence for this it is indeed necessary to map those pages, which thanks to integration with bus_dma(9) works easily. For this we remember the PCI devices' DMA tag in the interrupt handle during the MSI map, so that we can use the smmu(4)-hooked DMA tag to load the physical address. While some systems might prefer to implement "trapping" pages for MSIs, to make sure devices cannot trigger other devices' interrupts, we only make sure the whole page is mapped. Having the IOMMU create a mapping for each MSI is a bit wasteful, but for now it's the simplest way to implement it. Discussed with and ok kettenis@ --- sys/arch/arm64/dev/acpipci.c | 54 ++++++++++++++++++++------ sys/arch/arm64/dev/pci_machdep.c | 4 +- sys/arch/arm64/dev/pciecam.c | 57 ++++++++++++++++++++++++++-- sys/arch/arm64/include/pci_machdep.h | 3 +- sys/dev/fdt/dwpcie.c | 57 ++++++++++++++++++++++++++-- 5 files changed, 153 insertions(+), 22 deletions(-) diff --git a/sys/arch/arm64/dev/acpipci.c b/sys/arch/arm64/dev/acpipci.c index 08d7553d7ae..1d744147c43 100644 --- a/sys/arch/arm64/dev/acpipci.c +++ b/sys/arch/arm64/dev/acpipci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpipci.c,v 1.27 2021/03/15 22:48:57 patrick Exp $ */ +/* $OpenBSD: acpipci.c,v 1.28 2021/03/22 20:30:21 patrick Exp $ */ /* * Copyright (c) 2018 Mark Kettenis * @@ -79,6 +79,12 @@ struct acpipci_softc { uint32_t sc_seg; }; +struct acpipci_intr_handle { + struct arm_intr_handle aih_ih; + bus_dma_tag_t aih_dmat; + bus_dmamap_t aih_map; +}; + int acpipci_match(struct device *, void *, void *); void acpipci_attach(struct device *, struct device *, void *); @@ -516,7 +522,8 @@ acpipci_intr_establish(void *v, pci_intr_handle_t ih, int level, { struct acpipci_softc *sc = v; struct interrupt_controller *ic; - struct arm_intr_handle *aih; + struct acpipci_intr_handle *aih; + bus_dma_segment_t seg; void *cookie; extern LIST_HEAD(, interrupt_controller) interrupt_controllers; @@ -539,17 +546,37 @@ acpipci_intr_establish(void *v, pci_intr_handle_t ih, int level, if (cookie == NULL) return NULL; - /* TODO: translate address to the PCI device's view */ + aih = malloc(sizeof(*aih), M_DEVBUF, M_WAITOK); + aih->aih_ih.ih_ic = ic; + aih->aih_ih.ih_ih = cookie; + aih->aih_dmat = ih.ih_dmat; + + if (bus_dmamap_create(aih->aih_dmat, sizeof(uint32_t), 1, + sizeof(uint32_t), 0, BUS_DMA_WAITOK, &aih->aih_map)) { + free(aih, M_DEVBUF, sizeof(*aih)); + ic->ic_disestablish(cookie); + return NULL; + } + + memset(&seg, 0, sizeof(seg)); + seg.ds_addr = addr; + seg.ds_len = sizeof(uint32_t); + if (bus_dmamap_load_raw(aih->aih_dmat, aih->aih_map, + &seg, 1, sizeof(uint32_t), BUS_DMA_WAITOK)) { + bus_dmamap_destroy(aih->aih_dmat, aih->aih_map); + free(aih, M_DEVBUF, sizeof(*aih)); + ic->ic_disestablish(cookie); + return NULL; + } + + addr = aih->aih_map->dm_segs[0].ds_addr; if (ih.ih_type == PCI_MSIX) { pci_msix_enable(ih.ih_pc, ih.ih_tag, &sc->sc_bus_memt, ih.ih_intrpin, addr, data); } else pci_msi_enable(ih.ih_pc, ih.ih_tag, addr, data); - aih = malloc(sizeof(*aih), M_DEVBUF, M_WAITOK); - aih->ih_ic = ic; - aih->ih_ih = cookie; cookie = aih; } else { if (ci != NULL && !CPU_IS_PRIMARY(ci)) @@ -564,12 +591,15 @@ acpipci_intr_establish(void *v, pci_intr_handle_t ih, int level, void acpipci_intr_disestablish(void *v, void *cookie) { - struct arm_intr_handle *aih = cookie; - struct interrupt_controller *ic = aih->ih_ic; - - if (ic->ic_establish_msi) - ic->ic_disestablish(aih->ih_ih); - else + struct acpipci_intr_handle *aih = cookie; + struct interrupt_controller *ic = aih->aih_ih.ih_ic; + + if (ic->ic_establish_msi) { + ic->ic_disestablish(aih->aih_ih.ih_ih); + bus_dmamap_unload(aih->aih_dmat, aih->aih_map); + bus_dmamap_destroy(aih->aih_dmat, aih->aih_map); + free(aih, M_DEVBUF, sizeof(*aih)); + } else acpi_intr_disestablish(cookie); } diff --git a/sys/arch/arm64/dev/pci_machdep.c b/sys/arch/arm64/dev/pci_machdep.c index 8df655247b4..efdf2a57002 100644 --- a/sys/arch/arm64/dev/pci_machdep.c +++ b/sys/arch/arm64/dev/pci_machdep.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pci_machdep.c,v 1.4 2019/06/25 16:46:32 kettenis Exp $ */ +/* $OpenBSD: pci_machdep.c,v 1.5 2021/03/22 20:30:21 patrick Exp $ */ /* * Copyright (c) 2019 Mark Kettenis @@ -129,6 +129,7 @@ _pci_intr_map_msi(struct pci_attach_args *pa, pci_intr_handle_t *ihp) ihp->ih_pc = pa->pa_pc; ihp->ih_tag = pa->pa_tag; ihp->ih_type = PCI_MSI; + ihp->ih_dmat = pa->pa_dmat; return 0; } @@ -159,6 +160,7 @@ _pci_intr_map_msix(struct pci_attach_args *pa, int vec, ihp->ih_tag = pa->pa_tag; ihp->ih_intrpin = vec; ihp->ih_type = PCI_MSIX; + ihp->ih_dmat = pa->pa_dmat; return 0; } diff --git a/sys/arch/arm64/dev/pciecam.c b/sys/arch/arm64/dev/pciecam.c index c5f03544cdd..91f2797b9ee 100644 --- a/sys/arch/arm64/dev/pciecam.c +++ b/sys/arch/arm64/dev/pciecam.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pciecam.c,v 1.13 2021/02/28 21:06:58 patrick Exp $ */ +/* $OpenBSD: pciecam.c,v 1.14 2021/03/22 20:30:21 patrick Exp $ */ /* * Copyright (c) 2013,2017 Patrick Wildt * @@ -92,6 +92,12 @@ struct pciecam_softc { struct arm64_pci_chipset sc_pc; }; +struct pciecam_intr_handle { + struct arm_intr_handle pih_ih; + bus_dma_tag_t pih_dmat; + bus_dmamap_t pih_map; +}; + int pciecam_match(struct device *, void *, void *); void pciecam_attach(struct device *, struct device *, void *); void pciecam_attach_hook(struct device *, struct device *, struct pcibus_attach_args *); @@ -110,6 +116,10 @@ void pciecam_intr_disestablish(void *, void *); int pciecam_bs_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, bus_space_handle_t *); paddr_t pciecam_bs_mmap(bus_space_tag_t, bus_addr_t, off_t, int, int); +struct interrupt_controller pciecam_ic = { + .ic_barrier = intr_barrier +}; + struct cfattach pciecam_ca = { sizeof (struct pciecam_softc), pciecam_match, pciecam_attach }; @@ -363,6 +373,8 @@ pciecam_intr_establish(void *self, pci_intr_handle_t ih, int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name) { struct pciecam_softc *sc = (struct pciecam_softc *)self; + struct pciecam_intr_handle *pih; + bus_dma_segment_t seg; void *cookie; KASSERT(ih.ih_type != PCI_NONE); @@ -377,8 +389,31 @@ pciecam_intr_establish(void *self, pci_intr_handle_t ih, int level, if (cookie == NULL) return NULL; - /* TODO: translate address to the PCI device's view */ + pih = malloc(sizeof(*pih), M_DEVBUF, M_WAITOK); + pih->pih_ih.ih_ic = &pciecam_ic; + pih->pih_ih.ih_ih = cookie; + pih->pih_dmat = ih.ih_dmat; + + if (bus_dmamap_create(pih->pih_dmat, sizeof(uint32_t), 1, + sizeof(uint32_t), 0, BUS_DMA_WAITOK, &pih->pih_map)) { + free(pih, M_DEVBUF, sizeof(*pih)); + fdt_intr_disestablish(cookie); + return NULL; + } + + memset(&seg, 0, sizeof(seg)); + seg.ds_addr = addr; + seg.ds_len = sizeof(uint32_t); + if (bus_dmamap_load_raw(pih->pih_dmat, pih->pih_map, + &seg, 1, sizeof(uint32_t), BUS_DMA_WAITOK)) { + bus_dmamap_destroy(pih->pih_dmat, pih->pih_map); + free(pih, M_DEVBUF, sizeof(*pih)); + fdt_intr_disestablish(cookie); + return NULL; + } + + addr = pih->pih_map->dm_segs[0].ds_addr; if (ih.ih_type == PCI_MSIX) { pci_msix_enable(ih.ih_pc, ih.ih_tag, &sc->sc_bus, ih.ih_intrpin, addr, data); @@ -396,15 +431,29 @@ pciecam_intr_establish(void *self, pci_intr_handle_t ih, int level, cookie = fdt_intr_establish_imap_cpu(sc->sc_node, reg, sizeof(reg), level, ci, func, arg, name); + if (cookie == NULL) + return NULL; + + pih = malloc(sizeof(*pih), M_DEVBUF, M_WAITOK); + pih->pih_ih.ih_ic = &pciecam_ic; + pih->pih_ih.ih_ih = cookie; + pih->pih_dmat = NULL; } - return cookie; + return pih; } void pciecam_intr_disestablish(void *sc, void *cookie) { - /* do something */ + struct pciecam_intr_handle *pih = cookie; + + fdt_intr_disestablish(pih->pih_ih.ih_ih); + if (pih->pih_dmat) { + bus_dmamap_unload(pih->pih_dmat, pih->pih_map); + bus_dmamap_destroy(pih->pih_dmat, pih->pih_map); + } + free(pih, M_DEVBUF, sizeof(*pih)); } /* diff --git a/sys/arch/arm64/include/pci_machdep.h b/sys/arch/arm64/include/pci_machdep.h index 4366fd97ca1..eefb6c581cc 100644 --- a/sys/arch/arm64/include/pci_machdep.h +++ b/sys/arch/arm64/include/pci_machdep.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pci_machdep.h,v 1.8 2021/02/25 23:07:48 patrick Exp $ */ +/* $OpenBSD: pci_machdep.h,v 1.9 2021/03/22 20:30:21 patrick Exp $ */ /* * Copyright (c) 2003-2004 Opsycon AB (www.opsycon.se / www.opsycon.com) @@ -40,6 +40,7 @@ typedef struct { pcitag_t ih_tag; int ih_intrpin; int ih_type; + bus_dma_tag_t ih_dmat; } pci_intr_handle_t; struct pci_attach_args; diff --git a/sys/dev/fdt/dwpcie.c b/sys/dev/fdt/dwpcie.c index 9d87788a8f5..21b5b6bf9de 100644 --- a/sys/dev/fdt/dwpcie.c +++ b/sys/dev/fdt/dwpcie.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dwpcie.c,v 1.27 2021/03/01 21:03:24 patrick Exp $ */ +/* $OpenBSD: dwpcie.c,v 1.28 2021/03/22 20:30:21 patrick Exp $ */ /* * Copyright (c) 2018 Mark Kettenis * @@ -225,6 +225,12 @@ struct dwpcie_softc { void *sc_ih; }; +struct dwpcie_intr_handle { + struct arm_intr_handle pih_ih; + bus_dma_tag_t pih_dmat; + bus_dmamap_t pih_map; +}; + int dwpcie_match(struct device *, void *, void *); void dwpcie_attach(struct device *, struct device *, void *); @@ -285,6 +291,10 @@ int dwpcie_bs_iomap(bus_space_tag_t, bus_addr_t, bus_size_t, int, int dwpcie_bs_memmap(bus_space_tag_t, bus_addr_t, bus_size_t, int, bus_space_handle_t *); +struct interrupt_controller dwpcie_ic = { + .ic_barrier = intr_barrier +}; + void dwpcie_attach(struct device *parent, struct device *self, void *aux) { @@ -1195,6 +1205,8 @@ dwpcie_intr_establish(void *v, pci_intr_handle_t ih, int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name) { struct dwpcie_softc *sc = v; + struct dwpcie_intr_handle *pih; + bus_dma_segment_t seg; void *cookie; KASSERT(ih.ih_type != PCI_NONE); @@ -1209,8 +1221,31 @@ dwpcie_intr_establish(void *v, pci_intr_handle_t ih, int level, if (cookie == NULL) return NULL; - /* TODO: translate address to the PCI device's view */ + pih = malloc(sizeof(*pih), M_DEVBUF, M_WAITOK); + pih->pih_ih.ih_ic = &dwpcie_ic; + pih->pih_ih.ih_ih = cookie; + pih->pih_dmat = ih.ih_dmat; + + if (bus_dmamap_create(pih->pih_dmat, sizeof(uint32_t), 1, + sizeof(uint32_t), 0, BUS_DMA_WAITOK, &pih->pih_map)) { + free(pih, M_DEVBUF, sizeof(*pih)); + fdt_intr_disestablish(cookie); + return NULL; + } + + memset(&seg, 0, sizeof(seg)); + seg.ds_addr = addr; + seg.ds_len = sizeof(uint32_t); + if (bus_dmamap_load_raw(pih->pih_dmat, pih->pih_map, + &seg, 1, sizeof(uint32_t), BUS_DMA_WAITOK)) { + bus_dmamap_destroy(pih->pih_dmat, pih->pih_map); + free(pih, M_DEVBUF, sizeof(*pih)); + fdt_intr_disestablish(cookie); + return NULL; + } + + addr = pih->pih_map->dm_segs[0].ds_addr; if (ih.ih_type == PCI_MSIX) { pci_msix_enable(ih.ih_pc, ih.ih_tag, &sc->sc_bus_memt, ih.ih_intrpin, addr, data); @@ -1228,15 +1263,29 @@ dwpcie_intr_establish(void *v, pci_intr_handle_t ih, int level, cookie = fdt_intr_establish_imap_cpu(sc->sc_node, reg, sizeof(reg), level, ci, func, arg, name); + if (cookie == NULL) + return NULL; + + pih = malloc(sizeof(*pih), M_DEVBUF, M_WAITOK); + pih->pih_ih.ih_ic = &dwpcie_ic; + pih->pih_ih.ih_ih = cookie; + pih->pih_dmat = NULL; } - return cookie; + return pih; } void dwpcie_intr_disestablish(void *v, void *cookie) { - panic("%s", __func__); + struct dwpcie_intr_handle *pih = cookie; + + fdt_intr_disestablish(pih->pih_ih.ih_ih); + if (pih->pih_dmat) { + bus_dmamap_unload(pih->pih_dmat, pih->pih_map); + bus_dmamap_destroy(pih->pih_dmat, pih->pih_map); + } + free(pih, M_DEVBUF, sizeof(*pih)); } int -- 2.20.1