Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F171246880
D59259.id185371.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
59 KB
Referenced Files
None
Subscribers
None
D59259.id185371.diff
View Options
diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc
--- a/sys/conf/files.powerpc
+++ b/sys/conf/files.powerpc
@@ -388,7 +388,9 @@
powerpc/powernv/platform_powernv.c optional powernv
powerpc/powernv/powernv_centaur.c optional powernv
powerpc/powernv/powernv_xscom.c optional powernv
-powerpc/powernv/xive.c optional powernv
+powerpc/powernv/xive.c optional powernv | pseries powerpc64 | pseries powerpc64le
+powerpc/powernv/xive_if.m optional powernv | pseries powerpc64 | pseries powerpc64le
+powerpc/powernv/xive_native.c optional powernv
powerpc/powerpc/altivec.c standard
powerpc/powerpc/autoconf.c standard
powerpc/powerpc/bus_machdep.c standard
@@ -461,6 +463,7 @@
powerpc/pseries/rtas_pci.c optional pseries pci
powerpc/pseries/vdevice.c optional pseries powerpc64 | pseries powerpc64le
powerpc/pseries/xics.c optional pseries powerpc64 | pseries powerpc64le
+powerpc/pseries/xive_phyp.c optional pseries powerpc64 | pseries powerpc64le
powerpc/psim/iobus.c optional psim
powerpc/psim/ata_iobus.c optional ata psim
powerpc/psim/openpic_iobus.c optional psim
diff --git a/sys/powerpc/powernv/platform_powernv.c b/sys/powerpc/powernv/platform_powernv.c
--- a/sys/powerpc/powernv/platform_powernv.c
+++ b/sys/powerpc/powernv/platform_powernv.c
@@ -58,8 +58,6 @@
extern void *ap_pcpu;
#endif
-void (*powernv_smp_ap_extra_init)(void);
-
static int powernv_probe(platform_t);
static int powernv_attach(platform_t);
void powernv_mem_regions(platform_t, struct mem_region *phys, int *physsz,
@@ -69,7 +67,6 @@
static int powernv_smp_first_cpu(platform_t, struct cpuref *cpuref);
static int powernv_smp_next_cpu(platform_t, struct cpuref *cpuref);
static int powernv_smp_get_bsp(platform_t, struct cpuref *cpuref);
-static void powernv_smp_ap_init(platform_t);
#ifdef SMP
static int powernv_smp_start_cpu(platform_t, struct pcpu *cpu);
static void powernv_smp_cpu_off(platform_t plat);
@@ -88,7 +85,6 @@
PLATFORMMETHOD(platform_numa_mem_regions, powernv_numa_mem_regions),
PLATFORMMETHOD(platform_timebase_freq, powernv_timebase_freq),
- PLATFORMMETHOD(platform_smp_ap_init, powernv_smp_ap_init),
PLATFORMMETHOD(platform_smp_first_cpu, powernv_smp_first_cpu),
PLATFORMMETHOD(platform_smp_next_cpu, powernv_smp_next_cpu),
PLATFORMMETHOD(platform_smp_get_bsp, powernv_smp_get_bsp),
@@ -571,14 +567,6 @@
opal_call(OPAL_CEC_REBOOT);
}
-static void
-powernv_smp_ap_init(platform_t platform)
-{
-
- if (powernv_smp_ap_extra_init != NULL)
- powernv_smp_ap_extra_init();
-}
-
static void
powernv_cpu_idle(sbintime_t sbt)
{
diff --git a/sys/powerpc/powernv/xive.h b/sys/powerpc/powernv/xive.h
new file mode 100644
--- /dev/null
+++ b/sys/powerpc/powernv/xive.h
@@ -0,0 +1,158 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2019 Justin Hibbits
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ */
+
+#ifndef _POWERPC_POWERNV_XIVE_H_
+#define _POWERPC_POWERNV_XIVE_H_
+
+#include <sys/malloc.h>
+#include <sys/pcpu.h>
+#include <sys/_cpuset.h>
+#include <sys/_mutex.h>
+
+#include <vm/vm.h>
+
+struct resource;
+struct trapframe;
+
+#define XIVE_PRIORITY 7 /* Random non-zero number */
+#define XIVE_NPRIORITIES 8 /* Hardware supports priorities 0-7 */
+#define MAX_XIVE_IRQS (1<<24) /* 24-bit XIRR field */
+
+/* TIMA rings */
+#define XIVE_TM_QW1_OS 0x010 /* Guest OS registers */
+#define XIVE_TM_QW2_HV_POOL 0x020 /* Hypervisor pool registers */
+#define XIVE_TM_QW3_HV 0x030 /* Hypervisor registers */
+
+/* Per-ring registers */
+#define XIVE_TM_NSR 0x00
+#define XIVE_TM_CPPR 0x01
+#define XIVE_TM_IPB 0x02
+#define XIVE_TM_LSMFB 0x03
+#define XIVE_TM_ACK_CNT 0x04
+#define XIVE_TM_INC 0x05
+#define XIVE_TM_AGE 0x06
+#define XIVE_TM_PIPR 0x07
+
+#define TM_WORD0 0x0
+#define TM_WORD2 0x8
+#define TM_QW2W2_VP 0x80000000
+
+/* Special-command offsets from the TIMA base. */
+#define XIVE_TM_SPC_ACK 0x800
+#define TM_QW3NSR_HE_SHIFT 14
+#define TM_QW3_NSR_HE_NONE 0
+#define TM_QW3_NSR_HE_POOL 1
+#define TM_QW3_NSR_HE_PHYS 2
+#define TM_QW3_NSR_HE_LSI 3
+#define XIVE_TM_SPC_PULL_POOL_CTX 0x828
+
+/* Offsets within an ESB page. */
+#define XIVE_IRQ_LOAD_EOI 0x000
+#define XIVE_IRQ_STORE_EOI 0x400
+#define XIVE_IRQ_PQ_00 0xc00
+#define XIVE_IRQ_PQ_01 0xd00
+#define XIVE_IRQ_PQ_10 0xe00
+
+#define XIVE_IRQ_VAL_P 0x02
+#define XIVE_IRQ_VAL_Q 0x01
+
+/* Backend-neutral xive_irq::flags. */
+#define XIVE_IRQ_FLAG_H_INT_ESB 0x00000010
+#define XIVE_IRQ_FLAG_SHIFT_BUG 0x00000008
+#define XIVE_IRQ_FLAG_LSI 0x00000004
+#define XIVE_IRQ_FLAG_STORE_EOI 0x00000002
+#define XIVE_IRQ_FLAG_TRIGGER_PAGE 0x00000001
+
+struct xive_softc {
+ struct mtx sc_mtx;
+ struct resource *sc_mem;
+ vm_size_t sc_prov_page_size;
+ uint32_t sc_offset;
+ uint32_t sc_eq_shift;
+ uint8_t sc_prio;
+};
+
+struct xive_queue {
+ uint32_t *q_page;
+ uint32_t *q_eoi_page;
+ uint32_t q_toggle;
+ uint32_t q_size;
+ uint32_t q_index;
+ uint32_t q_mask;
+};
+
+struct xive_irq {
+ uint32_t girq;
+ uint32_t lirq;
+ uint64_t vp;
+ uint64_t flags;
+ uint8_t prio;
+ bool masked;
+ vm_offset_t eoi_page;
+ vm_offset_t trig_page;
+ vm_size_t esb_size;
+ int chip;
+};
+
+struct xive_cpu {
+ uint64_t vp;
+ uint64_t flags;
+ struct xive_irq ipi_data;
+ struct xive_queue queue;
+ uint64_t cam;
+ uint32_t chip;
+};
+
+MALLOC_DECLARE(M_XIVE);
+DPCPU_DECLARE(struct xive_cpu, xive_cpu_data);
+
+/*
+ * Called by each backend's attach method after MMIO + per-CPU VP setup:
+ * allocates queue pages, sets up IPIs, registers the PIC, and runs
+ * ap_init on the BSP.
+ */
+int xive_core_attach(device_t dev);
+
+/* Populate an IRQ descriptor via XIVE_IRQ_POPULATE; map ESB pages. */
+int xive_init_irq(device_t dev, struct xive_irq *irqd, u_int lisn);
+
+/* MMIO helpers usable by backends (e.g., for pool-ring context ops). */
+uint16_t xive_read_2(struct xive_softc *sc, bus_size_t offset);
+void xive_write_1(struct xive_softc *sc, bus_size_t offset, uint8_t val);
+uint64_t xive_read_mmap8(vm_offset_t addr);
+void xive_write_mmap8(vm_offset_t addr, uint64_t val);
+
+/* PIC method implementations. */
+void xive_bind(device_t, u_int, cpuset_t, void **);
+void xive_dispatch(device_t, struct trapframe *);
+void xive_enable(device_t, u_int, u_int, void **);
+void xive_eoi(device_t, u_int, void *);
+void xive_ipi(device_t, u_int);
+void xive_mask(device_t, u_int, void *);
+void xive_unmask(device_t, u_int, void *);
+void xive_translate_code(device_t, u_int, int, enum intr_trigger *,
+ enum intr_polarity *);
+
+/* Shared per-CPU init: unmask this CPU's IPI and enable interrupts (CPPR). */
+void xive_ap_init_common(device_t);
+
+/*
+ * Base driver_t. xive_native and xive_phyp derive from this via
+ * DEFINE_CLASS_1 and inherit all shared PIC methods (including the
+ * default pic_ap_init = xive_ap_init_common; native overrides).
+ */
+DECLARE_CLASS(xive_driver);
+
+#endif /* _POWERPC_POWERNV_XIVE_H_ */
diff --git a/sys/powerpc/powernv/xive.c b/sys/powerpc/powernv/xive.c
--- a/sys/powerpc/powernv/xive.c
+++ b/sys/powerpc/powernv/xive.c
@@ -25,12 +25,34 @@
* SUCH DAMAGE.
*/
+/*
+ * XIVE Exploitation-mode driver core.
+ *
+ * The XIVE, present in the POWER9 CPU, can run in two modes: XICS emulation
+ * mode, and "Exploitation mode". XICS emulation mode is compatible with the
+ * POWER8 and earlier XICS interrupt controller. Exploitation mode gives us
+ * raw access to the XIVE MMIO for interrupt delivery, improving performance
+ * significantly.
+ *
+ * This file holds the backend-neutral core: PIC KOBJ methods, in-memory
+ * event queue handling, ESB-page MMIO for EOI/mask/unmask, and the shared
+ * attach helper. Two backends supply the source/queue setup differences:
+ *
+ * xive_native.c (powernv) - OPAL calls, hypervisor-privileged.
+ * xive_phyp.c (pseries) - PAPR H_INT_* hypercalls, guest OS.
+ *
+ * Each backend registers XIVE_* methods (see xive_if.m) on its driver
+ * and sets sc->sc_offset to the TIMA ring it operates on (QW3_HV on
+ * powernv, QW1_OS on pseries) before calling xive_core_attach().
+ *
+ * See xive.h for register definitions and struct layouts.
+ */
+
#include <sys/cdefs.h>
#include "opt_platform.h"
#include <sys/param.h>
#include <sys/systm.h>
-#include <sys/module.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/endian.h>
@@ -50,83 +72,25 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
-#ifdef POWERNV
-#include <powerpc/powernv/opal.h>
-#endif
-
+#include "xive.h"
#include "pic_if.h"
+#include "xive_if.h"
-#define XIVE_PRIORITY 7 /* Random non-zero number */
-#define MAX_XIVE_IRQS (1<<24) /* 24-bit XIRR field */
-
-/* Registers */
-#define XIVE_TM_QW1_OS 0x010 /* Guest OS registers */
-#define XIVE_TM_QW2_HV_POOL 0x020 /* Hypervisor pool registers */
-#define XIVE_TM_QW3_HV 0x030 /* Hypervisor registers */
-
-#define XIVE_TM_NSR 0x00
-#define XIVE_TM_CPPR 0x01
-#define XIVE_TM_IPB 0x02
-#define XIVE_TM_LSMFB 0x03
-#define XIVE_TM_ACK_CNT 0x04
-#define XIVE_TM_INC 0x05
-#define XIVE_TM_AGE 0x06
-#define XIVE_TM_PIPR 0x07
-
-#define TM_WORD0 0x0
-#define TM_WORD2 0x8
-#define TM_QW2W2_VP 0x80000000
-
-#define XIVE_TM_SPC_ACK 0x800
-#define TM_QW3NSR_HE_SHIFT 14
-#define TM_QW3_NSR_HE_NONE 0
-#define TM_QW3_NSR_HE_POOL 1
-#define TM_QW3_NSR_HE_PHYS 2
-#define TM_QW3_NSR_HE_LSI 3
-#define XIVE_TM_SPC_PULL_POOL_CTX 0x828
-
-#define XIVE_IRQ_LOAD_EOI 0x000
-#define XIVE_IRQ_STORE_EOI 0x400
-#define XIVE_IRQ_PQ_00 0xc00
-#define XIVE_IRQ_PQ_01 0xd00
-
-#define XIVE_IRQ_VAL_P 0x02
-#define XIVE_IRQ_VAL_Q 0x01
-
-struct xive_softc;
-struct xive_irq;
-
-extern void (*powernv_smp_ap_extra_init)(void);
-
-/* Private support */
-static void xive_setup_cpu(void);
-static void xive_smp_cpu_startup(void);
-static void xive_init_irq(struct xive_irq *irqd, u_int irq);
-static struct xive_irq *xive_configure_irq(u_int irq);
-static int xive_provision_page(struct xive_softc *sc);
-
-/* Interfaces */
-static int xive_probe(device_t);
-static int xive_attach(device_t);
-static int xics_probe(device_t);
-static int xics_attach(device_t);
-
-static void xive_bind(device_t, u_int, cpuset_t, void **);
-static void xive_dispatch(device_t, struct trapframe *);
-static void xive_enable(device_t, u_int, u_int, void **);
-static void xive_eoi(device_t, u_int, void *);
-static void xive_ipi(device_t, u_int);
-static void xive_mask(device_t, u_int, void *);
-static void xive_unmask(device_t, u_int, void *);
-static void xive_translate_code(device_t dev, u_int irq, int code,
- enum intr_trigger *trig, enum intr_polarity *pol);
-
-static device_method_t xive_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, xive_probe),
- DEVMETHOD(device_attach, xive_attach),
-
- /* PIC interface */
+MALLOC_DEFINE(M_XIVE, "xive", "XIVE Memory");
+
+DPCPU_DEFINE(struct xive_cpu, xive_cpu_data);
+
+static int xive_ipi_vector = -1;
+
+static struct xive_irq *xive_configure_irq(device_t dev, u_int lisn);
+
+/*
+ * Base class holding backend-neutral PIC methods. Never attached
+ * directly; xive_native and xive_phyp derive from this via
+ * DEFINE_CLASS_1 in their respective files. pic_ap_init defaults to
+ * xive_ap_init_common; native overrides to add pool-ring context mgmt.
+ */
+static device_method_t xive_methods[] = {
DEVMETHOD(pic_bind, xive_bind),
DEVMETHOD(pic_dispatch, xive_dispatch),
DEVMETHOD(pic_enable, xive_enable),
@@ -135,312 +99,84 @@
DEVMETHOD(pic_mask, xive_mask),
DEVMETHOD(pic_unmask, xive_unmask),
DEVMETHOD(pic_translate_code, xive_translate_code),
-
- DEVMETHOD_END
-};
-
-static device_method_t xics_methods[] = {
- /* Device interface */
- DEVMETHOD(device_probe, xics_probe),
- DEVMETHOD(device_attach, xics_attach),
-
+ DEVMETHOD(pic_ap_init, xive_ap_init_common),
DEVMETHOD_END
};
-struct xive_softc {
- struct mtx sc_mtx;
- struct resource *sc_mem;
- vm_size_t sc_prov_page_size;
- uint32_t sc_offset;
-};
-
-struct xive_queue {
- uint32_t *q_page;
- uint32_t *q_eoi_page;
- uint32_t q_toggle;
- uint32_t q_size;
- uint32_t q_index;
- uint32_t q_mask;
-};
-
-struct xive_irq {
- uint32_t girq;
- uint32_t lirq;
- uint64_t vp;
- uint64_t flags;
-#define OPAL_XIVE_IRQ_SHIFT_BUG 0x00000008
-#define OPAL_XIVE_IRQ_LSI 0x00000004
-#define OPAL_XIVE_IRQ_STORE_EOI 0x00000002
-#define OPAL_XIVE_IRQ_TRIGGER_PAGE 0x00000001
- uint8_t prio;
- vm_offset_t eoi_page;
- vm_offset_t trig_page;
- vm_size_t esb_size;
- int chip;
-};
-
-struct xive_cpu {
- uint64_t vp;
- uint64_t flags;
- struct xive_irq ipi_data;
- struct xive_queue queue; /* We only use a single queue for now. */
- uint64_t cam;
- uint32_t chip;
-};
-
-static driver_t xive_driver = {
- "xive",
- xive_methods,
- sizeof(struct xive_softc)
-};
-
-static driver_t xics_driver = {
- "xivevc",
- xics_methods,
- 0
-};
-
-EARLY_DRIVER_MODULE(xive, ofwbus, xive_driver, 0, 0, BUS_PASS_INTERRUPT - 1);
-EARLY_DRIVER_MODULE(xivevc, ofwbus, xics_driver, 0, 0, BUS_PASS_INTERRUPT);
-
-MALLOC_DEFINE(M_XIVE, "xive", "XIVE Memory");
-
-DPCPU_DEFINE_STATIC(struct xive_cpu, xive_cpu_data);
-
-static int xive_ipi_vector = -1;
+DEFINE_CLASS_0(xive, xive_driver, xive_methods, sizeof(struct xive_softc));
/*
- * XIVE Exploitation mode driver.
- *
- * The XIVE, present in the POWER9 CPU, can run in two modes: XICS emulation
- * mode, and "Exploitation mode". XICS emulation mode is compatible with the
- * POWER8 and earlier XICS interrupt controller, using OPAL calls to emulate
- * hypervisor calls and memory accesses. Exploitation mode gives us raw access
- * to the XIVE MMIO, improving performance significantly.
- *
- * The XIVE controller is a very bizarre interrupt controller. It uses queues
- * in memory to pass interrupts around, and maps itself into 512GB of physical
- * device address space, giving each interrupt in the system one or more pages
- * of address space. An IRQ is tied to a virtual processor, which could be a
- * physical CPU thread, or a guest CPU thread (LPAR running on a physical
- * thread). Thus, the controller can route interrupts directly to guest OSes
- * bypassing processing by the hypervisor, thereby improving performance of the
- * guest OS.
- *
- * An IRQ, in addition to being tied to a virtual processor, has one or two
- * page mappings: an EOI page, and an optional trigger page. The trigger page
- * could be the same as the EOI page. Level-sensitive interrupts (LSIs) don't
- * have a trigger page, as they're external interrupts controlled by physical
- * lines. MSIs and IPIs have trigger pages. An IPI is really just another IRQ
- * in the XIVE, which is triggered by software.
- *
- * An interesting behavior of the XIVE controller is that oftentimes the
- * contents of an address location don't actually matter, but the direction of
- * the action is the signifier (read vs write), and the address is significant.
- * Hence, masking and unmasking an interrupt is done by reading different
- * addresses in the EOI page, and triggering an interrupt consists of writing to
- * the trigger page.
- *
- * Additionally, the MMIO region mapped is CPU-sensitive, just like the
- * per-processor register space (private access) in OpenPIC. In order for a CPU
- * to receive interrupts it must itself configure its CPPR (Current Processor
- * Priority Register), it cannot be set by any other processor. This
- * necessitates the xive_smp_cpu_startup() function.
- *
- * Queues are pages of memory, sized powers-of-two, that are shared with the
- * XIVE. The XIVE writes into the queue with an alternating polarity bit, which
- * flips when the queue wraps.
- */
-
-/*
- * Offset-based read/write interfaces.
+ * Offset-based read/write into the current TIMA ring.
*/
-static uint16_t
+uint16_t
xive_read_2(struct xive_softc *sc, bus_size_t offset)
{
return (bus_read_2(sc->sc_mem, sc->sc_offset + offset));
}
-static void
+void
xive_write_1(struct xive_softc *sc, bus_size_t offset, uint8_t val)
{
bus_write_1(sc->sc_mem, sc->sc_offset + offset, val);
}
-/* EOI and Trigger page access interfaces. */
-static uint64_t
+/* EOI and trigger page access. */
+uint64_t
xive_read_mmap8(vm_offset_t addr)
{
return (*(volatile uint64_t *)addr);
}
-static void
+void
xive_write_mmap8(vm_offset_t addr, uint64_t val)
{
*(uint64_t *)(addr) = val;
}
-/* Device interfaces. */
-static int
-xive_probe(device_t dev)
+/*
+ * ESB accessors. A source normally exposes its ESB as MMIO pages that we
+ * map at setup time, but PAPR may hand back a source with no MMIO at all
+ * (QEMU does this for every LSI), whose state bits are only reachable
+ * through the backend.
+ */
+static uint64_t
+xive_esb_read(device_t dev, struct xive_irq *irqd, uint32_t offset)
{
- if (!ofw_bus_is_compatible(dev, "ibm,opal-xive-pe"))
- return (ENXIO);
-
- device_set_desc(dev, "External Interrupt Virtualization Engine");
+ if (irqd->flags & XIVE_IRQ_FLAG_H_INT_ESB)
+ return (XIVE_ESB_OP(dev, irqd, offset, 0, false));
- /* Make sure we always win against the xicp driver. */
- return (BUS_PROBE_DEFAULT);
+ return (xive_read_mmap8(irqd->eoi_page + offset));
}
-static int
-xics_probe(device_t dev)
+static void
+xive_esb_write(device_t dev, struct xive_irq *irqd, uint32_t offset,
+ uint64_t val)
{
- if (!ofw_bus_is_compatible(dev, "ibm,opal-xive-vc"))
- return (ENXIO);
-
- device_set_desc(dev, "External Interrupt Virtualization Engine Root");
- return (BUS_PROBE_DEFAULT);
-}
-
-static int
-xive_attach(device_t dev)
-{
- struct xive_softc *sc = device_get_softc(dev);
- struct xive_cpu *xive_cpud;
- phandle_t phandle = ofw_bus_get_node(dev);
- int64_t vp_block;
- int error;
- int rid;
- int i, order;
- uint64_t vp_id;
- int64_t ipi_irq;
-
- opal_call(OPAL_XIVE_RESET, OPAL_XIVE_XICS_MODE_EXP);
-
- error = OF_getencprop(phandle, "ibm,xive-provision-page-size",
- (pcell_t *)&sc->sc_prov_page_size, sizeof(sc->sc_prov_page_size));
-
- rid = 1; /* Get the Hypervisor-level register set. */
- sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
- &rid, RF_ACTIVE);
- sc->sc_offset = XIVE_TM_QW3_HV;
-
- mtx_init(&sc->sc_mtx, "XIVE", NULL, MTX_DEF);
-
- /* Workaround for qemu single-thread powernv */
- if (mp_maxid == 0)
- order = 1;
+ if (irqd->flags & XIVE_IRQ_FLAG_H_INT_ESB)
+ XIVE_ESB_OP(dev, irqd, offset, val, true);
else
- order = fls(mp_maxid + (mp_maxid - 1)) - 1;
-
- do {
- vp_block = opal_call(OPAL_XIVE_ALLOCATE_VP_BLOCK, order);
- if (vp_block == OPAL_BUSY)
- DELAY(10);
- else if (vp_block == OPAL_XIVE_PROVISIONING)
- xive_provision_page(sc);
- else
- break;
- } while (1);
-
- if (vp_block < 0) {
- device_printf(dev,
- "Unable to allocate VP block. Opal error %d\n",
- (int)vp_block);
- bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->sc_mem);
- return (ENXIO);
- }
-
- /*
- * Set up the VPs. Try to do as much as we can in attach, to lessen
- * what's needed at AP spawn time.
- */
- CPU_FOREACH(i) {
- vp_id = pcpu_find(i)->pc_hwref;
-
- xive_cpud = DPCPU_ID_PTR(i, xive_cpu_data);
- xive_cpud->vp = vp_id + vp_block;
- opal_call(OPAL_XIVE_GET_VP_INFO, xive_cpud->vp, NULL,
- vtophys(&xive_cpud->cam), NULL, vtophys(&xive_cpud->chip));
-
- xive_cpud->cam = be64toh(xive_cpud->cam);
- xive_cpud->chip = be64toh(xive_cpud->chip);
-
- /* Allocate the queue page and populate the queue state data. */
- xive_cpud->queue.q_page = contigmalloc(PAGE_SIZE, M_XIVE,
- M_ZERO | M_WAITOK, 0, BUS_SPACE_MAXADDR, PAGE_SIZE, 0);
- xive_cpud->queue.q_size = 1 << PAGE_SHIFT;
- xive_cpud->queue.q_mask =
- ((xive_cpud->queue.q_size / sizeof(int)) - 1);
- xive_cpud->queue.q_toggle = 0;
- xive_cpud->queue.q_index = 0;
- do {
- error = opal_call(OPAL_XIVE_SET_VP_INFO, xive_cpud->vp,
- OPAL_XIVE_VP_ENABLED, 0);
- } while (error == OPAL_BUSY);
- error = opal_call(OPAL_XIVE_SET_QUEUE_INFO, vp_id,
- XIVE_PRIORITY, vtophys(xive_cpud->queue.q_page), PAGE_SHIFT,
- OPAL_XIVE_EQ_ALWAYS_NOTIFY | OPAL_XIVE_EQ_ENABLED);
-
- do {
- ipi_irq = opal_call(OPAL_XIVE_ALLOCATE_IRQ,
- xive_cpud->chip);
- } while (ipi_irq == OPAL_BUSY);
-
- if (ipi_irq < 0)
- device_printf(root_pic,
- "Failed allocating IPI. OPAL error %d\n",
- (int)ipi_irq);
- else {
- xive_init_irq(&xive_cpud->ipi_data, ipi_irq);
- xive_cpud->ipi_data.vp = vp_id;
- xive_cpud->ipi_data.lirq = MAX_XIVE_IRQS;
- opal_call(OPAL_XIVE_SET_IRQ_CONFIG, ipi_irq,
- xive_cpud->ipi_data.vp, XIVE_PRIORITY,
- MAX_XIVE_IRQS);
- }
- }
-
- powerpc_register_pic(dev, OF_xref_from_node(phandle), MAX_XIVE_IRQS,
- 1 /* Number of IPIs */, FALSE);
- root_pic = dev;
-
- xive_setup_cpu();
- powernv_smp_ap_extra_init = xive_smp_cpu_startup;
-
- return (0);
-}
-
-static int
-xics_attach(device_t dev)
-{
- phandle_t phandle = ofw_bus_get_node(dev);
-
- /* The XIVE (root PIC) will handle all our interrupts */
- powerpc_register_pic(root_pic, OF_xref_from_node(phandle),
- MAX_XIVE_IRQS, 1 /* Number of IPIs */, FALSE);
-
- return (0);
+ xive_write_mmap8(irqd->eoi_page + offset, val);
}
/*
* PIC I/F methods.
*/
-static void
+void
xive_bind(device_t dev, u_int irq, cpuset_t cpumask, void **priv)
{
+ struct xive_softc *sc = device_get_softc(dev);
struct xive_irq *irqd;
- int cpu;
- int ncpus, i, error;
+ int cpu, ncpus, i, error;
if (*priv == NULL)
- *priv = xive_configure_irq(irq);
+ *priv = xive_configure_irq(dev, irq);
+ if (*priv == NULL)
+ return;
irqd = *priv;
@@ -462,13 +198,10 @@
ncpus++;
}
- opal_call(OPAL_XIVE_SYNC, OPAL_XIVE_SYNC_QUEUE, irq);
+ XIVE_SYNC(dev, irq);
- irqd->vp = pcpu_find(cpu)->pc_hwref;
- error = opal_call(OPAL_XIVE_SET_IRQ_CONFIG, irq, irqd->vp,
- XIVE_PRIORITY, irqd->lirq);
-
- if (error < 0)
+ error = XIVE_IRQ_CONFIGURE(dev, irq, cpu, sc->sc_prio, irqd->lirq);
+ if (error != 0)
panic("Cannot bind interrupt %d to CPU %d", irq, cpu);
xive_eoi(dev, irq, irqd);
@@ -492,7 +225,7 @@
return (i & 0x7fffffff);
}
-static void
+void
xive_dispatch(device_t dev, struct trapframe *tf)
{
struct xive_softc *sc;
@@ -541,11 +274,12 @@
xive_write_1(sc, XIVE_TM_CPPR, 0xff);
}
-static void
+void
xive_enable(device_t dev, u_int irq, u_int vector, void **priv)
{
+ struct xive_softc *sc = device_get_softc(dev);
struct xive_irq *irqd;
- cell_t status, cpu;
+ int cpu, error;
if (irq == MAX_XIVE_IRQS) {
if (xive_ipi_vector == -1)
@@ -553,30 +287,32 @@
return;
}
if (*priv == NULL)
- *priv = xive_configure_irq(irq);
+ *priv = xive_configure_irq(dev, irq);
+ if (*priv == NULL)
+ return;
irqd = *priv;
/* Bind to this CPU to start */
- cpu = PCPU_GET(hwref);
+ cpu = PCPU_GET(cpuid);
irqd->lirq = vector;
for (;;) {
- status = opal_call(OPAL_XIVE_SET_IRQ_CONFIG, irq, cpu,
- XIVE_PRIORITY, vector);
- if (status != OPAL_BUSY)
+ error = XIVE_IRQ_CONFIGURE(dev, irq, cpu, sc->sc_prio,
+ vector);
+ if (error != EBUSY)
break;
DELAY(10);
}
- if (status != 0)
- panic("OPAL_SET_XIVE IRQ %d -> cpu %d failed: %d", irq,
- cpu, status);
+ if (error != 0)
+ panic("XIVE irq_configure %d -> cpu %d failed: %d", irq,
+ cpu, error);
xive_unmask(dev, irq, *priv);
}
-static void
+void
xive_eoi(device_t dev, u_int irq, void *priv)
{
struct xive_irq *rirq;
@@ -589,18 +325,27 @@
} else
rirq = priv;
- if (rirq->flags & OPAL_XIVE_IRQ_STORE_EOI)
- xive_write_mmap8(rirq->eoi_page + XIVE_IRQ_STORE_EOI, 0);
- else if (rirq->flags & OPAL_XIVE_IRQ_LSI)
- xive_read_mmap8(rirq->eoi_page + XIVE_IRQ_LOAD_EOI);
- else {
- eoi_val = xive_read_mmap8(rirq->eoi_page + XIVE_IRQ_PQ_00);
+ if (rirq->masked)
+ return;
+
+ if (rirq->flags & XIVE_IRQ_FLAG_STORE_EOI)
+ xive_esb_write(dev, rirq, XIVE_IRQ_STORE_EOI, 0);
+ else if (rirq->flags & XIVE_IRQ_FLAG_LSI) {
+ /*
+ * LSIs are re-triggered in hardware while still asserted, so
+ * the EOI cycle is all that is needed. That matters for an
+ * H_INT_ESB source, which has no trigger page to re-arm by
+ * hand and is always an LSI in practice.
+ */
+ xive_esb_read(dev, rirq, XIVE_IRQ_LOAD_EOI);
+ } else {
+ eoi_val = xive_esb_read(dev, rirq, XIVE_IRQ_PQ_00);
if ((eoi_val & XIVE_IRQ_VAL_Q) && rirq->trig_page != 0)
xive_write_mmap8(rirq->trig_page, 0);
}
}
-static void
+void
xive_ipi(device_t dev, u_int cpu)
{
struct xive_cpu *xive_cpud;
@@ -612,7 +357,7 @@
xive_write_mmap8(xive_cpud->ipi_data.trig_page, 0);
}
-static void
+void
xive_mask(device_t dev, u_int irq, void *priv)
{
struct xive_irq *rirq;
@@ -623,22 +368,28 @@
rirq = priv;
- if (!(rirq->flags & OPAL_XIVE_IRQ_LSI))
+ if (!(rirq->flags & XIVE_IRQ_FLAG_LSI))
return;
- xive_read_mmap8(rirq->eoi_page + XIVE_IRQ_PQ_01);
+ xive_esb_read(dev, rirq, XIVE_IRQ_PQ_10);
+ rirq->masked = true;
}
-static void
+void
xive_unmask(device_t dev, u_int irq, void *priv)
{
struct xive_irq *rirq;
rirq = priv;
- xive_read_mmap8(rirq->eoi_page + XIVE_IRQ_PQ_00);
+ if (rirq->masked) {
+ rirq->masked = false;
+ xive_esb_read(dev, rirq, XIVE_IRQ_LOAD_EOI);
+ return;
+ }
+ xive_esb_read(dev, rirq, XIVE_IRQ_PQ_00);
}
-static void
+void
xive_translate_code(device_t dev, u_int irq, int code,
enum intr_trigger *trig, enum intr_polarity *pol)
{
@@ -659,57 +410,50 @@
}
}
-/* Private functions. */
/*
- * Setup the current CPU. Called by the BSP at driver attachment, and by each
- * AP at wakeup (via xive_smp_cpu_startup()).
+ * Per-CPU init shared by both backends. Native's pic_ap_init wraps this
+ * with pool-ring context management first; phyp uses it directly.
*/
-static void
-xive_setup_cpu(void)
+void
+xive_ap_init_common(device_t dev)
{
- struct xive_softc *sc;
- struct xive_cpu *cpup;
- uint32_t val;
+ struct xive_softc *sc = device_get_softc(dev);
+ struct xive_cpu *cpup = DPCPU_PTR(xive_cpu_data);
- cpup = DPCPU_PTR(xive_cpu_data);
+ xive_unmask(dev, cpup->ipi_data.girq, &cpup->ipi_data);
+ xive_write_1(sc, XIVE_TM_CPPR, 0xff);
+}
- sc = device_get_softc(root_pic);
+/*
+ * Populate an IRQ structure and map its ESB pages into KVA.
+ * The backend's irq_populate leaves eoi_page/trig_page as physical
+ * addresses; we pmap_mapdev them into KVA before storing back.
+ */
+int
+xive_init_irq(device_t dev, struct xive_irq *irqd, u_int lisn)
+{
+ vm_paddr_t eoi_phys, trig_phys;
+ int error;
- val = bus_read_4(sc->sc_mem, XIVE_TM_QW2_HV_POOL + TM_WORD2);
- if (val & TM_QW2W2_VP)
- bus_read_8(sc->sc_mem, XIVE_TM_SPC_PULL_POOL_CTX);
+ bzero(irqd, sizeof(*irqd));
- bus_write_4(sc->sc_mem, XIVE_TM_QW2_HV_POOL + TM_WORD0, 0xff);
- bus_write_4(sc->sc_mem, XIVE_TM_QW2_HV_POOL + TM_WORD2,
- TM_QW2W2_VP | cpup->cam);
+ error = XIVE_IRQ_POPULATE(dev, irqd, lisn);
+ if (error != 0)
+ return (error);
+ irqd->girq = lisn;
- xive_unmask(root_pic, cpup->ipi_data.girq, &cpup->ipi_data);
- xive_write_1(sc, XIVE_TM_CPPR, 0xff);
-}
+ /* Nothing to map: the backend reaches this source's ESB for us. */
+ if (irqd->flags & XIVE_IRQ_FLAG_H_INT_ESB) {
+ irqd->eoi_page = 0;
+ irqd->trig_page = 0;
+ return (0);
+ }
-/* Populate an IRQ structure, mapping the EOI and trigger pages. */
-static void
-xive_init_irq(struct xive_irq *irqd, u_int irq)
-{
- uint64_t eoi_phys, trig_phys;
- uint32_t esb_shift;
-
- opal_call(OPAL_XIVE_GET_IRQ_INFO, irq,
- vtophys(&irqd->flags), vtophys(&eoi_phys),
- vtophys(&trig_phys), vtophys(&esb_shift),
- vtophys(&irqd->chip));
-
- irqd->flags = be64toh(irqd->flags);
- eoi_phys = be64toh(eoi_phys);
- trig_phys = be64toh(trig_phys);
- esb_shift = be32toh(esb_shift);
- irqd->chip = be32toh(irqd->chip);
-
- irqd->girq = irq;
- irqd->esb_size = 1 << esb_shift;
- irqd->eoi_page = (vm_offset_t)pmap_mapdev(eoi_phys, irqd->esb_size);
+ eoi_phys = (vm_paddr_t)irqd->eoi_page;
+ trig_phys = (vm_paddr_t)irqd->trig_page;
- if (eoi_phys == trig_phys)
+ irqd->eoi_page = (vm_offset_t)pmap_mapdev(eoi_phys, irqd->esb_size);
+ if (trig_phys == eoi_phys)
irqd->trig_page = irqd->eoi_page;
else if (trig_phys != 0)
irqd->trig_page = (vm_offset_t)pmap_mapdev(trig_phys,
@@ -717,53 +461,96 @@
else
irqd->trig_page = 0;
- opal_call(OPAL_XIVE_GET_IRQ_CONFIG, irq, vtophys(&irqd->vp),
- vtophys(&irqd->prio), vtophys(&irqd->lirq));
-
- irqd->vp = be64toh(irqd->vp);
- irqd->prio = be64toh(irqd->prio);
- irqd->lirq = be32toh(irqd->lirq);
+ return (0);
}
-/* Allocate an IRQ struct before populating it. */
+/* Allocate an IRQ struct and populate it. NULL if the source is unusable. */
static struct xive_irq *
-xive_configure_irq(u_int irq)
+xive_configure_irq(device_t dev, u_int lisn)
{
struct xive_irq *irqd;
- irqd = malloc(sizeof(struct xive_irq), M_XIVE, M_WAITOK);
-
- xive_init_irq(irqd, irq);
+ irqd = malloc(sizeof(*irqd), M_XIVE, M_WAITOK);
+ if (xive_init_irq(dev, irqd, lisn) != 0) {
+ free(irqd, M_XIVE);
+ return (NULL);
+ }
return (irqd);
}
/*
- * Part of the OPAL API. OPAL_XIVE_ALLOCATE_VP_BLOCK might require more pages,
- * provisioned through this call.
+ * Shared attach helper. Backend attach:
+ * - claims its DT node and MMIO resource,
+ * - resets the controller and does any per-CPU VP setup,
+ * - sets sc->sc_offset (TIMA ring),
+ * - calls xive_core_attach() which handles queue pages, IPI setup,
+ * powerpc_register_pic(), and running ap_init on the BSP.
+ * The backend's XIVE_* methods are dispatched via KOBJ from the driver.
*/
-static int
-xive_provision_page(struct xive_softc *sc)
+int
+xive_core_attach(device_t dev)
{
- void *prov_page;
- int error;
+ struct xive_softc *sc = device_get_softc(dev);
+ struct xive_cpu *xive_cpud;
+ phandle_t phandle = ofw_bus_get_node(dev);
+ vm_size_t qsize;
+ u_int ipi_lisn;
+ int error, i;
- do {
- prov_page = contigmalloc(sc->sc_prov_page_size, M_XIVE, 0,
- 0, BUS_SPACE_MAXADDR,
- sc->sc_prov_page_size, sc->sc_prov_page_size);
+ mtx_init(&sc->sc_mtx, "XIVE", NULL, MTX_DEF);
- error = opal_call(OPAL_XIVE_DONATE_PAGE, -1,
- vtophys(prov_page));
- } while (error == OPAL_XIVE_PROVISIONING);
+ CPU_FOREACH(i) {
+ xive_cpud = DPCPU_ID_PTR(i, xive_cpu_data);
- return (0);
-}
+ /*
+ * Allocate the queue page and populate the queue state. The
+ * queue must be naturally aligned to its own size, which is
+ * not necessarily PAGE_SIZE: PAPR advertises the sizes it
+ * accepts, and KVM only takes 64K.
+ */
+ qsize = 1UL << sc->sc_eq_shift;
+ xive_cpud->queue.q_page = contigmalloc(qsize, M_XIVE,
+ M_ZERO | M_WAITOK, 0, BUS_SPACE_MAXADDR, qsize, 0);
+ xive_cpud->queue.q_size = qsize;
+ xive_cpud->queue.q_mask =
+ ((xive_cpud->queue.q_size / sizeof(int)) - 1);
+ xive_cpud->queue.q_toggle = 0;
+ xive_cpud->queue.q_index = 0;
-/* The XIVE_TM_CPPR register must be set by each thread */
-static void
-xive_smp_cpu_startup(void)
-{
+ error = XIVE_QUEUE_CONFIG(dev, xive_cpud, i,
+ vtophys(xive_cpud->queue.q_page), sc->sc_eq_shift,
+ sc->sc_prio);
+ if (error != 0)
+ device_printf(dev,
+ "queue_config CPU %d failed: %d\n", i, error);
+
+ error = XIVE_IPI_ALLOC(dev, xive_cpud, i, &ipi_lisn);
+ if (error != 0) {
+ device_printf(dev,
+ "IPI alloc for CPU %d failed: %d\n", i, error);
+ continue;
+ }
+
+ error = xive_init_irq(dev, &xive_cpud->ipi_data, ipi_lisn);
+ if (error != 0) {
+ device_printf(dev,
+ "IPI init for CPU %d failed: %d\n", i, error);
+ continue;
+ }
+ xive_cpud->ipi_data.lirq = MAX_XIVE_IRQS;
+ error = XIVE_IRQ_CONFIGURE(dev, ipi_lisn, i, sc->sc_prio,
+ MAX_XIVE_IRQS);
+ if (error != 0)
+ device_printf(dev,
+ "IPI configure for CPU %d failed: %d\n", i, error);
+ }
- xive_setup_cpu();
+ powerpc_register_pic(dev, OF_xref_from_node(phandle), MAX_XIVE_IRQS,
+ 1 /* Number of IPIs */, FALSE);
+ root_pic = dev;
+
+ PIC_AP_INIT(dev);
+
+ return (0);
}
diff --git a/sys/powerpc/powernv/xive_if.m b/sys/powerpc/powernv/xive_if.m
new file mode 100644
--- /dev/null
+++ b/sys/powerpc/powernv/xive_if.m
@@ -0,0 +1,90 @@
+#-
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright 2026 Justin Hibbits
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+
+CODE {
+#include <sys/bus.h>
+#include <sys/types.h>
+
+#include <vm/vm.h>
+
+struct xive_cpu;
+struct xive_irq;
+}
+
+# Backend contract for the shared XIVE core (xive.c). Two backends
+# implement this interface: xive_native (OPAL, powernv) and xive_phyp
+# (PAPR H_INT_*, pseries). Every call but esb_op runs on a cold path
+# (attach, IRQ setup, IRQ bind), and esb_op is only reached by sources
+# that already pay for a hypercall, so KOBJ dispatch is never the cost
+# that matters.
+#
+# Backends translate the FreeBSD cpuid to whatever target encoding
+# their transport requires. Return 0 on success, EBUSY when a retry
+# is warranted, or another errno-like value on failure.
+INTERFACE xive;
+
+# Bind a queue page to (cpu, prio) so the controller can post events.
+METHOD int queue_config {
+ device_t dev;
+ struct xive_cpu *cpud;
+ int cpu;
+ vm_paddr_t qpage;
+ int qshift;
+ uint8_t prio;
+};
+
+# Allocate a LISN suitable for use as this CPU's IPI.
+METHOD int ipi_alloc {
+ device_t dev;
+ struct xive_cpu *cpud;
+ int cpu;
+ u_int *lisn_out;
+};
+
+# Fill irqd from the controller: flags, EOI/trigger page phys addrs,
+# esb_shift, chip, current target/prio/sw_irq.
+METHOD int irq_populate {
+ device_t dev;
+ struct xive_irq *irqd;
+ u_int lisn;
+};
+
+# Route lisn to (cpu, prio) with the given sw_irq (logical) tag.
+METHOD int irq_configure {
+ device_t dev;
+ u_int lisn;
+ int cpu;
+ uint8_t prio;
+ uint32_t sw_irq;
+};
+
+# Wait for pending events on lisn to drain.
+METHOD int sync {
+ device_t dev;
+ u_int lisn;
+};
+
+# Load from or store to a source's ESB, for sources that expose no ESB
+# MMIO and must be driven by the transport instead. Only called for irqs
+# flagged XIVE_IRQ_FLAG_H_INT_ESB, so a backend that never sets that flag
+# need not implement this. Returns the loaded value, 0 for a store, or
+# ~0 if the operation failed.
+METHOD uint64_t esb_op {
+ device_t dev;
+ struct xive_irq *irqd;
+ uint32_t offset;
+ uint64_t data;
+ bool store;
+};
diff --git a/sys/powerpc/powernv/xive_native.c b/sys/powerpc/powernv/xive_native.c
new file mode 100644
--- /dev/null
+++ b/sys/powerpc/powernv/xive_native.c
@@ -0,0 +1,351 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2019 Justin Hibbits
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * XIVE native (OPAL) backend for POWERNV.
+ *
+ * Attaches to "ibm,opal-xive-pe" (presenter) and "ibm,opal-xive-vc"
+ * (virtualization controller root). Provides the backend ops that
+ * the shared XIVE core in xive.c calls; supplies pool-ring context
+ * management in its own pic_ap_init.
+ */
+
+#include <sys/cdefs.h>
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/intr_machdep.h>
+#include <machine/md_var.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <powerpc/powernv/opal.h>
+
+#include "xive.h"
+#include "pic_if.h"
+#include "xive_if.h"
+
+static int xive_native_probe(device_t);
+static int xive_native_attach(device_t);
+static int xive_native_vc_probe(device_t);
+static int xive_native_vc_attach(device_t);
+static void xive_native_ap_init(device_t);
+static int xive_native_provision_page(struct xive_softc *sc);
+
+static int xive_native_queue_config(device_t dev, struct xive_cpu *cpud,
+ int cpu, vm_paddr_t qpage, int qshift, uint8_t prio);
+static int xive_native_ipi_alloc(device_t dev, struct xive_cpu *cpud,
+ int cpu, u_int *lisn_out);
+static int xive_native_irq_populate(device_t dev, struct xive_irq *irqd,
+ u_int lisn);
+static int xive_native_irq_configure(device_t dev, u_int lisn, int cpu,
+ uint8_t prio, uint32_t sw_irq);
+static int xive_native_sync(device_t dev, u_int lisn);
+
+static device_method_t xive_native_methods[] = {
+ DEVMETHOD(device_probe, xive_native_probe),
+ DEVMETHOD(device_attach, xive_native_attach),
+
+ /* Override the base's default pic_ap_init for pool-ring context. */
+ DEVMETHOD(pic_ap_init, xive_native_ap_init),
+
+ DEVMETHOD(xive_queue_config, xive_native_queue_config),
+ DEVMETHOD(xive_ipi_alloc, xive_native_ipi_alloc),
+ DEVMETHOD(xive_irq_populate, xive_native_irq_populate),
+ DEVMETHOD(xive_irq_configure, xive_native_irq_configure),
+ DEVMETHOD(xive_sync, xive_native_sync),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(xive, xive_native_driver, xive_native_methods,
+ sizeof(struct xive_softc), xive_driver);
+
+static device_method_t xive_native_vc_methods[] = {
+ DEVMETHOD(device_probe, xive_native_vc_probe),
+ DEVMETHOD(device_attach, xive_native_vc_attach),
+
+ DEVMETHOD_END
+};
+
+static driver_t xive_native_vc_driver = {
+ "xivevc",
+ xive_native_vc_methods,
+ 0
+};
+
+EARLY_DRIVER_MODULE(xive, ofwbus, xive_native_driver, 0, 0,
+ BUS_PASS_INTERRUPT - 1);
+EARLY_DRIVER_MODULE(xivevc, ofwbus, xive_native_vc_driver, 0, 0,
+ BUS_PASS_INTERRUPT);
+
+static int
+xive_native_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "ibm,opal-xive-pe"))
+ return (ENXIO);
+
+ device_set_desc(dev, "External Interrupt Virtualization Engine");
+
+ /* Make sure we always win against the xicp driver. */
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+xive_native_vc_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "ibm,opal-xive-vc"))
+ return (ENXIO);
+
+ device_set_desc(dev, "External Interrupt Virtualization Engine Root");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+xive_native_attach(device_t dev)
+{
+ struct xive_softc *sc = device_get_softc(dev);
+ struct xive_cpu *xive_cpud;
+ phandle_t phandle = ofw_bus_get_node(dev);
+ int64_t vp_block;
+ int error, rid, i, order;
+ uint64_t vp_id;
+
+ opal_call(OPAL_XIVE_RESET, OPAL_XIVE_XICS_MODE_EXP);
+
+ error = OF_getencprop(phandle, "ibm,xive-provision-page-size",
+ (pcell_t *)&sc->sc_prov_page_size, sizeof(sc->sc_prov_page_size));
+ if (error < 0)
+ sc->sc_prov_page_size = 0;
+
+ rid = 1; /* Get the Hypervisor-level register set. */
+ sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem == NULL) {
+ device_printf(dev, "Unable to allocate HV MMIO resource\n");
+ return (ENXIO);
+ }
+ sc->sc_offset = XIVE_TM_QW3_HV;
+ sc->sc_prio = XIVE_PRIORITY;
+ sc->sc_eq_shift = PAGE_SHIFT;
+
+ /* Workaround for qemu single-thread powernv */
+ if (mp_maxid == 0)
+ order = 1;
+ else
+ order = fls(mp_maxid + (mp_maxid - 1)) - 1;
+
+ for (;;) {
+ vp_block = opal_call(OPAL_XIVE_ALLOCATE_VP_BLOCK, order);
+ if (vp_block == OPAL_BUSY)
+ DELAY(10);
+ else if (vp_block == OPAL_XIVE_PROVISIONING)
+ xive_native_provision_page(sc);
+ else
+ break;
+ }
+
+ if (vp_block < 0) {
+ device_printf(dev,
+ "Unable to allocate VP block. Opal error %d\n",
+ (int)vp_block);
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->sc_mem);
+ return (ENXIO);
+ }
+
+ /* Per-CPU VP setup. Queue setup happens in the shared core. */
+ CPU_FOREACH(i) {
+ vp_id = pcpu_find(i)->pc_hwref;
+ xive_cpud = DPCPU_ID_PTR(i, xive_cpu_data);
+ xive_cpud->vp = vp_id + vp_block;
+
+ opal_call(OPAL_XIVE_GET_VP_INFO, xive_cpud->vp, NULL,
+ vtophys(&xive_cpud->cam), NULL, vtophys(&xive_cpud->chip));
+ xive_cpud->cam = be64toh(xive_cpud->cam);
+ xive_cpud->chip = be64toh(xive_cpud->chip);
+
+ do {
+ error = opal_call(OPAL_XIVE_SET_VP_INFO, xive_cpud->vp,
+ OPAL_XIVE_VP_ENABLED, 0);
+ } while (error == OPAL_BUSY);
+ }
+
+ return (xive_core_attach(dev));
+}
+
+static int
+xive_native_vc_attach(device_t dev)
+{
+ phandle_t phandle = ofw_bus_get_node(dev);
+
+ /* The XIVE (root PIC) will handle all our interrupts. */
+ powerpc_register_pic(root_pic, OF_xref_from_node(phandle),
+ MAX_XIVE_IRQS, 1 /* Number of IPIs */, FALSE);
+
+ return (0);
+}
+
+/*
+ * HV-privileged pic_ap_init: load our own pool VP context before enabling
+ * the CPPR. Runs on the BSP at end of xive_core_attach and on every AP
+ * from mp_machdep.c via PIC_AP_INIT(root_pic).
+ */
+static void
+xive_native_ap_init(device_t dev)
+{
+ struct xive_softc *sc = device_get_softc(dev);
+ struct xive_cpu *cpup = DPCPU_PTR(xive_cpu_data);
+ uint32_t val;
+
+ val = bus_read_4(sc->sc_mem, XIVE_TM_QW2_HV_POOL + TM_WORD2);
+ if (val & TM_QW2W2_VP)
+ bus_read_8(sc->sc_mem, XIVE_TM_SPC_PULL_POOL_CTX);
+
+ bus_write_4(sc->sc_mem, XIVE_TM_QW2_HV_POOL + TM_WORD0, 0xff);
+ bus_write_4(sc->sc_mem, XIVE_TM_QW2_HV_POOL + TM_WORD2,
+ TM_QW2W2_VP | cpup->cam);
+
+ xive_ap_init_common(dev);
+}
+
+/*
+ * XIVE KOBJ methods.
+ */
+static int
+xive_native_queue_config(device_t dev __unused, struct xive_cpu *cpud __unused,
+ int cpu, vm_paddr_t qpage, int qshift, uint8_t prio)
+{
+ uint64_t hwref = pcpu_find(cpu)->pc_hwref;
+ int64_t rc;
+
+ rc = opal_call(OPAL_XIVE_SET_QUEUE_INFO, hwref, prio, qpage, qshift,
+ OPAL_XIVE_EQ_ALWAYS_NOTIFY | OPAL_XIVE_EQ_ENABLED);
+ return (rc == 0 ? 0 : EIO);
+}
+
+static int
+xive_native_ipi_alloc(device_t dev __unused, struct xive_cpu *cpud,
+ int cpu __unused, u_int *lisn_out)
+{
+ int64_t ipi;
+
+ do {
+ ipi = opal_call(OPAL_XIVE_ALLOCATE_IRQ, cpud->chip);
+ } while (ipi == OPAL_BUSY);
+
+ if (ipi < 0)
+ return (EIO);
+ *lisn_out = (u_int)ipi;
+ return (0);
+}
+
+static int
+xive_native_irq_populate(device_t dev __unused, struct xive_irq *irqd,
+ u_int lisn)
+{
+ uint64_t flags, eoi_phys, trig_phys, vp;
+ uint32_t esb_shift, chip, lirq;
+ uint8_t prio;
+
+ opal_call(OPAL_XIVE_GET_IRQ_INFO, lisn,
+ vtophys(&flags), vtophys(&eoi_phys), vtophys(&trig_phys),
+ vtophys(&esb_shift), vtophys(&chip));
+
+ irqd->flags = be64toh(flags); /* backend flags == core flags */
+ irqd->eoi_page = (vm_offset_t)be64toh(eoi_phys);
+ irqd->trig_page = (vm_offset_t)be64toh(trig_phys);
+ irqd->esb_size = 1U << be32toh(esb_shift);
+ irqd->chip = be32toh(chip);
+
+ opal_call(OPAL_XIVE_GET_IRQ_CONFIG, lisn, vtophys(&vp),
+ vtophys(&prio), vtophys(&lirq));
+ irqd->vp = be64toh(vp);
+ irqd->prio = be64toh(prio);
+ irqd->lirq = be32toh(lirq);
+
+ return (0);
+}
+
+static int
+xive_native_irq_configure(device_t dev __unused, u_int lisn, int cpu,
+ uint8_t prio, uint32_t sw_irq)
+{
+ uint64_t hwref = pcpu_find(cpu)->pc_hwref;
+ int64_t rc;
+
+ rc = opal_call(OPAL_XIVE_SET_IRQ_CONFIG, lisn, hwref, prio, sw_irq);
+ if (rc == OPAL_BUSY)
+ return (EBUSY);
+ return (rc == 0 ? 0 : EIO);
+}
+
+static int
+xive_native_sync(device_t dev __unused, u_int lisn)
+{
+
+ opal_call(OPAL_XIVE_SYNC, OPAL_XIVE_SYNC_QUEUE, lisn);
+ return (0);
+}
+
+/*
+ * OPAL_XIVE_ALLOCATE_VP_BLOCK may need pages of memory donated to satisfy
+ * larger block orders. Called from the attach loop on OPAL_XIVE_PROVISIONING.
+ */
+static int
+xive_native_provision_page(struct xive_softc *sc)
+{
+ void *prov_page;
+ int error;
+
+ do {
+ prov_page = contigmalloc(sc->sc_prov_page_size, M_XIVE, 0,
+ 0, BUS_SPACE_MAXADDR,
+ sc->sc_prov_page_size, sc->sc_prov_page_size);
+
+ error = opal_call(OPAL_XIVE_DONATE_PAGE, -1,
+ vtophys(prov_page));
+ } while (error == OPAL_XIVE_PROVISIONING);
+
+ return (0);
+}
diff --git a/sys/powerpc/pseries/phyp-hvcall.h b/sys/powerpc/pseries/phyp-hvcall.h
--- a/sys/powerpc/pseries/phyp-hvcall.h
+++ b/sys/powerpc/pseries/phyp-hvcall.h
@@ -331,9 +331,23 @@
#define H_GET_DMA_XLATES_L 0x324
/* Reserved ... */
#define H_REGISTER_PROC_TBL 0x37c
-#define MAX_HCALL_OPCODE H_REGISTER_PROC_TBL
+/* PAPR XIVE hypercalls (PAPR 2.9+). */
+#define H_INT_GET_SOURCE_INFO 0x3a8
+#define H_INT_SET_SOURCE_CONFIG 0x3ac
+#define H_INT_GET_SOURCE_CONFIG 0x3b0
+#define H_INT_GET_QUEUE_INFO 0x3b4
+#define H_INT_SET_QUEUE_CONFIG 0x3b8
+#define H_INT_GET_QUEUE_CONFIG 0x3bc
+#define H_INT_SET_OS_REPORTING_LINE 0x3c0
+#define H_INT_GET_OS_REPORTING_LINE 0x3c4
+#define H_INT_ESB 0x3c8
+#define H_INT_SYNC 0x3cc
+#define H_INT_RESET 0x3d0
+#define MAX_HCALL_OPCODE H_INT_RESET
int64_t phyp_hcall(uint64_t opcode, ...);
+int64_t phyp_hcall4(uint64_t opcode, uint64_t a1, uint64_t a2, uint64_t a3,
+ uint64_t a4, uint64_t a5, uint64_t a6, uint64_t *out);
int64_t phyp_pft_hcall(uint64_t opcode, uint64_t flags, uint64_t pteidx,
uint64_t pte_hi, uint64_t pte_lo, uint64_t *pteidx_out, uint64_t *ptelo_out,
uint64_t *r6);
diff --git a/sys/powerpc/pseries/phyp-hvcall.S b/sys/powerpc/pseries/phyp-hvcall.S
--- a/sys/powerpc/pseries/phyp-hvcall.S
+++ b/sys/powerpc/pseries/phyp-hvcall.S
@@ -47,6 +47,33 @@
blr /* return r3 = status */
ASEND(phyp_hcall)
+/*
+ * HV call returning four output values in R4-R7.
+ *
+ * C signature:
+ * int64_t phyp_hcall4(uint64_t opcode, uint64_t a1, uint64_t a2,
+ * uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6, uint64_t *out);
+ *
+ * On entry the caller places the output pointer in r10 (the 8th arg).
+ * We stash it, invoke the hypervisor, then write r4..r7 into *out.
+ */
+ASENTRY(phyp_hcall4)
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-64(%r1)
+ std %r10,48(%r1) /* save output pointer */
+ hc /* invoke the hypervisor */
+ ld %r10,48(%r1) /* restore output pointer */
+ std %r4,0(%r10)
+ std %r5,8(%r10)
+ std %r6,16(%r10)
+ std %r7,24(%r10)
+ ld %r1,0(%r1)
+ ld %r0,16(%r1)
+ mtlr %r0
+ blr /* return r3 = status */
+ASEND(phyp_hcall4)
+
/*
* PFT HV calls take a special ABI (see PAPR 14.5.4.1)
*
diff --git a/sys/powerpc/pseries/xive_phyp.c b/sys/powerpc/pseries/xive_phyp.c
new file mode 100644
--- /dev/null
+++ b/sys/powerpc/pseries/xive_phyp.c
@@ -0,0 +1,434 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2026 Justin Hibbits
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * XIVE PAPR (phyp) backend for pSeries.
+ *
+ * Attaches to "ibm,power-ivpe" and uses PAPR H_INT_* hypercalls to drive
+ * the XIVE from a KVM/PowerVM guest. Runs in XIVE-native mode: TIMA and
+ * ESB pages are mapped directly into the guest, only source/queue setup
+ * goes through hypercalls.
+ *
+ * IPI LISNs are drawn from the pool published in "ibm,xive-lisn-ranges".
+ */
+
+#include <sys/cdefs.h>
+#include "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/intr_machdep.h>
+#include <machine/md_var.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <powerpc/powernv/xive.h>
+#include <powerpc/pseries/phyp-hvcall.h>
+
+#include "pic_if.h"
+#include "xive_if.h"
+
+/*
+ * PAPR numbers hypercall flag bits MSB-first, so bit 0 is the sign bit.
+ * The hypervisor rejects any flag word with a bit set outside the small
+ * set it knows about, so these must be exact.
+ */
+#define XIVE_PAPR_BIT(b) (1ULL << (63 - (b)))
+
+/* PAPR XIVE source flags (returned by H_INT_GET_SOURCE_INFO). */
+#define XIVE_SRC_H_INT_ESB XIVE_PAPR_BIT(60)
+#define XIVE_SRC_LSI XIVE_PAPR_BIT(61)
+#define XIVE_SRC_TRIGGER XIVE_PAPR_BIT(62)
+#define XIVE_SRC_STORE_EOI XIVE_PAPR_BIT(63)
+
+/*
+ * H_INT_SET_QUEUE_CONFIG flags. There is no "enable" bit: a non-zero
+ * queue shift enables the queue, a zero shift resets it.
+ */
+#define XIVE_EQ_ALWAYS_NOTIFY XIVE_PAPR_BIT(63)
+
+/* H_INT_SET_SOURCE_CONFIG flags. */
+#define XIVE_SRC_SET_EISN XIVE_PAPR_BIT(62)
+#define XIVE_SRC_MASK XIVE_PAPR_BIT(63)
+
+/* H_INT_ESB flags. */
+#define XIVE_ESB_STORE XIVE_PAPR_BIT(63)
+
+struct xive_phyp_softc {
+ struct xive_softc xps_core; /* Must be first for softc alias. */
+ uint32_t xps_ipi_base;
+ uint32_t xps_ipi_count;
+ uint32_t xps_ipi_next; /* bump allocator */
+};
+
+static int xive_phyp_probe(device_t);
+static int xive_phyp_attach(device_t);
+
+static int xive_phyp_queue_config(device_t dev, struct xive_cpu *cpud,
+ int cpu, vm_paddr_t qpage, int qshift, uint8_t prio);
+static int xive_phyp_ipi_alloc(device_t dev, struct xive_cpu *cpud,
+ int cpu, u_int *lisn_out);
+static int xive_phyp_irq_populate(device_t dev, struct xive_irq *irqd,
+ u_int lisn);
+static int xive_phyp_irq_configure(device_t dev, u_int lisn, int cpu,
+ uint8_t prio, uint32_t sw_irq);
+static int xive_phyp_sync(device_t dev, u_int lisn);
+static uint64_t xive_phyp_esb_op(device_t dev, struct xive_irq *irqd,
+ uint32_t offset, uint64_t data, bool store);
+
+static device_method_t xive_phyp_methods[] = {
+ DEVMETHOD(device_probe, xive_phyp_probe),
+ DEVMETHOD(device_attach, xive_phyp_attach),
+
+ DEVMETHOD(xive_queue_config, xive_phyp_queue_config),
+ DEVMETHOD(xive_ipi_alloc, xive_phyp_ipi_alloc),
+ DEVMETHOD(xive_irq_populate, xive_phyp_irq_populate),
+ DEVMETHOD(xive_irq_configure, xive_phyp_irq_configure),
+ DEVMETHOD(xive_sync, xive_phyp_sync),
+ DEVMETHOD(xive_esb_op, xive_phyp_esb_op),
+
+ DEVMETHOD_END
+};
+
+DEFINE_CLASS_1(xive, xive_phyp_driver, xive_phyp_methods,
+ sizeof(struct xive_phyp_softc), xive_driver);
+
+EARLY_DRIVER_MODULE(xive_phyp, ofwbus, xive_phyp_driver, 0, 0,
+ BUS_PASS_INTERRUPT - 1);
+
+static int
+xive_phyp_probe(device_t dev)
+{
+
+ if (!ofw_bus_is_compatible(dev, "ibm,power-ivpe"))
+ return (ENXIO);
+
+ device_set_desc(dev, "PAPR External Interrupt Virtualization Engine");
+
+ /* Win against xicp when both bindings are present in the DT. */
+ return (BUS_PROBE_DEFAULT);
+}
+
+/*
+ * Pick the least urgent priority the hypervisor has not reserved for itself.
+ * The root node's "ibm,plat-res-int-priorities" lists the reserved ranges as
+ * <start, count> pairs; QEMU reserves 7 and up, so a guest using
+ * XIVE_PRIORITY gets H_P4 back from every H_INT_SET_*_CONFIG.
+ */
+static int
+xive_phyp_pick_priority(device_t dev, uint8_t *priop)
+{
+ pcell_t *ranges;
+ ssize_t nelems;
+ int found, i, prio;
+
+ nelems = OF_getencprop_alloc_multi(OF_finddevice("/"),
+ "ibm,plat-res-int-priorities", sizeof(pcell_t), (void **)&ranges);
+ if (nelems <= 0 || (nelems % 2) != 0) {
+ device_printf(dev,
+ "Missing or malformed ibm,plat-res-int-priorities\n");
+ if (nelems > 0)
+ OF_prop_free(ranges);
+ return (ENXIO);
+ }
+
+ found = -1;
+ for (prio = 0; prio < XIVE_NPRIORITIES; prio++) {
+ for (i = 0; i < nelems; i += 2) {
+ if ((uint32_t)prio >= ranges[i] &&
+ (uint32_t)prio - ranges[i] < ranges[i + 1])
+ break;
+ }
+ if (i == nelems)
+ found = prio;
+ }
+ OF_prop_free(ranges);
+
+ if (found < 0) {
+ device_printf(dev, "All interrupt priorities are reserved\n");
+ return (ENXIO);
+ }
+
+ *priop = found;
+ return (0);
+}
+
+/*
+ * Choose an event queue size from "ibm,xive-eq-sizes", a list of the page
+ * shifts the hypervisor accepts. PAGE_SIZE is not a safe assumption: KVM
+ * only takes 64K queues, and rejects anything else with H_HARDWARE.
+ */
+static int
+xive_phyp_pick_eq_shift(device_t dev, phandle_t node, uint32_t *shiftp)
+{
+ pcell_t *sizes;
+ ssize_t nelems;
+ uint32_t best;
+ int i;
+
+ nelems = OF_getencprop_alloc_multi(node, "ibm,xive-eq-sizes",
+ sizeof(pcell_t), (void **)&sizes);
+ if (nelems <= 0) {
+ device_printf(dev, "Missing ibm,xive-eq-sizes property\n");
+ return (ENXIO);
+ }
+
+ /* Smallest advertised queue that still holds a full page. */
+ best = 0;
+ for (i = 0; i < nelems; i++) {
+ if (sizes[i] < PAGE_SHIFT)
+ continue;
+ if (best == 0 || sizes[i] < best)
+ best = sizes[i];
+ }
+ OF_prop_free(sizes);
+
+ if (best == 0) {
+ device_printf(dev, "No usable ibm,xive-eq-sizes entry\n");
+ return (ENXIO);
+ }
+
+ *shiftp = best;
+ return (0);
+}
+
+static int
+xive_phyp_attach(device_t dev)
+{
+ struct xive_phyp_softc *psc = device_get_softc(dev);
+ struct xive_softc *sc = &psc->xps_core;
+ phandle_t phandle = ofw_bus_get_node(dev);
+ pcell_t lisn_range[2];
+ int rid, error;
+ int64_t rc;
+
+ rc = phyp_hcall(H_INT_RESET, 0ULL);
+ if (rc != H_SUCCESS) {
+ device_printf(dev, "H_INT_RESET failed: %ld\n", (long)rc);
+ return (ENXIO);
+ }
+
+ rid = 1; /* TIMA per-thread page. */
+ sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->sc_mem == NULL) {
+ device_printf(dev, "Unable to allocate TIMA MMIO resource\n");
+ return (ENXIO);
+ }
+ sc->sc_offset = XIVE_TM_QW1_OS;
+
+ error = xive_phyp_pick_priority(dev, &sc->sc_prio);
+ if (error != 0) {
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->sc_mem);
+ return (error);
+ }
+
+ error = xive_phyp_pick_eq_shift(dev, phandle, &sc->sc_eq_shift);
+ if (error != 0) {
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->sc_mem);
+ return (error);
+ }
+
+ /*
+ * Consume the LISN pool for our IPIs. We only need mp_ncpus of them
+ * so a bump allocator over the first range is sufficient.
+ */
+ error = OF_getencprop(phandle, "ibm,xive-lisn-ranges",
+ lisn_range, sizeof(lisn_range));
+ if (error < (int)sizeof(lisn_range)) {
+ device_printf(dev, "Missing ibm,xive-lisn-ranges property\n");
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->sc_mem);
+ return (ENXIO);
+ }
+ psc->xps_ipi_base = lisn_range[0];
+ psc->xps_ipi_count = lisn_range[1];
+ psc->xps_ipi_next = 0;
+
+ if (psc->xps_ipi_count < (uint32_t)mp_ncpus) {
+ device_printf(dev,
+ "LISN pool (%u) smaller than mp_ncpus (%d)\n",
+ psc->xps_ipi_count, mp_ncpus);
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->sc_mem);
+ return (ENXIO);
+ }
+
+ return (xive_core_attach(dev));
+}
+
+/*
+ * XIVE KOBJ methods.
+ */
+static int
+xive_phyp_queue_config(device_t dev, struct xive_cpu *cpud __unused,
+ int cpu, vm_paddr_t qpage, int qshift, uint8_t prio)
+{
+ uint64_t hwref = pcpu_find(cpu)->pc_hwref;
+ int64_t rc;
+
+ rc = phyp_hcall(H_INT_SET_QUEUE_CONFIG, XIVE_EQ_ALWAYS_NOTIFY,
+ hwref, (uint64_t)prio, (uint64_t)qpage, (uint64_t)qshift);
+ if (rc == H_BUSY)
+ return (EBUSY);
+ if (rc != H_SUCCESS) {
+ device_printf(dev, "H_INT_SET_QUEUE_CONFIG(target %#lx, prio "
+ "%u, qpage %#lx, qshift %d) failed: %ld\n", (u_long)hwref,
+ prio, (u_long)qpage, qshift, (long)rc);
+ return (EIO);
+ }
+ return (0);
+}
+
+static int
+xive_phyp_ipi_alloc(device_t dev, struct xive_cpu *cpud __unused,
+ int cpu __unused, u_int *lisn_out)
+{
+ struct xive_phyp_softc *psc = device_get_softc(dev);
+
+ if (psc->xps_ipi_next >= psc->xps_ipi_count)
+ return (ENOSPC);
+ *lisn_out = psc->xps_ipi_base + psc->xps_ipi_next++;
+ return (0);
+}
+
+static int
+xive_phyp_irq_populate(device_t dev, struct xive_irq *irqd, u_int lisn)
+{
+ uint64_t out[4];
+ int64_t rc;
+
+ /*
+ * PAPR H_INT_GET_SOURCE_INFO returns in R4-R7:
+ * out[0]: flags (XIVE_SRC_*)
+ * out[1]: EOI ESB page phys
+ * out[2]: trigger page phys
+ * out[3]: esb shift
+ */
+ rc = phyp_hcall4(H_INT_GET_SOURCE_INFO, 0ULL, (uint64_t)lisn,
+ 0, 0, 0, 0, out);
+ if (rc != H_SUCCESS)
+ return (EIO);
+
+ irqd->flags = 0;
+ if (out[0] & XIVE_SRC_STORE_EOI)
+ irqd->flags |= XIVE_IRQ_FLAG_STORE_EOI;
+ if (out[0] & XIVE_SRC_LSI)
+ irqd->flags |= XIVE_IRQ_FLAG_LSI;
+ if (out[0] & XIVE_SRC_TRIGGER)
+ irqd->flags |= XIVE_IRQ_FLAG_TRIGGER_PAGE;
+ /*
+ * Such a source has no ESB MMIO at all, and reports -1 for both
+ * pages; the core reaches it through XIVE_ESB_OP instead. QEMU sets
+ * this for every LSI so that it can re-trigger a still-asserted
+ * level under KVM.
+ */
+ if (out[0] & XIVE_SRC_H_INT_ESB)
+ irqd->flags |= XIVE_IRQ_FLAG_H_INT_ESB;
+
+ /* The hypervisor returns -1 for a page that isn't present. */
+ irqd->eoi_page = (out[1] == (uint64_t)-1) ? 0 : (vm_offset_t)out[1];
+ irqd->trig_page = (out[2] == (uint64_t)-1) ? 0 : (vm_offset_t)out[2];
+ irqd->esb_size = 1U << out[3];
+ irqd->chip = 0; /* Guest has no chip affinity to speak of. */
+
+ /*
+ * H_INT_GET_SOURCE_CONFIG returns in R4-R7:
+ * out[0]: flags
+ * out[1]: target (hwref)
+ * out[2]: priority
+ * out[3]: sw_irq (logical IRQ)
+ */
+ rc = phyp_hcall4(H_INT_GET_SOURCE_CONFIG, 0ULL, (uint64_t)lisn,
+ 0, 0, 0, 0, out);
+ if (rc == H_SUCCESS) {
+ irqd->vp = out[1];
+ irqd->prio = (uint8_t)out[2];
+ irqd->lirq = (uint32_t)out[3];
+ }
+ return (0);
+}
+
+static int
+xive_phyp_irq_configure(device_t dev, u_int lisn, int cpu,
+ uint8_t prio, uint32_t sw_irq)
+{
+ uint64_t hwref = pcpu_find(cpu)->pc_hwref;
+ int64_t rc;
+
+ rc = phyp_hcall(H_INT_SET_SOURCE_CONFIG, XIVE_SRC_SET_EISN,
+ (uint64_t)lisn, hwref, (uint64_t)prio, (uint64_t)sw_irq);
+ if (rc == H_BUSY)
+ return (EBUSY);
+ if (rc != H_SUCCESS) {
+ device_printf(dev, "H_INT_SET_SOURCE_CONFIG(lisn %u, target "
+ "%#lx, prio %u) failed: %ld\n", lisn, (u_long)hwref, prio,
+ (long)rc);
+ return (EIO);
+ }
+ return (0);
+}
+
+/*
+ * Drive the ESB of a source that has no MMIO mapping. Called from the
+ * interrupt path, so it stays quiet on failure rather than risking a
+ * printf storm; a failing EOI shows up as a source that stops firing.
+ */
+static uint64_t
+xive_phyp_esb_op(device_t dev __unused, struct xive_irq *irqd,
+ uint32_t offset, uint64_t data, bool store)
+{
+ int64_t rc;
+
+ rc = phyp_hcall(H_INT_ESB, store ? XIVE_ESB_STORE : 0ULL,
+ (uint64_t)irqd->girq, (uint64_t)offset, data);
+ if (rc != H_SUCCESS)
+ return (~0ULL);
+
+ /* A store returns -1 in R4; report it as the 0 the core expects. */
+ return (rc == H_SUCCESS ? 0 : ~0ULL);
+}
+
+static int
+xive_phyp_sync(device_t dev __unused, u_int lisn)
+{
+ int64_t rc;
+
+ rc = phyp_hcall(H_INT_SYNC, 0ULL, (uint64_t)lisn);
+ return (rc == H_SUCCESS ? 0 : EIO);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Sep 10, 7:57 PM (10 h, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38366376
Default Alt Text
D59259.id185371.diff (59 KB)
Attached To
Mode
D59259: powerpc/pseries: Add XIVE native mode to pseries
Attached
Detach File
Event Timeline
Log In to Comment