Each VM has a VPE table which is a single linear table with an entry for each possible vCPU in the VM. Each VPE has a 'valid' bit and a pointer to a VPE descriptor which has IMPDEF contents. When the VM is made valid, the table can contain a mix of already-valid and not-yet-valid VPEs.
Cache this information. Since the GICv5 architecture requires a simple linear table here, we can assume that the guest is unlikely to populate it sparsely, so we use a simple array rather than a hashtable. We will gradually expand the information we put into our VPE struct, but for now we just have a boolean tracking whether the VPE is valid or not, the guest address of the VPED, and a back-pointer to the VMTE. Signed-off-by: Peter Maydell <[email protected]> --- hw/intc/arm_gicv5.c | 62 ++++++++++++++++++++++++++++++-- hw/intc/trace-events | 1 + include/hw/intc/arm_gicv5_regs.h | 3 ++ 3 files changed, 64 insertions(+), 2 deletions(-) diff --git a/hw/intc/arm_gicv5.c b/hw/intc/arm_gicv5.c index 4f74236c62b..c3d824f045b 100644 --- a/hw/intc/arm_gicv5.c +++ b/hw/intc/arm_gicv5.c @@ -23,10 +23,20 @@ OBJECT_DEFINE_TYPE(GICv5, gicv5, ARM_GICV5, ARM_GICV5_COMMON) +typedef struct VMTE VMTE; + +/* Cached per-VPE information, from VPE and VPE descriptor */ +typedef struct VPE { + VMTE *vmte; + hwaddr vped_addr; + bool valid; +} VPE; + /* In the GICv5VMTConfig::vm_cache this is how we store VMTE info */ -typedef struct VMTE { +struct VMTE { GICv5ISTConfig lpi_ist; GICv5ISTConfig spi_ist; + VPE *vpet; /* Address of the in-memory L2_VMTE for this VMTE */ hwaddr vmte_addr; hwaddr vpet_addr; @@ -35,7 +45,7 @@ typedef struct VMTE { GICv5Domain domain; /* We don't cache the VMD_ADDR as we don't use VMDs */ /* We don't cache VALID: the existence of this struct implies VALID=1 */ -} VMTE; +}; static const char *domain_name[] = { [GICV5_ID_S] = "Secure", @@ -101,6 +111,8 @@ static void VMTE_free(void *data) g_hash_table_destroy(vmte->spi_ist.lpi_cache); } + g_free(vmte->vpet); + g_free(vmte); } @@ -1219,6 +1231,51 @@ static void initialize_vspi_ist_config(GICv5Common *cs, GICv5Domain domain, id_bits, istsz, l2sz, structure); } +static void init_vpe_from_vpete(VPE *vpe, VMTE *vmte, uint64_t vpete) +{ + /* Given the value of the in-memory VPETE, set up one VPE struct */ + vpe->vmte = vmte; + vpe->valid = FIELD_EX64(vpete, VPETE, VALID); + if (vpe->valid) { + vpe->vped_addr = vpete & R_VPETE_VPED_ADDR_MASK; + vpe->vped_addr &= ~MAKE_64BIT_MASK(0, QEMU_GICV5_VPED_SZ); + } +} + +static void initialize_vpet(GICv5Common *cs, GICv5Domain domain, + GICv5VMTConfig *cfg, VMTE *vmte) +{ + /* + * Allocate our cached VPE table, and populate it from guest memory. + * Note that (per R_DLKTX) the VPE descriptor is supposed to be all + * zeroes at this point, and behaviour is UNPREDICTABLE if it is not, + * so we do not need to read the descriptor here. In any case QEMU + * doesn't use the VPE descriptors. + */ + int num_vpes = 1 << vmte->vpe_id_bits; + hwaddr vpete_addr = vmte->vpet_addr; + + vmte->vpet = g_new0(VPE, num_vpes); + + for (int i = 0; i < num_vpes; i++, vpete_addr += 8) { + MemTxResult res; + uint64_t vpete; + + vpete = address_space_ldq_le(&cs->dma_as, vpete_addr, + cfg->txattrs, &res); + if (res != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "VPET lookup failed for VPE 0x%x" + " at physical address 0x" HWADDR_FMT_plx "\n", + i, vpete_addr); + return; + } + + init_vpe_from_vpete(&vmte->vpet[i], vmte, vpete); + trace_gicv5_initialize_vpet(domain_name[vmte->domain], vmte->vmid, + i, vmte->vpet[i].valid); + } +} + static void cache_vmte(GICv5Common *cs, GICv5Domain domain, GICv5VMTConfig *cfg, uint32_t vmid, uint64_t *l2_vmte, hwaddr l2_addr) @@ -1243,6 +1300,7 @@ static void cache_vmte(GICv5Common *cs, GICv5Domain domain, * it has 2^vpe_id_bits entries of 2^3 == 8 bytes each */ vmte->vpet_addr &= ~MAKE_64BIT_MASK(0, vmte->vpe_id_bits + 3); + initialize_vpet(cs, domain, cfg, vmte); /* * If the VALID bits are set for the LPI and SPI ISTs, we can read diff --git a/hw/intc/trace-events b/hw/intc/trace-events index a086b14c825..9057f83af57 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -251,6 +251,7 @@ gicv5_vmt_invalid(const char *domain) "GICv5 IRS %s VM table no longer valid" gicv5_vmap_l2_vmtr(const char *domain, uint32_t vmid) "GICv5 IRS %s VM ID %u now valid" 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" # 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_regs.h b/include/hw/intc/arm_gicv5_regs.h index f1ebbb6aaf8..f4243abd182 100644 --- a/include/hw/intc/arm_gicv5_regs.h +++ b/include/hw/intc/arm_gicv5_regs.h @@ -306,6 +306,9 @@ FIELD(L2_VMTE_3, SPI_ID_BITS, 59, 5) #define VMT_L2_IDX_BITS 7 #define VMT_L2_TABLE_SIZE 4096 +FIELD(VPETE, VALID, 0, 1) +FIELD(VPETE, VPED_ADDR, 3, 53) + /* * Format used for gicv5_request_config() return value, which matches * the ICC_ICSR_EL1 bit layout. -- 2.43.0
