git: 0efad4aceb53 - main - bhyve: Support legacy PCI interrupts on arm64

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Wed, 10 Apr 2024 15:19:24 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=0efad4aceb533ef32905de586ab0112b1c563996

commit 0efad4aceb533ef32905de586ab0112b1c563996
Author:     Jessica Clarke <jrtc27@jrtc27.com>
AuthorDate: 2024-02-17 01:44:51 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2024-04-10 15:17:56 +0000

    bhyve: Support legacy PCI interrupts on arm64
    
    This allows us to remove various #ifdef hacks and enable building more
    PCI devices.
    
    Note that a hole is left in the interrupt mapping for the RTC rather
    than having the two core devices straddle the PCIe interrupts. QEMU's
    virt machine also takes this approach.
    
    Reviewed by:    jhb
    MFC after:      2 weeks
    Obtained from:  CheriBSD
---
 usr.sbin/bhyve/Makefile                   |  6 +++
 usr.sbin/bhyve/aarch64/bhyverun_machdep.c | 10 ++++-
 usr.sbin/bhyve/aarch64/fdt.c              | 39 +++++++++++-------
 usr.sbin/bhyve/aarch64/fdt.h              |  2 +-
 usr.sbin/bhyve/aarch64/pci_irq.c          | 66 +++++++++++++++++++++++++++++++
 usr.sbin/bhyve/aarch64/pci_irq_machdep.h  | 49 +++++++++++++++++++++++
 usr.sbin/bhyve/amd64/Makefile.inc         |  6 ---
 usr.sbin/bhyve/amd64/mptbl.c              |  6 +--
 usr.sbin/bhyve/amd64/pci_irq.c            | 35 ++++++++++++----
 usr.sbin/bhyve/amd64/pci_irq_machdep.h    | 60 ++++++++++++++++++++++++++++
 usr.sbin/bhyve/pci_emul.c                 | 64 ++++++++----------------------
 usr.sbin/bhyve/pci_emul.h                 | 13 +++---
 usr.sbin/bhyve/{amd64 => }/pci_irq.h      | 16 ++++----
 usr.sbin/bhyve/virtio.c                   |  7 ----
 usr.sbin/bhyve/virtio.h                   |  2 -
 15 files changed, 276 insertions(+), 105 deletions(-)

diff --git a/usr.sbin/bhyve/Makefile b/usr.sbin/bhyve/Makefile
index 124476ef7d2f..d5d7dfc26ab9 100644
--- a/usr.sbin/bhyve/Makefile
+++ b/usr.sbin/bhyve/Makefile
@@ -37,9 +37,14 @@ SRCS=	\
 	net_backend_slirp.c	\
 	net_backends.c		\
 	net_utils.c		\
+	pci_ahci.c		\
+	pci_e82545.c		\
 	pci_emul.c		\
+	pci_hda.c		\
 	pci_hostbridge.c	\
+	pci_irq.c		\
 	pci_nvme.c		\
+	pci_uart.c		\
 	pci_virtio_9p.c		\
 	pci_virtio_block.c	\
 	pci_virtio_console.c	\
@@ -47,6 +52,7 @@ SRCS=	\
 	pci_virtio_net.c	\
 	pci_virtio_rnd.c	\
 	pci_virtio_scsi.c	\
+	pci_xhci.c		\
 	qemu_fwcfg.c		\
 	qemu_loader.c		\
 	smbiostbl.c		\
diff --git a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
index 22b8b85e6b9b..2aa7d2d9b4fd 100644
--- a/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
+++ b/usr.sbin/bhyve/aarch64/bhyverun_machdep.c
@@ -47,6 +47,7 @@
 #include "fdt.h"
 #include "mem.h"
 #include "pci_emul.h"
+#include "pci_irq.h"
 #include "uart_emul.h"
 
 /* Start of mem + 1M */
@@ -63,7 +64,10 @@
 #define	GIC_REDIST_BASE		0x2f100000
 #define	GIC_REDIST_SIZE(ncpu)	((ncpu) * 2 * PAGE_SIZE_64K)
 
-#define	PCIE_INTR	33
+#define	PCIE_INTA	34
+#define	PCIE_INTB	35
+#define	PCIE_INTC	36
+#define	PCIE_INTD	37
 
 void
 bhyve_init_config(void)
