[PATCH for-10.1 v2 11/37] vfio: Introduce new files for VFIORegion definitions and declarations

Cédric Le Goater posted 37 patches 1 week ago
[PATCH for-10.1 v2 11/37] vfio: Introduce new files for VFIORegion definitions and declarations
Posted by Cédric Le Goater 1 week ago
Gather all VFIORegion related declarations and definitions into their
own files to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h".
They were introduced for 'vfio-platform' support in commits
db0da029a185 ("vfio: Generalize region support") and a664477db8da
("hw/vfio/pci: Introduce VFIORegion").

To be noted that the 'vfio-platform' devices have been deprecated and
will be removed in QEMU 10.2. Until then, make the declarations
available externally for 'sysbus-fdt.c'.

Cc: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Cédric Le Goater <clg@redhat.com>
---
 hw/vfio/vfio-display.h          |   1 +
 include/hw/vfio/vfio-common.h   |  32 +--
 include/hw/vfio/vfio-platform.h |   2 +
 include/hw/vfio/vfio-region.h   |  47 ++++
 hw/core/sysbus-fdt.c            |   1 +
 hw/vfio/helpers.c               | 363 -----------------------------
 hw/vfio/pci-quirks.c            |   1 +
 hw/vfio/pci.c                   |   1 +
 hw/vfio/platform.c              |   1 +
 hw/vfio/region.c                | 394 ++++++++++++++++++++++++++++++++
 hw/vfio/meson.build             |   1 +
 hw/vfio/trace-events            |  16 +-
 12 files changed, 459 insertions(+), 401 deletions(-)
 create mode 100644 include/hw/vfio/vfio-region.h
 create mode 100644 hw/vfio/region.c

diff --git a/hw/vfio/vfio-display.h b/hw/vfio/vfio-display.h
index 99b8cb67ef7558d3eefe3105a831e3fcb30afc4d..2606c34b396a88cec3e8f884adb158e48e8105f1 100644
--- a/hw/vfio/vfio-display.h
+++ b/hw/vfio/vfio-display.h
@@ -11,6 +11,7 @@
 
 #include "ui/console.h"
 #include "hw/display/ramfb.h"
+#include "hw/vfio/vfio-region.h"
 
 typedef struct VFIODMABuf {
     QemuDmaBuf *buf;
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index 8d48f5300a791d8858fe29d1bb905f814ef11990..3d470d79325526e0508683c445a7635c78a57e34 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -39,25 +39,6 @@ enum {
     VFIO_DEVICE_TYPE_CCW = 2,
     VFIO_DEVICE_TYPE_AP = 3,
 };
-
-typedef struct VFIOMmap {
-    MemoryRegion mem;
-    void *mmap;
-    off_t offset;
-    size_t size;
-} VFIOMmap;
-
-typedef struct VFIORegion {
-    struct VFIODevice *vbasedev;
-    off_t fd_offset; /* offset of region within device fd */
-    MemoryRegion *mem; /* slow, read/write access */
-    size_t size;
-    uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
-    uint32_t nr_mmaps;
-    VFIOMmap *mmaps;
-    uint8_t nr; /* cache the region number for debug */
-} VFIORegion;
-
 struct VFIOGroup;
 
 typedef struct VFIOContainer {
@@ -168,17 +149,7 @@ void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index);
 void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index);
 bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex,
                             int action, int fd, Error **errp);
-void vfio_region_write(void *opaque, hwaddr addr,
-                           uint64_t data, unsigned size);
-uint64_t vfio_region_read(void *opaque,
-                          hwaddr addr, unsigned size);
-int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
-                      int index, const char *name);
-int vfio_region_mmap(VFIORegion *region);
-void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
-void vfio_region_unmap(VFIORegion *region);
-void vfio_region_exit(VFIORegion *region);
-void vfio_region_finalize(VFIORegion *region);
+
 void vfio_reset_handler(void *opaque);
 struct vfio_device_info *vfio_get_device_info(int fd);
 bool vfio_device_is_mdev(VFIODevice *vbasedev);
@@ -194,7 +165,6 @@ int vfio_kvm_device_del_fd(int fd, Error **errp);
 bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp);
 void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer);
 
-extern const MemoryRegionOps vfio_region_ops;
 typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList;
 typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList;
 extern VFIOGroupList vfio_group_list;
diff --git a/include/hw/vfio/vfio-platform.h b/include/hw/vfio/vfio-platform.h
index c414c3dffcc840a2f40a1b252d0f7b4e309c78d4..3191545717da51abc41d10cd3646cd047b4a676c 100644
--- a/include/hw/vfio/vfio-platform.h
+++ b/include/hw/vfio/vfio-platform.h
@@ -47,6 +47,8 @@ typedef struct VFIOINTp {
 /* function type for user side eventfd handler */
 typedef void (*eventfd_user_side_handler_t)(VFIOINTp *intp);
 
+typedef struct VFIORegion VFIORegion;
+
 struct VFIOPlatformDevice {
     SysBusDevice sbdev;
     VFIODevice vbasedev; /* not a QOM object */
diff --git a/include/hw/vfio/vfio-region.h b/include/hw/vfio/vfio-region.h
new file mode 100644
index 0000000000000000000000000000000000000000..9dc0535e7ce46fbb671e70918b93cb115857efe6
--- /dev/null
+++ b/include/hw/vfio/vfio-region.h
@@ -0,0 +1,47 @@
+/*
+ * VFIO region
+ *
+ * Copyright Red Hat, Inc. 2025
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_VFIO_REGION_H
+#define HW_VFIO_REGION_H
+
+#include "exec/memory.h"
+
+typedef struct VFIOMmap {
+    MemoryRegion mem;
+    void *mmap;
+    off_t offset;
+    size_t size;
+} VFIOMmap;
+
+typedef struct VFIODevice VFIODevice;
+
+typedef struct VFIORegion {
+    struct VFIODevice *vbasedev;
+    off_t fd_offset; /* offset of region within device fd */
+    MemoryRegion *mem; /* slow, read/write access */
+    size_t size;
+    uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
+    uint32_t nr_mmaps;
+    VFIOMmap *mmaps;
+    uint8_t nr; /* cache the region number for debug */
+} VFIORegion;
+
+
+void vfio_region_write(void *opaque, hwaddr addr,
+                           uint64_t data, unsigned size);
+uint64_t vfio_region_read(void *opaque,
+                          hwaddr addr, unsigned size);
+int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
+                      int index, const char *name);
+int vfio_region_mmap(VFIORegion *region);
+void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
+void vfio_region_unmap(VFIORegion *region);
+void vfio_region_exit(VFIORegion *region);
+void vfio_region_finalize(VFIORegion *region);
+
+#endif /* HW_VFIO_REGION_H */
diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c
index e85066b905637b1ca34b5965f383df341f3a4eb7..c339a27875cbee8131b064674aa09adf4b9efa25 100644
--- a/hw/core/sysbus-fdt.c
+++ b/hw/core/sysbus-fdt.c
@@ -35,6 +35,7 @@
 #include "hw/vfio/vfio-platform.h"
 #include "hw/vfio/vfio-calxeda-xgmac.h"
 #include "hw/vfio/vfio-amd-xgbe.h"
