Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [PATCH kvmtool 0/9] vfio: Add support for platform devices
@ 2026-10-24 16:18 Mostafa Saleh
  2026-10-24 16:18 ` [PATCH kvmtool 1/9] vfio: Fix fd leak in vfio_group_get_for_dev() Mostafa Saleh
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:18 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

This patch series extend VFIO support to platform devices by using
VFIO-platform.

One of the main difference is how the guest discover the device, which
is done through the device tree.
kvmtool can only describe:
- regs
- interrupts

However, the guest still needs a compatible string and other per device
bindings.
To avoid having any hardcoded DT nodes in the source code, kvmtool will
accept a dtb argument with each assigned device that will be copied to
the guest node.

Patches
=======
1-4: Fixes, code refactoring and improving interrupt support.
5: DT common function.
6-8: VFIO-platform support.
9: Custom device tree support.

Patch 2 is better viewed with --color-moved

Testing
=======
Tested on Lenovo Mini-x Gen 10 (Snapdragon-X) by assigning 2 devices,
QCE and BAM as follows:

echo 1dfa000.crypto > /sys/bus/platform/drivers/qcrypto/unbind
echo 1dc4000.dma-controller > /sys/bus/platform/drivers/bam-dma-engine/unbind

echo "vfio-platform" > /sys/bus/platform/devices/1dfa000.crypto/driver_override
echo "vfio-platform" > /sys/bus/platform/devices/1dc4000.dma-controller/driver_override

echo 1dc4000.dma-controller > /sys/bus/platform/drivers/vfio-platform/bind
echo 1dfa000.crypto > /sys/bus/platform/drivers/vfio-platform/bind

./lkvm-static run --irqchip gicv3 -k Image -p "nokaslr" -d rootfs.ext2 --force-pci -c 1  --debug --vfio-
platform 1dc4000.dma-controller,purwa-cryptobam.dtb --vfio-platform 1dfa000.crypto,purwa-qce.dtb

There are some fixes on the list that are needed to be able to unbind
the bam device [1]

Mostafa Saleh (9):
  vfio: Fix fd leak in vfio_group_get_for_dev()
  vfio: Factor out wired IRQ setup and teardown
  vfio: Support edge-triggered wired IRQs
  fdt: Allow fdt_irq_fn to generate multiple interrupts
  fdt: Add helper to copy an FDT node
  vfio: Reserve guest address space for VFIO platform
  vfio: Add basic support for vfio-platform
  vfio: platform: Add interrupts support
  vfio: platform: Take the guest node from a device tree template

 Makefile                       |   1 +
 arm64/fdt.c                    |  36 ++--
 arm64/include/kvm/kvm-arch.h   |  10 +-
 builtin-run.c                  |   3 +
 hw/cfi_flash.c                 |   4 +-
 hw/rtc.c                       |   4 +-
 hw/serial.c                    |   3 +-
 include/kvm/fdt.h              |  32 +++-
 include/kvm/vfio.h             |  41 ++++-
 mips/include/kvm/kvm-arch.h    |   2 +
 powerpc/include/kvm/kvm-arch.h |   2 +
 riscv/fdt.c                    |   2 +-
 riscv/include/kvm/kvm-arch.h   |   7 +-
 riscv/irq.c                    |  27 ++-
 vfio/core.c                    | 152 +++++++++++++++-
 vfio/pci.c                     | 111 +-----------
 vfio/platform.c                | 317 +++++++++++++++++++++++++++++++++
 virtio/mmio.c                  |  10 +-
 x86/include/kvm/kvm-arch.h     |   2 +
 19 files changed, 619 insertions(+), 147 deletions(-)
 create mode 100644 vfio/platform.c

-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH kvmtool 1/9] vfio: Fix fd leak in vfio_group_get_for_dev()
  2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
@ 2026-10-24 16:18 ` Mostafa Saleh
  2026-10-24 16:18 ` [PATCH kvmtool 2/9] vfio: Factor out wired IRQ setup and teardown Mostafa Saleh
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:18 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

The fd opened to read the iommu_group link of the device is closed
on every path except the one that finds the group already open, so
every device after the first one in a group leaked an fd.
Return through the goto on that path too.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 vfio/core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/vfio/core.c b/vfio/core.c
index fbe0098..e0c56ab 100644
--- a/vfio/core.c
+++ b/vfio/core.c
@@ -544,7 +544,7 @@ vfio_group_get_for_dev(struct kvm *kvm, struct vfio_device *vdev)
 	list_for_each_entry(group, &vfio_groups, list) {
 		if (group->id == group_id) {
 			group->refs++;
-			return group;
+			goto out_close;
 		}
 	}
 
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH kvmtool 2/9] vfio: Factor out wired IRQ setup and teardown
  2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
  2026-10-24 16:18 ` [PATCH kvmtool 1/9] vfio: Fix fd leak in vfio_group_get_for_dev() Mostafa Saleh
@ 2026-10-24 16:18 ` Mostafa Saleh
  2026-10-24 16:18 ` [PATCH kvmtool 3/9] vfio: Support edge-triggered wired IRQs Mostafa Saleh
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:18 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

Factor the wired IRQ eventfd and KVM irqfd setup and teardown out of
vfio_pci_enable_intx() and vfio_pci_disable_intx() into
vfio_irq_enable() and vfio_irq_disable() in vfio/core.c, and move
union vfio_irq_eventfd to vfio.h so both PCI INTx and VFIO platform
devices can share them.

The is a mechanical code move, no functional change.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 include/kvm/vfio.h |  27 +++++++++--
 vfio/core.c        | 107 +++++++++++++++++++++++++++++++++++++++++++
 vfio/pci.c         | 110 +++------------------------------------------
 3 files changed, 137 insertions(+), 107 deletions(-)

diff --git a/include/kvm/vfio.h b/include/kvm/vfio.h
index ac7b622..9a5783e 100644
--- a/include/kvm/vfio.h
+++ b/include/kvm/vfio.h
@@ -6,6 +6,7 @@
 #include "kvm/pci.h"
 
 #include <linux/vfio.h>
+#include <string.h>
 
 #define vfio_dev_err(vdev, fmt, ...) \
 	pr_err("%s: " fmt, (vdev)->params->name, ##__VA_ARGS__)
@@ -70,13 +71,17 @@ struct vfio_pci_msi_common {
 #define VFIO_PCI_IRQ_MODE_MSI		(1 << 1)
 #define VFIO_PCI_IRQ_MODE_MSIX		(1 << 2)
 
+struct vfio_irq {
+	int				gsi;
+	int				trigger_fd;
+	int				unmask_fd;
+};
+
 struct vfio_pci_device {
 	struct pci_device_header	hdr;
 
 	unsigned long			irq_modes;
-	int				intx_fd;
-	int				unmask_fd;
-	unsigned int			intx_gsi;
+	struct vfio_irq			intx;
 	struct vfio_pci_msi_common	msi;
 	struct vfio_pci_msi_common	msix;
 	struct vfio_pci_msix_table	msix_table;
@@ -119,10 +124,26 @@ struct vfio_group {
 	struct list_head		list;
 };
 
+/* Wrapper around UAPI vfio_irq_set */
+union vfio_irq_eventfd {
+	struct vfio_irq_set	irq;
+	u8 buffer[sizeof(struct vfio_irq_set) + sizeof(int)];
+};
+
+static inline void set_vfio_irq_eventd_payload(union vfio_irq_eventfd *evfd,
+					       int fd)
+{
+	memcpy(&evfd->irq.data, &fd, sizeof(fd));
+}
+
 int vfio_device_parser(const struct option *opt, const char *arg, int unset);
 int vfio_map_region(struct kvm *kvm, struct vfio_device *vdev,
 		    struct vfio_region *region);
 void vfio_unmap_region(struct kvm *kvm, struct vfio_region *region);
+int vfio_irq_enable(struct kvm *kvm, struct vfio_device *vdev,
+		    unsigned int index, struct vfio_irq *irq);
+void vfio_irq_disable(struct kvm *kvm, struct vfio_device *vdev,
+		      unsigned int index, struct vfio_irq *irq);
 int vfio_pci_setup_device(struct kvm *kvm, struct vfio_device *device);
 void vfio_pci_teardown_device(struct kvm *kvm, struct vfio_device *vdev);
 
diff --git a/vfio/core.c b/vfio/core.c
index e0c56ab..24d3f2d 100644
--- a/vfio/core.c
+++ b/vfio/core.c
@@ -1,8 +1,10 @@
+#include "kvm/irq.h"
 #include "kvm/kvm.h"
 #include "kvm/vfio.h"
 #include "kvm/ioport.h"
 
 #include <linux/list.h>
+#include <sys/eventfd.h>
 #include <libgen.h>
 
 #define VFIO_DEV_DIR		"/dev/vfio"
@@ -282,6 +284,111 @@ void vfio_unmap_region(struct kvm *kvm, struct vfio_region *region)
 	}
 }
 
+void vfio_irq_disable(struct kvm *kvm, struct vfio_device *vdev,
+		      unsigned int index, struct vfio_irq *irq)
+{
+	int gsi = irq->gsi;
+	struct vfio_irq_set irq_set = {
+		.argsz	= sizeof(irq_set),
+		.flags	= VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER,
+		.index	= index,
+	};
+
+	if (irq->trigger_fd == -1)
+		return;
+
+	ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
+	irq__del_irqfd(kvm, gsi, irq->trigger_fd);
+
+	close(irq->trigger_fd);
+	close(irq->unmask_fd);
+	irq->trigger_fd = -1;
+}
+
+int vfio_irq_enable(struct kvm *kvm, struct vfio_device *vdev,
+		    unsigned int index, struct vfio_irq *irq)
+{
+	int ret;
+	int trigger_fd, unmask_fd;
+	union vfio_irq_eventfd	trigger;
+	union vfio_irq_eventfd	unmask;
+	int gsi = irq->gsi;
+
+	if (irq->trigger_fd != -1)
+		return 0;
+
+	/*
+	 * PCI IRQ is level-triggered, so we use two eventfds. trigger_fd
+	 * signals an interrupt from host to guest, and unmask_fd signals the
+	 * deassertion of the line from guest to host.
+	 */
+	trigger_fd = eventfd(0, 0);
+	if (trigger_fd < 0) {
+		vfio_dev_err(vdev, "failed to create trigger eventfd");
+		return trigger_fd;
+	}
+
+	unmask_fd = eventfd(0, 0);
+	if (unmask_fd < 0) {
+		vfio_dev_err(vdev, "failed to create unmask eventfd");
+		close(trigger_fd);
+		return unmask_fd;
+	}
+
+	ret = irq__add_irqfd(kvm, gsi, trigger_fd, unmask_fd);
+	if (ret)
+		goto err_close;
+
+	trigger.irq = (struct vfio_irq_set) {
+		.argsz	= sizeof(trigger),
+		.flags	= VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER,
+		.index	= index,
+		.start	= 0,
+		.count	= 1,
+	};
+	set_vfio_irq_eventd_payload(&trigger, trigger_fd);
+
+	ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &trigger);
+	if (ret < 0) {
+		vfio_dev_err(vdev, "failed to setup VFIO IRQ");
+		goto err_delete_line;
+	}
+
+	unmask.irq = (struct vfio_irq_set) {
+		.argsz	= sizeof(unmask),
+		.flags	= VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK,
+		.index	= index,
+		.start	= 0,
+		.count	= 1,
+	};
+	set_vfio_irq_eventd_payload(&unmask, unmask_fd);
+
+	ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &unmask);
+	if (ret < 0) {
+		vfio_dev_err(vdev, "failed to setup unmask IRQ");
+		goto err_remove_event;
+	}
+
+	irq->trigger_fd = trigger_fd;
+	irq->unmask_fd = unmask_fd;
+
+	return 0;
+
+err_remove_event:
+	/* Remove trigger event */
+	trigger.irq.flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER;
+	trigger.irq.count = 0;
+	ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &trigger);
+
+err_delete_line:
+	irq__del_irqfd(kvm, gsi, trigger_fd);
+
+err_close:
+	close(trigger_fd);
+	close(unmask_fd);
+	return ret;
+}
+
 static int vfio_configure_device(struct kvm *kvm, struct vfio_device *vdev)
 {
 	int ret;
diff --git a/vfio/pci.c b/vfio/pci.c
index 0bcd60e..fb98435 100644
--- a/vfio/pci.c
+++ b/vfio/pci.c
@@ -17,17 +17,6 @@
 #define PCI_CAP_EXP_RC_ENDPOINT_SIZEOF_V1	12
 #endif
 
-/* Wrapper around UAPI vfio_irq_set */
-union vfio_irq_eventfd {
-	struct vfio_irq_set	irq;
-	u8 buffer[sizeof(struct vfio_irq_set) + sizeof(int)];
-};
-
-static void set_vfio_irq_eventd_payload(union vfio_irq_eventfd *evfd, int fd)
-{
-	memcpy(&evfd->irq.data, &fd, sizeof(fd));
-}
-
 /*
  * To support MSI and MSI-X with common code, track the host and guest states of
  * the MSI/MSI-X capability, and of individual vectors.
@@ -1213,108 +1202,21 @@ static int vfio_pci_init_msis(struct kvm *kvm, struct vfio_device *vdev,
 static void vfio_pci_disable_intx(struct kvm *kvm, struct vfio_device *vdev)
 {
 	struct vfio_pci_device *pdev = &vdev->pci;
-	int gsi = pdev->intx_gsi;
-	struct vfio_irq_set irq_set = {
-		.argsz	= sizeof(irq_set),
-		.flags	= VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER,
-		.index	= VFIO_PCI_INTX_IRQ_INDEX,
-	};
+	int gsi = pdev->intx.gsi;
 
-	if (pdev->intx_fd == -1)
+	if (pdev->intx.trigger_fd == -1)
 		return;
 
 	pr_debug("user requested MSI, disabling INTx %d", gsi);
 
-	ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &irq_set);
-	irq__del_irqfd(kvm, gsi, pdev->intx_fd);
-
-	close(pdev->intx_fd);
-	close(pdev->unmask_fd);
-	pdev->intx_fd = -1;
+	vfio_irq_disable(kvm, vdev, VFIO_PCI_INTX_IRQ_INDEX, &pdev->intx);
 }
 
 static int vfio_pci_enable_intx(struct kvm *kvm, struct vfio_device *vdev)
 {
-	int ret;
-	int trigger_fd, unmask_fd;
-	union vfio_irq_eventfd	trigger;
-	union vfio_irq_eventfd	unmask;
 	struct vfio_pci_device *pdev = &vdev->pci;
-	int gsi = pdev->intx_gsi;
 
-	if (pdev->intx_fd != -1)
-		return 0;
-
-	/*
-	 * PCI IRQ is level-triggered, so we use two eventfds. trigger_fd
-	 * signals an interrupt from host to guest, and unmask_fd signals the
-	 * deassertion of the line from guest to host.
-	 */
-	trigger_fd = eventfd(0, 0);
-	if (trigger_fd < 0) {
-		vfio_dev_err(vdev, "failed to create trigger eventfd");
-		return trigger_fd;
-	}
-
-	unmask_fd = eventfd(0, 0);
-	if (unmask_fd < 0) {
-		vfio_dev_err(vdev, "failed to create unmask eventfd");
-		close(trigger_fd);
-		return unmask_fd;
-	}
-
-	ret = irq__add_irqfd(kvm, gsi, trigger_fd, unmask_fd);
-	if (ret)
-		goto err_close;
-
-	trigger.irq = (struct vfio_irq_set) {
-		.argsz	= sizeof(trigger),
-		.flags	= VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER,
-		.index	= VFIO_PCI_INTX_IRQ_INDEX,
-		.start	= 0,
-		.count	= 1,
-	};
-	set_vfio_irq_eventd_payload(&trigger, trigger_fd);
-
-	ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &trigger);
-	if (ret < 0) {
-		vfio_dev_err(vdev, "failed to setup VFIO IRQ");
-		goto err_delete_line;
-	}
-
-	unmask.irq = (struct vfio_irq_set) {
-		.argsz	= sizeof(unmask),
-		.flags	= VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK,
-		.index	= VFIO_PCI_INTX_IRQ_INDEX,
-		.start	= 0,
-		.count	= 1,
-	};
-	set_vfio_irq_eventd_payload(&unmask, unmask_fd);
-
-	ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &unmask);
-	if (ret < 0) {
-		vfio_dev_err(vdev, "failed to setup unmask IRQ");
-		goto err_remove_event;
-	}
-
-	pdev->intx_fd = trigger_fd;
-	pdev->unmask_fd = unmask_fd;
-
-	return 0;
-
-err_remove_event:
-	/* Remove trigger event */
-	trigger.irq.flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER;
-	trigger.irq.count = 0;
-	ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &trigger);
-
-err_delete_line:
-	irq__del_irqfd(kvm, gsi, trigger_fd);
-
-err_close:
-	close(trigger_fd);
-	close(unmask_fd);
-	return ret;
+	return vfio_irq_enable(kvm, vdev, VFIO_PCI_INTX_IRQ_INDEX, &pdev->intx);
 }
 
 static int vfio_pci_init_intx(struct kvm *kvm, struct vfio_device *vdev)
@@ -1345,9 +1247,9 @@ static int vfio_pci_init_intx(struct kvm *kvm, struct vfio_device *vdev)
 	}
 
 	/* Guest is going to ovewrite our irq_line... */
-	pdev->intx_gsi = pdev->hdr.irq_line - KVM_IRQ_OFFSET;
+	pdev->intx.gsi = pdev->hdr.irq_line - KVM_IRQ_OFFSET;
 
-	pdev->intx_fd = -1;
+	pdev->intx.trigger_fd = -1;
 
 	return 0;
 }
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH kvmtool 3/9] vfio: Support edge-triggered wired IRQs
  2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
  2026-10-24 16:18 ` [PATCH kvmtool 1/9] vfio: Fix fd leak in vfio_group_get_for_dev() Mostafa Saleh
  2026-10-24 16:18 ` [PATCH kvmtool 2/9] vfio: Factor out wired IRQ setup and teardown Mostafa Saleh
