[PATCH v2 4/5] iommu: Add Broadcom BCM2712 IOMMU driver
Robin Murphy
robin.murphy at arm.com
Tue Jul 28 03:51:04 PDT 2026
On 27/07/2026 9:43 pm, Daniel Drake wrote:
[...]
> + * Each BCM2712 IOMMU has multiple devices hardwired into it, whose
> + * DMA transactions all route through the IOMMU. There is no stream ID tagging
> + * or any other kind of segmentation to differentiate between requests from
> + * different devices. It is also not possible to toggle a specific device
> + * between iommu-mapped and bypass modes.
Hmm, so it's kind of like an IOMMU and a GART had a baby?
> + * With these limitations in mind, we configure the hardware to operate over two
> + * windows:
> + * 1. The lower part of the address space operates in identity/bypass mode.
> + * This enables non-IOMMU-consumer devices to work as normal (including
> + * devices set up by the firmware, devices that don't require large DMA
> + * allocations, devices that handle scatter-gather natively, etc.)
> + * 2. A 4GB translation aperture is defined at a fixed, high address, beyond
> + * regular RAM and MMIO space. Mappings are operated in this aperture for
> + * devices that wish to take advantage of the IOMMU.
[...]
> +static struct iommu_domain *bcm2712_iommu_domain_alloc(struct device *dev)
> +{
> + struct bcm2712_iommu *mmu = dev_iommu_priv_get(dev);
> + struct bcm2712_iommu_domain *domain;
> + struct pt_iommu_bcm2712_cfg cfg;
> + int ret;
> +
> + domain = kzalloc_obj(*domain);
> + if (!domain)
> + return NULL;
> +
> + domain->mmu = mmu;
> + domain->pt.iommu.iommu_device = mmu->dev;
> + memset(&cfg, 0, sizeof(cfg));
> + cfg.common.features = BIT(PT_FEAT_DMA_INCOHERENT);
> +
> + /* Bigpage and superpage sizes are typically 64K and 1M, but may vary */
> + if (mmu->bigpage_size)
> + cfg.bigpage_lg2 = ilog2(mmu->bigpage_size);
> + if (mmu->superpage_size)
> + cfg.superpage_lg2 = ilog2(mmu->superpage_size);
> +
> + /* 2-level format: 10-bit L1 + 10-bit L2 + 12-bit page offset */
> + cfg.common.hw_max_vasz_lg2 =
> + (2 * PTES_PER_IOPG_SHIFT) + IOMMU_PAGE_SHIFT;
> +
> + /* PTEs encode a 28-bit output address PFN */
> + cfg.common.hw_max_oasz_lg2 = 28 + IOMMU_PAGE_SHIFT;
> +
> + ret = pt_iommu_bcm2712_init(&domain->pt, &cfg, GFP_KERNEL);
> + if (ret)
> + goto err;
> +
> + /* Set up a default (error) page used to catch illegal reads/writes */
> + domain->default_page = iommu_alloc_pages_sz(GFP_KERNEL, PAGE_SIZE);
> + if (!domain->default_page)
> + goto err;
> +
> + domain->base.geometry.aperture_start = BCM2712_APERTURE_BASE;
> + domain->base.geometry.aperture_end = BCM2712_APERTURE_END - 1;
> + domain->base.geometry.force_aperture = true;
...and thus (as things stand) this is a lie :(
If you want to operate like an IOMMU, then the translation aperture and
the bypass window should really be mutually-exclusive; conversely if you
want to operate like a GART then you can't really pretend to support
blocking domains which will also disrupt devices outside the IOMMU
group. Trying to compromise leaves you with rather a
worst-of-both-worlds setup as-is...
Thanks,
Robin.
> + domain->base.ops = &bcm2712_paging_domain_ops;
> + return &domain->base;
> +
> +err:
> + bcm2712_iommu_domain_free(&domain->base);
> + return NULL;
> +}
More information about the linux-arm-kernel
mailing list