[PATCH] iommu/io-pgtable-arm: Add misisng concatenated PGD cases

Mostafa Saleh smostafa at google.com
Tue Dec 9 04:37:10 PST 2025


On Tue, Dec 09, 2025 at 11:34:34AM +0000, Robin Murphy wrote:
> On 2025-11-30 7:45 pm, Mostafa Saleh wrote:
> > arm_lpae_concat_mandatory() assumes that OAS >= IAS which is not
> > correct for SMMUs supporting AArch32, and have OAS = 32/36 bits,
> > as IAS would be 40 bits.
> 
> But that is only when *using* AArch32 format. The bit in chapter 3.4 of the
> SMMU architecture is talking about the maximum IAS that an SMMU
> implementation needs to be able to accommodate based on its configuration,
> but it does then attempt to clarify that the actual IPA size in use by any
> given context should depend on the VMSA format in use:
> 
> "VMSAv8-32 LPAE always supports an IPA size of 40 bits, whereas VMSAv8-64
> and VMSAv9-128 limits the maximum IPA size to the maximum PA size."
> 
> Rule R_SRKBC in the Arm ARM lays out the exact T0SZ constraints with this
> AArch32/AArch64 detail.

I see, thanks a lot for the explanation, I got confused by the this
statement:
Note: If AArch32 is implemented, IAS == MAX(40, OAS), otherwise IAS == OAS.

However, I think this is still a bug but somewere else, as at the moment
the SMMUv3 dirver will use the SMMU IAS (40-bits) as input for AArch64
stage-2 page tables, so we need either to limit the IAS as:

diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index d16d35c78c06..d21153156daa 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -2561,7 +2561,7 @@ static int arm_smmu_domain_finalise(struct arm_smmu_domain *smmu_domain,
        case ARM_SMMU_DOMAIN_S2:
                if (enable_dirty)
                        return -EOPNOTSUPP;
-               pgtbl_cfg.ias = smmu->ias;
+               pgtbl_cfg.ias = min(smmu->ias, smmu->oas);
                pgtbl_cfg.oas = smmu->oas;
                fmt = ARM_64_LPAE_S2;
                finalise_stage_fn = arm_smmu_domain_finalise_s2;

Or, don't populate IAS depending on AArch32 support as the driver
doesn't support it, effectively reverting:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f0c453dbcce7767cd868deb809ba68083c93954e

> 
> > In that case, concatenation is mandatory in some cases.
> > Document those and add the checks, this can be simplified; instead
> > of adding extra checks we can say that concatenation is mandatory for:
> > - 4K, start level = 0 and OAS <= 42 bits.
> > - 16K, start level = 1 and OAS <= 40 bits.
> > Which cover all the missing cases.
> 
> AArch32 uses a fixed 4K granule, so this cannot impact 16K. Note that we
> don't support AArch32 for SMMUv3 anyway, and while this does technically
> apply to SMMUv1/2, Arm's MMU-400/401/500 implementations all have OAS fixed
> at 40/40/48 bits respectively, so I'd imagine it's quite a rare case to
> encounter in practice.

Yes, I don't have access to such HW.

Thanks,
Mostafa
> 
> Thanks,
> Robin.
> 
> > Reported-by: Tomasz Nowicki <tnowicki at google.com>
> > Fixes: 4dcac8407fe1 ("iommu/io-pgtable-arm: Fix stage-2 concatenation with 16K")
> > Signed-off-by: Mostafa Saleh <smostafa at google.com>
> > ---
> >   drivers/iommu/io-pgtable-arm.c | 10 ++++++----
> >   1 file changed, 6 insertions(+), 4 deletions(-)
> > 
> > diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
> > index e6626004b323..ecf27e86b429 100644
> > --- a/drivers/iommu/io-pgtable-arm.c
> > +++ b/drivers/iommu/io-pgtable-arm.c
> > @@ -223,6 +223,8 @@ static inline int arm_lpae_max_entries(int i, struct arm_lpae_io_pgtable *data)
> >    *   b) 40 bits PA size with 16K: use level 2 instead of level 1 (16 tables for ias = oas)
> >    *   c) 42 bits PA size with 4K: use level 1 instead of level 0 (8 tables for ias = oas)
> >    *   d) 48 bits PA size with 16K: use level 1 instead of level 0 (2 tables for ias = oas)
> > + *   f) 32/36 bits PA size with 4K and 40 bits IAS: use level 1 instead of level 0
> > + *   g) 32/36 bits PA size with 16K and 40 bits IAS: use level 2 instead of level 1
> >    */
> >   static inline bool arm_lpae_concat_mandatory(struct io_pgtable_cfg *cfg,
> >   					     struct arm_lpae_io_pgtable *data)
> > @@ -234,13 +236,13 @@ static inline bool arm_lpae_concat_mandatory(struct io_pgtable_cfg *cfg,
> >   	if ((ARM_LPAE_GRANULE(data) == SZ_16K) && (data->start_level == 0))
> >   		return (oas == 48) || (ias == 48);
> > -	/* Covers 2.a and 2.c */
> > +	/* Covers 2.a, 2.c and 2.f */
> >   	if ((ARM_LPAE_GRANULE(data) == SZ_4K) && (data->start_level == 0))
> > -		return (oas == 40) || (oas == 42);
> > +		return (oas <= 42);
> > -	/* Case 2.b */
> > +	/* Case 2.b and 2.g */
> >   	return (ARM_LPAE_GRANULE(data) == SZ_16K) &&
> > -	       (data->start_level == 1) && (oas == 40);
> > +	       (data->start_level == 1) && (oas <= 40);
> >   }
> >   static dma_addr_t __arm_lpae_dma_addr(void *pages)
> 



More information about the linux-arm-kernel mailing list