git: 41ce5498f8e6 - main - Add OFW support to arm64's IOMMU framework. This is needed to support non-PCI devices like memory-mapped display controllers. Split-out some initialization code from iommu_ctx_alloc() into iommu_ctx_init() method so we could pass controller's MD-data obtained from DTS to the driver prior to a CTX initialization.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Wed, 18 May 2022 13:18:11 UTC
The branch main has been updated by br: URL: https://cgit.FreeBSD.org/src/commit/?id=41ce5498f8e69e6820962e813eb3b40c465079d0 commit 41ce5498f8e69e6820962e813eb3b40c465079d0 Author: Ruslan Bukin <br@FreeBSD.org> AuthorDate: 2022-05-18 13:11:23 +0000 Commit: Ruslan Bukin <br@FreeBSD.org> CommitDate: 2022-05-18 13:11:23 +0000 Add OFW support to arm64's IOMMU framework. This is needed to support non-PCI devices like memory-mapped display controllers. Split-out some initialization code from iommu_ctx_alloc() into iommu_ctx_init() method so we could pass controller's MD-data obtained from DTS to the driver prior to a CTX initialization. Tested on Morello SoC. Sponsored by: UKRI --- sys/arm64/iommu/iommu.c | 163 ++++++++++++++++++++++++++++++++++++++++----- sys/arm64/iommu/iommu.h | 1 + sys/arm64/iommu/iommu_if.m | 24 +++++++ sys/arm64/iommu/smmu.c | 92 +++++++++++++++++-------- sys/arm64/iommu/smmu_fdt.c | 2 + 5 files changed, 239 insertions(+), 43 deletions(-) diff --git a/sys/arm64/iommu/iommu.c b/sys/arm64/iommu/iommu.c index 447f3e141610..aa48dcf5ab5e 100644 --- a/sys/arm64/iommu/iommu.c +++ b/sys/arm64/iommu/iommu.c @@ -57,6 +57,12 @@ __FBSDID("$FreeBSD$"); #include <dev/iommu/busdma_iommu.h> #include <machine/vmparam.h> +#ifdef FDT +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#endif + #include "iommu.h" #include "iommu_if.h" @@ -180,23 +186,149 @@ iommu_tag_init(struct bus_dma_tag_iommu *t) } static struct iommu_ctx * -iommu_ctx_alloc(device_t dev, struct iommu_domain *iodom, bool disabled) +iommu_ctx_alloc(device_t requester, struct iommu_domain *iodom, bool disabled) { struct iommu_unit *iommu; struct iommu_ctx *ioctx; iommu = iodom->iommu; - ioctx = IOMMU_CTX_ALLOC(iommu->dev, iodom, dev, disabled); + ioctx = IOMMU_CTX_ALLOC(iommu->dev, iodom, requester, disabled); if (ioctx == NULL) return (NULL); + ioctx->domain = iodom; + + return (ioctx); +} + +static int +iommu_ctx_init(device_t requester, struct iommu_ctx *ioctx) +{ + struct bus_dma_tag_iommu *tag; + struct iommu_domain *iodom; + struct iommu_unit *iommu; + int error; + + iodom = ioctx->domain; + iommu = iodom->iommu; + + error = IOMMU_CTX_INIT(iommu->dev, ioctx); + if (error) + return (error); + + tag = ioctx->tag = malloc(sizeof(struct bus_dma_tag_iommu), + M_IOMMU, M_WAITOK | M_ZERO); + tag->owner = requester; + tag->ctx = ioctx; + tag->ctx->domain = iodom; + + iommu_tag_init(tag); + + return (error); +} + +static struct iommu_unit * +iommu_lookup(device_t dev) +{ + struct iommu_entry *entry; + struct iommu_unit *iommu; + + IOMMU_LIST_LOCK(); + LIST_FOREACH(entry, &iommu_list, next) { + iommu = entry->iommu; + if (iommu->dev == dev) { + IOMMU_LIST_UNLOCK(); + return (iommu); + } + } + IOMMU_LIST_UNLOCK(); + + return (NULL); +} + +struct iommu_ctx * +iommu_get_ctx_ofw(device_t dev, int channel) +{ + struct iommu_domain *iodom; + struct iommu_unit *iommu; + struct iommu_ctx *ioctx; + phandle_t node, parent; + device_t iommu_dev; + pcell_t *cells; + int niommus; + int ncells; + int error; + + node = ofw_bus_get_node(dev); + if (node <= 0) { + device_printf(dev, + "%s called on not ofw based device.\n", __func__); + return (NULL); + } + + error = ofw_bus_parse_xref_list_get_length(node, + "iommus", "#iommu-cells", &niommus); + if (error) { + device_printf(dev, "%s can't get iommu list.\n", __func__); + return (NULL); + } + + if (niommus == 0) { + device_printf(dev, "%s iommu list is empty.\n", __func__); + return (NULL); + } + + error = ofw_bus_parse_xref_list_alloc(node, "iommus", "#iommu-cells", + channel, &parent, &ncells, &cells); + if (error != 0) { + device_printf(dev, "%s can't get iommu device xref.\n", + __func__); + return (NULL); + } + + iommu_dev = OF_device_from_xref(parent); + if (iommu_dev == NULL) { + device_printf(dev, "%s can't get iommu device.\n", __func__); + return (NULL); + } + + iommu = iommu_lookup(iommu_dev); + if (iommu == NULL) { + device_printf(dev, "%s can't lookup iommu.\n", __func__); + return (NULL); + } + /* - * iommu can also be used for non-PCI based devices. - * This should be reimplemented as new newbus method with - * pci_get_rid() as a default for PCI device class. + * In our current configuration we have a domain per each ctx, + * so allocate a domain first. */ - ioctx->rid = pci_get_rid(dev); + iodom = iommu_domain_alloc(iommu); + if (iodom == NULL) { + device_printf(dev, "%s can't allocate domain.\n", __func__); + return (NULL); + } + + ioctx = iommu_ctx_alloc(dev, iodom, false); + if (ioctx == NULL) { + iommu_domain_free(iodom); + return (NULL); + } + + ioctx->domain = iodom; + + error = IOMMU_OFW_MD_DATA(iommu->dev, ioctx, cells, ncells); + if (error) { + device_printf(dev, "%s can't set MD data\n", __func__); + return (NULL); + } + + error = iommu_ctx_init(dev, ioctx); + if (error) { + IOMMU_CTX_FREE(iommu->dev, ioctx); + iommu_domain_free(iodom); + return (NULL); + } return (ioctx); } @@ -205,9 +337,9 @@ struct iommu_ctx * iommu_get_ctx(struct iommu_unit *iommu, device_t requester, uint16_t rid, bool disabled, bool rmrr) { - struct iommu_ctx *ioctx; struct iommu_domain *iodom; - struct bus_dma_tag_iommu *tag; + struct iommu_ctx *ioctx; + int error; IOMMU_LOCK(iommu); ioctx = IOMMU_CTX_LOOKUP(iommu->dev, requester); @@ -231,15 +363,12 @@ iommu_get_ctx(struct iommu_unit *iommu, device_t requester, return (NULL); } - tag = ioctx->tag = malloc(sizeof(struct bus_dma_tag_iommu), - M_IOMMU, M_WAITOK | M_ZERO); - tag->owner = requester; - tag->ctx = ioctx; - tag->ctx->domain = iodom; - - iommu_tag_init(tag); - - ioctx->domain = iodom; + error = iommu_ctx_init(requester, ioctx); + if (error) { + IOMMU_CTX_FREE(iommu->dev, ioctx); + iommu_domain_free(iodom); + return (NULL); + } return (ioctx); } diff --git a/sys/arm64/iommu/iommu.h b/sys/arm64/iommu/iommu.h index 2071173070df..c221f619b5db 100644 --- a/sys/arm64/iommu/iommu.h +++ b/sys/arm64/iommu/iommu.h @@ -40,5 +40,6 @@ int iommu_unregister(struct iommu_unit *unit); int iommu_register(struct iommu_unit *unit); +struct iommu_ctx * iommu_get_ctx_ofw(device_t dev, int channel); #endif /* _ARM64_IOMMU_IOMMU_H_ */ diff --git a/sys/arm64/iommu/iommu_if.m b/sys/arm64/iommu/iommu_if.m index ef8c3e7b57b8..6d4f963c5ff1 100644 --- a/sys/arm64/iommu/iommu_if.m +++ b/sys/arm64/iommu/iommu_if.m @@ -42,6 +42,12 @@ #include <dev/pci/pcivar.h> #include <dev/iommu/iommu.h> +#ifdef FDT +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> +#endif + INTERFACE iommu; # @@ -116,6 +122,14 @@ METHOD struct iommu_ctx * ctx_alloc { bool disabled; }; +# +# Initialize the new iommu context. +# +METHOD int ctx_init { + device_t dev; + struct iommu_ctx *ioctx; +}; + # # Free the iommu context. # @@ -123,3 +137,13 @@ METHOD void ctx_free { device_t dev; struct iommu_ctx *ioctx; }; + +# +# Notify controller we have machine-dependent data. +# +METHOD int ofw_md_data { + device_t dev; + struct iommu_ctx *ioctx; + pcell_t *cells; + int ncells; +}; diff --git a/sys/arm64/iommu/smmu.c b/sys/arm64/iommu/smmu.c index 5d4401c5cee9..57f6826faa5e 100644 --- a/sys/arm64/iommu/smmu.c +++ b/sys/arm64/iommu/smmu.c @@ -1844,42 +1844,63 @@ smmu_ctx_alloc(device_t dev, struct iommu_domain *iodom, device_t child, bool disabled) { struct smmu_domain *domain; + struct smmu_ctx *ctx; + + domain = (struct smmu_domain *)iodom; + + ctx = malloc(sizeof(struct smmu_ctx), M_SMMU, M_WAITOK | M_ZERO); + ctx->dev = child; + ctx->domain = domain; + if (disabled) + ctx->bypass = true; + + IOMMU_DOMAIN_LOCK(iodom); + LIST_INSERT_HEAD(&domain->ctx_list, ctx, next); + IOMMU_DOMAIN_UNLOCK(iodom); + + return (&ctx->ioctx); +} + +static int +smmu_ctx_init(device_t dev, struct iommu_ctx *ioctx) +{ + struct smmu_domain *domain; + struct iommu_domain *iodom; struct smmu_softc *sc; struct smmu_ctx *ctx; devclass_t pci_class; u_int sid; int err; + ctx = (struct smmu_ctx *)ioctx; + sc = device_get_softc(dev); - domain = (struct smmu_domain *)iodom; - pci_class = devclass_find("pci"); - if (device_get_devclass(device_get_parent(child)) != pci_class) - return (NULL); + domain = ctx->domain; + iodom = (struct iommu_domain *)domain; + pci_class = devclass_find("pci"); + if (device_get_devclass(device_get_parent(ctx->dev)) == pci_class) { #ifdef DEV_ACPI - err = smmu_pci_get_sid_acpi(child, NULL, &sid); + err = smmu_pci_get_sid_acpi(ctx->dev, NULL, &sid); #else - err = smmu_pci_get_sid_fdt(child, NULL, &sid); + err = smmu_pci_get_sid_fdt(ctx->dev, NULL, &sid); #endif - if (err) - return (NULL); + if (err) + return (err); + + ioctx->rid = pci_get_rid(dev); + ctx->sid = sid; + ctx->vendor = pci_get_vendor(ctx->dev); + ctx->device = pci_get_device(ctx->dev); + } if (sc->features & SMMU_FEATURE_2_LVL_STREAM_TABLE) { - err = smmu_init_l1_entry(sc, sid); + err = smmu_init_l1_entry(sc, ctx->sid); if (err) - return (NULL); + return (err); } - ctx = malloc(sizeof(struct smmu_ctx), M_SMMU, M_WAITOK | M_ZERO); - ctx->vendor = pci_get_vendor(child); - ctx->device = pci_get_device(child); - ctx->dev = child; - ctx->sid = sid; - ctx->domain = domain; - if (disabled) - ctx->bypass = true; - /* * Neoverse N1 SDP: * 0x800 xhci @@ -1889,14 +1910,11 @@ smmu_ctx_alloc(device_t dev, struct iommu_domain *iodom, device_t child, smmu_init_ste(sc, domain->cd, ctx->sid, ctx->bypass); - if (iommu_is_buswide_ctx(iodom->iommu, pci_get_bus(ctx->dev))) - smmu_set_buswide(dev, domain, ctx); + if (device_get_devclass(device_get_parent(ctx->dev)) == pci_class) + if (iommu_is_buswide_ctx(iodom->iommu, pci_get_bus(ctx->dev))) + smmu_set_buswide(dev, domain, ctx); - IOMMU_DOMAIN_LOCK(iodom); - LIST_INSERT_HEAD(&domain->ctx_list, ctx, next); - IOMMU_DOMAIN_UNLOCK(iodom); - - return (&ctx->ioctx); + return (0); } static void @@ -1993,6 +2011,24 @@ smmu_find(device_t dev, device_t child) return (0); } +#ifdef FDT +static int +smmu_ofw_md_data(device_t dev, struct iommu_ctx *ioctx, pcell_t *cells, + int ncells) +{ + struct smmu_ctx *ctx; + + ctx = (struct smmu_ctx *)ioctx; + + if (ncells != 1) + return (-1); + + ctx->sid = cells[0]; + + return (0); +} +#endif + static device_method_t smmu_methods[] = { /* Device interface */ DEVMETHOD(device_detach, smmu_detach), @@ -2004,8 +2040,12 @@ static device_method_t smmu_methods[] = { DEVMETHOD(iommu_domain_alloc, smmu_domain_alloc), DEVMETHOD(iommu_domain_free, smmu_domain_free), DEVMETHOD(iommu_ctx_alloc, smmu_ctx_alloc), + DEVMETHOD(iommu_ctx_init, smmu_ctx_init), DEVMETHOD(iommu_ctx_free, smmu_ctx_free), DEVMETHOD(iommu_ctx_lookup, smmu_ctx_lookup), +#ifdef FDT + DEVMETHOD(iommu_ofw_md_data, smmu_ofw_md_data), +#endif /* Bus interface */ DEVMETHOD(bus_read_ivar, smmu_read_ivar), diff --git a/sys/arm64/iommu/smmu_fdt.c b/sys/arm64/iommu/smmu_fdt.c index f2d441fe8340..e5541b50058f 100644 --- a/sys/arm64/iommu/smmu_fdt.c +++ b/sys/arm64/iommu/smmu_fdt.c @@ -176,6 +176,8 @@ smmu_fdt_attach(device_t dev) return (ENXIO); } + OF_device_register_xref(OF_xref_from_node(node), dev); + return (0); error: