All of lore.kernel.org
 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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.