@ 2026-10-24 16:18 ` Mostafa Saleh
  2026-10-24 16:18 ` [PATCH kvmtool 4/9] fdt: Allow fdt_irq_fn to generate multiple interrupts Mostafa Saleh
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:18 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

PCI INTx is are level-triggered and automasked, but platform
device interrupts can also be edge-triggered.

Pass an automasked boolean to vfio_irq_enable() and only create and
register unmask_fd when the interrupt is automasked.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 include/kvm/vfio.h |  2 +-
 vfio/core.c        | 51 ++++++++++++++++++++++++++--------------------
 vfio/pci.c         |  3 ++-
 3 files changed, 32 insertions(+), 24 deletions(-)

diff --git a/include/kvm/vfio.h b/include/kvm/vfio.h
index 9a5783e..0b2b105 100644
--- a/include/kvm/vfio.h
+++ b/include/kvm/vfio.h
@@ -141,7 +141,7 @@ int vfio_map_region(struct kvm *kvm, struct vfio_device *vdev,
 		    struct vfio_region *region);
 void vfio_unmap_region(struct kvm *kvm, struct vfio_region *region);
 int vfio_irq_enable(struct kvm *kvm, struct vfio_device *vdev,
-		    unsigned int index, struct vfio_irq *irq);
+		    unsigned int index, bool automasked, struct vfio_irq *irq);
 void vfio_irq_disable(struct kvm *kvm, struct vfio_device *vdev,
 		      unsigned int index, struct vfio_irq *irq);
 int vfio_pci_setup_device(struct kvm *kvm, struct vfio_device *device);
diff --git a/vfio/core.c b/vfio/core.c
index 24d3f2d..aa124d2 100644
--- a/vfio/core.c
+++ b/vfio/core.c
@@ -301,15 +301,16 @@ void vfio_irq_disable(struct kvm *kvm, struct vfio_device *vdev,
 	irq__del_irqfd(kvm, gsi, irq->trigger_fd);
 
 	close(irq->trigger_fd);
-	close(irq->unmask_fd);
+	if (irq->unmask_fd >= 0)
+		close(irq->unmask_fd);
 	irq->trigger_fd = -1;
 }
 
 int vfio_irq_enable(struct kvm *kvm, struct vfio_device *vdev,
-		    unsigned int index, struct vfio_irq *irq)
+		    unsigned int index, bool automasked, struct vfio_irq *irq)
 {
 	int ret;
-	int trigger_fd, unmask_fd;
+	int trigger_fd, unmask_fd = -1;
 	union vfio_irq_eventfd	trigger;
 	union vfio_irq_eventfd	unmask;
 	int gsi = irq->gsi;
@@ -318,7 +319,7 @@ int vfio_irq_enable(struct kvm *kvm, struct vfio_device *vdev,
 		return 0;
 
 	/*
-	 * PCI IRQ is level-triggered, so we use two eventfds. trigger_fd
+	 * Level-triggered IRQ is automasked, so we use two eventfds. trigger_fd
 	 * signals an interrupt from host to guest, and unmask_fd signals the
 	 * deassertion of the line from guest to host.
 	 */
@@ -328,11 +329,13 @@ int vfio_irq_enable(struct kvm *kvm, struct vfio_device *vdev,
 		return trigger_fd;
 	}
 
-	unmask_fd = eventfd(0, 0);
-	if (unmask_fd < 0) {
-		vfio_dev_err(vdev, "failed to create unmask eventfd");
-		close(trigger_fd);
-		return unmask_fd;
+	if (automasked) {
+		unmask_fd = eventfd(0, 0);
+		if (unmask_fd < 0) {
+			vfio_dev_err(vdev, "failed to create unmask eventfd");
+			close(trigger_fd);
+			return unmask_fd;
+		}
 	}
 
 	ret = irq__add_irqfd(kvm, gsi, trigger_fd, unmask_fd);
@@ -354,19 +357,22 @@ int vfio_irq_enable(struct kvm *kvm, struct vfio_device *vdev,
 		goto err_delete_line;
 	}
 
-	unmask.irq = (struct vfio_irq_set) {
-		.argsz	= sizeof(unmask),
-		.flags	= VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_UNMASK,
-		.index	= index,
-		.start	= 0,
-		.count	= 1,
-	};
-	set_vfio_irq_eventd_payload(&unmask, unmask_fd);
+	if (unmask_fd >= 0) {
+		unmask.irq = (struct vfio_irq_set) {
+			.argsz	= sizeof(unmask),
+			.flags	= VFIO_IRQ_SET_DATA_EVENTFD |
+				  VFIO_IRQ_SET_ACTION_UNMASK,
+			.index	= index,
+			.start	= 0,
+			.count	= 1,
+		};
+		set_vfio_irq_eventd_payload(&unmask, unmask_fd);
 
-	ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &unmask);
-	if (ret < 0) {
-		vfio_dev_err(vdev, "failed to setup unmask IRQ");
-		goto err_remove_event;
+		ret = ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, &unmask);
+		if (ret < 0) {
+			vfio_dev_err(vdev, "failed to setup unmask IRQ");
+			goto err_remove_event;
+		}
 	}
 
 	irq->trigger_fd = trigger_fd;
@@ -385,7 +391,8 @@ err_delete_line:
 
 err_close:
 	close(trigger_fd);
-	close(unmask_fd);
+	if (unmask_fd >= 0)
+		close(unmask_fd);
 	return ret;
 }
 
diff --git a/vfio/pci.c b/vfio/pci.c
index fb98435..9bf2f9e 100644
--- a/vfio/pci.c
+++ b/vfio/pci.c
@@ -1216,7 +1216,8 @@ static int vfio_pci_enable_intx(struct kvm *kvm, struct vfio_device *vdev)
 {
 	struct vfio_pci_device *pdev = &vdev->pci;
 
-	return vfio_irq_enable(kvm, vdev, VFIO_PCI_INTX_IRQ_INDEX, &pdev->intx);
+	return vfio_irq_enable(kvm, vdev, VFIO_PCI_INTX_IRQ_INDEX, true,
+			       &pdev->intx);
 }
 
 static int vfio_pci_init_intx(struct kvm *kvm, struct vfio_device *vdev)
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH kvmtool 4/9] fdt: Allow fdt_irq_fn to generate multiple interrupts
  2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
                   ` (2 preceding siblings ...)
  2026-10-24 16:18 ` [PATCH kvmtool 3/9] vfio: Support edge-triggered wired IRQs Mostafa Saleh
@ 2026-10-24 16:18 ` Mostafa Saleh
  2026-10-24 16:18 ` [PATCH kvmtool 5/9] fdt: Add helper to copy an FDT node Mostafa Saleh
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:18 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

fdt_irq_fn currently accepts a single interrupt line and trigger type,
which is sufficient for emulated MMIO devices with one interrupt.
Assigned platform devices can expose multiple interrupts that must be
written into a single "interrupts" property.

Update fdt_irq_fn and its arm64 and riscv implementations to accept an
array of interrupt lines and trigger types, and convert existing callers
to use the fdt_irq_fn typedef.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 arm64/fdt.c                  | 36 ++++++++++++++++++++++++++----------
 hw/cfi_flash.c               |  4 +---
 hw/rtc.c                     |  4 +---
 hw/serial.c                  |  3 ++-
 include/kvm/fdt.h            |  4 +++-
 riscv/fdt.c                  |  2 +-
 riscv/include/kvm/kvm-arch.h |  4 +++-
 riscv/irq.c                  | 27 +++++++++++++++++++--------
 virtio/mmio.c                | 10 ++++------
 9 files changed, 60 insertions(+), 34 deletions(-)

diff --git a/arm64/fdt.c b/arm64/fdt.c
index 3cbd36e..e2c37ad 100644
--- a/arm64/fdt.c
+++ b/arm64/fdt.c
@@ -60,15 +60,29 @@ static void generate_cpu_nodes(void *fdt, struct kvm *kvm)
 	_FDT(fdt_end_node(fdt));
 }
 
-static void generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type)
+static void generate_irq_prop(void *fdt, const u8 *irqs,
+			      const enum irq_type *irq_types,
+			      unsigned int num_irqs)
 {
-	u32 irq_prop[] = {
-		cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI),
-		cpu_to_fdt32(irq - GIC_SPI_IRQ_BASE),
-		cpu_to_fdt32(irq_type)
-	};
+	unsigned int i;
+	u32 *irq_prop;
 
-	_FDT(fdt_property(fdt, "interrupts", irq_prop, sizeof(irq_prop)));
+	if (!num_irqs)
+		return;
+
+	irq_prop = calloc(num_irqs, 3 * sizeof(*irq_prop));
+	if (!irq_prop)
+		die("Failed to allocate the interrupts property");
+
+	for (i = 0; i < num_irqs; i++) {
+		irq_prop[3 * i] = cpu_to_fdt32(GIC_FDT_IRQ_TYPE_SPI);
+		irq_prop[3 * i + 1] = cpu_to_fdt32(irqs[i] - GIC_SPI_IRQ_BASE);
+		irq_prop[3 * i + 2] = cpu_to_fdt32(irq_types[i]);
+	}
+
+	_FDT(fdt_property(fdt, "interrupts", irq_prop,
+			  3 * sizeof(*irq_prop) * num_irqs));
+	free(irq_prop);
 }
 
 static bool emit_dma_regions;