+#include "hw/vfio/vfio-region.h"
 #include "hw/display/ramfb.h"
 #include "hw/uefi/var-service-api.h"
 #include "hw/arm/fdt.h"
diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c
index 4b255d4f3a9e81f55df00c68fc71da769fd5bd04..89403943a7a219e491b6812d50b27b7f1fd7b4a4 100644
--- a/hw/vfio/helpers.c
+++ b/hw/vfio/helpers.c
@@ -147,118 +147,6 @@ bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex,
     return false;
 }
 
-/*
- * IO Port/MMIO - Beware of the endians, VFIO is always little endian
- */
-void vfio_region_write(void *opaque, hwaddr addr,
-                       uint64_t data, unsigned size)
-{
-    VFIORegion *region = opaque;
-    VFIODevice *vbasedev = region->vbasedev;
-    union {
-        uint8_t byte;
-        uint16_t word;
-        uint32_t dword;
-        uint64_t qword;
-    } buf;
-
-    switch (size) {
-    case 1:
-        buf.byte = data;
-        break;
-    case 2:
-        buf.word = cpu_to_le16(data);
-        break;
-    case 4:
-        buf.dword = cpu_to_le32(data);
-        break;
-    case 8:
-        buf.qword = cpu_to_le64(data);
-        break;
-    default:
-        hw_error("vfio: unsupported write size, %u bytes", size);
-        break;
-    }
-
-    if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
-        error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64
-                     ",%d) failed: %m",
-                     __func__, vbasedev->name, region->nr,
-                     addr, data, size);
-    }
-
-    trace_vfio_region_write(vbasedev->name, region->nr, addr, data, size);
-
-    /*
-     * A read or write to a BAR always signals an INTx EOI.  This will
-     * do nothing if not pending (including not in INTx mode).  We assume
-     * that a BAR access is in response to an interrupt and that BAR
-     * accesses will service the interrupt.  Unfortunately, we don't know
-     * which access will service the interrupt, so we're potentially
-     * getting quite a few host interrupts per guest interrupt.
-     */
-    vbasedev->ops->vfio_eoi(vbasedev);
-}
-
-uint64_t vfio_region_read(void *opaque,
-                          hwaddr addr, unsigned size)
-{
-    VFIORegion *region = opaque;
-    VFIODevice *vbasedev = region->vbasedev;
-    union {
-        uint8_t byte;
-        uint16_t word;
-        uint32_t dword;
-        uint64_t qword;
-    } buf;
-    uint64_t data = 0;
-
-    if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
-        error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m",
-                     __func__, vbasedev->name, region->nr,
-                     addr, size);
-        return (uint64_t)-1;
-    }
-    switch (size) {
-    case 1:
-        data = buf.byte;
-        break;
-    case 2:
-        data = le16_to_cpu(buf.word);
-        break;
-    case 4:
-        data = le32_to_cpu(buf.dword);
-        break;
-    case 8:
-        data = le64_to_cpu(buf.qword);
-        break;
-    default:
-        hw_error("vfio: unsupported read size, %u bytes", size);
-        break;
-    }
-
-    trace_vfio_region_read(vbasedev->name, region->nr, addr, size, data);
-
-    /* Same as write above */
-    vbasedev->ops->vfio_eoi(vbasedev);
-
-    return data;
-}
-
-const MemoryRegionOps vfio_region_ops = {
-    .read = vfio_region_read,
-    .write = vfio_region_write,
-    .endianness = DEVICE_LITTLE_ENDIAN,
-    .valid = {
-        .min_access_size = 1,
-        .max_access_size = 8,
-    },
-    .impl = {
-        .min_access_size = 1,
-        .max_access_size = 8,
-    },
-};
-
 int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size)
 {
     vbmap->pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size();
@@ -306,257 +194,6 @@ vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id)
     return vfio_get_cap((void *)info, info->cap_offset, id);
 }
 
