Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [PATCH v2 0/2] qemu vfio-pci: PCIe and generic config space mangling
@ 2013-02-18 20:15 Alex Williamson
  2013-02-18 20:15 ` [PATCH v2 1/2] vfio-pci: Generalize PCI config mangling Alex Williamson
  2013-02-18 20:15 ` [PATCH v2 2/2] vfio-pci: Add PCIe capability mangling based on bus type Alex Williamson
  0 siblings, 2 replies; 3+ messages in thread
From: Alex Williamson @ 2013-02-18 20:15 UTC (permalink / raw)
  To: alex.williamson, qemu-devel; +Cc: kvm

v2: Removing trailing \n from error_report calls

This series generalizes the places where we already rely on QEMU
config space emulation to make it easier to add various other fields.
It turns out that when we start running on Q35 Windows cares quite
a bit about the PCIe type we expose.  Drivers will fail to load if
not set to something valid and appropriate for the bus.  We don't
have anything in place yet to tell what the bus is, so for now we
rely on a temporary option that will eventually get replaced by
something automatic.  Thanks,

Alex

---

Alex Williamson (2):
      vfio-pci: Generalize PCI config mangling
      vfio-pci: Add PCIe capability mangling based on bus type


 hw/vfio_pci.c |  209 ++++++++++++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 170 insertions(+), 39 deletions(-)

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH v2 1/2] vfio-pci: Generalize PCI config mangling
  2013-02-18 20:15 [PATCH v2 0/2] qemu vfio-pci: PCIe and generic config space mangling Alex Williamson
@ 2013-02-18 20:15 ` Alex Williamson
  2013-02-18 20:15 ` [PATCH v2 2/2] vfio-pci: Add PCIe capability mangling based on bus type Alex Williamson
  1 sibling, 0 replies; 3+ messages in thread
From: Alex Williamson @ 2013-02-18 20:15 UTC (permalink / raw)
  To: alex.williamson, qemu-devel; +Cc: kvm

Kernel-side vfio virtualizes all of config space, but some parts are
unique to Qemu.  For instance we may or may not expose the ROM BAR,
Qemu manages MSI/MSIX, and Qemu manages the multi-function bit so that
single function devices can appear as multi-function and vica versa.
Generalize this into a bitmap of Qemu emulated bits.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 hw/vfio_pci.c |   80 ++++++++++++++++++++++++++++++---------------------------
 1 file changed, 42 insertions(+), 38 deletions(-)

diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c
index ad9ae36..2106b87 100644
--- a/hw/vfio_pci.c
+++ b/hw/vfio_pci.c
@@ -117,6 +117,7 @@ typedef struct VFIODevice {
     int fd;
     VFIOINTx intx;
     unsigned int config_size;
+    uint8_t *emulated_config_bits; /* QEMU emulated bits, little-endian */
     off_t config_offset; /* Offset of config space region within device fd */
     unsigned int rom_size;
     off_t rom_offset; /* Offset of ROM region within device fd */
@@ -963,44 +964,29 @@ static const MemoryRegionOps vfio_bar_ops = {
 static uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len)
 {
     VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
-    uint32_t val = 0;
+    uint32_t emu_bits = 0, emu_val = 0, phys_val = 0, val;
 
-    /*
-     * We only need QEMU PCI config support for the ROM BAR, the MSI and MSIX
-     * capabilities, and the multifunction bit below.  We let VFIO handle
-     * virtualizing everything else.  Performance is not a concern here.
-     */
-    if (ranges_overlap(addr, len, PCI_ROM_ADDRESS, 4) ||
-        (pdev->cap_present & QEMU_PCI_CAP_MSIX &&
-         ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) ||
-        (pdev->cap_present & QEMU_PCI_CAP_MSI &&
-         ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size))) {
+    memcpy(&emu_bits, vdev->emulated_config_bits + addr, len);
+    emu_bits = le32_to_cpu(emu_bits);
 
-        val = pci_default_read_config(pdev, addr, len);
-    } else {
-        if (pread(vdev->fd, &val, len, vdev->config_offset + addr) != len) {
+    if (emu_bits) {
+        emu_val = pci_default_read_config(pdev, addr, len);
+    }
+
+    if (~emu_bits & (0xffffffffU >> (32 - len * 8))) {
+        ssize_t ret;
+
+        ret = pread(vdev->fd, &phys_val, len, vdev->config_offset + addr);
+        if (ret != len) {
             error_report("%s(%04x:%02x:%02x.%x, 0x%x, 0x%x) failed: %m",
                          __func__, vdev->host.domain, vdev->host.bus,
                          vdev->host.slot, vdev->host.function, addr, len);
             return -errno;
         }
-        val = le32_to_cpu(val);
+        phys_val = le32_to_cpu(phys_val);
     }
 
-    /* Multifunction bit is virualized in QEMU */
-    if (unlikely(ranges_overlap(addr, len, PCI_HEADER_TYPE, 1))) {
-        uint32_t mask = PCI_HEADER_TYPE_MULTI_FUNCTION;
-
-        if (len == 4) {
-            mask <<= 16;
-        }
-
-        if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) {
-            val |= mask;
-        } else {
-            val &= ~mask;
-        }
-    }
+    val = (emu_val & emu_bits) | (phys_val & ~emu_bits);
 
     DPRINTF("%s(%04x:%02x:%02x.%x, @0x%x, len=0x%x) %x\n", __func__,
             vdev->host.domain, vdev->host.bus, vdev->host.slot,
@@ -1026,12 +1012,6 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
                      vdev->host.slot, vdev->host.function, addr, val, len);
     }
 
-    /* Write standard header bits to emulation */
-    if (addr < PCI_CONFIG_HEADER_SIZE) {
-        pci_default_write_config(pdev, addr, val, len);
-        return;
-    }
-
     /* MSI/MSI-X Enabling/Disabling */
     if (pdev->cap_present & QEMU_PCI_CAP_MSI &&
         ranges_overlap(addr, len, pdev->msi_cap, vdev->msi_cap_size)) {
@@ -1046,9 +1026,7 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
         } else if (was_enabled && !is_enabled) {
             vfio_disable_msi(vdev);
         }
-    }
-
-    if (pdev->cap_present & QEMU_PCI_CAP_MSIX &&
+    } else if (pdev->cap_present & QEMU_PCI_CAP_MSIX &&
         ranges_overlap(addr, len, pdev->msix_cap, MSIX_CAP_LENGTH)) {
         int is_enabled, was_enabled = msix_enabled(pdev);
 
@@ -1061,6 +1039,9 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
         } else if (was_enabled && !is_enabled) {
             vfio_disable_msix(vdev);
         }
+    } else {
+        /* Write everything to QEMU to keep emulated bits correct */
+        pci_default_write_config(pdev, addr, val, len);
     }
 }
 
@@ -2003,6 +1984,16 @@ static int vfio_initfn(PCIDevice *pdev)
         goto out_put;
     }
 
+    /* vfio emulates a lot for us, but some bits need extra love */
+    vdev->emulated_config_bits = g_malloc0(vdev->config_size);
+
+    /* QEMU can choose to expose the ROM or not */
+    memset(vdev->emulated_config_bits + PCI_ROM_ADDRESS, 0xff, 4);
+
+    /* QEMU can change multi-function devices to single function, or reverse */
+    vdev->emulated_config_bits[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
@@ -2025,6 +2016,17 @@ static int vfio_initfn(PCIDevice *pdev)
         goto out_teardown;
     }
 
+    /* QEMU emulates all of MSI & MSIX */
+    if (pdev->cap_present & QEMU_PCI_CAP_MSIX) {
+        memset(vdev->emulated_config_bits + pdev->msix_cap, 0xff,
+               MSIX_CAP_LENGTH);
+    }
+
+    if (pdev->cap_present & QEMU_PCI_CAP_MSI) {
+        memset(vdev->emulated_config_bits + pdev->msi_cap, 0xff,
+               vdev->msi_cap_size);
+    }
+
     if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) {
         vdev->intx.mmap_timer = qemu_new_timer_ms(vm_clock,
                                                   vfio_intx_mmap_enable, vdev);
@@ -2042,6 +2044,7 @@ out_teardown:
     vfio_teardown_msi(vdev);
     vfio_unmap_bars(vdev);
 out_put:
+    g_free(vdev->emulated_config_bits);
     vfio_put_device(vdev);
     vfio_put_group(group);
     return ret;
@@ -2059,6 +2062,7 @@ static void vfio_exitfn(PCIDevice *pdev)
     }
     vfio_teardown_msi(vdev);
     vfio_unmap_bars(vdev);
+    g_free(vdev->emulated_config_bits);
     vfio_put_device(vdev);
     vfio_put_group(group);
 }


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH v2 2/2] vfio-pci: Add PCIe capability mangling based on bus type
  2013-02-18 20:15 [PATCH v2 0/2] qemu vfio-pci: PCIe and generic config space mangling Alex Williamson
  2013-02-18 20:15 ` [PATCH v2 1/2] vfio-pci: Generalize PCI config mangling Alex Williamson
@ 2013-02-18 20:15 ` Alex Williamson
  1 sibling, 0 replies; 3+ messages in thread