@@ -78,9 +92,11 @@ void generate_dma_region_prop(void *fdt)
 		_FDT(fdt_property_cell(fdt, "memory-region", PHANDLE_DMA));
 }
 
-static void generate_aux_props(void *fdt, u8 irq, enum irq_type irq_type)
+static void generate_aux_props(void *fdt, const u8 *irqs,
+			       const enum irq_type *irq_types,
+			       unsigned int num_irqs)
 {
-	generate_irq_prop(fdt, irq, irq_type);
+	generate_irq_prop(fdt, irqs, irq_types, num_irqs);
 	generate_dma_region_prop(fdt);
 }
 
@@ -125,7 +141,7 @@ static int setup_fdt(struct kvm *kvm)
 	void *fdt_dest		= guest_flat_to_host(kvm,
 						     kvm->arch.dtb_guest_start);
 	void (*generate_mmio_fdt_nodes)(void *, struct device_header *,
-					void (*)(void *, u8, enum irq_type));
+					fdt_irq_fn);
 	void (*generate_cpu_peripheral_fdt_nodes)(void *, struct kvm *)
 					= kvm->cpus[0]->generate_fdt_nodes;
 
diff --git a/hw/cfi_flash.c b/hw/cfi_flash.c
index 4413955..5641af6 100644
--- a/hw/cfi_flash.c
+++ b/hw/cfi_flash.c
@@ -518,9 +518,7 @@ static void cfi_flash_mmio(struct kvm_cpu *vcpu,
 #ifdef CONFIG_HAS_LIBFDT
 static void generate_cfi_flash_fdt_node(void *fdt,
 					struct device_header *dev_hdr,
-					void (*generate_irq_prop)(void *fdt,
-								  u8 irq,
-								enum irq_type))
+					fdt_irq_fn generate_irq_prop)
 {
 	struct cfi_flash_device *sfdev;
 	u64 reg_prop[2];
diff --git a/hw/rtc.c b/hw/rtc.c
index e3523a1..fc6533a 100644
--- a/hw/rtc.c
+++ b/hw/rtc.c
@@ -136,9 +136,7 @@ static void cmos_ram_io(struct kvm_cpu *vcpu, u64 addr, u8 *data,
 #ifdef CONFIG_HAS_LIBFDT
 static void generate_rtc_fdt_node(void *fdt,
 				  struct device_header *dev_hdr,
-				  void (*generate_irq_prop)(void *fdt,
-							    u8 irq,
-							    enum irq_type))
+				  fdt_irq_fn generate_irq_prop)
 {
 	u64 reg_prop[2] = { cpu_to_fdt64(RTC_BASE_ADDRESS), cpu_to_fdt64(2) };
 
diff --git a/hw/serial.c b/hw/serial.c
index 68e6186..8e26837 100644
--- a/hw/serial.c
+++ b/hw/serial.c
@@ -434,6 +434,7 @@ void serial8250_generate_fdt_node(void *fdt, struct device_header *dev_hdr,
 		cpu_to_fdt64(addr),
 		cpu_to_fdt64(8),
 	};
+	enum irq_type irq_type = IRQ_TYPE_LEVEL_HIGH;
 
 	snprintf(dev_name, DEVICE_NAME_MAX_LEN, "U6_16550A@%llx", addr);
 
@@ -446,7 +447,7 @@ void serial8250_generate_fdt_node(void *fdt, struct device_header *dev_hdr,
 	_FDT(fdt_begin_node(fdt, dev_name));
 	_FDT(fdt_property_string(fdt, "compatible", "ns16550a"));
 	_FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
-	irq_fn(fdt, dev->irq, IRQ_TYPE_LEVEL_HIGH);
+	irq_fn(fdt, &dev->irq, &irq_type, 1);
 	_FDT(fdt_property_cell(fdt, "clock-frequency", 1843200));
 	_FDT(fdt_end_node(fdt));
 }
diff --git a/include/kvm/fdt.h b/include/kvm/fdt.h
index 060c37b..e84c5c1 100644
--- a/include/kvm/fdt.h
+++ b/include/kvm/fdt.h
@@ -25,7 +25,9 @@ enum irq_type {
 	IRQ_TYPE_LEVEL_MASK	= (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH),
 };
 
-typedef void (*fdt_irq_fn)(void *fdt, u8 irq, enum irq_type irq_type);
+typedef void (*fdt_irq_fn)(void *fdt, const u8 *irqs,
+			   const enum irq_type *irq_types,
+			   unsigned int num_irqs);
 
 extern char *fdt_stdout_path;
 
diff --git a/riscv/fdt.c b/riscv/fdt.c
index 1eb45fc..b571724 100644
--- a/riscv/fdt.c
+++ b/riscv/fdt.c
@@ -351,7 +351,7 @@ static int setup_fdt(struct kvm *kvm)
 	void *fdt_dest		= guest_flat_to_host(kvm,
 						     kvm->arch.dtb_guest_start);
 	void (*generate_mmio_fdt_nodes)(void *, struct device_header *,
-					void (*)(void *, u8, enum irq_type));
+					fdt_irq_fn);
 
 	/* Create new tree without a reserve map */
 	_FDT(fdt_create(fdt, FDT_MAX_SIZE));
diff --git a/riscv/include/kvm/kvm-arch.h b/riscv/include/kvm/kvm-arch.h
index 1bb2d32..460b90b 100644
--- a/riscv/include/kvm/kvm-arch.h
+++ b/riscv/include/kvm/kvm-arch.h
@@ -117,7 +117,9 @@ void riscv__del_irqfd(struct kvm *kvm, unsigned int gsi, int trigger_fd);
 
 int riscv__setup_irqfd_lines(struct kvm *kvm);
 
-void riscv__generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type);
+void riscv__generate_irq_prop(void *fdt, const u8 *irqs,
+			      const enum irq_type *irq_types,
+			      unsigned int num_irqs);
 
 void riscv__irqchip_create(struct kvm *kvm);
 
diff --git a/riscv/irq.c b/riscv/irq.c
index 62dbaa8..2c35f25 100644
--- a/riscv/irq.c
+++ b/riscv/irq.c
@@ -119,18 +119,29 @@ int riscv__setup_irqfd_lines(struct kvm *kvm)
 	return 0;
 }
 
-void riscv__generate_irq_prop(void *fdt, u8 irq, enum irq_type irq_type)
+void riscv__generate_irq_prop(void *fdt, const u8 *irqs,
+			      const enum irq_type *irq_types,
+			      unsigned int num_irqs)
 {
-	u32 prop[2], size;
+	unsigned int i, cells = riscv_irqchip_line_sensing ? 2 : 1;
+	u32 *prop;
 
-	prop[0] = cpu_to_fdt32(irq);
-	size = sizeof(u32);
-	if (riscv_irqchip_line_sensing) {
-		prop[1] = cpu_to_fdt32(irq_type);
-		size += sizeof(u32);
+	if (!num_irqs)
+		return;
+
+	prop = calloc(num_irqs, cells * sizeof(*prop));
+	if (!prop)
+		die("Failed to allocate the interrupts property");
+
+	for (i = 0; i < num_irqs; i++) {
+		prop[cells * i] = cpu_to_fdt32(irqs[i]);
+		if (riscv_irqchip_line_sensing)
+			prop[cells * i + 1] = cpu_to_fdt32(irq_types[i]);
 	}
 
-	_FDT(fdt_property(fdt, "interrupts", prop, size));
+	_FDT(fdt_property(fdt, "interrupts", prop,
+			  cells * sizeof(*prop) * num_irqs));
+	free(prop);
 }
 
 static void (*riscv__irqchip_create_funcs[])(struct kvm *kvm) = {
diff --git a/virtio/mmio.c b/virtio/mmio.c
index fae73b5..0c66724 100644
--- a/virtio/mmio.c
+++ b/virtio/mmio.c
@@ -113,9 +113,7 @@ int virtio_mmio_signal_config(struct kvm *kvm, struct virtio_device *vdev)
 static
 void generate_virtio_mmio_fdt_node(void *fdt,
 				   struct device_header *dev_hdr,
-				   void (*generate_irq_prop)(void *fdt,
-							     u8 irq,
-							     enum irq_type))
+				   fdt_irq_fn generate_irq_prop)
 {
 	char dev_name[DEVICE_NAME_MAX_LEN];
 	struct virtio_mmio *vmmio = container_of(dev_hdr,
@@ -126,6 +124,7 @@ void generate_virtio_mmio_fdt_node(void *fdt,
 		cpu_to_fdt64(addr),
 		cpu_to_fdt64(VIRTIO_MMIO_IO_SIZE),
 	};
+	enum irq_type irq_type = IRQ_TYPE_EDGE_RISING;
 
 	snprintf(dev_name, DEVICE_NAME_MAX_LEN, "virtio@%llx", addr);
 
@@ -133,14 +132,13 @@ void generate_virtio_mmio_fdt_node(void *fdt,
 	_FDT(fdt_property_string(fdt, "compatible", "virtio,mmio"));
 	_FDT(fdt_property(fdt, "reg", reg_prop, sizeof(reg_prop)));
 	_FDT(fdt_property(fdt, "dma-coherent", NULL, 0));
-	generate_irq_prop(fdt, vmmio->irq, IRQ_TYPE_EDGE_RISING);
+	generate_irq_prop(fdt, &vmmio->irq, &irq_type, 1);
 	_FDT(fdt_end_node(fdt));
 }
 #else
 static void generate_virtio_mmio_fdt_node(void *fdt,
 					  struct device_header *dev_hdr,
-					  void (*generate_irq_prop)(void *fdt,
-								    u8 irq))
+					  fdt_irq_fn generate_irq_prop)
 {
 	die("Unable to generate device tree nodes without libfdt\n");
 }
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH kvmtool 5/9] fdt: Add helper to copy an FDT node
  2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
                   ` (3 preceding siblings ...)
  2026-10-24 16:18 ` [PATCH kvmtool 4/9] fdt: Allow fdt_irq_fn to generate multiple interrupts Mostafa Saleh
@ 2026-10-24 16:18 ` Mostafa Saleh
  2026-10-24 16:18 ` [PATCH kvmtool 6/9] vfio: Reserve guest address space for VFIO platform Mostafa Saleh
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:18 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

Add fdt__copy_node() to recursively copy all properties and subnodes of
a source FDT node into a destination FDT.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 include/kvm/fdt.h | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/include/kvm/fdt.h b/include/kvm/fdt.h
index e84c5c1..e7b65e8 100644
--- a/include/kvm/fdt.h
+++ b/include/kvm/fdt.h
@@ -8,6 +8,7 @@
 #include <linux/types.h>
 
 #include "kvm/fdt-arch.h"
+#include "kvm/util.h"
 
 #define FDT_MAX_SIZE	0x10000
 
@@ -41,4 +42,31 @@ extern char *fdt_stdout_path;
 		}							\
 	} while (0)
 
