summaryrefslogtreecommitdiffstats
path: root/hw/intc/arm_gicv3_redist.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/intc/arm_gicv3_redist.c')
-rw-r--r--hw/intc/arm_gicv3_redist.c54
1 files changed, 54 insertions, 0 deletions
diff --git a/hw/intc/arm_gicv3_redist.c b/hw/intc/arm_gicv3_redist.c
index d81d8e5f07..d1645ba22c 100644
--- a/hw/intc/arm_gicv3_redist.c
+++ b/hw/intc/arm_gicv3_redist.c
@@ -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 */