All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
@ 2026-02-09 11:25 Liviu Dudau
  2026-02-09 12:31 ` Steven Price
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Liviu Dudau @ 2026-02-09 11:25 UTC (permalink / raw)
  To: Will Deacon
  Cc: Robin Murphy, Joerg Roedel, Rob Clark, Boris Brezillon,
	Steven Price, linux-arm-kernel, iommu, dri-devel, linux-kernel,
	Karunika Choo, Liviu Dudau

From: Liviu Dudau <liviu@dudau.co.uk>

The Arm Mali v10+ GPU drivers have been (ab)using the ARM_64_LPAE_S1
format as they are mostly compatible with it and some of the gaps
left in the code to allow for ARM_MALI_LPAE format (pre-v10 GPUs)
is helping to paper over differences. In preparation for adding support
for changes introduced in v15 GPUs, add a format specific for modern
Mali GPUs.

Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
---

This patch is trying to gauge interest in adding proper support for Arm Mali
CSF GPUs via the simple approach of extending the generic Arm page table code
to add support for the PTE format of the GPUs. In order to test the changes
I've decided to add the phba bits to the arm_lpae_s1_cfg struct to validate
the allocation and setup of the page table entries, but in the end I'm
targetting the specific arm_mali_csf_cfg structure that will support
the GPUs PTEs.

I'm interested to learn if this approach is considered sane and what I need to
pay attention to when adding a new struct to the io_pgtable_cfg union. The patch
is intentionally not complete with all the changes that switching to the new
struct will entail as I didn't wanted to be dragged into a full code review, but
I can add them if wanted.


Best regards,
Liviu

---
 drivers/iommu/io-pgtable-arm.c | 161 ++++++++++++++++++++++++++++++++-
 drivers/iommu/io-pgtable.c     |   1 +
 include/linux/io-pgtable.h     |  18 ++++
 3 files changed, 179 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index 05d63fe92e436..48aea598ab0c9 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -482,6 +482,7 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
 	arm_lpae_iopte pte;
 
 	if (data->iop.fmt == ARM_64_LPAE_S1 ||
+	    data->iop.fmt == ARM_MALI_CSF ||
 	    data->iop.fmt == ARM_32_LPAE_S1) {
 		pte = ARM_LPAE_PTE_nG;
 		if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
@@ -569,6 +570,8 @@ static int arm_lpae_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
 		return -EINVAL;
 
 	prot = arm_lpae_prot_to_pte(data, iommu_prot);
+	if (data->iop.fmt == ARM_MALI_CSF)
+		prot |= cfg->arm_lpae_s1_cfg.pbha;
 	ret = __arm_lpae_map(data, iova, paddr, pgsize, pgcount, prot, lvl,
 			     ptep, gfp, mapped);
 	/*
@@ -864,7 +867,8 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
 		return -EINVAL;
 	if (WARN_ON((iova + size - 1) & ~(BIT(cfg->ias) - 1)))
 		return -EINVAL;
-	if (data->iop.fmt != ARM_64_LPAE_S1)
+	if (data->iop.fmt != ARM_64_LPAE_S1 ||
+	    data->iop.fmt != ARM_MALI_CSF)
 		return -EINVAL;
 
 	return __arm_lpae_iopte_walk(data, &walk_data, ptep, lvl);
@@ -1236,6 +1240,155 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
 	return NULL;
 }
 
+static struct io_pgtable *
+arm_mali_csf_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
+{
+	unsigned int max_addr_bits = 48;
+	unsigned long granule, page_sizes;
+	struct arm_lpae_io_pgtable *data;
+	typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr;
+	int levels, va_bits, pg_shift;
+	u64 reg;
+
+	if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_TTBR1 |
+			    IO_PGTABLE_QUIRK_NO_WARN))
+		return NULL;
+
+	if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K)))
+		return NULL;
+
+	if (cfg->pgsize_bitmap & PAGE_SIZE)
+		granule = PAGE_SIZE;
+	else if (cfg->pgsize_bitmap & ~PAGE_MASK)
+		granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK);
+	else if (cfg->pgsize_bitmap & PAGE_MASK)
+		granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK);
+	else
+		granule = 0;
+
+	switch (granule) {
+	case SZ_4K:
+		page_sizes = (SZ_4K | SZ_2M | SZ_1G);
+		break;
+	case SZ_16K:
+		page_sizes = (SZ_16K | SZ_32M | SZ_64G);
+		break;
+	default:
+		page_sizes = 0;
+	}
+
+	cfg->pgsize_bitmap &= page_sizes;
+	cfg->ias = min(cfg->ias, max_addr_bits);
+	cfg->oas = min(cfg->oas, max_addr_bits);
+
+	data = kmalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return NULL;
+
+	pg_shift = __ffs(cfg->pgsize_bitmap);
+	data->bits_per_level = pg_shift - ilog2(sizeof(arm_lpae_iopte));
+
+	va_bits = cfg->ias - pg_shift;
+	levels = DIV_ROUND_UP(va_bits, data->bits_per_level);
+	data->start_level = ARM_LPAE_MAX_LEVELS - levels;
+
+	/* Calculate the actual size of our pgd (without concatenation) */
+	data->pgd_bits = va_bits - (data->bits_per_level * (levels - 1));
+
+	data->iop.ops = (struct io_pgtable_ops) {
+		.map_pages	= arm_lpae_map_pages,
+		.unmap_pages	= arm_lpae_unmap_pages,
+		.iova_to_phys	= arm_lpae_iova_to_phys,
+		.read_and_clear_dirty = arm_lpae_read_and_clear_dirty,
+		.pgtable_walk	= arm_lpae_pgtable_walk,
+	};
+
+	/* TCR */
+	if (cfg->coherent_walk) {
+		tcr->sh = ARM_LPAE_TCR_SH_IS;
+		tcr->irgn = ARM_LPAE_TCR_RGN_WBWA;
+		tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
+		if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA)
+			goto out_free_data;
+	} else {
+		tcr->sh = ARM_LPAE_TCR_SH_OS;
+		tcr->irgn = ARM_LPAE_TCR_RGN_NC;
+		if (!(cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA))
+			tcr->orgn = ARM_LPAE_TCR_RGN_NC;
+		else
+			tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
+	}
+
+	switch (ARM_LPAE_GRANULE(data)) {
+	case SZ_4K:
+		tcr->tg = ARM_LPAE_TCR_TG0_4K;
+		break;
+	case SZ_16K:
+		tcr->tg = ARM_LPAE_TCR_TG0_16K;
+		break;
+	case SZ_64K:
+		tcr->tg = ARM_LPAE_TCR_TG0_64K;
+		break;
+	}
+
+	switch (cfg->oas) {
+	case 32:
+		tcr->ips = ARM_LPAE_TCR_PS_32_BIT;
+		break;
+	case 36:
+		tcr->ips = ARM_LPAE_TCR_PS_36_BIT;
+		break;
+	case 40:
+		tcr->ips = ARM_LPAE_TCR_PS_40_BIT;
+		break;
+	case 42:
+		tcr->ips = ARM_LPAE_TCR_PS_42_BIT;
+		break;
+	case 44:
+		tcr->ips = ARM_LPAE_TCR_PS_44_BIT;
+		break;
+	case 48:
+		tcr->ips = ARM_LPAE_TCR_PS_48_BIT;
+		break;
+	case 52:
+		tcr->ips = ARM_LPAE_TCR_PS_52_BIT;
+		break;
+	default:
+		goto out_free_data;
+	}
+
+	tcr->tsz = 64ULL - cfg->ias;
+
+	/* MAIRs */
+	reg = (ARM_LPAE_MAIR_ATTR_NC
+	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
+	      (ARM_LPAE_MAIR_ATTR_WBRWA
+	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
+	      (ARM_LPAE_MAIR_ATTR_DEVICE
+	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)) |
+	      (ARM_LPAE_MAIR_ATTR_INC_OWBRWA
+	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE));
+
+	cfg->arm_lpae_s1_cfg.mair = reg;
+
+	/* Looking good; allocate a pgd */
+	data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data),
+					   GFP_KERNEL, cfg, cookie);
+	if (!data->pgd)
+		goto out_free_data;
+
+	/* Ensure the empty pgd is visible before any actual TTBR write */
+	wmb();
+
+	/* TTBR */
+	cfg->arm_lpae_s1_cfg.ttbr = virt_to_phys(data->pgd);
+	return &data->iop;
+
+out_free_data:
+	kfree(data);
+	return NULL;
+}
+
 struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
 	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
 	.alloc	= arm_64_lpae_alloc_pgtable_s1,
@@ -1265,3 +1418,9 @@ struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
 	.alloc	= arm_mali_lpae_alloc_pgtable,
 	.free	= arm_lpae_free_pgtable,
 };
+
+struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns = {
+	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
+	.alloc	= arm_mali_csf_alloc_pgtable,
+	.free	= arm_lpae_free_pgtable,
+};
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 843fec8e8a511..1f43f898a8121 100644
--- a/drivers/iommu/io-pgtable.c
+++ b/drivers/iommu/io-pgtable.c
@@ -20,6 +20,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
 	[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
 	[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
 	[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
+	[ARM_MALI_CSF] = &io_pgtable_arm_mali_csf_init_fns,
 #endif
 #ifdef CONFIG_IOMMU_IO_PGTABLE_DART
 	[APPLE_DART] = &io_pgtable_apple_dart_init_fns,
diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
index 7a1516011ccf7..fc9776f71a963 100644
--- a/include/linux/io-pgtable.h
+++ b/include/linux/io-pgtable.h
@@ -17,6 +17,7 @@ enum io_pgtable_fmt {
 	ARM_MALI_LPAE,
 	APPLE_DART,
 	APPLE_DART2,
+	ARM_MALI_CSF,
 	IO_PGTABLE_NUM_FMTS,
 };
 
@@ -148,6 +149,8 @@ struct io_pgtable_cfg {
 				u32	tsz:6;
 			}	tcr;
 			u64	mair;
+			/* ToDo: remove this when switching to arm_mali_csf_cfg struct */
+			u64	pbha;
 		} arm_lpae_s1_cfg;
 
 		struct {
@@ -175,6 +178,20 @@ struct io_pgtable_cfg {
 			u64	memattr;
 		} arm_mali_lpae_cfg;
 
+		/* ToDo: switch to this structure for Mali CSF GPUs
+		  struct {
+			u64	transtab;
+			struct {
+				u32	pbha:4;
+				u32	ra:1;
+				u32	sh:2;
+				u32	memattr:2;
+				u32	mode:4;
+			} transcfg;
+			u64 memattr;
+		} arm_mali_csf_cfg;
+		*/
+
 		struct {
 			u64 ttbr[4];
 			u32 n_ttbrs;
@@ -320,6 +337,7 @@ extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
 extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
 extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns;
 extern struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns;
+extern struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns;
 extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v1_init_fns;
 extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v2_init_fns;
 extern struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns;
-- 
2.52.0



^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 11:25 [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format Liviu Dudau
@ 2026-02-09 12:31 ` Steven Price
  2026-02-09 13:27   ` Liviu Dudau
  2026-02-09 14:16 ` Daniel Almeida
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 12+ messages in thread
From: Steven Price @ 2026-02-09 12:31 UTC (permalink / raw)
  To: Liviu Dudau, Will Deacon
  Cc: Robin Murphy, Joerg Roedel, Rob Clark, Boris Brezillon,
	linux-arm-kernel, iommu, dri-devel, linux-kernel, Karunika Choo,
	Liviu Dudau

On 09/02/2026 11:25, Liviu Dudau wrote:
> From: Liviu Dudau <liviu@dudau.co.uk>
> 
> The Arm Mali v10+ GPU drivers have been (ab)using the ARM_64_LPAE_S1
> format as they are mostly compatible with it and some of the gaps
> left in the code to allow for ARM_MALI_LPAE format (pre-v10 GPUs)
> is helping to paper over differences. In preparation for adding support
> for changes introduced in v15 GPUs, add a format specific for modern
> Mali GPUs.
> 
> Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
> ---
> 
> This patch is trying to gauge interest in adding proper support for Arm Mali
> CSF GPUs via the simple approach of extending the generic Arm page table code
> to add support for the PTE format of the GPUs. In order to test the changes
> I've decided to add the phba bits to the arm_lpae_s1_cfg struct to validate
> the allocation and setup of the page table entries, but in the end I'm
> targetting the specific arm_mali_csf_cfg structure that will support
> the GPUs PTEs.

Other than the addition of the PBHA bits (which are part of the VMSAv8
page table format anyway) what are we expecting to be different between
the Mali format and the CPU architectural format?

For Midgard GPUs the page table format was "inspired" by LPAE but was
explicitly different in some cases - so a new format was required. But I
can't actually spot any differences in the GPU format to what VMSAv8-64
describes (albeit the GPU format is less flexible than all the options
the CPU format describes).

I can see why we might want more functionality (e.g. PBHA): I'm just not
sure what the reason for having another special Mali format is - why
can't this be in the generic code?

