diff options
author | aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | 2009-04-05 17:40:08 +0000 |
---|---|---|
committer | aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | 2009-04-05 17:40:08 +0000 |
commit | 8eca6b1bc770982595db2f7207c65051572436cb (patch) | |
tree | 68ca13c0ea9c46ed473bf67aa9cb4e8d525d317c /hw | |
parent | 1da92db280961b3949712609b8771f582dab1f4a (diff) |
Fix oops on 2.6.25 guest (Rusty Russell)
I believe this is behind the following:
https://bugs.edge.launchpad.net/ubuntu/jaunty/+source/linux/+bug/331128
virtio_pci in 2.6.25 didn't do feature negotiation correctly: it acked every
bit. Fortunately, we can detect this.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6975 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'hw')
-rw-r--r-- | hw/virtio-net.c | 16 | ||||
-rw-r--r-- | hw/virtio.c | 9 | ||||
-rw-r--r-- | hw/virtio.h | 3 |
3 files changed, 27 insertions, 1 deletions
diff --git a/hw/virtio-net.c b/hw/virtio-net.c index ad55bb7610..ae9b7d92c7 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -113,6 +113,21 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) return features; } +static uint32_t virtio_net_bad_features(VirtIODevice *vdev) +{ + uint32_t features = 0; + + /* Linux kernel 2.6.25. It understood MAC (as everyone must), + * but also these: */ + features |= (1 << VIRTIO_NET_F_MAC); + features |= (1 << VIRTIO_NET_F_GUEST_CSUM); + features |= (1 << VIRTIO_NET_F_GUEST_TSO4); + features |= (1 << VIRTIO_NET_F_GUEST_TSO6); + features |= (1 << VIRTIO_NET_F_GUEST_ECN); + + return features & virtio_net_get_features(vdev); +} + static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) { VirtIONet *n = to_virtio_net(vdev); @@ -580,6 +595,7 @@ PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn) n->vdev.set_config = virtio_net_set_config; n->vdev.get_features = virtio_net_get_features; n->vdev.set_features = virtio_net_set_features; + n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx); diff --git a/hw/virtio.c b/hw/virtio.c index 8a72d8d216..93a7de6899 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -451,6 +451,13 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) switch (addr) { case VIRTIO_PCI_GUEST_FEATURES: + /* Guest does not negotiate properly? We have to assume nothing. */ + if (val & (1 << VIRTIO_F_BAD_FEATURE)) { + if (vdev->bad_features) + val = vdev->bad_features(vdev); + else + val = 0; + } if (vdev->set_features) vdev->set_features(vdev, val); vdev->features = val; @@ -490,7 +497,7 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) switch (addr) { case VIRTIO_PCI_HOST_FEATURES: ret = vdev->get_features(vdev); - ret |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY); + ret |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY) | (1 << VIRTIO_F_BAD_FEATURE); break; case VIRTIO_PCI_GUEST_FEATURES: ret = vdev->features; diff --git a/hw/virtio.h b/hw/virtio.h index 18c7a1a7c7..cce8a47475 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -32,6 +32,8 @@ /* We notify when the ring is completely used, even if the guest is supressing * callbacks */ #define VIRTIO_F_NOTIFY_ON_EMPTY 24 +/* A guest should never accept this. It implies negotiation is broken. */ +#define VIRTIO_F_BAD_FEATURE 30 /* from Linux's linux/virtio_ring.h */ @@ -82,6 +84,7 @@ struct VirtIODevice size_t config_len; void *config; uint32_t (*get_features)(VirtIODevice *vdev); + uint32_t (*bad_features)(VirtIODevice *vdev); void (*set_features)(VirtIODevice *vdev, uint32_t val); void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config); |