Load MSI pages through bus_dma(9). Our interrupt controllers for MSIs
authorpatrick <patrick@openbsd.org>
Mon, 22 Mar 2021 20:30:21 +0000 (20:30 +0000)
committerpatrick <patrick@openbsd.org>
Mon, 22 Mar 2021 20:30:21 +0000 (20:30 +0000)
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
sys/arch/arm64/dev/pci_machdep.c
sys/arch/arm64/dev/pciecam.c
sys/arch/arm64/include/pci_machdep.h
sys/dev/fdt/dwpcie.c

index 08d7553..1d74414 100644 (file)
@@ -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);
 }
 
index 8df6552..efdf2a5 100644 (file)
@@ -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 <kettenis@openbsd.org>
@@ -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;
 }
index c5f0354..91f2797 100644 (file)
@@ -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 <patrick@blueri.se>
  *
@@ -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));
 }
 
 /*
index 4366fd9..eefb6c5 100644 (file)
@@ -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;
index 9d87788..21b5b6b 100644 (file)
@@ -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 <kettenis@openbsd.org>
  *
@@ -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