• R/O
  • HTTP
  • SSH
  • HTTPS

提交

标签
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Commit MetaInfo

修订版961b4912c1330aaf11a354c9d8f5c63e1ba0ae3b (tree)
时间2022-01-28 23:29:47
作者Peter Maydell <peter.maydell@lina...>
CommiterPeter Maydell

Log Message

hw/intc/arm_gicv3_its: Implement MOVI

Implement the ITS MOVI command. This command specifies a (physical) LPI
by DeviceID and EventID and provides a new ICID for it. The ITS must
find the interrupt translation table entry for the LPI, which will
tell it the old ICID. It then moves the pending state of the LPI from
the old redistributor to the new one and updates the ICID field in
the translation table entry.

This is another GICv3 ITS command that we forgot to implement. Linux
does use this one, but only if the guest powers off one of its CPUs.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20220122182444.724087-15-peter.maydell@linaro.org

更改概述

差异

--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -634,6 +634,149 @@ static ItsCmdResult process_movall(GICv3ITSState *s, uint64_t value,
634634 return CMD_CONTINUE;
635635 }
636636
637+static ItsCmdResult process_movi(GICv3ITSState *s, uint64_t value,
638+ uint32_t offset)
639+{
640+ AddressSpace *as = &s->gicv3->dma_as;
641+ MemTxResult res = MEMTX_OK;
642+ uint32_t devid, eventid, intid;
643+ uint16_t old_icid, new_icid;
644+ uint64_t old_cte, new_cte;
645+ uint64_t old_rdbase, new_rdbase;
646+ uint64_t dte;
647+ bool dte_valid, ite_valid, cte_valid;
648+ uint64_t num_eventids;
649+ IteEntry ite = {};
650+
651+ devid = FIELD_EX64(value, MOVI_0, DEVICEID);
652+
653+ offset += NUM_BYTES_IN_DW;
654+ value = address_space_ldq_le(as, s->cq.base_addr + offset,
655+ MEMTXATTRS_UNSPECIFIED, &res);
656+ if (res != MEMTX_OK) {
657+ return CMD_STALL;
658+ }
659+ eventid = FIELD_EX64(value, MOVI_1, EVENTID);
660+
661+ offset += NUM_BYTES_IN_DW;
662+ value = address_space_ldq_le(as, s->cq.base_addr + offset,
663+ MEMTXATTRS_UNSPECIFIED, &res);
664+ if (res != MEMTX_OK) {
665+ return CMD_STALL;
666+ }
667+ new_icid = FIELD_EX64(value, MOVI_2, ICID);
668+
669+ if (devid >= s->dt.num_entries) {
670+ qemu_log_mask(LOG_GUEST_ERROR,
671+ "%s: invalid command attributes: devid %d>=%d",
672+ __func__, devid, s->dt.num_entries);
673+ return CMD_CONTINUE;
674+ }
675+ dte = get_dte(s, devid, &res);
676+ if (res != MEMTX_OK) {
677+ return CMD_STALL;
678+ }
679+
680+ dte_valid = FIELD_EX64(dte, DTE, VALID);
681+ if (!dte_valid) {
682+ qemu_log_mask(LOG_GUEST_ERROR,
683+ "%s: invalid command attributes: "
684+ "invalid dte: %"PRIx64" for %d\n",
685+ __func__, dte, devid);
686+ return CMD_CONTINUE;
687+ }
688+
689+ num_eventids = 1ULL << (FIELD_EX64(dte, DTE, SIZE) + 1);
690+ if (eventid >= num_eventids) {
691+ qemu_log_mask(LOG_GUEST_ERROR,
692+ "%s: invalid command attributes: eventid %d >= %"
693+ PRId64 "\n",
694+ __func__, eventid, num_eventids);
695+ return CMD_CONTINUE;
696+ }
697+
698+ ite_valid = get_ite(s, eventid, dte, &old_icid, &intid, &res);
699+ if (res != MEMTX_OK) {
700+ return CMD_STALL;
701+ }
702+
703+ if (!ite_valid) {
704+ qemu_log_mask(LOG_GUEST_ERROR,
705+ "%s: invalid command attributes: invalid ITE\n",
706+ __func__);
707+ return CMD_CONTINUE;
708+ }
709+
710+ if (old_icid >= s->ct.num_entries) {
711+ qemu_log_mask(LOG_GUEST_ERROR,
712+ "%s: invalid ICID 0x%x in ITE (table corrupted?)\n",
713+ __func__, old_icid);
714+ return CMD_CONTINUE;
715+ }
716+
717+ if (new_icid >= s->ct.num_entries) {
718+ qemu_log_mask(LOG_GUEST_ERROR,
719+ "%s: invalid command attributes: ICID 0x%x\n",
720+ __func__, new_icid);
721+ return CMD_CONTINUE;
722+ }
723+
724+ cte_valid = get_cte(s, old_icid, &old_cte, &res);
725+ if (res != MEMTX_OK) {
726+ return CMD_STALL;
727+ }
728+ if (!cte_valid) {
729+ qemu_log_mask(LOG_GUEST_ERROR,
730+ "%s: invalid command attributes: "
731+ "invalid cte: %"PRIx64"\n",
732+ __func__, old_cte);
733+ return CMD_CONTINUE;
734+ }
735+
736+ cte_valid = get_cte(s, new_icid, &new_cte, &res);
737+ if (res != MEMTX_OK) {
738+ return CMD_STALL;
739+ }
740+ if (!cte_valid) {
741+ qemu_log_mask(LOG_GUEST_ERROR,
742+ "%s: invalid command attributes: "
743+ "invalid cte: %"PRIx64"\n",
744+ __func__, new_cte);
745+ return CMD_CONTINUE;
746+ }
747+
748+ old_rdbase = FIELD_EX64(old_cte, CTE, RDBASE);
749+ if (old_rdbase >= s->gicv3->num_cpu) {
750+ qemu_log_mask(LOG_GUEST_ERROR,
751+ "%s: CTE has invalid rdbase 0x%"PRIx64"\n",
752+ __func__, old_rdbase);
753+ return CMD_CONTINUE;
754+ }
755+
756+ new_rdbase = FIELD_EX64(new_cte, CTE, RDBASE);
757+ if (new_rdbase >= s->gicv3->num_cpu) {
758+ qemu_log_mask(LOG_GUEST_ERROR,
759+ "%s: CTE has invalid rdbase 0x%"PRIx64"\n",
760+ __func__, new_rdbase);
761+ return CMD_CONTINUE;
762+ }
763+
764+ if (old_rdbase != new_rdbase) {
765+ /* Move the LPI from the old redistributor to the new one */
766+ gicv3_redist_mov_lpi(&s->gicv3->cpu[old_rdbase],
767+ &s->gicv3->cpu[new_rdbase],
768+ intid);
769+ }
770+
771+ /* Update the ICID field in the interrupt translation table entry */
772+ ite.itel = FIELD_DP64(ite.itel, ITE_L, VALID, 1);
773+ ite.itel = FIELD_DP64(ite.itel, ITE_L, INTTYPE, ITE_INTTYPE_PHYSICAL);
774+ ite.itel = FIELD_DP64(ite.itel, ITE_L, INTID, intid);
775+ ite.itel = FIELD_DP64(ite.itel, ITE_L, DOORBELL, INTID_SPURIOUS);
776+ ite.iteh = FIELD_DP32(ite.iteh, ITE_H, ICID, new_icid);
777+ return update_ite(s, eventid, dte, ite) ? CMD_CONTINUE : CMD_STALL;
778+}
779+
637780 /*
638781 * Current implementation blocks until all
639782 * commands are processed
@@ -731,6 +874,9 @@ static void process_cmdq(GICv3ITSState *s)
731874 gicv3_redist_update_lpi(&s->gicv3->cpu[i]);
732875 }
733876 break;
877+ case GITS_CMD_MOVI:
878+ result = process_movi(s, data, cq_offset);
879+ break;
734880 case GITS_CMD_MOVALL:
735881 result = process_movall(s, data, cq_offset);
736882 break;
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -681,6 +681,59 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
681681 gicv3_redist_lpi_pending(cs, irq, level);
682682 }
683683
684+void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq)
685+{
686+ /*
687+ * Move the specified LPI's pending state from the source redistributor
688+ * to the destination.
689+ *
690+ * If LPIs are disabled on dest this is CONSTRAINED UNPREDICTABLE:
691+ * we choose to NOP. If LPIs are disabled on source there's nothing
692+ * to be transferred anyway.
693+ */
694+ AddressSpace *as = &src->gic->dma_as;
695+ uint64_t idbits;
696+ uint32_t pendt_size;
697+ uint64_t src_baddr;
698+ uint8_t src_pend;
699+
700+ if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
701+ !(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
702+ return;
703+ }
704+
705+ idbits = MIN(FIELD_EX64(src->gicr_propbaser, GICR_PROPBASER, IDBITS),
706+ GICD_TYPER_IDBITS);
707+ idbits = MIN(FIELD_EX64(dest->gicr_propbaser, GICR_PROPBASER, IDBITS),
708+ idbits);
709+
710+ pendt_size = 1ULL << (idbits + 1);
711+ if ((irq / 8) >= pendt_size) {
712+ return;
713+ }
714+
715+ src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
716+
717+ address_space_read(as, src_baddr + (irq / 8),
718+ MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
719+ if (!extract32(src_pend, irq % 8, 1)) {
720+ /* Not pending on source, nothing to do */
721+ return;
722+ }
723+ src_pend &= ~(1 << (irq % 8));
724+ address_space_write(as, src_baddr + (irq / 8),
725+ MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend));
726+ if (irq == src->hpplpi.irq) {
727+ /*
728+ * We just made this LPI not-pending so only need to update
729+ * if it was previously the highest priority pending LPI
730+ */
731+ gicv3_redist_update_lpi(src);
732+ }
733+ /* Mark it pending on the destination */
734+ gicv3_redist_lpi_pending(dest, irq, 1);
735+}
736+
684737 void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
685738 {
686739 /*
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -315,6 +315,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
315315 #define CMD_MASK 0xff
316316
317317 /* ITS Commands */
318+#define GITS_CMD_MOVI 0x01
318319 #define GITS_CMD_INT 0x03
319320 #define GITS_CMD_CLEAR 0x04
320321 #define GITS_CMD_SYNC 0x05
@@ -360,6 +361,11 @@ FIELD(MAPC, RDBASE, 16, 32)
360361 FIELD(MOVALL_2, RDBASE1, 16, 36)
361362 FIELD(MOVALL_3, RDBASE2, 16, 36)
362363
364+/* MOVI command fields */
365+FIELD(MOVI_0, DEVICEID, 32, 32)
366+FIELD(MOVI_1, EVENTID, 0, 32)
367+FIELD(MOVI_2, ICID, 0, 16)
368+
363369 /*
364370 * 12 bytes Interrupt translation Table Entry size
365371 * as per Table 5.3 in GICv3 spec
@@ -503,6 +509,16 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs);
503509 */
504510 void gicv3_redist_update_lpi_only(GICv3CPUState *cs);
505511 /**
512+ * gicv3_redist_mov_lpi:
513+ * @src: source redistributor
514+ * @dest: destination redistributor
515+ * @irq: LPI to update
516+ *
517+ * Move the pending state of the specified LPI from @src to @dest,
518+ * as required by the ITS MOVI command.
519+ */
520+void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq);
521+/**
506522 * gicv3_redist_movall_lpis:
507523 * @src: source redistributor
508524 * @dest: destination redistributor