Guests may read back APLIC registers to inspect interrupt state and
routing configuration. When virtualising APLIC, Xen must intercept
these MMIO reads and present a consistent, restricted view of the
virtual interrupt controller state. Note that at the moment only
MSI mode is supported.

Introduce vaplic_emulate_load() to handle guest loads from virtual
APLIC registers. Readback is filtered through the domain’s authorised
interrupt bitmap so that guests can observe state only for interrupts
they are permitted to control.

Registers defined by the AIA specification to read as zero are handled
accordingly, while other registers return masked values derived from
the underlying virtual APLIC state. Unsupported accesses are treated
as fatal, as they indicate a emulation error.

Co-developed-by: Romain Caritey <[email protected]>
Signed-off-by: Oleksii Kurochko <[email protected]>
---
 xen/arch/riscv/include/asm/intc.h |  4 ++
 xen/arch/riscv/vaplic.c           | 69 +++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/xen/arch/riscv/include/asm/intc.h 
b/xen/arch/riscv/include/asm/intc.h
index 92a74eede4a0..45d41e191e30 100644
--- a/xen/arch/riscv/include/asm/intc.h
+++ b/xen/arch/riscv/include/asm/intc.h
@@ -56,6 +56,10 @@ struct vintc_ops {
     /* Initialize some vINTC-related stuff for a vCPU */
     int (*vcpu_init)(struct vcpu *vcpu);
 
+    /* Emulate load to virtual interrupt controller MMIOs */
+    int (*emulate_load)(const struct vcpu *vcpu, unsigned long addr,
+                        uint32_t *out);
+
     /* Emulate store to virtual interrupt controller MMIOs */
     int (*emulate_store)(const struct vcpu *vcpu, unsigned long addr,
                          uint32_t in);
diff --git a/xen/arch/riscv/vaplic.c b/xen/arch/riscv/vaplic.c
index 5540b4884179..293729ad0ad4 100644
--- a/xen/arch/riscv/vaplic.c
+++ b/xen/arch/riscv/vaplic.c
@@ -137,6 +137,74 @@ int vaplic_map_device_irqs_to_domain(struct domain *d,
     return 0;
 }
 
+static int vaplic_emulate_load(const struct vcpu *vcpu,
+                               const unsigned long addr, uint32_t *out)
+{
+    const struct vaplic *vaplic = to_vaplic(vcpu->domain->arch.vintc);
+    struct aplic_priv *priv = vaplic->base.info->private;
+    const unsigned long offset = addr & APLIC_REG_OFFSET_MASK;
+    const uint32_t *auth_irq_bmp = vcpu->domain->arch.vintc->private;
+    bool auth_bit;
+
+    switch ( offset )
+    {
+    case APLIC_DOMAINCFG:
+        *out = vaplic->regs.domaincfg;
+        break;
+
+    case APLIC_SETIPNUM:
+    case APLIC_SETIPNUM_LE:
+    case APLIC_CLRIPNUM:
+    case APLIC_SETIENUM:
+        /*
+         * Based on the RISC-V AIA sepc a read of these registers
+         * always returns zero
+         */
+        *out = 0;
+        break;
+
+    case APLIC_SETIP_BASE ... APLIC_SETIP_LAST:
+        auth_bit = auth_irq_bmp[regval_to_irqn(offset - APLIC_SETIP_BASE)];
+        *out = APLIC_REG_GET(priv->regs, addr - priv->paddr_start) & auth_bit;
+        break;
+
+    case APLIC_CLRIP_BASE ... APLIC_CLRIP_LAST:
+        auth_bit = auth_irq_bmp[regval_to_irqn(offset - APLIC_CLRIP_BASE)];
+        *out = APLIC_REG_GET(priv->regs, addr - priv->paddr_start) & auth_bit;
+        break;
+
+    case APLIC_SETIE_BASE ... APLIC_SETIE_LAST:
+        auth_bit = auth_irq_bmp[regval_to_irqn(offset - APLIC_CLRIP_BASE)];
+        *out = APLIC_REG_GET(priv->regs, addr - priv->paddr_start) & auth_bit;
+        break;
+
+    case APLIC_CLRIE_BASE ... APLIC_CLRIE_LAST:
+        auth_bit = auth_irq_bmp[regval_to_irqn(offset - APLIC_CLRIE_BASE)];
+        *out = APLIC_REG_GET(priv->regs, addr - priv->paddr_start) & auth_bit;
+        break;
+
+    case APLIC_TARGET_BASE ... APLIC_TARGET_LAST:
+        /*
+         * As target registers start for 1:
+         *  0x3000 genmsi
+         *  0x3004 target[1]
+         *  0x3008 target[2]
+         *   ...
+         *  0x3FFC target[1023]
+         * It is necessary to calculate an interrupt number by substracting
+         * of APLIC_GENMSI instead of APLIC_TARGET_BASE.
+         */
+        auth_bit = auth_irq_bmp[regval_to_irqn(offset - APLIC_GENMSI)];
+        *out = APLIC_REG_GET(priv->regs, addr - priv->paddr_start) & auth_bit;
+        break;
+
+    default:
+        panic("%s: unsupported register offset: %#lx", __func__, offset);
+    }
+
+    return 0;
+}
+
 static void vaplic_dm_update_target(const unsigned long hart_id, uint32_t 
*iprio)
 {
     *iprio &= APLIC_TARGET_IPRIO_MASK;
@@ -327,6 +395,7 @@ static const struct vintc_ops vaplic_ops = {
     .map_device_irqs_to_domain = vaplic_map_device_irqs_to_domain,
     .is_access = vaplic_is_access,
     .emulate_store = vaplic_emulate_store,
+    .emulate_load = vaplic_emulate_load,
 };
 
 static struct vintc * __init vaplic_alloc(void)
-- 
2.53.0


Reply via email to