Legacy virtio is native endian: if the guest and host endianness differ, we have to tell vhost so it can swap bytes where appropriate. This is done through a vhost ring ioctl.
The support for this ioctl is advertised by a vhost feature. QEMU may choose to use it if a cross-endian situation is detected. Of course, virtio 1.0 doesn't need this and will never ack the feature back to vhost. Signed-off-by: Greg Kurz <gk...@linux.vnet.ibm.com> --- hw/virtio/vhost.c | 46 +++++++++++++++++++++++++++++++++++---------- include/hw/virtio/vhost.h | 1 + 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 771abae..db52898 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -548,13 +548,18 @@ static int vhost_virtqueue_set_addr(struct vhost_dev *dev, return 0; } -static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log) +static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log, + bool enable_cross_endian) { uint64_t features = dev->acked_features; int r; if (enable_log) { features |= 0x1ULL << VHOST_F_LOG_ALL; } + if (!enable_cross_endian) { + features &= ~(1ULL << VHOST_F_SET_ENDIAN_LEGACY); + } + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_FEATURES, &features); return r < 0 ? -errno : 0; } @@ -562,7 +567,7 @@ static int vhost_dev_set_features(struct vhost_dev *dev, bool enable_log) static int vhost_dev_set_log(struct vhost_dev *dev, bool enable_log) { int r, t, i; - r = vhost_dev_set_features(dev, enable_log); + r = vhost_dev_set_features(dev, enable_log, dev->cross_endian_enabled); if (r < 0) { goto err_features; } @@ -580,7 +585,7 @@ err_vq: dev->log_enabled); assert(t >= 0); } - t = vhost_dev_set_features(dev, dev->log_enabled); + t = vhost_dev_set_features(dev, dev->log_enabled, dev->cross_endian_enabled); assert(t >= 0); err_features: return r; @@ -650,9 +655,9 @@ static void vhost_log_stop(MemoryListener *listener, } static int vhost_virtqueue_start(struct vhost_dev *dev, - struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, - unsigned idx) + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx) { hwaddr s, l, a; int r; @@ -663,6 +668,9 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, struct vhost_vring_state state = { .index = vhost_vq_index }; + struct vhost_vring_endian endian = { + .index = vhost_vq_index + }; struct VirtQueue *vvq = virtio_get_queue(vdev, idx); assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); @@ -679,6 +687,15 @@ static int vhost_virtqueue_start(struct vhost_dev *dev, return -errno; } + if (dev->cross_endian_enabled) { + endian.is_big_endian = virtio_is_big_endian(vdev); + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_ENDIAN_LEGACY, + &endian); + if (r) { + return -errno; + } + } + s = l = virtio_queue_get_desc_size(vdev, idx); a = virtio_queue_get_desc_addr(vdev, idx); vq->desc = cpu_physical_memory_map(a, &l, 0); @@ -877,6 +894,7 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, hdev->memory_changed = false; memory_listener_register(&hdev->memory_listener, &address_space_memory); hdev->force = force; + hdev->cross_endian_enabled = false; return 0; fail_vq: while (--i >= 0) { @@ -1046,14 +1064,22 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) { int i, r; - if (virtio_legacy_is_cross_endian(vdev)) { - error_report("vhost does not support cross-endian"); - return -ENOSYS; + if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1) && + virtio_legacy_is_cross_endian(vdev)) { + if (hdev->features & (1ULL << VHOST_F_SET_ENDIAN_LEGACY)) { + hdev->cross_endian_enabled = true; + } else { + error_report("vhost does not support cross-endian"); + return -ENOSYS; + } + } else { + hdev->cross_endian_enabled = false; } hdev->started = true; - r = vhost_dev_set_features(hdev, hdev->log_enabled); + r = vhost_dev_set_features(hdev, hdev->log_enabled, + hdev->cross_endian_enabled); if (r < 0) { goto fail_features; } diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 88e1e56..61569bf 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -52,6 +52,7 @@ struct vhost_dev { hwaddr mem_changed_end_addr; const VhostOps *vhost_ops; void *opaque; + bool cross_endian_enabled; }; int vhost_dev_init(struct vhost_dev *hdev, void *opaque,