From 1b5fc0dea469976fcbf6dc2e8f2637a2154af2fb Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:15:31 +0200 Subject: virtio: introduce device specific migration calls In order to migrate virtio subsections, they should be streamed after the device itself. We need the device specific code to be called from the common migration code to achieve this. This patch introduces load and save methods for this purpose. Suggested-by: Andreas Färber Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/virtio/virtio.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'include/hw/virtio') diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 3e54e90aad..3505ce511e 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -150,6 +150,8 @@ typedef struct VirtioDeviceClass { * must mask in frontend instead. */ void (*guest_notifier_mask)(VirtIODevice *vdev, int n, bool mask); + void (*save)(VirtIODevice *vdev, QEMUFile *f); + int (*load)(VirtIODevice *vdev, QEMUFile *f, int version_id); } VirtioDeviceClass; void virtio_init(VirtIODevice *vdev, const char *name, @@ -184,7 +186,7 @@ void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); void virtio_save(VirtIODevice *vdev, QEMUFile *f); -int virtio_load(VirtIODevice *vdev, QEMUFile *f); +int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id); void virtio_notify_config(VirtIODevice *vdev); -- cgit v1.2.3-55-g7522 From 98ed8ecfc9dd9e22e4251251492f062dde32c3c4 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:26:29 +0200 Subject: exec: introduce target_words_bigendian() helper We currently have a virtio_is_big_endian() helper that provides the target endianness to the virtio code. As of today, the helper returns a fixed compile-time value. Of course, this will have to change if we want to support target endianness changes at run-time. Let's move the TARGET_WORDS_BIGENDIAN bits out to a new helper and have virtio_is_big_endian() implemented on top of it. This patch doesn't change any functionality. Signed-off-by: Greg Kurz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- exec.c | 8 ++------ hw/virtio/virtio-pci.c | 3 --- include/hw/virtio/virtio.h | 6 ++++++ 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'include/hw/virtio') diff --git a/exec.c b/exec.c index a94c5832f4..18d6c35942 100644 --- a/exec.c +++ b/exec.c @@ -2759,14 +2759,12 @@ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, } #endif -#if !defined(CONFIG_USER_ONLY) - /* * A helper function for the _utterly broken_ virtio device model to find out if * it's running on a big endian machine. Don't do this at home kids! */ -bool virtio_is_big_endian(void); -bool virtio_is_big_endian(void) +bool target_words_bigendian(void); +bool target_words_bigendian(void) { #if defined(TARGET_WORDS_BIGENDIAN) return true; @@ -2775,8 +2773,6 @@ bool virtio_is_big_endian(void) #endif } -#endif - #ifndef CONFIG_USER_ONLY bool cpu_physical_memory_is_io(hwaddr phys_addr) { diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 57e1e6141e..e11f759e94 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -89,9 +89,6 @@ /* Flags track per-device state like workarounds for quirks in older guests. */ #define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0) -/* HACK for virtio to determine if it's running a big endian guest */ -bool virtio_is_big_endian(void); - static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, VirtIOPCIProxy *dev); diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 3505ce511e..9000ee2f50 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -255,4 +255,10 @@ void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, bool set_handler); void virtio_queue_notify_vq(VirtQueue *vq); void virtio_irq(VirtQueue *vq); + +bool target_words_bigendian(void); +static inline bool virtio_is_big_endian(void) +{ + return target_words_bigendian(); +} #endif -- cgit v1.2.3-55-g7522 From 616a655219a92ae7cf5d6a7862e6489c6282009e Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:38:54 +0200 Subject: virtio: add endian-ambivalent support to VirtIODevice Some CPU families can dynamically change their endianness. This means we can have little endian ppc or big endian arm guests for example. This has an impact on legacy virtio data structures since they are target endian. We hence introduce a new property to track the endianness of each virtio device. It is reasonnably assumed that endianness won't change while the device is in use : we hence capture the device endianness when it gets reset. We migrate this property in a subsection, after the device descriptor. This means the load code must not rely on it until it is restored. As a consequence, the vring sanity checks had to be moved after the call to vmstate_load_state(). We enforce paranoia by poisoning the property at the begining of virtio_load(). Signed-off-by: Greg Kurz Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 8 ++-- hw/virtio/virtio.c | 99 ++++++++++++++++++++++++++++++++++++++++------ include/hw/virtio/virtio.h | 13 ++++-- 3 files changed, 101 insertions(+), 19 deletions(-) (limited to 'include/hw/virtio') diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index e11f759e94..317324f23d 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -406,13 +406,13 @@ static uint64_t virtio_pci_config_read(void *opaque, hwaddr addr, break; case 2: val = virtio_config_readw(vdev, addr); - if (virtio_is_big_endian()) { + if (virtio_is_big_endian(vdev)) { val = bswap16(val); } break; case 4: val = virtio_config_readl(vdev, addr); - if (virtio_is_big_endian()) { + if (virtio_is_big_endian(vdev)) { val = bswap32(val); } break; @@ -440,13 +440,13 @@ static void virtio_pci_config_write(void *opaque, hwaddr addr, virtio_config_writeb(vdev, addr, val); break; case 2: - if (virtio_is_big_endian()) { + if (virtio_is_big_endian(vdev)) { val = bswap16(val); } virtio_config_writew(vdev, addr, val); break; case 4: - if (virtio_is_big_endian()) { + if (virtio_is_big_endian(vdev)) { val = bswap32(val); } virtio_config_writel(vdev, addr, val); diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 7b317ce279..a0676e06af 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -545,6 +545,27 @@ void virtio_set_status(VirtIODevice *vdev, uint8_t val) vdev->status = val; } +bool target_words_bigendian(void); +static enum virtio_device_endian virtio_default_endian(void) +{ + if (target_words_bigendian()) { + return VIRTIO_DEVICE_ENDIAN_BIG; + } else { + return VIRTIO_DEVICE_ENDIAN_LITTLE; + } +} + +static enum virtio_device_endian virtio_current_cpu_endian(void) +{ + CPUClass *cc = CPU_GET_CLASS(current_cpu); + + if (cc->virtio_is_big_endian(current_cpu)) { + return VIRTIO_DEVICE_ENDIAN_BIG; + } else { + return VIRTIO_DEVICE_ENDIAN_LITTLE; + } +} + void virtio_reset(void *opaque) { VirtIODevice *vdev = opaque; @@ -552,6 +573,13 @@ void virtio_reset(void *opaque) int i; virtio_set_status(vdev, 0); + if (current_cpu) { + /* Guest initiated reset */ + vdev->device_endian = virtio_current_cpu_endian(); + } else { + /* System reset */ + vdev->device_endian = virtio_default_endian(); + } if (k->reset) { k->reset(vdev); @@ -840,6 +868,24 @@ void virtio_notify_config(VirtIODevice *vdev) virtio_notify_vector(vdev, vdev->config_vector); } +static bool virtio_device_endian_needed(void *opaque) +{ + VirtIODevice *vdev = opaque; + + assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); + return vdev->device_endian != virtio_default_endian(); +} + +static const VMStateDescription vmstate_virtio_device_endian = { + .name = "virtio/device_endian", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(device_endian, VirtIODevice), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_virtio = { .name = "virtio", .version_id = 1, @@ -847,6 +893,13 @@ static const VMStateDescription vmstate_virtio = { .minimum_version_id_old = 1, .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() + }, + .subsections = (VMStateSubsection[]) { + { + .vmsd = &vmstate_virtio_device_endian, + .needed = &virtio_device_endian_needed + }, + { 0 } } }; @@ -925,6 +978,12 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev); + /* + * We poison the endianness to ensure it does not get used before + * subsections have been loaded. + */ + vdev->device_endian = VIRTIO_DEVICE_ENDIAN_UNKNOWN; + if (k->load_config) { ret = k->load_config(qbus->parent, f); if (ret) @@ -977,18 +1036,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) vdev->vq[i].notification = true; if (vdev->vq[i].pa) { - uint16_t nheads; virtqueue_init(&vdev->vq[i]); - nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx; - /* Check it isn't doing very strange things with descriptor numbers. */ - if (nheads > vdev->vq[i].vring.num) { - error_report("VQ %d size 0x%x Guest index 0x%x " - "inconsistent with Host index 0x%x: delta 0x%x", - i, vdev->vq[i].vring.num, - vring_avail_idx(&vdev->vq[i]), - vdev->vq[i].last_avail_idx, nheads); - return -1; - } } else if (vdev->vq[i].last_avail_idx) { error_report("VQ %d address 0x0 " "inconsistent with Host index 0x%x", @@ -1011,7 +1059,33 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) } } - return vmstate_load_state(f, &vmstate_virtio, vdev, 1); + /* Subsections */ + ret = vmstate_load_state(f, &vmstate_virtio, vdev, 1); + if (ret) { + return ret; + } + + if (vdev->device_endian == VIRTIO_DEVICE_ENDIAN_UNKNOWN) { + vdev->device_endian = virtio_default_endian(); + } + + for (i = 0; i < num; i++) { + if (vdev->vq[i].pa) { + uint16_t nheads; + nheads = vring_avail_idx(&vdev->vq[i]) - vdev->vq[i].last_avail_idx; + /* Check it isn't doing strange things with descriptor numbers. */ + if (nheads > vdev->vq[i].vring.num) { + error_report("VQ %d size 0x%x Guest index 0x%x " + "inconsistent with Host index 0x%x: delta 0x%x", + i, vdev->vq[i].vring.num, + vring_avail_idx(&vdev->vq[i]), + vdev->vq[i].last_avail_idx, nheads); + return -1; + } + } + } + + return 0; } void virtio_cleanup(VirtIODevice *vdev) @@ -1068,6 +1142,7 @@ void virtio_init(VirtIODevice *vdev, const char *name, } vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, vdev); + vdev->device_endian = virtio_default_endian(); } hwaddr virtio_queue_get_desc_addr(VirtIODevice *vdev, int n) diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 9000ee2f50..a60104ca24 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -104,6 +104,12 @@ typedef struct VirtQueueElement #define VIRTIO_DEVICE(obj) \ OBJECT_CHECK(VirtIODevice, (obj), TYPE_VIRTIO_DEVICE) +enum virtio_device_endian { + VIRTIO_DEVICE_ENDIAN_UNKNOWN, + VIRTIO_DEVICE_ENDIAN_LITTLE, + VIRTIO_DEVICE_ENDIAN_BIG, +}; + struct VirtIODevice { DeviceState parent_obj; @@ -121,6 +127,7 @@ struct VirtIODevice bool vm_running; VMChangeStateEntry *vmstate; char *bus_name; + uint8_t device_endian; }; typedef struct VirtioDeviceClass { @@ -256,9 +263,9 @@ void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, void virtio_queue_notify_vq(VirtQueue *vq); void virtio_irq(VirtQueue *vq); -bool target_words_bigendian(void); -static inline bool virtio_is_big_endian(void) +static inline bool virtio_is_big_endian(VirtIODevice *vdev) { - return target_words_bigendian(); + assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); + return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; } #endif -- cgit v1.2.3-55-g7522 From 0f5d1d2a49778863db54b4b1ac2dc008a8f21f11 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Tue, 24 Jun 2014 19:39:55 +0200 Subject: virtio: memory accessors for endian-ambivalent targets This is the virtio-access.h header file taken from Rusty's "endian-ambivalent targets using legacy virtio" patch. It introduces helpers that should be used when accessing vring data or by drivers for data that contains headers. The virtio config space is also target endian, but the current code already handles that with the virtio_is_big_endian() helper. There is no obvious benefit at using the virtio accessors in this case. Now we have two distinct paths: a fast inline one for fixed endian targets, and a slow out-of-line one for targets that define the new TARGET_IS_BIENDIAN macro. Signed-off-by: Rusty Russell [ relicensed virtio-access.h to GPLv2+ on Rusty's request, pass &address_space_memory to physical memory accessors, per-device endianness, virtio tswap16 and tswap64 helpers, faspath for fixed endian targets, Greg Kurz ] Cc: Cédric Le Goater Signed-off-by: Greg Kurz Reviewed-by: Alexander Graf Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/virtio/virtio-access.h | 170 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 include/hw/virtio/virtio-access.h (limited to 'include/hw/virtio') diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h new file mode 100644 index 0000000000..46456fd9da --- /dev/null +++ b/include/hw/virtio/virtio-access.h @@ -0,0 +1,170 @@ +/* + * Virtio Accessor Support: In case your target can change endian. + * + * Copyright IBM, Corp. 2013 + * + * Authors: + * Rusty Russell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + */ +#ifndef _QEMU_VIRTIO_ACCESS_H +#define _QEMU_VIRTIO_ACCESS_H +#include "hw/virtio/virtio.h" +#include "exec/address-spaces.h" + +static inline bool virtio_access_is_big_endian(VirtIODevice *vdev) +{ +#if defined(TARGET_IS_BIENDIAN) + return virtio_is_big_endian(vdev); +#elif defined(TARGET_WORDS_BIGENDIAN) + return true; +#else + return false; +#endif +} + +static inline uint16_t virtio_lduw_phys(VirtIODevice *vdev, hwaddr pa) +{ + if (virtio_access_is_big_endian(vdev)) { + return lduw_be_phys(&address_space_memory, pa); + } + return lduw_le_phys(&address_space_memory, pa); +} + +static inline uint32_t virtio_ldl_phys(VirtIODevice *vdev, hwaddr pa) +{ + if (virtio_access_is_big_endian(vdev)) { + return ldl_be_phys(&address_space_memory, pa); + } + return ldl_le_phys(&address_space_memory, pa); +} + +static inline uint64_t virtio_ldq_phys(VirtIODevice *vdev, hwaddr pa) +{ + if (virtio_access_is_big_endian(vdev)) { + return ldq_be_phys(&address_space_memory, pa); + } + return ldq_le_phys(&address_space_memory, pa); +} + +static inline void virtio_stw_phys(VirtIODevice *vdev, hwaddr pa, + uint16_t value) +{ + if (virtio_access_is_big_endian(vdev)) { + stw_be_phys(&address_space_memory, pa, value); + } else { + stw_le_phys(&address_space_memory, pa, value); + } +} + +static inline void virtio_stl_phys(VirtIODevice *vdev, hwaddr pa, + uint32_t value) +{ + if (virtio_access_is_big_endian(vdev)) { + stl_be_phys(&address_space_memory, pa, value); + } else { + stl_le_phys(&address_space_memory, pa, value); + } +} + +static inline void virtio_stw_p(VirtIODevice *vdev, void *ptr, uint16_t v) +{ + if (virtio_access_is_big_endian(vdev)) { + stw_be_p(ptr, v); + } else { + stw_le_p(ptr, v); + } +} + +static inline void virtio_stl_p(VirtIODevice *vdev, void *ptr, uint32_t v) +{ + if (virtio_access_is_big_endian(vdev)) { + stl_be_p(ptr, v); + } else { + stl_le_p(ptr, v); + } +} + +static inline void virtio_stq_p(VirtIODevice *vdev, void *ptr, uint64_t v) +{ + if (virtio_access_is_big_endian(vdev)) { + stq_be_p(ptr, v); + } else { + stq_le_p(ptr, v); + } +} + +static inline int virtio_lduw_p(VirtIODevice *vdev, const void *ptr) +{ + if (virtio_access_is_big_endian(vdev)) { + return lduw_be_p(ptr); + } else { + return lduw_le_p(ptr); + } +} + +static inline int virtio_ldl_p(VirtIODevice *vdev, const void *ptr) +{ + if (virtio_access_is_big_endian(vdev)) { + return ldl_be_p(ptr); + } else { + return ldl_le_p(ptr); + } +} + +static inline uint64_t virtio_ldq_p(VirtIODevice *vdev, const void *ptr) +{ + if (virtio_access_is_big_endian(vdev)) { + return ldq_be_p(ptr); + } else { + return ldq_le_p(ptr); + } +} + +static inline uint16_t virtio_tswap16(VirtIODevice *vdev, uint16_t s) +{ +#ifdef HOST_WORDS_BIGENDIAN + return virtio_access_is_big_endian(vdev) ? s : bswap16(s); +#else + return virtio_access_is_big_endian(vdev) ? bswap16(s) : s; +#endif +} + +static inline void virtio_tswap16s(VirtIODevice *vdev, uint16_t *s) +{ + *s = virtio_tswap16(vdev, *s); +} + +static inline uint32_t virtio_tswap32(VirtIODevice *vdev, uint32_t s) +{ +#ifdef HOST_WORDS_BIGENDIAN + return virtio_access_is_big_endian(vdev) ? s : bswap32(s); +#else + return virtio_access_is_big_endian(vdev) ? bswap32(s) : s; +#endif +} + +static inline void virtio_tswap32s(VirtIODevice *vdev, uint32_t *s) +{ + *s = virtio_tswap32(vdev, *s); +} + +static inline uint64_t virtio_tswap64(VirtIODevice *vdev, uint64_t s) +{ +#ifdef HOST_WORDS_BIGENDIAN + return virtio_access_is_big_endian(vdev) ? s : bswap64(s); +#else + return virtio_access_is_big_endian(vdev) ? bswap64(s) : s; +#endif +} + +static inline void virtio_tswap64s(VirtIODevice *vdev, uint64_t *s) +{ + *s = virtio_tswap64(vdev, *s); +} +#endif /* _QEMU_VIRTIO_ACCESS_H */ -- cgit v1.2.3-55-g7522