@@ -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;
}
@@ -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,
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 <gkurz@linux.vnet.ibm.com> --- hw/virtio/vhost.c | 46 +++++++++++++++++++++++++++++++++++---------- include/hw/virtio/vhost.h | 1 + 2 files changed, 37 insertions(+), 10 deletions(-)