diff options
Diffstat (limited to 'hw/virtio-pci.c')
-rw-r--r-- | hw/virtio-pci.c | 25 |
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; + } } } |