@@ -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_VMOVI 0x21
#define GITS_CMD_VMOVP 0x22
#define GITS_CMD_VSYNC 0x25
#define GITS_CMD_VMAPP 0x29
@@ -403,6 +404,13 @@ FIELD(VMOVP_2, RDBASE, 16, 36)
FIELD(VMOVP_2, DB, 63, 1) /* GICv4.1 only */
FIELD(VMOVP_3, DEFAULT_DOORBELL, 0, 32) /* GICv4.1 only */
+/* VMOVI command fields */
+FIELD(VMOVI_0, DEVICEID, 32, 32)
+FIELD(VMOVI_1, EVENTID, 0, 32)
+FIELD(VMOVI_1, VPEID, 32, 16)
+FIELD(VMOVI_2, D, 0, 1)
+FIELD(VMOVI_2, DOORBELL, 32, 32)
+
/*
* 12 bytes Interrupt translation Table Entry size
* as per Table 5.3 in GICv3 spec
@@ -614,6 +622,21 @@ void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq);
* by the ITS MOVALL command.
*/
void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest);
+/**
+ * gicv3_redist_mov_vlpi:
+ * @src: source redistributor
+ * @src_vptaddr: (guest) address of source VLPI table
+ * @dest: destination redistributor
+ * @dest_vptaddr: (guest) address of destination VLPI table
+ * @irq: VLPI to update
+ * @doorbell: doorbell for destination (1023 for "no doorbell")
+ *
+ * Move the pending state of the specified VLPI from @src to @dest,
+ * as required by the ITS VMOVI command.
+ */
+void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
+ GICv3CPUState *dest, uint64_t dest_vptaddr,
+ int irq, int doorbell);
void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
void gicv3_init_cpuif(GICv3State *s);
@@ -1084,6 +1084,85 @@ static ItsCmdResult process_vmovp(GICv3ITSState *s, const uint64_t *cmdpkt)
return cbdata.result;
}
+static ItsCmdResult process_vmovi(GICv3ITSState *s, const uint64_t *cmdpkt)
+{
+ uint32_t devid, eventid, vpeid, doorbell;
+ bool doorbell_valid;
+ DTEntry dte;
+ ITEntry ite;
+ VTEntry old_vte, new_vte;
+ ItsCmdResult cmdres;
+
+ if (!its_feature_virtual(s)) {
+ return CMD_CONTINUE;
+ }
+
+ devid = FIELD_EX64(cmdpkt[0], VMOVI_0, DEVICEID);
+ eventid = FIELD_EX64(cmdpkt[1], VMOVI_1, EVENTID);
+ vpeid = FIELD_EX64(cmdpkt[1], VMOVI_1, VPEID);
+ doorbell_valid = FIELD_EX64(cmdpkt[2], VMOVI_2, D);
+ doorbell = FIELD_EX64(cmdpkt[2], VMOVI_2, DOORBELL);
+
+ trace_gicv3_its_cmd_vmovi(devid, eventid, vpeid, doorbell_valid, doorbell);
+
+ if (doorbell_valid && !valid_doorbell(doorbell)) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid doorbell 0x%x\n", __func__, doorbell);
+ return CMD_CONTINUE;
+ }
+
+ cmdres = lookup_ite(s, __func__, devid, eventid, &ite, &dte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+
+ if (ite.inttype != ITE_INTTYPE_VIRTUAL) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: ITE is not for virtual interrupt\n",
+ __func__);
+ return CMD_CONTINUE;
+ }
+
+ cmdres = lookup_vte(s, __func__, ite.vpeid, &old_vte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+ cmdres = lookup_vte(s, __func__, vpeid, &new_vte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+
+ if (!intid_in_lpi_range(ite.intid) ||
+ ite.intid >= (1ULL << (old_vte.vptsize + 1)) ||
+ ite.intid >= (1ULL << (new_vte.vptsize + 1))) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: ITE intid 0x%x out of range\n",
+ __func__, ite.intid);
+ return CMD_CONTINUE;
+ }
+
+ ite.vpeid = vpeid;
+ if (doorbell_valid) {
+ ite.doorbell = doorbell;
+ }
+
+ /*
+ * Move the LPI from the old redistributor to the new one. We don't
+ * need to do anything if the guest somehow specified the
+ * same pending table for source and destination.
+ */
+ if (old_vte.vptaddr != new_vte.vptaddr) {
+ gicv3_redist_mov_vlpi(&s->gicv3->cpu[old_vte.rdbase],
+ old_vte.vptaddr << 16,
+ &s->gicv3->cpu[new_vte.rdbase],
+ new_vte.vptaddr << 16,
+ ite.intid,
+ ite.doorbell);
+ }
+
+ /* Update the ITE to the new VPEID and possibly doorbell values */
+ return update_ite(s, eventid, &dte, &ite) ? CMD_CONTINUE_OK : CMD_STALL;
+}
+
static ItsCmdResult process_inv(GICv3ITSState *s, const uint64_t *cmdpkt)
{
uint32_t devid, eventid;
@@ -1282,6 +1361,9 @@ static void process_cmdq(GICv3ITSState *s)
case GITS_CMD_VMOVP:
result = process_vmovp(s, cmdpkt);
break;
+ case GITS_CMD_VMOVI:
+ result = process_vmovi(s, cmdpkt);
+ break;
default:
trace_gicv3_its_cmd_unknown(cmd);
break;
@@ -808,6 +808,16 @@ void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
*/
}
+void gicv3_redist_mov_vlpi(GICv3CPUState *src, uint64_t src_vptaddr,
+ GICv3CPUState *dest, uint64_t dest_vptaddr,
+ int irq, int doorbell)
+{
+ /*
+ * The redistributor handling for moving a VLPI will be added
+ * in a subsequent commit.
+ */
+}
+
void gicv3_redist_inv_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr)
{
/*
@@ -193,6 +193,7 @@ gicv3_its_cmd_vmapti(uint32_t devid, uint32_t eventid, uint32_t vpeid, uint32_t
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_vmovp(uint32_t vpeid, uint64_t rdbase) "GICv3 ITS: command VMOVP vPEID 0x%x RDbase 0x%" PRIx64
gicv3_its_cmd_vsync(void) "GICv3 ITS: command VSYNC"
+gicv3_its_cmd_vmovi(uint32_t devid, uint32_t eventid, uint32_t vpeid, int dbvalid, uint32_t doorbell) "GICv3 ITS: command VMOVI DeviceID 0x%x EventID 0x%x vPEID 0x%x D %d Dbell_pINTID 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"