typedef struct VFIODeviceOps VFIODeviceOps;
typedef struct VFIODeviceIOOps VFIODeviceIOOps;
typedef struct VFIOMigration VFIOMigration;
@@ -323,6 +330,21 @@ bool vfio_device_has_region_cap(VFIODevice
*vbasedev, int region, uint16_t cap_t
int vfio_device_get_irq_info(VFIODevice *vbasedev, int index,
struct vfio_irq_info *info);
+
+/**
+ * Create a dmabuf fd by first translating the addresses in the
+ * iovec array into VFIO region offsets and then invoking the
+ * VFIO_DEVICE_FEATURE_DMA_BUF feature.
+ *
+ * @iov: array of iovec entries associated with a buffer
+ * @iov_cnt: number of entries in the iovec array
+ * @errp: pointer to Error*, to store an error if it happens.
+ *
+ * Returns the created dmabuf fd or either
VFIO_DMABUF_CREATE_ERR_UNSPEC
+ * or VFIO_DMABUF_CREATE_ERR_INVALID_IOV on error.
+ */
+int vfio_device_create_dmabuf_fd(struct iovec *iov, unsigned int
iov_cnt,
+ Error **errp);
#endif
/* Returns 0 on success, or a negative errno. */
diff --git a/hw/vfio/device.c b/hw/vfio/device.c
index 4f11959633..d95503a5f1 100644
--- a/hw/vfio/device.c
+++ b/hw/vfio/device.c
@@ -21,7 +21,9 @@
#include "qemu/osdep.h"
#include <sys/ioctl.h>
+#include "system/ramblock.h"
#include "hw/vfio/vfio-device.h"
+#include "hw/vfio/vfio-region.h"
#include "hw/vfio/pci.h"
#include "hw/core/iommu.h"
#include "trace.h"
@@ -667,3 +669,110 @@ static VFIODeviceIOOps
vfio_device_io_ops_ioctl = {
.region_read = vfio_device_io_region_read,
.region_write = vfio_device_io_region_write,
};
+
+/*
+ * This helper looks up the VFIODevice corresponding to the given iov.
It
+ * can be useful to determinine if a buffer (represented by the iov)
belongs
+ * to a VFIO device or not. This is mainly invoked when external
components
+ * such as virtio-gpu request creation of dmabuf fds for buffers that may
+ * belong to a VFIO device.
+ */
+static bool vfio_device_lookup(struct iovec *iov, VFIODevice
**vbasedevp,
+ RAMBlock **first_rbp, Error **errp)
+{
+ VFIODevice *vbasedev;
+ RAMBlock *first_rb;
+ ram_addr_t offset;
+
+ first_rb = qemu_ram_block_from_host(iov[0].iov_base, false,
&offset);
+ if (!first_rb) {
+ error_setg(errp, "Could not find first ramblock\n");
no \n
+ return false;
+ }
+
+ *first_rbp = first_rb;
+ QLIST_FOREACH(vbasedev, &vfio_device_list, next) {
wrong list : global_next.
+ if (vbasedev->dev == first_rb->mr->dev) {
+ *vbasedevp = vbasedev;
+ return true;
+ }
+ }
+ error_setg(errp, "No VFIO device found to create dmabuf from\n");
no \n
+ return false;
+}
+
+/*
+ * This helper looks up the VFIORegion corresponding to the given
address.
+ * It also verifies that the RAMBlock associated with the address is the
+ * same as the first_rb passed in. This is to ensure that all addresses
+ * in the iov belong to the same region.
+ */
+static bool vfio_region_lookup(void *addr, VFIORegion **regionp,
+ RAMBlock *first_rb, ram_addr_t *offsetp,
+ Error **errp)
+{
+ VFIORegion *region;
+ RAMBlock *rb;
+
+ rb = qemu_ram_block_from_host(addr, false, offsetp);
+ if (!rb || rb != first_rb) {
+ error_setg(errp, "Dmabuf segments must belong to the same
region\n");
remove \n
+ return false;
+ }
+
+ region = vfio_get_region_from_mr(rb->mr);
+ if (region) {
+ *regionp = region;
+ return true;
+ }
+ error_setg(errp, "Could not find valid region for dmabuf segment\n");
here too.
+ return false;
+}
+
+int vfio_device_create_dmabuf_fd(struct iovec *iov, unsigned int
iov_cnt,
+ Error **errp)
+{
+ g_autofree struct vfio_device_feature *feature = NULL;
+ struct vfio_device_feature_dma_buf *dma_buf;
+ RAMBlock *first_rb = NULL;
+ VFIODevice *vbasedev;
+ VFIORegion *region;
+ ram_addr_t offset;
+ size_t argsz;
+ int i, ret;
+
+ if (!vfio_device_lookup(iov, &vbasedev, &first_rb, errp)) {
+ return VFIO_DMABUF_CREATE_ERR_INVALID_IOV;
return -EINVAL;
+ }
+
+ argsz = sizeof(*feature) + sizeof (*dma_buf) +
+ sizeof(struct vfio_region_dma_range) * iov_cnt;
+ feature = g_malloc0(argsz);
+ *feature = (struct vfio_device_feature) {
+ .argsz = argsz,
+ .flags = VFIO_DEVICE_FEATURE_GET |
VFIO_DEVICE_FEATURE_DMA_BUF,
+ };
+ dma_buf = (struct vfio_device_feature_dma_buf *)feature->data;
+
+ for (i = 0; i < iov_cnt; i++) {
+ if (!vfio_region_lookup(iov[i].iov_base, ®ion,
+ first_rb, &offset, errp)) {
+ return VFIO_DMABUF_CREATE_ERR_INVALID_IOV;
return -EINVAL;
+ }
+
+ dma_buf->region_index = region->nr;
+ dma_buf->dma_ranges[i].offset = offset;
+ dma_buf->dma_ranges[i].length = iov[i].iov_len;
+ }
+
+ dma_buf->nr_ranges = iov_cnt;
+ dma_buf->open_flags = O_RDONLY | O_CLOEXEC;
+
+ ret = vfio_device_get_feature(vbasedev, feature);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "Could not create dmabuf fd via VFIO device");
+ return VFIO_DMABUF_CREATE_ERR_UNSPEC;
return ret;
+ }
+ return ret;
+}
diff --git a/hw/vfio/dmabuf-stubs.c b/hw/vfio/dmabuf-stubs.c
new file mode 100644
index 0000000000..fb5318e929
--- /dev/null
+++ b/hw/vfio/dmabuf-stubs.c
Fails to build on MinGW/Windows.
hw/vfio/dmabuf-stubs.c:12:5: error: no previous prototype for
'vfio_device_create_dmabuf_fd'
hw/vfio/dmabuf-stubs.c:19:7: error: no previous prototype for
'vfio_device_mmap_dmabuf'
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) 2026 Intel and/or its affiliates.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/iov.h"
+#include "hw/vfio/vfio-device.h"
+
+int vfio_device_create_dmabuf_fd(struct iovec *iov, unsigned int
iov_cnt,
+ Error **errp)
+{
+ error_setg(errp, "VFIO dmabuf support is not enabled");
+ return VFIO_DMABUF_CREATE_ERR_UNSPEC;
return -ENOTSUP;
+}
diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build
index c7c31b78f6..aa6f4bc1b4 100644
--- a/hw/vfio/meson.build
+++ b/hw/vfio/meson.build
@@ -38,3 +38,4 @@ stub_ss.add(files('iommufd-stubs.c'))
system_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files(
'display.c',
))
+system_ss.add(when: 'CONFIG_VFIO', if_false: files('dmabuf-stubs.c'))