@@ -295,6 +299,7 @@ bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp)
 	const char *bootrom;
 	uint64_t elr;
 	int error;
+	int pcie_intrs[4] = {PCIE_INTA, PCIE_INTB, PCIE_INTC, PCIE_INTD};
 
 	bootrom = get_config_value("bootrom");
 	if (bootrom == NULL) {
@@ -324,7 +329,8 @@ bhyve_init_platform(struct vmctx *ctx, struct vcpu *bsp)
 	if (init_mmio_uart(ctx))
 		fdt_add_uart(UART_MMIO_BASE, UART_MMIO_SIZE, UART_INTR);
 	fdt_add_timer();
-	fdt_add_pcie(PCIE_INTR);
+	pci_irq_init(pcie_intrs);
+	fdt_add_pcie(pcie_intrs);
 
 	return (0);
 }
diff --git a/usr.sbin/bhyve/aarch64/fdt.c b/usr.sbin/bhyve/aarch64/fdt.c
index 4b99e7009296..65876f0f90e8 100644
--- a/usr.sbin/bhyve/aarch64/fdt.c
+++ b/usr.sbin/bhyve/aarch64/fdt.c
@@ -271,12 +271,12 @@ fdt_add_timer(void)
 }
 
 void
-fdt_add_pcie(int intr)
+fdt_add_pcie(int intrs[static 4])
 {
 	void *fdt, *prop;
+	int slot, pin, intr, i;
 
 	assert(gic_phandle != 0);
-	assert(intr >= GIC_FIRST_SPI);
 
 	fdt = fdtroot;
 
@@ -320,24 +320,35 @@ fdt_add_pcie(int intr)
 
 	fdt_property_u32(fdt, "#interrupt-cells", 1);
 	fdt_property_u32(fdt, "interrupt-parent", gic_phandle);
+
+	/*
+	 * Describe standard swizzled interrupts routing (pins rotated by one
+	 * for each consecutive slot). Must match pci_irq_route().
+	 */
 	fdt_property_placeholder(fdt, "interrupt-map-mask",
 	    4 * sizeof(uint32_t), &prop);
-	SET_PROP_U32(prop, 0, 0);
+	SET_PROP_U32(prop, 0, 3 << 11);
 	SET_PROP_U32(prop, 1, 0);
 	SET_PROP_U32(prop, 2, 0);
 	SET_PROP_U32(prop, 3, 7);
 	fdt_property_placeholder(fdt, "interrupt-map",
-	    10 * sizeof(uint32_t), &prop);
-	SET_PROP_U32(prop, 0, 0);
-	SET_PROP_U32(prop, 1, 0);
-	SET_PROP_U32(prop, 2, 0);
-	SET_PROP_U32(prop, 3, 1);
-	SET_PROP_U32(prop, 4, gic_phandle);
-	SET_PROP_U32(prop, 5, 0);
-	SET_PROP_U32(prop, 6, 0);
-	SET_PROP_U32(prop, 7, GIC_SPI);
-	SET_PROP_U32(prop, 8, intr - GIC_FIRST_SPI);
-	SET_PROP_U32(prop, 9, IRQ_TYPE_LEVEL_HIGH);
+	    160 * sizeof(uint32_t), &prop);
+	for (i = 0; i < 16; ++i) {
+		pin = i % 4;
+		slot = i / 4;
+		intr = intrs[(pin + slot) % 4];
+		assert(intr >= GIC_FIRST_SPI);
+		SET_PROP_U32(prop, 10 * i + 0, slot << 11);
+		SET_PROP_U32(prop, 10 * i + 1, 0);
+		SET_PROP_U32(prop, 10 * i + 2, 0);
+		SET_PROP_U32(prop, 10 * i + 3, pin + 1);
+		SET_PROP_U32(prop, 10 * i + 4, gic_phandle);
+		SET_PROP_U32(prop, 10 * i + 5, 0);
+		SET_PROP_U32(prop, 10 * i + 6, 0);
+		SET_PROP_U32(prop, 10 * i + 7, GIC_SPI);
+		SET_PROP_U32(prop, 10 * i + 8, intr - GIC_FIRST_SPI);
+		SET_PROP_U32(prop, 10 * i + 9, IRQ_TYPE_LEVEL_HIGH);
+	}
 
 	fdt_end_node(fdt);
 }