-static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
-                                          struct vfio_region_info *info)
-{
-    struct vfio_info_cap_header *hdr;
-    struct vfio_region_info_cap_sparse_mmap *sparse;
-    int i, j;
-
-    hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP);
-    if (!hdr) {
-        return -ENODEV;
-    }
-
-    sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header);
-
-    trace_vfio_region_sparse_mmap_header(region->vbasedev->name,
-                                         region->nr, sparse->nr_areas);
-
-    region->mmaps = g_new0(VFIOMmap, sparse->nr_areas);
-
-    for (i = 0, j = 0; i < sparse->nr_areas; i++) {
-        if (sparse->areas[i].size) {
-            trace_vfio_region_sparse_mmap_entry(i, sparse->areas[i].offset,
-                                            sparse->areas[i].offset +
-                                            sparse->areas[i].size - 1);
-            region->mmaps[j].offset = sparse->areas[i].offset;
-            region->mmaps[j].size = sparse->areas[i].size;
-            j++;
-        }
-    }
-
-    region->nr_mmaps = j;
-    region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap));
-
-    return 0;
-}
-
-int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
-                      int index, const char *name)
-{
-    g_autofree struct vfio_region_info *info = NULL;
-    int ret;
-
-    ret = vfio_get_region_info(vbasedev, index, &info);
-    if (ret) {
-        return ret;
-    }
-
-    region->vbasedev = vbasedev;
-    region->flags = info->flags;
-    region->size = info->size;
-    region->fd_offset = info->offset;
-    region->nr = index;
-
-    if (region->size) {
-        region->mem = g_new0(MemoryRegion, 1);
-        memory_region_init_io(region->mem, obj, &vfio_region_ops,
-                              region, name, region->size);
-
-        if (!vbasedev->no_mmap &&
-            region->flags & VFIO_REGION_INFO_FLAG_MMAP) {
-
-            ret = vfio_setup_region_sparse_mmaps(region, info);
-
-            if (ret) {
-                region->nr_mmaps = 1;
-                region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
-                region->mmaps[0].offset = 0;
-                region->mmaps[0].size = region->size;
-            }
-        }
-    }
-
-    trace_vfio_region_setup(vbasedev->name, index, name,
-                            region->flags, region->fd_offset, region->size);
-    return 0;
-}
-
-static void vfio_subregion_unmap(VFIORegion *region, int index)
-{
-    trace_vfio_region_unmap(memory_region_name(&region->mmaps[index].mem),
-                            region->mmaps[index].offset,
-                            region->mmaps[index].offset +
-                            region->mmaps[index].size - 1);
-    memory_region_del_subregion(region->mem, &region->mmaps[index].mem);
-    munmap(region->mmaps[index].mmap, region->mmaps[index].size);
-    object_unparent(OBJECT(&region->mmaps[index].mem));
-    region->mmaps[index].mmap = NULL;
-}
-
-int vfio_region_mmap(VFIORegion *region)
-{
-    int i, ret, prot = 0;
-    char *name;
-
-    if (!region->mem) {
-        return 0;
-    }
-
-    prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
-    prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
-
-    for (i = 0; i < region->nr_mmaps; i++) {
-        size_t align = MIN(1ULL << ctz64(region->mmaps[i].size), 1 * GiB);
-        void *map_base, *map_align;
-
-        /*
-         * Align the mmap for more efficient mapping in the kernel.  Ideally
-         * we'd know the PMD and PUD mapping sizes to use as discrete alignment
-         * intervals, but we don't.  As of Linux v6.12, the largest PUD size
-         * supporting huge pfnmap is 1GiB (ARCH_SUPPORTS_PUD_PFNMAP is only set
-         * on x86_64).  Align by power-of-two size, capped at 1GiB.
-         *
-         * NB. qemu_memalign() and friends actually allocate memory, whereas
-         * the region size here can exceed host memory, therefore we manually
-         * create an oversized anonymous mapping and clean it up for alignment.
-         */
-        map_base = mmap(0, region->mmaps[i].size + align, PROT_NONE,
-                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-        if (map_base == MAP_FAILED) {
-            ret = -errno;
-            goto no_mmap;
-        }
-
-        map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align);
-        munmap(map_base, map_align - map_base);
-        munmap(map_align + region->mmaps[i].size,
-               align - (map_align - map_base));
-
-        region->mmaps[i].mmap = mmap(map_align, region->mmaps[i].size, prot,
-                                     MAP_SHARED | MAP_FIXED,
-                                     region->vbasedev->fd,
-                                     region->fd_offset +
-                                     region->mmaps[i].offset);
-        if (region->mmaps[i].mmap == MAP_FAILED) {
-            ret = -errno;
-            goto no_mmap;
-        }
-
-        name = g_strdup_printf("%s mmaps[%d]",
-                               memory_region_name(region->mem), i);
-        memory_region_init_ram_device_ptr(&region->mmaps[i].mem,
-                                          memory_region_owner(region->mem),
-                                          name, region->mmaps[i].size,
-                                          region->mmaps[i].mmap);
-        g_free(name);
-        memory_region_add_subregion(region->mem, region->mmaps[i].offset,
-                                    &region->mmaps[i].mem);
-
-        trace_vfio_region_mmap(memory_region_name(&region->mmaps[i].mem),
-                               region->mmaps[i].offset,
-                               region->mmaps[i].offset +
-                               region->mmaps[i].size - 1);
-    }
-
-    return 0;
-
-no_mmap:
-    trace_vfio_region_mmap_fault(memory_region_name(region->mem), i,
-                                 region->fd_offset + region->mmaps[i].offset,
-                                 region->fd_offset + region->mmaps[i].offset +
-                                 region->mmaps[i].size - 1, ret);
-
-    region->mmaps[i].mmap = NULL;
-
-    for (i--; i >= 0; i--) {
-        vfio_subregion_unmap(region, i);
-    }
-
-    return ret;
-}
-
-void vfio_region_unmap(VFIORegion *region)
-{
-    int i;
-
-    if (!region->mem) {
-        return;
-    }
-
-    for (i = 0; i < region->nr_mmaps; i++) {
-        if (region->mmaps[i].mmap) {
-            vfio_subregion_unmap(region, i);
-        }
-    }
-}
-
-void vfio_region_exit(VFIORegion *region)
-{
-    int i;
-
-    if (!region->mem) {
-        return;
-    }
-
-    for (i = 0; i < region->nr_mmaps; i++) {
-        if (region->mmaps[i].mmap) {
-            memory_region_del_subregion(region->mem, &region->mmaps[i].mem);
-        }
-    }
-
-    trace_vfio_region_exit(region->vbasedev->name, region->nr);
-}
-
-void vfio_region_finalize(VFIORegion *region)
-{
-    int i;
-
-    if (!region->mem) {
-        return;
-    }
-
-    for (i = 0; i < region->nr_mmaps; i++) {
-        if (region->mmaps[i].mmap) {
-            munmap(region->mmaps[i].mmap, region->mmaps[i].size);
-            object_unparent(OBJECT(&region->mmaps[i].mem));
-        }
-    }
-
-    object_unparent(OBJECT(region->mem));
-
-    g_free(region->mem);
-    g_free(region->mmaps);
-
-    trace_vfio_region_finalize(region->vbasedev->name, region->nr);
-
-    region->mem = NULL;
-    region->mmaps = NULL;
-    region->nr_mmaps = 0;
-    region->size = 0;
-    region->flags = 0;
-    region->nr = 0;
-}
-
-void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled)
-{
-    int i;
-
-    if (!region->mem) {
-        return;
-    }
-
-    for (i = 0; i < region->nr_mmaps; i++) {
-        if (region->mmaps[i].mmap) {
-            memory_region_set_enabled(&region->mmaps[i].mem, enabled);
-        }
-    }
-
-    trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem),
-                                        enabled);
-}
-
 int vfio_get_region_info(VFIODevice *vbasedev, int index,
                          struct vfio_region_info **info)
 {
diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
index 3f002252acfb7ac809107c99bdbdbaf66d56a50d..4591ec68da877b307f43ea1a830c315721b57e9e 100644
--- a/hw/vfio/pci-quirks.c
+++ b/hw/vfio/pci-quirks.c
@@ -26,6 +26,7 @@
 #include "hw/qdev-properties.h"
 #include "pci.h"
 #include "pci-quirks.h"
+#include "hw/vfio/vfio-region.h"
 #include "trace.h"
 
 /*
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 158deca06cb240622a254f5059c47873e5fcc7de..7457e81cdb07bc6657bb1514349c172a152cb540 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -45,6 +45,7 @@
 #include "migration/qemu-file.h"
 #include "system/iommufd.h"
 #include "vfio-migration-internal.h"
+#include "hw/vfio/vfio-region.h"
 
 #define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug"
 
diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c
index 67bc57409c1f5a6978690c3dc07d249ea0248aa0..83b53d57149a343a00eb6d6f78c1cbea004dbaa2 100644
--- a/hw/vfio/platform.c
+++ b/hw/vfio/platform.c
@@ -37,6 +37,7 @@
 #include "hw/platform-bus.h"
 #include "hw/qdev-properties.h"
 #include "system/kvm.h"
+#include "hw/vfio/vfio-region.h"
 
 /*
  * Functions used whatever the injection method
diff --git a/hw/vfio/region.c b/hw/vfio/region.c
new file mode 100644
index 0000000000000000000000000000000000000000..08cd69e7047ab950151832864a14af7af774ff3b
--- /dev/null
+++ b/hw/vfio/region.c
@@ -0,0 +1,394 @@
+/*
+ * VFIO regions
+ *
+ * Copyright Red Hat, Inc. 2012
+ *
+ * Authors:
+ *  Alex Williamson <alex.williamson@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.  See
+ * the COPYING file in the top-level directory.
+ *
+ * Based on qemu-kvm device-assignment:
+ *  Adapted for KVM by Qumranet.
+ *  Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com)
+ *  Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com)
+ *  Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com)
+ *  Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com)
+ *  Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com)
+ */
+
+#include "qemu/osdep.h"
+#include <sys/ioctl.h>
+
+#include "hw/vfio/vfio-common.h"
+#include "hw/vfio/pci.h"
+#include "hw/hw.h"
+#include "trace.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/units.h"
+#include "monitor/monitor.h"
+
+/*
+ * IO Port/MMIO - Beware of the endians, VFIO is always little endian
+ */
+void vfio_region_write(void *opaque, hwaddr addr,
+                       uint64_t data, unsigned size)
+{
+    VFIORegion *region = opaque;
+    VFIODevice *vbasedev = region->vbasedev;
+    union {
+        uint8_t byte;
+        uint16_t word;
+        uint32_t dword;
+        uint64_t qword;
+    } buf;
+
+    switch (size) {
+    case 1:
+        buf.byte = data;
+        break;
+    case 2:
+        buf.word = cpu_to_le16(data);
+        break;
+    case 4:
+        buf.dword = cpu_to_le32(data);
+        break;
+    case 8:
+        buf.qword = cpu_to_le64(data);
+        break;
+    default:
+        hw_error("vfio: unsupported write size, %u bytes", size);
+        break;
+    }
+
+    if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
+        error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64
+                     ",%d) failed: %m",
+                     __func__, vbasedev->name, region->nr,
+                     addr, data, size);
+    }
+
+    trace_vfio_region_write(vbasedev->name, region->nr, addr, data, size);
+
+    /*
+     * A read or write to a BAR always signals an INTx EOI.  This will
+     * do nothing if not pending (including not in INTx mode).  We assume
+     * that a BAR access is in response to an interrupt and that BAR
+     * accesses will service the interrupt.  Unfortunately, we don't know
+     * which access will service the interrupt, so we're potentially
+     * getting quite a few host interrupts per guest interrupt.
+     */
+    vbasedev->ops->vfio_eoi(vbasedev);
+}
+
+uint64_t vfio_region_read(void *opaque,
+                          hwaddr addr, unsigned size)
+{
+    VFIORegion *region = opaque;
+    VFIODevice *vbasedev = region->vbasedev;
+    union {
+        uint8_t byte;
+        uint16_t word;
+        uint32_t dword;
+        uint64_t qword;
+    } buf;
+    uint64_t data = 0;
+
+    if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
+        error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m",
+                     __func__, vbasedev->name, region->nr,
+                     addr, size);
+        return (uint64_t)-1;
+    }
+    switch (size) {
+    case 1:
+        data = buf.byte;
+        break;
+    case 2:
+        data = le16_to_cpu(buf.word);
+        break;
+    case 4:
+        data = le32_to_cpu(buf.dword);
+        break;
+    case 8:
+        data = le64_to_cpu(buf.qword);
+        break;
+    default:
+        hw_error("vfio: unsupported read size, %u bytes", size);
+        break;
+    }
+
+    trace_vfio_region_read(vbasedev->name, region->nr, addr, size, data);
+
+    /* Same as write above */
+    vbasedev->ops->vfio_eoi(vbasedev);
+
+    return data;
+}
+
+static const MemoryRegionOps vfio_region_ops = {
+    .read = vfio_region_read,
+    .write = vfio_region_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+    .impl = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+    },
+};
+
+static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
+                                          struct vfio_region_info *info)
+{
+    struct vfio_info_cap_header *hdr;
+    struct vfio_region_info_cap_sparse_mmap *sparse;
+    int i, j;
+
+    hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP);
+    if (!hdr) {
+        return -ENODEV;
+    }
+
+    sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header);
+
+    trace_vfio_region_sparse_mmap_header(region->vbasedev->name,
+                                         region->nr, sparse->nr_areas);
+
+    region->mmaps = g_new0(VFIOMmap, sparse->nr_areas);
+
+    for (i = 0, j = 0; i < sparse->nr_areas; i++) {
+        if (sparse->areas[i].size) {
+            trace_vfio_region_sparse_mmap_entry(i, sparse->areas[i].offset,
+                                            sparse->areas[i].offset +
+                                            sparse->areas[i].size - 1);
+            region->mmaps[j].offset = sparse->areas[i].offset;
+            region->mmaps[j].size = sparse->areas[i].size;
+            j++;
+        }
+    }
+
+    region->nr_mmaps = j;
+    region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap));
+
+    return 0;
+}
+
+int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
+                      int index, const char *name)
+{
+    g_autofree struct vfio_region_info *info = NULL;
+    int ret;
+
+    ret = vfio_get_region_info(vbasedev, index, &info);
+    if (ret) {
+        return ret;
+    }
+
+    region->vbasedev = vbasedev;
+    region->flags = info->flags;
+    region->size = info->size;
+    region->fd_offset = info->offset;
+    region->nr = index;
+
+    if (region->size) {
+        region->mem = g_new0(MemoryRegion, 1);
+        memory_region_init_io(region->mem, obj, &vfio_region_ops,
+                              region, name, region->size);
+
+        if (!vbasedev->no_mmap &&
+            region->flags & VFIO_REGION_INFO_FLAG_MMAP) {
+
+            ret = vfio_setup_region_sparse_mmaps(region, info);
+
+            if (ret) {
+                region->nr_mmaps = 1;
+                region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
+                region->mmaps[0].offset = 0;
+                region->mmaps[0].size = region->size;
+            }
+        }
+    }
+
+    trace_vfio_region_setup(vbasedev->name, index, name,
+                            region->flags, region->fd_offset, region->size);
+    return 0;
+}
+
+static void vfio_subregion_unmap(VFIORegion *region, int index)
+{
+    trace_vfio_region_unmap(memory_region_name(&region->mmaps[index].mem),
+                            region->mmaps[index].offset,
+                            region->mmaps[index].offset +
+                            region->mmaps[index].size - 1);
+    memory_region_del_subregion(region->mem, &region->mmaps[index].mem);
+    munmap(region->mmaps[index].mmap, region->mmaps[index].size);
+    object_unparent(OBJECT(&region->mmaps[index].mem));
+    region->mmaps[index].mmap = NULL;
+}
+
+int vfio_region_mmap(VFIORegion *region)
+{
+    int i, ret, prot = 0;
+    char *name;
+
+    if (!region->mem) {
+        return 0;
+    }
+
+    prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
+    prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
+
+    for (i = 0; i < region->nr_mmaps; i++) {
+        size_t align = MIN(1ULL << ctz64(region->mmaps[i].size), 1 * GiB);
+        void *map_base, *map_align;
+
+        /*
+         * Align the mmap for more efficient mapping in the kernel.  Ideally
+         * we'd know the PMD and PUD mapping sizes to use as discrete alignment
+         * intervals, but we don't.  As of Linux v6.12, the largest PUD size
+         * supporting huge pfnmap is 1GiB (ARCH_SUPPORTS_PUD_PFNMAP is only set
+         * on x86_64).  Align by power-of-two size, capped at 1GiB.
+         *
+         * NB. qemu_memalign() and friends actually allocate memory, whereas
+         * the region size here can exceed host memory, therefore we manually
+         * create an oversized anonymous mapping and clean it up for alignment.
+         */
+        map_base = mmap(0, region->mmaps[i].size + align, PROT_NONE,
+                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+        if (map_base == MAP_FAILED) {
+            ret = -errno;
+            goto no_mmap;
+        }
+
+        map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align);
+        munmap(map_base, map_align - map_base);
+        munmap(map_align + region->mmaps[i].size,
+               align - (map_align - map_base));
+
+        region->mmaps[i].mmap = mmap(map_align, region->mmaps[i].size, prot,
+                                     MAP_SHARED | MAP_FIXED,
+                                     region->vbasedev->fd,
+                                     region->fd_offset +
+                                     region->mmaps[i].offset);
+        if (region->mmaps[i].mmap == MAP_FAILED) {
+            ret = -errno;
+            goto no_mmap;
+        }
+
+        name = g_strdup_printf("%s mmaps[%d]",
+                               memory_region_name(region->mem), i);
+        memory_region_init_ram_device_ptr(&region->mmaps[i].mem,
+                                          memory_region_owner(region->mem),
+                                          name, region->mmaps[i].size,
+                                          region->mmaps[i].mmap);
+        g_free(name);
+        memory_region_add_subregion(region->mem, region->mmaps[i].offset,
+                                    &region->mmaps[i].mem);
+
+        trace_vfio_region_mmap(memory_region_name(&region->mmaps[i].mem),
+                               region->mmaps[i].offset,
+                               region->mmaps[i].offset +
+                               region->mmaps[i].size - 1);
+    }
+
+    return 0;
+
+no_mmap:
+    trace_vfio_region_mmap_fault(memory_region_name(region->mem), i,
+                                 region->fd_offset + region->mmaps[i].offset,
+                                 region->fd_offset + region->mmaps[i].offset +
+                                 region->mmaps[i].size - 1, ret);
+
+    region->mmaps[i].mmap = NULL;
+
+    for (i--; i >= 0; i--) {
+        vfio_subregion_unmap(region, i);
+    }
+
+    return ret;
+}
+
+void vfio_region_unmap(VFIORegion *region)
+{
+    int i;
+
+    if (!region->mem) {
+        return;
+    }
+
+    for (i = 0; i < region->nr_mmaps; i++) {
+        if (region->mmaps[i].mmap) {
+            vfio_subregion_unmap(region, i);
+        }
+    }
+}
+
+void vfio_region_exit(VFIORegion *region)
+{
+    int i;
+
+    if (!region->mem) {
+        return;
+    }
+
+    for (i = 0; i < region->nr_mmaps; i++) {
+        if (region->mmaps[i].mmap) {
+            memory_region_del_subregion(region->mem, &region->mmaps[i].mem);
+        }
+    }
+
+    trace_vfio_region_exit(region->vbasedev->name, region->nr);
+}
+
+void vfio_region_finalize(VFIORegion *region)
+{
+    int i;
+
+    if (!region->mem) {
+        return;
+    }
+
+    for (i = 0; i < region->nr_mmaps; i++) {
+        if (region->mmaps[i].mmap) {
+            munmap(region->mmaps[i].mmap, region->mmaps[i].size);
+            object_unparent(OBJECT(&region->mmaps[i].mem));
+        }
+    }
+
+    object_unparent(OBJECT(region->mem));
+
+    g_free(region->mem);
+    g_free(region->mmaps);
+
+    trace_vfio_region_finalize(region->vbasedev->name, region->nr);
+
+    region->mem = NULL;
+    region->mmaps = NULL;
+    region->nr_mmaps = 0;
+    region->size = 0;
+    region->flags = 0;
+    region->nr = 0;
+}
+
+void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled)
+{
+    int i;
+
+    if (!region->mem) {
+        return;
+    }
+
+    for (i = 0; i < region->nr_mmaps; i++) {
+        if (region->mmaps[i].mmap) {
+            memory_region_set_enabled(&region->mmaps[i].mem, enabled);
+        }
+    }
+
+    trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem),
+                                        enabled);
+}
diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build
index a8939c838657b09c38f93ad69d541df5aea30a6f..07010c7c9e01a39ae3449c54d2027a2cdd0a7a4d 100644
--- a/hw/vfio/meson.build
+++ b/hw/vfio/meson.build
@@ -23,6 +23,7 @@ system_ss.add(when: 'CONFIG_VFIO', if_true: files(
   'migration.c',
   'migration-multifd.c',
   'cpr.c',
+  'region.c',
 ))
 system_ss.add(when: ['CONFIG_VFIO', 'CONFIG_IOMMUFD'], if_true: files(
   'iommufd.c',
diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events
index 9347e3a5f6607ec6907f9b426da9ab90553292cf..81f4130100c48012c15b5b4858446149a7eaf5b6 100644
--- a/hw/vfio/trace-events
+++ b/hw/vfio/trace-events
@@ -90,8 +90,6 @@ vfio_pci_igd_host_bridge_enabled(const char *name) "%s"
 vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s"
 
 # common.c
-vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)"
-vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64
 vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64
 vfio_listener_region_skip(const char *name, uint64_t start, uint64_t end) "SKIPPING %s 0x%"PRIx64" - 0x%"PRIx64
 vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d"
@@ -107,6 +105,15 @@ vfio_disconnect_container(int fd) "close container->fd=%d"
 vfio_put_group(int fd) "close group->fd=%d"
 vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u"
 vfio_put_base_device(int fd) "close vdev->fd=%d"
+vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x"
+vfio_legacy_dma_unmap_overflow_workaround(void) ""
+vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64
+vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64
+vfio_reset_handler(void) ""
+
+# region.c
+vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)"
+vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64
 vfio_region_setup(const char *dev, int index, const char *name, unsigned long flags, unsigned long offset, unsigned long size) "Device %s, region %d \"%s\", flags: 0x%lx, offset: 0x%lx, size: 0x%lx"
 vfio_region_mmap_fault(const char *name, int index, unsigned long offset, unsigned long size, int fault) "Region %s mmaps[%d], [0x%lx - 0x%lx], fault: %d"
 vfio_region_mmap(const char *name, unsigned long offset, unsigned long end) "Region %s [0x%lx - 0x%lx]"
