Add an "x-vf-migration" property to the IGB PF device and expose a
DVSEC (Designated Vendor-Specific Extended Capability) at offset 0x160
in VF extended config space when migration is enabled.

The DVSEC provides the register interface for VF live migration:
 - CAPS:       supported features (state migration)
 - CTRL:       command doorbell
 - STATUS:     state and error reporting
 - BUF_ADDR:   shared DMA buffer address (GPA)

Move IgbVfState from igbvf.c to igb_common.h so it can be shared with
the migration module, and add the migration state field.

AI-used-for: code (prototype)
Signed-off-by: Cédric Le Goater <[email protected]>
---
 MAINTAINERS            |   5 ++
 hw/net/igb_common.h    |  16 +++++++
 hw/net/igb_migration.h |  72 ++++++++++++++++++++++++++++
 hw/net/igb.c           |   2 +
 hw/net/igb_migration.c | 106 +++++++++++++++++++++++++++++++++++++++++
 hw/net/igbvf.c         |  52 ++++++++++++++++----
 hw/net/meson.build     |   2 +-
 7 files changed, 245 insertions(+), 10 deletions(-)
 create mode 100644 hw/net/igb_migration.h
 create mode 100644 hw/net/igb_migration.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 4a49a40294eb..f88b526be238 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2816,6 +2816,11 @@ F: tests/functional/x86_64/test_netdev_ethtool.py
 F: tests/qtest/igb-test.c
 F: tests/qtest/libqos/igb.c
 
+igb VF migration
+M: Cédric Le Goater <[email protected]>
+S: Maintained
+F: hw/net/igb_migration.*
+
 eepro100
 M: Stefan Weil <[email protected]>
 S: Maintained
diff --git a/hw/net/igb_common.h b/hw/net/igb_common.h
index b316a5bcfa5c..f0e2529e757b 100644
--- a/hw/net/igb_common.h
+++ b/hw/net/igb_common.h
@@ -26,7 +26,9 @@
 #ifndef HW_NET_IGB_COMMON_H
 #define HW_NET_IGB_COMMON_H
 
+#include "hw/pci/pci_device.h"
 #include "igb_regs.h"
+#include "igb_migration.h"
 
 #define TYPE_IGBVF "igbvf"
 
@@ -154,4 +156,18 @@ uint64_t igb_mmio_read(void *opaque, hwaddr addr, unsigned 
size);
 void igb_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size);
 void igb_vf_reset(void *opaque, uint16_t vfn);
 
+OBJECT_DECLARE_SIMPLE_TYPE(IgbVfState, IGBVF)
+
+struct IgbVfState {
+    PCIDevice parent_obj;
+
+    uint16_t vfn;
+    bool migration_enabled;
+
+    MemoryRegion mmio;
+    MemoryRegion msix;
+
+    IgbVfMigState mig;
+};
+
 #endif
