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(). Signed-off-by: Shameer Kolothum <[email protected]> --- hw/arm/tegra241-cmdqv.c | 83 +++++++++++++++++++++++++++++++++++++++++ hw/arm/tegra241-cmdqv.h | 2 + hw/arm/trace-events | 3 ++ 3 files changed, 88 insertions(+) diff --git a/hw/arm/tegra241-cmdqv.c b/hw/arm/tegra241-cmdqv.c index 9e0e07e85e..3dc5315677 100644 --- a/hw/arm/tegra241-cmdqv.c +++ b/hw/arm/tegra241-cmdqv.c @@ -8,12 +8,76 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/log.h" +#include "trace.h" #include "hw/arm/smmuv3.h" +#include "hw/core/irq.h" #include "smmuv3-accel.h" #include "tegra241-cmdqv.h" +static void tegra241_cmdqv_event_read(void *opaque) +{ + Tegra241CMDQV *cmdqv = opaque; + struct { + struct iommufd_vevent_header hdr; + struct iommu_vevent_tegra241_cmdqv vevent; + } buf; + uint32_t last_seq = cmdqv->last_event_seq; + ssize_t bytes; + + bytes = read(cmdqv->veventq->veventq_fd, &buf, sizeof(buf)); + if (bytes <= 0) { + if (errno == EAGAIN || errno == EINTR) { + return; + } + error_report_once("Tegra241 CMDQV: vEVENTQ: read failed (%m)"); + return; + } + + if (bytes == sizeof(buf.hdr) && + (buf.hdr.flags & IOMMU_VEVENTQ_FLAG_LOST_EVENTS)) { + error_report_once("Tegra241 CMDQV: vEVENTQ has lost events"); + return; + } + + if (bytes < sizeof(buf)) { + error_report_once("Tegra241 `CMDQV: vEVENTQ: incomplete read (%zd/%zd bytes)", + bytes, sizeof(buf)); + cmdqv->event_start = false; + return; + } + + /* Check sequence in hdr for lost events if any */ + if (cmdqv->event_start && (buf.hdr.sequence - last_seq != 1)) { + error_report_once("Tegra241 CMDQV: vEVENTQ: detected lost %u event(s)", + buf.hdr.sequence - last_seq - 1); + } + + if (buf.vevent.lvcmdq_err_map[0] || buf.vevent.lvcmdq_err_map[1]) { + cmdqv->vintf_cmdq_err_map[0] = + buf.vevent.lvcmdq_err_map[0] & 0xffffffff; + cmdqv->vintf_cmdq_err_map[1] = + (buf.vevent.lvcmdq_err_map[0] >> 32) & 0xffffffff; + cmdqv->vintf_cmdq_err_map[2] = + buf.vevent.lvcmdq_err_map[1] & 0xffffffff; + cmdqv->vintf_cmdq_err_map[3] = + (buf.vevent.lvcmdq_err_map[1] >> 32) & 0xffffffff; + for (int i = 0; i < 4; i++) { + cmdqv->cmdq_err_map[i] = cmdqv->vintf_cmdq_err_map[i]; + } + cmdqv->vi_err_map[0] |= 0x1; + qemu_irq_pulse(cmdqv->irq); + trace_tegra241_cmdqv_err_map( + cmdqv->vintf_cmdq_err_map[3], cmdqv->vintf_cmdq_err_map[2], + cmdqv->vintf_cmdq_err_map[1], cmdqv->vintf_cmdq_err_map[0]); + } + + cmdqv->last_event_seq = buf.hdr.sequence; + cmdqv->event_start = true; +} + static bool tegra241_cmdqv_mmap_vintf_page0(Tegra241CMDQV *cmdqv, Error **errp) { IOMMUFDViommu *viommu = cmdqv->s_accel->viommu; @@ -435,6 +499,7 @@ static void tegra241_cmdqv_free_veventq(SMMUv3State *s) if (!veventq) { return; } + qemu_set_fd_handler(veventq->veventq_fd, NULL, NULL, NULL); close(veventq->veventq_fd); iommufd_backend_free_id(accel->viommu->iommufd, veventq->veventq_id); g_free(veventq); @@ -449,6 +514,7 @@ static bool tegra241_cmdqv_alloc_veventq(SMMUv3State *s, Error **errp) IOMMUFDVeventq *veventq; uint32_t veventq_id; uint32_t veventq_fd; + int flags; if (cmdqv->veventq) { return true; @@ -462,13 +528,30 @@ static bool tegra241_cmdqv_alloc_veventq(SMMUv3State *s, Error **errp) return false; } + flags = fcntl(veventq_fd, F_GETFL); + if (flags < 0) { + error_setg(errp, "Failed to get flags for vEVENTQ fd"); + goto free_veventq; + } + if (fcntl(veventq_fd, F_SETFL, O_NONBLOCK | flags) < 0) { + error_setg(errp, "Failed to set O_NONBLOCK on vEVENTQ fd"); + goto free_veventq; + } + veventq = g_new(IOMMUFDVeventq, 1); veventq->veventq_id = veventq_id; veventq->veventq_fd = veventq_fd; veventq->viommu = viommu; cmdqv->veventq = veventq; + /* Set up event handler for veventq fd */ + qemu_set_fd_handler(veventq_fd, tegra241_cmdqv_event_read, NULL, s); return true; + +free_veventq: + close(veventq_fd); + iommufd_backend_free_id(viommu->iommufd, veventq_id); + return false; } static void tegra241_cmdqv_free_viommu(SMMUv3State *s) diff --git a/hw/arm/tegra241-cmdqv.h b/hw/arm/tegra241-cmdqv.h index a3a1621e3a..fddb3ac6e9 100644 --- a/hw/arm/tegra241-cmdqv.h +++ b/hw/arm/tegra241-cmdqv.h @@ -35,6 +35,8 @@ typedef struct Tegra241CMDQV { bool vintf_page0_mapped; IOMMUFDHWqueue *vcmdq[128]; IOMMUFDVeventq *veventq; + uint32_t last_event_seq; + bool event_start; /* Register Cache */ uint32_t config; diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 3457536fb0..76bda0efef 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -72,6 +72,9 @@ smmuv3_accel_unset_iommu_device(int devfn, uint32_t devid) "devfn=0x%x (idev dev smmuv3_accel_translate_ste(uint32_t vsid, uint32_t hwpt_id, uint64_t ste_1, uint64_t ste_0) "vSID=0x%x hwpt_id=0x%x ste=%"PRIx64":%"PRIx64 smmuv3_accel_install_ste(uint32_t vsid, const char * type, uint32_t hwpt_id) "vSID=0x%x ste type=%s hwpt_id=0x%x" +# tegra241-cmdqv +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" + # strongarm.c strongarm_uart_update_parameters(const char *label, int speed, char parity, int data_bits, int stop_bits) "%s speed=%d parity=%c data=%d stop=%d" strongarm_ssp_read_underrun(void) "SSP rx underrun" -- 2.43.0
