The IRS_VMAP_VPER allows software to mark a VPE as valid if
it was not originally valid when the VM was created. For us
this means updating the valid bit in our cached VPE struct
and in the VPE table in guest memory.

Signed-off-by: Peter Maydell <[email protected]>
---
 hw/intc/arm_gicv5.c                | 94 ++++++++++++++++++++++++++++++
 hw/intc/arm_gicv5_common.c         |  1 +
 hw/intc/trace-events               |  1 +
 include/hw/intc/arm_gicv5_common.h |  1 +
 4 files changed, 97 insertions(+)

diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c
index c3d824f045b..e762c058a98 100644
--- a/hw/intc/arm_gicv5.c
+++ b/hw/intc/arm_gicv5.c
@@ -1890,6 +1890,65 @@ static void irs_vmap_l2_vistr_write(GICv5 *s, 
GICv5Domain domain,
     map_l2_istr(cs, vist, intid);
 }
 
+static void irs_vmap_vper_write(GICv5 *s, GICv5Domain domain, uint64_t value)
+{
+    GICv5Common *cs = ARM_GICV5_COMMON(s);
+    GICv5VMTConfig *cfg = &s->vmt_config[domain];
+    bool m = FIELD_EX64(value, IRS_VMAP_VPER, M);
+    uint32_t vmid = FIELD_EX64(value, IRS_VMAP_VPER, VM_ID);
+    uint32_t vpeid = FIELD_EX64(value, IRS_VMAP_VPER, VPE_ID);
+    VMTE *vmte;
+    hwaddr vpete_addr;
+    MemTxResult res;
+    uint64_t vpete;
+
+    if (!gicv5_domain_has_virt(cs, domain) || !cfg->valid) {
+        /* Whole register RAZ/WI if no virt or no VM table */
+        return;
+    }
+
+    /* M bit is WO/UNKNOWN; write the other fields */
+    cs->irs_vmap_vper[domain] = value & ~R_IRS_VMAP_VPER_M_MASK;
+
+    /* M = 0 and bad VMID must be ignored */
+    if (!m || vmid >= (1 << cfg->vm_id_bits)) {
+        return;
+    }
+
+    vmte = g_hash_table_lookup(cfg->vm_cache, GINT_TO_POINTER(vmid));
+    if (!vmte) {
+        /* VM is not valid */
+        return;
+    }
+
+    if (vpeid >= (1 << vmte->vpe_id_bits) || vmte->vpet[vpeid].valid) {
+        /* Invalid VPE ID, VPE already valid */
+        return;
+    }
+
+    vpete_addr = vmte->vpet_addr + vpeid * 8;
+    vpete = address_space_ldq_le(&cs->dma_as, vpete_addr, cfg->txattrs, &res);
+    if (res != MEMTX_OK) {
+        goto txfail;
+    }
+    vpete = FIELD_DP64(vpete, VPETE, VALID, 1);
+    address_space_stq_le(&cs->dma_as, vpete_addr, vpete, cfg->txattrs, &res);
+    if (res != MEMTX_OK) {
+        goto txfail;
+    }
+
+    init_vpe_from_vpete(&vmte->vpet[vpeid], vmte, vpete);
+    trace_gicv5_irs_vmap_vper_write(domain_name[domain], vmte->vmid, vpeid);
+
+    return;
+
+txfail:
+    qemu_log_mask(LOG_GUEST_ERROR,
+                  "VPETE update failed for VMID 0x%x VPEID 0x%x at "
+                  "physical address 0x" HWADDR_FMT_plx "\n",
+                  vmid, vpeid, vpete_addr);
+}
+
 static void spi_sample(GICv5SPIState *spi)
 {
     /*
@@ -2093,6 +2152,22 @@ static bool config_readl(GICv5 *s, GICv5Domain domain, 
hwaddr offset,
             *data = 0;
         }
         return true;
+    case A_IRS_VMAP_VPER:
+        if (gicv5_domain_has_virt(cs, domain) && s->vmt_config[domain].valid) {
+            *data = extract64(cs->irs_vmap_vper[domain], 0, 32);
+        } else {
+            /* RAZ/WI if no virt or no VM table */
+            *data = 0;
+        }
+        return true;
+    case A_IRS_VMAP_VPER + 4:
+        if (gicv5_domain_has_virt(cs, domain) && s->vmt_config[domain].valid) {
+            *data = extract64(cs->irs_vmap_vper[domain], 32, 32);
+        } else {
+            /* RAZ/WI if no virt or no VM table */
+            *data = 0;
+        }
+        return true;
     case A_IRS_SPI_STATUSR:
         /*
          * QEMU writes to IRS_SPI_{CFGR,DOMAINR,SELR,VMR} take effect
@@ -2251,6 +2326,14 @@ static bool config_writel(GICv5 *s, GICv5Domain domain, 
hwaddr offset,
                                 deposit64(cs->irs_vmap_vistr[domain],
                                           32, 32, data));
         return true;
+    case A_IRS_VMAP_VPER:
+        irs_vmap_vper_write(s, domain,
+                            deposit64(cs->irs_vmap_vper[domain], 0, 32, data));
+        return true;
+    case A_IRS_VMAP_VPER + 4:
+        irs_vmap_vper_write(s, domain,
+                            deposit64(cs->irs_vmap_vper[domain], 32, 32, 
data));
+        return true;
     case A_IRS_SPI_SELR:
         cs->irs_spi_selr[domain] = data;
         return true;
@@ -2375,6 +2458,14 @@ static bool config_readll(GICv5 *s, GICv5Domain domain, 
hwaddr offset,
             *data = 0;
         }
         return true;
+    case A_IRS_VMAP_VPER:
+        if (gicv5_domain_has_virt(cs, domain) && s->vmt_config[domain].valid) {
+            *data = cs->irs_vmap_vper[domain];
+        } else {
+            /* RAZ/WI if no virt or no VM table */
+            *data = 0;
+        }
+        return true;
     }
 
     return false;
