diff --git a/sys/arm64/arm64/gic_v3.c b/sys/arm64/arm64/gic_v3.c
index dfa0f6c7cad1..10639ac00673 100644
--- a/sys/arm64/arm64/gic_v3.c
+++ b/sys/arm64/arm64/gic_v3.c
@@ -835,15 +835,66 @@ gic_v3_map_intr(device_t dev, struct intr_map_data *data,
 	return (error);
 }
 
+struct gic_v3_setup_periph_args {
+	device_t		 dev;
+	struct intr_irqsrc	*isrc;
+};
+
+static void
+gic_v3_setup_periph(void *argp)
+{
+	struct gic_v3_setup_periph_args *args = argp;
+	struct intr_irqsrc *isrc = args->isrc;
+	struct gic_v3_irqsrc *gi = (struct gic_v3_irqsrc *)isrc;
+	device_t dev = args->dev;
+	u_int irq = gi->gi_irq;
+	struct gic_v3_softc *sc = device_get_softc(dev);
+	uint32_t reg;
+
+	MPASS(irq <= GIC_LAST_SPI);
+
+	/*
+	 * We need the lock for both SGIs and PPIs for an atomic CPU_SET() at a
+	 * minimum, but we also need it below for SPIs.
+	 */
+	mtx_lock_spin(&sc->gic_mtx);
+
+	if (isrc->isrc_flags & INTR_ISRCF_PPI)
+		CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
+
+	if (irq >= GIC_FIRST_PPI && irq <= GIC_LAST_SPI) {
+		/* Set the trigger and polarity */
+		if (irq <= GIC_LAST_PPI)
+			reg = gic_r_read(sc, 4,
+			    GICR_SGI_BASE_SIZE + GICD_ICFGR(irq));
+		else
+			reg = gic_d_read(sc, 4, GICD_ICFGR(irq));
+		if (gi->gi_trig == INTR_TRIGGER_LEVEL)
+			reg &= ~(2 << ((irq % 16) * 2));
+		else
+			reg |= 2 << ((irq % 16) * 2);
+
+		if (irq <= GIC_LAST_PPI) {
+			gic_r_write(sc, 4,
+			    GICR_SGI_BASE_SIZE + GICD_ICFGR(irq), reg);
+			gic_v3_wait_for_rwp(sc, REDIST);
+		} else {
+			gic_d_write(sc, 4, GICD_ICFGR(irq), reg);
+			gic_v3_wait_for_rwp(sc, DIST);
+		}
+	}
+
+	mtx_unlock_spin(&sc->gic_mtx);
+}
+
 static int
 gic_v3_setup_intr(device_t dev, struct intr_irqsrc *isrc,
     struct resource *res, struct intr_map_data *data)
 {
-	struct gic_v3_softc *sc = device_get_softc(dev);
 	struct gic_v3_irqsrc *gi = (struct gic_v3_irqsrc *)isrc;
+	struct gic_v3_setup_periph_args pargs;
 	enum intr_trigger trig;
 	enum intr_polarity pol;
-	uint32_t reg;
 	u_int irq;
 	int error;
 
@@ -872,41 +923,18 @@ gic_v3_setup_intr(device_t dev, struct intr_irqsrc *isrc,
 		gi->gi_trig = trig;
 	}
 
-	/*
-	 * XXX - In case that per CPU interrupt is going to be enabled in time
-	 *       when SMP is already started, we need some IPI call which
-	 *       enables it on others CPUs. Further, it's more complicated as
-	 *       pic_enable_source() and pic_disable_source() should act on
-	 *       per CPU basis only. Thus, it should be solved here somehow.
-	 */
-	if (isrc->isrc_flags & INTR_ISRCF_PPI)
-		CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu);
-
-	if (irq >= GIC_FIRST_PPI && irq <= GIC_LAST_SPI) {
-		mtx_lock_spin(&sc->gic_mtx);
-
-		/* Set the trigger and polarity */
-		if (irq <= GIC_LAST_PPI)
-			reg = gic_r_read(sc, 4,
-			    GICR_SGI_BASE_SIZE + GICD_ICFGR(irq));
-		else
-			reg = gic_d_read(sc, 4, GICD_ICFGR(irq));
-		if (trig == INTR_TRIGGER_LEVEL)
-			reg &= ~(2 << ((irq % 16) * 2));
-		else
-			reg |= 2 << ((irq % 16) * 2);
-
-		if (irq <= GIC_LAST_PPI) {
-			gic_r_write(sc, 4,
-			    GICR_SGI_BASE_SIZE + GICD_ICFGR(irq), reg);
-			gic_v3_wait_for_rwp(sc, REDIST);
-		} else {
-			gic_d_write(sc, 4, GICD_ICFGR(irq), reg);
-			gic_v3_wait_for_rwp(sc, DIST);
-		}
-
-		mtx_unlock_spin(&sc->gic_mtx);
+	pargs.dev = dev;
+	pargs.isrc = isrc;
 
+	if (isrc->isrc_flags & INTR_ISRCF_PPI) {
+		/*
+		 * If APs haven't been fired up yet, smp_rendezvous() will just
+		 * execute it on the single CPU and gic_v3_init_secondary() will
+		 * clean up afterwards.
+		 */
+		smp_rendezvous(NULL, gic_v3_setup_periph, NULL, &pargs);
+	} else if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) {
+		gic_v3_setup_periph(&pargs);
 		gic_v3_bind_intr(dev, isrc);
 	}
 
