summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--hw/virtio-pci.c25
1 files changed, 24 insertions, 1 deletions
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 6eb19cdf8c..e23ab91992 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -79,6 +79,10 @@
  * 12 is historical, and due to x86 page size. */
 #define VIRTIO_PCI_QUEUE_ADDR_SHIFT    12
 
+/* We can catch some guest bugs inside here so we continue supporting older
+   guests. */
+#define VIRTIO_PCI_BUG_BUS_MASTER	(1 << 0)
+
 /* QEMU doesn't strictly need write barriers since everything runs in
  * lock-step.  We'll leave the calls to wmb() in though to make it obvious for
  * KVM or if kqemu gets SMP support.
@@ -90,6 +94,7 @@
 typedef struct {
     PCIDevice pci_dev;
     VirtIODevice *vdev;
+    uint32_t bugs;
     uint32_t addr;
     uint32_t class_code;
     uint32_t nvectors;
@@ -144,6 +149,13 @@ static int virtio_pci_load_config(void * opaque, QEMUFile *f)
     if (proxy->vdev->config_vector != VIRTIO_NO_VECTOR) {
         return msix_vector_use(&proxy->pci_dev, proxy->vdev->config_vector);
     }
+
+    /* Try to find out if the guest has bus master disabled, but is
+       in ready state. Then we have a buggy guest OS. */
+    if (!(proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+        !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
+        proxy->bugs |= VIRTIO_PCI_BUG_BUS_MASTER;
+    }
     return 0;
 }
 
@@ -168,6 +180,7 @@ static void virtio_pci_reset(DeviceState *d)
     VirtIOPCIProxy *proxy = container_of(d, VirtIOPCIProxy, pci_dev.qdev);
     virtio_reset(proxy->vdev);
     msix_reset(&proxy->pci_dev);
+    proxy->bugs = 0;
 }
 
 static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
@@ -211,6 +224,14 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
             virtio_reset(proxy->vdev);
             msix_unuse_all_vectors(&proxy->pci_dev);
         }
+
+        /* Linux before 2.6.34 sets the device as OK without enabling
+           the PCI device bus master bit. In this case we need to disable
+           some safety checks. */
+        if ((val & VIRTIO_CONFIG_S_DRIVER_OK) &&
+            !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) {
+            proxy->bugs |= VIRTIO_PCI_BUG_BUS_MASTER;
+        }
         break;
     case VIRTIO_MSI_CONFIG_VECTOR:
         msix_vector_unuse(&proxy->pci_dev, vdev->config_vector);
@@ -377,7 +398,9 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,
 
     if (PCI_COMMAND == address) {
         if (!(val & PCI_COMMAND_MASTER)) {
-            proxy->vdev->status &= ~VIRTIO_CONFIG_S_DRIVER_OK;
+            if (!(proxy->bugs & VIRTIO_PCI_BUG_BUS_MASTER)) {
+                proxy->vdev->status &= ~VIRTIO_CONFIG_S_DRIVER_OK;
+            }
         }
     }