@@ -116,11 +123,6 @@ vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps e
 vfio_region_unmap(const char *name, unsigned long offset, unsigned long end) "Region %s unmap [0x%lx - 0x%lx]"
 vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries"
 vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]"
-vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x"
-vfio_legacy_dma_unmap_overflow_workaround(void) ""
-vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64
-vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64
-vfio_reset_handler(void) ""
 
 # platform.c
 vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s"
-- 
2.49.0


RE: [PATCH for-10.1 v2 11/37] vfio: Introduce new files for VFIORegion definitions and declarations
Posted by Duan, Zhenzhong 6 days, 4 hours ago

>-----Original Message-----
>From: Cédric Le Goater <clg@redhat.com>
>Subject: [PATCH for-10.1 v2 11/37] vfio: Introduce new files for VFIORegion
>definitions and declarations
>
>Gather all VFIORegion related declarations and definitions into their
>own files to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h".
>They were introduced for 'vfio-platform' support in commits
>db0da029a185 ("vfio: Generalize region support") and a664477db8da
>("hw/vfio/pci: Introduce VFIORegion").
>
>To be noted that the 'vfio-platform' devices have been deprecated and
>will be removed in QEMU 10.2. Until then, make the declarations
>available externally for 'sysbus-fdt.c'.
>
>Cc: Eric Auger <eric.auger@redhat.com>
>Signed-off-by: Cédric Le Goater <clg@redhat.com>
>---
> hw/vfio/vfio-display.h          |   1 +
> include/hw/vfio/vfio-common.h   |  32 +--
> include/hw/vfio/vfio-platform.h |   2 +
> include/hw/vfio/vfio-region.h   |  47 ++++
> hw/core/sysbus-fdt.c            |   1 +
> hw/vfio/helpers.c               | 363 -----------------------------
> hw/vfio/pci-quirks.c            |   1 +
> hw/vfio/pci.c                   |   1 +
> hw/vfio/platform.c              |   1 +
> hw/vfio/region.c                | 394 ++++++++++++++++++++++++++++++++
> hw/vfio/meson.build             |   1 +
> hw/vfio/trace-events            |  16 +-
> 12 files changed, 459 insertions(+), 401 deletions(-)
> create mode 100644 include/hw/vfio/vfio-region.h
> create mode 100644 hw/vfio/region.c
>
>diff --git a/hw/vfio/vfio-display.h b/hw/vfio/vfio-display.h
>index
>99b8cb67ef7558d3eefe3105a831e3fcb30afc4d..2606c34b396a88cec3e8f884adb
>158e48e8105f1 100644
>--- a/hw/vfio/vfio-display.h
>+++ b/hw/vfio/vfio-display.h
>@@ -11,6 +11,7 @@
>
> #include "ui/console.h"
> #include "hw/display/ramfb.h"
>+#include "hw/vfio/vfio-region.h"
>
> typedef struct VFIODMABuf {
>     QemuDmaBuf *buf;
>diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
>index
>8d48f5300a791d8858fe29d1bb905f814ef11990..3d470d79325526e0508683c445
>a7635c78a57e34 100644
>--- a/include/hw/vfio/vfio-common.h
>+++ b/include/hw/vfio/vfio-common.h
>@@ -39,25 +39,6 @@ enum {
>     VFIO_DEVICE_TYPE_CCW = 2,
>     VFIO_DEVICE_TYPE_AP = 3,
> };
>-
>-typedef struct VFIOMmap {
>-    MemoryRegion mem;
>-    void *mmap;
>-    off_t offset;
>-    size_t size;
>-} VFIOMmap;
>-
>-typedef struct VFIORegion {
>-    struct VFIODevice *vbasedev;
>-    off_t fd_offset; /* offset of region within device fd */
>-    MemoryRegion *mem; /* slow, read/write access */
>-    size_t size;
>-    uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
>-    uint32_t nr_mmaps;
>-    VFIOMmap *mmaps;
>-    uint8_t nr; /* cache the region number for debug */
>-} VFIORegion;
>-
> struct VFIOGroup;
>
> typedef struct VFIOContainer {
>@@ -168,17 +149,7 @@ void vfio_unmask_single_irqindex(VFIODevice
>*vbasedev, int index);
> void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index);
> bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex,
>                             int action, int fd, Error **errp);
>-void vfio_region_write(void *opaque, hwaddr addr,
>-                           uint64_t data, unsigned size);
>-uint64_t vfio_region_read(void *opaque,
>-                          hwaddr addr, unsigned size);
>-int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
>-                      int index, const char *name);
>-int vfio_region_mmap(VFIORegion *region);
>-void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
>-void vfio_region_unmap(VFIORegion *region);
>-void vfio_region_exit(VFIORegion *region);
>-void vfio_region_finalize(VFIORegion *region);
>+
> void vfio_reset_handler(void *opaque);
> struct vfio_device_info *vfio_get_device_info(int fd);
> bool vfio_device_is_mdev(VFIODevice *vbasedev);
>@@ -194,7 +165,6 @@ int vfio_kvm_device_del_fd(int fd, Error **errp);
> bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp);
> void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer);
>
>-extern const MemoryRegionOps vfio_region_ops;
> typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList;
> typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList;
> extern VFIOGroupList vfio_group_list;
>diff --git a/include/hw/vfio/vfio-platform.h b/include/hw/vfio/vfio-platform.h
>index
>c414c3dffcc840a2f40a1b252d0f7b4e309c78d4..3191545717da51abc41d10cd364
>6cd047b4a676c 100644
>--- a/include/hw/vfio/vfio-platform.h
>+++ b/include/hw/vfio/vfio-platform.h
>@@ -47,6 +47,8 @@ typedef struct VFIOINTp {
> /* function type for user side eventfd handler */
> typedef void (*eventfd_user_side_handler_t)(VFIOINTp *intp);
>
>+typedef struct VFIORegion VFIORegion;
>+
> struct VFIOPlatformDevice {
>     SysBusDevice sbdev;
>     VFIODevice vbasedev; /* not a QOM object */
>diff --git a/include/hw/vfio/vfio-region.h b/include/hw/vfio/vfio-region.h
>new file mode 100644
>index
>0000000000000000000000000000000000000000..9dc0535e7ce46fbb671e70918
>b93cb115857efe6
>--- /dev/null
>+++ b/include/hw/vfio/vfio-region.h
>@@ -0,0 +1,47 @@
>+/*
>+ * VFIO region
>+ *
>+ * Copyright Red Hat, Inc. 2025
>+ *
>+ * SPDX-License-Identifier: GPL-2.0-or-later
>+ */
>+
>+#ifndef HW_VFIO_REGION_H
>+#define HW_VFIO_REGION_H
>+
>+#include "exec/memory.h"
>+
>+typedef struct VFIOMmap {
>+    MemoryRegion mem;
>+    void *mmap;
>+    off_t offset;
>+    size_t size;
>+} VFIOMmap;
>+
>+typedef struct VFIODevice VFIODevice;
>+
>+typedef struct VFIORegion {
>+    struct VFIODevice *vbasedev;
>+    off_t fd_offset; /* offset of region within device fd */
>+    MemoryRegion *mem; /* slow, read/write access */
>+    size_t size;
>+    uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
>+    uint32_t nr_mmaps;
>+    VFIOMmap *mmaps;
>+    uint8_t nr; /* cache the region number for debug */
>+} VFIORegion;
>+
>+
>+void vfio_region_write(void *opaque, hwaddr addr,
>+                           uint64_t data, unsigned size);
>+uint64_t vfio_region_read(void *opaque,
>+                          hwaddr addr, unsigned size);
>+int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
>+                      int index, const char *name);
>+int vfio_region_mmap(VFIORegion *region);
>+void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
>+void vfio_region_unmap(VFIORegion *region);
>+void vfio_region_exit(VFIORegion *region);
>+void vfio_region_finalize(VFIORegion *region);
>+
>+#endif /* HW_VFIO_REGION_H */
>diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c
>index
>e85066b905637b1ca34b5965f383df341f3a4eb7..c339a27875cbee8131b064674a
>a09adf4b9efa25 100644
>--- a/hw/core/sysbus-fdt.c
>+++ b/hw/core/sysbus-fdt.c
>@@ -35,6 +35,7 @@
> #include "hw/vfio/vfio-platform.h"
> #include "hw/vfio/vfio-calxeda-xgmac.h"
> #include "hw/vfio/vfio-amd-xgbe.h"
>+#include "hw/vfio/vfio-region.h"
> #include "hw/display/ramfb.h"
> #include "hw/uefi/var-service-api.h"
> #include "hw/arm/fdt.h"
>diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c
>index
>4b255d4f3a9e81f55df00c68fc71da769fd5bd04..89403943a7a219e491b6812d50b
>27b7f1fd7b4a4 100644
>--- a/hw/vfio/helpers.c
>+++ b/hw/vfio/helpers.c
>@@ -147,118 +147,6 @@ bool vfio_set_irq_signaling(VFIODevice *vbasedev, int
>index, int subindex,
>     return false;
> }
>
>-/*
>- * IO Port/MMIO - Beware of the endians, VFIO is always little endian
>- */
>-void vfio_region_write(void *opaque, hwaddr addr,
>-                       uint64_t data, unsigned size)
>-{
>-    VFIORegion *region = opaque;
>-    VFIODevice *vbasedev = region->vbasedev;
>-    union {
>-        uint8_t byte;
>-        uint16_t word;
>-        uint32_t dword;
>-        uint64_t qword;
>-    } buf;
>-
>-    switch (size) {
>-    case 1:
>-        buf.byte = data;
>-        break;
>-    case 2:
>-        buf.word = cpu_to_le16(data);
>-        break;
>-    case 4:
>-        buf.dword = cpu_to_le32(data);
>-        break;
>-    case 8:
>-        buf.qword = cpu_to_le64(data);
>-        break;
>-    default:
>-        hw_error("vfio: unsupported write size, %u bytes", size);
>-        break;
>-    }
>-
>-    if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
>-        error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64
>-                     ",%d) failed: %m",
>-                     __func__, vbasedev->name, region->nr,
>-                     addr, data, size);
>-    }
>-
>-    trace_vfio_region_write(vbasedev->name, region->nr, addr, data, size);
>-
>-    /*
>-     * A read or write to a BAR always signals an INTx EOI.  This will
>-     * do nothing if not pending (including not in INTx mode).  We assume
>-     * that a BAR access is in response to an interrupt and that BAR
>-     * accesses will service the interrupt.  Unfortunately, we don't know
>-     * which access will service the interrupt, so we're potentially
>-     * getting quite a few host interrupts per guest interrupt.
>-     */
>-    vbasedev->ops->vfio_eoi(vbasedev);
>-}
>-
>-uint64_t vfio_region_read(void *opaque,
>-                          hwaddr addr, unsigned size)
>-{
>-    VFIORegion *region = opaque;
>-    VFIODevice *vbasedev = region->vbasedev;
>-    union {
>-        uint8_t byte;
>-        uint16_t word;
>-        uint32_t dword;
>-        uint64_t qword;
>-    } buf;
>-    uint64_t data = 0;
>-
>-    if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) {
>-        error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m",
>-                     __func__, vbasedev->name, region->nr,
>-                     addr, size);
>-        return (uint64_t)-1;
>-    }
>-    switch (size) {
>-    case 1:
>-        data = buf.byte;
>-        break;
>-    case 2:
>-        data = le16_to_cpu(buf.word);
>-        break;
>-    case 4:
>-        data = le32_to_cpu(buf.dword);
>-        break;
>-    case 8:
>-        data = le64_to_cpu(buf.qword);
>-        break;
>-    default:
>-        hw_error("vfio: unsupported read size, %u bytes", size);
>-        break;
>-    }
>-
>-    trace_vfio_region_read(vbasedev->name, region->nr, addr, size, data);
>-
>-    /* Same as write above */
>-    vbasedev->ops->vfio_eoi(vbasedev);
>-
>-    return data;
>-}
>-
>-const MemoryRegionOps vfio_region_ops = {
>-    .read = vfio_region_read,
>-    .write = vfio_region_write,
>-    .endianness = DEVICE_LITTLE_ENDIAN,
>-    .valid = {
>-        .min_access_size = 1,
>-        .max_access_size = 8,
>-    },
>-    .impl = {
>-        .min_access_size = 1,
>-        .max_access_size = 8,
>-    },
>-};
>-
> int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size)
> {
>     vbmap->pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size();
>@@ -306,257 +194,6 @@ vfio_get_device_info_cap(struct vfio_device_info
>*info, uint16_t id)
>     return vfio_get_cap((void *)info, info->cap_offset, id);
> }
>
>-static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
>-                                          struct vfio_region_info *info)
>-{
>-    struct vfio_info_cap_header *hdr;
>-    struct vfio_region_info_cap_sparse_mmap *sparse;
>-    int i, j;
>-
>-    hdr = vfio_get_region_info_cap(info,
>VFIO_REGION_INFO_CAP_SPARSE_MMAP);
>-    if (!hdr) {
>-        return -ENODEV;
>-    }
>-
>-    sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap,
>header);
>-
>-    trace_vfio_region_sparse_mmap_header(region->vbasedev->name,
>-                                         region->nr, sparse->nr_areas);
>-
>-    region->mmaps = g_new0(VFIOMmap, sparse->nr_areas);
>-
>-    for (i = 0, j = 0; i < sparse->nr_areas; i++) {
>-        if (sparse->areas[i].size) {
>-            trace_vfio_region_sparse_mmap_entry(i, sparse->areas[i].offset,
>-                                            sparse->areas[i].offset +
>-                                            sparse->areas[i].size - 1);
>-            region->mmaps[j].offset = sparse->areas[i].offset;
>-            region->mmaps[j].size = sparse->areas[i].size;
>-            j++;
>-        }
>-    }
>-
>-    region->nr_mmaps = j;
>-    region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap));
>-
>-    return 0;
>-}
>-
>-int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
>-                      int index, const char *name)
>-{
>-    g_autofree struct vfio_region_info *info = NULL;
>-    int ret;
>-
>-    ret = vfio_get_region_info(vbasedev, index, &info);
>-    if (ret) {
>-        return ret;
>-    }
>-
>-    region->vbasedev = vbasedev;
>-    region->flags = info->flags;
>-    region->size = info->size;
>-    region->fd_offset = info->offset;
>-    region->nr = index;
>-
>-    if (region->size) {
>-        region->mem = g_new0(MemoryRegion, 1);
>-        memory_region_init_io(region->mem, obj, &vfio_region_ops,
>-                              region, name, region->size);
>-
>-        if (!vbasedev->no_mmap &&
>-            region->flags & VFIO_REGION_INFO_FLAG_MMAP) {
>-
>-            ret = vfio_setup_region_sparse_mmaps(region, info);
>-
>-            if (ret) {
>-                region->nr_mmaps = 1;
>-                region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
>-                region->mmaps[0].offset = 0;
>-                region->mmaps[0].size = region->size;
>-            }
>-        }
>-    }
>-
>-    trace_vfio_region_setup(vbasedev->name, index, name,
>-                            region->flags, region->fd_offset, region->size);
>-    return 0;
>-}
>-
>-static void vfio_subregion_unmap(VFIORegion *region, int index)
>-{
>-    trace_vfio_region_unmap(memory_region_name(&region-
>>mmaps[index].mem),
>-                            region->mmaps[index].offset,
>-                            region->mmaps[index].offset +
>-                            region->mmaps[index].size - 1);
>-    memory_region_del_subregion(region->mem, &region->mmaps[index].mem);
>-    munmap(region->mmaps[index].mmap, region->mmaps[index].size);
>-    object_unparent(OBJECT(&region->mmaps[index].mem));
>-    region->mmaps[index].mmap = NULL;
>-}
>-
>-int vfio_region_mmap(VFIORegion *region)
>-{
>-    int i, ret, prot = 0;
>-    char *name;
>-
>-    if (!region->mem) {
>-        return 0;
>-    }
>-
>-    prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
>-    prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
>-
>-    for (i = 0; i < region->nr_mmaps; i++) {
>-        size_t align = MIN(1ULL << ctz64(region->mmaps[i].size), 1 * GiB);
>-        void *map_base, *map_align;
>-
>-        /*
>-         * Align the mmap for more efficient mapping in the kernel.  Ideally
>-         * we'd know the PMD and PUD mapping sizes to use as discrete alignment
>-         * intervals, but we don't.  As of Linux v6.12, the largest PUD size
>-         * supporting huge pfnmap is 1GiB (ARCH_SUPPORTS_PUD_PFNMAP is only
>set
>-         * on x86_64).  Align by power-of-two size, capped at 1GiB.
>-         *
>-         * NB. qemu_memalign() and friends actually allocate memory, whereas
>-         * the region size here can exceed host memory, therefore we manually
>-         * create an oversized anonymous mapping and clean it up for alignment.
>-         */
>-        map_base = mmap(0, region->mmaps[i].size + align, PROT_NONE,
>-                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
>-        if (map_base == MAP_FAILED) {
>-            ret = -errno;
>-            goto no_mmap;
>-        }
>-
>-        map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align);
>-        munmap(map_base, map_align - map_base);
>-        munmap(map_align + region->mmaps[i].size,
>-               align - (map_align - map_base));
>-
>-        region->mmaps[i].mmap = mmap(map_align, region->mmaps[i].size, prot,
>-                                     MAP_SHARED | MAP_FIXED,
>-                                     region->vbasedev->fd,
>-                                     region->fd_offset +
>-                                     region->mmaps[i].offset);
>-        if (region->mmaps[i].mmap == MAP_FAILED) {
>-            ret = -errno;
>-            goto no_mmap;
>-        }
>-
>-        name = g_strdup_printf("%s mmaps[%d]",
>-                               memory_region_name(region->mem), i);
>-        memory_region_init_ram_device_ptr(&region->mmaps[i].mem,
>-                                          memory_region_owner(region->mem),
>-                                          name, region->mmaps[i].size,
>-                                          region->mmaps[i].mmap);
>-        g_free(name);
>-        memory_region_add_subregion(region->mem, region->mmaps[i].offset,
>-                                    &region->mmaps[i].mem);
>-
>-        trace_vfio_region_mmap(memory_region_name(&region->mmaps[i].mem),
>-                               region->mmaps[i].offset,
>-                               region->mmaps[i].offset +
>-                               region->mmaps[i].size - 1);
>-    }
>-
>-    return 0;
>-
>-no_mmap:
>-    trace_vfio_region_mmap_fault(memory_region_name(region->mem), i,
>-                                 region->fd_offset + region->mmaps[i].offset,
>-                                 region->fd_offset + region->mmaps[i].offset +
>-                                 region->mmaps[i].size - 1, ret);
>-
>-    region->mmaps[i].mmap = NULL;
>-
>-    for (i--; i >= 0; i--) {
>-        vfio_subregion_unmap(region, i);
>-    }
>-
>-    return ret;
>-}
>-
>-void vfio_region_unmap(VFIORegion *region)
>-{
>-    int i;
>-
>-    if (!region->mem) {
>-        return;
>-    }
>-
>-    for (i = 0; i < region->nr_mmaps; i++) {
>-        if (region->mmaps[i].mmap) {
>-            vfio_subregion_unmap(region, i);
>-        }
>-    }
>-}
>-
>-void vfio_region_exit(VFIORegion *region)
>-{
>-    int i;
>-
>-    if (!region->mem) {
>-        return;
>-    }
>-
>-    for (i = 0; i < region->nr_mmaps; i++) {
>-        if (region->mmaps[i].mmap) {
>-            memory_region_del_subregion(region->mem, &region->mmaps[i].mem);
>-        }
>-    }
>-
>-    trace_vfio_region_exit(region->vbasedev->name, region->nr);
>-}
>-
>-void vfio_region_finalize(VFIORegion *region)
>-{
>-    int i;
>-
>-    if (!region->mem) {
>-        return;
>-    }
>-
>-    for (i = 0; i < region->nr_mmaps; i++) {
>-        if (region->mmaps[i].mmap) {
>-            munmap(region->mmaps[i].mmap, region->mmaps[i].size);
>-            object_unparent(OBJECT(&region->mmaps[i].mem));
>-        }
>-    }
>-
>-    object_unparent(OBJECT(region->mem));
>-
>-    g_free(region->mem);
>-    g_free(region->mmaps);
>-
>-    trace_vfio_region_finalize(region->vbasedev->name, region->nr);
>-
>-    region->mem = NULL;
>-    region->mmaps = NULL;
>-    region->nr_mmaps = 0;
>-    region->size = 0;
>-    region->flags = 0;
>-    region->nr = 0;
>-}
>-
>-void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled)
>-{
>-    int i;
>-
>-    if (!region->mem) {
>-        return;
>-    }
>-
>-    for (i = 0; i < region->nr_mmaps; i++) {
>-        if (region->mmaps[i].mmap) {
>-            memory_region_set_enabled(&region->mmaps[i].mem, enabled);
>-        }
>-    }
>-
>-    trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem),
>-                                        enabled);
>-}
>-
> int vfio_get_region_info(VFIODevice *vbasedev, int index,
>                          struct vfio_region_info **info)
> {
>diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c
>index
>3f002252acfb7ac809107c99bdbdbaf66d56a50d..4591ec68da877b307f43ea1a83
>0c315721b57e9e 100644
>--- a/hw/vfio/pci-quirks.c
>+++ b/hw/vfio/pci-quirks.c
>@@ -26,6 +26,7 @@
> #include "hw/qdev-properties.h"
> #include "pci.h"
> #include "pci-quirks.h"
>+#include "hw/vfio/vfio-region.h"

This looks unnecessary as pci.h already include it,

> #include "trace.h"
>
> /*
>diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
>index
>158deca06cb240622a254f5059c47873e5fcc7de..7457e81cdb07bc6657bb151434
>9c172a152cb540 100644
>--- a/hw/vfio/pci.c
>+++ b/hw/vfio/pci.c
>@@ -45,6 +45,7 @@
> #include "migration/qemu-file.h"
> #include "system/iommufd.h"
> #include "vfio-migration-internal.h"
>+#include "hw/vfio/vfio-region.h"


Same here, otherwise

Reviewed-by: Zhenzhong Duan <zhenzhong.duan@intel.com>

Thanks
Zhenzhong