diff --git a/usr.sbin/bhyve/aarch64/fdt.h b/usr.sbin/bhyve/aarch64/fdt.h
index a559d2289187..6534266173d0 100644
--- a/usr.sbin/bhyve/aarch64/fdt.h
+++ b/usr.sbin/bhyve/aarch64/fdt.h
@@ -40,7 +40,7 @@ int	fdt_init(struct vmctx *ctx, int ncpu, vm_paddr_t addrp,
 void	fdt_add_gic(uint64_t dist_base, uint64_t dist_size,
 	    uint64_t redist_base, uint64_t redist_size);
 void	fdt_add_timer(void);
-void	fdt_add_pcie(int intr);
+void	fdt_add_pcie(int intrs[static 4]);
 void	fdt_add_uart(uint64_t uart_base, uint64_t uart_size, int intr);
 void	fdt_finalize(void);
 
diff --git a/usr.sbin/bhyve/aarch64/pci_irq.c b/usr.sbin/bhyve/aarch64/pci_irq.c
new file mode 100644
index 000000000000..f2c4602fe2e9
--- /dev/null
+++ b/usr.sbin/bhyve/aarch64/pci_irq.c
@@ -0,0 +1,66 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Jessica Clarke <jrtc27@FreeBSD.org>
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#include <vmmapi.h>
+
+#include "pci_emul.h"
+#include "pci_irq.h"
+
+static int gic_irqs[4];
+
+void
+pci_irq_init(int intrs[static 4])
+{
+	int i;
+
+	for (i = 0; i < 4; ++i)
+		gic_irqs[i] = intrs[i];
+}
+
+void
+pci_irq_assert(struct pci_devinst *pi)
+{
+	vm_assert_irq(pi->pi_vmctx, pi->pi_lintr.irq.gic_irq);
+}
+
+void
+pci_irq_deassert(struct pci_devinst *pi)
+{
+	vm_deassert_irq(pi->pi_vmctx, pi->pi_lintr.irq.gic_irq);
+}
+
+void
+pci_irq_route(struct pci_devinst *pi, struct pci_irq *irq)
+{
+	/*
+	 * Assign swizzled IRQ for this INTx if one is not yet assigned. Must
+	 * match fdt_add_pcie().
+	 */
+	if (irq->gic_irq == 0)
+		irq->gic_irq =
+		    gic_irqs[(pi->pi_slot + pi->pi_lintr.pin - 1) % 4];
+}
diff --git a/usr.sbin/bhyve/aarch64/pci_irq_machdep.h b/usr.sbin/bhyve/aarch64/pci_irq_machdep.h
new file mode 100644
index 000000000000..c10af3aa53b2
--- /dev/null
+++ b/usr.sbin/bhyve/aarch64/pci_irq_machdep.h
@@ -0,0 +1,49 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Jessica Clarke <jrtc27@FreeBSD.org>
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#ifndef __PCI_IRQ_MD_H__
+#define	__PCI_IRQ_MD_H__
+
+struct pci_irq {
+	int	gic_irq;
+};
+
+void	pci_irq_init(int intrs[static 4]);
+
+static inline void
+pci_irq_init_irq(struct pci_irq *irq)
+{
+	irq->gic_irq = 0;
+}
+
+static inline uint8_t
+pci_irq_intline(struct pci_irq *irq __unused)
+{
+	return (255);
+}
+
+#endif
diff --git a/usr.sbin/bhyve/amd64/Makefile.inc b/usr.sbin/bhyve/amd64/Makefile.inc
index d281e3cf1581..53320a70178b 100644
--- a/usr.sbin/bhyve/amd64/Makefile.inc
+++ b/usr.sbin/bhyve/amd64/Makefile.inc
@@ -8,16 +8,10 @@ SRCS+=	\
 	ioapic.c	\
 	kernemu_dev.c	\
 	mptbl.c		\
-	pci_ahci.c	\
-	pci_e82545.c	\
 	pci_fbuf.c	\
 	pci_gvt-d.c	\
-	pci_hda.c	\
-	pci_irq.c	\
 	pci_lpc.c	\
 	pci_passthru.c	\
-	pci_uart.c	\
-	pci_xhci.c	\
 	pctestdev.c	\
 	pm.c		\
 	post.c		\
diff --git a/usr.sbin/bhyve/amd64/mptbl.c b/usr.sbin/bhyve/amd64/mptbl.c
index 4be412524b60..328cc77980ac 100644
--- a/usr.sbin/bhyve/amd64/mptbl.c
+++ b/usr.sbin/bhyve/amd64/mptbl.c
@@ -208,8 +208,8 @@ mpt_count_ioint_entries(void)
 }
 
 static void
