hw/intc/arm_gicv3_its: Implement MOVALL
Implement the ITS MOVALL command, which takes all the pending interrupts on a source redistributor and makes the not-pending on that source redistributor and pending on a destination redistributor. This is a GICv3 ITS command which we forgot to implement. (It is not used by Linux guests.) Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20220122182444.724087-14-peter.maydell@linaro.org
This commit is contained in:
parent
8b8bb0146b
commit
f6d1d9b407
@ -582,6 +582,58 @@ static ItsCmdResult process_mapd(GICv3ITSState *s, uint64_t value,
|
||||
return update_dte(s, devid, valid, size, itt_addr) ? CMD_CONTINUE : CMD_STALL;
|
||||
}
|
||||
|
||||
static ItsCmdResult process_movall(GICv3ITSState *s, uint64_t value,
|
||||
uint32_t offset)
|
||||
{
|
||||
AddressSpace *as = &s->gicv3->dma_as;
|
||||
MemTxResult res = MEMTX_OK;
|
||||
uint64_t rd1, rd2;
|
||||
|
||||
/* No fields in dwords 0 or 1 */
|
||||
offset += NUM_BYTES_IN_DW;
|
||||
offset += NUM_BYTES_IN_DW;
|
||||
value = address_space_ldq_le(as, s->cq.base_addr + offset,
|
||||
MEMTXATTRS_UNSPECIFIED, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
|
||||
rd1 = FIELD_EX64(value, MOVALL_2, RDBASE1);
|
||||
if (rd1 >= s->gicv3->num_cpu) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: RDBASE1 %" PRId64
|
||||
" out of range (must be less than %d)\n",
|
||||
__func__, rd1, s->gicv3->num_cpu);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
offset += NUM_BYTES_IN_DW;
|
||||
value = address_space_ldq_le(as, s->cq.base_addr + offset,
|
||||
MEMTXATTRS_UNSPECIFIED, &res);
|
||||
if (res != MEMTX_OK) {
|
||||
return CMD_STALL;
|
||||
}
|
||||
|
||||
rd2 = FIELD_EX64(value, MOVALL_3, RDBASE2);
|
||||
if (rd2 >= s->gicv3->num_cpu) {
|
||||
qemu_log_mask(LOG_GUEST_ERROR,
|
||||
"%s: RDBASE2 %" PRId64
|
||||
" out of range (must be less than %d)\n",
|
||||
__func__, rd2, s->gicv3->num_cpu);
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
if (rd1 == rd2) {
|
||||
/* Move to same target must succeed as a no-op */
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
/* Move all pending LPIs from redistributor 1 to redistributor 2 */
|
||||
gicv3_redist_movall_lpis(&s->gicv3->cpu[rd1], &s->gicv3->cpu[rd2]);
|
||||
|
||||
return CMD_CONTINUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Current implementation blocks until all
|
||||
* commands are processed
|
||||
@ -679,6 +731,9 @@ static void process_cmdq(GICv3ITSState *s)
|
||||
gicv3_redist_update_lpi(&s->gicv3->cpu[i]);
|
||||
}
|
||||
break;
|
||||
case GITS_CMD_MOVALL:
|
||||
result = process_movall(s, data, cq_offset);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -681,6 +681,60 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level)
|
||||
gicv3_redist_lpi_pending(cs, irq, level);
|
||||
}
|
||||
|
||||
void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest)
|
||||
{
|
||||
/*
|
||||
* We must move all pending LPIs from the source redistributor
|
||||
* to the destination. That is, for every pending LPI X on
|
||||
* src, we must set it not-pending on src and pending on dest.
|
||||
* LPIs that are already pending on dest are not cleared.
|
||||
*
|
||||
* If LPIs are disabled on dest this is CONSTRAINED UNPREDICTABLE:
|
||||
* we choose to NOP. If LPIs are disabled on source there's nothing
|
||||
* to be transferred anyway.
|
||||
*/
|
||||
AddressSpace *as = &src->gic->dma_as;
|
||||
uint64_t idbits;
|
||||
uint32_t pendt_size;
|
||||
uint64_t src_baddr, dest_baddr;
|
||||
int i;
|
||||
|
||||
if (!(src->gicr_ctlr & GICR_CTLR_ENABLE_LPIS) ||
|
||||
!(dest->gicr_ctlr & GICR_CTLR_ENABLE_LPIS)) {
|
||||
return;
|
||||
}
|
||||
|
||||
idbits = MIN(FIELD_EX64(src->gicr_propbaser, GICR_PROPBASER, IDBITS),
|
||||
GICD_TYPER_IDBITS);
|
||||
idbits = MIN(FIELD_EX64(dest->gicr_propbaser, GICR_PROPBASER, IDBITS),
|
||||
idbits);
|
||||
|
||||
pendt_size = 1ULL << (idbits + 1);
|
||||
src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
|
||||
dest_baddr = dest->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK;
|
||||
|
||||
for (i = GICV3_LPI_INTID_START / 8; i < pendt_size / 8; i++) {
|
||||
uint8_t src_pend, dest_pend;
|
||||
|
||||
address_space_read(as, src_baddr + i, MEMTXATTRS_UNSPECIFIED,
|
||||
&src_pend, sizeof(src_pend));
|
||||
if (!src_pend) {
|
||||
continue;
|
||||
}
|
||||
address_space_read(as, dest_baddr + i, MEMTXATTRS_UNSPECIFIED,
|
||||
&dest_pend, sizeof(dest_pend));
|
||||
dest_pend |= src_pend;
|
||||
src_pend = 0;
|
||||
address_space_write(as, src_baddr + i, MEMTXATTRS_UNSPECIFIED,
|
||||
&src_pend, sizeof(src_pend));
|
||||
address_space_write(as, dest_baddr + i, MEMTXATTRS_UNSPECIFIED,
|
||||
&dest_pend, sizeof(dest_pend));
|
||||
}
|
||||
|
||||
gicv3_redist_update_lpi(src);
|
||||
gicv3_redist_update_lpi(dest);
|
||||
}
|
||||
|
||||
void gicv3_redist_set_irq(GICv3CPUState *cs, int irq, int level)
|
||||
{
|
||||
/* Update redistributor state for a change in an external PPI input line */
|
||||
|
@ -324,6 +324,7 @@ FIELD(GITS_TYPER, CIL, 36, 1)
|
||||
#define GITS_CMD_MAPI 0x0B
|
||||
#define GITS_CMD_INV 0x0C
|
||||
#define GITS_CMD_INVALL 0x0D
|
||||
#define GITS_CMD_MOVALL 0x0E
|
||||
#define GITS_CMD_DISCARD 0x0F
|
||||
|
||||
/* MAPC command fields */
|
||||
@ -355,6 +356,10 @@ FIELD(MAPC, RDBASE, 16, 32)
|
||||
#define L2_TABLE_VALID_MASK CMD_FIELD_VALID_MASK
|
||||
#define TABLE_ENTRY_VALID_MASK (1ULL << 0)
|
||||
|
||||
/* MOVALL command fields */
|
||||
FIELD(MOVALL_2, RDBASE1, 16, 36)
|
||||
FIELD(MOVALL_3, RDBASE2, 16, 36)
|
||||
|
||||
/*
|
||||
* 12 bytes Interrupt translation Table Entry size
|
||||
* as per Table 5.3 in GICv3 spec
|
||||
@ -497,6 +502,17 @@ void gicv3_redist_update_lpi(GICv3CPUState *cs);
|
||||
* an incoming migration has loaded new state.
|
||||
*/
|
||||
void gicv3_redist_update_lpi_only(GICv3CPUState *cs);
|
||||
/**
|
||||
* gicv3_redist_movall_lpis:
|
||||
* @src: source redistributor
|
||||
* @dest: destination redistributor
|
||||
*
|
||||
* Scan the LPI pending table for @src, and for each pending LPI there
|
||||
* mark it as not-pending for @src and pending for @dest, as required
|
||||
* by the ITS MOVALL command.
|
||||
*/
|
||||
void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest);
|
||||
|
||||
void gicv3_redist_send_sgi(GICv3CPUState *cs, int grp, int irq, bool ns);
|
||||
void gicv3_init_cpuif(GICv3State *s);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user