hw/arm/tegra241-cmdqv: Read and propagate Tegra241 CMDQV errors
Install an event handler on the CMDQV vEVENTQ fd to read and propagate host received CMDQV errors to the guest. The handler runs in QEMU's main loop, using a non-blocking fd registered via qemu_set_fd_handler(). Reviewed-by: Nicolin Chen <nicolinc@nvidia.com> Reviewed-by: Eric Auger <eric.auger@redhat.com> Tested-by: Nicolin Chen <nicolinc@nvidia.com> Signed-off-by: Shameer Kolothum <skolothumtho@nvidia.com> Tested-by: Eric Auger <eric.auger@redhat.com> Message-id: 20260609112552.378999-24-skolothumtho@nvidia.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Shameer Kolothum committed
Jun 9, 2026 at 12:25 UTC
1de52612f3ff339598b8c19ee5caa563a371a323
2 files changed
+64
hw/arm/tegra241-cmdqv.c
+63
@@ -12,6 +12,7 @@
12
13
#include "hw/arm/smmuv3.h"
14
#include "hw/arm/smmuv3-common.h"
15
+#include "hw/core/irq.h"
16
#include "smmuv3-accel.h"
17
#include "tegra241-cmdqv.h"
18
#include "trace.h"
@@ -729,6 +730,51 @@ out:
730
trace_tegra241_cmdqv_write_mmio(offset, value, size);
731
}
732
733
+static void tegra241_cmdqv_event_read(void *opaque)
734
+{
735
+ Tegra241CMDQV *cmdqv = opaque;
736
+ IOMMUFDVeventq *veventq = cmdqv->veventq;
737
+ struct {
738
+ struct iommufd_vevent_header hdr;
739
+ struct iommu_vevent_tegra241_cmdqv vevent;
740
+ } buf;
741
+ Error *local_err = NULL;
742
+
743
+ if (!smmuv3_accel_event_read_validate(veventq,
744
+ IOMMU_VEVENTQ_TYPE_TEGRA241_CMDQV,
745
+ &buf, sizeof(buf), &local_err)) {
746
+ warn_report_err_once(local_err);
747
+ return;
748
+ }
749
+
750
+ if (buf.vevent.lvcmdq_err_map[0] || buf.vevent.lvcmdq_err_map[1]) {
751
+ cmdqv->vintf_cmdq_err_map[0] =
752
+ extract64(buf.vevent.lvcmdq_err_map[0], 0, 32);
753
+ cmdqv->vintf_cmdq_err_map[1] =
754
+ extract64(buf.vevent.lvcmdq_err_map[0], 32, 32);
755
+ cmdqv->vintf_cmdq_err_map[2] =
756
+ extract64(buf.vevent.lvcmdq_err_map[1], 0, 32);
757
+ cmdqv->vintf_cmdq_err_map[3] =
758
+ extract64(buf.vevent.lvcmdq_err_map[1], 32, 32);
759
+ /*
760
+ * CMDQV_CMDQ_ERR_MAP and VINTF0_LVCMDQ_ERR_MAP are distinct
761
+ * registers (different MMIO offsets). With only VINTF0 exposed
762
+ * they carry the same data, so mirror.
763
+ */
764
+ for (int i = 0; i < 4; i++) {
765
+ cmdqv->cmdq_err_map[i] = cmdqv->vintf_cmdq_err_map[i];
766
+ }
767
+ /* Set the VINTF0 bit in VI_ERR_MAP_0 (only VINTF0 is exposed). */
768
+ cmdqv->vi_err_map[0] |= BIT(0);
769
+ if (!(cmdqv->vi_int_mask[0] & BIT(0))) {
770
+ qemu_irq_pulse(cmdqv->irq);
771
+ }
772
+ trace_tegra241_cmdqv_err_map(
773
+ cmdqv->vintf_cmdq_err_map[3], cmdqv->vintf_cmdq_err_map[2],
774
+ cmdqv->vintf_cmdq_err_map[1], cmdqv->vintf_cmdq_err_map[0]);
775
+ }
776
+}
777
+
778
static void tegra241_cmdqv_free_viommu(SMMUv3State *s)
779
{
780
SMMUv3AccelState *accel = s->s_accel;
@@ -740,6 +786,7 @@ static void tegra241_cmdqv_free_viommu(SMMUv3State *s)
786
return;
787
}
788
if (veventq) {
789
+ qemu_set_fd_handler(veventq->veventq_fd, NULL, NULL, NULL);
790
close(veventq->veventq_fd);
791
iommufd_backend_free_id(viommu->iommufd, veventq->veventq_id);
792
g_free(veventq);
@@ -759,6 +806,7 @@ tegra241_cmdqv_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev,
806
Tegra241CMDQV *cmdqv = s->s_accel->cmdqv;
807
uint32_t viommu_id, veventq_id, veventq_fd;
808
IOMMUFDVeventq *veventq;
809
+ int flags;
810
811
if (!iommufd_backend_alloc_viommu(idev->iommufd, idev->devid,
812
IOMMU_VIOMMU_TYPE_TEGRA241_CMDQV,
@@ -784,14 +832,29 @@ tegra241_cmdqv_alloc_viommu(SMMUv3State *s, HostIOMMUDeviceIOMMUFD *idev,
832
goto munmap_page0;
833
}
834
835
+ flags = fcntl(veventq_fd, F_GETFL);
836
+ if (flags < 0) {
837
+ error_setg(errp, "Failed to get flags for vEVENTQ fd");
838
+ goto free_veventq;
839
+ }
840
+ if (fcntl(veventq_fd, F_SETFL, O_NONBLOCK | flags) < 0) {
841
+ error_setg(errp, "Failed to set O_NONBLOCK on vEVENTQ fd");
842
+ goto free_veventq;
843
+ }
844
+
845
veventq = g_new(IOMMUFDVeventq, 1);
846
veventq->veventq_id = veventq_id;
847
veventq->veventq_fd = veventq_fd;
848
cmdqv->veventq = veventq;
849
850
+ /* Set up event handler for veventq fd */
851
+ qemu_set_fd_handler(veventq_fd, tegra241_cmdqv_event_read, NULL, cmdqv);
852
*out_viommu_id = viommu_id;
853
return true;
854
855
+free_veventq:
856
+ close(veventq_fd);
857
+ iommufd_backend_free_id(idev->iommufd, veventq_id);
858
munmap_page0:
859
munmap(cmdqv->vintf_page0, VINTF_PAGE_SIZE);
860
cmdqv->vintf_page0 = NULL;
hw/arm/trace-events
+1
@@ -75,6 +75,7 @@ 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_err_map(uint32_t map3, uint32_t map2, uint32_t map1, uint32_t map0) "hw irq received. error (hex) maps: %04X:%04X:%04X:%04X"
79
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
80
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
81
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