-mpt_generate_pci_int(int bus, int slot, int pin, int pirq_pin __unused,
-    int ioapic_irq, void *arg)
+mpt_generate_pci_int(int bus, int slot, int pin, struct pci_irq *irq,
+    void *arg)
 {
 	int_entry_ptr *mpiep, mpie;
 
@@ -226,7 +226,7 @@ mpt_generate_pci_int(int bus, int slot, int pin, int pirq_pin __unused,
 	mpie->src_bus_id = bus;
 	mpie->src_bus_irq = slot << 2 | (pin - 1);
 	mpie->dst_apic_id = mpie[-1].dst_apic_id;
-	mpie->dst_apic_int = ioapic_irq;
+	mpie->dst_apic_int = irq->ioapic_irq;
 
 	*mpiep = mpie + 1;
 }
diff --git a/usr.sbin/bhyve/amd64/pci_irq.c b/usr.sbin/bhyve/amd64/pci_irq.c
index 24593befee49..7e1aee7fbb1d 100644
--- a/usr.sbin/bhyve/amd64/pci_irq.c
+++ b/usr.sbin/bhyve/amd64/pci_irq.c
@@ -39,6 +39,7 @@
 
 #include "acpi.h"
 #include "inout.h"
+#include "ioapic.h"
 #include "pci_emul.h"
 #include "pci_irq.h"
 #include "pci_lpc.h"
@@ -156,7 +157,7 @@ pci_irq_assert(struct pci_devinst *pi)
 	struct pirq *pirq;
 	int pin;
 
-	pin = pi->pi_lintr.pirq_pin;
+	pin = pi->pi_lintr.irq.pirq_pin;
 	if (pin > 0) {
 		assert(pin <= NPIRQS);
 		pirq = &pirqs[pin - 1];
@@ -164,13 +165,13 @@ pci_irq_assert(struct pci_devinst *pi)
 		pirq->active_count++;
 		if (pirq->active_count == 1 && pirq_valid_irq(pirq->reg)) {
 			vm_isa_assert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
-			    pi->pi_lintr.ioapic_irq);
+			    pi->pi_lintr.irq.ioapic_irq);
 			pthread_mutex_unlock(&pirq->lock);
 			return;
 		}
 		pthread_mutex_unlock(&pirq->lock);
 	}
-	vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
+	vm_ioapic_assert_irq(pi->pi_vmctx, pi->pi_lintr.irq.ioapic_irq);
 }
 
 void
@@ -179,7 +180,7 @@ pci_irq_deassert(struct pci_devinst *pi)
 	struct pirq *pirq;
 	int pin;
 
-	pin = pi->pi_lintr.pirq_pin;
+	pin = pi->pi_lintr.irq.pirq_pin;
 	if (pin > 0) {
 		assert(pin <= NPIRQS);
 		pirq = &pirqs[pin - 1];
@@ -187,16 +188,16 @@ pci_irq_deassert(struct pci_devinst *pi)
 		pirq->active_count--;
 		if (pirq->active_count == 0 && pirq_valid_irq(pirq->reg)) {
 			vm_isa_deassert_irq(pi->pi_vmctx, pirq->reg & PIRQ_IRQ,
-			    pi->pi_lintr.ioapic_irq);
+			    pi->pi_lintr.irq.ioapic_irq);
 			pthread_mutex_unlock(&pirq->lock);
 			return;
 		}
 		pthread_mutex_unlock(&pirq->lock);
 	}
