summaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
Diffstat (limited to 'hw')
-rw-r--r--hw/arm/smmuv3-internal.h14
-rw-r--r--hw/arm/smmuv3.c64
-rw-r--r--hw/arm/trace-events3
3 files changed, 81 insertions, 0 deletions
diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h
index 8da38d46c0..e27c128c07 100644
--- a/hw/arm/smmuv3-internal.h
+++ b/hw/arm/smmuv3-internal.h
@@ -139,4 +139,18 @@ static inline uint32_t smmuv3_idreg(int regoffset)
return smmuv3_ids[regoffset / 4];
}
+static inline bool smmuv3_eventq_irq_enabled(SMMUv3State *s)
+{
+ return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, EVENTQ_IRQEN);
+}
+
+static inline bool smmuv3_gerror_irq_enabled(SMMUv3State *s)
+{
+ return FIELD_EX32(s->irq_ctrl, IRQ_CTRL, GERROR_IRQEN);
+}
+
+/* public until callers get introduced */
+void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask);
+void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t gerrorn);
+
#endif
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
index b61f274393..c0cedcaba3 100644
--- a/hw/arm/smmuv3.c
+++ b/hw/arm/smmuv3.c
@@ -31,6 +31,70 @@
#include "hw/arm/smmuv3.h"
#include "smmuv3-internal.h"
+/**
+ * smmuv3_trigger_irq - pulse @irq if enabled and update
+ * GERROR register in case of GERROR interrupt
+ *
+ * @irq: irq type
+ * @gerror_mask: mask of gerrors to toggle (relevant if @irq is GERROR)
+ */
+void smmuv3_trigger_irq(SMMUv3State *s, SMMUIrq irq, uint32_t gerror_mask)
+{
+
+ bool pulse = false;
+
+ switch (irq) {
+ case SMMU_IRQ_EVTQ:
+ pulse = smmuv3_eventq_irq_enabled(s);
+ break;
+ case SMMU_IRQ_PRIQ:
+ qemu_log_mask(LOG_UNIMP, "PRI not yet supported\n");
+ break;
+ case SMMU_IRQ_CMD_SYNC:
+ pulse = true;
+ break;
+ case SMMU_IRQ_GERROR:
+ {
+ uint32_t pending = s->gerror ^ s->gerrorn;
+ uint32_t new_gerrors = ~pending & gerror_mask;
+
+ if (!new_gerrors) {
+ /* only toggle non pending errors */
+ return;
+ }
+ s->gerror ^= new_gerrors;
+ trace_smmuv3_write_gerror(new_gerrors, s->gerror);
+
+ pulse = smmuv3_gerror_irq_enabled(s);
+ break;
+ }
+ }
+ if (pulse) {
+ trace_smmuv3_trigger_irq(irq);
+ qemu_irq_pulse(s->irq[irq]);
+ }
+}
+
+void smmuv3_write_gerrorn(SMMUv3State *s, uint32_t new_gerrorn)
+{
+ uint32_t pending = s->gerror ^ s->gerrorn;
+ uint32_t toggled = s->gerrorn ^ new_gerrorn;
+
+ if (toggled & ~pending) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "guest toggles non pending errors = 0x%x\n",
+ toggled & ~pending);
+ }
+
+ /*
+ * We do not raise any error in case guest toggles bits corresponding
+ * to not active IRQs (CONSTRAINED UNPREDICTABLE)
+ */
+ s->gerrorn = new_gerrorn;
+
+ trace_smmuv3_write_gerrorn(toggled & pending, s->gerrorn);
+}
+
static void smmuv3_init_regs(SMMUv3State *s)
{
/**
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
index 983ed4b68c..e192baf62d 100644
--- a/hw/arm/trace-events
+++ b/hw/arm/trace-events
@@ -15,3 +15,6 @@ smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "base
#hw/arm/smmuv3.c
smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)"
+smmuv3_trigger_irq(int irq) "irq=%d"
+smmuv3_write_gerror(uint32_t toggled, uint32_t gerror) "toggled=0x%x, new GERROR=0x%x"
+smmuv3_write_gerrorn(uint32_t acked, uint32_t gerrorn) "acked=0x%x, new GERRORN=0x%x"