Implement the GICv5 functions corresponding to the stream protocol SetEnabled, SetPending, SetHandling, and SetTarget commands. These work exactly like SetPriority: the IRS looks up the L2TE and updates the corresponding field in it with the new value.
Signed-off-by: Peter Maydell <[email protected]> --- hw/intc/arm_gicv5.c | 133 +++++++++++++++++++++++++++++ hw/intc/trace-events | 4 + include/hw/intc/arm_gicv5_stream.h | 68 +++++++++++++++ include/hw/intc/arm_gicv5_types.h | 15 ++++ 4 files changed, 220 insertions(+) diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c index af27fb7e63..3c6ef17573 100644 --- a/hw/intc/arm_gicv5.c +++ b/hw/intc/arm_gicv5.c @@ -492,6 +492,139 @@ void gicv5_set_priority(GICv5Common *cs, uint32_t id, put_l2_iste(cs, cfg, &h); } +void gicv5_set_enabled(GICv5Common *cs, uint32_t id, + bool enabled, GICv5Domain domain, + GICv5IntType type, bool virtual) +{ + const GICv5ISTConfig *cfg; + GICv5 *s = ARM_GICV5(cs); + uint32_t *l2_iste_p; + L2_ISTE_Handle h; + + trace_gicv5_set_enabled(domain_name[domain], inttype_name(type), virtual, + id, enabled); + if (virtual) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_enabled: tried to set " + "enable state of a virtual interrupt\n"); + return; + } + if (type != GICV5_LPI) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_enabled: tried to set " + "enable state of bad interrupt type %d\n", type); + return; + } + cfg = &s->phys_lpi_config[domain]; + l2_iste_p = get_l2_iste(cs, cfg, id, &h); + if (!l2_iste_p) { + return; + } + *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, ENABLE, enabled); + put_l2_iste(cs, cfg, &h); +} + +void gicv5_set_pending(GICv5Common *cs, uint32_t id, + bool pending, GICv5Domain domain, + GICv5IntType type, bool virtual) +{ + const GICv5ISTConfig *cfg; + GICv5 *s = ARM_GICV5(cs); + uint32_t *l2_iste_p; + L2_ISTE_Handle h; + + trace_gicv5_set_pending(domain_name[domain], inttype_name(type), virtual, + id, pending); + if (virtual) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_pending: tried to set " + "pending state of a virtual interrupt\n"); + return; + } + if (type != GICV5_LPI) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_pending: tried to set " + "pending state of bad interrupt type %d\n", type); + return; + } + cfg = &s->phys_lpi_config[domain]; + l2_iste_p = get_l2_iste(cs, cfg, id, &h); + if (!l2_iste_p) { + return; + } + *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, PENDING, pending); + put_l2_iste(cs, cfg, &h); +} + +void gicv5_set_handling(GICv5Common *cs, uint32_t id, + GICv5HandlingMode handling, GICv5Domain domain, + GICv5IntType type, bool virtual) +{ + const GICv5ISTConfig *cfg; + GICv5 *s = ARM_GICV5(cs); + uint32_t *l2_iste_p; + L2_ISTE_Handle h; + + trace_gicv5_set_handling(domain_name[domain], inttype_name(type), virtual, + id, handling); + if (virtual) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_handling: tried to set " + "handling mode of a virtual interrupt\n"); + return; + } + if (type != GICV5_LPI) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_handling: tried to set " + "handling mode of bad interrupt type %d\n", type); + return; + } + cfg = &s->phys_lpi_config[domain]; + l2_iste_p = get_l2_iste(cs, cfg, id, &h); + if (!l2_iste_p) { + return; + } + *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, HM, handling); + put_l2_iste(cs, cfg, &h); +} + +void gicv5_set_target(GICv5Common *cs, uint32_t id, uint32_t iaffid, + GICv5RoutingMode irm, GICv5Domain domain, + GICv5IntType type, bool virtual) +{ + const GICv5ISTConfig *cfg; + GICv5 *s = ARM_GICV5(cs); + uint32_t *l2_iste_p; + L2_ISTE_Handle h; + + trace_gicv5_set_target(domain_name[domain], inttype_name(type), virtual, + id, iaffid, irm); + if (virtual) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_target: tried to set " + "target of a virtual interrupt\n"); + return; + } + if (irm != GICV5_TARGETED) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_target: tried to set " + "1-of-N routing\n"); + /* + * In the cpuif insn "GIC CDAFF", IRM is RES0 for a GIC which does not + * support 1-of-N routing. So warn, and fall through to treat + * IRM=1 the same as IRM=0. + */ + } + if (type != GICV5_LPI) { + qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_target: tried to set " + "target of bad interrupt type %d\n", type); + return; + } + cfg = &s->phys_lpi_config[domain]; + l2_iste_p = get_l2_iste(cs, cfg, id, &h); + if (!l2_iste_p) { + return; + } + /* + * For QEMU we do not implement 1-of-N routing, and so L2_ISTE.IRM is RES0. + * We never read it, and we can skip explicitly writing it to zero here. + */ + *l2_iste_p = FIELD_DP32(*l2_iste_p, L2_ISTE, IAFFID, iaffid); + put_l2_iste(cs, cfg, &h); +} + static void irs_map_l2_istr_write(GICv5 *s, GICv5Domain domain, uint64_t value) { GICv5Common *cs = ARM_GICV5_COMMON(s); diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 42f5e73d54..37ca6e8e12 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -236,6 +236,10 @@ gicv5_spi(uint32_t id, int level) "GICv5 SPI ID %u asserted at level %d" gicv5_ist_valid(const char *domain, uint64_t base, uint8_t id_bits, uint8_t l2_idx_bits, uint8_t istsz, bool structure) "GICv5 IRS %s IST now valid: base 0x%" PRIx64 " id_bits %u l2_idx_bits %u IST entry size %u 2-level %d" 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_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" # arm_gicv5_common.c gicv5_common_realize(uint32_t irsid, uint32_t num_cpus, uint32_t spi_base, uint32_t spi_irs_range, uint32_t spi_range) "GICv5 IRS realized: IRS ID %u, %u CPUs, SPI base %u, SPI IRS range %u, SPI range %u" diff --git a/include/hw/intc/arm_gicv5_stream.h b/include/hw/intc/arm_gicv5_stream.h index 3239a86f1a..db0e3e01c6 100644 --- a/include/hw/intc/arm_gicv5_stream.h +++ b/include/hw/intc/arm_gicv5_stream.h @@ -58,4 +58,72 @@ void gicv5_set_priority(GICv5Common *cs, uint32_t id, uint8_t priority, GICv5Domain domain, GICv5IntType type, bool virtual); +/** + * gicv5_set_enabled + * @cs: GIC IRS to send command to + * @id: interrupt ID + * @enabled: new enabled state + * @domain: interrupt Domain to act on + * @type: interrupt type (LPI or SPI) + * @virtual: true if this is a virtual interrupt + * + * Set enabled state of an interrupt; matches stream interface + * SetEnabled command from CPUIF to IRS. There is no report back + * of success/failure to the CPUIF in the protocol. + */ +void gicv5_set_enabled(GICv5Common *cs, uint32_t id, + bool enabled, GICv5Domain domain, + GICv5IntType type, bool virtual); + +/** + * gicv5_set_pending + * @cs: GIC IRS to send command to + * @id: interrupt ID + * @pending: new pending state + * @domain: interrupt Domain to act on + * @type: interrupt type (LPI or SPI) + * @virtual: true if this is a virtual interrupt + * + * Set pending state of an interrupt; matches stream interface + * SetPending command from CPUIF to IRS. There is no report back + * of success/failure to the CPUIF in the protocol. + */ +void gicv5_set_pending(GICv5Common *cs, uint32_t id, + bool pending, GICv5Domain domain, + GICv5IntType type, bool virtual); + +/** + * gicv5_set_handling + * @cs: GIC IRS to send command to + * @id: interrupt ID + * @handling: new handling mode + * @domain: interrupt Domain to act on + * @type: interrupt type (LPI or SPI) + * @virtual: true if this is a virtual interrupt + * + * Set handling mode of an interrupt (edge/level); matches stream interface + * SetHandling command from CPUIF to IRS. There is no report back + * of success/failure to the CPUIF in the protocol. + */ +void gicv5_set_handling(GICv5Common *cs, uint32_t id, + GICv5HandlingMode handling, GICv5Domain domain, + GICv5IntType type, bool virtual); + +/** + * gicv5_set_target + * @cs: GIC IRS to send command to + * @id: interrupt ID + * @iaffid: new target PE's interrupt affinity + * @irm: interrupt routing mode (targeted vs 1-of-N) + * @domain: interrupt Domain to act on + * @type: interrupt type (LPI or SPI) + * @virtual: true if this is a virtual interrupt + * + * Set handling mode of an interrupt (edge/level); matches stream interface + * SetHandling command from CPUIF to IRS. There is no report back + * of success/failure to the CPUIF in the protocol. + */ +void gicv5_set_target(GICv5Common *cs, uint32_t id, uint32_t iaffid, + GICv5RoutingMode irm, GICv5Domain domain, + GICv5IntType type, bool virtual); #endif diff --git a/include/hw/intc/arm_gicv5_types.h b/include/hw/intc/arm_gicv5_types.h index b4452a7b7d..15d4d5c3f4 100644 --- a/include/hw/intc/arm_gicv5_types.h +++ b/include/hw/intc/arm_gicv5_types.h @@ -55,4 +55,19 @@ typedef enum GICv5IntType { GICV5_SPI = 3, } GICv5IntType; +/* Interrupt handling mode (same encoding as L2_ISTE.HM) */ +typedef enum GICv5HandlingMode { + GICV5_EDGE = 0, + GICV5_LEVEL = 1, +} GICv5HandlingMode; + +/* + * Interrupt routing mode (same encoding as L2_ISTE.IRM). + * Note that 1-of-N support is option and QEMU does not implement it. + */ +typedef enum GICv5RoutingMode { + GICV5_TARGETED = 0, + GICV5_1OFN = 1, +} GICv5RoutingMode; + #endif -- 2.43.0
