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