Commit 8eca6b1bc770982595db2f7207c65051572436cb
1 parent
1da92db2
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
Showing
3 changed files
with
27 additions
and
1 deletions
hw/virtio-net.c
@@ -113,6 +113,21 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) | @@ -113,6 +113,21 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) | ||
113 | return features; | 113 | return features; |
114 | } | 114 | } |
115 | 115 | ||
116 | +static uint32_t virtio_net_bad_features(VirtIODevice *vdev) | ||
117 | +{ | ||
118 | + uint32_t features = 0; | ||
119 | + | ||
120 | + /* Linux kernel 2.6.25. It understood MAC (as everyone must), | ||
121 | + * but also these: */ | ||
122 | + features |= (1 << VIRTIO_NET_F_MAC); | ||
123 | + features |= (1 << VIRTIO_NET_F_GUEST_CSUM); | ||
124 | + features |= (1 << VIRTIO_NET_F_GUEST_TSO4); | ||
125 | + features |= (1 << VIRTIO_NET_F_GUEST_TSO6); | ||
126 | + features |= (1 << VIRTIO_NET_F_GUEST_ECN); | ||
127 | + | ||
128 | + return features & virtio_net_get_features(vdev); | ||
129 | +} | ||
130 | + | ||
116 | static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) | 131 | static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) |
117 | { | 132 | { |
118 | VirtIONet *n = to_virtio_net(vdev); | 133 | VirtIONet *n = to_virtio_net(vdev); |
@@ -580,6 +595,7 @@ PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn) | @@ -580,6 +595,7 @@ PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn) | ||
580 | n->vdev.set_config = virtio_net_set_config; | 595 | n->vdev.set_config = virtio_net_set_config; |
581 | n->vdev.get_features = virtio_net_get_features; | 596 | n->vdev.get_features = virtio_net_get_features; |
582 | n->vdev.set_features = virtio_net_set_features; | 597 | n->vdev.set_features = virtio_net_set_features; |
598 | + n->vdev.bad_features = virtio_net_bad_features; | ||
583 | n->vdev.reset = virtio_net_reset; | 599 | n->vdev.reset = virtio_net_reset; |
584 | n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); | 600 | n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); |
585 | n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx); | 601 | n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx); |
hw/virtio.c
@@ -451,6 +451,13 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) | @@ -451,6 +451,13 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) | ||
451 | 451 | ||
452 | switch (addr) { | 452 | switch (addr) { |
453 | case VIRTIO_PCI_GUEST_FEATURES: | 453 | case VIRTIO_PCI_GUEST_FEATURES: |
454 | + /* Guest does not negotiate properly? We have to assume nothing. */ | ||
455 | + if (val & (1 << VIRTIO_F_BAD_FEATURE)) { | ||
456 | + if (vdev->bad_features) | ||
457 | + val = vdev->bad_features(vdev); | ||
458 | + else | ||
459 | + val = 0; | ||
460 | + } | ||
454 | if (vdev->set_features) | 461 | if (vdev->set_features) |
455 | vdev->set_features(vdev, val); | 462 | vdev->set_features(vdev, val); |
456 | vdev->features = val; | 463 | vdev->features = val; |
@@ -490,7 +497,7 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) | @@ -490,7 +497,7 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) | ||
490 | switch (addr) { | 497 | switch (addr) { |
491 | case VIRTIO_PCI_HOST_FEATURES: | 498 | case VIRTIO_PCI_HOST_FEATURES: |
492 | ret = vdev->get_features(vdev); | 499 | ret = vdev->get_features(vdev); |
493 | - ret |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY); | 500 | + ret |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY) | (1 << VIRTIO_F_BAD_FEATURE); |
494 | break; | 501 | break; |
495 | case VIRTIO_PCI_GUEST_FEATURES: | 502 | case VIRTIO_PCI_GUEST_FEATURES: |
496 | ret = vdev->features; | 503 | ret = vdev->features; |
hw/virtio.h
@@ -32,6 +32,8 @@ | @@ -32,6 +32,8 @@ | ||
32 | /* We notify when the ring is completely used, even if the guest is supressing | 32 | /* We notify when the ring is completely used, even if the guest is supressing |
33 | * callbacks */ | 33 | * callbacks */ |
34 | #define VIRTIO_F_NOTIFY_ON_EMPTY 24 | 34 | #define VIRTIO_F_NOTIFY_ON_EMPTY 24 |
35 | +/* A guest should never accept this. It implies negotiation is broken. */ | ||
36 | +#define VIRTIO_F_BAD_FEATURE 30 | ||
35 | 37 | ||
36 | /* from Linux's linux/virtio_ring.h */ | 38 | /* from Linux's linux/virtio_ring.h */ |
37 | 39 | ||
@@ -82,6 +84,7 @@ struct VirtIODevice | @@ -82,6 +84,7 @@ struct VirtIODevice | ||
82 | size_t config_len; | 84 | size_t config_len; |
83 | void *config; | 85 | void *config; |
84 | uint32_t (*get_features)(VirtIODevice *vdev); | 86 | uint32_t (*get_features)(VirtIODevice *vdev); |
87 | + uint32_t (*bad_features)(VirtIODevice *vdev); | ||
85 | void (*set_features)(VirtIODevice *vdev, uint32_t val); | 88 | void (*set_features)(VirtIODevice *vdev, uint32_t val); |
86 | void (*get_config)(VirtIODevice *vdev, uint8_t *config); | 89 | void (*get_config)(VirtIODevice *vdev, uint8_t *config); |
87 | void (*set_config)(VirtIODevice *vdev, const uint8_t *config); | 90 | void (*set_config)(VirtIODevice *vdev, const uint8_t *config); |