The IRS_VPE_DBR register provides access to the doorbell config of the VPE selected with IRS_VPE_SELR. The config is sampled into the register when SELR is successfully written. Writes to IRS_VPE_DBR will update the config if the VPE selected by IRS_VPE_SELR is still valid; if the VPE became invalid between the SELR write and the CR0/DBR write then IRS_VPE_STATUSR.V is updated to report this.
Signed-off-by: Peter Maydell <[email protected]> --- hw/intc/arm_gicv5.c | 105 ++++++++++++++++++++++++++++- hw/intc/arm_gicv5_common.c | 1 + hw/intc/trace-events | 2 + include/hw/intc/arm_gicv5_common.h | 1 + 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c index 455242596bc..f7d2d4a7143 100644 --- a/hw/intc/arm_gicv5.c +++ b/hw/intc/arm_gicv5.c @@ -29,7 +29,11 @@ typedef struct VMTE VMTE; typedef struct VPE { VMTE *vmte; hwaddr vped_addr; - bool valid; + uint32_t db_intid; /* doorbell interrupt ID */ + uint8_t dbpm; /* doorbell priority mask */ + bool valid; /* is the VPE valid? */ + bool db_valid; /* are doorbell settings valid? */ + bool db_req; /* is a doorbell requested? */ } VPE; /* In the GICv5VMTConfig::vm_cache this is how we store VMTE info */ @@ -1257,6 +1261,8 @@ static void init_vpe_from_vpete(VPE *vpe, VMTE *vmte, uint64_t vpete) if (vpe->valid) { vpe->vped_addr = vpete & R_VPETE_VPED_ADDR_MASK; vpe->vped_addr &= ~MAKE_64BIT_MASK(0, QEMU_GICV5_VPED_SZ); + /* Doorbell settings always start invalid */ + vpe->db_valid = false; } } @@ -2008,7 +2014,20 @@ static void irs_vpe_selr_write(GICv5 *s, GICv5Domain domain, uint64_t value) vpe = vpe_from_selr(s, domain); if (vpe) { - /* We successfully selected a valid VPE */ + /* + * We successfully selected a valid VPE. Sample the config + * into IRS_VPE_DBR. + */ + uint64_t dbr = 0; + + if (vpe->db_valid) { + dbr = FIELD_DP64(dbr, IRS_VPE_DBR, DBV, vpe->db_valid); + dbr = FIELD_DP64(dbr, IRS_VPE_DBR, DBPM, vpe->dbpm); + dbr = FIELD_DP64(dbr, IRS_VPE_DBR, REQ_DB, vpe->db_req); + dbr = FIELD_DP64(dbr, IRS_VPE_DBR, INTID, vpe->db_intid); + } + cs->irs_vpe_dbr[domain] = dbr; + status |= R_IRS_VPE_STATUSR_V_MASK; } @@ -2017,6 +2036,53 @@ static void irs_vpe_selr_write(GICv5 *s, GICv5Domain domain, uint64_t value) cs->irs_vpe_statusr[domain] = status; } +static void irs_vpe_dbr_write(GICv5 *s, GICv5Domain domain, uint64_t value) +{ + GICv5Common *cs = ARM_GICV5_COMMON(s); + VPE *vpe; + + /* RES0 if we don't have virt */ + if (!gicv5_domain_has_virt(cs, domain)) { + return; + } + + cs->irs_vpe_dbr[domain] = value; + + if (!FIELD_EX64(cs->irs_vpe_statusr[domain], IRS_VPE_STATUSR, V)) { + /* No valid VPE selected via SELR */ + return; + } + + vpe = vpe_from_selr(s, domain); + if (!vpe) { + /* VPE became invalid since SELR was last written */ + cs->irs_vpe_statusr[domain] &= ~R_IRS_VPE_STATUSR_V_MASK; + return; + } + + vpe->db_valid = FIELD_EX64(value, IRS_VPE_DBR, DBV); + if (vpe->db_valid) { + vpe->dbpm = FIELD_EX64(value, IRS_VPE_DBR, DBPM); + vpe->db_intid = FIELD_EX64(value, IRS_VPE_DBR, INTID); + vpe->db_req = FIELD_EX64(value, IRS_VPE_DBR, REQ_DB); + + trace_gicv5_irs_vpe_dbr_write(domain_name[domain], vpe->vmte->vmid, + FIELD_EX64(cs->irs_vpe_selr[domain], + IRS_VPE_SELR, VPE_ID), + vpe->dbpm, vpe->db_intid, vpe->db_req); + + /* + * TODO: when we implement VPE doorbells, check here whether + * this config change means we should now generate a VPE + * doorbell event for this VPE. + */ + } else { + trace_gicv5_irs_vpe_dbr_write_nv(domain_name[domain], vpe->vmte->vmid, + FIELD_EX64(cs->irs_vpe_selr[domain], + IRS_VPE_SELR, VPE_ID)); + } +} + static void spi_sample(GICv5SPIState *spi) { /* @@ -2252,6 +2318,22 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, hwaddr offset, *data = 0; } return true; + case A_IRS_VPE_DBR: + if (gicv5_domain_has_virt(cs, domain)) { + *data = extract64(cs->irs_vpe_dbr[domain], 0, 32); + } else { + /* RAZ/WI if no virt */ + *data = 0; + } + return true; + case A_IRS_VPE_DBR + 4: + if (gicv5_domain_has_virt(cs, domain)) { + *data = extract64(cs->irs_vpe_dbr[domain], 32, 32); + } else { + /* RAZ/WI if no virt */ + *data = 0; + } + return true; case A_IRS_VPE_STATUSR: if (gicv5_domain_has_virt(cs, domain)) { *data = cs->irs_vpe_statusr[domain]; @@ -2434,6 +2516,14 @@ static bool config_writel(GICv5 *s, GICv5Domain domain, hwaddr offset, irs_vpe_selr_write(s, domain, deposit64(cs->irs_vpe_selr[domain], 32, 32, data)); return true; + case A_IRS_VPE_DBR: + irs_vpe_dbr_write(s, domain, + deposit64(cs->irs_vpe_dbr[domain], 0, 32, data)); + return true; + case A_IRS_VPE_DBR + 4: + irs_vpe_dbr_write(s, domain, + deposit64(cs->irs_vpe_dbr[domain], 32, 32, data)); + return true; case A_IRS_SPI_SELR: cs->irs_spi_selr[domain] = data; return true; @@ -2574,6 +2664,14 @@ static bool config_readll(GICv5 *s, GICv5Domain domain, hwaddr offset, *data = 0; } return true; + case A_IRS_VPE_DBR: + if (gicv5_domain_has_virt(cs, domain)) { + *data = cs->irs_vpe_dbr[domain]; + } else { + /* RAZ/WI if no virt */ + *data = 0; + } + return true; } return false; @@ -2607,6 +2705,9 @@ static bool config_writell(GICv5 *s, GICv5Domain domain, hwaddr offset, case A_IRS_VPE_SELR: irs_vpe_selr_write(s, domain, data); return true; + case A_IRS_VPE_DBR: + irs_vpe_dbr_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 553598e3178..50365c4dfe6 100644 --- a/hw/intc/arm_gicv5_common.c +++ b/hw/intc/arm_gicv5_common.c @@ -115,6 +115,7 @@ static void gicv5_common_reset_hold(Object *obj, ResetType type) memset(cs->irs_vmap_l2_vistr, 0, sizeof(cs->irs_vmap_l2_vistr)); memset(cs->irs_vmap_vper, 0, sizeof(cs->irs_vmap_vper)); memset(cs->irs_vpe_selr, 0, sizeof(cs->irs_vpe_selr)); + memset(cs->irs_vpe_dbr, 0, sizeof(cs->irs_vpe_dbr)); for (int i = 0; i < NUM_GICV5_DOMAINS; i++) { cs->irs_vpe_statusr[i] = R_IRS_VPE_STATUSR_IDLE_MASK; diff --git a/hw/intc/trace-events b/hw/intc/trace-events index edfbf7d7157..b187ceefe45 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -254,6 +254,8 @@ gicv5_irs_vmap_vistr_write(const char *domain, uint32_t vmid, const char *type, gicv5_initialize_vpet(const char *domain, uint32_t vmid, uint32_t vpeid, bool valid) "GICv5 IRS %s VM ID %u VPE %u starts as valid %d" gicv5_irs_vmap_vper_write(const char *domain, uint32_t vmid, uint32_t vpeid) "GICv5 IRS %s VM ID %u VPE %u now made valid" gicv5_irs_vpe_selr_write(const char *domain, uint64_t selr, uint32_t status) "GICv5 IRS %s IRS_VPE_SELR written 0x%" PRIx64 " IRS_VPE_STATUSR now 0x%x" +gicv5_irs_vpe_dbr_write(const char *domain, uint32_t vmid, uint32_t vpeid, uint8_t dbpm, uint32_t db_intid, bool db_req) "GICv5 IRS %s VM ID %u VPE %u IRS_VPE_DBR doorbell now valid: prio mask %u intid %u requested %d" +gicv5_irs_vpe_dbr_write_nv(const char *domain, uint32_t vmid, uint32_t vpeid) "GICv5 IRS %s VM ID %u VPE %u IRS_VPE_DBR doorbell info 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 5823a7f78a0..d82cc23d10c 100644 --- a/include/hw/intc/arm_gicv5_common.h +++ b/include/hw/intc/arm_gicv5_common.h @@ -98,6 +98,7 @@ struct GICv5Common { uint64_t irs_vmap_vper[NUM_GICV5_DOMAINS]; uint64_t irs_vpe_selr[NUM_GICV5_DOMAINS]; uint32_t irs_vpe_statusr[NUM_GICV5_DOMAINS]; + uint64_t irs_vpe_dbr[NUM_GICV5_DOMAINS]; /* * Pointer to an array of state information for the SPIs. Array -- 2.43.0
