summaryrefslogtreecommitdiff
path: root/hw/virtio-pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'hw/virtio-pci.c')
-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;
+ }
}
}