diff --git a/hw/net/igb_migration.h b/hw/net/igb_migration.h
new file mode 100644
index 000000000000..3da28e11e49e
--- /dev/null
+++ b/hw/net/igb_migration.h
@@ -0,0 +1,72 @@
+/*
+ * QEMU Intel 82576 SR/IOV VF Migration Support
+ *
+ * Copyright (c) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef HW_NET_IGB_MIGRATION_H
+#define HW_NET_IGB_MIGRATION_H
+
+#include "hw/pci/pci_device.h"
+
+/*
+ * Migration interface exposed as a DVSEC (Designated Vendor-Specific
+ * Extended Capability) in VF extended config space.
+ *
+ * DVSEC layout at IGB_MIG_DVSEC_OFFSET (0x160):
+ *
+ *   +0x00  PCIe extended cap header   (cap_id=0x23, ver=1, next)
+ *   +0x04  DVSEC header 1             (len | rev | vendor_id)
+ *   +0x08  DVSEC header 2             (DVSEC ID)
+ *   +0x0A  Reserved                   (padding for DWORD alignment)
+ *   +0x0C  CAPS                       (RO: F_STATE[0])
+ *   +0x10  CTRL                       (WO: doorbell command)
+ *   +0x14  STATUS                     (RO: state[7:0], error_code[15:8])
+ *   +0x18  BUF_ADDR_LO                (RW: shared buffer GPA low)
+ *   +0x1C  BUF_ADDR_HI                (RW: shared buffer GPA high)
+ */
+
+#define IGB_MIG_DVSEC_OFFSET    0x160
+#define IGB_MIG_DVSEC_SIZE      0x20
+#define IGB_MIG_DVSEC_VER       1
+#define IGB_MIG_DVSEC_ID        1
+
+/* Register offsets relative to DVSEC base */
+#define IGB_MIG_CAPS            0x0C
+#define IGB_MIG_CTRL            0x10
+#define IGB_MIG_STATUS          0x14
+#define IGB_MIG_BUF_ADDR_LO     0x18
+#define IGB_MIG_BUF_ADDR_HI     0x1C
+
+/* CAPS register layout */
+#define IGB_MIG_CAP_F_STATE             (1u << 0)
+
+/* STATUS register: state in [7:0], error code [15:8] */
+#define IGB_MIG_STATUS_STATE_MASK       0xFF
+#define IGB_MIG_STATUS_ERROR_CODE_SHIFT 8
+#define IGB_MIG_STATUS_ERR(code) \
+    ((uint32_t)(code) << IGB_MIG_STATUS_ERROR_CODE_SHIFT)
+
+/* Device states (based on VFIO migration v2) */
+#define IGB_MIG_STATE_ERROR             0
+#define IGB_MIG_STATE_STOP              1
+#define IGB_MIG_STATE_RUNNING           2
+#define IGB_MIG_STATE_STOP_COPY         3
+#define IGB_MIG_STATE_RESUMING          4
+
+typedef struct IgbVfMigState {
+    uint32_t mig_state;
+    uint64_t mig_data_buf_addr;
+} IgbVfMigState;
+
+typedef struct IgbVfState IgbVfState;
+
+bool igbvf_add_migration_dvsec(PCIDevice *dev, Error **errp);
+void igbvf_mig_state_reset(IgbVfState *s);
+uint32_t igbvf_mig_config_read(IgbVfState *s, uint32_t addr, int size);
+bool igbvf_mig_config_write(IgbVfState *s, uint32_t addr, uint32_t val,
+                            int size);
+
+#endif
diff --git a/hw/net/igb.c b/hw/net/igb.c
index c076807e7110..7268e5473fc3 100644
--- a/hw/net/igb.c
+++ b/hw/net/igb.c
@@ -79,6 +79,7 @@ struct IGBState {
 
     IGBCore core;
     bool has_flr;
+    bool vf_migration;
 };
 
 #define IGB_CAP_SRIOV_OFFSET    (0x160)