-	vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.ioapic_irq);
+	vm_ioapic_deassert_irq(pi->pi_vmctx, pi->pi_lintr.irq.ioapic_irq);
 }
 
-int
+static int
 pirq_alloc_pin(struct pci_devinst *pi)
 {
 	struct vmctx *ctx = pi->pi_vmctx;
@@ -248,6 +249,26 @@ pirq_irq(int pin)
 	return (pirqs[pin - 1].reg & PIRQ_IRQ);
 }
 
+void
+pci_irq_route(struct pci_devinst *pi, struct pci_irq *irq)
+{
+	/*
+	 * Attempt to allocate an I/O APIC pin for this intpin if one
+	 * is not yet assigned.
+	 */
+	if (irq->ioapic_irq == 0)
+		irq->ioapic_irq = ioapic_pci_alloc_irq(pi);
+	assert(irq->ioapic_irq > 0);
+
+	/*
+	 * Attempt to allocate a PIRQ pin for this intpin if one is
+	 * not yet assigned.
+	 */
+	if (irq->pirq_pin == 0)
+		irq->pirq_pin = pirq_alloc_pin(pi);
+	assert(irq->pirq_pin > 0);
+}
+
 /* XXX: Generate $PIR table. */
 
 static void
diff --git a/usr.sbin/bhyve/amd64/pci_irq_machdep.h b/usr.sbin/bhyve/amd64/pci_irq_machdep.h
new file mode 100644
index 000000000000..c4be29babe34
--- /dev/null
+++ b/usr.sbin/bhyve/amd64/pci_irq_machdep.h
@@ -0,0 +1,60 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2014 Hudson River Trading LLC
+ * Written by: John H. Baldwin <jhb@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
+ */
+
+#ifndef __PCI_IRQ_MD_H__
+#define	__PCI_IRQ_MD_H__
+
+struct vmctx;
+
+struct pci_irq {
+	int	pirq_pin;
+	int	ioapic_irq;
+};
+
+void	pci_irq_init(struct vmctx *ctx);
+void	pci_irq_reserve(int irq);
+void	pci_irq_use(int irq);
+int	pirq_irq(int pin);
+uint8_t	pirq_read(int pin);
+void	pirq_write(struct vmctx *ctx, int pin, uint8_t val);
+
+static inline void
+pci_irq_init_irq(struct pci_irq *irq)
+{
+	irq->pirq_pin = 0;
+	irq->ioapic_irq = 0;
+}
+
+static inline uint8_t
+pci_irq_intline(struct pci_irq *irq)
+{
+	return (pirq_irq(irq->pirq_pin));
+}
+
+#endif
diff --git a/usr.sbin/bhyve/pci_emul.c b/usr.sbin/bhyve/pci_emul.c
index f2c9c4c9b6e7..0b0be09c4de7 100644
--- a/usr.sbin/bhyve/pci_emul.c
+++ b/usr.sbin/bhyve/pci_emul.c
@@ -52,12 +52,10 @@
 #include "debug.h"
 #ifdef __amd64__
 #include "amd64/inout.h"
-#include "amd64/ioapic.h"
 #endif
 #include "mem.h"
 #include "pci_emul.h"
 #ifdef __amd64__
-#include "amd64/pci_irq.h"
 #include "amd64/pci_lpc.h"
 #include "pci_passthru.h"
 #endif
