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 <[email protected]>
---
 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

Reply via email to