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


Reply via email to