> 
> I'm interested to learn if this approach is considered sane and what I need to
> pay attention to when adding a new struct to the io_pgtable_cfg union. The patch
> is intentionally not complete with all the changes that switching to the new
> struct will entail as I didn't wanted to be dragged into a full code review, but
> I can add them if wanted.
> 
> 
> Best regards,
> Liviu
> 
> ---
>  drivers/iommu/io-pgtable-arm.c | 161 ++++++++++++++++++++++++++++++++-
>  drivers/iommu/io-pgtable.c     |   1 +
>  include/linux/io-pgtable.h     |  18 ++++
>  3 files changed, 179 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
> index 05d63fe92e436..48aea598ab0c9 100644
> --- a/drivers/iommu/io-pgtable-arm.c
> +++ b/drivers/iommu/io-pgtable-arm.c
> @@ -482,6 +482,7 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
>  	arm_lpae_iopte pte;
>  
>  	if (data->iop.fmt == ARM_64_LPAE_S1 ||
> +	    data->iop.fmt == ARM_MALI_CSF ||
>  	    data->iop.fmt == ARM_32_LPAE_S1) {
>  		pte = ARM_LPAE_PTE_nG;
>  		if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
> @@ -569,6 +570,8 @@ static int arm_lpae_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
>  		return -EINVAL;
>  
>  	prot = arm_lpae_prot_to_pte(data, iommu_prot);
> +	if (data->iop.fmt == ARM_MALI_CSF)
> +		prot |= cfg->arm_lpae_s1_cfg.pbha;
>  	ret = __arm_lpae_map(data, iova, paddr, pgsize, pgcount, prot, lvl,
>  			     ptep, gfp, mapped);
>  	/*
> @@ -864,7 +867,8 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
>  		return -EINVAL;
>  	if (WARN_ON((iova + size - 1) & ~(BIT(cfg->ias) - 1)))
>  		return -EINVAL;
> -	if (data->iop.fmt != ARM_64_LPAE_S1)
> +	if (data->iop.fmt != ARM_64_LPAE_S1 ||
> +	    data->iop.fmt != ARM_MALI_CSF)
>  		return -EINVAL;
>  
>  	return __arm_lpae_iopte_walk(data, &walk_data, ptep, lvl);
> @@ -1236,6 +1240,155 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
>  	return NULL;
>  }
>  
> +static struct io_pgtable *
> +arm_mali_csf_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
> +{
> +	unsigned int max_addr_bits = 48;
> +	unsigned long granule, page_sizes;
> +	struct arm_lpae_io_pgtable *data;
> +	typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr;
> +	int levels, va_bits, pg_shift;
> +	u64 reg;
> +
> +	if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_TTBR1 |
> +			    IO_PGTABLE_QUIRK_NO_WARN))
> +		return NULL;
> +
> +	if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K)))
> +		return NULL;

This check should be moved down...

> +
> +	if (cfg->pgsize_bitmap & PAGE_SIZE)
> +		granule = PAGE_SIZE;
> +	else if (cfg->pgsize_bitmap & ~PAGE_MASK)
> +		granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK);
> +	else if (cfg->pgsize_bitmap & PAGE_MASK)
> +		granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK);
> +	else
> +		granule = 0;
> +
> +	switch (granule) {
> +	case SZ_4K:
> +		page_sizes = (SZ_4K | SZ_2M | SZ_1G);
> +		break;
> +	case SZ_16K:
> +		page_sizes = (SZ_16K | SZ_32M | SZ_64G);
> +		break;
> +	default:
> +		page_sizes = 0;
> +	}
> +
> +	cfg->pgsize_bitmap &= page_sizes;

... to after this line. Otherwise we can end up with cfg->pgsize_bitmap
being zero and the function succeeding.

Generally this is mostly just a copy of arm_lpae_alloc_pgtable() (with
arm_lpae_restrict_pgsizes() inlined) - but with added bugs. Which is why
I'm wary of adding another Mali-special unless there's good reason. It
still refers to a whole bunch of _LPAE_ defines/functions - which means
any refactor to LPAE would have to fix up this code too.

Thanks,
Steve

> +	cfg->ias = min(cfg->ias, max_addr_bits);
> +	cfg->oas = min(cfg->oas, max_addr_bits);
> +
> +	data = kmalloc(sizeof(*data), GFP_KERNEL);
> +	if (!data)
> +		return NULL;
> +
> +	pg_shift = __ffs(cfg->pgsize_bitmap);
> +	data->bits_per_level = pg_shift - ilog2(sizeof(arm_lpae_iopte));
> +
> +	va_bits = cfg->ias - pg_shift;
> +	levels = DIV_ROUND_UP(va_bits, data->bits_per_level);
> +	data->start_level = ARM_LPAE_MAX_LEVELS - levels;
> +
> +	/* Calculate the actual size of our pgd (without concatenation) */
> +	data->pgd_bits = va_bits - (data->bits_per_level * (levels - 1));
> +
> +	data->iop.ops = (struct io_pgtable_ops) {
> +		.map_pages	= arm_lpae_map_pages,
> +		.unmap_pages	= arm_lpae_unmap_pages,
> +		.iova_to_phys	= arm_lpae_iova_to_phys,
> +		.read_and_clear_dirty = arm_lpae_read_and_clear_dirty,
> +		.pgtable_walk	= arm_lpae_pgtable_walk,
> +	};
> +
> +	/* TCR */
> +	if (cfg->coherent_walk) {
> +		tcr->sh = ARM_LPAE_TCR_SH_IS;
> +		tcr->irgn = ARM_LPAE_TCR_RGN_WBWA;
> +		tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
> +		if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA)
> +			goto out_free_data;
> +	} else {
> +		tcr->sh = ARM_LPAE_TCR_SH_OS;
> +		tcr->irgn = ARM_LPAE_TCR_RGN_NC;
> +		if (!(cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA))
> +			tcr->orgn = ARM_LPAE_TCR_RGN_NC;
> +		else
> +			tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
> +	}
> +
> +	switch (ARM_LPAE_GRANULE(data)) {
> +	case SZ_4K:
> +		tcr->tg = ARM_LPAE_TCR_TG0_4K;
> +		break;
> +	case SZ_16K:
> +		tcr->tg = ARM_LPAE_TCR_TG0_16K;
> +		break;
> +	case SZ_64K:
> +		tcr->tg = ARM_LPAE_TCR_TG0_64K;
> +		break;
> +	}
> +
> +	switch (cfg->oas) {
> +	case 32:
> +		tcr->ips = ARM_LPAE_TCR_PS_32_BIT;
> +		break;
> +	case 36:
> +		tcr->ips = ARM_LPAE_TCR_PS_36_BIT;
> +		break;
> +	case 40:
> +		tcr->ips = ARM_LPAE_TCR_PS_40_BIT;
> +		break;
> +	case 42:
> +		tcr->ips = ARM_LPAE_TCR_PS_42_BIT;
> +		break;
> +	case 44:
> +		tcr->ips = ARM_LPAE_TCR_PS_44_BIT;
> +		break;
> +	case 48:
> +		tcr->ips = ARM_LPAE_TCR_PS_48_BIT;
> +		break;
> +	case 52:
> +		tcr->ips = ARM_LPAE_TCR_PS_52_BIT;
> +		break;
> +	default:
> +		goto out_free_data;
> +	}
> +
> +	tcr->tsz = 64ULL - cfg->ias;
> +
> +	/* MAIRs */
> +	reg = (ARM_LPAE_MAIR_ATTR_NC
> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
> +	      (ARM_LPAE_MAIR_ATTR_WBRWA
> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
> +	      (ARM_LPAE_MAIR_ATTR_DEVICE
> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)) |
> +	      (ARM_LPAE_MAIR_ATTR_INC_OWBRWA
> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE));
> +
> +	cfg->arm_lpae_s1_cfg.mair = reg;
> +
> +	/* Looking good; allocate a pgd */
> +	data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data),
> +					   GFP_KERNEL, cfg, cookie);
> +	if (!data->pgd)
> +		goto out_free_data;
> +
> +	/* Ensure the empty pgd is visible before any actual TTBR write */
> +	wmb();
> +
> +	/* TTBR */
> +	cfg->arm_lpae_s1_cfg.ttbr = virt_to_phys(data->pgd);
> +	return &data->iop;
> +
> +out_free_data:
> +	kfree(data);
> +	return NULL;
> +}
> +
>  struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
>  	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
>  	.alloc	= arm_64_lpae_alloc_pgtable_s1,
> @@ -1265,3 +1418,9 @@ struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
>  	.alloc	= arm_mali_lpae_alloc_pgtable,
>  	.free	= arm_lpae_free_pgtable,
>  };
> +
> +struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns = {
> +	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
> +	.alloc	= arm_mali_csf_alloc_pgtable,
> +	.free	= arm_lpae_free_pgtable,
> +};
> diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
> index 843fec8e8a511..1f43f898a8121 100644
> --- a/drivers/iommu/io-pgtable.c
> +++ b/drivers/iommu/io-pgtable.c
> @@ -20,6 +20,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
>  	[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
>  	[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
>  	[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
> +	[ARM_MALI_CSF] = &io_pgtable_arm_mali_csf_init_fns,
>  #endif
>  #ifdef CONFIG_IOMMU_IO_PGTABLE_DART
>  	[APPLE_DART] = &io_pgtable_apple_dart_init_fns,
> diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
> index 7a1516011ccf7..fc9776f71a963 100644
> --- a/include/linux/io-pgtable.h
> +++ b/include/linux/io-pgtable.h
> @@ -17,6 +17,7 @@ enum io_pgtable_fmt {
>  	ARM_MALI_LPAE,
>  	APPLE_DART,
>  	APPLE_DART2,
> +	ARM_MALI_CSF,
>  	IO_PGTABLE_NUM_FMTS,
>  };
>  
> @@ -148,6 +149,8 @@ struct io_pgtable_cfg {
>  				u32	tsz:6;
>  			}	tcr;
>  			u64	mair;
> +			/* ToDo: remove this when switching to arm_mali_csf_cfg struct */
> +			u64	pbha;
>  		} arm_lpae_s1_cfg;
>  
>  		struct {
> @@ -175,6 +178,20 @@ struct io_pgtable_cfg {
>  			u64	memattr;
>  		} arm_mali_lpae_cfg;
>  
> +		/* ToDo: switch to this structure for Mali CSF GPUs
> +		  struct {
> +			u64	transtab;
> +			struct {
> +				u32	pbha:4;
> +				u32	ra:1;
> +				u32	sh:2;
> +				u32	memattr:2;
> +				u32	mode:4;
> +			} transcfg;
> +			u64 memattr;
> +		} arm_mali_csf_cfg;
> +		*/
> +
>  		struct {
>  			u64 ttbr[4];
>  			u32 n_ttbrs;
> @@ -320,6 +337,7 @@ extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
>  extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
>  extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns;
>  extern struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns;
> +extern struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns;
>  extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v1_init_fns;
>  extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v2_init_fns;
>  extern struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns;



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 12:31 ` Steven Price
@ 2026-02-09 13:27   ` Liviu Dudau
  2026-02-09 13:57     ` Steven Price
  0 siblings, 1 reply; 12+ messages in thread
From: Liviu Dudau @ 2026-02-09 13:27 UTC (permalink / raw)
  To: Steven Price
  Cc: Will Deacon, Robin Murphy, Joerg Roedel, Rob Clark,
	Boris Brezillon, linux-arm-kernel, iommu, dri-devel, linux-kernel,
	Karunika Choo, Liviu Dudau

On Mon, Feb 09, 2026 at 12:31:36PM +0000, Steven Price wrote:
> On 09/02/2026 11:25, Liviu Dudau wrote:
> > From: Liviu Dudau <liviu@dudau.co.uk>
> > 
> > The Arm Mali v10+ GPU drivers have been (ab)using the ARM_64_LPAE_S1
> > format as they are mostly compatible with it and some of the gaps
> > left in the code to allow for ARM_MALI_LPAE format (pre-v10 GPUs)
> > is helping to paper over differences. In preparation for adding support
> > for changes introduced in v15 GPUs, add a format specific for modern
> > Mali GPUs.
> > 
> > Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
> > ---
> > 
> > This patch is trying to gauge interest in adding proper support for Arm Mali
> > CSF GPUs via the simple approach of extending the generic Arm page table code
> > to add support for the PTE format of the GPUs. In order to test the changes
> > I've decided to add the phba bits to the arm_lpae_s1_cfg struct to validate
> > the allocation and setup of the page table entries, but in the end I'm
> > targetting the specific arm_mali_csf_cfg structure that will support
> > the GPUs PTEs.
> 
> Other than the addition of the PBHA bits (which are part of the VMSAv8
> page table format anyway) what are we expecting to be different between
> the Mali format and the CPU architectural format?

The bits at the moment are not that different, mostly renaming. However, v15+
GPUs are going to introduce a different scheme for the PTEs, so adding the
code now will help future changes.

> 
> For Midgard GPUs the page table format was "inspired" by LPAE but was
> explicitly different in some cases - so a new format was required. But I
> can't actually spot any differences in the GPU format to what VMSAv8-64
> describes (albeit the GPU format is less flexible than all the options
> the CPU format describes).

Yes, the supported page table sizes is a bit of a head scratcher. v10+ claim
that 16KB pages are not supported, only 4KB and 64KB. v15+ GPUs are going
to claim that only 4KB and 16KB page sizes are supported, not 64KB.

> 
> I can see why we might want more functionality (e.g. PBHA): I'm just not
> sure what the reason for having another special Mali format is - why
> can't this be in the generic code?

What do you mean by "generic code"? I thought these files are the most generic
support code for Arm SMMUs. But to answer your question: I think once we introduce
the v15+ GPU's page table formats keeping track of which type of page table you're
using without a CSF specific one will get trickier.

Ultimately the role of this RFC is to start a discussion and to figure out a path
forward for CSF GPUs where we want now to tighen a bit the formats we support and
add PBHA and in the future we want to add support for v15+ page formats.

Best regards,
Liviu


> 
> > 
> > I'm interested to learn if this approach is considered sane and what I need to
> > pay attention to when adding a new struct to the io_pgtable_cfg union. The patch
> > is intentionally not complete with all the changes that switching to the new
> > struct will entail as I didn't wanted to be dragged into a full code review, but
> > I can add them if wanted.
> > 
> > 
> > Best regards,
> > Liviu
> > 
> > ---
> >  drivers/iommu/io-pgtable-arm.c | 161 ++++++++++++++++++++++++++++++++-
> >  drivers/iommu/io-pgtable.c     |   1 +
> >  include/linux/io-pgtable.h     |  18 ++++
> >  3 files changed, 179 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
> > index 05d63fe92e436..48aea598ab0c9 100644
> > --- a/drivers/iommu/io-pgtable-arm.c
> > +++ b/drivers/iommu/io-pgtable-arm.c
> > @@ -482,6 +482,7 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
> >  	arm_lpae_iopte pte;
> >  
> >  	if (data->iop.fmt == ARM_64_LPAE_S1 ||
> > +	    data->iop.fmt == ARM_MALI_CSF ||
> >  	    data->iop.fmt == ARM_32_LPAE_S1) {
> >  		pte = ARM_LPAE_PTE_nG;
> >  		if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
> > @@ -569,6 +570,8 @@ static int arm_lpae_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
> >  		return -EINVAL;
> >  
> >  	prot = arm_lpae_prot_to_pte(data, iommu_prot);
> > +	if (data->iop.fmt == ARM_MALI_CSF)
> > +		prot |= cfg->arm_lpae_s1_cfg.pbha;
> >  	ret = __arm_lpae_map(data, iova, paddr, pgsize, pgcount, prot, lvl,
> >  			     ptep, gfp, mapped);
> >  	/*
> > @@ -864,7 +867,8 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
> >  		return -EINVAL;
> >  	if (WARN_ON((iova + size - 1) & ~(BIT(cfg->ias) - 1)))
> >  		return -EINVAL;
> > -	if (data->iop.fmt != ARM_64_LPAE_S1)
> > +	if (data->iop.fmt != ARM_64_LPAE_S1 ||
> > +	    data->iop.fmt != ARM_MALI_CSF)
> >  		return -EINVAL;
> >  
> >  	return __arm_lpae_iopte_walk(data, &walk_data, ptep, lvl);
> > @@ -1236,6 +1240,155 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
> >  	return NULL;
> >  }
> >  
> > +static struct io_pgtable *
> > +arm_mali_csf_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
> > +{
> > +	unsigned int max_addr_bits = 48;
> > +	unsigned long granule, page_sizes;
> > +	struct arm_lpae_io_pgtable *data;
> > +	typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr;
> > +	int levels, va_bits, pg_shift;
> > +	u64 reg;
> > +
> > +	if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_TTBR1 |
> > +			    IO_PGTABLE_QUIRK_NO_WARN))
> > +		return NULL;
> > +
> > +	if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K)))
> > +		return NULL;
> 
> This check should be moved down...
> 
> > +
> > +	if (cfg->pgsize_bitmap & PAGE_SIZE)
> > +		granule = PAGE_SIZE;
> > +	else if (cfg->pgsize_bitmap & ~PAGE_MASK)
> > +		granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK);
> > +	else if (cfg->pgsize_bitmap & PAGE_MASK)
> > +		granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK);
> > +	else
> > +		granule = 0;
> > +
> > +	switch (granule) {
> > +	case SZ_4K:
> > +		page_sizes = (SZ_4K | SZ_2M | SZ_1G);
> > +		break;
> > +	case SZ_16K:
> > +		page_sizes = (SZ_16K | SZ_32M | SZ_64G);
> > +		break;
> > +	default:
> > +		page_sizes = 0;
> > +	}
> > +
> > +	cfg->pgsize_bitmap &= page_sizes;
> 
> ... to after this line. Otherwise we can end up with cfg->pgsize_bitmap
> being zero and the function succeeding.
> 
> Generally this is mostly just a copy of arm_lpae_alloc_pgtable() (with
> arm_lpae_restrict_pgsizes() inlined) - but with added bugs. Which is why
> I'm wary of adding another Mali-special unless there's good reason. It
> still refers to a whole bunch of _LPAE_ defines/functions - which means
> any refactor to LPAE would have to fix up this code too.
> 
> Thanks,
> Steve
> 
> > +	cfg->ias = min(cfg->ias, max_addr_bits);
> > +	cfg->oas = min(cfg->oas, max_addr_bits);
> > +
> > +	data = kmalloc(sizeof(*data), GFP_KERNEL);
> > +	if (!data)
> > +		return NULL;
> > +
> > +	pg_shift = __ffs(cfg->pgsize_bitmap);
> > +	data->bits_per_level = pg_shift - ilog2(sizeof(arm_lpae_iopte));
> > +
> > +	va_bits = cfg->ias - pg_shift;
> > +	levels = DIV_ROUND_UP(va_bits, data->bits_per_level);
> > +	data->start_level = ARM_LPAE_MAX_LEVELS - levels;
> > +
> > +	/* Calculate the actual size of our pgd (without concatenation) */
> > +	data->pgd_bits = va_bits - (data->bits_per_level * (levels - 1));
> > +
> > +	data->iop.ops = (struct io_pgtable_ops) {
> > +		.map_pages	= arm_lpae_map_pages,
> > +		.unmap_pages	= arm_lpae_unmap_pages,
> > +		.iova_to_phys	= arm_lpae_iova_to_phys,
> > +		.read_and_clear_dirty = arm_lpae_read_and_clear_dirty,
> > +		.pgtable_walk	= arm_lpae_pgtable_walk,
> > +	};
> > +
> > +	/* TCR */
> > +	if (cfg->coherent_walk) {
> > +		tcr->sh = ARM_LPAE_TCR_SH_IS;
> > +		tcr->irgn = ARM_LPAE_TCR_RGN_WBWA;
> > +		tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
> > +		if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA)
> > +			goto out_free_data;
> > +	} else {
> > +		tcr->sh = ARM_LPAE_TCR_SH_OS;
> > +		tcr->irgn = ARM_LPAE_TCR_RGN_NC;
> > +		if (!(cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA))
> > +			tcr->orgn = ARM_LPAE_TCR_RGN_NC;
> > +		else
> > +			tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
> > +	}
> > +
> > +	switch (ARM_LPAE_GRANULE(data)) {
> > +	case SZ_4K:
> > +		tcr->tg = ARM_LPAE_TCR_TG0_4K;
> > +		break;
> > +	case SZ_16K:
> > +		tcr->tg = ARM_LPAE_TCR_TG0_16K;
> > +		break;
> > +	case SZ_64K:
> > +		tcr->tg = ARM_LPAE_TCR_TG0_64K;
> > +		break;
> > +	}
> > +
> > +	switch (cfg->oas) {
> > +	case 32:
> > +		tcr->ips = ARM_LPAE_TCR_PS_32_BIT;
> > +		break;
> > +	case 36:
> > +		tcr->ips = ARM_LPAE_TCR_PS_36_BIT;
> > +		break;
> > +	case 40:
> > +		tcr->ips = ARM_LPAE_TCR_PS_40_BIT;
> > +		break;
> > +	case 42:
> > +		tcr->ips = ARM_LPAE_TCR_PS_42_BIT;
> > +		break;
> > +	case 44:
> > +		tcr->ips = ARM_LPAE_TCR_PS_44_BIT;
> > +		break;
> > +	case 48:
> > +		tcr->ips = ARM_LPAE_TCR_PS_48_BIT;
> > +		break;
> > +	case 52:
> > +		tcr->ips = ARM_LPAE_TCR_PS_52_BIT;
> > +		break;
> > +	default:
> > +		goto out_free_data;
> > +	}
> > +
> > +	tcr->tsz = 64ULL - cfg->ias;
> > +
> > +	/* MAIRs */
> > +	reg = (ARM_LPAE_MAIR_ATTR_NC
> > +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
> > +	      (ARM_LPAE_MAIR_ATTR_WBRWA
> > +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
> > +	      (ARM_LPAE_MAIR_ATTR_DEVICE
> > +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)) |
> > +	      (ARM_LPAE_MAIR_ATTR_INC_OWBRWA
> > +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE));
> > +
> > +	cfg->arm_lpae_s1_cfg.mair = reg;
> > +
> > +	/* Looking good; allocate a pgd */
> > +	data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data),
> > +					   GFP_KERNEL, cfg, cookie);
> > +	if (!data->pgd)
> > +		goto out_free_data;
> > +
> > +	/* Ensure the empty pgd is visible before any actual TTBR write */
> > +	wmb();
> > +
> > +	/* TTBR */
> > +	cfg->arm_lpae_s1_cfg.ttbr = virt_to_phys(data->pgd);
> > +	return &data->iop;
> > +
> > +out_free_data:
> > +	kfree(data);
> > +	return NULL;
> > +}
> > +
> >  struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
> >  	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
> >  	.alloc	= arm_64_lpae_alloc_pgtable_s1,
> > @@ -1265,3 +1418,9 @@ struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
> >  	.alloc	= arm_mali_lpae_alloc_pgtable,
> >  	.free	= arm_lpae_free_pgtable,
> >  };
> > +
> > +struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns = {
> > +	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
> > +	.alloc	= arm_mali_csf_alloc_pgtable,
> > +	.free	= arm_lpae_free_pgtable,
> > +};
> > diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
> > index 843fec8e8a511..1f43f898a8121 100644
> > --- a/drivers/iommu/io-pgtable.c
> > +++ b/drivers/iommu/io-pgtable.c
> > @@ -20,6 +20,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
> >  	[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
> >  	[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
> >  	[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
> > +	[ARM_MALI_CSF] = &io_pgtable_arm_mali_csf_init_fns,
> >  #endif
> >  #ifdef CONFIG_IOMMU_IO_PGTABLE_DART
> >  	[APPLE_DART] = &io_pgtable_apple_dart_init_fns,
> > diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
> > index 7a1516011ccf7..fc9776f71a963 100644
> > --- a/include/linux/io-pgtable.h
> > +++ b/include/linux/io-pgtable.h
> > @@ -17,6 +17,7 @@ enum io_pgtable_fmt {
> >  	ARM_MALI_LPAE,
> >  	APPLE_DART,
> >  	APPLE_DART2,
> > +	ARM_MALI_CSF,
> >  	IO_PGTABLE_NUM_FMTS,
> >  };
> >  
> > @@ -148,6 +149,8 @@ struct io_pgtable_cfg {
> >  				u32	tsz:6;
> >  			}	tcr;
> >  			u64	mair;
> > +			/* ToDo: remove this when switching to arm_mali_csf_cfg struct */
> > +			u64	pbha;
> >  		} arm_lpae_s1_cfg;
> >  
> >  		struct {
> > @@ -175,6 +178,20 @@ struct io_pgtable_cfg {
> >  			u64	memattr;
> >  		} arm_mali_lpae_cfg;
> >  
> > +		/* ToDo: switch to this structure for Mali CSF GPUs
> > +		  struct {
> > +			u64	transtab;
> > +			struct {
> > +				u32	pbha:4;
> > +				u32	ra:1;
> > +				u32	sh:2;
> > +				u32	memattr:2;
> > +				u32	mode:4;
> > +			} transcfg;
> > +			u64 memattr;
> > +		} arm_mali_csf_cfg;
> > +		*/
> > +
> >  		struct {
> >  			u64 ttbr[4];
> >  			u32 n_ttbrs;
> > @@ -320,6 +337,7 @@ extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
> >  extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
> >  extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns;
> >  extern struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns;
> > +extern struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns;
> >  extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v1_init_fns;
> >  extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v2_init_fns;
> >  extern struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns;
> 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 13:27   ` Liviu Dudau
@ 2026-02-09 13:57     ` Steven Price
  2026-02-09 15:22       ` Liviu Dudau
  0 siblings, 1 reply; 12+ messages in thread
From: Steven Price @ 2026-02-09 13:57 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Will Deacon, Robin Murphy, Joerg Roedel, Rob Clark,
	Boris Brezillon, linux-arm-kernel, iommu, dri-devel, linux-kernel,
	Karunika Choo, Liviu Dudau

On 09/02/2026 13:27, Liviu Dudau wrote:
> On Mon, Feb 09, 2026 at 12:31:36PM +0000, Steven Price wrote:
>> On 09/02/2026 11:25, Liviu Dudau wrote:
>>> From: Liviu Dudau <liviu@dudau.co.uk>
>>>
>>> The Arm Mali v10+ GPU drivers have been (ab)using the ARM_64_LPAE_S1
>>> format as they are mostly compatible with it and some of the gaps
>>> left in the code to allow for ARM_MALI_LPAE format (pre-v10 GPUs)
>>> is helping to paper over differences. In preparation for adding support
>>> for changes introduced in v15 GPUs, add a format specific for modern
>>> Mali GPUs.
>>>
>>> Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
>>> ---
>>>
>>> This patch is trying to gauge interest in adding proper support for Arm Mali
>>> CSF GPUs via the simple approach of extending the generic Arm page table code
>>> to add support for the PTE format of the GPUs. In order to test the changes
>>> I've decided to add the phba bits to the arm_lpae_s1_cfg struct to validate
>>> the allocation and setup of the page table entries, but in the end I'm
>>> targetting the specific arm_mali_csf_cfg structure that will support
>>> the GPUs PTEs.
>>
>> Other than the addition of the PBHA bits (which are part of the VMSAv8
>> page table format anyway) what are we expecting to be different between
>> the Mali format and the CPU architectural format?
> 
> The bits at the moment are not that different, mostly renaming. However, v15+
> GPUs are going to introduce a different scheme for the PTEs, so adding the
> code now will help future changes.
> 
>>
>> For Midgard GPUs the page table format was "inspired" by LPAE but was
>> explicitly different in some cases - so a new format was required. But I
>> can't actually spot any differences in the GPU format to what VMSAv8-64
>> describes (albeit the GPU format is less flexible than all the options
>> the CPU format describes).
> 
> Yes, the supported page table sizes is a bit of a head scratcher. v10+ claim
> that 16KB pages are not supported, only 4KB and 64KB. v15+ GPUs are going
> to claim that only 4KB and 16KB page sizes are supported, not 64KB.

Ok, so this is a difference, although the CPU archiecture I believe says
all three "translation granule sizes" (i.e. page sizes) are optional. So
not supporting 64KB is just an implementation choice not a new format.

>>
>> I can see why we might want more functionality (e.g. PBHA): I'm just not
>> sure what the reason for having another special Mali format is - why
>> can't this be in the generic code?
> 
> What do you mean by "generic code"? I thought these files are the most generic

I just mean code shared between drivers - yes this file is the right
place - but the RFC adds what amounts to a copy/paste of the existing
code, just with 64KB dropped (at least from my skim through the code).
We need a more compelling reason to fork.

> support code for Arm SMMUs. But to answer your question: I think once we introduce
> the v15+ GPU's page table formats keeping track of which type of page table you're
> using without a CSF specific one will get trickier.

So I don't know what's being proposed in the latest GPUs - if there is
an actual incompatibility then it might be justified to create a new
format in the code. I'm somewhat surprised as we'd delibrately gone the
other way (dropping the old Mali-specific format and moving to the
standard format). But I think we need to know what the incompatibility
is to be able to judge the right approach in the code.

> Ultimately the role of this RFC is to start a discussion and to figure out a path
> forward for CSF GPUs where we want now to tighen a bit the formats we support and
> add PBHA and in the future we want to add support for v15+ page formats.

PBHA is definitely an area for discussion. AIUI there are out-of-tree
patches floating about for CPU support, but it hasn't been upstreamed. I
don't know if any serious attempt has been made to push it upstream, but
it's tricky because the architecture basically just says "IMPLEMENTATION
DEFINED" which means you are no longer coding to the architecture but a
specific implementation - and there's remarkably little documentation
about what PBHA is used for in practice.

I haven't looked into the GPU situation with PBHA - again it would be
good to have more details on how the bits would be set.

Thanks,
Steve

> Best regards,
> Liviu
> 
> 
>>
>>>
>>> I'm interested to learn if this approach is considered sane and what I need to
>>> pay attention to when adding a new struct to the io_pgtable_cfg union. The patch
>>> is intentionally not complete with all the changes that switching to the new
>>> struct will entail as I didn't wanted to be dragged into a full code review, but
>>> I can add them if wanted.
>>>
>>>
>>> Best regards,
>>> Liviu
>>>
>>> ---
>>>  drivers/iommu/io-pgtable-arm.c | 161 ++++++++++++++++++++++++++++++++-
>>>  drivers/iommu/io-pgtable.c     |   1 +
>>>  include/linux/io-pgtable.h     |  18 ++++
>>>  3 files changed, 179 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
>>> index 05d63fe92e436..48aea598ab0c9 100644
>>> --- a/drivers/iommu/io-pgtable-arm.c
>>> +++ b/drivers/iommu/io-pgtable-arm.c
>>> @@ -482,6 +482,7 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
>>>  	arm_lpae_iopte pte;
>>>  
>>>  	if (data->iop.fmt == ARM_64_LPAE_S1 ||
>>> +	    data->iop.fmt == ARM_MALI_CSF ||
>>>  	    data->iop.fmt == ARM_32_LPAE_S1) {
>>>  		pte = ARM_LPAE_PTE_nG;
>>>  		if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
>>> @@ -569,6 +570,8 @@ static int arm_lpae_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
>>>  		return -EINVAL;
>>>  
>>>  	prot = arm_lpae_prot_to_pte(data, iommu_prot);
>>> +	if (data->iop.fmt == ARM_MALI_CSF)
>>> +		prot |= cfg->arm_lpae_s1_cfg.pbha;
>>>  	ret = __arm_lpae_map(data, iova, paddr, pgsize, pgcount, prot, lvl,
>>>  			     ptep, gfp, mapped);
>>>  	/*
>>> @@ -864,7 +867,8 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
>>>  		return -EINVAL;
>>>  	if (WARN_ON((iova + size - 1) & ~(BIT(cfg->ias) - 1)))
>>>  		return -EINVAL;
>>> -	if (data->iop.fmt != ARM_64_LPAE_S1)
>>> +	if (data->iop.fmt != ARM_64_LPAE_S1 ||
>>> +	    data->iop.fmt != ARM_MALI_CSF)
>>>  		return -EINVAL;
>>>  
>>>  	return __arm_lpae_iopte_walk(data, &walk_data, ptep, lvl);
>>> @@ -1236,6 +1240,155 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
>>>  	return NULL;
>>>  }
>>>  
>>> +static struct io_pgtable *
>>> +arm_mali_csf_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
>>> +{
>>> +	unsigned int max_addr_bits = 48;
>>> +	unsigned long granule, page_sizes;
>>> +	struct arm_lpae_io_pgtable *data;
>>> +	typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr;
>>> +	int levels, va_bits, pg_shift;
>>> +	u64 reg;
>>> +
>>> +	if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_TTBR1 |
>>> +			    IO_PGTABLE_QUIRK_NO_WARN))
>>> +		return NULL;
>>> +
>>> +	if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K)))
>>> +		return NULL;
>>
>> This check should be moved down...
>>
>>> +
>>> +	if (cfg->pgsize_bitmap & PAGE_SIZE)
>>> +		granule = PAGE_SIZE;
>>> +	else if (cfg->pgsize_bitmap & ~PAGE_MASK)
>>> +		granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK);
>>> +	else if (cfg->pgsize_bitmap & PAGE_MASK)
>>> +		granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK);
>>> +	else
>>> +		granule = 0;
>>> +
>>> +	switch (granule) {
>>> +	case SZ_4K:
>>> +		page_sizes = (SZ_4K | SZ_2M | SZ_1G);
>>> +		break;
>>> +	case SZ_16K:
>>> +		page_sizes = (SZ_16K | SZ_32M | SZ_64G);
>>> +		break;
>>> +	default:
>>> +		page_sizes = 0;
>>> +	}
>>> +
>>> +	cfg->pgsize_bitmap &= page_sizes;
>>
>> ... to after this line. Otherwise we can end up with cfg->pgsize_bitmap
>> being zero and the function succeeding.
>>
>> Generally this is mostly just a copy of arm_lpae_alloc_pgtable() (with
>> arm_lpae_restrict_pgsizes() inlined) - but with added bugs. Which is why
>> I'm wary of adding another Mali-special unless there's good reason. It
>> still refers to a whole bunch of _LPAE_ defines/functions - which means
>> any refactor to LPAE would have to fix up this code too.
>>
>> Thanks,
>> Steve
>>
>>> +	cfg->ias = min(cfg->ias, max_addr_bits);
>>> +	cfg->oas = min(cfg->oas, max_addr_bits);
>>> +
>>> +	data = kmalloc(sizeof(*data), GFP_KERNEL);
>>> +	if (!data)
>>> +		return NULL;
>>> +
>>> +	pg_shift = __ffs(cfg->pgsize_bitmap);
>>> +	data->bits_per_level = pg_shift - ilog2(sizeof(arm_lpae_iopte));
>>> +
>>> +	va_bits = cfg->ias - pg_shift;
>>> +	levels = DIV_ROUND_UP(va_bits, data->bits_per_level);
>>> +	data->start_level = ARM_LPAE_MAX_LEVELS - levels;
>>> +
>>> +	/* Calculate the actual size of our pgd (without concatenation) */
>>> +	data->pgd_bits = va_bits - (data->bits_per_level * (levels - 1));
>>> +
>>> +	data->iop.ops = (struct io_pgtable_ops) {
>>> +		.map_pages	= arm_lpae_map_pages,
>>> +		.unmap_pages	= arm_lpae_unmap_pages,
>>> +		.iova_to_phys	= arm_lpae_iova_to_phys,
>>> +		.read_and_clear_dirty = arm_lpae_read_and_clear_dirty,
>>> +		.pgtable_walk	= arm_lpae_pgtable_walk,
>>> +	};
>>> +
>>> +	/* TCR */
>>> +	if (cfg->coherent_walk) {
>>> +		tcr->sh = ARM_LPAE_TCR_SH_IS;
>>> +		tcr->irgn = ARM_LPAE_TCR_RGN_WBWA;
>>> +		tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
>>> +		if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA)
>>> +			goto out_free_data;
>>> +	} else {
>>> +		tcr->sh = ARM_LPAE_TCR_SH_OS;
>>> +		tcr->irgn = ARM_LPAE_TCR_RGN_NC;
>>> +		if (!(cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA))
>>> +			tcr->orgn = ARM_LPAE_TCR_RGN_NC;
>>> +		else
>>> +			tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
>>> +	}
>>> +
>>> +	switch (ARM_LPAE_GRANULE(data)) {
>>> +	case SZ_4K:
>>> +		tcr->tg = ARM_LPAE_TCR_TG0_4K;
>>> +		break;
>>> +	case SZ_16K:
>>> +		tcr->tg = ARM_LPAE_TCR_TG0_16K;
>>> +		break;
>>> +	case SZ_64K:
>>> +		tcr->tg = ARM_LPAE_TCR_TG0_64K;
>>> +		break;
>>> +	}
>>> +
>>> +	switch (cfg->oas) {
>>> +	case 32:
>>> +		tcr->ips = ARM_LPAE_TCR_PS_32_BIT;
>>> +		break;
>>> +	case 36:
>>> +		tcr->ips = ARM_LPAE_TCR_PS_36_BIT;
>>> +		break;
>>> +	case 40:
>>> +		tcr->ips = ARM_LPAE_TCR_PS_40_BIT;
>>> +		break;
>>> +	case 42:
>>> +		tcr->ips = ARM_LPAE_TCR_PS_42_BIT;
>>> +		break;
>>> +	case 44:
>>> +		tcr->ips = ARM_LPAE_TCR_PS_44_BIT;
>>> +		break;
>>> +	case 48:
>>> +		tcr->ips = ARM_LPAE_TCR_PS_48_BIT;
>>> +		break;
>>> +	case 52:
>>> +		tcr->ips = ARM_LPAE_TCR_PS_52_BIT;
>>> +		break;
>>> +	default:
>>> +		goto out_free_data;
>>> +	}
>>> +
>>> +	tcr->tsz = 64ULL - cfg->ias;
>>> +
>>> +	/* MAIRs */
>>> +	reg = (ARM_LPAE_MAIR_ATTR_NC
>>> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
>>> +	      (ARM_LPAE_MAIR_ATTR_WBRWA
>>> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
>>> +	      (ARM_LPAE_MAIR_ATTR_DEVICE
>>> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)) |
>>> +	      (ARM_LPAE_MAIR_ATTR_INC_OWBRWA
>>> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE));
>>> +
>>> +	cfg->arm_lpae_s1_cfg.mair = reg;
>>> +
>>> +	/* Looking good; allocate a pgd */
>>> +	data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data),
>>> +					   GFP_KERNEL, cfg, cookie);
>>> +	if (!data->pgd)
>>> +		goto out_free_data;
>>> +
>>> +	/* Ensure the empty pgd is visible before any actual TTBR write */
>>> +	wmb();
>>> +
>>> +	/* TTBR */
>>> +	cfg->arm_lpae_s1_cfg.ttbr = virt_to_phys(data->pgd);
>>> +	return &data->iop;
>>> +
>>> +out_free_data:
>>> +	kfree(data);
>>> +	return NULL;
>>> +}
>>> +
>>>  struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
>>>  	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
>>>  	.alloc	= arm_64_lpae_alloc_pgtable_s1,
>>> @@ -1265,3 +1418,9 @@ struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
>>>  	.alloc	= arm_mali_lpae_alloc_pgtable,
>>>  	.free	= arm_lpae_free_pgtable,
>>>  };
>>> +
>>> +struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns = {
>>> +	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
>>> +	.alloc	= arm_mali_csf_alloc_pgtable,
>>> +	.free	= arm_lpae_free_pgtable,
>>> +};
>>> diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
>>> index 843fec8e8a511..1f43f898a8121 100644
>>> --- a/drivers/iommu/io-pgtable.c
>>> +++ b/drivers/iommu/io-pgtable.c
>>> @@ -20,6 +20,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
>>>  	[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
>>>  	[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
>>>  	[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
>>> +	[ARM_MALI_CSF] = &io_pgtable_arm_mali_csf_init_fns,
>>>  #endif
>>>  #ifdef CONFIG_IOMMU_IO_PGTABLE_DART
>>>  	[APPLE_DART] = &io_pgtable_apple_dart_init_fns,
>>> diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
>>> index 7a1516011ccf7..fc9776f71a963 100644
>>> --- a/include/linux/io-pgtable.h
>>> +++ b/include/linux/io-pgtable.h
>>> @@ -17,6 +17,7 @@ enum io_pgtable_fmt {
>>>  	ARM_MALI_LPAE,
>>>  	APPLE_DART,
>>>  	APPLE_DART2,
>>> +	ARM_MALI_CSF,
>>>  	IO_PGTABLE_NUM_FMTS,
>>>  };
>>>  
>>> @@ -148,6 +149,8 @@ struct io_pgtable_cfg {
>>>  				u32	tsz:6;
>>>  			}	tcr;
>>>  			u64	mair;
>>> +			/* ToDo: remove this when switching to arm_mali_csf_cfg struct */
>>> +			u64	pbha;
>>>  		} arm_lpae_s1_cfg;
>>>  
>>>  		struct {
>>> @@ -175,6 +178,20 @@ struct io_pgtable_cfg {
>>>  			u64	memattr;
>>>  		} arm_mali_lpae_cfg;
>>>  
>>> +		/* ToDo: switch to this structure for Mali CSF GPUs
>>> +		  struct {
>>> +			u64	transtab;
>>> +			struct {
>>> +				u32	pbha:4;
>>> +				u32	ra:1;
>>> +				u32	sh:2;
>>> +				u32	memattr:2;
>>> +				u32	mode:4;
>>> +			} transcfg;
>>> +			u64 memattr;
>>> +		} arm_mali_csf_cfg;
>>> +		*/
>>> +
>>>  		struct {
>>>  			u64 ttbr[4];
>>>  			u32 n_ttbrs;
>>> @@ -320,6 +337,7 @@ extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
>>>  extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
>>>  extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns;
>>>  extern struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns;
>>> +extern struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns;
>>>  extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v1_init_fns;
>>>  extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v2_init_fns;
>>>  extern struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns;
>>



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 11:25 [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format Liviu Dudau
  2026-02-09 12:31 ` Steven Price
@ 2026-02-09 14:16 ` Daniel Almeida
  2026-02-09 16:04   ` Liviu Dudau
  2026-02-10  6:28 ` kernel test robot
  2026-02-10 10:17 ` kernel test robot
  3 siblings, 1 reply; 12+ messages in thread
From: Daniel Almeida @ 2026-02-09 14:16 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Will Deacon, Robin Murphy, Joerg Roedel, Rob Clark,
	Boris Brezillon, Steven Price, linux-arm-kernel, iommu, dri-devel,
	linux-kernel, Karunika Choo, Liviu Dudau, Alice Ryhl,
	rust-for-linux




> On 9 Feb 2026, at 08:27, Liviu Dudau <liviu.dudau@arm.com> wrote:
> 
> From: Liviu Dudau <liviu@dudau.co.uk>
> 
> The Arm Mali v10+ GPU drivers have been (ab)using the ARM_64_LPAE_S1
> format as they are mostly compatible with it and some of the gaps
> left in the code to allow for ARM_MALI_LPAE format (pre-v10 GPUs)
> is helping to paper over differences. In preparation for adding support
> for changes introduced in v15 GPUs, add a format specific for modern
> Mali GPUs.
> 
> Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
> ---
> 
> This patch is trying to gauge interest in adding proper support for Arm Mali
> CSF GPUs via the simple approach of extending the generic Arm page table code
> to add support for the PTE format of the GPUs. In order to test the changes
> I've decided to add the phba bits to the arm_lpae_s1_cfg struct to validate
> the allocation and setup of the page table entries, but in the end I'm
> targetting the specific arm_mali_csf_cfg structure that will support
> the GPUs PTEs.
> 
> I'm interested to learn if this approach is considered sane and what I need to
> pay attention to when adding a new struct to the io_pgtable_cfg union. The patch
> is intentionally not complete with all the changes that switching to the new
> struct will entail as I didn't wanted to be dragged into a full code review, but
> I can add them if wanted.
> 
> 
> Best regards,
> Liviu
> 
> ---
> drivers/iommu/io-pgtable-arm.c | 161 ++++++++++++++++++++++++++++++++-
> drivers/iommu/io-pgtable.c     |   1 +
> include/linux/io-pgtable.h     |  18 ++++
> 3 files changed, 179 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
> index 05d63fe92e436..48aea598ab0c9 100644
> --- a/drivers/iommu/io-pgtable-arm.c
> +++ b/drivers/iommu/io-pgtable-arm.c
> @@ -482,6 +482,7 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
>    arm_lpae_iopte pte;
> 
>    if (data->iop.fmt == ARM_64_LPAE_S1 ||
> +        data->iop.fmt == ARM_MALI_CSF ||
>        data->iop.fmt == ARM_32_LPAE_S1) {
>        pte = ARM_LPAE_PTE_nG;
>        if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
> @@ -569,6 +570,8 @@ static int arm_lpae_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
>        return -EINVAL;
> 
>    prot = arm_lpae_prot_to_pte(data, iommu_prot);
> +    if (data->iop.fmt == ARM_MALI_CSF)
> +        prot |= cfg->arm_lpae_s1_cfg.pbha;
>    ret = __arm_lpae_map(data, iova, paddr, pgsize, pgcount, prot, lvl,
>                 ptep, gfp, mapped);
>    /*
> @@ -864,7 +867,8 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
>        return -EINVAL;
>    if (WARN_ON((iova + size - 1) & ~(BIT(cfg->ias) - 1)))
>        return -EINVAL;
> -    if (data->iop.fmt != ARM_64_LPAE_S1)
> +    if (data->iop.fmt != ARM_64_LPAE_S1 ||
> +        data->iop.fmt != ARM_MALI_CSF)
>        return -EINVAL;
> 
>    return __arm_lpae_iopte_walk(data, &walk_data, ptep, lvl);
> @@ -1236,6 +1240,155 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
>    return NULL;
> }
> 
> +static struct io_pgtable *
> +arm_mali_csf_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
> +{
> +    unsigned int max_addr_bits = 48;
> +    unsigned long granule, page_sizes;
> +    struct arm_lpae_io_pgtable *data;
> +    typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr;
> +    int levels, va_bits, pg_shift;
> +    u64 reg;
> +
> +    if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_TTBR1 |
> +                IO_PGTABLE_QUIRK_NO_WARN))
> +        return NULL;
> +
> +    if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K)))
> +        return NULL;
> +
> +    if (cfg->pgsize_bitmap & PAGE_SIZE)
> +        granule = PAGE_SIZE;
> +    else if (cfg->pgsize_bitmap & ~PAGE_MASK)
> +        granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK);
> +    else if (cfg->pgsize_bitmap & PAGE_MASK)
> +        granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK);
> +    else
> +        granule = 0;
> +
> +    switch (granule) {
> +    case SZ_4K:
> +        page_sizes = (SZ_4K | SZ_2M | SZ_1G);
> +        break;
> +    case SZ_16K:
> +        page_sizes = (SZ_16K | SZ_32M | SZ_64G);
> +        break;
> +    default:
> +        page_sizes = 0;
> +    }
> +
> +    cfg->pgsize_bitmap &= page_sizes;
> +    cfg->ias = min(cfg->ias, max_addr_bits);
> +    cfg->oas = min(cfg->oas, max_addr_bits);
> +
> +    data = kmalloc(sizeof(*data), GFP_KERNEL);
> +    if (!data)
> +        return NULL;
> +
> +    pg_shift = __ffs(cfg->pgsize_bitmap);
> +    data->bits_per_level = pg_shift - ilog2(sizeof(arm_lpae_iopte));
> +
> +    va_bits = cfg->ias - pg_shift;
> +    levels = DIV_ROUND_UP(va_bits, data->bits_per_level);
> +    data->start_level = ARM_LPAE_MAX_LEVELS - levels;
> +
> +    /* Calculate the actual size of our pgd (without concatenation) */
> +    data->pgd_bits = va_bits - (data->bits_per_level * (levels - 1));
> +
> +    data->iop.ops = (struct io_pgtable_ops) {
> +        .map_pages    = arm_lpae_map_pages,
> +        .unmap_pages    = arm_lpae_unmap_pages,
> +        .iova_to_phys    = arm_lpae_iova_to_phys,
> +        .read_and_clear_dirty = arm_lpae_read_and_clear_dirty,
> +        .pgtable_walk    = arm_lpae_pgtable_walk,
> +    };
> +
> +    /* TCR */
> +    if (cfg->coherent_walk) {
> +        tcr->sh = ARM_LPAE_TCR_SH_IS;
> +        tcr->irgn = ARM_LPAE_TCR_RGN_WBWA;
> +        tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
> +        if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA)
> +            goto out_free_data;
> +    } else {
> +        tcr->sh = ARM_LPAE_TCR_SH_OS;
> +        tcr->irgn = ARM_LPAE_TCR_RGN_NC;
> +        if (!(cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA))
> +            tcr->orgn = ARM_LPAE_TCR_RGN_NC;
> +        else
> +            tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
> +    }
> +
> +    switch (ARM_LPAE_GRANULE(data)) {
> +    case SZ_4K:
> +        tcr->tg = ARM_LPAE_TCR_TG0_4K;
> +        break;
> +    case SZ_16K:
> +        tcr->tg = ARM_LPAE_TCR_TG0_16K;
> +        break;
> +    case SZ_64K:
> +        tcr->tg = ARM_LPAE_TCR_TG0_64K;
> +        break;
> +    }
> +
> +    switch (cfg->oas) {
> +    case 32:
> +        tcr->ips = ARM_LPAE_TCR_PS_32_BIT;
> +        break;
> +    case 36:
> +        tcr->ips = ARM_LPAE_TCR_PS_36_BIT;
> +        break;
> +    case 40:
> +        tcr->ips = ARM_LPAE_TCR_PS_40_BIT;
> +        break;
> +    case 42:
> +        tcr->ips = ARM_LPAE_TCR_PS_42_BIT;
> +        break;
> +    case 44:
> +        tcr->ips = ARM_LPAE_TCR_PS_44_BIT;
> +        break;
> +    case 48:
> +        tcr->ips = ARM_LPAE_TCR_PS_48_BIT;
> +        break;
> +    case 52:
> +        tcr->ips = ARM_LPAE_TCR_PS_52_BIT;
> +        break;
> +    default:
> +        goto out_free_data;
> +    }
> +
> +    tcr->tsz = 64ULL - cfg->ias;
> +
> +    /* MAIRs */
> +    reg = (ARM_LPAE_MAIR_ATTR_NC
> +           << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
> +          (ARM_LPAE_MAIR_ATTR_WBRWA
> +           << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
> +          (ARM_LPAE_MAIR_ATTR_DEVICE
> +           << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)) |
> +          (ARM_LPAE_MAIR_ATTR_INC_OWBRWA
> +           << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE));
> +
> +    cfg->arm_lpae_s1_cfg.mair = reg;
> +
> +    /* Looking good; allocate a pgd */
> +    data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data),
> +                       GFP_KERNEL, cfg, cookie);
> +    if (!data->pgd)
> +        goto out_free_data;
> +
> +    /* Ensure the empty pgd is visible before any actual TTBR write */
> +    wmb();
> +
> +    /* TTBR */
> +    cfg->arm_lpae_s1_cfg.ttbr = virt_to_phys(data->pgd);
> +    return &data->iop;
> +
> +out_free_data:
> +    kfree(data);
> +    return NULL;
> +}
> +
> struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
>    .caps    = IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
>    .alloc    = arm_64_lpae_alloc_pgtable_s1,
> @@ -1265,3 +1418,9 @@ struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
>    .alloc    = arm_mali_lpae_alloc_pgtable,
>    .free    = arm_lpae_free_pgtable,
> };
> +
> +struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns = {
> +    .caps    = IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
> +    .alloc    = arm_mali_csf_alloc_pgtable,
> +    .free    = arm_lpae_free_pgtable,
> +};
> diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
> index 843fec8e8a511..1f43f898a8121 100644
> --- a/drivers/iommu/io-pgtable.c
> +++ b/drivers/iommu/io-pgtable.c
> @@ -20,6 +20,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
>    [ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
>    [ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
>    [ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
> +    [ARM_MALI_CSF] = &io_pgtable_arm_mali_csf_init_fns,
> #endif
> #ifdef CONFIG_IOMMU_IO_PGTABLE_DART
>    [APPLE_DART] = &io_pgtable_apple_dart_init_fns,
> diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
> index 7a1516011ccf7..fc9776f71a963 100644
> --- a/include/linux/io-pgtable.h
> +++ b/include/linux/io-pgtable.h
> @@ -17,6 +17,7 @@ enum io_pgtable_fmt {
>    ARM_MALI_LPAE,
>    APPLE_DART,
>    APPLE_DART2,
> +    ARM_MALI_CSF,
>    IO_PGTABLE_NUM_FMTS,
> };
> 
> @@ -148,6 +149,8 @@ struct io_pgtable_cfg {
>                u32    tsz:6;
>            }    tcr;
>            u64    mair;
> +            /* ToDo: remove this when switching to arm_mali_csf_cfg struct */
> +            u64    pbha;
>        } arm_lpae_s1_cfg;
> 
>        struct {
> @@ -175,6 +178,20 @@ struct io_pgtable_cfg {
>            u64    memattr;
>        } arm_mali_lpae_cfg;
> 
> +        /* ToDo: switch to this structure for Mali CSF GPUs
> +          struct {
> +            u64    transtab;
> +            struct {
> +                u32    pbha:4;
> +                u32    ra:1;
> +                u32    sh:2;
> +                u32    memattr:2;
> +                u32    mode:4;
> +            } transcfg;
> +            u64 memattr;
> +        } arm_mali_csf_cfg;
> +        */
> +
>        struct {
>            u64 ttbr[4];
>            u32 n_ttbrs;
> @@ -320,6 +337,7 @@ extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
> extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
> extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns;
> extern struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns;
> +extern struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns;
> extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v1_init_fns;
> extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v2_init_fns;
> extern struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns;
> --
> 2.52.0
> 

+cc Alice Ryhl and the rust-for-linux mailing list in light of the Rust abstractions for this component


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 13:57     ` Steven Price
@ 2026-02-09 15:22       ` Liviu Dudau
  2026-02-09 15:35         ` Boris Brezillon
  0 siblings, 1 reply; 12+ messages in thread
From: Liviu Dudau @ 2026-02-09 15:22 UTC (permalink / raw)
  To: Steven Price
  Cc: Will Deacon, Robin Murphy, Joerg Roedel, Rob Clark,
	Boris Brezillon, linux-arm-kernel, iommu, dri-devel, linux-kernel,
	Karunika Choo, Liviu Dudau

On Mon, Feb 09, 2026 at 01:57:29PM +0000, Steven Price wrote:
> On 09/02/2026 13:27, Liviu Dudau wrote:
> > On Mon, Feb 09, 2026 at 12:31:36PM +0000, Steven Price wrote:
> >> On 09/02/2026 11:25, Liviu Dudau wrote:
> >>> From: Liviu Dudau <liviu@dudau.co.uk>
> >>>
> >>> The Arm Mali v10+ GPU drivers have been (ab)using the ARM_64_LPAE_S1
> >>> format as they are mostly compatible with it and some of the gaps
> >>> left in the code to allow for ARM_MALI_LPAE format (pre-v10 GPUs)
> >>> is helping to paper over differences. In preparation for adding support
> >>> for changes introduced in v15 GPUs, add a format specific for modern
> >>> Mali GPUs.
> >>>
> >>> Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
> >>> ---
> >>>
> >>> This patch is trying to gauge interest in adding proper support for Arm Mali
> >>> CSF GPUs via the simple approach of extending the generic Arm page table code
> >>> to add support for the PTE format of the GPUs. In order to test the changes
> >>> I've decided to add the phba bits to the arm_lpae_s1_cfg struct to validate
> >>> the allocation and setup of the page table entries, but in the end I'm
> >>> targetting the specific arm_mali_csf_cfg structure that will support
> >>> the GPUs PTEs.
> >>
> >> Other than the addition of the PBHA bits (which are part of the VMSAv8
> >> page table format anyway) what are we expecting to be different between
> >> the Mali format and the CPU architectural format?
> > 
> > The bits at the moment are not that different, mostly renaming. However, v15+
> > GPUs are going to introduce a different scheme for the PTEs, so adding the
> > code now will help future changes.
> > 
> >>
> >> For Midgard GPUs the page table format was "inspired" by LPAE but was
> >> explicitly different in some cases - so a new format was required. But I
> >> can't actually spot any differences in the GPU format to what VMSAv8-64
> >> describes (albeit the GPU format is less flexible than all the options
> >> the CPU format describes).
> > 
> > Yes, the supported page table sizes is a bit of a head scratcher. v10+ claim
> > that 16KB pages are not supported, only 4KB and 64KB. v15+ GPUs are going
> > to claim that only 4KB and 16KB page sizes are supported, not 64KB.
> 
> Ok, so this is a difference, although the CPU archiecture I believe says
> all three "translation granule sizes" (i.e. page sizes) are optional. So
> not supporting 64KB is just an implementation choice not a new format.
> 
> >>
> >> I can see why we might want more functionality (e.g. PBHA): I'm just not
> >> sure what the reason for having another special Mali format is - why
> >> can't this be in the generic code?
> > 
> > What do you mean by "generic code"? I thought these files are the most generic
> 
> I just mean code shared between drivers - yes this file is the right
> place - but the RFC adds what amounts to a copy/paste of the existing
> code, just with 64KB dropped (at least from my skim through the code).
> We need a more compelling reason to fork.
> 
> > support code for Arm SMMUs. But to answer your question: I think once we introduce
> > the v15+ GPU's page table formats keeping track of which type of page table you're
> > using without a CSF specific one will get trickier.
> 
> So I don't know what's being proposed in the latest GPUs - if there is
> an actual incompatibility then it might be justified to create a new
> format in the code. I'm somewhat surprised as we'd delibrately gone the
> other way (dropping the old Mali-specific format and moving to the
> standard format). But I think we need to know what the incompatibility
> is to be able to judge the right approach in the code.

Architectural changes are still under review, but I've spotted a couple of things
that made me think that the existing definitions need updating.


> 
> > Ultimately the role of this RFC is to start a discussion and to figure out a path
> > forward for CSF GPUs where we want now to tighen a bit the formats we support and
> > add PBHA and in the future we want to add support for v15+ page formats.
> 
> PBHA is definitely an area for discussion. AIUI there are out-of-tree
> patches floating about for CPU support, but it hasn't been upstreamed. I
> don't know if any serious attempt has been made to push it upstream, but
> it's tricky because the architecture basically just says "IMPLEMENTATION
> DEFINED" which means you are no longer coding to the architecture but a
> specific implementation - and there's remarkably little documentation
> about what PBHA is used for in practice.
> 
> I haven't looked into the GPU situation with PBHA - again it would be
> good to have more details on how the bits would be set.

I have a patch series that adds support in Panthor to apply some PBHA bits defined
in the DT based on an ID also defined in the DT and passed along as a VM_BIND parameter
if you want to play with it. However I have no direct knowledge on which PBHA values
would make a difference on the supported platforms (RK3xxx for example).

Best regards,
Liviu

> 
> Thanks,
> Steve
> 
> > Best regards,
> > Liviu
> > 
> > 
> >>
> >>>
> >>> I'm interested to learn if this approach is considered sane and what I need to
> >>> pay attention to when adding a new struct to the io_pgtable_cfg union. The patch
> >>> is intentionally not complete with all the changes that switching to the new
> >>> struct will entail as I didn't wanted to be dragged into a full code review, but
> >>> I can add them if wanted.
> >>>
> >>>
> >>> Best regards,
> >>> Liviu
> >>>
> >>> ---
> >>>  drivers/iommu/io-pgtable-arm.c | 161 ++++++++++++++++++++++++++++++++-
> >>>  drivers/iommu/io-pgtable.c     |   1 +
> >>>  include/linux/io-pgtable.h     |  18 ++++
> >>>  3 files changed, 179 insertions(+), 1 deletion(-)
> >>>
> >>> diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
> >>> index 05d63fe92e436..48aea598ab0c9 100644
> >>> --- a/drivers/iommu/io-pgtable-arm.c
> >>> +++ b/drivers/iommu/io-pgtable-arm.c
> >>> @@ -482,6 +482,7 @@ static arm_lpae_iopte arm_lpae_prot_to_pte(struct arm_lpae_io_pgtable *data,
> >>>  	arm_lpae_iopte pte;
> >>>  
> >>>  	if (data->iop.fmt == ARM_64_LPAE_S1 ||
> >>> +	    data->iop.fmt == ARM_MALI_CSF ||
> >>>  	    data->iop.fmt == ARM_32_LPAE_S1) {
> >>>  		pte = ARM_LPAE_PTE_nG;
> >>>  		if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
> >>> @@ -569,6 +570,8 @@ static int arm_lpae_map_pages(struct io_pgtable_ops *ops, unsigned long iova,
> >>>  		return -EINVAL;
> >>>  
> >>>  	prot = arm_lpae_prot_to_pte(data, iommu_prot);
> >>> +	if (data->iop.fmt == ARM_MALI_CSF)
> >>> +		prot |= cfg->arm_lpae_s1_cfg.pbha;
> >>>  	ret = __arm_lpae_map(data, iova, paddr, pgsize, pgcount, prot, lvl,
> >>>  			     ptep, gfp, mapped);
> >>>  	/*
> >>> @@ -864,7 +867,8 @@ static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
> >>>  		return -EINVAL;
> >>>  	if (WARN_ON((iova + size - 1) & ~(BIT(cfg->ias) - 1)))
> >>>  		return -EINVAL;
> >>> -	if (data->iop.fmt != ARM_64_LPAE_S1)
> >>> +	if (data->iop.fmt != ARM_64_LPAE_S1 ||
> >>> +	    data->iop.fmt != ARM_MALI_CSF)
> >>>  		return -EINVAL;
> >>>  
> >>>  	return __arm_lpae_iopte_walk(data, &walk_data, ptep, lvl);
> >>> @@ -1236,6 +1240,155 @@ arm_mali_lpae_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
> >>>  	return NULL;
> >>>  }
> >>>  
> >>> +static struct io_pgtable *
> >>> +arm_mali_csf_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
> >>> +{
> >>> +	unsigned int max_addr_bits = 48;
> >>> +	unsigned long granule, page_sizes;
> >>> +	struct arm_lpae_io_pgtable *data;
> >>> +	typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr;
> >>> +	int levels, va_bits, pg_shift;
> >>> +	u64 reg;
> >>> +
> >>> +	if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_TTBR1 |
> >>> +			    IO_PGTABLE_QUIRK_NO_WARN))
> >>> +		return NULL;
> >>> +
> >>> +	if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K)))
> >>> +		return NULL;
> >>
> >> This check should be moved down...
> >>
> >>> +
> >>> +	if (cfg->pgsize_bitmap & PAGE_SIZE)
> >>> +		granule = PAGE_SIZE;
> >>> +	else if (cfg->pgsize_bitmap & ~PAGE_MASK)
> >>> +		granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK);
> >>> +	else if (cfg->pgsize_bitmap & PAGE_MASK)
> >>> +		granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK);
> >>> +	else
> >>> +		granule = 0;
> >>> +
> >>> +	switch (granule) {
> >>> +	case SZ_4K:
> >>> +		page_sizes = (SZ_4K | SZ_2M | SZ_1G);
> >>> +		break;
> >>> +	case SZ_16K:
> >>> +		page_sizes = (SZ_16K | SZ_32M | SZ_64G);
> >>> +		break;
> >>> +	default:
> >>> +		page_sizes = 0;
> >>> +	}
> >>> +
> >>> +	cfg->pgsize_bitmap &= page_sizes;
> >>
> >> ... to after this line. Otherwise we can end up with cfg->pgsize_bitmap
> >> being zero and the function succeeding.
> >>
> >> Generally this is mostly just a copy of arm_lpae_alloc_pgtable() (with
> >> arm_lpae_restrict_pgsizes() inlined) - but with added bugs. Which is why
> >> I'm wary of adding another Mali-special unless there's good reason. It
> >> still refers to a whole bunch of _LPAE_ defines/functions - which means
> >> any refactor to LPAE would have to fix up this code too.
> >>
> >> Thanks,
> >> Steve
> >>
> >>> +	cfg->ias = min(cfg->ias, max_addr_bits);
> >>> +	cfg->oas = min(cfg->oas, max_addr_bits);
> >>> +
> >>> +	data = kmalloc(sizeof(*data), GFP_KERNEL);
> >>> +	if (!data)
> >>> +		return NULL;
> >>> +
> >>> +	pg_shift = __ffs(cfg->pgsize_bitmap);
> >>> +	data->bits_per_level = pg_shift - ilog2(sizeof(arm_lpae_iopte));
> >>> +
> >>> +	va_bits = cfg->ias - pg_shift;
> >>> +	levels = DIV_ROUND_UP(va_bits, data->bits_per_level);
> >>> +	data->start_level = ARM_LPAE_MAX_LEVELS - levels;
> >>> +
> >>> +	/* Calculate the actual size of our pgd (without concatenation) */
> >>> +	data->pgd_bits = va_bits - (data->bits_per_level * (levels - 1));
> >>> +
> >>> +	data->iop.ops = (struct io_pgtable_ops) {
> >>> +		.map_pages	= arm_lpae_map_pages,
> >>> +		.unmap_pages	= arm_lpae_unmap_pages,
> >>> +		.iova_to_phys	= arm_lpae_iova_to_phys,
> >>> +		.read_and_clear_dirty = arm_lpae_read_and_clear_dirty,
> >>> +		.pgtable_walk	= arm_lpae_pgtable_walk,
> >>> +	};
> >>> +
> >>> +	/* TCR */
> >>> +	if (cfg->coherent_walk) {
> >>> +		tcr->sh = ARM_LPAE_TCR_SH_IS;
> >>> +		tcr->irgn = ARM_LPAE_TCR_RGN_WBWA;
> >>> +		tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
> >>> +		if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA)
> >>> +			goto out_free_data;
> >>> +	} else {
> >>> +		tcr->sh = ARM_LPAE_TCR_SH_OS;
> >>> +		tcr->irgn = ARM_LPAE_TCR_RGN_NC;
> >>> +		if (!(cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA))
> >>> +			tcr->orgn = ARM_LPAE_TCR_RGN_NC;
> >>> +		else
> >>> +			tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
> >>> +	}
> >>> +
> >>> +	switch (ARM_LPAE_GRANULE(data)) {
> >>> +	case SZ_4K:
> >>> +		tcr->tg = ARM_LPAE_TCR_TG0_4K;
> >>> +		break;
> >>> +	case SZ_16K:
> >>> +		tcr->tg = ARM_LPAE_TCR_TG0_16K;
> >>> +		break;
> >>> +	case SZ_64K:
> >>> +		tcr->tg = ARM_LPAE_TCR_TG0_64K;
> >>> +		break;
> >>> +	}
> >>> +
> >>> +	switch (cfg->oas) {
> >>> +	case 32:
> >>> +		tcr->ips = ARM_LPAE_TCR_PS_32_BIT;
> >>> +		break;
> >>> +	case 36:
> >>> +		tcr->ips = ARM_LPAE_TCR_PS_36_BIT;
> >>> +		break;
> >>> +	case 40:
> >>> +		tcr->ips = ARM_LPAE_TCR_PS_40_BIT;
> >>> +		break;
> >>> +	case 42:
> >>> +		tcr->ips = ARM_LPAE_TCR_PS_42_BIT;
> >>> +		break;
> >>> +	case 44:
> >>> +		tcr->ips = ARM_LPAE_TCR_PS_44_BIT;
> >>> +		break;
> >>> +	case 48:
> >>> +		tcr->ips = ARM_LPAE_TCR_PS_48_BIT;
> >>> +		break;
> >>> +	case 52:
> >>> +		tcr->ips = ARM_LPAE_TCR_PS_52_BIT;
> >>> +		break;
> >>> +	default:
> >>> +		goto out_free_data;
> >>> +	}
> >>> +
> >>> +	tcr->tsz = 64ULL - cfg->ias;
> >>> +
> >>> +	/* MAIRs */
> >>> +	reg = (ARM_LPAE_MAIR_ATTR_NC
> >>> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
> >>> +	      (ARM_LPAE_MAIR_ATTR_WBRWA
> >>> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
> >>> +	      (ARM_LPAE_MAIR_ATTR_DEVICE
> >>> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)) |
> >>> +	      (ARM_LPAE_MAIR_ATTR_INC_OWBRWA
> >>> +	       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE));
> >>> +
> >>> +	cfg->arm_lpae_s1_cfg.mair = reg;
> >>> +
> >>> +	/* Looking good; allocate a pgd */
> >>> +	data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data),
> >>> +					   GFP_KERNEL, cfg, cookie);
> >>> +	if (!data->pgd)
> >>> +		goto out_free_data;
> >>> +
> >>> +	/* Ensure the empty pgd is visible before any actual TTBR write */
> >>> +	wmb();
> >>> +
> >>> +	/* TTBR */
> >>> +	cfg->arm_lpae_s1_cfg.ttbr = virt_to_phys(data->pgd);
> >>> +	return &data->iop;
> >>> +
> >>> +out_free_data:
> >>> +	kfree(data);
> >>> +	return NULL;
> >>> +}
> >>> +
> >>>  struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns = {
> >>>  	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
> >>>  	.alloc	= arm_64_lpae_alloc_pgtable_s1,
> >>> @@ -1265,3 +1418,9 @@ struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns = {
> >>>  	.alloc	= arm_mali_lpae_alloc_pgtable,
> >>>  	.free	= arm_lpae_free_pgtable,
> >>>  };
> >>> +
> >>> +struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns = {
> >>> +	.caps	= IO_PGTABLE_CAP_CUSTOM_ALLOCATOR,
> >>> +	.alloc	= arm_mali_csf_alloc_pgtable,
> >>> +	.free	= arm_lpae_free_pgtable,
> >>> +};
> >>> diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
> >>> index 843fec8e8a511..1f43f898a8121 100644
> >>> --- a/drivers/iommu/io-pgtable.c
> >>> +++ b/drivers/iommu/io-pgtable.c
> >>> @@ -20,6 +20,7 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] = {
> >>>  	[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
> >>>  	[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
> >>>  	[ARM_MALI_LPAE] = &io_pgtable_arm_mali_lpae_init_fns,
> >>> +	[ARM_MALI_CSF] = &io_pgtable_arm_mali_csf_init_fns,
> >>>  #endif
> >>>  #ifdef CONFIG_IOMMU_IO_PGTABLE_DART
> >>>  	[APPLE_DART] = &io_pgtable_apple_dart_init_fns,
> >>> diff --git a/include/linux/io-pgtable.h b/include/linux/io-pgtable.h
> >>> index 7a1516011ccf7..fc9776f71a963 100644
> >>> --- a/include/linux/io-pgtable.h
> >>> +++ b/include/linux/io-pgtable.h
> >>> @@ -17,6 +17,7 @@ enum io_pgtable_fmt {
> >>>  	ARM_MALI_LPAE,
> >>>  	APPLE_DART,
> >>>  	APPLE_DART2,
> >>> +	ARM_MALI_CSF,
> >>>  	IO_PGTABLE_NUM_FMTS,
> >>>  };
> >>>  
> >>> @@ -148,6 +149,8 @@ struct io_pgtable_cfg {
> >>>  				u32	tsz:6;
> >>>  			}	tcr;
> >>>  			u64	mair;
> >>> +			/* ToDo: remove this when switching to arm_mali_csf_cfg struct */
> >>> +			u64	pbha;
> >>>  		} arm_lpae_s1_cfg;
> >>>  
> >>>  		struct {
> >>> @@ -175,6 +178,20 @@ struct io_pgtable_cfg {
> >>>  			u64	memattr;
> >>>  		} arm_mali_lpae_cfg;
> >>>  
> >>> +		/* ToDo: switch to this structure for Mali CSF GPUs
> >>> +		  struct {
> >>> +			u64	transtab;
> >>> +			struct {
> >>> +				u32	pbha:4;
> >>> +				u32	ra:1;
> >>> +				u32	sh:2;
> >>> +				u32	memattr:2;
> >>> +				u32	mode:4;
> >>> +			} transcfg;
> >>> +			u64 memattr;
> >>> +		} arm_mali_csf_cfg;
> >>> +		*/
> >>> +
> >>>  		struct {
> >>>  			u64 ttbr[4];
> >>>  			u32 n_ttbrs;
> >>> @@ -320,6 +337,7 @@ extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
> >>>  extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
> >>>  extern struct io_pgtable_init_fns io_pgtable_arm_v7s_init_fns;
> >>>  extern struct io_pgtable_init_fns io_pgtable_arm_mali_lpae_init_fns;
> >>> +extern struct io_pgtable_init_fns io_pgtable_arm_mali_csf_init_fns;
> >>>  extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v1_init_fns;
> >>>  extern struct io_pgtable_init_fns io_pgtable_amd_iommu_v2_init_fns;
> >>>  extern struct io_pgtable_init_fns io_pgtable_apple_dart_init_fns;
> >>
> 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 15:22       ` Liviu Dudau
@ 2026-02-09 15:35         ` Boris Brezillon
  2026-02-09 15:44           ` Steven Price
  0 siblings, 1 reply; 12+ messages in thread
From: Boris Brezillon @ 2026-02-09 15:35 UTC (permalink / raw)
  To: Liviu Dudau
  Cc: Steven Price, Will Deacon, Robin Murphy, Joerg Roedel, Rob Clark,
	linux-arm-kernel, iommu, dri-devel, linux-kernel, Karunika Choo,
	Liviu Dudau

On Mon, 9 Feb 2026 15:22:09 +0000
Liviu Dudau <liviu.dudau@arm.com> wrote:

> > > Ultimately the role of this RFC is to start a discussion and to figure out a path
> > > forward for CSF GPUs where we want now to tighen a bit the formats we support and
> > > add PBHA and in the future we want to add support for v15+ page formats.  
> > 
> > PBHA is definitely an area for discussion. AIUI there are out-of-tree
> > patches floating about for CPU support, but it hasn't been upstreamed. I
> > don't know if any serious attempt has been made to push it upstream, but
> > it's tricky because the architecture basically just says "IMPLEMENTATION
> > DEFINED" which means you are no longer coding to the architecture but a
> > specific implementation - and there's remarkably little documentation
> > about what PBHA is used for in practice.
> > 
> > I haven't looked into the GPU situation with PBHA - again it would be
> > good to have more details on how the bits would be set.  
> 
> I have a patch series that adds support in Panthor to apply some PBHA bits defined
> in the DT based on an ID also defined in the DT and passed along as a VM_BIND parameter
> if you want to play with it. However I have no direct knowledge on which PBHA values
> would make a difference on the supported platforms (RK3xxx for example).

I don't know if that's what it's going be used for, but one very
specific use case I'd like to see this PBHA extension backed by is
"read-zero/write-discard" behavior that's needed for sparse bindings.
Unfortunately, I've not heard on any HW-support for that in older
gens...


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 15:35         ` Boris Brezillon
@ 2026-02-09 15:44           ` Steven Price
  2026-02-09 16:02             ` Liviu Dudau
  0 siblings, 1 reply; 12+ messages in thread
From: Steven Price @ 2026-02-09 15:44 UTC (permalink / raw)
  To: Boris Brezillon, Liviu Dudau
  Cc: Will Deacon, Robin Murphy, Joerg Roedel, Rob Clark,
	linux-arm-kernel, iommu, dri-devel, linux-kernel, Karunika Choo,
	Liviu Dudau

On 09/02/2026 15:35, Boris Brezillon wrote:
> On Mon, 9 Feb 2026 15:22:09 +0000
> Liviu Dudau <liviu.dudau@arm.com> wrote:
> 
>>>> Ultimately the role of this RFC is to start a discussion and to figure out a path
>>>> forward for CSF GPUs where we want now to tighen a bit the formats we support and
>>>> add PBHA and in the future we want to add support for v15+ page formats.  
>>>
>>> PBHA is definitely an area for discussion. AIUI there are out-of-tree
>>> patches floating about for CPU support, but it hasn't been upstreamed. I
>>> don't know if any serious attempt has been made to push it upstream, but
>>> it's tricky because the architecture basically just says "IMPLEMENTATION
>>> DEFINED" which means you are no longer coding to the architecture but a
>>> specific implementation - and there's remarkably little documentation
>>> about what PBHA is used for in practice.
>>>
>>> I haven't looked into the GPU situation with PBHA - again it would be
>>> good to have more details on how the bits would be set.  
>>
>> I have a patch series that adds support in Panthor to apply some PBHA bits defined
>> in the DT based on an ID also defined in the DT and passed along as a VM_BIND parameter
>> if you want to play with it. However I have no direct knowledge on which PBHA values
>> would make a difference on the supported platforms (RK3xxx for example).

So we need something better than a DT entry saying e.g. "ID 3 is bit
pattern 0100". We need something that describes the actual behaviour of
a PBHA value. Otherwise user space will end up needing to know the exact
hardware platform it's running on to know what ID values mean.

> I don't know if that's what it's going be used for, but one very
> specific use case I'd like to see this PBHA extension backed by is
> "read-zero/write-discard" behavior that's needed for sparse bindings.
> Unfortunately, I've not heard on any HW-support for that in older
> gens...

*This* is a good example of something useful that could be exposed. If
the DT can describe that the hardware supports a
"read-zero/write-discard" with a specific bit pattern, then we can
advertise that to user space and provide a flag for VM_BIND which gives
that behaviour. And user space can make good use of it.

But from what I've heard the implementations tend to have something more
like a hint-mechanism where it affects the behaviour of the caches but
not the functional effect. This makes it much harder to expose to user
space in a meaningful way because it's highly platform dependant what
"don't allocate in the system level cache" actually means in terms of
performance effects. But it's possible we could describe more of a usage
based flag - i.e. "PBHA bits good for a tiler heap".

Thanks,
Steve



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 15:44           ` Steven Price
@ 2026-02-09 16:02             ` Liviu Dudau
  0 siblings, 0 replies; 12+ messages in thread
From: Liviu Dudau @ 2026-02-09 16:02 UTC (permalink / raw)
  To: Steven Price
  Cc: Boris Brezillon, Will Deacon, Robin Murphy, Joerg Roedel,
	Rob Clark, linux-arm-kernel, iommu, dri-devel, linux-kernel,
	Karunika Choo, Liviu Dudau

On Mon, Feb 09, 2026 at 03:44:07PM +0000, Steven Price wrote:
> On 09/02/2026 15:35, Boris Brezillon wrote:
> > On Mon, 9 Feb 2026 15:22:09 +0000
> > Liviu Dudau <liviu.dudau@arm.com> wrote:
> > 
> >>>> Ultimately the role of this RFC is to start a discussion and to figure out a path
> >>>> forward for CSF GPUs where we want now to tighen a bit the formats we support and
> >>>> add PBHA and in the future we want to add support for v15+ page formats.  
> >>>
> >>> PBHA is definitely an area for discussion. AIUI there are out-of-tree
> >>> patches floating about for CPU support, but it hasn't been upstreamed. I
> >>> don't know if any serious attempt has been made to push it upstream, but
> >>> it's tricky because the architecture basically just says "IMPLEMENTATION
> >>> DEFINED" which means you are no longer coding to the architecture but a
> >>> specific implementation - and there's remarkably little documentation
> >>> about what PBHA is used for in practice.
> >>>
> >>> I haven't looked into the GPU situation with PBHA - again it would be
> >>> good to have more details on how the bits would be set.  
> >>
> >> I have a patch series that adds support in Panthor to apply some PBHA bits defined
> >> in the DT based on an ID also defined in the DT and passed along as a VM_BIND parameter
> >> if you want to play with it. However I have no direct knowledge on which PBHA values
> >> would make a difference on the supported platforms (RK3xxx for example).
> 
> So we need something better than a DT entry saying e.g. "ID 3 is bit
> pattern 0100". We need something that describes the actual behaviour of
> a PBHA value. Otherwise user space will end up needing to know the exact
> hardware platform it's running on to know what ID values mean.

Yes, the reason why I haven't published the Panthor patches yet is because of that.
DDK currently has a set of memory group classes that we can clean up and publish
that will define that ID 3 (e.g.) is for "inner shareable memory" and then the
DT specifies what bits need to be encoded in the PBHA to achieve that on the
target system. I don't think we can standardise the PBHA bits as their use
by the system integrator is specific to a vendor or sometimes even to a given SoC.

> 
> > I don't know if that's what it's going be used for, but one very
> > specific use case I'd like to see this PBHA extension backed by is
> > "read-zero/write-discard" behavior that's needed for sparse bindings.
> > Unfortunately, I've not heard on any HW-support for that in older
> > gens...

I'm not aware of any existing SoC that supports that, but it is something we're
looking into supporting in the v15+ GPUs.

> 
> *This* is a good example of something useful that could be exposed. If
> the DT can describe that the hardware supports a
> "read-zero/write-discard" with a specific bit pattern, then we can
> advertise that to user space and provide a flag for VM_BIND which gives
> that behaviour. And user space can make good use of it.
> 
> But from what I've heard the implementations tend to have something more
> like a hint-mechanism where it affects the behaviour of the caches but
> not the functional effect. This makes it much harder to expose to user
> space in a meaningful way because it's highly platform dependant what
> "don't allocate in the system level cache" actually means in terms of
> performance effects. But it's possible we could describe more of a usage
> based flag - i.e. "PBHA bits good for a tiler heap".

See above. The idea is to have up to 255 IDs that have some select values properly
defined in their intent and leave the rest for partner use. You then add the ID
to the VM_BIND call and it gets applied to the BO's pages.

Best regards,
Liviu

> 
> Thanks,
> Steve
> 


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 14:16 ` Daniel Almeida
@ 2026-02-09 16:04   ` Liviu Dudau
  0 siblings, 0 replies; 12+ messages in thread
From: Liviu Dudau @ 2026-02-09 16:04 UTC (permalink / raw)
  To: Daniel Almeida
  Cc: Will Deacon, Robin Murphy, Joerg Roedel, Rob Clark,
	Boris Brezillon, Steven Price, linux-arm-kernel, iommu, dri-devel,
	linux-kernel, Karunika Choo, Liviu Dudau, Alice Ryhl,
	rust-for-linux

On Mon, Feb 09, 2026 at 11:16:38AM -0300, Daniel Almeida wrote:
>

[....]

> +cc Alice Ryhl and the rust-for-linux mailing list in light of the Rust abstractions for this component

Appologies for the omission, I will bear in mind the next time to include Alice and you as well.

Best regards,
Liviu


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 11:25 [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format Liviu Dudau
  2026-02-09 12:31 ` Steven Price
  2026-02-09 14:16 ` Daniel Almeida
@ 2026-02-10  6:28 ` kernel test robot
  2026-02-10 10:17 ` kernel test robot
  3 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2026-02-10  6:28 UTC (permalink / raw)
  To: Liviu Dudau; +Cc: oe-kbuild-all

Hi Liviu,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build warnings:

[auto build test WARNING on arm-perf/for-next/perf]
[also build test WARNING on linus/master v6.19 next-20260209]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Liviu-Dudau/iommu-io-pgtable-Add-support-for-Arm-Mali-v10-GPUs-page-table-format/20260209-193039
base:   https://git.kernel.org/pub/scm/linux/kernel/git/will/linux.git for-next/perf
patch link:    https://lore.kernel.org/r/20260209112542.194140-1-liviu.dudau%40arm.com
patch subject: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
config: arc-allmodconfig (https://download.01.org/0day-ci/archive/20260210/202602101447.y5x0XBO5-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260210/202602101447.y5x0XBO5-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602101447.y5x0XBO5-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/iommu/io-pgtable-arm.c: In function 'arm_mali_csf_alloc_pgtable':
>> drivers/iommu/io-pgtable-arm.c:1274:30: warning: conversion from 'long long unsigned int' to 'long unsigned int' changes value from '68753047552' to '33570816' [-Woverflow]
    1274 |                 page_sizes = (SZ_16K | SZ_32M | SZ_64G);
         |                              ^


vim +1274 drivers/iommu/io-pgtable-arm.c

  1242	
  1243	static struct io_pgtable *
  1244	arm_mali_csf_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
  1245	{
  1246		unsigned int max_addr_bits = 48;
  1247		unsigned long granule, page_sizes;
  1248		struct arm_lpae_io_pgtable *data;
  1249		typeof(&cfg->arm_lpae_s1_cfg.tcr) tcr = &cfg->arm_lpae_s1_cfg.tcr;
  1250		int levels, va_bits, pg_shift;
  1251		u64 reg;
  1252	
  1253		if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_TTBR1 |
  1254				    IO_PGTABLE_QUIRK_NO_WARN))
  1255			return NULL;
  1256	
  1257		if (!(cfg->pgsize_bitmap & (SZ_4K | SZ_16K)))
  1258			return NULL;
  1259	
  1260		if (cfg->pgsize_bitmap & PAGE_SIZE)
  1261			granule = PAGE_SIZE;
  1262		else if (cfg->pgsize_bitmap & ~PAGE_MASK)
  1263			granule = 1UL << __fls(cfg->pgsize_bitmap & ~PAGE_MASK);
  1264		else if (cfg->pgsize_bitmap & PAGE_MASK)
  1265			granule = 1UL << __ffs(cfg->pgsize_bitmap & PAGE_MASK);
  1266		else
  1267			granule = 0;
  1268	
  1269		switch (granule) {
  1270		case SZ_4K:
  1271			page_sizes = (SZ_4K | SZ_2M | SZ_1G);
  1272			break;
  1273		case SZ_16K:
> 1274			page_sizes = (SZ_16K | SZ_32M | SZ_64G);
  1275			break;
  1276		default:
  1277			page_sizes = 0;
  1278		}
  1279	
  1280		cfg->pgsize_bitmap &= page_sizes;
  1281		cfg->ias = min(cfg->ias, max_addr_bits);
  1282		cfg->oas = min(cfg->oas, max_addr_bits);
  1283	
  1284		data = kmalloc(sizeof(*data), GFP_KERNEL);
  1285		if (!data)
  1286			return NULL;
  1287	
  1288		pg_shift = __ffs(cfg->pgsize_bitmap);
  1289		data->bits_per_level = pg_shift - ilog2(sizeof(arm_lpae_iopte));
  1290	
  1291		va_bits = cfg->ias - pg_shift;
  1292		levels = DIV_ROUND_UP(va_bits, data->bits_per_level);
  1293		data->start_level = ARM_LPAE_MAX_LEVELS - levels;
  1294	
  1295		/* Calculate the actual size of our pgd (without concatenation) */
  1296		data->pgd_bits = va_bits - (data->bits_per_level * (levels - 1));
  1297	
  1298		data->iop.ops = (struct io_pgtable_ops) {
  1299			.map_pages	= arm_lpae_map_pages,
  1300			.unmap_pages	= arm_lpae_unmap_pages,
  1301			.iova_to_phys	= arm_lpae_iova_to_phys,
  1302			.read_and_clear_dirty = arm_lpae_read_and_clear_dirty,
  1303			.pgtable_walk	= arm_lpae_pgtable_walk,
  1304		};
  1305	
  1306		/* TCR */
  1307		if (cfg->coherent_walk) {
  1308			tcr->sh = ARM_LPAE_TCR_SH_IS;
  1309			tcr->irgn = ARM_LPAE_TCR_RGN_WBWA;
  1310			tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
  1311			if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA)
  1312				goto out_free_data;
  1313		} else {
  1314			tcr->sh = ARM_LPAE_TCR_SH_OS;
  1315			tcr->irgn = ARM_LPAE_TCR_RGN_NC;
  1316			if (!(cfg->quirks & IO_PGTABLE_QUIRK_ARM_OUTER_WBWA))
  1317				tcr->orgn = ARM_LPAE_TCR_RGN_NC;
  1318			else
  1319				tcr->orgn = ARM_LPAE_TCR_RGN_WBWA;
  1320		}
  1321	
  1322		switch (ARM_LPAE_GRANULE(data)) {
  1323		case SZ_4K:
  1324			tcr->tg = ARM_LPAE_TCR_TG0_4K;
  1325			break;
  1326		case SZ_16K:
  1327			tcr->tg = ARM_LPAE_TCR_TG0_16K;
  1328			break;
  1329		case SZ_64K:
  1330			tcr->tg = ARM_LPAE_TCR_TG0_64K;
  1331			break;
  1332		}
  1333	
  1334		switch (cfg->oas) {
  1335		case 32:
  1336			tcr->ips = ARM_LPAE_TCR_PS_32_BIT;
  1337			break;
  1338		case 36:
  1339			tcr->ips = ARM_LPAE_TCR_PS_36_BIT;
  1340			break;
  1341		case 40:
  1342			tcr->ips = ARM_LPAE_TCR_PS_40_BIT;
  1343			break;
  1344		case 42:
  1345			tcr->ips = ARM_LPAE_TCR_PS_42_BIT;
  1346			break;
  1347		case 44:
  1348			tcr->ips = ARM_LPAE_TCR_PS_44_BIT;
  1349			break;
  1350		case 48:
  1351			tcr->ips = ARM_LPAE_TCR_PS_48_BIT;
  1352			break;
  1353		case 52:
  1354			tcr->ips = ARM_LPAE_TCR_PS_52_BIT;
  1355			break;
  1356		default:
  1357			goto out_free_data;
  1358		}
  1359	
  1360		tcr->tsz = 64ULL - cfg->ias;
  1361	
  1362		/* MAIRs */
  1363		reg = (ARM_LPAE_MAIR_ATTR_NC
  1364		       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_NC)) |
  1365		      (ARM_LPAE_MAIR_ATTR_WBRWA
  1366		       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_CACHE)) |
  1367		      (ARM_LPAE_MAIR_ATTR_DEVICE
  1368		       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_DEV)) |
  1369		      (ARM_LPAE_MAIR_ATTR_INC_OWBRWA
  1370		       << ARM_LPAE_MAIR_ATTR_SHIFT(ARM_LPAE_MAIR_ATTR_IDX_INC_OCACHE));
  1371	
  1372		cfg->arm_lpae_s1_cfg.mair = reg;
  1373	
  1374		/* Looking good; allocate a pgd */
  1375		data->pgd = __arm_lpae_alloc_pages(ARM_LPAE_PGD_SIZE(data),
  1376						   GFP_KERNEL, cfg, cookie);
  1377		if (!data->pgd)
  1378			goto out_free_data;
  1379	
  1380		/* Ensure the empty pgd is visible before any actual TTBR write */
  1381		wmb();
  1382	
  1383		/* TTBR */
  1384		cfg->arm_lpae_s1_cfg.ttbr = virt_to_phys(data->pgd);
  1385		return &data->iop;
  1386	
  1387	out_free_data:
  1388		kfree(data);
  1389		return NULL;
  1390	}
  1391	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
  2026-02-09 11:25 [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format Liviu Dudau
                   ` (2 preceding siblings ...)
  2026-02-10  6:28 ` kernel test robot
@ 2026-02-10 10:17 ` kernel test robot
  3 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2026-02-10 10:17 UTC (permalink / raw)
  To: Liviu Dudau; +Cc: llvm, oe-kbuild-all

Hi Liviu,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build warnings:

[auto build test WARNING on arm-perf/for-next/perf]
[also build test WARNING on linus/master v6.19 next-20260209]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Liviu-Dudau/iommu-io-pgtable-Add-support-for-Arm-Mali-v10-GPUs-page-table-format/20260209-193039
base:   https://git.kernel.org/pub/scm/linux/kernel/git/will/linux.git for-next/perf
patch link:    https://lore.kernel.org/r/20260209112542.194140-1-liviu.dudau%40arm.com
patch subject: [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format
config: sparc64-allmodconfig (https://download.01.org/0day-ci/archive/20260210/202602101827.8rqDJVFA-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260210/202602101827.8rqDJVFA-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602101827.8rqDJVFA-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> drivers/iommu/io-pgtable-arm.c:870:38: warning: overlapping comparisons always evaluate to true [-Wtautological-overlap-compare]
     870 |         if (data->iop.fmt != ARM_64_LPAE_S1 ||
         |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
     871 |             data->iop.fmt != ARM_MALI_CSF)
         |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1 warning generated.


vim +870 drivers/iommu/io-pgtable-arm.c

   847	
   848	static int arm_lpae_read_and_clear_dirty(struct io_pgtable_ops *ops,
   849						 unsigned long iova, size_t size,
   850						 unsigned long flags,
   851						 struct iommu_dirty_bitmap *dirty)
   852	{
   853		struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
   854		struct io_pgtable_cfg *cfg = &data->iop.cfg;
   855		struct io_pgtable_walk_data walk_data = {
   856			.iop = &data->iop,
   857			.data = dirty,
   858			.visit = visit_dirty,
   859			.flags = flags,
   860			.addr = iova,
   861			.end = iova + size,
   862		};
   863		arm_lpae_iopte *ptep = data->pgd;
   864		int lvl = data->start_level;
   865	
   866		if (WARN_ON(!size))
   867			return -EINVAL;
   868		if (WARN_ON((iova + size - 1) & ~(BIT(cfg->ias) - 1)))
   869			return -EINVAL;
 > 870		if (data->iop.fmt != ARM_64_LPAE_S1 ||
   871		    data->iop.fmt != ARM_MALI_CSF)
   872			return -EINVAL;
   873	
   874		return __arm_lpae_iopte_walk(data, &walk_data, ptep, lvl);
   875	}
   876	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-02-10 10:18 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-09 11:25 [RFC PATCH] iommu/io-pgtable: Add support for Arm Mali v10+ GPUs page table format Liviu Dudau
2026-02-09 12:31 ` Steven Price
2026-02-09 13:27   ` Liviu Dudau
2026-02-09 13:57     ` Steven Price
2026-02-09 15:22       ` Liviu Dudau
2026-02-09 15:35         ` Boris Brezillon
2026-02-09 15:44           ` Steven Price
2026-02-09 16:02             ` Liviu Dudau
2026-02-09 14:16 ` Daniel Almeida
2026-02-09 16:04   ` Liviu Dudau
2026-02-10  6:28 ` kernel test robot
2026-02-10 10:17 ` kernel test robot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.