@samitouri / QOSamiQemu / commits / 1d0f775354

hw/arm/tegra241-cmdqv: Route allocated VCMDQ Page0 accesses to the mmap'd host VINTF page0

Introduce tegra241_cmdqv_vintf_lvcmdq_ptr() to route VCMDQ Page 0 register accesses through the mmap'd host VINTF Page 0 backing once a hardware queue has been allocated for the VCMDQ. The two QEMU-trapped Page 0 apertures (direct at 0x10000, VINTF at 0x30000) are hardware aliases of the same underlying registers. A subsequent patch installs the VINTF aperture as a RAM-device into guest MMIO; in this patch both remain QEMU-trapped. The direct VCMDQ aperture stays QEMU-trapped (rather than aliased to the VINTF mmap) so that writes to an unallocated VCMDQ remain well-defined. The CMDQV architecture allows software to program a VCMDQ through the direct aperture without first allocating it to a VINTF; aliasing to the VINTF mmap would route those writes into unallocated logical slots where the hardware silently drops them. A VCMDQ Page 0 access is served from one of two sources: - Cache-backed: no hw_queue is allocated for the VCMDQ (HW_QUEUE_ALLOC has not yet succeeded). Both apertures use QEMU's register cache. - HW-backed: HW_QUEUE_ALLOC has succeeded. Both apertures access the registers directly through the mmap'd host VINTF Page 0. tegra241_cmdqv_sync_vcmdq() copies any cached writes (CONS_INDX, PROD_INDX, CONFIG, GERRORN) into the mmap'd page on the cache-to-HW transition so the guest's earlier register state survives. Freeing a VCMDQ clears the cached Page0 registers. Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Message-id: 20260609112552.378999-20-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

