From: Nicolin Chen <[email protected]> CMDQV HW performs DMA accesses to 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_minrampagesize() 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. Note this is done at SMMUv3 init, before any guest queue GPA is known, so the cap is conservative. Maximum queue size is 8MiB; it is recommended to back the VM with hugepage sizes large enough so CMDQS stays at the HW maximum. Smaller backing pages reduce CMDQS accordingly. Signed-off-by: Nicolin Chen <[email protected]> Signed-off-by: Shameer Kolothum <[email protected]> --- hw/arm/tegra241-cmdqv.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index ac17b56c1c..661c46e3d9 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/hostmem.h" #include "tegra241-cmdqv.h" #include "trace.h" @@ -874,6 +876,8 @@ free_viommu: static void tegra241_cmdqv_init_regs(SMMUv3State *s, Tegra241CMDQV *cmdqv) { int i; + long pgsize; + uint32_t val; cmdqv->config = V_CONFIG_RESET; cmdqv->param = FIELD_DP32(0, PARAM, CMDQV_VER, CMDQV_VER); @@ -905,6 +909,22 @@ 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_minrampagesize(); + if (pgsize == LONG_MAX) { + pgsize = qemu_real_host_page_size(); + } + 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
