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
master
Peter Maydell 2022-01-22 18:24:43 +00:00
parent 8b8bb0146b
commit f6d1d9b407
3 changed files with 125 additions and 0 deletions

View File

@ -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;
}

View File

@ -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 */

View File

@ -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);