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 <[email protected]> --- 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 d0ba8fe669..0f32bdf357 100644 --- a/hw/intc/arm_gicv5.c +++ b/hw/intc/arm_gicv5.c @@ -930,6 +930,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) { @@ -1051,6 +1066,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; @@ -1139,6 +1172,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 b358691105..bcbe88cde7 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 00b1dc2b45..5254e68fbb 100644 --- a/include/hw/intc/arm_gicv5_common.h +++ b/include/hw/intc/arm_gicv5_common.h @@ -87,6 +87,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. -- 2.43.0