+#ifdef CONFIG_HAS_LIBFDT
+static inline void fdt__copy_node(void *fdt_dest, const void *fdt_src,
+				  int offset)
+{
+	int prop_offset, subnode_offset, len;
+	const char *name;
+	const void *prop;
+
+	fdt_for_each_property_offset(prop_offset, fdt_src, offset) {
+		prop = fdt_getprop_by_offset(fdt_src, prop_offset, &name, &len);
+		if (!prop)
+			continue;
+
+		_FDT(fdt_property(fdt_dest, name, prop, len));
+	}
+
+	fdt_for_each_subnode(subnode_offset, fdt_src, offset) {
+		name = fdt_get_name(fdt_src, subnode_offset, NULL);
+		if (!name)
+			continue;
+		_FDT(fdt_begin_node(fdt_dest, name));
+		fdt__copy_node(fdt_dest, fdt_src, subnode_offset);
+		_FDT(fdt_end_node(fdt_dest));
+	}
+}
+#endif
+
 #endif /* KVM__FDT_H */
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH kvmtool 6/9] vfio: Reserve guest address space for VFIO platform
  2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
                   ` (4 preceding siblings ...)
  2026-10-24 16:18 ` [PATCH kvmtool 5/9] fdt: Add helper to copy an FDT node Mostafa Saleh
@ 2026-10-24 16:18 ` Mostafa Saleh
  2026-10-24 16:19 ` [PATCH kvmtool 7/9] vfio: Add basic support for vfio-platform Mostafa Saleh
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:18 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

Assigned platform devices need guest physical addresses for their
MMIO regions.

Reserve an area called KVM_VFIO_MMIO_AREA.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 arm64/include/kvm/kvm-arch.h   | 10 ++++++----
 mips/include/kvm/kvm-arch.h    |  2 ++
 powerpc/include/kvm/kvm-arch.h |  2 ++
 riscv/include/kvm/kvm-arch.h   |  3 +++
 x86/include/kvm/kvm-arch.h     |  2 ++
 5 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/arm64/include/kvm/kvm-arch.h b/arm64/include/kvm/kvm-arch.h
index e7dd526..cddb404 100644
--- a/arm64/include/kvm/kvm-arch.h
+++ b/arm64/include/kvm/kvm-arch.h
@@ -19,8 +19,8 @@
  * +-------+----+-------+-------+--------+-----+---------+---......
  * |  PCI  |////| plat  |       |        |     |         |
  * |  I/O  |////| MMIO: | Flash | virtio | GIC |   PCI   |  DRAM
- * | space |////| UART, |       |  MMIO  |     |  (AXI)  |
- * |       |////| RTC,  |       |        |     |         |
+ * | space |////| UART, |       |  /VFIO |     |  (AXI)  |
+ * |       |////| RTC,  |       |  MMIO  |     |         |
  * |       |////| PVTIME|       |        |     |         |
  * +-------+----+-------+-------+--------+-----+---------+---......
  */
@@ -47,8 +47,10 @@
 #define KVM_FLASH_MAX_SIZE	0x1000000
 
 #define KVM_VIRTIO_MMIO_AREA	(KVM_FLASH_MMIO_BASE + KVM_FLASH_MAX_SIZE)
-#define ARM_VIRTIO_MMIO_SIZE	(ARM_AXI_AREA - \
-				(KVM_VIRTIO_MMIO_AREA + ARM_GIC_SIZE))
+#define ARM_VIRTIO_MMIO_SIZE	SZ_128M
+
+#define KVM_VFIO_MMIO_AREA	(KVM_VIRTIO_MMIO_AREA + ARM_VIRTIO_MMIO_SIZE)
+#define KVM_VFIO_MMIO_SIZE	SZ_32M
 
 #define ARM_GIC_DIST_BASE	(ARM_AXI_AREA - ARM_GIC_DIST_SIZE)
 #define ARM_GIC_CPUI_BASE	(ARM_GIC_DIST_BASE - ARM_GIC_CPUI_SIZE)
diff --git a/mips/include/kvm/kvm-arch.h b/mips/include/kvm/kvm-arch.h
index f5b9044..d9a9a64 100644
--- a/mips/include/kvm/kvm-arch.h
+++ b/mips/include/kvm/kvm-arch.h
@@ -15,6 +15,8 @@
 #define KVM_PCI_CFG_AREA	KVM_MMIO_START
 #define KVM_PCI_MMIO_AREA	(KVM_MMIO_START + 0x1000000)
 #define KVM_VIRTIO_MMIO_AREA	(KVM_MMIO_START + 0x2000000)
