The IRS_PE_CR0, IRS_PE_SELR, IRS_PE_STATUSR registers allow software
to set and query per-CPU config. Software writes the AFFID of a CPU
to IRS_PE_SELR, and can then read and write the 1ofN config for that
CPU to IRS_PE_CR0, and read the CPU's online status from
IRS_PE_STATUSR.
For QEMU, we do not implement 1-of-N interrupt routing, so IRS_PE_CR0
can be RAZ/WI. Our CPUs are always online and selecting a new one
via SELR is instantaneous, so IRS_PE_STATUSR will return either
ONLINE | V | IDLE if a valid AFFID was written to SELR, or just IDLE
if an invalid AFFID was written.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
---
hw/intc/arm_gicv5.c | 39 ++++++++++++++++++++++++++++++
hw/intc/arm_gicv5_common.c | 1 +
include/hw/intc/arm_gicv5_common.h | 1 +
3 files changed, 41 insertions(+)
diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index 7b0d9e16c4..a95a9dc16b 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -968,6 +968,21 @@ static void spi_sample(GICv5SPIState *spi)
}
}
+static bool irs_pe_selr_valid(GICv5Common *cs, GICv5Domain domain)
+{
+ /*
+ * Return true if IRS_PE_SELR has a valid AFFID in it. We don't
+ * expect the guest to do this except perhaps once at startup, so
+ * do a simple linear scan through the cpu_iaffids array.
+ */
+ for (int i = 0; i < cs->num_cpu_iaffids; i++) {
+ if (cs->irs_pe_selr[domain] == cs->cpu_iaffids[i]) {
+ return true;
+ }
+ }
+ return false;
+}
+
static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
uint64_t *data, MemTxAttrs attrs)
{
@@ -1091,6 +1106,24 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset,
/* Sync is a no-op for QEMU: we are always IDLE */
*data = R_IRS_SYNC_STATUSR_IDLE_MASK;
return true;
+ case A_IRS_PE_SELR:
+ *data = cs->irs_pe_selr[domain];
+ return true;
+ case A_IRS_PE_CR0:
+ /* We don't implement 1ofN, so this is RAZ/WI for us */
+ *data = 0;
+ return true;
+ case A_IRS_PE_STATUSR:
+ /*
+ * Our CPUs are always online, so we're really just reporting
+ * whether the guest wrote a valid AFFID to IRS_PE_SELR
+ */
+ v = R_IRS_PE_STATUSR_IDLE_MASK;
+ if (irs_pe_selr_valid(cs, domain)) {
+ v |= R_IRS_PE_STATUSR_V_MASK | R_IRS_PE_STATUSR_ONLINE_MASK;
+ }
+ *data = v;
+ return true;
}
return false;
@@ -1179,6 +1212,12 @@ static bool config_writel(GICv5 *s, GICv5Domain domain, hwaddr offset,
case A_IRS_SYNCR:
/* Sync is a no-op for QEMU: ignore write */
return true;
+ case A_IRS_PE_SELR:
+ cs->irs_pe_selr[domain] = data;
+ return true;
+ case A_IRS_PE_CR0:
+ /* We don't implement 1ofN, so this is RAZ/WI for us */
+ return true;
}
return false;
diff --git a/hw/intc/arm_gicv5_common.c b/hw/intc/arm_gicv5_common.c
index b1c8ec4521..5510e6239a 100644
--- a/hw/intc/arm_gicv5_common.c
+++ b/hw/intc/arm_gicv5_common.c
@@ -68,6 +68,7 @@ static void gicv5_common_reset_hold(Object *obj, ResetType type)
memset(cs->irs_ist_cfgr, 0, sizeof(cs->irs_ist_cfgr));
memset(cs->irs_cr0, 0, sizeof(cs->irs_cr0));
memset(cs->irs_cr1, 0, sizeof(cs->irs_cr1));
+ memset(cs->irs_pe_selr, 0, sizeof(cs->irs_pe_selr));
if (cs->spi) {
GICv5Domain mp_domain;
diff --git a/include/hw/intc/arm_gicv5_common.h b/include/hw/intc/arm_gicv5_common.h
index ac0532abe8..34ad38c198 100644
--- a/include/hw/intc/arm_gicv5_common.h
+++ b/include/hw/intc/arm_gicv5_common.h
@@ -86,6 +86,7 @@ struct GICv5Common {
uint32_t irs_spi_selr[NUM_GICV5_DOMAINS];
uint32_t irs_cr0[NUM_GICV5_DOMAINS];
uint32_t irs_cr1[NUM_GICV5_DOMAINS];
+ uint32_t irs_pe_selr[NUM_GICV5_DOMAINS];
/*
* Pointer to an array of state information for the SPIs. Array
--
2.43.0