diff mbox

[2/3] virtio: pci cfg access

Message ID 1435916258-7705-3-git-send-email-kraxel@redhat.com
State New
Headers show

Commit Message

Gerd Hoffmann July 3, 2015, 9:37 a.m. UTC
virtio regions can also be accessed using a window in pci cfg space.
Add support for it.  Enable it in case the virtio regions are mapped
high (above 4g), so direct mmio access doesn't work for us even in
32bit mode.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 src/hw/virtio-pci.c | 164 +++++++++++++++++++++++++++++++++++++++++++++-------
 src/hw/virtio-pci.h |  13 ++++-
 2 files changed, 155 insertions(+), 22 deletions(-)
diff mbox

Patch

diff --git a/src/hw/virtio-pci.c b/src/hw/virtio-pci.c
index 769710a..55a766f 100644
--- a/src/hw/virtio-pci.c
+++ b/src/hw/virtio-pci.c
@@ -27,9 +27,10 @@ 
 u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size)
 {
     u32 addr = cap->addr + offset;
-    u64 var;
+    u64 var = 0;
 
-    if (cap->is_io) {
+    switch (cap->mode) {
+    case VP_ACCESS_IO:
         switch (size) {
         case 8:
             var = inl(addr);
@@ -44,10 +45,10 @@  u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size)
         case 1:
             var = inb(addr);
             break;
-        default:
-            var = 0;
         }
-    } else {
+        break;
+
+    case VP_ACCESS_MMIO:
         switch (size) {
         case 8:
             var = readl((void*)addr);
@@ -62,9 +63,43 @@  u64 _vp_read(struct vp_cap *cap, u32 offset, u8 size)
         case 1:
             var = readb((void*)addr);
             break;
-        default:
-            var = 0;
         }
+        break;
+
+    case VP_ACCESS_PCICFG:
+        pci_config_writeb(cap->bdf, cap->cfg +
+                          offsetof(struct virtio_pci_cfg_cap, cap.bar),
+                          cap->bar);
+        pci_config_writel(cap->bdf, cap->cfg +
+                          offsetof(struct virtio_pci_cfg_cap, cap.offset),
+                          addr);
+        pci_config_writel(cap->bdf, cap->cfg +
+                          offsetof(struct virtio_pci_cfg_cap, cap.length),
+                          (size > 4) ? 4 : size);
+        switch (size) {
+        case 8:
+            var = pci_config_readl(cap->bdf, cap->cfg +
+                                   offsetof(struct virtio_pci_cfg_cap, pci_cfg_data));
+            pci_config_writel(cap->bdf, cap->cfg +
+                              offsetof(struct virtio_pci_cfg_cap, cap.offset),
+                              addr + 4);
+            var |= (u64)pci_config_readl(cap->bdf, cap->cfg +
+                                         offsetof(struct virtio_pci_cfg_cap, pci_cfg_data)) << 32;
+            break;
+        case 4:
+            var = pci_config_readl(cap->bdf, cap->cfg +
+                                   offsetof(struct virtio_pci_cfg_cap, pci_cfg_data));
+            break;
+        case 2:
+            var = pci_config_readw(cap->bdf, cap->cfg +
+                                   offsetof(struct virtio_pci_cfg_cap, pci_cfg_data));
+            break;
+        case 1:
+            var = pci_config_readb(cap->bdf, cap->cfg +
+                                   offsetof(struct virtio_pci_cfg_cap, pci_cfg_data));
+            break;
+        }
+
     }
     dprintf(9, "vp read   %x (%d) -> 0x%llx\n", addr, size, var);
     return var;
@@ -75,7 +110,8 @@  void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var)
     u32 addr = cap->addr + offset;
 
     dprintf(9, "vp write  %x (%d) <- 0x%llx\n", addr, size, var);