@@ -597,6 +598,7 @@ static const VMStateDescription igb_vmstate = {
 static const Property igb_properties[] = {
     DEFINE_NIC_PROPERTIES(IGBState, conf),
     DEFINE_PROP_BOOL("x-pcie-flr-init", IGBState, has_flr, true),
+    DEFINE_PROP_BOOL("x-vf-migration", IGBState, vf_migration, false),
 };
 
 static void igb_class_init(ObjectClass *class, const void *data)
diff --git a/hw/net/igb_migration.c b/hw/net/igb_migration.c
new file mode 100644
index 000000000000..4dfebd82344c
--- /dev/null
+++ b/hw/net/igb_migration.c
@@ -0,0 +1,106 @@
+/*
+ * QEMU Intel 82576 SR/IOV VF Migration Support
+ *
+ * Copyright (c) 2026 Red Hat, Inc.
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "qemu/osdep.h"
+#include "hw/pci/pci_device.h"
+#include "hw/pci/pcie.h"
+#include "igb_common.h"
+#include "igb_migration.h"
+
+static void igbvf_mig_update_status(IgbVfState *s, uint8_t err)
+{
+    IgbVfMigState *ms = &s->mig;
+    PCIDevice *dev = PCI_DEVICE(s);
+    uint32_t status;
+
+    status = ms->mig_state & IGB_MIG_STATUS_STATE_MASK;
+
+    if (err) {
+        status = IGB_MIG_STATE_ERROR | IGB_MIG_STATUS_ERR(err);
+    }
+
+    pci_set_long(dev->config + IGB_MIG_DVSEC_OFFSET + IGB_MIG_STATUS, status);
+}
+
+
+bool igbvf_add_migration_dvsec(PCIDevice *dev, Error **errp)
+{
+    uint16_t offset = IGB_MIG_DVSEC_OFFSET;
+    uint32_t caps;
+
+    pcie_add_capability(dev, PCI_EXT_CAP_ID_DVSEC, 1, offset,
+                        IGB_MIG_DVSEC_SIZE);
+
+    /* DVSEC header 1: length[31:20] | rev[19:16] | vendor_id[15:0] */
+    pci_set_long(dev->config + offset + 0x4,
+                 (IGB_MIG_DVSEC_SIZE << 20) |
+                 (IGB_MIG_DVSEC_VER << 16) |
+                 PCI_VENDOR_ID_INTEL);
+
+    /* DVSEC header 2: DVSEC ID */
+    pci_set_word(dev->config + offset + 0x8, IGB_MIG_DVSEC_ID);
+
+    /* CAPS: features (state migration only) */
+    caps = IGB_MIG_CAP_F_STATE;
+    pci_set_long(dev->config + offset + IGB_MIG_CAPS, caps);
+
+    /* STATUS: initial state is RUNNING */
+    pci_set_long(dev->config + offset + IGB_MIG_STATUS,
+                 IGB_MIG_STATE_RUNNING);
+
+    /* BUF_ADDR_LO and BUF_ADDR_HI are writable */
+    memset(dev->wmask + offset + IGB_MIG_BUF_ADDR_LO, 0xff, 4);
+    memset(dev->wmask + offset + IGB_MIG_BUF_ADDR_HI, 0xff, 4);
+
+    return true;
+}
+
+uint32_t igbvf_mig_config_read(IgbVfState *s, uint32_t addr, int size)
+{
+    PCIDevice *dev = PCI_DEVICE(s);
+
+    return pci_default_read_config(dev, addr, size);
+}
+
+bool igbvf_mig_config_write(IgbVfState *s, uint32_t addr, uint32_t val,
+                            int size)
+{
+    PCIDevice *dev = PCI_DEVICE(s);
+    uint32_t offset = addr - IGB_MIG_DVSEC_OFFSET;
+
+    switch (offset) {
+    case IGB_MIG_CTRL:
+        /* Command handling will be added in a later commit */
+        break;
+
+    case IGB_MIG_BUF_ADDR_LO:
+    case IGB_MIG_BUF_ADDR_HI:
+        pci_default_write_config(dev, addr, val, size);
+        break;
+
+    default:
+        break;
+    }
+
+    return true;
+}
+
+void igbvf_mig_state_reset(IgbVfState *s)
+{
+    IgbVfMigState *ms = &s->mig;
+
+    ms->mig_state = IGB_MIG_STATE_RUNNING;
+    ms->mig_data_buf_addr = 0;
+
+    pci_set_long(PCI_DEVICE(s)->config +
+                 IGB_MIG_DVSEC_OFFSET + IGB_MIG_BUF_ADDR_LO, 0);
+    pci_set_long(PCI_DEVICE(s)->config +
+                 IGB_MIG_DVSEC_OFFSET + IGB_MIG_BUF_ADDR_HI, 0);
+
+    igbvf_mig_update_status(s, 0);
+}
diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c
index 9a165c7063ee..30dfdb574ac7 100644
--- a/hw/net/igbvf.c
+++ b/hw/net/igbvf.c
@@ -38,27 +38,21 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/range.h"
 #include "hw/core/hw-error.h"
 #include "hw/net/mii.h"
 #include "hw/pci/pci_device.h"
 #include "hw/pci/pcie.h"
+#include "hw/pci/pcie_sriov.h"
 #include "hw/pci/msix.h"
 #include "net/eth.h"
 #include "net/net.h"
 #include "igb_common.h"
 #include "igb_core.h"
+#include "igb_migration.h"
 #include "trace.h"
 #include "qapi/error.h"
 