+#define KVM_VFIO_MMIO_AREA	(KVM_MMIO_START + 0x3000000)
+#define KVM_VFIO_MMIO_SIZE	SZ_32M
 #define KVM_MMIO_SIZE		0x10000000
 
 /*
diff --git a/powerpc/include/kvm/kvm-arch.h b/powerpc/include/kvm/kvm-arch.h
index ff42c13..cebf73c 100644
--- a/powerpc/include/kvm/kvm-arch.h
+++ b/powerpc/include/kvm/kvm-arch.h
@@ -42,6 +42,8 @@
 #define KVM_PCI_CFG_AREA		0x1000000
 #define KVM_PCI_MMIO_AREA		0x2000000
 #define KVM_VIRTIO_MMIO_AREA		0x3000000
+#define KVM_VFIO_MMIO_AREA		0x4000000
+#define KVM_VFIO_MMIO_SIZE		SZ_32M
 
 #define KVM_IRQ_OFFSET			16
 
diff --git a/riscv/include/kvm/kvm-arch.h b/riscv/include/kvm/kvm-arch.h
index 460b90b..b347216 100644
--- a/riscv/include/kvm/kvm-arch.h
+++ b/riscv/include/kvm/kvm-arch.h
@@ -45,6 +45,9 @@
 #define KVM_PCI_CFG_AREA	RISCV_PCI
 #define KVM_PCI_MMIO_AREA	(KVM_PCI_CFG_AREA + RISCV_PCI_CFG_SIZE)
 #define KVM_VIRTIO_MMIO_AREA	(RISCV_RTC_MMIO_BASE + RISCV_RTC_MMIO_SIZE)
+#define KVM_VIRTIO_MMIO_SIZE	SZ_128M
+#define KVM_VFIO_MMIO_AREA	(KVM_VIRTIO_MMIO_AREA + KVM_VIRTIO_MMIO_SIZE)
+#define KVM_VFIO_MMIO_SIZE	SZ_32M
 
 #define KVM_IOEVENTFD_HAS_PIO	0
 
diff --git a/x86/include/kvm/kvm-arch.h b/x86/include/kvm/kvm-arch.h
index 73a957b..cc2defa 100644
--- a/x86/include/kvm/kvm-arch.h
+++ b/x86/include/kvm/kvm-arch.h
@@ -24,6 +24,8 @@
 #define KVM_PCI_CFG_AREA	(KVM_MMIO_START + 0x1000000)
 #define KVM_PCI_MMIO_AREA	(KVM_MMIO_START + 0x2000000)
 #define KVM_VIRTIO_MMIO_AREA	(KVM_MMIO_START + 0x3000000)
+#define KVM_VFIO_MMIO_AREA	(KVM_MMIO_START + 0x4000000)
+#define KVM_VFIO_MMIO_SIZE	SZ_32M
 
 #define KVM_IRQ_OFFSET		5
 
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH kvmtool 7/9] vfio: Add basic support for vfio-platform
  2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
                   ` (5 preceding siblings ...)
  2026-10-24 16:18 ` [PATCH kvmtool 6/9] vfio: Reserve guest address space for VFIO platform Mostafa Saleh
@ 2026-10-24 16:19 ` Mostafa Saleh
  2026-10-24 16:19 ` [PATCH kvmtool 8/9] vfio: platform: Add interrupts support Mostafa Saleh
  2026-10-24 16:19 ` [PATCH kvmtool 9/9] vfio: platform: Take the guest node from a device tree template Mostafa Saleh
  8 siblings, 0 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:19 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

A VFIO device can be assigned to a guest with new cmdline
--vfio-platform ${dev} , where ${dev} is the name of the device under
/sys/bus/platform.

The device is expected to be bound to vfio-platform driver before
running the tool.

The MMIO regions of the device are mapped into the guest address
space reserved for VFIO, and described in a device tree node.
The node only carries reg, so no guest driver binds to it yet.
Interrupts and the rest of the node are added in the next patches.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 Makefile           |   1 +
 builtin-run.c      |   3 +
 include/kvm/vfio.h |   3 +
 vfio/core.c        |  22 +++++++
 vfio/platform.c    | 158 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 187 insertions(+)
 create mode 100644 vfio/platform.c

diff --git a/Makefile b/Makefile
index 1c0e7f5..8d1c0d9 100644
--- a/Makefile
+++ b/Makefile
@@ -65,6 +65,7 @@ OBJS	+= pci.o
 OBJS	+= term.o
 OBJS	+= vfio/core.o
 OBJS	+= vfio/pci.o
+OBJS	+= vfio/platform.o
 OBJS	+= virtio/blk.o
 OBJS	+= virtio/scsi.o
 OBJS	+= virtio/console.o
diff --git a/builtin-run.c b/builtin-run.c
index 81f255f..b09fc2e 100644
--- a/builtin-run.c
+++ b/builtin-run.c
@@ -261,6 +261,9 @@ static int loglevel_parser(const struct option *opt, const char *arg, int unset)
 	OPT_GROUP("VFIO options:"),					\
 	OPT_CALLBACK('\0', "vfio-pci", NULL, "[domain:]bus:dev.fn",	\
 		     "Assign a PCI device to the virtual machine",	\
+		     vfio_device_parser, kvm),				\
+	OPT_CALLBACK('\0', "vfio-platform", NULL, "device",		\
+		     "Assign a platform device to the virtual machine",	\
 		     vfio_device_parser, kvm),				\
 									\
 	OPT_GROUP("Debug options:"),					\
diff --git a/include/kvm/vfio.h b/include/kvm/vfio.h
index 0b2b105..f641292 100644
--- a/include/kvm/vfio.h
+++ b/include/kvm/vfio.h
@@ -24,6 +24,7 @@
 
 enum vfio_device_type {
 	VFIO_DEVICE_PCI,
+	VFIO_DEVICE_PLATFORM,
 };
 
 /* MSI/MSI-X capability enabled */
@@ -146,5 +147,7 @@ void vfio_irq_disable(struct kvm *kvm, struct vfio_device *vdev,
 		      unsigned int index, struct vfio_irq *irq);
 int vfio_pci_setup_device(struct kvm *kvm, struct vfio_device *device);
 void vfio_pci_teardown_device(struct kvm *kvm, struct vfio_device *vdev);
+int vfio_platform_setup_device(struct kvm *kvm, struct vfio_device *device);
+void vfio_platform_teardown_device(struct kvm *kvm, struct vfio_device *vdev);
 
 #endif /* KVM__VFIO_H */
diff --git a/vfio/core.c b/vfio/core.c
index aa124d2..6823a95 100644
--- a/vfio/core.c
+++ b/vfio/core.c
@@ -41,6 +41,19 @@ static int vfio_device_pci_parser(const struct option *opt, char *arg,
 	return 0;
 }
 
+/* @arg is the name of the device under /sys/bus/platform/devices */
+static int vfio_device_platform_parser(const struct option *opt, char *arg,
+				       struct vfio_device_params *dev)
+{
+	dev->type = VFIO_DEVICE_PLATFORM;
+	dev->bus = "platform";
+	dev->name = strdup(arg);
+	if (!dev->name)
+		return -ENOMEM;
+
+	return 0;
+}
+
 int vfio_device_parser(const struct option *opt, const char *arg, int unset)
 {
 	int ret = -EINVAL;
@@ -72,6 +85,8 @@ int vfio_device_parser(const struct option *opt, const char *arg, int unset)
 
 	if (!strcmp(opt->long_name, "vfio-pci"))
 		ret = vfio_device_pci_parser(opt, cur, dev);
+	else if (!strcmp(opt->long_name, "vfio-platform"))
+		ret = vfio_device_platform_parser(opt, cur, dev);
 	else
 		ret = -EINVAL;
 
@@ -433,6 +448,10 @@ static int vfio_configure_device(struct kvm *kvm, struct vfio_device *vdev)
 		BUG_ON(!(vdev->info.flags & VFIO_DEVICE_FLAGS_PCI));
 		ret = vfio_pci_setup_device(kvm, vdev);
 		break;
+	case VFIO_DEVICE_PLATFORM:
+		BUG_ON(!(vdev->info.flags & VFIO_DEVICE_FLAGS_PLATFORM));
+		ret = vfio_platform_setup_device(kvm, vdev);
+		break;
 	default:
 		BUG_ON(1);
 		ret = -EINVAL;
@@ -703,6 +722,9 @@ static void vfio_device_exit(struct kvm *kvm, struct vfio_device *vdev)
 	case VFIO_DEVICE_PCI:
 		vfio_pci_teardown_device(kvm, vdev);
 		break;
+	case VFIO_DEVICE_PLATFORM:
+		vfio_platform_teardown_device(kvm, vdev);
+		break;
 	default:
 		vfio_dev_warn(vdev, "no teardown function for device");
 	}
diff --git a/vfio/platform.c b/vfio/platform.c
new file mode 100644
index 0000000..c5b447f
--- /dev/null
+++ b/vfio/platform.c
@@ -0,0 +1,158 @@
+#include "linux/sizes.h"
+
+#include "kvm/kvm.h"
+#include "kvm/vfio.h"
+
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include "kvm/fdt.h"
+
+static u64 vfio_mmio_io_space_next = KVM_VFIO_MMIO_AREA;
+
+static int vfio_mmio_alloc_io_space(u64 size, u64 *addr)
+{
+	u64 align = max_t(u64, PAGE_SIZE, MAX_PAGE_SIZE);
+	u64 end = KVM_VFIO_MMIO_AREA + KVM_VFIO_MMIO_SIZE;
+
+	vfio_mmio_io_space_next = ALIGN(vfio_mmio_io_space_next, align);
+	size = ALIGN(size, align);
+	if (size > end - vfio_mmio_io_space_next)
+		return -ENOSPC;
+
+	*addr = vfio_mmio_io_space_next;
+	vfio_mmio_io_space_next += size;
+
+	return 0;
+}
+
+#ifdef CONFIG_HAS_LIBFDT
+static void generate_mmio_fdt_node(void *fdt,
+				   struct device_header *dev_hdr,
+				   fdt_irq_fn generate_irq_prop)
+{
+	struct vfio_device *vdev = container_of(dev_hdr,
+						struct vfio_device, dev_hdr);
+	unsigned int i, num_regions = vdev->info.num_regions;
+	char node_name[PATH_MAX];
+	u64 *reg_prop;
+
+	reg_prop = calloc(num_regions, 2 * sizeof(*reg_prop));
+	if (!reg_prop)
+		die("Failed to allocate the reg property");
+
+	for (i = 0; i < num_regions; i++) {
+		reg_prop[2 * i] = cpu_to_fdt64(vdev->regions[i].guest_phys_addr);
+		reg_prop[2 * i + 1] = cpu_to_fdt64(vdev->regions[i].info.size);
+	}
+
+	snprintf(node_name, sizeof(node_name), "%s@%llx",
+		 vdev->params->name, vdev->regions[0].guest_phys_addr);
+
+	_FDT(fdt_begin_node(fdt, node_name));
+	_FDT(fdt_property(fdt, "reg", reg_prop,
+			  2 * sizeof(*reg_prop) * num_regions));
+	_FDT(fdt_end_node(fdt));
+
+	free(reg_prop);
+}
+#endif /* CONFIG_HAS_LIBFDT */
+
+static void vfio_platform_unmap_regions(struct kvm *kvm,
+					struct vfio_device *vdev,
+					unsigned int nr_regions)
+{
+	unsigned int i;
+
+	for (i = 0; i < nr_regions; i++)
+		vfio_unmap_region(kvm, &vdev->regions[i]);
+}
+
+static int vfio_platform_configure_regs(struct kvm *kvm,
+					struct vfio_device *vdev)
+{
+	int ret = 0;
+	unsigned int i = 0;
+	struct vfio_region *region;
+	struct vfio_region_info *info;
+
+	if (!vdev->info.num_regions)
+		return -ENODEV;
+
+	for (i = 0; i < vdev->info.num_regions; i++) {
+		info = &vdev->regions[i].info;
+		*info = (struct vfio_region_info) {
+			.argsz = sizeof(*info),
+			.index = i,
+		};
+
+		if (ioctl(vdev->fd, VFIO_DEVICE_GET_REGION_INFO, info)) {
+			ret = -errno;
+			vfio_dev_err(vdev, "failed to get info for region[%u]", i);
+			goto err_unmap;
+		}
+
+		if (!info->size) {
+			ret = -EINVAL;
+			goto err_unmap;
+		}
+
+		region = &vdev->regions[i];
+		region->vdev = vdev;
+		region->is_ioport = false;
+		ret = vfio_mmio_alloc_io_space(region->info.size,
+					       &region->guest_phys_addr);
+		if (ret) {
+			vfio_dev_err(vdev, "no guest address space left for region[%u] (0x%llx bytes)",
+				     i, info->size);
+			goto err_unmap;
+		}
+
+		ret = vfio_map_region(kvm, vdev, region);
+		if (ret) {
+			vfio_dev_err(vdev, "failed to map region[%u] at 0x%llx", i, info->offset);
+			goto err_unmap;
+		}
+	}
+
+	return 0;
+
+err_unmap:
+	vfio_platform_unmap_regions(kvm, vdev, i);
+	return ret;
+}
+
+int vfio_platform_setup_device(struct kvm *kvm, struct vfio_device *vdev)
+{
+	int ret;
+
+	ret = vfio_platform_configure_regs(kvm, vdev);
+	if (ret) {
+		vfio_dev_err(vdev, "failed to configure regs");
+		return ret;
+	}
+
+	vdev->dev_hdr = (struct device_header) {
+		.bus_type	= DEVICE_BUS_MMIO,
+#ifdef CONFIG_HAS_LIBFDT
+		.data		= generate_mmio_fdt_node,
+#endif
+	};
+
+	ret = device__register(&vdev->dev_hdr);
+	if (ret) {
+		vfio_dev_err(vdev, "failed to register VFIO device");
+		vfio_platform_unmap_regions(kvm, vdev, vdev->info.num_regions);
+		return ret;
+	}
+
+	return 0;
+}
+
+void vfio_platform_teardown_device(struct kvm *kvm, struct vfio_device *vdev)
+{
+	vfio_platform_unmap_regions(kvm, vdev, vdev->info.num_regions);
+
+	device__unregister(&vdev->dev_hdr);
+}
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH kvmtool 8/9] vfio: platform: Add interrupts support
  2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
                   ` (6 preceding siblings ...)
  2026-10-24 16:19 ` [PATCH kvmtool 7/9] vfio: Add basic support for vfio-platform Mostafa Saleh
@ 2026-10-24 16:19 ` Mostafa Saleh
  2026-10-24 16:19 ` [PATCH kvmtool 9/9] vfio: platform: Take the guest node from a device tree template Mostafa Saleh
  8 siblings, 0 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:19 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

Configures interrupts of the device, relying on the new common
VFIO interrupt code.

And generated the dt node for interrupts.

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 include/kvm/vfio.h |   9 +++-
 vfio/platform.c    | 113 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 119 insertions(+), 3 deletions(-)

diff --git a/include/kvm/vfio.h b/include/kvm/vfio.h
index f641292..b3034a0 100644
--- a/include/kvm/vfio.h
+++ b/include/kvm/vfio.h
@@ -89,6 +89,10 @@ struct vfio_pci_device {
 	struct vfio_pci_msix_pba	msix_pba;
 };
 
+struct vfio_platform_device {
+	struct vfio_irq			*irqs;
+};
+
 struct vfio_region {
 	struct vfio_region_info		info;
 	struct vfio_device		*vdev;
@@ -109,7 +113,10 @@ struct vfio_device {
 
 	char				*sysfs_path;
 
-	struct vfio_pci_device		pci;
+	union {
+		struct vfio_pci_device		pci;
+		struct vfio_platform_device	platform;
+	};
 };
 
 struct vfio_device_params {
diff --git a/vfio/platform.c b/vfio/platform.c
index c5b447f..22052a6 100644
--- a/vfio/platform.c
+++ b/vfio/platform.c
@@ -1,5 +1,6 @@
 #include "linux/sizes.h"
 
+#include "kvm/irq.h"
 #include "kvm/kvm.h"
 #include "kvm/vfio.h"
 
@@ -28,6 +29,36 @@ static int vfio_mmio_alloc_io_space(u64 size, u64 *addr)
 }
 
 #ifdef CONFIG_HAS_LIBFDT
+static void generate_interrupts_prop(void *fdt, struct vfio_device *vdev,
+				     fdt_irq_fn generate_irq_prop)
+{
+	struct vfio_platform_device *pdev = &vdev->platform;
+	unsigned int i, num_irqs = vdev->info.num_irqs;
+	enum irq_type *irq_types = NULL;
+	u8 *irqs = NULL;
+
+	if (num_irqs) {
+		irqs = calloc(num_irqs, sizeof(*irqs));
+		irq_types = calloc(num_irqs, sizeof(*irq_types));
+		if (!irqs || !irq_types)
+			die("Failed to allocate the interrupts property");
+
+		for (i = 0; i < num_irqs; i++) {
+			struct vfio_irq *irq = &pdev->irqs[i];
+
+			irqs[i] = irq->gsi + KVM_IRQ_OFFSET;
+			irq_types[i] = irq->unmask_fd >= 0 ?
+				       IRQ_TYPE_LEVEL_HIGH :
+				       IRQ_TYPE_EDGE_RISING;
+		}
+	}
+
+	generate_irq_prop(fdt, irqs, irq_types, num_irqs);
+
+	free(irqs);
+	free(irq_types);
+}
+
 static void generate_mmio_fdt_node(void *fdt,
 				   struct device_header *dev_hdr,
 				   fdt_irq_fn generate_irq_prop)
@@ -53,6 +84,7 @@ static void generate_mmio_fdt_node(void *fdt,
 	_FDT(fdt_begin_node(fdt, node_name));
 	_FDT(fdt_property(fdt, "reg", reg_prop,
 			  2 * sizeof(*reg_prop) * num_regions));
+	generate_interrupts_prop(fdt, vdev, generate_irq_prop);
 	_FDT(fdt_end_node(fdt));
 
 	free(reg_prop);
@@ -123,8 +155,65 @@ err_unmap:
 	return ret;
 }
 
+static int vfio_platform_setup_irq(struct kvm *kvm, struct vfio_device *vdev,
+				   unsigned int index)
+{
+	struct vfio_irq *irq = &vdev->platform.irqs[index];
+	struct vfio_irq_info irq_info = {
+		.argsz	= sizeof(irq_info),
+		.index	= index,
+	};
+	int ret;
+
+	if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info)) {
+		ret = -errno;
+		vfio_dev_err(vdev, "failed to get irq[%u] info", index);
+		return ret;
+	}
+
+	if (!(irq_info.flags & VFIO_IRQ_INFO_EVENTFD)) {
+		vfio_dev_err(vdev, "irq[%u] is not eventfd capable", index);
+		return -EINVAL;
+	}
+
+	irq->gsi = irq__alloc_line() - KVM_IRQ_OFFSET;
+	irq->trigger_fd = -1;
+
+	return vfio_irq_enable(kvm, vdev, index,
+			       irq_info.flags & VFIO_IRQ_INFO_AUTOMASKED, irq);
+}
+
+static int vfio_platform_configure_dev_irqs(struct kvm *kvm, struct vfio_device *vdev)
+{
+	struct vfio_platform_device *pdev = &vdev->platform;
+	unsigned int i, num_irqs = vdev->info.num_irqs;
+	int ret;
+
+	if (!num_irqs)
+		return 0;
+
+	pdev->irqs = calloc(num_irqs, sizeof(*pdev->irqs));
+	if (!pdev->irqs)
+		return -ENOMEM;
+
+	for (i = 0; i < num_irqs; i++) {
+		ret = vfio_platform_setup_irq(kvm, vdev, i);
+		if (ret)
+			goto err_teardown;
+	}
+
+	return 0;
+
+err_teardown:
+	while (i--)
+		vfio_irq_disable(kvm, vdev, i, &pdev->irqs[i]);
+	free(pdev->irqs);
+	return ret;
+}
+
 int vfio_platform_setup_device(struct kvm *kvm, struct vfio_device *vdev)
 {
+	unsigned int i;
 	int ret;
 
 	ret = vfio_platform_configure_regs(kvm, vdev);
@@ -133,6 +222,12 @@ int vfio_platform_setup_device(struct kvm *kvm, struct vfio_device *vdev)
 		return ret;
 	}
 