-    if (cap->is_io) {
+    switch (cap->mode) {
+    case VP_ACCESS_IO:
         switch (size) {
         case 4:
             outl(var, addr);
@@ -87,7 +123,9 @@  void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var)
             outb(var, addr);
             break;
         }
-    } else {
+        break;
+
+    case VP_ACCESS_MMIO:
         switch (size) {
         case 4:
             writel((void*)addr, var);
@@ -99,6 +137,36 @@  void _vp_write(struct vp_cap *cap, u32 offset, u8 size, u64 var)
             writeb((void*)addr, var);
             break;
         }
+        break;
+
+    case VP_ACCESS_PCICFG:
+        pci_config_writeb(cap->bdf, cap->cfg +
+                          offsetof(struct virtio_pci_cfg_cap, cap.bar),
+                          cap->bar);
+        pci_config_writel(cap->bdf, cap->cfg +
+                          offsetof(struct virtio_pci_cfg_cap, cap.offset),
+                          addr);
+        pci_config_writel(cap->bdf, cap->cfg +
+                          offsetof(struct virtio_pci_cfg_cap, cap.length),
+                          size);
+        switch (size) {
+        case 4:
+            pci_config_writel(cap->bdf, cap->cfg +
+                              offsetof(struct virtio_pci_cfg_cap, pci_cfg_data),
+                              var);
+            break;
+        case 2:
+            pci_config_writew(cap->bdf, cap->cfg +
+                              offsetof(struct virtio_pci_cfg_cap, pci_cfg_data),
+                              var);
+            break;
+        case 1:
+            pci_config_writeb(cap->bdf, cap->cfg +
+                              offsetof(struct virtio_pci_cfg_cap, pci_cfg_data),
+                              var);
+            break;
+        }
+
     }
 }
 
@@ -181,10 +249,26 @@  void vp_notify(struct vp_device *vp, struct vring_virtqueue *vq)
         u32 addr = vp->notify.addr +
             vq->queue_notify_off *
             vp->notify_off_multiplier;
-        if (vp->notify.is_io) {
+        switch (vp->notify.mode) {
+        case VP_ACCESS_IO:
             outw(vq->queue_index, addr);
-        } else {
+            break;
+        case VP_ACCESS_MMIO:
             writew((void*)addr, vq->queue_index);
+            break;
+        case VP_ACCESS_PCICFG:
+            pci_config_writeb(vp->notify.bdf, vp->notify.cfg +
+                              offsetof(struct virtio_pci_cfg_cap, cap.bar),
+                              vp->notify.bar);
+            pci_config_writel(vp->notify.bdf, vp->notify.cfg +
+                              offsetof(struct virtio_pci_cfg_cap, cap.offset),
+                              addr);
+            pci_config_writel(vp->notify.bdf, vp->notify.cfg +
+                              offsetof(struct virtio_pci_cfg_cap, cap.length),
+                              2);
+            pci_config_writew(vp->notify.bdf, vp->notify.cfg +
+                              offsetof(struct virtio_pci_cfg_cap, pci_cfg_data),
+                              vq->queue_index);
         }
         dprintf(9, "vp notify %x (%d) -- 0x%x\n",
                 addr, 2, vq->queue_index);
@@ -286,7 +370,9 @@  void vp_init_simple(struct vp_device *vp, struct pci_device *pci)
 {
     u8 cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, 0);
     struct vp_cap *vp_cap;
-    u32 addr, offset, mul;
+    const char *mode;
+    u32 offset, base, mul;
+    u64 addr;
     u8 type;
 
     memset(vp, 0, sizeof(*vp));
@@ -308,6 +394,20 @@  void vp_init_simple(struct vp_device *vp, struct pci_device *pci)
         case VIRTIO_PCI_CAP_DEVICE_CFG:
             vp_cap = &vp->device;
             break;
+        case VIRTIO_PCI_CAP_PCI_CFG:
+            vp->common.cfg = cap;
+            vp->common.bdf = pci->bdf;
+            vp->notify.cfg = cap;
+            vp->notify.bdf = pci->bdf;
+            vp->isr.cfg = cap;
+            vp->isr.bdf = pci->bdf;
+            vp->device.cfg = cap;
+            vp->device.bdf = pci->bdf;
+            vp_cap = NULL;
+            dprintf(1, "pci dev %x:%x virtio cap at 0x%x type %d [pci cfg access]\n",
+                    pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
+                    cap, type);
+            break;
         default:
             vp_cap = NULL;
             break;
@@ -318,20 +418,42 @@  void vp_init_simple(struct vp_device *vp, struct pci_device *pci)
                                            offsetof(struct virtio_pci_cap, bar));
             offset = pci_config_readl(pci->bdf, cap +
                                       offsetof(struct virtio_pci_cap, offset));