-OBJECT_DECLARE_SIMPLE_TYPE(IgbVfState, IGBVF)
-
-struct IgbVfState {
-    PCIDevice parent_obj;
-
-    MemoryRegion mmio;
-    MemoryRegion msix;
-};
-
 static hwaddr vf_to_pf_addr(hwaddr addr, uint16_t vfn, bool write)
 {
     switch (addr) {
@@ -199,10 +193,35 @@ static hwaddr vf_to_pf_addr(hwaddr addr, uint16_t vfn, 
bool write)
     return HWADDR_MAX;
 }
 
+static bool igbvf_addr_in_dvsec(uint32_t addr, int len)
+{
+    return ranges_overlap(addr, len,
+                          IGB_MIG_DVSEC_OFFSET, IGB_MIG_DVSEC_SIZE);
+}
+
+static uint32_t igbvf_read_config(PCIDevice *dev, uint32_t addr, int size)
+{
+    IgbVfState *s = IGBVF(dev);
+
+    if (s->migration_enabled && igbvf_addr_in_dvsec(addr, size)) {
+        return igbvf_mig_config_read(s, addr, size);
+    }
+
+    return pci_default_read_config(dev, addr, size);
+}
+
 static void igbvf_write_config(PCIDevice *dev, uint32_t addr, uint32_t val,
     int len)
 {
+    IgbVfState *s = IGBVF(dev);
+
     trace_igbvf_write_config(addr, val, len);
+
+    if (s->migration_enabled && igbvf_addr_in_dvsec(addr, len)) {
+        igbvf_mig_config_write(s, addr, val, len);
+        return;
+    }
+
     pci_default_write_config(dev, addr, val, len);
     if (object_property_get_bool(OBJECT(pcie_sriov_get_pf(dev)),
                                  "x-pcie-flr-init", &error_abort)) {
@@ -282,13 +301,27 @@ static void igbvf_pci_realize(PCIDevice *dev, Error 
**errp)
     }
 
     pcie_ari_init(dev, 0x150);
+
+    if (object_property_get_bool(OBJECT(pcie_sriov_get_pf(dev)),
+                                 "x-vf-migration", &error_abort)) {
+        s->vfn = pcie_sriov_vf_number(dev);
+        s->migration_enabled = true;
+        if (!igbvf_add_migration_dvsec(dev, errp)) {
+            return;
+        }
+    }
 }
 
 static void igbvf_qdev_reset_hold(Object *obj, ResetType type)
 {
     PCIDevice *vf = PCI_DEVICE(obj);
+    IgbVfState *s = IGBVF(vf);
 
     igb_vf_reset(pcie_sriov_get_pf(vf), pcie_sriov_vf_number(vf));
+
+    if (s->migration_enabled) {
+        igbvf_mig_state_reset(s);
+    }
 }
 
 static void igbvf_pci_uninit(PCIDevice *dev)
@@ -309,6 +342,7 @@ static void igbvf_class_init(ObjectClass *class, const void 
*data)
 
     c->realize = igbvf_pci_realize;
     c->exit = igbvf_pci_uninit;
+    c->config_read = igbvf_read_config;
     c->vendor_id = PCI_VENDOR_ID_INTEL;
     c->device_id = E1000_DEV_ID_82576_VF;
     c->revision = 1;
diff --git a/hw/net/meson.build b/hw/net/meson.build
index 84f142df222a..bb4b449b25ba 100644
--- a/hw/net/meson.build
+++ b/hw/net/meson.build
@@ -11,7 +11,7 @@ system_ss.add(when: 'CONFIG_E1000_PCI', if_true: 
files('e1000.c', 'e1000x_common
 system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: 
files('net_tx_pkt.c', 'net_rx_pkt.c'))
 system_ss.add(when: 'CONFIG_E1000E_PCI_EXPRESS', if_true: files('e1000e.c', 
'e1000e_core.c', 'e1000x_common.c'))
 system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('net_tx_pkt.c', 
'net_rx_pkt.c'))
-system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('igb.c', 
'igbvf.c', 'igb_core.c'))
+system_ss.add(when: 'CONFIG_IGB_PCI_EXPRESS', if_true: files('igb.c', 
'igbvf.c', 'igb_core.c', 'igb_migration.c'))
 system_ss.add(when: 'CONFIG_RTL8139_PCI', if_true: files('rtl8139.c'))
 system_ss.add(when: 'CONFIG_TULIP', if_true: files('tulip.c'))
 system_ss.add(when: 'CONFIG_VMXNET3_PCI', if_true: files('net_tx_pkt.c', 
'net_rx_pkt.c'))
-- 
2.55.0


Reply via email to