diff mbox

pci-assign: Remove

Message ID 20171020082538.17124-1-pbonzini@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Paolo Bonzini Oct. 20, 2017, 8:25 a.m. UTC
Legacy PCI device assignment has been removed from Linux in 4.12,
and had been deprecated 2 years ago there.  We can remove it from
QEMU as well.

The ROM loading code was shared with Xen PCI passthrough, so move
it to hw/xen.

Cc: Stefano Stabellini <sstabellini@kernel.org>
Cc: Anthony Perard <anthony.perard@citrix.com>
Cc: xen-devel@lists.xenproject.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
---
        Xen parts only compile-tested.

 docs/qdev-device-use.txt                           |   12 +-
 hw/i386/Makefile.objs                              |    1 -
 hw/i386/kvm/Makefile.objs                          |    2 +-
 hw/i386/kvm/pci-assign.c                           | 1883 --------------------
 hw/xen/Makefile.objs                               |    1 +
 .../xen_pt_load_rom.c}                             |    4 +-
 include/hw/pci/pci-assign.h                        |   27 -
 qdev-monitor.c                                     |    1 -
 scripts/device-crash-test                          |    2 -
 9 files changed, 6 insertions(+), 1927 deletions(-)
 delete mode 100644 hw/i386/kvm/pci-assign.c
 rename hw/{i386/pci-assign-load-rom.c => xen/xen_pt_load_rom.c} (96%)
 delete mode 100644 include/hw/pci/pci-assign.h

Comments

