From: Nicolin Chen <[email protected]>

CMDQV HW reads guest queue memory by its host physical address set
up via IOMMUFD. This requires the guest queue to be contiguous in
both guest PA and host PA space. With Tegra241 CMDQV enabled, we
must only advertise a command queue size (CMDQS) that the host can
safely back with physically contiguous memory. Allowing a queue size
larger than the host page size could cause the hardware to DMA across
page boundaries, leading to faults.

Use qemu_ram_backend_pagesize_min() to find the smallest memory-
backend page size in use, then cap IDR1.CMDQS so the guest cannot
configure a command queue that exceeds that contiguous backing.

Signed-off-by: Nicolin Chen <[email protected]>
Signed-off-by: Shameer Kolothum <[email protected]>
---
 hw/arm/tegra241-cmdqv.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c
index ad64f06260..7f617bcc97 100644
--- a/hw/arm/tegra241-cmdqv.c
+++ b/hw/arm/tegra241-cmdqv.c
@@ -16,6 +16,8 @@
 #include "hw/arm/smmuv3-common.h"
 #include "hw/core/irq.h"
 #include "smmuv3-accel.h"
+#include "smmuv3-internal.h"
+#include "system/ramblock.h"
 #include "tegra241-cmdqv.h"
 #include "trace.h"
 
@@ -856,6 +858,8 @@ free_viommu:
 static void tegra241_cmdqv_init_regs(SMMUv3State *s, Tegra241CMDQV *cmdqv)
 {
     int i;
+    size_t pgsize;
+    uint32_t val;
 
     cmdqv->config = V_CONFIG_RESET;
     cmdqv->param = FIELD_DP32(0, PARAM, CMDQV_VER, CMDQV_VER);
@@ -887,6 +891,19 @@ static void tegra241_cmdqv_init_regs(SMMUv3State *s, 
Tegra241CMDQV *cmdqv)
         cmdqv->vcmdq_base[i] = 0;
         cmdqv->vcmdq_cons_indx_base[i] = 0;
     }
+
+    /*
+     * CMDQ must not cross a physical RAM backend page. Adjust CMDQS so the
+     * queue fits entirely within the smallest backend page size, ensuring
+     * the command queue is physically contiguous in host memory.
+     *
+     *   IDR1.CMDQS = log2(max_qsz) - entry_shift
+     *
+     * where entry_shift = 4 (each CMDQ entry is 16 bytes = 2^4).
+     */
+    pgsize = qemu_ram_backend_pagesize_min();
+    val = FIELD_EX32(s->idr[1], IDR1, CMDQS);
+    s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS, MIN(ctz64(pgsize) - 4, 
val));
 }
 
 static void tegra241_cmdqv_reset(SMMUv3State *s)
-- 
2.43.0


Reply via email to