Implement the GICv5 IRS_VMT_BASER register, which holds the base address of the VM table. This is very similar to the IRS_IST_BASER register: it is 64 bits wide but can be accessed as two 32 bit pieces, it has a valid bit which the guest sets to make the table valid, and there is an associated IRS_VMT_STATUSR register which has an IDLE bit which the guest can read to find out when the write to VALID has completed.
Implement IRS_VMT_BASER and IRS_VMT_STATUSR, using the same pattern we have for IRS_IST_BASER and IRS_IST_STATUSR. Signed-off-by: Peter Maydell <[email protected]> --- hw/intc/arm_gicv5.c | 73 ++++++++++++++++++++++++++++++ hw/intc/arm_gicv5_common.c | 4 ++ hw/intc/trace-events | 2 + include/hw/intc/arm_gicv5_common.h | 1 + 4 files changed, 80 insertions(+) diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c index 63e4a165eea..05c6b9c57fc 100644 --- a/hw/intc/arm_gicv5.c +++ b/hw/intc/arm_gicv5.c @@ -1166,6 +1166,36 @@ static void irs_ist_baser_write(GICv5 *s, GICv5Domain domain, uint64_t value) } } +static void irs_vmt_baser_write(GICv5 *s, GICv5Domain domain, uint64_t value) +{ + GICv5Common *cs = ARM_GICV5_COMMON(s); + + if (!gicv5_domain_has_virt(cs, domain)) { + return; + } + + if (FIELD_EX64(cs->irs_vmt_baser[domain], IRS_VMT_BASER, VALID)) { + /* If VALID is set, ADDR is RO and we can only update VALID */ + bool valid = FIELD_EX64(value, IRS_VMT_BASER, VALID); + + if (valid) { + /* Ignore 1->1 transition */ + return; + } + cs->irs_vmt_baser[domain] = FIELD_DP64(cs->irs_vmt_baser[domain], + IRS_VMT_BASER, VALID, valid); + trace_gicv5_vmt_invalid(domain_name[domain]); + return; + } + + cs->irs_vmt_baser[domain] = value; + + if (FIELD_EX64(cs->irs_vmt_baser[domain], IRS_VMT_BASER, VALID)) { + trace_gicv5_vmt_valid(domain_name[domain], + cs->irs_vmt_baser[domain] & R_IRS_VMT_BASER_ADDR_MASK); + } +} + static void spi_sample(GICv5SPIState *spi) { /* @@ -1271,6 +1301,31 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset, case A_IRS_IST_CFGR: *data = cs->irs_ist_cfgr[domain]; return true; + case A_IRS_VMT_BASER: + if (gicv5_domain_has_virt(cs, domain)) { + *data = extract64(cs->irs_vmt_baser[domain], 0, 32); + } else { + *data = 0; + } + return true; + case A_IRS_VMT_BASER + 4: + if (gicv5_domain_has_virt(cs, domain)) { + *data = extract64(cs->irs_vmt_baser[domain], 32, 32); + } else { + *data = 0; + } + return true; + case A_IRS_VMT_STATUSR: + /* + * QEMU writes to IRS_VMT_BASER and IRS_VMAP_* take effect + * instantaneously, so the guest can never see the IDLE bit as 0. + */ + if (gicv5_domain_has_virt(cs, domain)) { + *data = R_IRS_VMT_STATUSR_IDLE_MASK; + } else { + *data = 0; + } + return true; case A_IRS_SPI_STATUSR: /* * QEMU writes to IRS_SPI_{CFGR,DOMAINR,SELR,VMR} take effect @@ -1373,6 +1428,14 @@ static bool config_writel(GICv5 *s, GICv5Domain domain, hwaddr offset, case A_IRS_MAP_L2_ISTR: irs_map_l2_istr_write(s, domain, data); return true; + case A_IRS_VMT_BASER: + irs_vmt_baser_write(s, domain, + deposit64(cs->irs_vmt_baser[domain], 0, 32, data)); + return true; + case A_IRS_VMT_BASER + 4: + irs_vmt_baser_write(s, domain, + deposit64(cs->irs_vmt_baser[domain], 32, 32, data)); + return true; case A_IRS_SPI_SELR: cs->irs_spi_selr[domain] = data; return true; @@ -1457,6 +1520,13 @@ static bool config_readll(GICv5 *s, GICv5Domain domain, hwaddr offset, case A_IRS_IST_BASER: *data = cs->irs_ist_baser[domain]; return true; + case A_IRS_VMT_BASER: + if (gicv5_domain_has_virt(cs, domain)) { + *data = cs->irs_vmt_baser[domain]; + } else { + *data = 0; + } + return true; } return false; @@ -1469,6 +1539,9 @@ static bool config_writell(GICv5 *s, GICv5Domain domain, hwaddr offset, case A_IRS_IST_BASER: irs_ist_baser_write(s, domain, data); return true; + case A_IRS_VMT_BASER: + irs_vmt_baser_write(s, domain, data); + return true; } return false; diff --git a/hw/intc/arm_gicv5_common.c b/hw/intc/arm_gicv5_common.c index 17fd67b5ccd..89669493b83 100644 --- a/hw/intc/arm_gicv5_common.c +++ b/hw/intc/arm_gicv5_common.c @@ -104,6 +104,10 @@ static void gicv5_common_reset_hold(Object *obj, ResetType type) */ cs->irs_spi_selr[i] = cs->spi_base + cs->spi_irs_range; } + + if (cs->has_virt) { + memset(cs->irs_vmt_baser, 0, sizeof(cs->irs_vmt_baser)); + } } static void gicv5_common_init(Object *obj) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 6681e32d1b7..3c7619cf3cd 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -246,6 +246,8 @@ gicv5_deactivate(const char *domain, const char *type, bool virtual, uint32_t id gicv5_spi_state(uint32_t spi_id, bool level, bool pending, bool active) "GICv5 IRS SPI ID %u now level %d pending %d active %d" gicv5_irs_recalc_hppi_fail(const char *domain, uint32_t iaffid, const char *reason) "GICv5 IRS %s IAFFID %u: no HPPI: %s" gicv5_irs_recalc_hppi(const char *domain, uint32_t iaffid, uint32_t id, uint8_t prio) "GICv5 IRS %s IAFFID %u: new HPPI ID 0x%x prio %u" +gicv5_vmt_valid(const char *domain, uint64_t base) "GICv5 IRS %s VM table now valid: base 0x%" PRIx64 +gicv5_vmt_invalid(const char *domain) "GICv5 IRS %s VM table no longer valid" # 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_common.h b/include/hw/intc/arm_gicv5_common.h index 7b24cad211b..f134602d66e 100644 --- a/include/hw/intc/arm_gicv5_common.h +++ b/include/hw/intc/arm_gicv5_common.h @@ -89,6 +89,7 @@ struct GICv5Common { uint32_t irs_cr0[NUM_GICV5_DOMAINS]; uint32_t irs_cr1[NUM_GICV5_DOMAINS]; uint32_t irs_pe_selr[NUM_GICV5_DOMAINS]; + uint64_t irs_vmt_baser[NUM_GICV5_DOMAINS]; /* * Pointer to an array of state information for the SPIs. Array -- 2.43.0
