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
This commit is contained in:
parent
2d692e2b31
commit
469cf23bf8
@ -314,6 +314,42 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
@ -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();
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user