@@ -2402,6 +2493,9 @@ static bool config_writell(GICv5 *s, GICv5Domain domain, 
hwaddr offset,
     case A_IRS_VMAP_L2_VISTR:
         irs_vmap_l2_vistr_write(s, domain, data);
         return true;
+    case A_IRS_VMAP_VPER:
+        irs_vmap_vper_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 b52dba3d3b4..6e0e2295fde 100644
--- a/hw/intc/arm_gicv5_common.c
+++ b/hw/intc/arm_gicv5_common.c
@@ -112,6 +112,7 @@ static void gicv5_common_reset_hold(Object *obj, ResetType 
type)
         memset(cs->irs_vmap_vmr, 0, sizeof(cs->irs_vmap_vmr));
         memset(cs->irs_vmap_vistr, 0, sizeof(cs->irs_vmap_vistr));
         memset(cs->irs_vmap_l2_vistr, 0, sizeof(cs->irs_vmap_l2_vistr));
+        memset(cs->irs_vmap_vper, 0, sizeof(cs->irs_vmap_vper));
     }
 }
 
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 9057f83af57..79381c109b7 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -252,6 +252,7 @@ gicv5_vmap_l2_vmtr(const char *domain, uint32_t vmid) 
"GICv5 IRS %s VM ID %u now
 gicv5_cache_vmte(const char *domain, uint32_t vmid) "GICv5 IRS %s VM ID %u 
valid and cached"
 gicv5_irs_vmap_vistr_write(const char *domain, uint32_t vmid, const char 
*type, const char *what) "GICv5 IRS %s VM ID %u %s IST now %s"
 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"
 
 # 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 8103f14ba44..c8574f66a63 100644
--- a/include/hw/intc/arm_gicv5_common.h
+++ b/include/hw/intc/arm_gicv5_common.h
@@ -95,6 +95,7 @@ struct GICv5Common {
     uint64_t irs_vmap_vmr[NUM_GICV5_DOMAINS];
     uint64_t irs_vmap_vistr[NUM_GICV5_DOMAINS];
     uint64_t irs_vmap_l2_vistr[NUM_GICV5_DOMAINS];
+    uint64_t irs_vmap_vper[NUM_GICV5_DOMAINS];
 
     /*
      * Pointer to an array of state information for the SPIs.  Array
-- 
2.43.0


Reply via email to