diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 8aed57e704..880bc6f647 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -61,6 +61,12 @@ typedef struct ITEntry { uint32_t vpeid; } ITEntry; +typedef struct VTEntry { + bool valid; + unsigned vptsize; + uint32_t rdbase; + uint64_t vptaddr; +} VTEntry; /* * The ITS spec permits a range of CONSTRAINED UNPREDICTABLE options @@ -842,6 +848,85 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt) return update_ite(s, eventid, &dte, &old_ite) ? CMD_CONTINUE : CMD_STALL; } +/* + * Update the vPE Table entry at index @vpeid with the entry @vte. + * Returns true on success, false if there was a memory access error. + */ +static bool update_vte(GICv3ITSState *s, uint32_t vpeid, const VTEntry *vte) +{ + AddressSpace *as = &s->gicv3->dma_as; + uint64_t entry_addr; + uint64_t vteval = 0; + MemTxResult res = MEMTX_OK; + + trace_gicv3_its_vte_write(vpeid, vte->valid, vte->vptsize, vte->vptaddr, + vte->rdbase); + + if (vte->valid) { + vteval = FIELD_DP64(vteval, VTE, VALID, 1); + vteval = FIELD_DP64(vteval, VTE, VPTSIZE, vte->vptsize); + vteval = FIELD_DP64(vteval, VTE, VPTADDR, vte->vptaddr); + vteval = FIELD_DP64(vteval, VTE, RDBASE, vte->rdbase); + } + + entry_addr = table_entry_addr(s, &s->vpet, vpeid, &res); + if (res != MEMTX_OK) { + return false; + } + if (entry_addr == -1) { + /* No L2 table for this index: discard write and continue */ + return true; + } + address_space_stq_le(as, entry_addr, vteval, MEMTXATTRS_UNSPECIFIED, &res); + return res == MEMTX_OK; +} + +static ItsCmdResult process_vmapp(GICv3ITSState *s, const uint64_t *cmdpkt) +{ + VTEntry vte; + uint32_t vpeid; + + if (!its_feature_virtual(s)) { + return CMD_CONTINUE; + } + + vpeid = FIELD_EX64(cmdpkt[1], VMAPP_1, VPEID); + vte.rdbase = FIELD_EX64(cmdpkt[2], VMAPP_2, RDBASE); + vte.valid = FIELD_EX64(cmdpkt[2], VMAPP_2, V); + vte.vptsize = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTSIZE); + vte.vptaddr = FIELD_EX64(cmdpkt[3], VMAPP_3, VPTADDR); + + trace_gicv3_its_cmd_vmapp(vpeid, vte.rdbase, vte.valid, + vte.vptaddr, vte.vptsize); + + /* + * For GICv4.0 the VPT_size field is only 5 bits, whereas we + * define our field macros to include the full GICv4.1 8 bits. + * The range check on VPT_size will catch the cases where + * the guest set the RES0-in-GICv4.0 bits [7:6]. + */ + if (vte.vptsize > FIELD_EX64(s->typer, GITS_TYPER, IDBITS)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid VPT_size 0x%x\n", __func__, vte.vptsize); + return CMD_CONTINUE; + } + + if (vte.valid && vte.rdbase >= s->gicv3->num_cpu) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid rdbase 0x%x\n", __func__, vte.rdbase); + return CMD_CONTINUE; + } + + if (vpeid >= s->vpet.num_entries) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: VPEID 0x%x out of range (must be less than 0x%x)\n", + __func__, vpeid, s->vpet.num_entries); + return CMD_CONTINUE; + } + + return update_vte(s, vpeid, &vte) ? CMD_CONTINUE : CMD_STALL; +} + /* * Current implementation blocks until all * commands are processed @@ -963,6 +1048,9 @@ static void process_cmdq(GICv3ITSState *s) case GITS_CMD_VMAPI: result = process_vmapti(s, cmdpkt, true); break; + case GITS_CMD_VMAPP: + result = process_vmapp(s, cmdpkt); + break; default: trace_gicv3_its_cmd_unknown(cmd); break; diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h index d3670a8894..bbb8a20ce6 100644 --- a/hw/intc/gicv3_internal.h +++ b/hw/intc/gicv3_internal.h @@ -329,6 +329,7 @@ FIELD(GITS_TYPER, CIL, 36, 1) #define GITS_CMD_INVALL 0x0D #define GITS_CMD_MOVALL 0x0E #define GITS_CMD_DISCARD 0x0F +#define GITS_CMD_VMAPP 0x29 #define GITS_CMD_VMAPTI 0x2A #define GITS_CMD_VMAPI 0x2B @@ -377,6 +378,17 @@ FIELD(VMAPTI_1, VPEID, 32, 16) FIELD(VMAPTI_2, VINTID, 0, 32) /* VMAPTI only */ FIELD(VMAPTI_2, DOORBELL, 32, 32) +/* VMAPP command fields */ +FIELD(VMAPP_0, ALLOC, 8, 1) /* GICv4.1 only */ +FIELD(VMAPP_0, PTZ, 9, 1) /* GICv4.1 only */ +FIELD(VMAPP_0, VCONFADDR, 16, 36) /* GICv4.1 only */ +FIELD(VMAPP_1, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */ +FIELD(VMAPP_1, VPEID, 32, 16) +FIELD(VMAPP_2, RDBASE, 16, 36) +FIELD(VMAPP_2, V, 63, 1) +FIELD(VMAPP_3, VPTSIZE, 0, 8) /* For GICv4.0, bits [7:6] are RES0 */ +FIELD(VMAPP_3, VPTADDR, 16, 36) + /* * 12 bytes Interrupt translation Table Entry size * as per Table 5.3 in GICv3 spec diff --git a/hw/intc/trace-events b/hw/intc/trace-events index c6b2b9ab45..2fcc9e40e5 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -189,6 +189,7 @@ gicv3_its_cmd_movall(uint64_t rd1, uint64_t rd2) "GICv3 ITS: command MOVALL RDba gicv3_its_cmd_movi(uint32_t devid, uint32_t eventid, uint32_t icid) "GICv3 ITS: command MOVI DeviceID 0x%x EventID 0x%x ICID 0x%x" gicv3_its_cmd_vmapi(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x Dbell_pINTID 0x%x" gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t vintid, uint32_t doorbell) "GICv3 ITS: command VMAPI DeviceID 0x%x EventID 0x%x vPEID 0x%x vINTID 0x%x Dbell_pINTID 0x%x" +gicv3_its_cmd_vmapp(uint32_t vpeid, uint64_t rdbase, int valid, uint64_t vptaddr, uint32_t vptsize) "GICv3 ITS: command VMAPP vPEID 0x%x RDbase 0x%" PRIx64 " V %d VPT_addr 0x%" PRIx64 " VPT_size 0x%x" gicv3_its_cmd_unknown(unsigned cmd) "GICv3 ITS: unknown command 0x%x" gicv3_its_cte_read(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table read for ICID 0x%x: valid %d RDBase 0x%x" gicv3_its_cte_write(uint32_t icid, int valid, uint32_t rdbase) "GICv3 ITS: Collection Table write for ICID 0x%x: valid %d RDBase 0x%x" @@ -199,6 +200,7 @@ gicv3_its_ite_write(uint64_t ittaddr, uint32_t eventid, int valid, int inttype, gicv3_its_dte_read(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table read for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64 gicv3_its_dte_write(uint32_t devid, int valid, uint32_t size, uint64_t ittaddr) "GICv3 ITS: Device Table write for DeviceID 0x%x: valid %d size 0x%x ITTaddr 0x%" PRIx64 gicv3_its_dte_read_fault(uint32_t devid) "GICv3 ITS: Device Table read for DeviceID 0x%x: faulted" +gicv3_its_vte_write(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table write for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x" # armv7m_nvic.c nvic_recompute_state(int vectpending, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d exception_prio %d"