diff options
Diffstat (limited to 'hw/intc/arm_gicv3_redist.c')
| -rw-r--r-- | hw/intc/arm_gicv3_redist.c | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c index d1645ba22c..412a04f59c 100644 --- a/hw/intc/arm_gicv3_redist.c +++ b/hw/intc/arm_gicv3_redist.c @@ -681,6 +681,59 @@ void gicv3_redist_process_lpi(GICv3CPUState *cs, int irq, int level) gicv3_redist_lpi_pending(cs, irq, level); } +void gicv3_redist_mov_lpi(GICv3CPUState *src, GICv3CPUState *dest, int irq) +{ + /* + * Move the specified LPI's pending state from the source redistributor + * to the destination. + * + * 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; + uint8_t src_pend; + + 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); + if ((irq / 8) >= pendt_size) { + return; + } + + src_baddr = src->gicr_pendbaser & R_GICR_PENDBASER_PHYADDR_MASK; + + address_space_read(as, src_baddr + (irq / 8), + MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend)); + if (!extract32(src_pend, irq % 8, 1)) { + /* Not pending on source, nothing to do */ + return; + } + src_pend &= ~(1 << (irq % 8)); + address_space_write(as, src_baddr + (irq / 8), + MEMTXATTRS_UNSPECIFIED, &src_pend, sizeof(src_pend)); + if (irq == src->hpplpi.irq) { + /* + * We just made this LPI not-pending so only need to update + * if it was previously the highest priority pending LPI + */ + gicv3_redist_update_lpi(src); + } + /* Mark it pending on the destination */ + gicv3_redist_lpi_pending(dest, irq, 1); +} + void gicv3_redist_movall_lpis(GICv3CPUState *src, GICv3CPUState *dest) { /* |