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 <peter.maydell@linaro.org>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
hw/intc/arm_gicv5.c | 152 +++++++++++++++++++++++++++++
hw/intc/trace-events | 4 +
include/hw/intc/arm_gicv5_stream.h | 68 +++++++++++++
include/hw/intc/arm_gicv5_types.h | 15 +++
4 files changed, 239 insertions(+)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index 7d654a91e6..d1eb96fce0 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -497,6 +497,158 @@ void gicv5_set_priority(GICv5Common *cs, uint32_t id, uint8_t priority,
}
}
+void gicv5_set_enabled(GICv5Common *cs, uint32_t id, bool enabled,
+ GICv5Domain domain, GICv5IntType type, bool virtual)
+{
+ GICv5 *s = ARM_GICV5(cs);
+
+ 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;
+ }
+
+ switch (type) {
+ case GICV5_LPI:
+ {
+ const GICv5ISTConfig *cfg = &s->phys_lpi_config[domain];
+ L2_ISTE_Handle h;
+ uint32_t *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);
+ break;
+ }
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_enabled: tried to set "
+ "enable state of bad interrupt type %d\n", type);
+ return;
+ }
+}
+
+void gicv5_set_pending(GICv5Common *cs, uint32_t id, bool pending,
+ GICv5Domain domain, GICv5IntType type, bool virtual)
+{
+ GICv5 *s = ARM_GICV5(cs);
+
+ 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;
+ }
+
+ switch (type) {
+ case GICV5_LPI:
+ {
+ const GICv5ISTConfig *cfg = &s->phys_lpi_config[domain];
+ L2_ISTE_Handle h;
+ uint32_t *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);
+ break;
+ }
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_pending: tried to set "
+ "pending state of bad interrupt type %d\n", type);
+ return;
+ }
+}
+
+void gicv5_set_handling(GICv5Common *cs, uint32_t id,
+ GICv5HandlingMode handling, GICv5Domain domain,
+ GICv5IntType type, bool virtual)
+{
+ GICv5 *s = ARM_GICV5(cs);
+
+ 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;
+ }
+
+ switch (type) {
+ case GICV5_LPI:
+ {
+ const GICv5ISTConfig *cfg = &s->phys_lpi_config[domain];
+ L2_ISTE_Handle h;
+ uint32_t *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);
+ break;
+ }
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_handling: tried to set "
+ "handling mode of bad interrupt type %d\n", type);
+ return;
+ }
+}
+
+void gicv5_set_target(GICv5Common *cs, uint32_t id, uint32_t iaffid,
+ GICv5RoutingMode irm, GICv5Domain domain,
+ GICv5IntType type, bool virtual)
+{
+ GICv5 *s = ARM_GICV5(cs);
+
+ 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.
+ */
+ }
+
+ switch (type) {
+ case GICV5_LPI:
+ {
+ const GICv5ISTConfig *cfg = &s->phys_lpi_config[domain];
+ L2_ISTE_Handle h;
+ uint32_t *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);
+ break;
+ }
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR, "gicv5_set_target: tried to set "
+ "target of bad interrupt type %d\n", type);
+ return;
+ }
+}
+
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 e1649cbb40..af2e1851c2 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 e2b937fe62..20de5b3f46 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