@@ -952,22 +980,49 @@ gic_v3_disable_intr(device_t dev, struct intr_irqsrc *isrc)
 }
 
 static void
-gic_v3_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+gic_v3_enable_intr_periph(void *argp)
 {
+	struct gic_v3_setup_periph_args *args = argp;
+	struct gic_v3_irqsrc *gi = (struct gic_v3_irqsrc *)args->isrc;
+	device_t dev = args->dev;
+	struct gic_v3_softc *sc = device_get_softc(dev);
+	u_int irq = gi->gi_irq;
+
+	/* SGIs and PPIs in corresponding Re-Distributor */
+	gic_r_write(sc, 4, GICR_SGI_BASE_SIZE + GICD_ISENABLER(irq),
+	    GICD_I_MASK(irq));
+	gic_v3_wait_for_rwp(sc, REDIST);
+}
+
+static void
+gic_v3_enable_intr_impl(device_t dev, struct intr_irqsrc *isrc, bool ap_startup)
+{
+	struct gic_v3_setup_periph_args pargs;
 	struct gic_v3_softc *sc;
 	struct gic_v3_irqsrc *gi;
 	u_int irq;
 
-	sc = device_get_softc(dev);
 	gi = (struct gic_v3_irqsrc *)isrc;
 	irq = gi->gi_irq;
+	pargs.isrc = isrc;
+	pargs.dev = dev;
 
 	if (irq <= GIC_LAST_PPI) {
-		/* SGIs and PPIs in corresponding Re-Distributor */
-		gic_r_write(sc, 4, GICR_SGI_BASE_SIZE + GICD_ISENABLER(irq),
-		    GICD_I_MASK(irq));
-		gic_v3_wait_for_rwp(sc, REDIST);
-	} else if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) {
+		/*
+		 * SGIs only need configured on the current AP.  We'll setup and
+		 * enable IPIs as APs come online.
+		 */
+		if (ap_startup || irq <= GIC_LAST_SGI)
+			gic_v3_enable_intr_periph(&pargs);
+		else
+			smp_rendezvous(NULL, gic_v3_enable_intr_periph, NULL,
+			    &pargs);
+		return;
+	}
+
+	sc = device_get_softc(dev);
+
+	if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) {
 		/* SPIs in distributor */
 		gic_d_write(sc, 4, GICD_ISENABLER(irq), GICD_I_MASK(irq));
 		gic_v3_wait_for_rwp(sc, DIST);
@@ -975,6 +1030,12 @@ gic_v3_enable_intr(device_t dev, struct intr_irqsrc *isrc)
 		panic("%s: Unsupported IRQ %u", __func__, irq);
 }
 
+static void
+gic_v3_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+	gic_v3_enable_intr_impl(dev, isrc, false);
+}
+
 static void
 gic_v3_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
 {
@@ -1010,8 +1071,6 @@ gic_v3_bind_intr(device_t dev, struct intr_irqsrc *isrc)
 	int cpu;
 
 	gi = (struct gic_v3_irqsrc *)isrc;
-	if (gi->gi_irq <= GIC_LAST_PPI)
-		return (EINVAL);
 
 	KASSERT(gi->gi_irq >= GIC_FIRST_SPI && gi->gi_irq <= GIC_LAST_SPI,
 	    ("%s: Attempting to bind an invalid IRQ", __func__));
@@ -1039,6 +1098,7 @@ gic_v3_bind_intr(device_t dev, struct intr_irqsrc *isrc)
 static void
 gic_v3_init_secondary(device_t dev)
 {
+	struct gic_v3_setup_periph_args pargs;
 	device_t child;
 	struct gic_v3_softc *sc;
 	gic_v3_initseq_t *init_func;
@@ -1064,14 +1124,19 @@ gic_v3_init_secondary(device_t dev)
 	for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++) {
 		isrc = GIC_INTR_ISRC(sc, irq);
 		if (intr_isrc_init_on_cpu(isrc, cpu))
-			gic_v3_enable_intr(dev, isrc);
+			gic_v3_enable_intr_impl(dev, isrc, true);
 	}
 
+	pargs.dev = dev;
+
 	/* Unmask attached PPI interrupts. */
 	for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) {
 		isrc = GIC_INTR_ISRC(sc, irq);
-		if (intr_isrc_init_on_cpu(isrc, cpu))
-			gic_v3_enable_intr(dev, isrc);
+		if (intr_isrc_init_on_cpu(isrc, cpu)) {
+			pargs.isrc = isrc;
+			gic_v3_setup_periph(&pargs);
+			gic_v3_enable_intr_impl(dev, isrc, true);
+		}
 	}
 
 	for (i = 0; i < sc->gic_nchildren; i++) {