Anthony PERARD Oct. 25, 2017, 10:52 a.m. UTC | #1
On Fri, Oct 20, 2017 at 10:25:38AM +0200, Paolo Bonzini wrote:
> Legacy PCI device assignment has been removed from Linux in 4.12,
> and had been deprecated 2 years ago there.  We can remove it from
> QEMU as well.
> 
> The ROM loading code was shared with Xen PCI passthrough, so move
> it to hw/xen.
> 
> Cc: Stefano Stabellini <sstabellini@kernel.org>
> Cc: Anthony Perard <anthony.perard@citrix.com>
> Cc: xen-devel@lists.xenproject.org
> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
> ---
>         Xen parts only compile-tested.
> 
>  docs/qdev-device-use.txt                           |   12 +-
>  hw/i386/Makefile.objs                              |    1 -
>  hw/i386/kvm/Makefile.objs                          |    2 +-
>  hw/i386/kvm/pci-assign.c                           | 1883 --------------------
>  hw/xen/Makefile.objs                               |    1 +
>  .../xen_pt_load_rom.c}                             |    4 +-
>  include/hw/pci/pci-assign.h                        |   27 -
>  qdev-monitor.c                                     |    1 -
>  scripts/device-crash-test                          |    2 -
>  9 files changed, 6 insertions(+), 1927 deletions(-)
>  delete mode 100644 hw/i386/kvm/pci-assign.c
>  rename hw/{i386/pci-assign-load-rom.c => xen/xen_pt_load_rom.c} (96%)
>  delete mode 100644 include/hw/pci/pci-assign.h
> 
> diff --git a/hw/i386/pci-assign-load-rom.c b/hw/xen/xen_pt_load_rom.c
> similarity index 96%
> rename from hw/i386/pci-assign-load-rom.c
> rename to hw/xen/xen_pt_load_rom.c
> index 43429b66be..2bc3b6c092 100644
> --- a/hw/i386/pci-assign-load-rom.c
> +++ b/hw/xen/xen_pt_load_rom.c
> @@ -12,7 +12,7 @@
>  #include "qemu/range.h"
>  #include "sysemu/sysemu.h"
>  #include "hw/pci/pci.h"
> -#include "hw/pci/pci-assign.h"
> +#include "xen_pt.h"
>  
>  /*
>   * Scan the assigned devices for the devices that have an option ROM, and then
> @@ -80,7 +80,7 @@ close_rom:
>      fseek(fp, 0, SEEK_SET);
>      val = 0;
>      if (!fwrite(&val, 1, 1, fp)) {
> -        DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
> +        XEN_PT_WARN("%s\n", "Failed to disable pci-sysfs rom file");

XEN_PT_WARN takes an extra argument, it should read:
XEN_PT_WARN(dev, "%s\n", "Failed to disable pci-sysfs rom file");

With that fixed:
Acked-by: Anthony PERARD <anthony.perard@citrix.com>
Philippe Mathieu-Daudé Oct. 25, 2017, 2:05 p.m. UTC | #2
> On Fri, Oct 20, 2017 at 10:25:38AM +0200, Paolo Bonzini wrote:
>> Legacy PCI device assignment has been removed from Linux in 4.12,
>> and had been deprecated 2 years ago there.  We can remove it from
>> QEMU as well.
>>
>> The ROM loading code was shared with Xen PCI passthrough, so move
>> it to hw/xen.
>>
>> Cc: Stefano Stabellini <sstabellini@kernel.org>
>> Cc: Anthony Perard <anthony.perard@citrix.com>
>> Cc: xen-devel@lists.xenproject.org
>> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
>> ---
>>         Xen parts only compile-tested.
>>
>>  docs/qdev-device-use.txt                           |   12 +-
>>  hw/i386/Makefile.objs                              |    1 -
>>  hw/i386/kvm/Makefile.objs                          |    2 +-
>>  hw/i386/kvm/pci-assign.c                           | 1883 --------------------

nice cleanup <3

>>  hw/xen/Makefile.objs                               |    1 +
>>  .../xen_pt_load_rom.c}                             |    4 +-
>>  include/hw/pci/pci-assign.h                        |   27 -
>>  qdev-monitor.c                                     |    1 -
>>  scripts/device-crash-test                          |    2 -
>>  9 files changed, 6 insertions(+), 1927 deletions(-)
>>  delete mode 100644 hw/i386/kvm/pci-assign.c
>>  rename hw/{i386/pci-assign-load-rom.c => xen/xen_pt_load_rom.c} (96%)
>>  delete mode 100644 include/hw/pci/pci-assign.h
>>
>> diff --git a/hw/i386/pci-assign-load-rom.c b/hw/xen/xen_pt_load_rom.c
>> similarity index 96%
>> rename from hw/i386/pci-assign-load-rom.c
>> rename to hw/xen/xen_pt_load_rom.c
>> index 43429b66be..2bc3b6c092 100644
>> --- a/hw/i386/pci-assign-load-rom.c
>> +++ b/hw/xen/xen_pt_load_rom.c
>> @@ -12,7 +12,7 @@
>>  #include "qemu/range.h"
>>  #include "sysemu/sysemu.h"
>>  #include "hw/pci/pci.h"
>> -#include "hw/pci/pci-assign.h"
>> +#include "xen_pt.h"
>>  
>>  /*
>>   * Scan the assigned devices for the devices that have an option ROM, and then
>> @@ -80,7 +80,7 @@ close_rom:
>>      fseek(fp, 0, SEEK_SET);
>>      val = 0;
>>      if (!fwrite(&val, 1, 1, fp)) {
>> -        DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
>> +        XEN_PT_WARN("%s\n", "Failed to disable pci-sysfs rom file");
> 
> XEN_PT_WARN takes an extra argument, it should read:
> XEN_PT_WARN(dev, "%s\n", "Failed to disable pci-sysfs rom file");
> 
> With that fixed:
> Acked-by: Anthony PERARD <anthony.perard@citrix.com>

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
diff mbox

Patch

diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt
index 1f297b5e9c..8f188d1d0b 100644
--- a/docs/qdev-device-use.txt
+++ b/docs/qdev-device-use.txt
@@ -366,17 +366,9 @@  bus=PCI-BUS,addr=DEVFN to control the PCI device address, as usual.
 === Host Device Assignment ===
 
 QEMU supports assigning host PCI devices (qemu-kvm only at this time)
-and host USB devices.
+and host USB devices.  PCI devices can only be assigned with -device:
 
-The old way to assign a host PCI device is
-
-    -pcidevice host=ADDR,dma=none,id=ID
-
-The new way is
-
-    -device pci-assign,host=ADDR,iommu=IOMMU,id=ID
-
-The old dma=none becomes iommu=off with -device.
+    -device vfio-pci,host=ADDR,id=ID
 
 The old way to assign a host USB device is
 
diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs
index 909ead6a77..2e5e1299ad 100644
--- a/hw/i386/Makefile.objs
+++ b/hw/i386/Makefile.objs
@@ -8,4 +8,3 @@  obj-$(CONFIG_XEN) += ../xenpv/ xen/
 
 obj-y += kvmvapic.o
 obj-y += acpi-build.o
-obj-y += pci-assign-load-rom.o
diff --git a/hw/i386/kvm/Makefile.objs b/hw/i386/kvm/Makefile.objs
index d8bce209bf..4224ed900e 100644
--- a/hw/i386/kvm/Makefile.objs
+++ b/hw/i386/kvm/Makefile.objs
@@ -1 +1 @@ 
-obj-y += clock.o apic.o i8259.o ioapic.o i8254.o pci-assign.o
+obj-y += clock.o apic.o i8259.o ioapic.o i8254.o
diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs
index 64a70bc6cb..9ea5c73423 100644
--- a/hw/xen/Makefile.objs
+++ b/hw/xen/Makefile.objs
@@ -3,3 +3,4 @@  common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o xen_pvdev.o xen-common
 
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen-host-pci-device.o
 obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt.o xen_pt_config_init.o xen_pt_graphics.o xen_pt_msi.o
+obj-$(CONFIG_XEN_PCI_PASSTHROUGH) += xen_pt_load_rom.o
diff --git a/hw/i386/pci-assign-load-rom.c b/hw/xen/xen_pt_load_rom.c
similarity index 96%
rename from hw/i386/pci-assign-load-rom.c
rename to hw/xen/xen_pt_load_rom.c
index 43429b66be..2bc3b6c092 100644
--- a/hw/i386/pci-assign-load-rom.c
+++ b/hw/xen/xen_pt_load_rom.c
@@ -12,7 +12,7 @@ 
 #include "qemu/range.h"
 #include "sysemu/sysemu.h"
 #include "hw/pci/pci.h"
-#include "hw/pci/pci-assign.h"
+#include "xen_pt.h"
 
 /*
  * Scan the assigned devices for the devices that have an option ROM, and then
@@ -80,7 +80,7 @@  close_rom:
     fseek(fp, 0, SEEK_SET);
     val = 0;
     if (!fwrite(&val, 1, 1, fp)) {
-        DEBUG("%s\n", "Failed to disable pci-sysfs rom file");
+        XEN_PT_WARN("%s\n", "Failed to disable pci-sysfs rom file");
     }
     fclose(fp);
 
diff --git a/include/hw/pci/pci-assign.h b/include/hw/pci/pci-assign.h
deleted file mode 100644
index 55f42c56fa..0000000000
--- a/include/hw/pci/pci-assign.h
+++ /dev/null
@@ -1,27 +0,0 @@ 
-/*
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- * Just split from hw/i386/kvm/pci-assign.c.
- */
-#ifndef PCI_ASSIGN_H
-#define PCI_ASSIGN_H
-
-#include "hw/pci/pci.h"
-
-//#define DEVICE_ASSIGNMENT_DEBUG
-
-#ifdef DEVICE_ASSIGNMENT_DEBUG
-#define DEBUG(fmt, ...)                                       \
-    do {                                                      \
-        fprintf(stderr, "%s: " fmt, __func__ , __VA_ARGS__);  \
-    } while (0)
-#else
-#define DEBUG(fmt, ...)
-#endif
-
-void *pci_assign_dev_load_option_rom(PCIDevice *dev, struct Object *owner,
-                                     int *size, unsigned int domain,
-                                     unsigned int bus, unsigned int slot,
-                                     unsigned int function);
-#endif /* PCI_ASSIGN_H */
diff --git a/qdev-monitor.c b/qdev-monitor.c
index 8fd6df93d2..b4abb4b5ea 100644
--- a/qdev-monitor.c
+++ b/qdev-monitor.c
@@ -46,7 +46,6 @@  typedef struct QDevAlias
 static const QDevAlias qdev_alias_table[] = {
     { "e1000", "e1000-82540em" },
     { "ich9-ahci", "ahci" },
-    { "kvm-pci-assign", "pci-assign" },
     { "lsi53c895a", "lsi" },
     { "virtio-9p-ccw", "virtio-9p", QEMU_ARCH_S390X },
     { "virtio-9p-pci", "virtio-9p", QEMU_ARCH_ALL & ~QEMU_ARCH_S390X },
diff --git a/scripts/device-crash-test b/scripts/device-crash-test
index 043b24a4aa..c11fd81c52 100755
--- a/scripts/device-crash-test
+++ b/scripts/device-crash-test
@@ -102,7 +102,6 @@  ERROR_WHITELIST = [
     {'device':'ivshmem', 'expected':True},                 # You must specify either 'shm' or 'chardev'
     {'device':'ivshmem-doorbell', 'expected':True},        # You must specify a 'chardev'
     {'device':'ivshmem-plain', 'expected':True},           # You must specify a 'memdev'
-    {'device':'kvm-pci-assign', 'expected':True},          # no host device specified
     {'device':'loader', 'expected':True},                  # please include valid arguments
     {'device':'nand', 'expected':True},                    # Unsupported NAND block size 0x1
     {'device':'nvdimm', 'expected':True},                  # 'memdev' property is not set
@@ -165,7 +164,6 @@  ERROR_WHITELIST = [
 
     # KVM-specific devices shouldn't be tried without accel=kvm:
     {'accel':'(?!kvm).*', 'device':'kvmclock', 'expected':True},
-    {'accel':'(?!kvm).*', 'device':'kvm-pci-assign', 'expected':True},
 
     # xen-specific machines and devices:
     {'accel':'(?!xen).*', 'machine':'xen.*', 'expected':True},
diff --git a/hw/i386/kvm/pci-assign.c b/hw/i386/kvm/pci-assign.c
deleted file mode 100644
index 33e20cb3e8..0000000000
--- a/hw/i386/kvm/pci-assign.c
+++ /dev/null
@@ -1,1883 +0,0 @@ 
-/*
- * Copyright (c) 2007, Neocleus Corporation.
- *
- * This work is licensed under the terms of the GNU GPL, version 2.  See
- * the COPYING file in the top-level directory.
- *
- *
- *  Assign a PCI device from the host to a guest VM.
- *
- *  This implementation uses the classic device assignment interface of KVM
- *  and is only available on x86 hosts. It is expected to be obsoleted by VFIO
- *  based device assignment.
- *
- *  Adapted for KVM (qemu-kvm) by Qumranet. QEMU version was based on qemu-kvm
- *  revision 4144fe9d48. See its repository for the history.
- *
- *  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 <linux/kvm.h>
-#include "qapi/error.h"
-#include "hw/hw.h"
-#include "hw/i386/pc.h"
-#include "qemu/error-report.h"
-#include "ui/console.h"
-#include "hw/loader.h"
-#include "monitor/monitor.h"
-#include "qemu/range.h"
-#include "sysemu/sysemu.h"
-#include "hw/pci/pci.h"
-#include "hw/pci/msi.h"
-#include "kvm_i386.h"
-#include "hw/pci/pci-assign.h"
-
-/* From linux/ioport.h */
-#define IORESOURCE_IO       0x00000100  /* Resource type */
-#define IORESOURCE_MEM      0x00000200
-#define IORESOURCE_IRQ      0x00000400
-#define IORESOURCE_DMA      0x00000800
-#define IORESOURCE_PREFETCH 0x00002000  /* No side effects */
-#define IORESOURCE_MEM_64   0x00100000
-
-typedef struct PCIRegion {
-    int type;           /* Memory or port I/O */
-    int valid;
-    uint64_t base_addr;
-    uint64_t size;    /* size of the region */
-    int resource_fd;
-} PCIRegion;
-
-typedef struct PCIDevRegions {
-    uint8_t bus, dev, func; /* Bus inside domain, device and function */
-    int irq;                /* IRQ number */
-    uint16_t region_number; /* number of active regions */
-
-    /* Port I/O or MMIO Regions */
-    PCIRegion regions[PCI_NUM_REGIONS - 1];
-    int config_fd;
-} PCIDevRegions;
-
-typedef struct AssignedDevRegion {
-    MemoryRegion container;
-    MemoryRegion real_iomem;
-    union {
-        uint8_t *r_virtbase; /* mmapped access address for memory regions */
-        uint32_t r_baseport; /* the base guest port for I/O regions */
-    } u;
-    pcibus_t e_size;    /* emulated size of region in bytes */
-    pcibus_t r_size;    /* real size of region in bytes */
-    PCIRegion *region;
-} AssignedDevRegion;
-
-#define ASSIGNED_DEVICE_PREFER_MSI_BIT  0
-#define ASSIGNED_DEVICE_SHARE_INTX_BIT  1
-
-#define ASSIGNED_DEVICE_PREFER_MSI_MASK (1 << ASSIGNED_DEVICE_PREFER_MSI_BIT)
-#define ASSIGNED_DEVICE_SHARE_INTX_MASK (1 << ASSIGNED_DEVICE_SHARE_INTX_BIT)
-
-typedef struct MSIXTableEntry {
-    uint32_t addr_lo;
-    uint32_t addr_hi;
-    uint32_t data;
-    uint32_t ctrl;
-} MSIXTableEntry;
-
-typedef enum AssignedIRQType {
-    ASSIGNED_IRQ_NONE = 0,
-    ASSIGNED_IRQ_INTX_HOST_INTX,
-    ASSIGNED_IRQ_INTX_HOST_MSI,
-    ASSIGNED_IRQ_MSI,
-    ASSIGNED_IRQ_MSIX
-} AssignedIRQType;
-
-typedef struct AssignedDevice {
-    PCIDevice dev;
-    PCIHostDeviceAddress host;
-    uint32_t dev_id;
-    uint32_t features;
-    int intpin;
-    AssignedDevRegion v_addrs[PCI_NUM_REGIONS - 1];
-    PCIDevRegions real_device;
-    PCIINTxRoute intx_route;
-    AssignedIRQType assigned_irq_type;
-    struct {
-#define ASSIGNED_DEVICE_CAP_MSI (1 << 0)
-#define ASSIGNED_DEVICE_CAP_MSIX (1 << 1)
-        uint32_t available;
-#define ASSIGNED_DEVICE_MSI_ENABLED (1 << 0)
-#define ASSIGNED_DEVICE_MSIX_ENABLED (1 << 1)
-#define ASSIGNED_DEVICE_MSIX_MASKED (1 << 2)
-        uint32_t state;
-    } cap;
-    uint8_t emulate_config_read[PCI_CONFIG_SPACE_SIZE];
-    uint8_t emulate_config_write[PCI_CONFIG_SPACE_SIZE];
-    int msi_virq_nr;
-    int *msi_virq;
-    MSIXTableEntry *msix_table;
-    hwaddr msix_table_addr;
-    uint16_t msix_table_size;
-    uint16_t msix_max;
-    MemoryRegion mmio;
-    char *configfd_name;
-    int32_t bootindex;
-} AssignedDevice;
-
-#define TYPE_PCI_ASSIGN "kvm-pci-assign"
-#define PCI_ASSIGN(obj) OBJECT_CHECK(AssignedDevice, (obj), TYPE_PCI_ASSIGN)
-
-static void assigned_dev_update_irq_routing(PCIDevice *dev);
-
-static void assigned_dev_load_option_rom(AssignedDevice *dev);
-
-static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev);
-
-static uint64_t assigned_dev_ioport_rw(AssignedDevRegion *dev_region,
-                                       hwaddr addr, int size,
-                                       uint64_t *data)
-{
-    uint64_t val = 0;
-    int fd = dev_region->region->resource_fd;
-
-    if (data) {
-        DEBUG("pwrite data=%" PRIx64 ", size=%d, e_phys=" TARGET_FMT_plx
-              ", addr="TARGET_FMT_plx"\n", *data, size, addr, addr);
-        if (pwrite(fd, data, size, addr) != size) {
-            error_report("%s - pwrite failed %s", __func__, strerror(errno));
-        }
-    } else {
-        if (pread(fd, &val, size, addr) != size) {
-            error_report("%s - pread failed %s", __func__, strerror(errno));
-            val = (1UL << (size * 8)) - 1;
-        }
-        DEBUG("pread val=%" PRIx64 ", size=%d, e_phys=" TARGET_FMT_plx
-              ", addr=" TARGET_FMT_plx "\n", val, size, addr, addr);
-    }
-    return val;
-}
-
-static void assigned_dev_ioport_write(void *opaque, hwaddr addr,
-                                      uint64_t data, unsigned size)
-{
-    assigned_dev_ioport_rw(opaque, addr, size, &data);
-}
-
-static uint64_t assigned_dev_ioport_read(void *opaque,
-                                         hwaddr addr, unsigned size)
-{
-    return assigned_dev_ioport_rw(opaque, addr, size, NULL);
-}
-
-static uint32_t slow_bar_readb(void *opaque, hwaddr addr)
-{
-    AssignedDevRegion *d = opaque;
-    uint8_t *in = d->u.r_virtbase + addr;
-    uint32_t r;
-
-    r = *in;
-    DEBUG("addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r);
-
-    return r;
-}
-
-static uint32_t slow_bar_readw(void *opaque, hwaddr addr)
-{
-    AssignedDevRegion *d = opaque;
-    uint16_t *in = (uint16_t *)(d->u.r_virtbase + addr);
-    uint32_t r;
-
-    r = *in;
-    DEBUG("addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r);
-
-    return r;
-}
-
-static uint32_t slow_bar_readl(void *opaque, hwaddr addr)
-{
-    AssignedDevRegion *d = opaque;
-    uint32_t *in = (uint32_t *)(d->u.r_virtbase + addr);
-    uint32_t r;
-
-    r = *in;
-    DEBUG("addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, r);
-
-    return r;
-}
-
-static void slow_bar_writeb(void *opaque, hwaddr addr, uint32_t val)
-{
-    AssignedDevRegion *d = opaque;
-    uint8_t *out = d->u.r_virtbase + addr;
-
-    DEBUG("addr=0x" TARGET_FMT_plx " val=0x%02x\n", addr, val);
-    *out = val;
-}
-
-static void slow_bar_writew(void *opaque, hwaddr addr, uint32_t val)
-{
-    AssignedDevRegion *d = opaque;
-    uint16_t *out = (uint16_t *)(d->u.r_virtbase + addr);
-
-    DEBUG("addr=0x" TARGET_FMT_plx " val=0x%04x\n", addr, val);
-    *out = val;
-}
-
-static void slow_bar_writel(void *opaque, hwaddr addr, uint32_t val)
-{
-    AssignedDevRegion *d = opaque;
-    uint32_t *out = (uint32_t *)(d->u.r_virtbase + addr);
-
-    DEBUG("addr=0x" TARGET_FMT_plx " val=0x%08x\n", addr, val);
-    *out = val;
-}
-
-static const MemoryRegionOps slow_bar_ops = {
-    .old_mmio = {
-        .read = { slow_bar_readb, slow_bar_readw, slow_bar_readl, },
-        .write = { slow_bar_writeb, slow_bar_writew, slow_bar_writel, },
-    },
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void assigned_dev_iomem_setup(PCIDevice *pci_dev, int region_num,
-                                     pcibus_t e_size)
-{
-    AssignedDevice *r_dev = PCI_ASSIGN(pci_dev);
-    AssignedDevRegion *region = &r_dev->v_addrs[region_num];
-    PCIRegion *real_region = &r_dev->real_device.regions[region_num];
-
-    if (e_size > 0) {
-        memory_region_init(&region->container, OBJECT(pci_dev),
-                           "assigned-dev-container", e_size);
-        memory_region_add_subregion(&region->container, 0, &region->real_iomem);
-
-        /* deal with MSI-X MMIO page */
-        if (real_region->base_addr <= r_dev->msix_table_addr &&
-                real_region->base_addr + real_region->size >
-                r_dev->msix_table_addr) {
-            uint64_t offset = r_dev->msix_table_addr - real_region->base_addr;
-
-            memory_region_add_subregion_overlap(&region->container,
-                                                offset,
-                                                &r_dev->mmio,
-                                                1);
-        }
-    }
-}
-
-static const MemoryRegionOps assigned_dev_ioport_ops = {
-    .read = assigned_dev_ioport_read,
-    .write = assigned_dev_ioport_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-};
-
-static void assigned_dev_ioport_setup(PCIDevice *pci_dev, int region_num,
-                                      pcibus_t size)
-{
-    AssignedDevice *r_dev = PCI_ASSIGN(pci_dev);
-    AssignedDevRegion *region = &r_dev->v_addrs[region_num];
-
-    region->e_size = size;
-    memory_region_init(&region->container, OBJECT(pci_dev),
-                       "assigned-dev-container", size);
-    memory_region_init_io(&region->real_iomem, OBJECT(pci_dev),
-                          &assigned_dev_ioport_ops, r_dev->v_addrs + region_num,
-                          "assigned-dev-iomem", size);
-    memory_region_add_subregion(&region->container, 0, &region->real_iomem);
-}
-
-static uint32_t assigned_dev_pci_read(PCIDevice *d, int pos, int len)
-{
-    AssignedDevice *pci_dev = PCI_ASSIGN(d);
-    uint32_t val;
-    ssize_t ret;
-    int fd = pci_dev->real_device.config_fd;
-
-again:
-    ret = pread(fd, &val, len, pos);
-    if (ret != len) {
-        if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) {
-            goto again;
-        }
-
-        hw_error("pci read failed, ret = %zd errno = %d\n", ret, errno);
-    }
-
-    return val;
-}
-
-static uint8_t assigned_dev_pci_read_byte(PCIDevice *d, int pos)
-{
-    return (uint8_t)assigned_dev_pci_read(d, pos, 1);
-}
-
-static void assigned_dev_pci_write(PCIDevice *d, int pos, uint32_t val, int len)
-{
-    AssignedDevice *pci_dev = PCI_ASSIGN(d);
-    ssize_t ret;
-    int fd = pci_dev->real_device.config_fd;
-
-again:
-    ret = pwrite(fd, &val, len, pos);
-    if (ret != len) {
-        if ((ret < 0) && (errno == EINTR || errno == EAGAIN)) {
-            goto again;
-        }
-
-        hw_error("pci write failed, ret = %zd errno = %d\n", ret, errno);
-    }
-}
-
-static void assigned_dev_emulate_config_read(AssignedDevice *dev,
-                                             uint32_t offset, uint32_t len)
-{
-    memset(dev->emulate_config_read + offset, 0xff, len);
-}
-
-static void assigned_dev_direct_config_read(AssignedDevice *dev,
-                                            uint32_t offset, uint32_t len)
-{
-    memset(dev->emulate_config_read + offset, 0, len);
-}
-
-static void assigned_dev_direct_config_write(AssignedDevice *dev,
-                                             uint32_t offset, uint32_t len)
-{
-    memset(dev->emulate_config_write + offset, 0, len);
-}
-
-static uint8_t pci_find_cap_offset(PCIDevice *d, uint8_t cap, uint8_t start)
-{
-    int id;
-    int max_cap = 48;
-    int pos = start ? start : PCI_CAPABILITY_LIST;
-    int status;
-
-    status = assigned_dev_pci_read_byte(d, PCI_STATUS);
-    if ((status & PCI_STATUS_CAP_LIST) == 0) {
-        return 0;
-    }
-
-    while (max_cap--) {
-        pos = assigned_dev_pci_read_byte(d, pos);
-        if (pos < 0x40) {
-            break;
-        }
-
-        pos &= ~3;
-        id = assigned_dev_pci_read_byte(d, pos + PCI_CAP_LIST_ID);
-
-        if (id == 0xff) {
-            break;
-        }
-        if (id == cap) {
-            return pos;
-        }
-
-        pos += PCI_CAP_LIST_NEXT;
-    }
-    return 0;
-}
-
-static void assigned_dev_register_regions(PCIRegion *io_regions,
-                                          unsigned long regions_num,
-                                          AssignedDevice *pci_dev,
-                                          Error **errp)
-{
-    uint32_t i;
-    PCIRegion *cur_region = io_regions;
-
-    for (i = 0; i < regions_num; i++, cur_region++) {
-        if (!cur_region->valid) {
-            continue;
-        }
-
-        /* handle memory io regions */
-        if (cur_region->type & IORESOURCE_MEM) {
-            int t = PCI_BASE_ADDRESS_SPACE_MEMORY;
-            if (cur_region->type & IORESOURCE_PREFETCH) {
-                t |= PCI_BASE_ADDRESS_MEM_PREFETCH;
-            }
-            if (cur_region->type & IORESOURCE_MEM_64) {
-                t |= PCI_BASE_ADDRESS_MEM_TYPE_64;
-            }
-
-            /* map physical memory */
-            pci_dev->v_addrs[i].u.r_virtbase = mmap(NULL, cur_region->size,
-                                                    PROT_WRITE | PROT_READ,
-                                                    MAP_SHARED,
-                                                    cur_region->resource_fd,
-                                                    (off_t)0);
-
-            if (pci_dev->v_addrs[i].u.r_virtbase == MAP_FAILED) {
-                pci_dev->v_addrs[i].u.r_virtbase = NULL;
-                error_setg_errno(errp, errno, "Couldn't mmap 0x%" PRIx64 "!",
-                                 cur_region->base_addr);
-                return;
-            }
-
-            pci_dev->v_addrs[i].r_size = cur_region->size;
-            pci_dev->v_addrs[i].e_size = 0;
-
-            /* add offset */
-            pci_dev->v_addrs[i].u.r_virtbase +=
-                (cur_region->base_addr & 0xFFF);
-
-            if (cur_region->size & 0xFFF) {
-                error_report("PCI region %d at address 0x%" PRIx64 " has "
-                             "size 0x%" PRIx64 ", which is not a multiple of "
-                             "4K.  You might experience some performance hit "
-                             "due to that.",
-                             i, cur_region->base_addr, cur_region->size);
-                memory_region_init_io(&pci_dev->v_addrs[i].real_iomem,
-                                      OBJECT(pci_dev), &slow_bar_ops,
-                                      &pci_dev->v_addrs[i],
-                                      "assigned-dev-slow-bar",
-                                      cur_region->size);
-            } else {
-                void *virtbase = pci_dev->v_addrs[i].u.r_virtbase;
-                char name[32];
-                snprintf(name, sizeof(name), "%s.bar%d",
-                         object_get_typename(OBJECT(pci_dev)), i);
-                memory_region_init_ram_ptr(&pci_dev->v_addrs[i].real_iomem,
-                                           OBJECT(pci_dev), name,
-                                           cur_region->size, virtbase);
-                vmstate_register_ram(&pci_dev->v_addrs[i].real_iomem,
-                                     &pci_dev->dev.qdev);
-            }
-
-            assigned_dev_iomem_setup(&pci_dev->dev, i, cur_region->size);
-            pci_register_bar((PCIDevice *) pci_dev, i, t,
-                             &pci_dev->v_addrs[i].container);
-            continue;
-        } else {
-            /* handle port io regions */
-            uint32_t val;
-            int ret;
-
-            /* Test kernel support for ioport resource read/write.  Old
-             * kernels return EIO.  New kernels only allow 1/2/4 byte reads
-             * so should return EINVAL for a 3 byte read */
-            ret = pread(pci_dev->v_addrs[i].region->resource_fd, &val, 3, 0);
-            if (ret >= 0) {
-                error_report("Unexpected return from I/O port read: %d", ret);
-                abort();
-            } else if (errno != EINVAL) {
-                error_report("Kernel doesn't support ioport resource "
-                             "access, hiding this region.");
-                close(pci_dev->v_addrs[i].region->resource_fd);
-                cur_region->valid = 0;
-                continue;
-            }
-
-            pci_dev->v_addrs[i].u.r_baseport = cur_region->base_addr;
-            pci_dev->v_addrs[i].r_size = cur_region->size;
-            pci_dev->v_addrs[i].e_size = 0;
-
-            assigned_dev_ioport_setup(&pci_dev->dev, i, cur_region->size);
-            pci_register_bar((PCIDevice *) pci_dev, i,
-                             PCI_BASE_ADDRESS_SPACE_IO,
-                             &pci_dev->v_addrs[i].container);
-        }
-    }
-
-    /* success */
-}
-
-static void get_real_id(const char *devpath, const char *idname, uint16_t *val,
-                        Error **errp)
-{
-    FILE *f;
-    char name[128];
-    long id;
-
-    snprintf(name, sizeof(name), "%s%s", devpath, idname);
-    f = fopen(name, "r");
-    if (f == NULL) {
-        error_setg_file_open(errp, errno, name);
-        return;
-    }
-    if (fscanf(f, "%li\n", &id) == 1) {
-        *val = id;
-    } else {
-        error_setg(errp, "Failed to parse contents of '%s'", name);
-    }
-    fclose(f);
-}
-
-static void get_real_vendor_id(const char *devpath, uint16_t *val,
-                               Error **errp)
-{
-    get_real_id(devpath, "vendor", val, errp);
-}
-
-static void get_real_device_id(const char *devpath, uint16_t *val,
-                               Error **errp)
-{
-    get_real_id(devpath, "device", val, errp);
-}
-
-static void get_real_device(AssignedDevice *pci_dev, Error **errp)
-{
-    char dir[128], name[128];
-    int fd, r = 0;
-    FILE *f;
-    uint64_t start, end, size, flags;
-    uint16_t id;
-    PCIRegion *rp;
-    PCIDevRegions *dev = &pci_dev->real_device;
-    Error *local_err = NULL;
-
-    dev->region_number = 0;
-
-    snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/%04x:%02x:%02x.%x/",
-             pci_dev->host.domain, pci_dev->host.bus,
-             pci_dev->host.slot, pci_dev->host.function);
-
-    snprintf(name, sizeof(name), "%sconfig", dir);
-
-    if (pci_dev->configfd_name && *pci_dev->configfd_name) {
-        dev->config_fd = monitor_fd_param(cur_mon, pci_dev->configfd_name,
-                                          &local_err);
-        if (local_err) {
-            error_propagate(errp, local_err);
-            return;
-        }
-    } else {
-        dev->config_fd = open(name, O_RDWR);
-
-        if (dev->config_fd == -1) {
-            error_setg_file_open(errp, errno, name);
-            return;
-        }
-    }
-again:
-    r = read(dev->config_fd, pci_dev->dev.config,
-             pci_config_size(&pci_dev->dev));
-    if (r < 0) {
-        if (errno == EINTR || errno == EAGAIN) {
-            goto again;
-        }
-        error_setg_errno(errp, errno, "read(\"%s\")",
-                         (pci_dev->configfd_name && *pci_dev->configfd_name) ?
-                         pci_dev->configfd_name : name);
-        return;
-    }
-
-    /* Restore or clear multifunction, this is always controlled by qemu */
-    if (pci_dev->dev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
-        pci_dev->dev.config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION;
-    } else {
-        pci_dev->dev.config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION;
-    }
-
-    /* Clear host resource mapping info.  If we choose not to register a
-     * BAR, such as might be the case with the option ROM, we can get
-     * confusing, unwritable, residual addresses from the host here. */
-    memset(&pci_dev->dev.config[PCI_BASE_ADDRESS_0], 0, 24);
-    memset(&pci_dev->dev.config[PCI_ROM_ADDRESS], 0, 4);
-
-    snprintf(name, sizeof(name), "%sresource", dir);
-
-    f = fopen(name, "r");
-    if (f == NULL) {
-        error_setg_file_open(errp, errno, name);
-        return;
-    }
-
-    for (r = 0; r < PCI_ROM_SLOT; r++) {
-        if (fscanf(f, "%" SCNi64 " %" SCNi64 " %" SCNi64 "\n",
-                   &start, &end, &flags) != 3) {
-            break;
-        }
-
-        rp = dev->regions + r;
-        rp->valid = 0;
-        rp->resource_fd = -1;
-        size = end - start + 1;
-        flags &= IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH
-                 | IORESOURCE_MEM_64;
-        if (size == 0 || (flags & ~IORESOURCE_PREFETCH) == 0) {
-            continue;
-        }
-        if (flags & IORESOURCE_MEM) {
-            flags &= ~IORESOURCE_IO;
-        } else {
-            flags &= ~IORESOURCE_PREFETCH;
-        }
-        snprintf(name, sizeof(name), "%sresource%d", dir, r);
-        fd = open(name, O_RDWR);
-        if (fd == -1) {
-            continue;
-        }
-        rp->resource_fd = fd;
-
-        rp->type = flags;
-        rp->valid = 1;
-        rp->base_addr = start;
-        rp->size = size;
-        pci_dev->v_addrs[r].region = rp;
-        DEBUG("region %d size %" PRIu64 " start 0x%" PRIx64
-              " type %d resource_fd %d\n",
-              r, rp->size, start, rp->type, rp->resource_fd);
-    }
-
-    fclose(f);
-
-    /* read and fill vendor ID */
-    get_real_vendor_id(dir, &id, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-    pci_dev->dev.config[0] = id & 0xff;
-    pci_dev->dev.config[1] = (id & 0xff00) >> 8;
-
-    /* read and fill device ID */
-    get_real_device_id(dir, &id, &local_err);
-    if (local_err) {
-        error_propagate(errp, local_err);
-        return;
-    }
-    pci_dev->dev.config[2] = id & 0xff;
-    pci_dev->dev.config[3] = (id & 0xff00) >> 8;
-
-    pci_word_test_and_clear_mask(pci_dev->emulate_config_write + PCI_COMMAND,
-                                 PCI_COMMAND_MASTER | PCI_COMMAND_INTX_DISABLE);
-
-    dev->region_number = r;
-}
-
-static void free_msi_virqs(AssignedDevice *dev)
-{
-    int i;
-
-    for (i = 0; i < dev->msi_virq_nr; i++) {
-        if (dev->msi_virq[i] >= 0) {
-            kvm_irqchip_release_virq(kvm_state, dev->msi_virq[i]);
-            dev->msi_virq[i] = -1;
-        }
-    }
-    g_free(dev->msi_virq);
-    dev->msi_virq = NULL;
-    dev->msi_virq_nr = 0;
-}
-
-static void free_assigned_device(AssignedDevice *dev)
-{
-    int i;
-
-    if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) {
-        assigned_dev_unregister_msix_mmio(dev);
-    }
-    for (i = 0; i < dev->real_device.region_number; i++) {
-        PCIRegion *pci_region = &dev->real_device.regions[i];
-        AssignedDevRegion *region = &dev->v_addrs[i];
-
-        if (!pci_region->valid) {
-            continue;
-        }
-        if (pci_region->type & IORESOURCE_IO) {
-            if (region->u.r_baseport) {
-                memory_region_del_subregion(&region->container,
-                                            &region->real_iomem);
-            }
-        } else if (pci_region->type & IORESOURCE_MEM) {
-            if (region->u.r_virtbase) {
-                memory_region_del_subregion(&region->container,
-                                            &region->real_iomem);
-
-                /* Remove MSI-X table subregion */
-                if (pci_region->base_addr <= dev->msix_table_addr &&
-                    pci_region->base_addr + pci_region->size >
-                    dev->msix_table_addr) {
-                    memory_region_del_subregion(&region->container,
-                                                &dev->mmio);
-                }
-                if (munmap(region->u.r_virtbase,
-                           (pci_region->size + 0xFFF) & 0xFFFFF000)) {
-                    error_report("Failed to unmap assigned device region: %s",
-                                 strerror(errno));
-                }
-            }
-        }
-        if (pci_region->resource_fd >= 0) {
-            close(pci_region->resource_fd);
-        }
-    }
-
-    if (dev->real_device.config_fd >= 0) {
-        close(dev->real_device.config_fd);
-    }
-
-    free_msi_virqs(dev);
-}
-
-/* This function tries to determine the cause of the PCI assignment failure. It
- * always returns the cause as a dynamically allocated, human readable string.
- * If the function fails to determine the cause for any internal reason, then
- * the returned string will state that fact.
- */
-static char *assign_failed_examine(const AssignedDevice *dev)
-{
-    char name[PATH_MAX], dir[PATH_MAX], driver[PATH_MAX] = {}, *ns;
-    uint16_t vendor_id, device_id;
-    int r;
-    Error *local_err = NULL;
-
-    snprintf(dir, sizeof(dir), "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/",
-            dev->host.domain, dev->host.bus, dev->host.slot,
-            dev->host.function);
-
-    snprintf(name, sizeof(name), "%sdriver", dir);
-
-    r = readlink(name, driver, sizeof(driver));
-    if ((r <= 0) || r >= sizeof(driver)) {
-        goto fail;
-    }
-
-    driver[r] = 0;
-    ns = strrchr(driver, '/');
-    if (!ns) {
-        goto fail;
-    }
-
-    ns++;
-
-    if ((get_real_vendor_id(dir, &vendor_id, &local_err), local_err) ||
-        (get_real_device_id(dir, &device_id, &local_err), local_err)) {
-        /* We're already analyzing an assignment error, so we suppress this
-         * one just like the others above.
-         */
-        error_free(local_err);
-        goto fail;
-    }
-
-    return g_strdup_printf(
-        "*** The driver '%s' is occupying your device %04x:%02x:%02x.%x.\n"
-        "***\n"
-        "*** You can try the following commands to free it:\n"
-        "***\n"
-        "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/new_id\n"
-        "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/%s/unbind\n"
-        "*** $ echo \"%04x:%02x:%02x.%x\" > /sys/bus/pci/drivers/"
-        "pci-stub/bind\n"
-        "*** $ echo \"%04x %04x\" > /sys/bus/pci/drivers/pci-stub/remove_id\n"
-        "***\n",
-        ns, dev->host.domain, dev->host.bus, dev->host.slot,
-        dev->host.function, vendor_id, device_id,
-        dev->host.domain, dev->host.bus, dev->host.slot, dev->host.function,
-        ns, dev->host.domain, dev->host.bus, dev->host.slot,
-        dev->host.function, vendor_id, device_id);
-
-fail:
-    return g_strdup("Couldn't find out why.\n");
-}
-
-static void assign_device(AssignedDevice *dev, Error **errp)
-{
-    uint32_t flags = KVM_DEV_ASSIGN_ENABLE_IOMMU;
-    int r;
-
-    /* Only pass non-zero PCI segment to capable module */
-    if (!kvm_check_extension(kvm_state, KVM_CAP_PCI_SEGMENT) &&
-        dev->host.domain) {
-        error_setg(errp, "Can't assign device inside non-zero PCI segment "
-                   "as this KVM module doesn't support it.");
-        return;
-    }
-
-    if (!kvm_check_extension(kvm_state, KVM_CAP_IOMMU)) {
-        error_setg(errp, "No IOMMU found.  Unable to assign device \"%s\"",
-                   dev->dev.qdev.id);
-        return;
-    }
-
-    if (dev->features & ASSIGNED_DEVICE_SHARE_INTX_MASK &&
-        kvm_has_intx_set_mask()) {
-        flags |= KVM_DEV_ASSIGN_PCI_2_3;
-    }
-
-    r = kvm_device_pci_assign(kvm_state, &dev->host, flags, &dev->dev_id);
-    if (r < 0) {
-        switch (r) {
-        case -EBUSY: {
-            char *cause;
-
-            cause = assign_failed_examine(dev);
-            error_setg_errno(errp, -r, "Failed to assign device \"%s\"",
-                             dev->dev.qdev.id);
-            error_append_hint(errp, "%s", cause);
-            g_free(cause);
-            break;
-        }
-        default:
-            error_setg_errno(errp, -r, "Failed to assign device \"%s\"",
-                             dev->dev.qdev.id);
-            break;
-        }
-    }
-}
-
-static int verify_irqchip_in_kernel(Error **errp)
-{
-    if (kvm_irqchip_in_kernel()) {
-        return -1;
-    }
-    error_setg(errp, "pci-assign requires KVM with in-kernel irqchip enabled");
-    return 0;
-}
-
-static int assign_intx(AssignedDevice *dev, Error **errp)
-{
-    AssignedIRQType new_type;
-    PCIINTxRoute intx_route;
-    bool intx_host_msi;
-    int r;
-
-    /* Interrupt PIN 0 means don't use INTx */
-    if (assigned_dev_pci_read_byte(&dev->dev, PCI_INTERRUPT_PIN) == 0) {
-        pci_device_set_intx_routing_notifier(&dev->dev, NULL);
-        return 0;
-    }
-
-    if (verify_irqchip_in_kernel(errp) < 0) {
-        return -ENOTSUP;
-    }
-
-    pci_device_set_intx_routing_notifier(&dev->dev,
-                                         assigned_dev_update_irq_routing);
-
-    intx_route = pci_device_route_intx_to_irq(&dev->dev, dev->intpin);
-    assert(intx_route.mode != PCI_INTX_INVERTED);
-
-    if (!pci_intx_route_changed(&dev->intx_route, &intx_route)) {
-        return 0;
-    }
-
-    switch (dev->assigned_irq_type) {
-    case ASSIGNED_IRQ_INTX_HOST_INTX:
-    case ASSIGNED_IRQ_INTX_HOST_MSI:
-        intx_host_msi = dev->assigned_irq_type == ASSIGNED_IRQ_INTX_HOST_MSI;
-        r = kvm_device_intx_deassign(kvm_state, dev->dev_id, intx_host_msi);
-        break;
-    case ASSIGNED_IRQ_MSI:
-        r = kvm_device_msi_deassign(kvm_state, dev->dev_id);
-        break;
-    case ASSIGNED_IRQ_MSIX:
-        r = kvm_device_msix_deassign(kvm_state, dev->dev_id);
-        break;
-    default:
-        r = 0;
-        break;
-    }
-    if (r) {
-        perror("assign_intx: deassignment of previous interrupt failed");
-    }
-    dev->assigned_irq_type = ASSIGNED_IRQ_NONE;
-
-    if (intx_route.mode == PCI_INTX_DISABLED) {
-        dev->intx_route = intx_route;
-        return 0;
-    }
-
-retry:
-    if (dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK &&
-        dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) {
-        intx_host_msi = true;
-        new_type = ASSIGNED_IRQ_INTX_HOST_MSI;
-    } else {
-        intx_host_msi = false;
-        new_type = ASSIGNED_IRQ_INTX_HOST_INTX;
-    }
-
-    r = kvm_device_intx_assign(kvm_state, dev->dev_id, intx_host_msi,
-                               intx_route.irq);
-    if (r < 0) {
-        if (r == -EIO && !(dev->features & ASSIGNED_DEVICE_PREFER_MSI_MASK) &&
-            dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) {
-            /* Retry with host-side MSI. There might be an IRQ conflict and
-             * either the kernel or the device doesn't support sharing. */
-            error_report("Host-side INTx sharing not supported, "
-                         "using MSI instead");
-            error_printf("Some devices do not work properly in this mode.\n");
-            dev->features |= ASSIGNED_DEVICE_PREFER_MSI_MASK;
-            goto retry;
-        }
-        error_setg_errno(errp, -r, "Failed to assign irq for \"%s\"",
-                         dev->dev.qdev.id);
-        error_append_hint(errp, "Perhaps you are assigning a device "
-                          "that shares an IRQ with another device?\n");
-        return r;
-    }
-
-    dev->intx_route = intx_route;
-    dev->assigned_irq_type = new_type;
-    return r;
-}
-
-static void deassign_device(AssignedDevice *dev)
-{
-    int r;
-
-    r = kvm_device_pci_deassign(kvm_state, dev->dev_id);
-    assert(r == 0);
-}
-
-/* The pci config space got updated. Check if irq numbers have changed
- * for our devices
- */
-static void assigned_dev_update_irq_routing(PCIDevice *dev)
-{
-    AssignedDevice *assigned_dev = PCI_ASSIGN(dev);
-    Error *err = NULL;
-    int r;
-
-    r = assign_intx(assigned_dev, &err);
-    if (r < 0) {
-        error_report_err(err);
-        err = NULL;
-        qdev_unplug(&dev->qdev, &err);
-        assert(!err);
-    }
-}
-
-static void assigned_dev_update_msi(PCIDevice *pci_dev)
-{
-    AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
-    uint8_t ctrl_byte = pci_get_byte(pci_dev->config + pci_dev->msi_cap +
-                                     PCI_MSI_FLAGS);
-    int r;
-
-    /* Some guests gratuitously disable MSI even if they're not using it,
-     * try to catch this by only deassigning irqs if the guest is using
-     * MSI or intends to start. */
-    if (assigned_dev->assigned_irq_type == ASSIGNED_IRQ_MSI ||
-        (ctrl_byte & PCI_MSI_FLAGS_ENABLE)) {
-        r = kvm_device_msi_deassign(kvm_state, assigned_dev->dev_id);
-        /* -ENXIO means no assigned irq */
-        if (r && r != -ENXIO) {
-            perror("assigned_dev_update_msi: deassign irq");
-        }
-
-        free_msi_virqs(assigned_dev);
-
-        assigned_dev->assigned_irq_type = ASSIGNED_IRQ_NONE;
-        pci_device_set_intx_routing_notifier(pci_dev, NULL);
-    }
-
-    if (ctrl_byte & PCI_MSI_FLAGS_ENABLE) {
-        int virq;
-
-        virq = kvm_irqchip_add_msi_route(kvm_state, 0, pci_dev);
-        if (virq < 0) {
-            perror("assigned_dev_update_msi: kvm_irqchip_add_msi_route");
-            return;
-        }
-
-        assigned_dev->msi_virq = g_malloc(sizeof(*assigned_dev->msi_virq));
-        assigned_dev->msi_virq_nr = 1;
-        assigned_dev->msi_virq[0] = virq;
-        if (kvm_device_msi_assign(kvm_state, assigned_dev->dev_id, virq) < 0) {
-            perror("assigned_dev_update_msi: kvm_device_msi_assign");
-        }
-
-        assigned_dev->intx_route.mode = PCI_INTX_DISABLED;
-        assigned_dev->intx_route.irq = -1;
-        assigned_dev->assigned_irq_type = ASSIGNED_IRQ_MSI;
-    } else {
-        Error *local_err = NULL;
-
-        assign_intx(assigned_dev, &local_err);
-        if (local_err) {
-            error_report_err(local_err);
-        }
-    }
-}
-
-static void assigned_dev_update_msi_msg(PCIDevice *pci_dev)
-{
-    AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
-    uint8_t ctrl_byte = pci_get_byte(pci_dev->config + pci_dev->msi_cap +
-                                     PCI_MSI_FLAGS);
-
-    if (assigned_dev->assigned_irq_type != ASSIGNED_IRQ_MSI ||
-        !(ctrl_byte & PCI_MSI_FLAGS_ENABLE)) {
-        return;
-    }
-
-    kvm_irqchip_update_msi_route(kvm_state, assigned_dev->msi_virq[0],
-                                 msi_get_message(pci_dev, 0), pci_dev);
-    kvm_irqchip_commit_routes(kvm_state);
-}
-
-static bool assigned_dev_msix_masked(MSIXTableEntry *entry)
-{
-    return (entry->ctrl & cpu_to_le32(0x1)) != 0;
-}
-
-/*
- * When MSI-X is first enabled the vector table typically has all the
- * vectors masked, so we can't use that as the obvious test to figure out
- * how many vectors to initially enable.  Instead we look at the data field
- * because this is what worked for pci-assign for a long time.  This makes
- * sure the physical MSI-X state tracks the guest's view, which is important
- * for some VF/PF and PF/fw communication channels.
- */
-static bool assigned_dev_msix_skipped(MSIXTableEntry *entry)
-{
-    return !entry->data;
-}
-
-static int assigned_dev_update_msix_mmio(PCIDevice *pci_dev)
-{
-    AssignedDevice *adev = PCI_ASSIGN(pci_dev);
-    uint16_t entries_nr = 0;
-    int i, r = 0;
-    MSIXTableEntry *entry = adev->msix_table;
-
-    /* Get the usable entry number for allocating */
-    for (i = 0; i < adev->msix_max; i++, entry++) {
-        if (assigned_dev_msix_skipped(entry)) {
-            continue;
-        }
-        entries_nr++;
-    }
-
-    DEBUG("MSI-X entries: %d\n", entries_nr);
-
-    /* It's valid to enable MSI-X with all entries masked */
-    if (!entries_nr) {
-        return 0;
-    }
-
-    r = kvm_device_msix_init_vectors(kvm_state, adev->dev_id, entries_nr);
-    if (r != 0) {
-        error_report("fail to set MSI-X entry number for MSIX! %s",
-                     strerror(-r));
-        return r;
-    }
-
-    free_msi_virqs(adev);
-
-    adev->msi_virq_nr = adev->msix_max;
-    adev->msi_virq = g_malloc(adev->msix_max * sizeof(*adev->msi_virq));
-
-    entry = adev->msix_table;
-    for (i = 0; i < adev->msix_max; i++, entry++) {
-        adev->msi_virq[i] = -1;
-
-        if (assigned_dev_msix_skipped(entry)) {
-            continue;
-        }
-
-        r = kvm_irqchip_add_msi_route(kvm_state, i, pci_dev);
-        if (r < 0) {
-            return r;
-        }
-        adev->msi_virq[i] = r;
-
-        DEBUG("MSI-X vector %d, gsi %d, addr %08x_%08x, data %08x\n", i,
-              r, entry->addr_hi, entry->addr_lo, entry->data);
-
-        r = kvm_device_msix_set_vector(kvm_state, adev->dev_id, i,
-                                       adev->msi_virq[i]);
-        if (r) {
-            error_report("fail to set MSI-X entry! %s", strerror(-r));
-            break;
-        }
-    }
-
-    return r;
-}
-
-static void assigned_dev_update_msix(PCIDevice *pci_dev)
-{
-    AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
-    uint16_t ctrl_word = pci_get_word(pci_dev->config + pci_dev->msix_cap +
-                                      PCI_MSIX_FLAGS);
-    int r;
-
-    /* Some guests gratuitously disable MSIX even if they're not using it,
-     * try to catch this by only deassigning irqs if the guest is using
-     * MSIX or intends to start. */
-    if ((assigned_dev->assigned_irq_type == ASSIGNED_IRQ_MSIX) ||
-        (ctrl_word & PCI_MSIX_FLAGS_ENABLE)) {
-        r = kvm_device_msix_deassign(kvm_state, assigned_dev->dev_id);
-        /* -ENXIO means no assigned irq */
-        if (r && r != -ENXIO) {
-            perror("assigned_dev_update_msix: deassign irq");
-        }
-
-        free_msi_virqs(assigned_dev);
-
-        assigned_dev->assigned_irq_type = ASSIGNED_IRQ_NONE;
-        pci_device_set_intx_routing_notifier(pci_dev, NULL);
-    }
-
-    if (ctrl_word & PCI_MSIX_FLAGS_ENABLE) {
-        if (assigned_dev_update_msix_mmio(pci_dev) < 0) {
-            perror("assigned_dev_update_msix_mmio");
-            return;
-        }
-
-        if (assigned_dev->msi_virq_nr > 0) {
-            if (kvm_device_msix_assign(kvm_state, assigned_dev->dev_id) < 0) {
-                perror("assigned_dev_enable_msix: assign irq");
-                return;
-            }
-        }
-        assigned_dev->intx_route.mode = PCI_INTX_DISABLED;
-        assigned_dev->intx_route.irq = -1;
-        assigned_dev->assigned_irq_type = ASSIGNED_IRQ_MSIX;
-    } else {
-        Error *local_err = NULL;
-
-        assign_intx(assigned_dev, &local_err);
-        if (local_err) {
-            error_report_err(local_err);
-        }
-    }
-}
-
-static uint32_t assigned_dev_pci_read_config(PCIDevice *pci_dev,
-                                             uint32_t address, int len)
-{
-    AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
-    uint32_t virt_val = pci_default_read_config(pci_dev, address, len);
-    uint32_t real_val, emulate_mask, full_emulation_mask;
-
-    emulate_mask = 0;
-    memcpy(&emulate_mask, assigned_dev->emulate_config_read + address, len);
-    emulate_mask = le32_to_cpu(emulate_mask);
-
-    full_emulation_mask = 0xffffffff >> (32 - len * 8);
-
-    if (emulate_mask != full_emulation_mask) {
-        real_val = assigned_dev_pci_read(pci_dev, address, len);
-        return (virt_val & emulate_mask) | (real_val & ~emulate_mask);
-    } else {
-        return virt_val;
-    }
-}
-
-static void assigned_dev_pci_write_config(PCIDevice *pci_dev, uint32_t address,
-                                          uint32_t val, int len)
-{
-    AssignedDevice *assigned_dev = PCI_ASSIGN(pci_dev);
-    uint16_t old_cmd = pci_get_word(pci_dev->config + PCI_COMMAND);
-    uint32_t emulate_mask, full_emulation_mask;
-    int ret;
-
-    pci_default_write_config(pci_dev, address, val, len);
-
-    if (kvm_has_intx_set_mask() &&
-        range_covers_byte(address, len, PCI_COMMAND + 1)) {
-        bool intx_masked = (pci_get_word(pci_dev->config + PCI_COMMAND) &
-                            PCI_COMMAND_INTX_DISABLE);
-
-        if (intx_masked != !!(old_cmd & PCI_COMMAND_INTX_DISABLE)) {
-            ret = kvm_device_intx_set_mask(kvm_state, assigned_dev->dev_id,
-                                           intx_masked);
-            if (ret) {
-                perror("assigned_dev_pci_write_config: set intx mask");
-            }
-        }
-    }
-    if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSI) {
-        if (range_covers_byte(address, len,
-                              pci_dev->msi_cap + PCI_MSI_FLAGS)) {
-            assigned_dev_update_msi(pci_dev);
-        } else if (ranges_overlap(address, len, /* 32bit MSI only */
-                                  pci_dev->msi_cap + PCI_MSI_ADDRESS_LO, 6)) {
-            assigned_dev_update_msi_msg(pci_dev);
-        }
-    }
-    if (assigned_dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) {
-        if (range_covers_byte(address, len,
-                              pci_dev->msix_cap + PCI_MSIX_FLAGS + 1)) {
-            assigned_dev_update_msix(pci_dev);
-        }
-    }
-
-    emulate_mask = 0;
-    memcpy(&emulate_mask, assigned_dev->emulate_config_write + address, len);
-    emulate_mask = le32_to_cpu(emulate_mask);
-
-    full_emulation_mask = 0xffffffff >> (32 - len * 8);
-
-    if (emulate_mask != full_emulation_mask) {
-        if (emulate_mask) {
-            val &= ~emulate_mask;
-            val |= assigned_dev_pci_read(pci_dev, address, len) & emulate_mask;
-        }
-        assigned_dev_pci_write(pci_dev, address, val, len);
-    }
-}
-
-static void assigned_dev_setup_cap_read(AssignedDevice *dev, uint32_t offset,
-                                        uint32_t len)
-{
-    assigned_dev_direct_config_read(dev, offset, len);
-    assigned_dev_emulate_config_read(dev, offset + PCI_CAP_LIST_NEXT, 1);
-}
-
-static int assigned_device_pci_cap_init(PCIDevice *pci_dev, Error **errp)
-{
-    AssignedDevice *dev = PCI_ASSIGN(pci_dev);
-    PCIRegion *pci_region = dev->real_device.regions;
-    int ret, pos;
-
-    /* Clear initial capabilities pointer and status copied from hw */
-    pci_set_byte(pci_dev->config + PCI_CAPABILITY_LIST, 0);
-    pci_set_word(pci_dev->config + PCI_STATUS,
-                 pci_get_word(pci_dev->config + PCI_STATUS) &
-                 ~PCI_STATUS_CAP_LIST);
-
-    /* Expose MSI capability
-     * MSI capability is the 1st capability in capability config */
-    pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSI, 0);
-    if (pos != 0 && kvm_check_extension(kvm_state, KVM_CAP_ASSIGN_DEV_IRQ)) {
-        if (verify_irqchip_in_kernel(errp) < 0) {
-            return -ENOTSUP;
-        }
-        dev->dev.cap_present |= QEMU_PCI_CAP_MSI;
-        dev->cap.available |= ASSIGNED_DEVICE_CAP_MSI;
-        /* Only 32-bit/no-mask currently supported */
-        ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSI, pos, 10,
-                                  errp);
-        if (ret < 0) {
-            return ret;
-        }
-        pci_dev->msi_cap = pos;
-
-        pci_set_word(pci_dev->config + pos + PCI_MSI_FLAGS,
-                     pci_get_word(pci_dev->config + pos + PCI_MSI_FLAGS) &
-                     PCI_MSI_FLAGS_QMASK);
-        pci_set_long(pci_dev->config + pos + PCI_MSI_ADDRESS_LO, 0);
-        pci_set_word(pci_dev->config + pos + PCI_MSI_DATA_32, 0);
-
-        /* Set writable fields */
-        pci_set_word(pci_dev->wmask + pos + PCI_MSI_FLAGS,
-                     PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE);
-        pci_set_long(pci_dev->wmask + pos + PCI_MSI_ADDRESS_LO, 0xfffffffc);
-        pci_set_word(pci_dev->wmask + pos + PCI_MSI_DATA_32, 0xffff);
-    }
-    /* Expose MSI-X capability */
-    pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_MSIX, 0);
-    if (pos != 0 && kvm_device_msix_supported(kvm_state)) {
-        int bar_nr;
-        uint32_t msix_table_entry;
-        uint16_t msix_max;
-
-        if (verify_irqchip_in_kernel(errp) < 0) {
-            return -ENOTSUP;
-        }
-        dev->dev.cap_present |= QEMU_PCI_CAP_MSIX;
-        dev->cap.available |= ASSIGNED_DEVICE_CAP_MSIX;
-        ret = pci_add_capability(pci_dev, PCI_CAP_ID_MSIX, pos, 12,
-                                  errp);
-        if (ret < 0) {
-            return ret;
-        }
-        pci_dev->msix_cap = pos;
-
-        msix_max = (pci_get_word(pci_dev->config + pos + PCI_MSIX_FLAGS) &
-                    PCI_MSIX_FLAGS_QSIZE) + 1;
-        msix_max = MIN(msix_max, KVM_MAX_MSIX_PER_DEV);
-        pci_set_word(pci_dev->config + pos + PCI_MSIX_FLAGS, msix_max - 1);
-
-        /* Only enable and function mask bits are writable */
-        pci_set_word(pci_dev->wmask + pos + PCI_MSIX_FLAGS,
-                     PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL);
-
-        msix_table_entry = pci_get_long(pci_dev->config + pos + PCI_MSIX_TABLE);
-        bar_nr = msix_table_entry & PCI_MSIX_FLAGS_BIRMASK;
-        msix_table_entry &= ~PCI_MSIX_FLAGS_BIRMASK;
-        dev->msix_table_addr = pci_region[bar_nr].base_addr + msix_table_entry;
-        dev->msix_table_size = msix_max * sizeof(MSIXTableEntry);
-        dev->msix_max = msix_max;
-    }
-
-    /* Minimal PM support, nothing writable, device appears to NAK changes */
-    pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PM, 0);
-    if (pos) {
-        uint16_t pmc;
-
-        ret = pci_add_capability(pci_dev, PCI_CAP_ID_PM, pos, PCI_PM_SIZEOF,
-                                  errp);
-        if (ret < 0) {
-            return ret;
-        }
-
-        assigned_dev_setup_cap_read(dev, pos, PCI_PM_SIZEOF);
-
-        pmc = pci_get_word(pci_dev->config + pos + PCI_CAP_FLAGS);
-        pmc &= (PCI_PM_CAP_VER_MASK | PCI_PM_CAP_DSI);
-        pci_set_word(pci_dev->config + pos + PCI_CAP_FLAGS, pmc);
-
-        /* assign_device will bring the device up to D0, so we don't need
-         * to worry about doing that ourselves here. */
-        pci_set_word(pci_dev->config + pos + PCI_PM_CTRL,
-                     PCI_PM_CTRL_NO_SOFT_RESET);
-
-        pci_set_byte(pci_dev->config + pos + PCI_PM_PPB_EXTENSIONS, 0);
-        pci_set_byte(pci_dev->config + pos + PCI_PM_DATA_REGISTER, 0);
-    }
-
-    pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_EXP, 0);
-    if (pos) {
-        uint8_t version, size = 0;
-        uint16_t type, devctl, lnksta;
-        uint32_t devcap, lnkcap;
-
-        version = pci_get_byte(pci_dev->config + pos + PCI_EXP_FLAGS);
-        version &= PCI_EXP_FLAGS_VERS;
-        if (version == 1) {
-            size = 0x14;
-        } else if (version == 2) {
-            /*
-             * Check for non-std size, accept reduced size to 0x34,
-             * which is what bcm5761 implemented, violating the
-             * PCIe v3.0 spec that regs should exist and be read as 0,
-             * not optionally provided and shorten the struct size.
-             */
-            size = MIN(0x3c, PCI_CONFIG_SPACE_SIZE - pos);
-            if (size < 0x34) {
-                error_setg(errp, "Invalid size PCIe cap-id 0x%x",
-                           PCI_CAP_ID_EXP);
-                return -EINVAL;
-            } else if (size != 0x3c) {
-                warn_report("%s: PCIe cap-id 0x%x has "
-                            "non-standard size 0x%x; std size should be 0x3c",
-                            __func__, PCI_CAP_ID_EXP, size);
-            }
-        } else if (version == 0) {
-            uint16_t vid, did;
-            vid = pci_get_word(pci_dev->config + PCI_VENDOR_ID);
-            did = pci_get_word(pci_dev->config + PCI_DEVICE_ID);
-            if (vid == PCI_VENDOR_ID_INTEL && did == 0x10ed) {
-                /*
-                 * quirk for Intel 82599 VF with invalid PCIe capability
-                 * version, should really be version 2 (same as PF)
-                 */
-                size = 0x3c;
-            }
-        }
-
-        if (size == 0) {
-            error_setg(errp, "Unsupported PCI express capability version %d",
-                       version);
-            return -EINVAL;
-        }
-
-        ret = pci_add_capability(pci_dev, PCI_CAP_ID_EXP, pos, size,
-                                  errp);
-        if (ret < 0) {
-            return ret;
-        }
-
-        assigned_dev_setup_cap_read(dev, pos, size);
-
-        type = pci_get_word(pci_dev->config + pos + PCI_EXP_FLAGS);
-        type = (type & PCI_EXP_FLAGS_TYPE) >> 4;
-        if (type != PCI_EXP_TYPE_ENDPOINT &&
-            type != PCI_EXP_TYPE_LEG_END && type != PCI_EXP_TYPE_RC_END) {
-            error_setg(errp, "Device assignment only supports endpoint "
-                       "assignment, device type %d", type);
-            return -EINVAL;
-        }
-
-        /* capabilities, pass existing read-only copy
-         * PCI_EXP_FLAGS_IRQ: updated by hardware, should be direct read */
-
-        /* device capabilities: hide FLR */
-        devcap = pci_get_long(pci_dev->config + pos + PCI_EXP_DEVCAP);
-        devcap &= ~PCI_EXP_DEVCAP_FLR;
-        pci_set_long(pci_dev->config + pos + PCI_EXP_DEVCAP, devcap);
-
-        /* device control: clear all error reporting enable bits, leaving
-         *                 only a few host values.  Note, these are
-         *                 all writable, but not passed to hw.
-         */
-        devctl = pci_get_word(pci_dev->config + pos + PCI_EXP_DEVCTL);
-        devctl = (devctl & (PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_PAYLOAD)) |
-                  PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN;
-        pci_set_word(pci_dev->config + pos + PCI_EXP_DEVCTL, devctl);
-        devctl = PCI_EXP_DEVCTL_BCR_FLR | PCI_EXP_DEVCTL_AUX_PME;
-        pci_set_word(pci_dev->wmask + pos + PCI_EXP_DEVCTL, ~devctl);
-
-        /* Clear device status */
-        pci_set_word(pci_dev->config + pos + PCI_EXP_DEVSTA, 0);
-
-        /* Link capabilities, expose links and latencues, clear reporting */
-        lnkcap = pci_get_long(pci_dev->config + pos + PCI_EXP_LNKCAP);
-        lnkcap &= (PCI_EXP_LNKCAP_SLS | PCI_EXP_LNKCAP_MLW |
-                   PCI_EXP_LNKCAP_ASPMS | PCI_EXP_LNKCAP_L0SEL |
-                   PCI_EXP_LNKCAP_L1EL);
-        pci_set_long(pci_dev->config + pos + PCI_EXP_LNKCAP, lnkcap);
-
-        /* Link control, pass existing read-only copy.  Should be writable? */
-
-        /* Link status, only expose current speed and width */
-        lnksta = pci_get_word(pci_dev->config + pos + PCI_EXP_LNKSTA);
-        lnksta &= (PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW);
-        pci_set_word(pci_dev->config + pos + PCI_EXP_LNKSTA, lnksta);
-
-        if (version >= 2) {
-            /* Slot capabilities, control, status - not needed for endpoints */
-            pci_set_long(pci_dev->config + pos + PCI_EXP_SLTCAP, 0);
-            pci_set_word(pci_dev->config + pos + PCI_EXP_SLTCTL, 0);
-            pci_set_word(pci_dev->config + pos + PCI_EXP_SLTSTA, 0);
-
-            /* Root control, capabilities, status - not needed for endpoints */
-            pci_set_word(pci_dev->config + pos + PCI_EXP_RTCTL, 0);
-            pci_set_word(pci_dev->config + pos + PCI_EXP_RTCAP, 0);
-            pci_set_long(pci_dev->config + pos + PCI_EXP_RTSTA, 0);
-
-            /* Device capabilities/control 2, pass existing read-only copy */
-            /* Link control 2, pass existing read-only copy */
-        }
-    }
-
-    pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_PCIX, 0);
-    if (pos) {
-        uint16_t cmd;
-        uint32_t status;
-
-        /* Only expose the minimum, 8 byte capability */
-        ret = pci_add_capability(pci_dev, PCI_CAP_ID_PCIX, pos, 8,
-                                  errp);
-        if (ret < 0) {
-            return ret;
-        }
-
-        assigned_dev_setup_cap_read(dev, pos, 8);
-
-        /* Command register, clear upper bits, including extended modes */
-        cmd = pci_get_word(pci_dev->config + pos + PCI_X_CMD);
-        cmd &= (PCI_X_CMD_DPERR_E | PCI_X_CMD_ERO | PCI_X_CMD_MAX_READ |
-                PCI_X_CMD_MAX_SPLIT);
-        pci_set_word(pci_dev->config + pos + PCI_X_CMD, cmd);
-
-        /* Status register, update with emulated PCI bus location, clear
-         * error bits, leave the rest. */
-        status = pci_get_long(pci_dev->config + pos + PCI_X_STATUS);
-        status &= ~(PCI_X_STATUS_BUS | PCI_X_STATUS_DEVFN);
-        status |= pci_get_bdf(pci_dev);
-        status &= ~(PCI_X_STATUS_SPL_DISC | PCI_X_STATUS_UNX_SPL |
-                    PCI_X_STATUS_SPL_ERR);
-        pci_set_long(pci_dev->config + pos + PCI_X_STATUS, status);
-    }
-
-    pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VPD, 0);
-    if (pos) {
-        /* Direct R/W passthrough */
-        ret = pci_add_capability(pci_dev, PCI_CAP_ID_VPD, pos, 8,
-                                  errp);
-        if (ret < 0) {
-            return ret;
-        }
-
-        assigned_dev_setup_cap_read(dev, pos, 8);
-
-        /* direct write for cap content */
-        assigned_dev_direct_config_write(dev, pos + 2, 6);
-    }
-
-    /* Devices can have multiple vendor capabilities, get them all */
-    for (pos = 0; (pos = pci_find_cap_offset(pci_dev, PCI_CAP_ID_VNDR, pos));
-        pos += PCI_CAP_LIST_NEXT) {
-        uint8_t len = pci_get_byte(pci_dev->config + pos + PCI_CAP_FLAGS);
-        /* Direct R/W passthrough */
-        ret = pci_add_capability(pci_dev, PCI_CAP_ID_VNDR, pos, len,
-                                  errp);
-        if (ret < 0) {
-            return ret;
-        }
-
-        assigned_dev_setup_cap_read(dev, pos, len);
-
-        /* direct write for cap content */
-        assigned_dev_direct_config_write(dev, pos + 2, len - 2);
-    }
-
-    /* If real and virtual capability list status bits differ, virtualize the
-     * access. */
-    if ((pci_get_word(pci_dev->config + PCI_STATUS) & PCI_STATUS_CAP_LIST) !=
-        (assigned_dev_pci_read_byte(pci_dev, PCI_STATUS) &
-         PCI_STATUS_CAP_LIST)) {
-        dev->emulate_config_read[PCI_STATUS] |= PCI_STATUS_CAP_LIST;
-    }
-
-    return 0;
-}
-
-static uint64_t
-assigned_dev_msix_mmio_read(void *opaque, hwaddr addr,
-                            unsigned size)
-{
-    AssignedDevice *adev = opaque;
-    uint64_t val;
-
-    memcpy(&val, (void *)((uint8_t *)adev->msix_table + addr), size);
-
-    return val;
-}
-
-static void assigned_dev_msix_mmio_write(void *opaque, hwaddr addr,
-                                         uint64_t val, unsigned size)
-{
-    AssignedDevice *adev = opaque;
-    PCIDevice *pdev = &adev->dev;
-    uint16_t ctrl;
-    MSIXTableEntry orig;
-    int i = addr >> 4;
-
-    if (i >= adev->msix_max) {
-        return; /* Drop write */
-    }
-
-    ctrl = pci_get_word(pdev->config + pdev->msix_cap + PCI_MSIX_FLAGS);
-
-    DEBUG("write to MSI-X table offset 0x%lx, val 0x%lx\n", addr, val);
-
-    if (ctrl & PCI_MSIX_FLAGS_ENABLE) {
-        orig = adev->msix_table[i];
-    }
-
-    memcpy((uint8_t *)adev->msix_table + addr, &val, size);
-
-    if (ctrl & PCI_MSIX_FLAGS_ENABLE) {
-        MSIXTableEntry *entry = &adev->msix_table[i];
-
-        if (!assigned_dev_msix_masked(&orig) &&
-            assigned_dev_msix_masked(entry)) {
-            /*
-             * Vector masked, disable it
-             *
-             * XXX It's not clear if we can or should actually attempt
-             * to mask or disable the interrupt.  KVM doesn't have
-             * support for pending bits and kvm_assign_set_msix_entry
-             * doesn't modify the device hardware mask.  Interrupts
-             * while masked are simply not injected to the guest, so
-             * are lost.  Can we get away with always injecting an
-             * interrupt on unmask?
-             */
-        } else if (assigned_dev_msix_masked(&orig) &&
-                   !assigned_dev_msix_masked(entry)) {
-            /* Vector unmasked */
-            if (i >= adev->msi_virq_nr || adev->msi_virq[i] < 0) {
-                /* Previously unassigned vector, start from scratch */
-                assigned_dev_update_msix(pdev);
-                return;
-            } else {
-                /* Update an existing, previously masked vector */
-                MSIMessage msg;
-                int ret;
-
-                msg.address = entry->addr_lo |
-                    ((uint64_t)entry->addr_hi << 32);
-                msg.data = entry->data;
-
-                ret = kvm_irqchip_update_msi_route(kvm_state,
-                                                   adev->msi_virq[i], msg,
-                                                   pdev);
-                if (ret) {
-                    error_report("Error updating irq routing entry (%d)", ret);
-                }
-                kvm_irqchip_commit_routes(kvm_state);
-            }
-        }
-    }
-}
-
-static const MemoryRegionOps assigned_dev_msix_mmio_ops = {
-    .read = assigned_dev_msix_mmio_read,
-    .write = assigned_dev_msix_mmio_write,
-    .endianness = DEVICE_NATIVE_ENDIAN,
-    .valid = {
-        .min_access_size = 4,
-        .max_access_size = 8,
-    },
-    .impl = {
-        .min_access_size = 4,
-        .max_access_size = 8,
-    },
-};
-
-static void assigned_dev_msix_reset(AssignedDevice *dev)
-{
-    MSIXTableEntry *entry;
-    int i;
-
-    if (!dev->msix_table) {
-        return;
-    }
-
-    memset(dev->msix_table, 0, dev->msix_table_size);
-
-    for (i = 0, entry = dev->msix_table; i < dev->msix_max; i++, entry++) {
-        entry->ctrl = cpu_to_le32(0x1); /* Masked */
-    }
-}
-
-static void assigned_dev_register_msix_mmio(AssignedDevice *dev, Error **errp)
-{
-    dev->msix_table = mmap(NULL, dev->msix_table_size, PROT_READ | PROT_WRITE,
-                           MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
-    if (dev->msix_table == MAP_FAILED) {
-        error_setg_errno(errp, errno, "failed to allocate msix_table");
-        dev->msix_table = NULL;
-        return;
-    }
-    dev->dev.msix_table = (uint8_t *)dev->msix_table;
-
-    assigned_dev_msix_reset(dev);
-
-    memory_region_init_io(&dev->mmio, OBJECT(dev), &assigned_dev_msix_mmio_ops,
-                          dev, "assigned-dev-msix", dev->msix_table_size);
-}
-
-static void assigned_dev_unregister_msix_mmio(AssignedDevice *dev)
-{
-    if (!dev->msix_table) {
-        return;
-    }
-
-    if (munmap(dev->msix_table, dev->msix_table_size) == -1) {
-        error_report("error unmapping msix_table! %s", strerror(errno));
-    }
-    dev->msix_table = NULL;
-    dev->dev.msix_table = NULL;
-}
-
-static const VMStateDescription vmstate_assigned_device = {
-    .name = "pci-assign",
-    .unmigratable = 1,
-};
-
-static void reset_assigned_device(DeviceState *dev)
-{
-    PCIDevice *pci_dev = PCI_DEVICE(dev);
-    AssignedDevice *adev = PCI_ASSIGN(pci_dev);
-    char reset_file[64];
-    const char reset[] = "1";
-    int fd, ret;
-
-    /*
-     * If a guest is reset without being shutdown, MSI/MSI-X can still
-     * be running.  We want to return the device to a known state on
-     * reset, so disable those here.  We especially do not want MSI-X
-     * enabled since it lives in MMIO space, which is about to get
-     * disabled.
-     */
-    if (adev->assigned_irq_type == ASSIGNED_IRQ_MSIX) {
-        uint16_t ctrl = pci_get_word(pci_dev->config +
-                                     pci_dev->msix_cap + PCI_MSIX_FLAGS);
-
-        pci_set_word(pci_dev->config + pci_dev->msix_cap + PCI_MSIX_FLAGS,
-                     ctrl & ~PCI_MSIX_FLAGS_ENABLE);
-        assigned_dev_update_msix(pci_dev);
-    } else if (adev->assigned_irq_type == ASSIGNED_IRQ_MSI) {
-        uint8_t ctrl = pci_get_byte(pci_dev->config +
-                                    pci_dev->msi_cap + PCI_MSI_FLAGS);
-
-        pci_set_byte(pci_dev->config + pci_dev->msi_cap + PCI_MSI_FLAGS,
-                     ctrl & ~PCI_MSI_FLAGS_ENABLE);
-        assigned_dev_update_msi(pci_dev);
-    }
-
-    snprintf(reset_file, sizeof(reset_file),
-             "/sys/bus/pci/devices/%04x:%02x:%02x.%01x/reset",
-             adev->host.domain, adev->host.bus, adev->host.slot,
-             adev->host.function);
-
-    /*
-     * Issue a device reset via pci-sysfs.  Note that we use write(2) here
-     * and ignore the return value because some kernels have a bug that
-     * returns 0 rather than bytes written on success, sending us into an
-     * infinite retry loop using other write mechanisms.
-     */
-    fd = open(reset_file, O_WRONLY);
-    if (fd != -1) {
-        ret = write(fd, reset, strlen(reset));
-        (void)ret;
-        close(fd);
-    }
-
-    /*
-     * When a 0 is written to the bus master register, the device is logically
-     * disconnected from the PCI bus. This avoids further DMA transfers.
-     */
-    assigned_dev_pci_write_config(pci_dev, PCI_COMMAND, 0, 1);
-}
-
-static void assigned_realize(struct PCIDevice *pci_dev, Error **errp)
-{
-    AssignedDevice *dev = PCI_ASSIGN(pci_dev);
-    uint8_t e_intx;
-    int r;
-    Error *local_err = NULL;
-
-    if (!kvm_enabled()) {
-        error_setg(&local_err, "pci-assign requires KVM support");
-        goto exit_with_error;
-    }
-
-    if (!dev->host.domain && !dev->host.bus && !dev->host.slot &&
-        !dev->host.function) {
-        error_setg(&local_err, "no host device specified");
-        goto exit_with_error;
-    }
-
-    /*
-     * Set up basic config space access control. Will be further refined during
-     * device initialization.
-     */
-    assigned_dev_emulate_config_read(dev, 0, PCI_CONFIG_SPACE_SIZE);
-    assigned_dev_direct_config_read(dev, PCI_STATUS, 2);
-    assigned_dev_direct_config_read(dev, PCI_REVISION_ID, 1);
-    assigned_dev_direct_config_read(dev, PCI_CLASS_PROG, 3);
-    assigned_dev_direct_config_read(dev, PCI_CACHE_LINE_SIZE, 1);
-    assigned_dev_direct_config_read(dev, PCI_LATENCY_TIMER, 1);
-    assigned_dev_direct_config_read(dev, PCI_BIST, 1);
-    assigned_dev_direct_config_read(dev, PCI_CARDBUS_CIS, 4);
-    assigned_dev_direct_config_read(dev, PCI_SUBSYSTEM_VENDOR_ID, 2);
-    assigned_dev_direct_config_read(dev, PCI_SUBSYSTEM_ID, 2);
-    assigned_dev_direct_config_read(dev, PCI_CAPABILITY_LIST + 1, 7);
-    assigned_dev_direct_config_read(dev, PCI_MIN_GNT, 1);
-    assigned_dev_direct_config_read(dev, PCI_MAX_LAT, 1);
-    memcpy(dev->emulate_config_write, dev->emulate_config_read,
-           sizeof(dev->emulate_config_read));
-
-    get_real_device(dev, &local_err);
-    if (local_err) {
-        goto out;
-    }
-
-    if (assigned_device_pci_cap_init(pci_dev, &local_err) < 0) {
-        goto out;
-    }
-
-    /* intercept MSI-X entry page in the MMIO */
-    if (dev->cap.available & ASSIGNED_DEVICE_CAP_MSIX) {
-        assigned_dev_register_msix_mmio(dev, &local_err);
-        if (local_err) {
-            goto out;
-        }
-    }
-
-    /* handle real device's MMIO/PIO BARs */
-    assigned_dev_register_regions(dev->real_device.regions,
-                                  dev->real_device.region_number, dev,
-                                  &local_err);
-    if (local_err) {
-        goto out;
-    }
-
-    /* handle interrupt routing */
-    e_intx = dev->dev.config[PCI_INTERRUPT_PIN] - 1;
-    dev->intpin = e_intx;
-    dev->intx_route.mode = PCI_INTX_DISABLED;
-    dev->intx_route.irq = -1;
-
-    /* assign device to guest */
-    assign_device(dev, &local_err);
-    if (local_err) {
-        goto out;
-    }
-
-    /* assign legacy INTx to the device */
-    r = assign_intx(dev, &local_err);
-    if (r < 0) {
-        goto assigned_out;
-    }
-
-    assigned_dev_load_option_rom(dev);
-
-    return;
-
-assigned_out:
-    deassign_device(dev);
-
-out:
-    free_assigned_device(dev);
-
-exit_with_error:
-    assert(local_err);
-    error_propagate(errp, local_err);
-}
-
-static void assigned_exitfn(struct PCIDevice *pci_dev)
-{
-    AssignedDevice *dev = PCI_ASSIGN(pci_dev);
-
-    deassign_device(dev);
-    free_assigned_device(dev);
-}
-
-static void assigned_dev_instance_init(Object *obj)
-{
-    PCIDevice *pci_dev = PCI_DEVICE(obj);
-    AssignedDevice *d = PCI_ASSIGN(pci_dev);
-
-    device_add_bootindex_property(obj, &d->bootindex,
-                                  "bootindex", NULL,
-                                  &pci_dev->qdev, NULL);
-}
-
-static Property assigned_dev_properties[] = {
-    DEFINE_PROP_PCI_HOST_DEVADDR("host", AssignedDevice, host),
-    DEFINE_PROP_BIT("prefer_msi", AssignedDevice, features,
-                    ASSIGNED_DEVICE_PREFER_MSI_BIT, false),
-    DEFINE_PROP_BIT("share_intx", AssignedDevice, features,
-                    ASSIGNED_DEVICE_SHARE_INTX_BIT, true),
-    DEFINE_PROP_STRING("configfd", AssignedDevice, configfd_name),
-    DEFINE_PROP_END_OF_LIST(),
-};
-
-static void assign_class_init(ObjectClass *klass, void *data)
-{
-    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
-    DeviceClass *dc = DEVICE_CLASS(klass);
-
-    k->realize      = assigned_realize;
-    k->exit         = assigned_exitfn;
-    k->config_read  = assigned_dev_pci_read_config;
-    k->config_write = assigned_dev_pci_write_config;
-    dc->props       = assigned_dev_properties;
-    dc->vmsd        = &vmstate_assigned_device;
-    dc->reset       = reset_assigned_device;
-    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
-    dc->desc        = "KVM-based PCI passthrough";
-}
-
-static const TypeInfo assign_info = {
-    .name               = TYPE_PCI_ASSIGN,
-    .parent             = TYPE_PCI_DEVICE,
-    .instance_size      = sizeof(AssignedDevice),
-    .class_init         = assign_class_init,
-    .instance_init      = assigned_dev_instance_init,
-};
-
-static void assign_register_types(void)
-{
-    type_register_static(&assign_info);
-}
-
-type_init(assign_register_types)
-
-static void assigned_dev_load_option_rom(AssignedDevice *dev)
-{
-    int size = 0;
-
-    pci_assign_dev_load_option_rom(&dev->dev, OBJECT(dev), &size,
-                                   dev->host.domain, dev->host.bus,
-                                   dev->host.slot, dev->host.function);
-}