+	ret = vfio_platform_configure_dev_irqs(kvm, vdev);
+	if (ret) {
+		vfio_dev_err(vdev, "failed to configure IRQs");
+		goto err_unmap;
+	}
+
 	vdev->dev_hdr = (struct device_header) {
 		.bus_type	= DEVICE_BUS_MMIO,
 #ifdef CONFIG_HAS_LIBFDT
@@ -143,16 +238,30 @@ int vfio_platform_setup_device(struct kvm *kvm, struct vfio_device *vdev)
 	ret = device__register(&vdev->dev_hdr);
 	if (ret) {
 		vfio_dev_err(vdev, "failed to register VFIO device");
-		vfio_platform_unmap_regions(kvm, vdev, vdev->info.num_regions);
-		return ret;
+		goto err_teardown_irqs;
 	}
 
 	return 0;
+
+err_teardown_irqs:
+	for (i = 0; i < vdev->info.num_irqs; i++)
+		vfio_irq_disable(kvm, vdev, i, &vdev->platform.irqs[i]);
+	free(vdev->platform.irqs);
+err_unmap:
+	vfio_platform_unmap_regions(kvm, vdev, vdev->info.num_regions);
+	return ret;
 }
 
 void vfio_platform_teardown_device(struct kvm *kvm, struct vfio_device *vdev)
 {
+	size_t i;
+	struct vfio_platform_device *pdev = &vdev->platform;
+
 	vfio_platform_unmap_regions(kvm, vdev, vdev->info.num_regions);
 
+	for (i = 0; i < vdev->info.num_irqs; ++i)
+		vfio_irq_disable(kvm, vdev, i, &pdev->irqs[i]);
+
 	device__unregister(&vdev->dev_hdr);
+	free(pdev->irqs);
 }
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH kvmtool 9/9] vfio: platform: Take the guest node from a device tree template
  2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
                   ` (7 preceding siblings ...)
  2026-10-24 16:19 ` [PATCH kvmtool 8/9] vfio: platform: Add interrupts support Mostafa Saleh
@ 2026-10-24 16:19 ` Mostafa Saleh
  8 siblings, 0 replies; 10+ messages in thread
From: Mostafa Saleh @ 2026-10-24 16:19 UTC (permalink / raw)
  To: kvm; +Cc: will, julien.thierry.kdev, praan, eric.auger, alex, Mostafa Saleh

At this point the device node has interrupts and regs, which is not
enough to probe a driver in the guest, it at least needs a compatible
string and other device bindings.
To avoid hardcoding those, let the user supply them as a compiled
device tree blob:

  --vfio-platform <device>[,<template.dtb>]

Every property of the template's root node, and every subnode, is
copied into the generated node with fdt__copy_node().

Signed-off-by: Mostafa Saleh <smostafa@google.com>
---
 builtin-run.c      |  2 +-
 include/kvm/vfio.h |  2 ++
 vfio/core.c        | 16 ++++++++++++++-
 vfio/platform.c    | 50 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/builtin-run.c b/builtin-run.c
index b09fc2e..a6fd8ec 100644
--- a/builtin-run.c
+++ b/builtin-run.c
@@ -262,7 +262,7 @@ static int loglevel_parser(const struct option *opt, const char *arg, int unset)
 	OPT_CALLBACK('\0', "vfio-pci", NULL, "[domain:]bus:dev.fn",	\
 		     "Assign a PCI device to the virtual machine",	\
 		     vfio_device_parser, kvm),				\
-	OPT_CALLBACK('\0', "vfio-platform", NULL, "device",		\
+	OPT_CALLBACK('\0', "vfio-platform", NULL, "device[,template.dtb]",	\
 		     "Assign a platform device to the virtual machine",	\
 		     vfio_device_parser, kvm),				\
 									\
diff --git a/include/kvm/vfio.h b/include/kvm/vfio.h
index b3034a0..de24ba1 100644
--- a/include/kvm/vfio.h
+++ b/include/kvm/vfio.h
@@ -121,6 +121,8 @@ struct vfio_device {
 
 struct vfio_device_params {
 	char				*name;
+	/* Platform devices: DTB whose root node is copied into the guest node */
+	char				*dt_template;
 	const char			*bus;
 	enum vfio_device_type		type;
 };
diff --git a/vfio/core.c b/vfio/core.c
index 6823a95..3ce1ffd 100644
--- a/vfio/core.c
+++ b/vfio/core.c
@@ -41,16 +41,30 @@ static int vfio_device_pci_parser(const struct option *opt, char *arg,
 	return 0;
 }
 
-/* @arg is the name of the device under /sys/bus/platform/devices */
+/*
+ * @arg is the name of the device under /sys/bus/platform/devices, optionally
+ * followed by ",<template.dtb>".
+ */
 static int vfio_device_platform_parser(const struct option *opt, char *arg,
 				       struct vfio_device_params *dev)
 {
+	char *dt_template = strtok(NULL, ",");
+
 	dev->type = VFIO_DEVICE_PLATFORM;
 	dev->bus = "platform";
+	dev->dt_template = NULL;
 	dev->name = strdup(arg);
 	if (!dev->name)
 		return -ENOMEM;
 
+	if (dt_template) {
+		dev->dt_template = strdup(dt_template);
+		if (!dev->dt_template) {
+			free(dev->name);
+			return -ENOMEM;
+		}
+	}
+
 	return 0;
 }
 
diff --git a/vfio/platform.c b/vfio/platform.c
index 22052a6..2d08f73 100644
--- a/vfio/platform.c
+++ b/vfio/platform.c
@@ -8,7 +8,11 @@
 
 #include <sys/ioctl.h>
 #include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
 #include "kvm/fdt.h"
+#include "kvm/read-write.h"
 
 static u64 vfio_mmio_io_space_next = KVM_VFIO_MMIO_AREA;
 
@@ -29,6 +33,48 @@ static int vfio_mmio_alloc_io_space(u64 size, u64 *addr)
 }
 
 #ifdef CONFIG_HAS_LIBFDT
+static void vfio_platform_generate_fdt_from_template(void *fdt,
+						     struct vfio_device *vdev)
+{
+	int fd;
+	struct stat st;
+	void *tmp_fdt;
+	int root_offset;
+
+	if (!vdev->params->dt_template)
+		return;
+
+	fd = open(vdev->params->dt_template, O_RDONLY);
+	if (fd < 0)
+		die("Failed to open device tree template '%s'", vdev->params->dt_template);
+
+	if (fstat(fd, &st) < 0)
+		die("Failed to stat device tree template");
+
+	if (st.st_size < (off_t)sizeof(struct fdt_header))
+		die("Invalid device tree blob (DTB) provided in '%s'",
+		    vdev->params->dt_template);
+
+	tmp_fdt = malloc(st.st_size);
+	if (!tmp_fdt)
+		die("Failed to allocate memory for device tree template");
+
+	if (read_in_full(fd, tmp_fdt, st.st_size) != st.st_size)
+		die("Failed to read device tree template");
+
+	if (fdt_check_header(tmp_fdt))
+		die("Invalid DTB: '%s'", vdev->params->dt_template);
+
+	root_offset = fdt_path_offset(tmp_fdt, "/");
+	if (root_offset < 0)
+		die("Failed to parse device tree template");
+
+	fdt__copy_node(fdt, tmp_fdt, root_offset);
+
+	free(tmp_fdt);
+	close(fd);
+}
+
 static void generate_interrupts_prop(void *fdt, struct vfio_device *vdev,
 				     fdt_irq_fn generate_irq_prop)
 {
@@ -82,6 +128,9 @@ static void generate_mmio_fdt_node(void *fdt,
 		 vdev->params->name, vdev->regions[0].guest_phys_addr);
 
 	_FDT(fdt_begin_node(fdt, node_name));
+	/* Extra user supplied binding. */
+	vfio_platform_generate_fdt_from_template(fdt, vdev);
+
 	_FDT(fdt_property(fdt, "reg", reg_prop,
 			  2 * sizeof(*reg_prop) * num_regions));
 	generate_interrupts_prop(fdt, vdev, generate_irq_prop);
@@ -264,4 +313,5 @@ void vfio_platform_teardown_device(struct kvm *kvm, struct vfio_device *vdev)
 
 	device__unregister(&vdev->dev_hdr);
 	free(pdev->irqs);
+	free(vdev->params->dt_template);
 }
-- 
2.56.0.307.g5fe7bd680c-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-10-24 16:20 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-24 16:18 [PATCH kvmtool 0/9] vfio: Add support for platform devices Mostafa Saleh
2026-10-24 16:18 ` [PATCH kvmtool 1/9] vfio: Fix fd leak in vfio_group_get_for_dev() Mostafa Saleh
2026-10-24 16:18 ` [PATCH kvmtool 2/9] vfio: Factor out wired IRQ setup and teardown Mostafa Saleh
2026-10-24 16:18 ` [PATCH kvmtool 3/9] vfio: Support edge-triggered wired IRQs Mostafa Saleh
2026-10-24 16:18 ` [PATCH kvmtool 4/9] fdt: Allow fdt_irq_fn to generate multiple interrupts Mostafa Saleh
2026-10-24 16:18 ` [PATCH kvmtool 5/9] fdt: Add helper to copy an FDT node Mostafa Saleh
2026-10-24 16:18 ` [PATCH kvmtool 6/9] vfio: Reserve guest address space for VFIO platform Mostafa Saleh
2026-10-24 16:19 ` [PATCH kvmtool 7/9] vfio: Add basic support for vfio-platform Mostafa Saleh
2026-10-24 16:19 ` [PATCH kvmtool 8/9] vfio: platform: Add interrupts support Mostafa Saleh
2026-10-24 16:19 ` [PATCH kvmtool 9/9] vfio: platform: Take the guest node from a device tree template Mostafa Saleh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox