summary refs log tree commit diff stats
path: root/hw/virtio/virtio.c
diff options
context:
space:
mode:
authorCornelia Huck <cornelia.huck@de.ibm.com>2015-06-04 12:34:15 +0200
committerMichael S. Tsirkin <mst@redhat.com>2015-06-10 18:15:04 +0200
commit0b352fd680e1ca7827ddea47b5e9b603320913b6 (patch)
tree2bceeeada8b354bac0e3be5de94fe5d07783d1d4 /hw/virtio/virtio.c
parent6c0196d702e8482a17638ee79f45ce27cdd1ef5d (diff)
downloadfocaccia-qemu-0b352fd680e1ca7827ddea47b5e9b603320913b6.tar.gz
focaccia-qemu-0b352fd680e1ca7827ddea47b5e9b603320913b6.zip
virtio: allow to fail setting status
virtio-1 allow setting of the FEATURES_OK status bit to fail if
the negotiated feature bits are inconsistent: let's fail
virtio_set_status() in that case and update virtio-ccw to post an
error to the guest.

Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>

Diffstat (limited to 'hw/virtio/virtio.c')
-rw-r--r--hw/virtio/virtio.c24
1 files changed, 23 insertions, 1 deletions
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index 336710009f..8a6ebae368 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -544,15 +544,37 @@ void virtio_update_irq(VirtIODevice *vdev)
     virtio_notify_vector(vdev, VIRTIO_NO_VECTOR);
 }
 
-void virtio_set_status(VirtIODevice *vdev, uint8_t val)
+static int virtio_validate_features(VirtIODevice *vdev)
+{
+    VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
+
+    if (k->validate_features) {
+        return k->validate_features(vdev);
+    } else {
+        return 0;
+    }
+}
+
+int virtio_set_status(VirtIODevice *vdev, uint8_t val)
 {
     VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev);
     trace_virtio_set_status(vdev, val);
 
+    if (virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) {
+        if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) &&
+            val & VIRTIO_CONFIG_S_FEATURES_OK) {
+            int ret = virtio_validate_features(vdev);
+
+            if (ret) {
+                return ret;
+            }
+        }
+    }
     if (k->set_status) {
         k->set_status(vdev, val);
     }
     vdev->status = val;
+    return 0;
 }
 
 bool target_words_bigendian(void);