On 6/1/26 1:42 PM, Shameer Kolothum wrote:
> 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]>
Reviewed-by: Eric Auger <[email protected]>
Eric
> ---
> 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)