In the GIC system instructions, VDPEND is special: the other "act on virtual interrupt domain" operations act on the resident VPE/VM, but this one allows the guest to pass in the VM ID, so that a hypervisor can pend an interrupt for a VM even if there isn't currently a VPE for that VM resident on the CPU.
Add the VM argument to gicv5_set_pending(), and fill it in with the VM argument from the guest for VDPEND, or the current VM (as set in ICH_CONTEXTR_EL2) for CDPEND and LDPEND. (The IRS will fail all virtual interrupt operations until we implement its virtualization support.) The placing of the VM field moves the PENDING bit from bit 32 to 63, so we need also to special case the reading of that bit. Signed-off-by: Peter Maydell <[email protected]> --- hw/intc/arm_gicv5.c | 5 +++-- hw/intc/trace-events | 2 +- include/hw/intc/arm_gicv5_stream.h | 3 ++- target/arm/tcg/gicv5-cpuif.c | 34 +++++++++++++++++++++++++++++- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c index 5ac50acc786..d503d8db426 100644 --- a/hw/intc/arm_gicv5.c +++ b/hw/intc/arm_gicv5.c @@ -855,13 +855,14 @@ void gicv5_set_enabled(GICv5Common *cs, uint32_t id, bool enabled, } void gicv5_set_pending(GICv5Common *cs, uint32_t id, bool pending, - GICv5Domain domain, GICv5IntType type, bool virtual) + GICv5Domain domain, GICv5IntType type, bool virtual, + uint32_t vm) { GICv5 *s = ARM_GICV5(cs); uint32_t iaffid; trace_gicv5_set_pending(domain_name[domain], inttype_name(type), virtual, - id, pending); + vm, id, pending); if (virtual) { qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_pending: tried to set " "pending state of a virtual interrupt\n"); diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 7512c6e6004..6681e32d1b7 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -237,7 +237,7 @@ gicv5_ist_valid(const char *domain, uint64_t base, uint8_t id_bits, uint8_t l2_i gicv5_ist_invalid(const char *domain) "GICv5 IRS %s IST no longer valid" gicv5_set_priority(const char *domain, const char *type, bool virtual, uint32_t id, uint8_t priority) "GICv5 IRS SetPriority %s %s virtual:%d ID %u prio %u" gicv5_set_enabled(const char *domain, const char *type, bool virtual, uint32_t id, bool enabled) "GICv5 IRS SetEnabled %s %s virtual:%d ID %u enabled %d" -gicv5_set_pending(const char *domain, const char *type, bool virtual, uint32_t id, bool pending) "GICv5 IRS SetPending %s %s virtual:%d ID %u pending %d" +gicv5_set_pending(const char *domain, const char *type, bool virtual, uint32_t vm, uint32_t id, bool pending) "GICv5 IRS SetPending %s %s virtual:%d VM %u ID %u pending %d" gicv5_set_handling(const char *domain, const char *type, bool virtual, uint32_t id, int handling) "GICv5 IRS SetHandling %s %s virtual:%d ID %u handling %d" gicv5_set_target(const char *domain, const char *type, bool virtual, uint32_t id, uint32_t iaffid, int irm) "GICv5 IRS SetTarget %s %s virtual:%d ID %u IAFFID %u routingmode %d" gicv5_request_config(const char *domain, const char *type, bool virtual, uint32_t id, uint64_t icsr) "GICv5 IRS RequestConfig %s %s virtual:%d ID %u ICSR 0x%" PRIx64 diff --git a/include/hw/intc/arm_gicv5_stream.h b/include/hw/intc/arm_gicv5_stream.h index 162d55a8e6b..f6d32c6beca 100644 --- a/include/hw/intc/arm_gicv5_stream.h +++ b/include/hw/intc/arm_gicv5_stream.h @@ -86,6 +86,7 @@ void gicv5_set_enabled(GICv5Common *cs, uint32_t id, * @domain: interrupt Domain to act on * @type: interrupt type (LPI or SPI) * @virtual: true if this is a virtual interrupt + * @vm: VM ID (for virtual interrupts) * * Set pending state of an interrupt; matches stream interface * SetPending command from CPUIF to IRS. There is no report back of @@ -93,7 +94,7 @@ void gicv5_set_enabled(GICv5Common *cs, uint32_t id, */ void gicv5_set_pending(GICv5Common *cs, uint32_t id, bool pending, GICv5Domain domain, - GICv5IntType type, bool virtual); + GICv5IntType type, bool virtual, uint32_t vm); /** * gicv5_set_handling diff --git a/target/arm/tcg/gicv5-cpuif.c b/target/arm/tcg/gicv5-cpuif.c index 4ee548a768b..7f0109e9b12 100644 --- a/target/arm/tcg/gicv5-cpuif.c +++ b/target/arm/tcg/gicv5-cpuif.c @@ -40,6 +40,11 @@ FIELD(GIC_CDPEND, ID, 0, 24) FIELD(GIC_CDPEND, TYPE, 29, 3) FIELD(GIC_CDPEND, PENDING, 32, 1) +FIELD(GIC_VDPEND, ID, 0, 24) +FIELD(GIC_VDPEND, TYPE, 29, 3) +FIELD(GIC_VDPEND, VM, 32, 16) +FIELD(GIC_VDPEND, PENDING, 63, 1) + FIELD(GIC_CDHM, ID, 0, 24) FIELD(GIC_CDHM, TYPE, 29, 3) FIELD(GIC_CDHM, HM, 32, 1) @@ -594,8 +599,35 @@ static void gic_cdpend_write(CPUARMState *env, const ARMCPRegInfo *ri, uint32_t id = FIELD_EX64(value, GIC_CDPEND, ID); bool virtual; GICv5Domain domain = gicv5_domain_for_insn(env, ri, &virtual); + uint32_t vm = 0; - gicv5_set_pending(gic, id, pending, domain, type, virtual); + if (virtual) { + if (ri->opc1 == 4) { + /* + * GIC VDPEND has a VM argument field, and PENDING + * is in a different bit location to normal + */ + vm = FIELD_EX64(value, GIC_VDPEND, VM); + pending = FIELD_EX64(value, GIC_VDPEND, PENDING); + } else { + /* + * GIC CDPEND for the virtual interrupt domain operates on + * the VM for the resident VPE. Our implementation of + * ICH_CONTEXTR_EL2 means we can trust that if the V bit + * is set the VM field is the right one. + */ + if (FIELD_EX64(env->gicv5_cpuif.ich_contextr_el2, + ICH_CONTEXTR_EL2, V)) { + vm = FIELD_EX64(env->gicv5_cpuif.ich_contextr_el2, + ICH_CONTEXTR_EL2, VM); + } else { + /* no resident VPE: instruction is a NOP */ + return; + } + } + } + + gicv5_set_pending(gic, id, pending, domain, type, virtual, vm); } static void gic_cdrcfg_write(CPUARMState *env, const ARMCPRegInfo *ri, -- 2.43.0