-            addr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0 + 4 * vp_cap->bar);
+            base = PCI_BASE_ADDRESS_0 + 4 * vp_cap->bar;
+            addr = pci_config_readl(pci->bdf, base);
             if (addr & PCI_BASE_ADDRESS_SPACE_IO) {
-                vp_cap->is_io = 1;
                 addr &= PCI_BASE_ADDRESS_IO_MASK;
+                vp_cap->mode = VP_ACCESS_IO;
+            } else if ((addr & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+                       PCI_BASE_ADDRESS_MEM_TYPE_64) {
+                addr &= PCI_BASE_ADDRESS_MEM_MASK;
+                addr |= (u64)pci_config_readl(pci->bdf, base + 4) << 32;
+                vp_cap->mode = (addr > 0xffffffffll) ?
+                    VP_ACCESS_PCICFG : VP_ACCESS_MMIO;
             } else {
-                vp_cap->is_io = 0;
                 addr &= PCI_BASE_ADDRESS_MEM_MASK;
+                vp_cap->mode = VP_ACCESS_MMIO;
             }
-            vp_cap->addr = addr + offset;
-            dprintf(3, "pci dev %x:%x virtio cap at 0x%x type %d "
-                    "bar %d at 0x%08x off +0x%04x [%s]\n",
+            switch (vp_cap->mode) {
+            case VP_ACCESS_IO:
+                mode = "io";
+                vp_cap->addr = addr + offset; // io addr
+                break;
+            case VP_ACCESS_MMIO:
+                mode = "mmio";
+                vp_cap->addr = addr + offset; // mem addr
+                break;
+            case VP_ACCESS_PCICFG:
+                mode = "pcicfg";
+                vp_cap->addr = offset; // bar offset
+                break;
+            default:
+                mode = "Huh?";
+                break;
+            }
+            dprintf(1, "pci dev %x:%x virtio cap at 0x%x type %d "
+                    "bar %d at 0x%08llx off +0x%04x [%s]\n",
                     pci_bdf_to_bus(pci->bdf), pci_bdf_to_dev(pci->bdf),
-                    vp_cap->cap, type, vp_cap->bar, addr, offset,
-                    vp_cap->is_io ? "io" : "mmio");
+                    vp_cap->cap, type, vp_cap->bar, addr, offset, mode);
         }
 
         cap = pci_find_capability(pci, PCI_CAP_ID_VNDR, cap);
@@ -347,7 +469,7 @@  void vp_init_simple(struct vp_device *vp, struct pci_device *pci)
         vp->legacy.bar = 0;
         vp->legacy.addr = pci_config_readl(pci->bdf, PCI_BASE_ADDRESS_0) &
             PCI_BASE_ADDRESS_IO_MASK;
-        vp->legacy.is_io = 1;
+        vp->legacy.mode = VP_ACCESS_IO;
     }
 
     vp_reset(vp);
diff --git a/src/hw/virtio-pci.h b/src/hw/virtio-pci.h
index 8d4ebe3..5ab2ec8 100644
--- a/src/hw/virtio-pci.h
+++ b/src/hw/virtio-pci.h
@@ -54,6 +54,11 @@  struct virtio_pci_notify_cap {
     u32 notify_off_multiplier;   /* Multiplier for queue_notify_off. */
 };
 
+struct virtio_pci_cfg_cap {
+    struct virtio_pci_cap cap;
+    u8 pci_cfg_data[4]; /* Data for BAR access. */
+};
+
 typedef struct virtio_pci_common_cfg {
     /* About the whole device. */
     u32 device_feature_select;   /* read-write */
@@ -85,11 +90,17 @@  typedef struct virtio_pci_isr {
 
 /* --- driver structs ----------------------------------------------- */
 
+#define VP_ACCESS_IO       1
+#define VP_ACCESS_MMIO     2
+#define VP_ACCESS_PCICFG   3
+
 struct vp_cap {
     u32 addr;
+    u16 bdf;
     u8 cap;
+    u8 cfg;
     u8 bar;
-    u8 is_io;
+    u8 mode;
 };
 
 struct vp_device {