@samitouri / QOSamiQemu / commits / 0552f94fbd

hw/arm/tegra241-cmdqv: Limit queue size based on backend page size

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 <nicolinc@nvidia.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-26-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Nicolin Chen committed Jun 9, 2026 at 12:25 UTC 0552f94fbdb6d2a3cee292911353241b1ba534cd
1 file changed +20
hw/arm/tegra241-cmdqv.c
+20
@@ -14,6 +14,8 @@
14 #include "hw/arm/smmuv3-common.h"
15 #include "hw/core/irq.h"
16 #include "smmuv3-accel.h"
17 +#include "smmuv3-internal.h"
18 +#include "system/hostmem.h"
19 #include "tegra241-cmdqv.h"
20 #include "trace.h"
21
@@ -866,6 +868,8 @@ free_viommu:
868 static void tegra241_cmdqv_init_regs(SMMUv3State *s, Tegra241CMDQV *cmdqv)
869 {
870 int i;
871 + long pgsize;
872 + uint32_t val;
873
874 cmdqv->config = V_CONFIG_RESET;
875 cmdqv->param = FIELD_DP32(0, PARAM, CMDQV_VER, CMDQV_VER);
@@ -897,6 +901,22 @@ static void tegra241_cmdqv_init_regs(SMMUv3State *s, Tegra241CMDQV *cmdqv)
901 cmdqv->vcmdq_base[i] = 0;
902 cmdqv->vcmdq_cons_indx_base[i] = 0;
903 }
904 +
905 + /*
906 + * CMDQ must not cross a physical RAM backend page. Adjust CMDQS so the
907 + * queue fits entirely within the smallest backend page size, ensuring
908 + * the command queue is physically contiguous in host memory.
909 + *
910 + * IDR1.CMDQS = log2(max_qsz) - entry_shift
911 + *
912 + * where entry_shift = 4 (each CMDQ entry is 16 bytes = 2^4).
913 + */
914 + pgsize = qemu_minrampagesize();
915 + if (pgsize == LONG_MAX) {
916 + pgsize = qemu_real_host_page_size();
917 + }
918 + val = FIELD_EX32(s->idr[1], IDR1, CMDQS);
919 + s->idr[1] = FIELD_DP32(s->idr[1], IDR1, CMDQS, MIN(ctz64(pgsize) - 4, val));
920 }
921
922 static void tegra241_cmdqv_reset(SMMUv3State *s)