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 | 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 | 131 | static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) |
| 117 | 132 | { |
| 118 | 133 | VirtIONet *n = to_virtio_net(vdev); |
| ... | ... | @@ -580,6 +595,7 @@ PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn) |
| 580 | 595 | n->vdev.set_config = virtio_net_set_config; |
| 581 | 596 | n->vdev.get_features = virtio_net_get_features; |
| 582 | 597 | n->vdev.set_features = virtio_net_set_features; |
| 598 | + n->vdev.bad_features = virtio_net_bad_features; | |
| 583 | 599 | n->vdev.reset = virtio_net_reset; |
| 584 | 600 | n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); |
| 585 | 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 | 451 | |
| 452 | 452 | switch (addr) { |
| 453 | 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 | 461 | if (vdev->set_features) |
| 455 | 462 | vdev->set_features(vdev, val); |
| 456 | 463 | vdev->features = val; |
| ... | ... | @@ -490,7 +497,7 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr) |
| 490 | 497 | switch (addr) { |
| 491 | 498 | case VIRTIO_PCI_HOST_FEATURES: |
| 492 | 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 | 501 | break; |
| 495 | 502 | case VIRTIO_PCI_GUEST_FEATURES: |
| 496 | 503 | ret = vdev->features; | ... | ... |
hw/virtio.h
| ... | ... | @@ -32,6 +32,8 @@ |
| 32 | 32 | /* We notify when the ring is completely used, even if the guest is supressing |
| 33 | 33 | * callbacks */ |
| 34 | 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 | 38 | /* from Linux's linux/virtio_ring.h */ |
| 37 | 39 | |
| ... | ... | @@ -82,6 +84,7 @@ struct VirtIODevice |
| 82 | 84 | size_t config_len; |
| 83 | 85 | void *config; |
| 84 | 86 | uint32_t (*get_features)(VirtIODevice *vdev); |
| 87 | + uint32_t (*bad_features)(VirtIODevice *vdev); | |
| 85 | 88 | void (*set_features)(VirtIODevice *vdev, uint32_t val); |
| 86 | 89 | void (*get_config)(VirtIODevice *vdev, uint8_t *config); |
| 87 | 90 | void (*set_config)(VirtIODevice *vdev, const uint8_t *config); | ... | ... |