aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2022-04-08 15:15:23 +0100
committerPeter Maydell <peter.maydell@linaro.org>2022-04-22 09:24:44 +0100
commit469cf23bf857412e45bb480eabd8f0cd890b41b1 (patch)
tree42cc6f06bab229bf54825e1b5ee8cb1f3a5fa5b4
parent2d692e2b314e212664e9ce72787ce0ecea615c61 (diff)
hw/intc/arm_gicv3_its: Handle virtual interrupts in process_its_cmd()
For GICv4, interrupt table entries read by process_its_cmd() may indicate virtual LPIs which are to be directly injected into a VM. Implement the ITS side of the code for handling this. This is similar to the existing handling of physical LPIs, but instead of looking up a collection ID in a collection table, we look up a vPEID in a vPE table. As with the physical LPIs, we leave the rest of the work to code in the redistributor device. The redistributor half will be implemented in a later commit; for now we just provide a stub function which does nothing. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220408141550.1271295-15-peter.maydell@linaro.org
-rw-r--r--hw/intc/arm_gicv3_its.c99
-rw-r--r--hw/intc/arm_gicv3_redist.c9
-rw-r--r--hw/intc/gicv3_internal.h17
-rw-r--r--hw/intc/trace-events2
4 files changed, 125 insertions, 2 deletions
diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c
index e7e1769fa4..d2c0ca5f72 100644
--- a/hw/intc/arm_gicv3_its.c
+++ b/hw/intc/arm_gicv3_its.c
@@ -315,6 +315,42 @@ out:
}
/*
+ * Read the vPE Table entry at index @vpeid. On success (including
+ * successfully determining that there is no valid entry for this index),
+ * we return MEMTX_OK and populate the VTEntry struct accordingly.
+ * If there is an error reading memory then we return the error code.
+ */
+static MemTxResult get_vte(GICv3ITSState *s, uint32_t vpeid, VTEntry *vte)
+{
+ MemTxResult res = MEMTX_OK;
+ AddressSpace *as = &s->gicv3->dma_as;
+ uint64_t entry_addr = table_entry_addr(s, &s->vpet, vpeid, &res);
+ uint64_t vteval;
+
+ if (entry_addr == -1) {
+ /* No L2 table entry, i.e. no valid VTE, or a memory error */
+ vte->valid = false;
+ goto out;
+ }
+ vteval = address_space_ldq_le(as, entry_addr, MEMTXATTRS_UNSPECIFIED, &res);
+ if (res != MEMTX_OK) {
+ goto out;
+ }
+ vte->valid = FIELD_EX64(vteval, VTE, VALID);
+ vte->vptsize = FIELD_EX64(vteval, VTE, VPTSIZE);
+ vte->vptaddr = FIELD_EX64(vteval, VTE, VPTADDR);
+ vte->rdbase = FIELD_EX64(vteval, VTE, RDBASE);
+out:
+ if (res != MEMTX_OK) {
+ trace_gicv3_its_vte_read_fault(vpeid);
+ } else {
+ trace_gicv3_its_vte_read(vpeid, vte->valid, vte->vptsize,
+ vte->vptaddr, vte->rdbase);
+ }
+ return res;
+}
+
+/*
* Given a (DeviceID, EventID), look up the corresponding ITE, including
* checking for the various invalid-value cases. If we find a valid ITE,
* fill in @ite and @dte and return CMD_CONTINUE_OK. Otherwise return
@@ -397,6 +433,38 @@ static ItsCmdResult lookup_cte(GICv3ITSState *s, const char *who,
return CMD_CONTINUE_OK;
}
+/*
+ * Given a VPEID, look up the corresponding VTE, including checking
+ * for various invalid-value cases. if we find a valid VTE, fill in @vte
+ * and return CMD_CONTINUE_OK; otherwise return CMD_STALL or CMD_CONTINUE
+ * (and the contents of @vte should not be relied on).
+ *
+ * The string @who is purely for the LOG_GUEST_ERROR messages,
+ * and should indicate the name of the calling function or similar.
+ */
+static ItsCmdResult lookup_vte(GICv3ITSState *s, const char *who,
+ uint32_t vpeid, VTEntry *vte)
+{
+ if (vpeid >= s->vpet.num_entries) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid VPEID 0x%x\n", who, vpeid);
+ return CMD_CONTINUE;
+ }
+
+ if (get_vte(s, vpeid, vte) != MEMTX_OK) {
+ return CMD_STALL;
+ }
+ if (!vte->valid) {
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: invalid VTE for VPEID 0x%x\n", who, vpeid);
+ return CMD_CONTINUE;
+ }
+
+ if (vte->rdbase >= s->gicv3->num_cpu) {
+ return CMD_CONTINUE;
+ }
+ return CMD_CONTINUE_OK;
+}
+
static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite,
int irqlevel)
{
@@ -411,6 +479,33 @@ static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite,
return CMD_CONTINUE_OK;
}
+static ItsCmdResult process_its_cmd_virt(GICv3ITSState *s, const ITEntry *ite,
+ int irqlevel)
+{
+ VTEntry vte;
+ ItsCmdResult cmdres;
+
+ cmdres = lookup_vte(s, __func__, ite->vpeid, &vte);
+ if (cmdres != CMD_CONTINUE_OK) {
+ return cmdres;
+ }
+
+ if (!intid_in_lpi_range(ite->intid) ||
+ ite->intid >= (1ULL << (vte.vptsize + 1))) {
+ qemu_log_mask(LOG_GUEST_ERROR, "%s: intid 0x%x out of range\n",
+ __func__, ite->intid);
+ return CMD_CONTINUE;
+ }
+
+ /*
+ * For QEMU the actual pending of the vLPI is handled in the
+ * redistributor code
+ */
+ gicv3_redist_process_vlpi(&s->gicv3->cpu[vte.rdbase], ite->intid,
+ vte.vptaddr << 16, ite->doorbell, irqlevel);
+ return CMD_CONTINUE_OK;
+}
+
/*
* This function handles the processing of following commands based on
* the ItsCmdType parameter passed:-
@@ -446,8 +541,8 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
__func__, ite.inttype);
return CMD_CONTINUE;
}
- /* The GICv4 virtual interrupt handling will go here */
- g_assert_not_reached();
+ cmdres = process_its_cmd_virt(s, &ite, irqlevel);
+ break;
default:
g_assert_not_reached();
}
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index dc9729e839..b08b599c88 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -788,6 +788,15 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
gicv3_redist_update_lpi(dest);
}
+void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
+ int doorbell, int level)
+{
+ /*
+ * The redistributor handling for being handed a VLPI by the ITS
+ * will be added in a subsequent commit.
+ */
+}
+
void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
{
/* Update redistributor state for a change in an external PPI input line */
diff --git a/hw/intc/gicv3_internal.h b/hw/intc/gicv3_internal.h
index bbb8a20ce6..6e22c8072e 100644
--- a/hw/intc/gicv3_internal.h
+++ b/hw/intc/gicv3_internal.h
@@ -527,6 +527,23 @@ MemTxResult gicv3_redist_write(void *opaque, hwaddr offset, uint64_t data,
void gicv3_dist_set_irq(GICv3State *s, int irq, int level);
void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level);
void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level);
+/**
+ * gicv3_redist_process_vlpi:
+ * @cs: GICv3CPUState
+ * @irq: (virtual) interrupt number
+ * @vptaddr: (guest) address of VLPI table
+ * @doorbell: doorbell (physical) interrupt number (1023 for "no doorbell")
+ * @level: level to set @irq to
+ *
+ * Process a virtual LPI being directly injected by the ITS. This function
+ * will update the VLPI table specified by @vptaddr and @vptsize. If the
+ * vCPU corresponding to that VLPI table is currently running on
+ * the CPU associated with this redistributor, directly inject the VLPI
+ * @irq. If the vCPU is not running on this CPU, raise the doorbell
+ * interrupt instead.
+ */
+void gicv3_redist_process_vlpi(GICv3CPUState *cs, int irq, uint64_t vptaddr,
+ int doorbell, int level);
void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level);
/**
* gicv3_redist_update_lpi:
diff --git a/hw/intc/trace-events b/hw/intc/trace-events
index 2fcc9e40e5..d529914eca 100644
--- a/hw/intc/trace-events
+++ b/hw/intc/trace-events
@@ -200,6 +200,8 @@ 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_read(uint32_t vpeid, int valid, uint32_t vptsize, uint64_t vptaddr, uint32_t rdbase) "GICv3 ITS: vPE Table read for vPEID 0x%x: valid %d VPTsize 0x%x VPTaddr 0x%" PRIx64 " RDbase 0x%x"
+gicv3_its_vte_read_fault(uint32_t vpeid) "GICv3 ITS: vPE Table read for vPEID 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