From 415019cefb419ee80cf82fd1b09b724a4fddde9b Mon Sep 17 00:00:00 2001 From: patrick Date: Fri, 25 Jun 2021 17:41:22 +0000 Subject: [PATCH] While it seems like we can choose any I/O virtual address for peripheral devices, this isn't really the case. It depends on the bus topology of how devices are connected. In the case of PCIe, devices are assigned addresses (in PCI BARs) from the PCI address spaces. Now if we take an address from one of these address spaces for our IOVA, transfers from from a PCI device to that address will terminate inside of the PCI bus. This is because from the PCI buses' point-of-view, the address we chose is part of its address space. To make sure we don't allocate addresses from there, reserve the PCI addresses in the IOVA. Note that smmu(4) currently gives each device its own IOVA. So the PCI addresses will be reserved only in IOVA from PCI devices, and only the addresses concerning the PCI bus it is connected to will be reserved. All other devices behind an smmu(4) will not have any changes to their IOVA. ok kettenis@ --- sys/arch/arm64/dev/acpiiort.c | 16 ++++++- sys/arch/arm64/dev/acpiiort.h | 5 +- sys/arch/arm64/dev/acpipci.c | 14 +++++- sys/arch/arm64/dev/apldart.c | 9 +++- sys/arch/arm64/dev/smmu.c | 28 ++++++----- sys/arch/arm64/dev/smmu_acpi.c | 3 +- sys/arch/arm64/dev/smmu_fdt.c | 14 +++++- sys/arch/arm64/dev/smmuvar.h | 3 +- sys/dev/fdt/dwpcie.c | 8 +++- sys/dev/fdt/pciecam.c | 10 +++- sys/dev/ofw/ofw_misc.c | 85 +++++++++++++++++++++++++++------- sys/dev/ofw/ofw_misc.h | 4 +- 12 files changed, 160 insertions(+), 39 deletions(-) diff --git a/sys/arch/arm64/dev/acpiiort.c b/sys/arch/arm64/dev/acpiiort.c index 8569b4932d7..7bee4dac83e 100644 --- a/sys/arch/arm64/dev/acpiiort.c +++ b/sys/arch/arm64/dev/acpiiort.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpiiort.c,v 1.3 2021/03/15 22:56:48 patrick Exp $ */ +/* $OpenBSD: acpiiort.c,v 1.4 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2021 Patrick Wildt * @@ -103,6 +103,20 @@ acpiiort_smmu_map(struct acpi_iort_node *node, uint32_t rid, return dmat; } +void +acpiiort_smmu_reserve_region(struct acpi_iort_node *node, uint32_t rid, + bus_addr_t addr, bus_size_t size) +{ + struct acpiiort_smmu *as; + + SIMPLEQ_FOREACH(as, &acpiiort_smmu_list, as_list) { + if (as->as_node == node) { + as->as_reserve(as->as_cookie, rid, addr, size); + return; + } + } +} + bus_dma_tag_t acpiiort_device_map(struct aml_node *root, bus_dma_tag_t dmat) { diff --git a/sys/arch/arm64/dev/acpiiort.h b/sys/arch/arm64/dev/acpiiort.h index 5aaaa02c17b..02369755e33 100644 --- a/sys/arch/arm64/dev/acpiiort.h +++ b/sys/arch/arm64/dev/acpiiort.h @@ -1,4 +1,4 @@ -/* $OpenBSD: acpiiort.h,v 1.3 2021/03/15 22:56:48 patrick Exp $ */ +/* $OpenBSD: acpiiort.h,v 1.4 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2021 Patrick Wildt * @@ -28,8 +28,11 @@ struct acpiiort_smmu { void *as_cookie; bus_dma_tag_t (*as_map)(void *, uint32_t, bus_dma_tag_t); + void (*as_reserve)(void *, uint32_t, + bus_addr_t, bus_size_t); }; void acpiiort_smmu_register(struct acpiiort_smmu *); bus_dma_tag_t acpiiort_smmu_map(struct acpi_iort_node *, uint32_t, bus_dma_tag_t); +void acpiiort_smmu_reserve_region(struct acpi_iort_node *, uint32_t, bus_addr_t, bus_size_t); bus_dma_tag_t acpiiort_device_map(struct aml_node *, bus_dma_tag_t); diff --git a/sys/arch/arm64/dev/acpipci.c b/sys/arch/arm64/dev/acpipci.c index 2b6566cdcc3..b7248cbed15 100644 --- a/sys/arch/arm64/dev/acpipci.c +++ b/sys/arch/arm64/dev/acpipci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpipci.c,v 1.29 2021/05/17 17:25:13 kettenis Exp $ */ +/* $OpenBSD: acpipci.c,v 1.30 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2018 Mark Kettenis * @@ -355,6 +355,7 @@ int acpipci_probe_device_hook(void *v, struct pci_attach_args *pa) { struct acpipci_mcfg *am = v; + struct acpipci_trans *at; struct acpi_table_header *hdr; struct acpi_iort *iort = NULL; struct acpi_iort_node *node; @@ -417,8 +418,17 @@ acpipci_probe_device_hook(void *v, struct pci_attach_args *pa) return 0; node = (struct acpi_iort_node *)((char *)iort + offset); - if (node->type == ACPI_IORT_SMMU) + if (node->type == ACPI_IORT_SMMU) { pa->pa_dmat = acpiiort_smmu_map(node, rid, pa->pa_dmat); + for (at = pa->pa_iot->bus_private; at; at = at->at_next) { + acpiiort_smmu_reserve_region(node, rid, + at->at_base, at->at_size); + } + for (at = pa->pa_memt->bus_private; at; at = at->at_next) { + acpiiort_smmu_reserve_region(node, rid, + at->at_base, at->at_size); + } + } return 0; } diff --git a/sys/arch/arm64/dev/apldart.c b/sys/arch/arm64/dev/apldart.c index dfa83508125..e662cc838bf 100644 --- a/sys/arch/arm64/dev/apldart.c +++ b/sys/arch/arm64/dev/apldart.c @@ -1,4 +1,4 @@ -/* $OpenBSD: apldart.c,v 1.4 2021/05/30 15:05:32 visa Exp $ */ +/* $OpenBSD: apldart.c,v 1.5 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2021 Mark Kettenis * @@ -133,6 +133,7 @@ struct cfdriver apldart_cd = { }; bus_dma_tag_t apldart_map(void *, uint32_t *, bus_dma_tag_t); +void apldart_reserve(void *, uint32_t *, bus_addr_t, bus_size_t); int apldart_intr(void *); void apldart_flush_tlb(struct apldart_softc *); @@ -279,6 +280,7 @@ apldart_attach(struct device *parent, struct device *self, void *aux) sc->sc_id.id_node = faa->fa_node; sc->sc_id.id_cookie = sc; sc->sc_id.id_map = apldart_map; + sc->sc_id.id_reserve = apldart_reserve; iommu_device_register(&sc->sc_id); } @@ -290,6 +292,11 @@ apldart_map(void *cookie, uint32_t *cells, bus_dma_tag_t dmat) return &sc->sc_bus_dmat; } +void +apldart_reserve(void *cookie, uint32_t *cells, bus_addr_t addr, bus_size_t size) +{ +} + int apldart_intr(void *arg) { diff --git a/sys/arch/arm64/dev/smmu.c b/sys/arch/arm64/dev/smmu.c index dd942391587..b4517057971 100644 --- a/sys/arch/arm64/dev/smmu.c +++ b/sys/arch/arm64/dev/smmu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smmu.c,v 1.16 2021/06/25 12:40:29 patrick Exp $ */ +/* $OpenBSD: smmu.c,v 1.17 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2008-2009,2014-2016 Dale Rahn * Copyright (c) 2021 Patrick Wildt @@ -750,21 +750,25 @@ smmu_domain_create(struct smmu_softc *sc, uint32_t sid) /* Reserve first page (to catch NULL access) */ extent_alloc_region(dom->sd_iovamap, 0, PAGE_SIZE, EX_WAITOK); -#if 0 - /* FIXME PCIe address space */ - { -#if 1 - /* Reserve 8040 PCI address space */ - extent_alloc_region(dom->sd_iovamap, 0xc0000000, 0x20000000, - EX_WAITOK); -#endif - } -#endif - SIMPLEQ_INSERT_TAIL(&sc->sc_domains, dom, sd_list); return dom; } +void +smmu_reserve_region(void *cookie, uint32_t sid, bus_addr_t addr, + bus_size_t size) +{ + struct smmu_softc *sc = cookie; + struct smmu_domain *dom; + + dom = smmu_domain_lookup(sc, sid); + if (dom == NULL) + return; + + extent_alloc_region(dom->sd_iovamap, addr, size, + EX_WAITOK | EX_CONFLICTOK); +} + /* basically pmap follows */ /* virtual to physical helpers */ diff --git a/sys/arch/arm64/dev/smmu_acpi.c b/sys/arch/arm64/dev/smmu_acpi.c index 10c1daf6137..c6b30c4082f 100644 --- a/sys/arch/arm64/dev/smmu_acpi.c +++ b/sys/arch/arm64/dev/smmu_acpi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smmu_acpi.c,v 1.2 2021/03/15 22:48:57 patrick Exp $ */ +/* $OpenBSD: smmu_acpi.c,v 1.3 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2021 Patrick Wildt * @@ -126,5 +126,6 @@ smmu_acpi_attach(struct device *parent, struct device *self, void *aux) as->as_node = node; as->as_cookie = sc; as->as_map = smmu_device_map; + as->as_reserve = smmu_reserve_region; acpiiort_smmu_register(as); } diff --git a/sys/arch/arm64/dev/smmu_fdt.c b/sys/arch/arm64/dev/smmu_fdt.c index 75c80c8efe6..cbf872d5d36 100644 --- a/sys/arch/arm64/dev/smmu_fdt.c +++ b/sys/arch/arm64/dev/smmu_fdt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smmu_fdt.c,v 1.3 2021/03/15 22:48:57 patrick Exp $ */ +/* $OpenBSD: smmu_fdt.c,v 1.4 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2021 Patrick Wildt * @@ -42,6 +42,7 @@ int smmu_fdt_match(struct device *, void *, void *); void smmu_fdt_attach(struct device *, struct device *, void *); bus_dma_tag_t smmu_fdt_map(void *, uint32_t *, bus_dma_tag_t); +void smmu_fdt_reserve(void *, uint32_t *, bus_addr_t, bus_size_t); struct cfattach smmu_fdt_ca = { sizeof(struct smmu_fdt_softc), smmu_fdt_match, smmu_fdt_attach @@ -108,6 +109,7 @@ smmu_fdt_attach(struct device *parent, struct device *self, void *aux) fsc->sc_id.id_node = faa->fa_node; fsc->sc_id.id_cookie = fsc; fsc->sc_id.id_map = smmu_fdt_map; + fsc->sc_id.id_reserve = smmu_fdt_reserve; iommu_device_register(&fsc->sc_id); } @@ -119,3 +121,13 @@ smmu_fdt_map(void *cookie, uint32_t *cells, bus_dma_tag_t dmat) return smmu_device_map(sc, cells[0], dmat); } + +void +smmu_fdt_reserve(void *cookie, uint32_t *cells, bus_addr_t addr, + bus_size_t size) +{ + struct smmu_fdt_softc *fsc = (struct smmu_fdt_softc *)cookie; + struct smmu_softc *sc = &fsc->sc_smmu; + + return smmu_reserve_region(sc, cells[0], addr, size); +} diff --git a/sys/arch/arm64/dev/smmuvar.h b/sys/arch/arm64/dev/smmuvar.h index 14dc0f20918..7824c970d4f 100644 --- a/sys/arch/arm64/dev/smmuvar.h +++ b/sys/arch/arm64/dev/smmuvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smmuvar.h,v 1.5 2021/06/25 12:40:29 patrick Exp $ */ +/* $OpenBSD: smmuvar.h,v 1.6 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2021 Patrick Wildt * @@ -80,3 +80,4 @@ int smmu_attach(struct smmu_softc *); int smmu_global_irq(void *); int smmu_context_irq(void *); bus_dma_tag_t smmu_device_map(void *, uint32_t, bus_dma_tag_t); +void smmu_reserve_region(void *, uint32_t, bus_addr_t, bus_size_t); diff --git a/sys/dev/fdt/dwpcie.c b/sys/dev/fdt/dwpcie.c index 71f48fc2470..0d67cb2b63f 100644 --- a/sys/dev/fdt/dwpcie.c +++ b/sys/dev/fdt/dwpcie.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dwpcie.c,v 1.33 2021/06/24 09:34:17 kettenis Exp $ */ +/* $OpenBSD: dwpcie.c,v 1.34 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2018 Mark Kettenis * @@ -1206,10 +1206,16 @@ dwpcie_probe_device_hook(void *v, struct pci_attach_args *pa) { struct dwpcie_softc *sc = v; uint16_t rid; + int i; rid = pci_requester_id(pa->pa_pc, pa->pa_tag); pa->pa_dmat = iommu_device_map_pci(sc->sc_node, rid, pa->pa_dmat); + for (i = 0; i < sc->sc_nranges; i++) { + iommu_reserve_region_pci(sc->sc_node, rid, + sc->sc_ranges[i].pci_base, sc->sc_ranges[i].size); + } + return 0; } diff --git a/sys/dev/fdt/pciecam.c b/sys/dev/fdt/pciecam.c index cae85bdae80..da640f698aa 100644 --- a/sys/dev/fdt/pciecam.c +++ b/sys/dev/fdt/pciecam.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pciecam.c,v 1.2 2021/05/19 20:10:38 kettenis Exp $ */ +/* $OpenBSD: pciecam.c,v 1.3 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2013,2017 Patrick Wildt * @@ -340,10 +340,18 @@ pciecam_probe_device_hook(void *v, struct pci_attach_args *pa) { struct pciecam_softc *sc = (struct pciecam_softc *)v; uint16_t rid; + int i; rid = pci_requester_id(pa->pa_pc, pa->pa_tag); pa->pa_dmat = iommu_device_map_pci(sc->sc_node, rid, pa->pa_dmat); + for (i = 0; i < sc->sc_pcirangeslen; i++) { + if (sc->sc_pciranges[i].flags >> 24 == 0) + continue; + iommu_reserve_region_pci(sc->sc_node, rid, + sc->sc_pciranges[i].pci_base, sc->sc_pciranges[i].size); + } + return 0; } diff --git a/sys/dev/ofw/ofw_misc.c b/sys/dev/ofw/ofw_misc.c index 389ad22423c..1f562eb89df 100644 --- a/sys/dev/ofw/ofw_misc.c +++ b/sys/dev/ofw/ofw_misc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ofw_misc.c,v 1.32 2021/04/07 17:12:22 kettenis Exp $ */ +/* $OpenBSD: ofw_misc.c,v 1.33 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2017 Mark Kettenis * @@ -896,18 +896,17 @@ iommu_device_do_map(uint32_t phandle, uint32_t *cells, bus_dma_tag_t dmat) return dmat; } -bus_dma_tag_t -iommu_device_map(int node, bus_dma_tag_t dmat) +int +iommu_device_lookup(int node, uint32_t *phandle, uint32_t *sid) { - uint32_t sid = 0; - uint32_t phandle = 0; uint32_t *cell; uint32_t *map; int len, icells, ncells; + int ret = 1; len = OF_getproplen(node, "iommus"); if (len <= 0) - return dmat; + return ret; map = malloc(len, M_TEMP, M_WAITOK); OF_getpropintarray(node, "iommus", map, len); @@ -925,8 +924,9 @@ iommu_device_map(int node, bus_dma_tag_t dmat) KASSERT(icells == 1); - phandle = cell[0]; - sid = cell[1]; + *phandle = cell[0]; + *sid = cell[1]; + ret = 0; break; cell += (1 + icells); @@ -936,22 +936,23 @@ iommu_device_map(int node, bus_dma_tag_t dmat) out: free(map, M_TEMP, len); - return iommu_device_do_map(phandle, &sid, dmat); + return ret; } -bus_dma_tag_t -iommu_device_map_pci(int node, uint32_t rid, bus_dma_tag_t dmat) +int +iommu_device_lookup_pci(int node, uint32_t rid, uint32_t *phandle, + uint32_t *sid) { - uint32_t sid_base, sid = 0; - uint32_t phandle = 0; + uint32_t sid_base; uint32_t *cell; uint32_t *map; uint32_t mask, rid_base; int len, length, icells, ncells; + int ret = 1; len = OF_getproplen(node, "iommu-map"); if (len <= 0) - return dmat; + return ret; map = malloc(len, M_TEMP, M_WAITOK); OF_getpropintarray(node, "iommu-map", map, len); @@ -976,8 +977,9 @@ iommu_device_map_pci(int node, uint32_t rid, bus_dma_tag_t dmat) sid_base = cell[2]; length = cell[3]; if (rid >= rid_base && rid < rid_base + length) { - sid = sid_base + (rid - rid_base); - phandle = cell[1]; + *sid = sid_base + (rid - rid_base); + *phandle = cell[1]; + ret = 0; break; } @@ -988,5 +990,56 @@ iommu_device_map_pci(int node, uint32_t rid, bus_dma_tag_t dmat) out: free(map, M_TEMP, len); + return ret; +} + +bus_dma_tag_t +iommu_device_map(int node, bus_dma_tag_t dmat) +{ + uint32_t phandle, sid; + + if (iommu_device_lookup(node, &phandle, &sid)) + return dmat; + + return iommu_device_do_map(phandle, &sid, dmat); +} + +bus_dma_tag_t +iommu_device_map_pci(int node, uint32_t rid, bus_dma_tag_t dmat) +{ + uint32_t phandle, sid; + + if (iommu_device_lookup_pci(node, rid, &phandle, &sid)) + return dmat; + return iommu_device_do_map(phandle, &sid, dmat); } + +void +iommu_device_do_reserve(uint32_t phandle, uint32_t *cells, bus_addr_t addr, + bus_size_t size) +{ + struct iommu_device *id; + + if (phandle == 0) + return; + + LIST_FOREACH(id, &iommu_devices, id_list) { + if (id->id_phandle == phandle) { + id->id_reserve(id->id_cookie, cells, addr, size); + break; + } + } +} + +void +iommu_reserve_region_pci(int node, uint32_t rid, bus_addr_t addr, + bus_size_t size) +{ + uint32_t phandle, sid; + + if (iommu_device_lookup_pci(node, rid, &phandle, &sid)) + return; + + return iommu_device_do_reserve(phandle, &sid, addr, size); +} diff --git a/sys/dev/ofw/ofw_misc.h b/sys/dev/ofw/ofw_misc.h index 4c8b67714af..03fb3a016a0 100644 --- a/sys/dev/ofw/ofw_misc.h +++ b/sys/dev/ofw/ofw_misc.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ofw_misc.h,v 1.20 2021/04/07 17:12:22 kettenis Exp $ */ +/* $OpenBSD: ofw_misc.h,v 1.21 2021/06/25 17:41:22 patrick Exp $ */ /* * Copyright (c) 2017 Mark Kettenis * @@ -243,6 +243,7 @@ struct iommu_device { int id_node; void *id_cookie; bus_dma_tag_t (*id_map)(void *, uint32_t *, bus_dma_tag_t); + void (*id_reserve)(void *, uint32_t *, bus_addr_t, bus_size_t); LIST_ENTRY(iommu_device) id_list; uint32_t id_phandle; @@ -251,5 +252,6 @@ struct iommu_device { void iommu_device_register(struct iommu_device *); bus_dma_tag_t iommu_device_map(int, bus_dma_tag_t); bus_dma_tag_t iommu_device_map_pci(int, uint32_t, bus_dma_tag_t); +void iommu_reserve_region_pci(int, uint32_t, bus_addr_t, bus_size_t); #endif /* _DEV_OFW_MISC_H_ */ -- 2.20.1