aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--hw/intc/arm_gicv3_its.c88
-rw-r--r--hw/intc/gicv3_internal.h12
-rw-r--r--hw/intc/trace-events2
3 files changed, 102 insertions, 0 deletions
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
@@ -843,6 +849,85 @@ static ItsCmdResult process_movi(GICv3ITSState *s, const uint64_t *cmdpkt)
}
/*
+ * 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"