mirror of https://github.com/xemu-project/xemu.git
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;
|
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
|
* Given a (DeviceID, EventID), look up the corresponding ITE, including
|
||||||
* checking for the various invalid-value cases. If we find a valid ITE,
|
* 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;
|
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,
|
static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite,
|
||||||
int irqlevel)
|
int irqlevel)
|
||||||
{
|
{
|
||||||
|
@ -411,6 +479,33 @@ static ItsCmdResult process_its_cmd_phys(GICv3ITSState *s, const ITEntry *ite,
|
||||||
return CMD_CONTINUE_OK;
|
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
|
* This function handles the processing of following commands based on
|
||||||
* the ItsCmdType parameter passed:-
|
* the ItsCmdType parameter passed:-
|
||||||
|
@ -446,8 +541,8 @@ static ItsCmdResult do_process_its_cmd(GICv3ITSState *s, uint32_t devid,
|
||||||
__func__, ite.inttype);
|
__func__, ite.inttype);
|
||||||
return CMD_CONTINUE;
|
return CMD_CONTINUE;
|
||||||
}
|
}
|
||||||
/* The GICv4 virtual interrupt handling will go here */
|
cmdres = process_its_cmd_virt(s, &ite, irqlevel);
|
||||||
g_assert_not_reached();
|
break;
|
||||||
default:
|
default:
|
||||||
g_assert_not_reached();
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
|
@ -788,6 +788,15 @@ void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
|
||||||
gicv3_redist_update_lpi(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)
|
void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
|
||||||
{
|
{
|
||||||
/* Update redistributor state for a change in an external PPI input line */
|
/* 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_dist_set_irq(GICv3State *s, int irq, int level);
|
||||||
void gicv3_redist_set_irq(GICv3CPUState *cs, 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);
|
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);
|
void gicv3_redist_lpi_pending(GICv3CPUState *cs, int irq, int level);
|
||||||
/**
|
/**
|
||||||
* gicv3_redist_update_lpi:
|
* 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_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_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_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"
|
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
|
# armv7m_nvic.c
|
||||||
|
|
Loading…
Reference in New Issue