@@ -81,9 +79,8 @@ struct funcinfo {
 };
 
 struct intxinfo {
-	int	ii_count;
-	int	ii_pirq_pin;
-	int	ii_ioapic_irq;
+	int		ii_count;
+	struct pci_irq	ii_irq;
 };
 
 struct slotinfo {
@@ -157,10 +154,8 @@ SYSRES_MEM(PCI_EMUL_ECFG_BASE, PCI_EMUL_ECFG_SIZE);
 #define	PCI_EMUL_MEMLIMIT32	PCI_EMUL_ECFG_BASE
 #define PCI_EMUL_MEMSIZE64	(32*GB)
 
-#ifdef __amd64__
 static void pci_lintr_route(struct pci_devinst *pi);
 static void pci_lintr_update(struct pci_devinst *pi);
-#endif
 
 static struct pci_devemu *pci_emul_finddev(const char *name);
 static void pci_cfgrw(int in, int bus, int slot, int func, int coff,
@@ -1133,13 +1128,10 @@ pci_emul_init(struct vmctx *ctx, struct pci_devemu *pde, int bus, int slot,
 	pdi->pi_bus = bus;
 	pdi->pi_slot = slot;
 	pdi->pi_func = func;
-#ifdef __amd64__
 	pthread_mutex_init(&pdi->pi_lintr.lock, NULL);
 	pdi->pi_lintr.pin = 0;
 	pdi->pi_lintr.state = IDLE;
-	pdi->pi_lintr.pirq_pin = 0;
-	pdi->pi_lintr.ioapic_irq = 0;
-#endif
+	pci_irq_init_irq(&pdi->pi_lintr.irq);
 	pdi->pi_d = pde;
 	snprintf(pdi->pi_name, PI_NAMESZ, "%s@pci.%d.%d.%d", pde->pe_emu, bus,
 	    slot, func);
@@ -1277,9 +1269,7 @@ msixcap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
 
 		pi->pi_msix.enabled = val & PCIM_MSIXCTRL_MSIX_ENABLE;
 		pi->pi_msix.function_mask = val & PCIM_MSIXCTRL_FUNCTION_MASK;
-#ifdef __amd64__
 		pci_lintr_update(pi);
-#endif
 	}
 
 	CFGWRITE(pi, offset, val, bytes);
@@ -1321,9 +1311,7 @@ msicap_cfgwrite(struct pci_devinst *pi, int capoff, int offset,
 	} else {
 		pi->pi_msi.maxmsgnum = 0;
 	}
-#ifdef __amd64__
 	pci_lintr_update(pi);
-#endif
 }
 
 static void
@@ -1617,7 +1605,6 @@ init_pci(struct vmctx *ctx)
 		bi->memlimit64 = pci_emul_membase64;
 	}
 
-#ifdef __amd64__
 	/*
 	 * PCI backends are initialized before routing INTx interrupts
 	 * so that LPC devices are able to reserve ISA IRQs before
@@ -1637,6 +1624,7 @@ init_pci(struct vmctx *ctx)
 			}
 		}
 	}
+#ifdef __amd64__
 	lpc_pirq_routed();
 #endif
 
@@ -1696,8 +1684,8 @@ init_pci(struct vmctx *ctx)
 
 #ifdef __amd64__
 static void
-pci_apic_prt_entry(int bus __unused, int slot, int pin, int pirq_pin __unused,
-    int ioapic_irq, void *arg __unused)
+pci_apic_prt_entry(int bus __unused, int slot, int pin, struct pci_irq *irq,
+    void *arg __unused)
 {
 
 	dsdt_line("  Package ()");
@@ -1705,17 +1693,17 @@ pci_apic_prt_entry(int bus __unused, int slot, int pin, int pirq_pin __unused,
 	dsdt_line("    0x%X,", slot << 16 | 0xffff);
 	dsdt_line("    0x%02X,", pin - 1);
 	dsdt_line("    Zero,");
-	dsdt_line("    0x%X", ioapic_irq);
+	dsdt_line("    0x%X", irq->ioapic_irq);
 	dsdt_line("  },");
 }
 
 static void
-pci_pirq_prt_entry(int bus __unused, int slot, int pin, int pirq_pin,
-    int ioapic_irq __unused, void *arg __unused)
+pci_pirq_prt_entry(int bus __unused, int slot, int pin, struct pci_irq *irq,
+    void *arg __unused)
 {
 	char *name;
 
-	name = lpc_pirq_name(pirq_pin);
+	name = lpc_pirq_name(irq->pirq_pin);
 	if (name == NULL)
 		return;
 	dsdt_line("  Package ()");
@@ -1968,7 +1956,6 @@ pci_generate_msi(struct pci_devinst *pi, int index)
 	}
 }
 
-#ifdef __amd64__
 static bool
 pci_lintr_permitted(struct pci_devinst *pi)
 {
@@ -2013,6 +2000,7 @@ pci_lintr_route(struct pci_devinst *pi)
 {
 	struct businfo *bi;
 	struct intxinfo *ii;
+	struct pci_irq *irq;
 
 	if (pi->pi_lintr.pin == 0)
 		return;
@@ -2020,26 +2008,10 @@ pci_lintr_route(struct pci_devinst *pi)
 	bi = pci_businfo[pi->pi_bus];
 	assert(bi != NULL);
 	ii = &bi->slotinfo[pi->pi_slot].si_intpins[pi->pi_lintr.pin - 1];
-
-	/*
-	 * Attempt to allocate an I/O APIC pin for this intpin if one
-	 * is not yet assigned.
-	 */
-	if (ii->ii_ioapic_irq == 0)
-		ii->ii_ioapic_irq = ioapic_pci_alloc_irq(pi);
-	assert(ii->ii_ioapic_irq > 0);
-
-	/*
-	 * Attempt to allocate a PIRQ pin for this intpin if one is
-	 * not yet assigned.
-	 */
-	if (ii->ii_pirq_pin == 0)
-		ii->ii_pirq_pin = pirq_alloc_pin(pi);
-	assert(ii->ii_pirq_pin > 0);
-
-	pi->pi_lintr.ioapic_irq = ii->ii_ioapic_irq;
-	pi->pi_lintr.pirq_pin = ii->ii_pirq_pin;
-	pci_set_cfgdata8(pi, PCIR_INTLINE, pirq_irq(ii->ii_pirq_pin));
+	irq = &ii->ii_irq;
+	pci_irq_route(pi, irq);
+	pi->pi_lintr.irq = *irq;
+	pci_set_cfgdata8(pi, PCIR_INTLINE, pci_irq_intline(irq));
 }
 
 void