Shameer Kolothum committed Jun 9, 2026 at 12:25 UTC 1d0f7753541784cbafb7aee3adc843816bf7c66d
2 files changed +91 -2
hw/arm/tegra241-cmdqv.c
+89
@@ -16,6 +16,16 @@
16 #include "tegra241-cmdqv.h"
17 #include "trace.h"
18
19 +static void tegra241_cmdqv_reset_vcmdq_cache(Tegra241CMDQV *cmdqv, int index)
20 +{
21 + cmdqv->vcmdq_cons_indx[index] = 0;
22 + cmdqv->vcmdq_prod_indx[index] = 0;
23 + cmdqv->vcmdq_config[index] = 0;
24 + cmdqv->vcmdq_status[index] = 0;
25 + cmdqv->vcmdq_gerror[index] = 0;
26 + cmdqv->vcmdq_gerrorn[index] = 0;
27 +}
28 +
29 static void tegra241_cmdqv_free_vcmdq(Tegra241CMDQV *cmdqv, int index)
30 {
31 IOMMUFDViommu *viommu = cmdqv->s_accel->viommu;
@@ -27,6 +37,7 @@ static void tegra241_cmdqv_free_vcmdq(Tegra241CMDQV *cmdqv, int index)
37 iommufd_backend_free_id(viommu->iommufd, vcmdq->hw_queue_id);
38 g_free(vcmdq);
39 cmdqv->vcmdq[index] = NULL;
40 + tegra241_cmdqv_reset_vcmdq_cache(cmdqv, index);
41 }
42
43 /*
@@ -45,6 +56,47 @@ static bool tegra241_cmdqv_vcmdq_ready_to_alloc(Tegra241CMDQV *cmdqv, int index)
56 tegra241_cmdqv_enabled(cmdqv) && tegra241_vintf_enabled(cmdqv);
57 }
58
59 +/*
60 + * Return a pointer into the mmap'd VINTF page0 for the VCMDQ Page 0
61 + * register at @offset0 in VCMDQ slot @index, or NULL when the VCMDQ
62 + * has no hw_queue allocated or the host VINTF page0 is not mmap'd.
63 + */
64 +static inline uint32_t *tegra241_cmdqv_vintf_lvcmdq_ptr(Tegra241CMDQV *cmdqv,
65 + int index, hwaddr offset0)
66 +{
67 + if (!cmdqv->vcmdq[index] || !cmdqv->vintf_page0) {
68 + return NULL;
69 + }
70 + return (uint32_t *)(cmdqv->vintf_page0 +
71 + (index * CMDQV_VCMDQ_STRIDE) +
72 + (offset0 - CMDQV_VCMDQ_PAGE0_BASE));
73 +}
74 +
75 +/*
76 + * Flush cached register writes into the mmap'd host VINTF page0 after a
77 + * successful HW_QUEUE_ALLOC, so the guest's earlier writes survive
78 + * the cache-to-hardware transition.
79 + */
80 +static void tegra241_cmdqv_sync_vcmdq(Tegra241CMDQV *cmdqv, int index)
81 +{
82 + uint32_t *ptr;
83 +
84 + ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, A_VCMDQ0_CONS_INDX);
85 + if (!ptr) {
86 + return;
87 + }
88 + *ptr = cmdqv->vcmdq_cons_indx[index];
89 +
90 + ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, A_VCMDQ0_PROD_INDX);
91 + *ptr = cmdqv->vcmdq_prod_indx[index];
92 +
93 + ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, A_VCMDQ0_CONFIG);
94 + *ptr = cmdqv->vcmdq_config[index];
95 +
96 + ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, A_VCMDQ0_GERRORN);
97 + *ptr = cmdqv->vcmdq_gerrorn[index];
98 +}
99 +
100 /*
101 * Allocate a host HW VCMDQ from the current cached BASE / size for @index.
102 * No-op (returns true) until the VCMDQ is ready to be allocated.
@@ -85,6 +137,9 @@ static bool tegra241_cmdqv_setup_vcmdq(Tegra241CMDQV *cmdqv, int index,
137 cmdqv->vcmdq_gerror[index] &= ~R_VCMDQ0_GERROR_CMDQ_INIT_ERR_MASK;
138 cmdqv->vcmdq_status[index] |= R_VCMDQ0_STATUS_CMDQ_EN_OK_MASK;
139
140 + /* Push cached writes to HW; freeing resets the cache. */
141 + tegra241_cmdqv_sync_vcmdq(cmdqv, index);
142 +
143 return true;
144 }
145
@@ -111,13 +166,23 @@ static void tegra241_cmdqv_setup_all_vcmdq(Tegra241CMDQV *cmdqv,
166 *
167 * The caller normalizes the MMIO offset such that @offset0 always refers
168 * to a VCMDQ0_* register, while @index selects the VCMDQ instance.
169 + *
170 + * If the VCMDQ is allocated and the host VINTF page0 is mmap'd, read
171 + * directly from the host VINTF page0 backing. Otherwise, fall back to
172 + * the cache.
173 */
174 static uint64_t tegra241_cmdqv_read_vcmdq_page0(Tegra241CMDQV *cmdqv,
175 hwaddr offset0, int index,
176 bool direct)
177 {
178 + uint32_t *ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, offset0);
179 uint64_t val = 0;
180
181 + if (ptr) {
182 + val = *ptr;
183 + goto out;
184 + }
185 +
186 switch (offset0) {
187 case A_VCMDQ0_CONS_INDX:
188 val = cmdqv->vcmdq_cons_indx[index];
@@ -142,7 +207,9 @@ static uint64_t tegra241_cmdqv_read_vcmdq_page0(Tegra241CMDQV *cmdqv,
207 "%s unhandled read access at 0x%" PRIx64 "\n",
208 __func__, offset0);
209 }
210 +out:
211 trace_tegra241_cmdqv_read_vcmdq_page0(index, direct ? "direct" : "vi",
212 + ptr ? "hw" : "cache",
213 offset0, val);
214 return val;
215 }
@@ -218,11 +285,31 @@ static uint64_t tegra241_cmdqv_config_vintf_read(Tegra241CMDQV *cmdqv,
285 *
286 * Page 0 registers are all 32-bit; this helper is only called for 4-byte
287 * writes.
288 + *
289 + * If the VCMDQ is allocated and the host VINTF page0 is mmap'd, write
290 + * directly to the VINTF page0 backing. Otherwise, update the cache.
291 */
292 static void tegra241_cmdqv_write_vcmdq_page0(Tegra241CMDQV *cmdqv,
293 hwaddr offset0, int index,
294 uint32_t value, bool direct)
295 {
296 + uint32_t *ptr = tegra241_cmdqv_vintf_lvcmdq_ptr(cmdqv, index, offset0);
297 + bool hw = false;
298 +
299 + if (ptr) {
300 + switch (offset0) {
301 + case A_VCMDQ0_CONS_INDX:
302 + case A_VCMDQ0_PROD_INDX:
303 + case A_VCMDQ0_CONFIG:
304 + case A_VCMDQ0_GERRORN:
305 + *ptr = value;
306 + hw = true;
307 + goto out;
308 + default:
309 + break;
310 + }
311 + }
312 +
313 switch (offset0) {
314 case A_VCMDQ0_CONS_INDX:
315 cmdqv->vcmdq_cons_indx[index] = value;
@@ -253,7 +340,9 @@ static void tegra241_cmdqv_write_vcmdq_page0(Tegra241CMDQV *cmdqv,
340 "%s unhandled write access at 0x%" PRIx64 "\n",
341 __func__, offset0);
342 }
343 +out:
344 trace_tegra241_cmdqv_write_vcmdq_page0(index, direct ? "direct" : "vi",
345 + hw ? "hw" : "cache",
346 offset0, value);
347 }
348
hw/arm/trace-events
+2 -2
@@ -75,9 +75,9 @@ smmuv3_accel_install_ste(uint32_t vsid, const char * type, uint32_t hwpt_id) "vS
75 # tegra241-cmdqv
76 tegra241_cmdqv_read_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x"
77 tegra241_cmdqv_write_mmio(uint64_t offset, uint64_t val, unsigned size) "offset: 0x%"PRIx64" val: 0x%"PRIx64" size: 0x%x"
78 -tegra241_cmdqv_read_vcmdq_page0(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64
78 +tegra241_cmdqv_read_vcmdq_page0(int index, const char *aperture, const char *backing, uint64_t offset0, uint64_t val) "vcmdq[%d] %s (%s) offset0: 0x%"PRIx64" val: 0x%"PRIx64
79 tegra241_cmdqv_read_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64
80 -tegra241_cmdqv_write_vcmdq_page0(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64
80 +tegra241_cmdqv_write_vcmdq_page0(int index, const char *aperture, const char *backing, uint64_t offset0, uint64_t val) "vcmdq[%d] %s (%s) offset0: 0x%"PRIx64" val: 0x%"PRIx64
81 tegra241_cmdqv_write_vcmdq_page1(int index, const char *aperture, uint64_t offset0, uint64_t val) "vcmdq[%d] %s offset0: 0x%"PRIx64" val: 0x%"PRIx64
82
83 # strongarm.c