diff --git a/core/interrupts.c b/core/interrupts.c
index 9bef5f5..1cdf3e4 100644
--- a/core/interrupts.c
+++ b/core/interrupts.c
@@ -32,17 +32,20 @@
 #define ICP_MFRR		0xc	/* 8-bit access */
 
 static LIST_HEAD(irq_sources);
+static LIST_HEAD(irq_sources2);
 static struct lock irq_lock = LOCK_UNLOCKED;
 
-void __register_irq_source(struct irq_source *is)
+void __register_irq_source(struct irq_source *is, bool secondary)
 {
 	struct irq_source *is1;
+	struct list_head *list = secondary ? &irq_sources2 : &irq_sources;
 
-	prlog(PR_DEBUG, "IRQ: Registering %04x..%04x ops @%p (data %p)\n",
-	      is->start, is->end - 1, is->ops, is->data);
+	prlog(PR_DEBUG, "IRQ: Registering %04x..%04x ops @%p (data %p)%s\n",
+	      is->start, is->end - 1, is->ops, is->data,
+	      secondary ? " [secondary]" : "");
 
 	lock(&irq_lock);
-	list_for_each(&irq_sources, is1, link) {
+	list_for_each(list, is1, link) {
 		if (is->end > is1->start && is->start < is1->end) {
 			prerror("register IRQ source overlap !\n");
 			prerror("  new: %x..%x old: %x..%x\n",
@@ -51,7 +54,7 @@ void __register_irq_source(struct irq_source *is)
 			assert(0);
 		}
 	}
-	list_add_tail(&irq_sources, &is->link);
+	list_add_tail(list, &is->link);
 	unlock(&irq_lock);
 }
 
@@ -67,13 +70,14 @@ void register_irq_source(const struct irq_source_ops *ops, void *data,
 	is->ops = ops;
 	is->data = data;
 
-	__register_irq_source(is);
+	__register_irq_source(is, false);
 }
 
 void unregister_irq_source(uint32_t start, uint32_t count)
 {
 	struct irq_source *is;
 
+	/* Note: We currently only unregister from the primary sources */
 	lock(&irq_lock);
 	list_for_each(&irq_sources, is, link) {
 		if (start >= is->start && start < is->end) {
@@ -102,39 +106,24 @@ static struct irq_source *irq_find_source(uint32_t isn)
 	struct irq_source *is;
 
 	lock(&irq_lock);
+	/*
+	 * XXX This really needs some kind of caching !
+	 */
 	list_for_each(&irq_sources, is, link) {
 		if (isn >= is->start && isn < is->end) {
 			unlock(&irq_lock);
 			return is;
 		}
 	}
-	unlock(&irq_lock);
-
-	return NULL;
-}
-
-void adjust_irq_source(struct irq_source *is, uint32_t new_count)
-{
-	struct irq_source *is1;
-	uint32_t new_end = is->start + new_count;
-
-	prlog(PR_DEBUG, "IRQ: Adjusting %04x..%04x to %04x..%04x\n",
-	      is->start, is->end - 1, is->start, new_end - 1);
-
-	lock(&irq_lock);
-	list_for_each(&irq_sources, is1, link) {
-		if (is1 == is)
-			continue;
-		if (new_end > is1->start && is->start < is1->end) {
-			prerror("adjust IRQ source overlap !\n");
-			prerror("  new: %x..%x old: %x..%x\n",
-				is->start, new_end - 1,
-				is1->start, is1->end - 1);
-			assert(0);
+	list_for_each(&irq_sources2, is, link) {
+		if (isn >= is->start && isn < is->end) {
+			unlock(&irq_lock);
+			return is;
 		}
 	}
-	is->end = new_end;
 	unlock(&irq_lock);
+
+	return NULL;
 }
 
 /*
@@ -474,4 +463,3 @@ void init_interrupts(void)
 	}
 }
 
-
diff --git a/hw/phb4.c b/hw/phb4.c
index a21aa1a..a6a6a51 100644
--- a/hw/phb4.c
+++ b/hw/phb4.c
@@ -3174,11 +3174,11 @@ static void phb4_create(struct dt_node *np)
 	phb4_init_hw(p, true);
 
 	/* Register all interrupt sources with XIVE */
-	xive_register_source(p->base_msi, p->num_irqs - 8, 16, p->int_mmio, 0,
-			     NULL, NULL);
-	xive_register_source(p->base_lsi, 8, 16,
-			     p->int_mmio + ((p->num_irqs - 8) << 16),
-			     XIVE_SRC_LSI, p, &phb4_lsi_ops);
+	xive_register_hw_source(p->base_msi, p->num_irqs - 8, 16, p->int_mmio, 0,
+				NULL, NULL);
+	xive_register_hw_source(p->base_lsi, 8, 16,
+				p->int_mmio + ((p->num_irqs - 8) << 16),
+				XIVE_SRC_LSI, p, &phb4_lsi_ops);
 
 	/* Platform additional setup */
 	if (platform.pci_setup_phb)
diff --git a/hw/psi.c b/hw/psi.c
index a7ea12c..9913c3a 100644
--- a/hw/psi.c
+++ b/hw/psi.c
@@ -785,9 +785,9 @@ static void psi_init_p9_interrupts(struct psi *psi)
 	out_be64(psi->regs + PSIHB_IVT_OFFSET, val);
 
 	/* Register sources */
-	xive_register_source(psi->interrupt, P9_PSI_NUM_IRQS,
-			     12, psi->esb_mmio, XIVE_SRC_LSI,
-			     psi, &psi_p9_irq_ops);
+	xive_register_hw_source(psi->interrupt, P9_PSI_NUM_IRQS,
+				12, psi->esb_mmio, XIVE_SRC_LSI,
+				psi, &psi_p9_irq_ops);
 }
 
 static void psi_init_interrupts(struct psi *psi)
diff --git a/hw/xive.c b/hw/xive.c
index a9150ed..80a95c2 100644
--- a/hw/xive.c
+++ b/hw/xive.c
@@ -103,6 +103,7 @@
 /* Use 64K for everything by default */
 #define IC_PAGE_SIZE	0x10000
 #define TM_PAGE_SIZE	0x10000
+#define IPI_ESB_SHIFT	(16 + 1)
 
 #define IC_BAR_DEFAULT	0x30203100000ull
 #define IC_BAR_SIZE	(8 * IC_PAGE_SIZE)
@@ -1243,9 +1244,6 @@ uint32_t xive_alloc_hw_irqs(uint32_t chip_id, uint32_t count, uint32_t align)
 	}
 	x->int_hw_bot = base;
 
-	/* Adjust the irq source to avoid overlaps */
-	adjust_irq_source(&x->ipis.is, base - x->int_base);
-
 	/* Initialize the corresponding IVT entries to sane defaults,
 	 * IE entry is valid, not routed and masked, EQ data is set
 	 * to the GIRQ number.
@@ -1590,7 +1588,7 @@ static void xive_source_eoi(struct irq_source *is, uint32_t isn)
 	ive = s->xive->ivt_base;
 	if (!ive)
 		return;
-	ive += 	GIRQ_TO_IDX(isn);
+	ive += GIRQ_TO_IDX(isn);
 
 	/* If it's invalid or masked, don't do anything */
 	if ((ive->w & IVE_MASKED) || !(ive->w & IVE_VALID))
@@ -1649,9 +1647,10 @@ static const struct irq_source_ops xive_irq_source_ops = {
 	.attributes = xive_source_attributes,
 };
 
-static void __xive_register_source(struct xive_src *s, uint32_t base,
-				   uint32_t count, uint32_t shift,
-				   void *mmio, uint32_t flags, void *data,
+static void __xive_register_source(struct xive *x, struct xive_src *s,
+				   uint32_t base, uint32_t count,
+				   uint32_t shift, void *mmio, uint32_t flags,
+				   bool secondary, void *data,
 				   const struct irq_source_ops *orig_ops)
 {
 	s->esb_base = base;
@@ -1659,24 +1658,50 @@ static void __xive_register_source(struct xive_src *s, uint32_t base,
 	s->esb_mmio = mmio;
 	s->flags = flags;
 	s->orig_ops = orig_ops;
-
+	s->xive = x;
 	s->is.start = base;
 	s->is.end = base + count;
 	s->is.ops = &xive_irq_source_ops;
 	s->is.data = data;
 
-	__register_irq_source(&s->is);
+	__register_irq_source(&s->is, secondary);
 }
 
-void xive_register_source(uint32_t base, uint32_t count, uint32_t shift,
-			  void *mmio, uint32_t flags, void *data,
-			  const struct irq_source_ops *ops)
+void xive_register_hw_source(uint32_t base, uint32_t count, uint32_t shift,
+			     void *mmio, uint32_t flags, void *data,
+			     const struct irq_source_ops *ops)
 {
 	struct xive_src *s;
+	struct xive *x = xive_from_isn(base);
+
+	assert(x);
 
 	s = malloc(sizeof(struct xive_src));
 	assert(s);
-	__xive_register_source(s, base, count, shift, mmio, flags, data, ops);
+	__xive_register_source(x, s, base, count, shift, mmio, flags,
+			       false, data, ops);
+}
+
+void xive_register_ipi_source(uint32_t base, uint32_t count, void *data,
+			      const struct irq_source_ops *ops)
+{
+	struct xive_src *s;
+	struct xive *x = xive_from_isn(base);
+	uint32_t base_idx = GIRQ_TO_IDX(base);
+	void *mmio_base;
+
+	assert(x);
+	assert(base >= x->int_base && (base + count) <= x->int_ipi_top);
+
+	s = malloc(sizeof(struct xive_src));
+	assert(s);
+
+	/* Callbacks assume the MMIO base corresponds to the first
+	 * interrupt of that source structure so adjust it
+	 */
+	mmio_base = x->esb_mmio + (1ul << IPI_ESB_SHIFT) * base_idx;
+	__xive_register_source(x, s, base, count, IPI_ESB_SHIFT, mmio_base,
+			       XIVE_SRC_EOI_PAGE1, false, data, ops);
 }
 
 static void init_one_xive(struct dt_node *np)
@@ -1744,9 +1769,12 @@ static void init_one_xive(struct dt_node *np)
 
 	/* Register built-in source controllers (aka IPIs) */
 	/* XXX Add new EOI mode for DD2 */
-	__xive_register_source(&x->ipis, x->int_base,
-			       x->int_hw_bot - x->int_base, 16 + 1,
-			       x->esb_mmio, XIVE_SRC_EOI_PAGE1, NULL, NULL);
+	__xive_register_source(x, &x->ipis, x->int_base,
+			       x->int_hw_bot - x->int_base, IPI_ESB_SHIFT,
+			       x->esb_mmio, XIVE_SRC_EOI_PAGE1,
+			       true, NULL, NULL);
+
+	/* XXX Add registration of escalation sources too */
 
 	/* Create a device-tree node for Linux use */
 	xive_create_mmio_dt_node(x);
diff --git a/include/interrupts.h b/include/interrupts.h
index 465073b..6b55099 100644
--- a/include/interrupts.h
+++ b/include/interrupts.h
@@ -291,11 +291,10 @@ struct irq_source {
 	struct list_node		link;
 };
 
-extern void __register_irq_source(struct irq_source *is);
+extern void __register_irq_source(struct irq_source *is, bool secondary);
 extern void register_irq_source(const struct irq_source_ops *ops, void *data,
 				uint32_t start, uint32_t count);
 extern void unregister_irq_source(uint32_t start, uint32_t count);
-extern void adjust_irq_source(struct irq_source *is, uint32_t new_count);
 
 extern uint32_t get_psi_interrupt(uint32_t chip_id);
 
diff --git a/include/xive.h b/include/xive.h
index 367daec..065aa69 100644
--- a/include/xive.h
+++ b/include/xive.h
@@ -376,9 +376,11 @@ uint32_t xive_get_notify_base(uint32_t girq);
 #define XIVE_SRC_LSI		0x00000004	/* No Q bit, no retrigger */
 
 struct irq_source_ops;
-void xive_register_source(uint32_t base, uint32_t count, uint32_t shift,
-			  void *mmio, uint32_t flags, void *data,
-			  const struct irq_source_ops *ops);
+void xive_register_hw_source(uint32_t base, uint32_t count, uint32_t shift,
+			     void *mmio, uint32_t flags, void *data,
+			     const struct irq_source_ops *ops);
+void xive_register_ipi_source(uint32_t base, uint32_t count, void *data,
+			      const struct irq_source_ops *ops);
 
 void xive_cpu_callin(struct cpu_thread *cpu);
 
