Re: [PATCH v2 4/5] iommu: Add Broadcom BCM2712 IOMMU driver

Robin Murphy <[email protected]> Tue, 28 Jul 2026 11:51:04 +0100
Newsgroups dev.linux.lists.iommu,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
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;
> +}