From: Alex Williamson @ 2013-02-18 20:15 UTC (permalink / raw)
  To: alex.williamson, qemu-devel; +Cc: kvm

Windows seems to pay particular interest to the PCIe header type of
devices and will fail to load drivers if we attached Endpoint devices
or Legacy Endpoint devices to the Root Complex.  We don't yet have a
good way to determine the bus type, so for now we add an experimental
x-bustype option which will later be replaced by some mechanism to
determine this automatcally.  The new option is defined as:

x-bustype=<n> where <n> is one of:
	0: Legacy PCI [default]
	1: PCI Express
	2: PCI Express Root Complex

Conversion of PCIe types is does as follows:

* Legacy PCI
  * No change, capability is unmodified for compatibility.
* PCI Express
  * Integrated Root Complex Endpoint -> Endpoint
* PCI Express Root Complext
  * Endpoint -> Integrated Root Complex Endpoint
  * Legacy Endpoint -> none, capability hidden

We also take this opportunity to explicitly limit supported devices
to Endpoints, Legacy Endpoints, and Root Complex Integrated Endpoints.
We don't currently have support for other types and users often cause
themselves problems by assigning them.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
---
 hw/vfio_pci.c |  129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 128 insertions(+), 1 deletion(-)

diff --git a/hw/vfio_pci.c b/hw/vfio_pci.c
index 2106b87..330a687 100644
--- a/hw/vfio_pci.c
+++ b/hw/vfio_pci.c
@@ -130,6 +130,7 @@ typedef struct VFIODevice {
     PCIHostDeviceAddress host;
     QLIST_ENTRY(VFIODevice) next;
     struct VFIOGroup *group;
+    uint8_t bustype;
     bool reset_works;
 } VFIODevice;
 
@@ -1506,6 +1507,122 @@ static uint8_t vfio_std_cap_max_size(PCIDevice *pdev, uint8_t pos)
     return next - pos;
 }
 
+static void vfio_set_word_bits(uint8_t *buf, uint16_t val, uint16_t mask)
+{
+    pci_set_word(buf, (pci_get_word(buf) & ~mask) | val);
+}
+
+static void vfio_add_emulated_word(VFIODevice *vdev, int pos,
+                                   uint16_t val, uint16_t mask)
+{
+    vfio_set_word_bits(vdev->pdev.config + pos, val, mask);
+    vfio_set_word_bits(vdev->pdev.wmask + pos, ~mask, mask);
+    vfio_set_word_bits(vdev->emulated_config_bits + pos, mask, mask);
+}
+
+static void vfio_set_long_bits(uint8_t *buf, uint32_t val, uint32_t mask)
+{
+    pci_set_long(buf, (pci_get_long(buf) & ~mask) | val);
+}
+
+static void vfio_add_emulated_long(VFIODevice *vdev, int pos,
+                                   uint32_t val, uint32_t mask)
+{
+    vfio_set_long_bits(vdev->pdev.config + pos, val, mask);
+    vfio_set_long_bits(vdev->pdev.wmask + pos, ~mask, mask);
+    vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask);
+}
+
+static int vfio_setup_pcie_cap(VFIODevice *vdev, int pos, uint8_t size)
+{
+    uint16_t flags;
+    uint8_t type;
+    enum {
+        VFIO_BUS_TYPE_PCI,
+        VFIO_BUS_TYPE_PCIE,
+        VFIO_BUS_TYPE_PCIE_RC,
+    };
+
+    flags = pci_get_word(vdev->pdev.config + pos + PCI_CAP_FLAGS);
+    type = (flags & PCI_EXP_FLAGS_TYPE) >> 4;
+
+    switch (type) {
+    case PCI_EXP_TYPE_ENDPOINT:
+    case PCI_EXP_TYPE_LEG_END:
+    case PCI_EXP_TYPE_RC_END:
+        break;
+    default:
+        error_report("vfio: Assignment of PCIe type 0x%x "
+                     "devices is not currently supported", type);
+        return -EINVAL;
+    }
+
+    if (vdev->bustype > VFIO_BUS_TYPE_PCIE_RC) {
+        error_report("vfio: Unknown bustype %d.  Accepted values: "
+                     "%d (Legacy PCI [default]), %d (PCI Express), "
+                     "%d (PCI Express Root-Complex)", vdev->bustype,
+                     VFIO_BUS_TYPE_PCI, VFIO_BUS_TYPE_PCIE,
+                     VFIO_BUS_TYPE_PCIE_RC);
+        return -EINVAL;
+    }
+
+    switch (vdev->bustype) {
+    case VFIO_BUS_TYPE_PCI:
+        /*
+         * Use express capability as-is on PCI bus.  It doesn't make much
+         * sense to even expose, but some drivers (ex. tg3) depend on it
+         * and guests don't seem to be particular about it.  We'll need
+         * to revist this if we ever expose an IOMMU to the guest.
+         */
+        return pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size);
+
+    case VFIO_BUS_TYPE_PCIE:
+        if (type == PCI_EXP_TYPE_RC_END) {
+            /* Type becomes non-Integrated Endpoint */
+            vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS,
+                                   PCI_EXP_TYPE_ENDPOINT << 4,
+                                   PCI_EXP_FLAGS_TYPE);
+            /* XXX Implement LNKCAP fields? */
+        }
+
+        return pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size);
+
+    case VFIO_BUS_TYPE_PCIE_RC:
+        switch (type) {
+        case PCI_EXP_TYPE_ENDPOINT:
+            /* Type becomes Integrated Endpoint */
+            vfio_add_emulated_word(vdev, pos + PCI_CAP_FLAGS,
+                                   PCI_EXP_TYPE_RC_END << 4,
+                                   PCI_EXP_FLAGS_TYPE);
+
+            /* Link Capabilities, Status, and Control goes away */
+            if (size < PCI_EXP_LNKCTL) {
+                break;
+            }
+            vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP, 0U, ~0U);
+            vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCTL, 0U, ~0U);
+
+            /* Link 2 Capabilities, Status, and Control goes away */
+            if (size < PCI_EXP_LNKCTL2) {
+                break;
+            }
+            vfio_add_emulated_long(vdev, pos + 0x2c, 0U, ~0U);
+            vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCTL2, 0U, ~0U);
+            break;
+        case PCI_EXP_TYPE_LEG_END:
+            /*
+             * Legacy endpoints don't belong on the root complex.  Windows
+             * seems to be happier with devices if we skip the capability.
+             */
+            return 0;
+        }
+
+        return pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size);
+    }
+
+    return -EINVAL;
+}
+
 static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos)
 {
     PCIDevice *pdev = &vdev->pdev;
@@ -1536,13 +1653,22 @@ static int vfio_add_std_cap(VFIODevice *vdev, uint8_t pos)
             return ret;
         }
     } else {
-        pdev->config[PCI_CAPABILITY_LIST] = 0; /* Begin the rebuild */
+        /* Begin the rebuild, use QEMU emulated list bits */
+        pdev->config[PCI_CAPABILITY_LIST] = 0;
+        vdev->emulated_config_bits[PCI_CAPABILITY_LIST] = 0xff;
+        vdev->emulated_config_bits[PCI_STATUS] |= PCI_STATUS_CAP_LIST;
     }
 
+    /* Use emulated next pointer to allow dropping caps */
+    pci_set_byte(vdev->emulated_config_bits + pos + 1, 0xff);
+
     switch (cap_id) {
     case PCI_CAP_ID_MSI:
         ret = vfio_setup_msi(vdev, pos);
         break;
+    case PCI_CAP_ID_EXP:
+        ret = vfio_setup_pcie_cap(vdev, pos, size);
+        break;
     case PCI_CAP_ID_MSIX:
         ret = vfio_setup_msix(vdev, pos);
         break;
@@ -2102,6 +2228,7 @@ static Property vfio_pci_dev_properties[] = {
     DEFINE_PROP_PCI_HOST_DEVADDR("host", VFIODevice, host),
     DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIODevice,
                        intx.mmap_timeout, 1100),
+    DEFINE_PROP_UINT8("x-bustype", VFIODevice, bustype, 0),
     /*
      * TODO - support passed fds... is this necessary?
      * DEFINE_PROP_STRING("vfiofd", VFIODevice, vfiofd_name),


^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2013-02-18 20:15 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-02-18 20:15 [PATCH v2 0/2] qemu vfio-pci: PCIe and generic config space mangling Alex Williamson
2013-02-18 20:15 ` [PATCH v2 1/2] vfio-pci: Generalize PCI config mangling Alex Williamson
2013-02-18 20:15 ` [PATCH v2 2/2] vfio-pci: Add PCIe capability mangling based on bus type Alex Williamson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox