vdev->guest_features is not masking features that are not supported by the guest. Fix this by introducing a common wrapper to be used by all virtio bus implementations.
Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> --- hw/s390-virtio-bus.c | 5 +---- hw/syborg_virtio.c | 4 +--- hw/virtio-pci.c | 9 ++------- hw/virtio.c | 24 ++++++++++++++++++------ hw/virtio.h | 1 + 5 files changed, 23 insertions(+), 20 deletions(-) diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c index 0ce6406..c4b9a99 100644 --- a/hw/s390-virtio-bus.c +++ b/hw/s390-virtio-bus.c @@ -254,10 +254,7 @@ void s390_virtio_device_update_status(VirtIOS390Device *dev) /* Update guest supported feature bitmap */ features = bswap32(ldl_be_phys(dev->feat_offs)); - if (vdev->set_features) { - vdev->set_features(vdev, features); - } - vdev->guest_features = features; + virtio_set_features(vdev, features); } VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus) diff --git a/hw/syborg_virtio.c b/hw/syborg_virtio.c index 00c7be8..6de952c 100644 --- a/hw/syborg_virtio.c +++ b/hw/syborg_virtio.c @@ -131,9 +131,7 @@ static void syborg_virtio_writel(void *opaque, target_phys_addr_t offset, } switch (offset >> 2) { case SYBORG_VIRTIO_GUEST_FEATURES: - if (vdev->set_features) - vdev->set_features(vdev, value); - vdev->guest_features = value; + virtio_set_features(vdev, value); break; case SYBORG_VIRTIO_QUEUE_BASE: if (value == 0) diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index ca5923c..64c6a94 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -285,14 +285,9 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) 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 = proxy->host_features & vdev->bad_features(vdev); - else - val = 0; + val = vdev->bad_features ? vdev->bad_features(vdev) : 0; } - if (vdev->set_features) - vdev->set_features(vdev, val); - vdev->guest_features = val; + virtio_set_features(vdev, val); break; case VIRTIO_PCI_QUEUE_PFN: pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; diff --git a/hw/virtio.c b/hw/virtio.c index 7011b5b..81ecc40 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -763,12 +763,25 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) } } +int virtio_set_features(VirtIODevice *vdev, uint32_t val) +{ + uint32_t supported_features = + vdev->binding->get_features(vdev->binding_opaque); + bool bad = (val & ~supported_features) != 0; + + val &= supported_features; + if (vdev->set_features) { + vdev->set_features(vdev, val); + } + vdev->guest_features = val; + return bad ? -1 : 0; +} + int virtio_load(VirtIODevice *vdev, QEMUFile *f) { int num, i, ret; uint32_t features; - uint32_t supported_features = - vdev->binding->get_features(vdev->binding_opaque); + uint32_t supported_features; if (vdev->binding->load_config) { ret = vdev->binding->load_config(vdev->binding_opaque, f); @@ -780,14 +793,13 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f) qemu_get_8s(f, &vdev->isr); qemu_get_be16s(f, &vdev->queue_sel); qemu_get_be32s(f, &features); - if (features & ~supported_features) { + + if (virtio_set_features(vdev, features) < 0) { + supported_features = vdev->binding->get_features(vdev->binding_opaque); error_report("Features 0x%x unsupported. Allowed features: 0x%x", features, supported_features); return -1; } - if (vdev->set_features) - vdev->set_features(vdev, features); - vdev->guest_features = features; vdev->config_len = qemu_get_be32(f); qemu_get_buffer(f, vdev->config, vdev->config_len); diff --git a/hw/virtio.h b/hw/virtio.h index 2d18209..25f5564 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -185,6 +185,7 @@ void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector); void virtio_set_status(VirtIODevice *vdev, uint8_t val); void virtio_reset(void *opaque); void virtio_update_irq(VirtIODevice *vdev); +int virtio_set_features(VirtIODevice *vdev, uint32_t val); void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding, void *opaque); -- 1.7.7.1