@@ -2124,12 +2096,10 @@ pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg)
 		for (pin = 0; pin < 4; pin++) {
 			ii = &si->si_intpins[pin];
 			if (ii->ii_count != 0)
-				cb(bus, slot, pin + 1, ii->ii_pirq_pin,
-				    ii->ii_ioapic_irq, arg);
+				cb(bus, slot, pin + 1, &ii->ii_irq, arg);
 		}
 	}
 }
-#endif /* __amd64__ */
 
 /*
  * Return 1 if the emulated device in 'slot' is a multi-function device.
@@ -2234,13 +2204,11 @@ pci_emul_cmd_changed(struct pci_devinst *pi, uint16_t old)
 		}
 	}
 
-#ifdef __amd64__
 	/*
 	 * If INTx has been unmasked and is pending, assert the
 	 * interrupt.
 	 */
 	pci_lintr_update(pi);
-#endif
 }
 
 static void
diff --git a/usr.sbin/bhyve/pci_emul.h b/usr.sbin/bhyve/pci_emul.h
index c367169113f8..063b4b84e044 100644
--- a/usr.sbin/bhyve/pci_emul.h
+++ b/usr.sbin/bhyve/pci_emul.h
@@ -40,6 +40,8 @@
 
 #include <assert.h>
 
+#include "pci_irq.h"
+
 #define	PCI_BARMAX	PCIR_MAX_BAR_0	/* BAR registers in a Type 0 header */
 #define PCI_BARMAX_WITH_ROM (PCI_BARMAX + 1)
 #define PCI_ROM_IDX (PCI_BARMAX + 1)
