[PATCH] drm/etnaviv: Honor userptr read-only at the GPU MMU

Ziyi Guo posted 1 patch 1 month ago
drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
[PATCH] drm/etnaviv: Honor userptr read-only at the GPU MMU
Posted by Ziyi Guo 1 month ago
The IOMMU mapping for a userptr BO was installed with a hard-coded
ETNAVIV_PROT_READ | ETNAVIV_PROT_WRITE, ignoring the read-only
attribute set when the BO was created without ETNA_USERPTR_WRITE.
Build the prot mask from etnaviv_obj->userptr.ro so MMUv2 clears
MMUv2_PTE_WRITEABLE for read-only userptr BOs.

MMUv1 PTEs carry no R/W bits and its fast path skips the MMU, so
read-only cannot be enforced there; refuse such a mapping with
-ENODEV.

Fixes: a8c21a5451d8 ("drm/etnaviv: add initial etnaviv DRM driver")
Signed-off-by: Ziyi Guo <n7l8m4@u.northwestern.edu>
---
 drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
index e3572461b599..28f26d60ac05 100644
--- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
+++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
@@ -269,10 +269,18 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu_context *context,
 {
 	struct sg_table *sgt = etnaviv_obj->sgt;
 	struct drm_mm_node *node;
+	int prot = ETNAVIV_PROT_READ;
 	int ret;
 
 	lockdep_assert_held(&etnaviv_obj->lock);
 
+	if (!etnaviv_obj->userptr.ptr || !etnaviv_obj->userptr.ro)
+		prot |= ETNAVIV_PROT_WRITE;
+
+	if (etnaviv_obj->userptr.ptr && etnaviv_obj->userptr.ro &&
+	    context->global->version == ETNAVIV_IOMMU_V1)
+		return -ENODEV;
+
 	mutex_lock(&context->lock);
 
 	/* v1 MMU can optimize single entry (contiguous) scatterlists */
@@ -301,7 +309,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu_context *context,
 
 	mapping->iova = node->start;
 	ret = etnaviv_iommu_map(context, node->start, etnaviv_obj->size, sgt,
-				ETNAVIV_PROT_READ | ETNAVIV_PROT_WRITE);
+				prot);
 
 	if (ret < 0) {
 		drm_mm_remove_node(node);
-- 
2.34.1
Re: [PATCH] drm/etnaviv: Honor userptr read-only at the GPU MMU
Posted by Lucas Stach 1 month ago
Am Freitag, dem 08.05.2026 um 18:05 +0000 schrieb Ziyi Guo:
> The IOMMU mapping for a userptr BO was installed with a hard-coded
> ETNAVIV_PROT_READ | ETNAVIV_PROT_WRITE, ignoring the read-only
> attribute set when the BO was created without ETNA_USERPTR_WRITE.
> Build the prot mask from etnaviv_obj->userptr.ro so MMUv2 clears
> MMUv2_PTE_WRITEABLE for read-only userptr BOs.
> 
> MMUv1 PTEs carry no R/W bits and its fast path skips the MMU, so
> read-only cannot be enforced there; refuse such a mapping with
> -ENODEV.

I don't think it's a good idea to completely reject those mappings on
MMUv1. We can always force a BO to be mapped through the
translation/protection region of the MMUv1 by adding the
ETNA_BO_FORCE_MMU flag, which is what you might want to do when
constructing a ro userptr BO.

Regards,
Lucas

> 
> Fixes: a8c21a5451d8 ("drm/etnaviv: add initial etnaviv DRM driver")
> Signed-off-by: Ziyi Guo <n7l8m4@u.northwestern.edu>
> ---
>  drivers/gpu/drm/etnaviv/etnaviv_mmu.c | 10 +++++++++-
>  1 file changed, 9 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
> index e3572461b599..28f26d60ac05 100644
> --- a/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
> +++ b/drivers/gpu/drm/etnaviv/etnaviv_mmu.c
> @@ -269,10 +269,18 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu_context *context,
>  {
>  	struct sg_table *sgt = etnaviv_obj->sgt;
>  	struct drm_mm_node *node;
> +	int prot = ETNAVIV_PROT_READ;
>  	int ret;
>  
>  	lockdep_assert_held(&etnaviv_obj->lock);
>  
> +	if (!etnaviv_obj->userptr.ptr || !etnaviv_obj->userptr.ro)
> +		prot |= ETNAVIV_PROT_WRITE;
> +
> +	if (etnaviv_obj->userptr.ptr && etnaviv_obj->userptr.ro &&
> +	    context->global->version == ETNAVIV_IOMMU_V1)
> +		return -ENODEV;
> +
>  	mutex_lock(&context->lock);
>  
>  	/* v1 MMU can optimize single entry (contiguous) scatterlists */
> @@ -301,7 +309,7 @@ int etnaviv_iommu_map_gem(struct etnaviv_iommu_context *context,
>  
>  	mapping->iova = node->start;
>  	ret = etnaviv_iommu_map(context, node->start, etnaviv_obj->size, sgt,
> -				ETNAVIV_PROT_READ | ETNAVIV_PROT_WRITE);
> +				prot);
>  
>  	if (ret < 0) {
>  		drm_mm_remove_node(node);