@@ -124,7 +126,6 @@ struct pci_devinst {
 	int	  pi_prevcap;
 	int	  pi_capend;
 
-#ifdef __amd64__
 	struct {
 		int8_t    	pin;
 		enum {
@@ -132,11 +133,9 @@ struct pci_devinst {
 			ASSERTED,
 			PENDING,
 		} state;
-		int		pirq_pin;
-		int	  	ioapic_irq;
+		struct pci_irq	irq;
 		pthread_mutex_t	lock;
 	} pi_lintr;
-#endif
 
 	struct {
 		int		enabled;
@@ -221,15 +220,13 @@ struct pciecap {
 } __packed;
 static_assert(sizeof(struct pciecap) == 60, "compile-time assertion failed");
 
-#ifdef __amd64__
-typedef void (*pci_lintr_cb)(int b, int s, int pin, int pirq_pin,
-    int ioapic_irq, void *arg);
+typedef void (*pci_lintr_cb)(int b, int s, int pin, struct pci_irq *irq,
+    void *arg);
 void	pci_lintr_assert(struct pci_devinst *pi);
 void	pci_lintr_deassert(struct pci_devinst *pi);
 void	pci_lintr_request(struct pci_devinst *pi);
 int	pci_count_lintr(int bus);
 void	pci_walk_lintr(int bus, pci_lintr_cb cb, void *arg);
-#endif
 
 int	init_pci(struct vmctx *ctx);
 void	pci_callback(void);
diff --git a/usr.sbin/bhyve/amd64/pci_irq.h b/usr.sbin/bhyve/pci_irq.h
similarity index 86%
rename from usr.sbin/bhyve/amd64/pci_irq.h
rename to usr.sbin/bhyve/pci_irq.h
index 50ea2eec00a9..8b556ddc91a2 100644
--- a/usr.sbin/bhyve/amd64/pci_irq.h
+++ b/usr.sbin/bhyve/pci_irq.h
@@ -32,14 +32,16 @@
 
 struct pci_devinst;
 
+#if defined(__amd64__)
+#include "amd64/pci_irq_machdep.h"
+#elif defined(__aarch64__)
+#include "aarch64/pci_irq_machdep.h"
+#else
+#error Unsupported platform
+#endif
+
 void	pci_irq_assert(struct pci_devinst *pi);
 void	pci_irq_deassert(struct pci_devinst *pi);
-void	pci_irq_init(struct vmctx *ctx);
-void	pci_irq_reserve(int irq);
-void	pci_irq_use(int irq);
-int	pirq_alloc_pin(struct pci_devinst *pi);
-int	pirq_irq(int pin);
-uint8_t	pirq_read(int pin);
-void	pirq_write(struct vmctx *ctx, int pin, uint8_t val);
+void	pci_irq_route(struct pci_devinst *pi, struct pci_irq *irq);
 
 #endif
diff --git a/usr.sbin/bhyve/virtio.c b/usr.sbin/bhyve/virtio.c
index eb09714d4f20..26df39ceb5b3 100644
--- a/usr.sbin/bhyve/virtio.c
+++ b/usr.sbin/bhyve/virtio.c
@@ -114,10 +114,8 @@ vi_reset_dev(struct virtio_softc *vs)
 	vs->vs_negotiated_caps = 0;
 	vs->vs_curq = 0;
 	/* vs->vs_status = 0; -- redundant */
-#ifdef __amd64__
 	if (vs->vs_isr)
 		pci_lintr_deassert(vs->vs_pi);
-#endif
 	vs->vs_isr = 0;
 	vs->vs_msix_cfg_idx = VIRTIO_MSI_NO_VECTOR;
 }
@@ -164,11 +162,8 @@ vi_intr_init(struct virtio_softc *vs, int barnum, int use_msix)
 	/* Only 1 MSI vector for bhyve */
 	pci_emul_add_msicap(vs->vs_pi, 1);
 
-	/* XXX-MJ missing an implementation for arm64 */
-#ifdef __amd64__
 	/* Legacy interrupts are mandatory for virtio devices */
 	pci_lintr_request(vs->vs_pi);
-#endif
 
 	return (0);
 }
@@ -656,10 +651,8 @@ bad:
 	case VIRTIO_PCI_ISR:
 		value = vs->vs_isr;
 		vs->vs_isr = 0;		/* a read clears this flag */
-#ifdef __amd64__
 		if (value)
 			pci_lintr_deassert(pi);
-#endif
 		break;
 	case VIRTIO_MSI_CONFIG_VECTOR:
 		value = vs->vs_msix_cfg_idx;
diff --git a/usr.sbin/bhyve/virtio.h b/usr.sbin/bhyve/virtio.h
index 4c6c8004b2d1..2b72b862ab21 100644
--- a/usr.sbin/bhyve/virtio.h
+++ b/usr.sbin/bhyve/virtio.h
@@ -358,9 +358,7 @@ vi_interrupt(struct virtio_softc *vs, uint8_t isr, uint16_t msix_idx)
 		VS_LOCK(vs);
 		vs->vs_isr |= isr;
 		pci_generate_msi(vs->vs_pi, 0);
-#ifdef __amd64__
 		pci_lintr_assert(vs->vs_pi);
-#endif
 		VS_UNLOCK(vs);
 	}
 }