summaryrefslogtreecommitdiffstats
path: root/hw/virtio
diff options
context:
space:
mode:
authorPeter Maydell2014-06-29 19:09:51 +0200
committerPeter Maydell2014-06-29 19:09:51 +0200
commit9328cfd2fe4a7ff86a41b2c26ea33974241d7d4e (patch)
tree3f877873730540d63eb0cf5901aedefaffa4f238 /hw/virtio
parentMerge remote-tracking branch 'remotes/riku/linux-user-for-upstream' into staging (diff)
parenttests: add human format test for string output visitor (diff)
downloadqemu-9328cfd2fe4a7ff86a41b2c26ea33974241d7d4e.tar.gz
qemu-9328cfd2fe4a7ff86a41b2c26ea33974241d7d4e.tar.xz
qemu-9328cfd2fe4a7ff86a41b2c26ea33974241d7d4e.zip
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
pc,vhost,virtio fixes, enhancements virtio bi-endian support new command to resync RTC misc bugfixes and cleanups Signed-off-by: Michael S. Tsirkin <mst@redhat.com> # gpg: Signature made Sun 29 Jun 2014 17:41:13 BST using RSA key ID D28D5469 # gpg: Good signature from "Michael S. Tsirkin <mst@kernel.org>" # gpg: aka "Michael S. Tsirkin <mst@redhat.com>" * remotes/mst/tags/for_upstream: (37 commits) tests: add human format test for string output visitor vhost-net: disable when cross-endian target-ppc: enable virtio endian ambivalent support virtio-9p: use virtio wrappers to access headers virtio-serial-bus: use virtio wrappers to access headers virtio-scsi: use virtio wrappers to access headers virtio-blk: use virtio wrappers to access headers virtio-balloon: use virtio wrappers to access page frame numbers virtio-net: use virtio wrappers to access headers virtio: allow byte swapping for vring virtio: memory accessors for endian-ambivalent targets virtio: add endian-ambivalent support to VirtIODevice cpu: introduce CPUClass::virtio_is_big_endian() exec: introduce target_words_bigendian() helper virtio: add subsections to the migration stream virtio-rng: implement per-device migration calls virtio-balloon: implement per-device migration calls virtio-serial: implement per-device migration calls virtio-blk: implement per-device migration calls virtio-net: implement per-device migration calls ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/virtio')
-rw-r--r--hw/virtio/vhost-user.c23
-rw-r--r--hw/virtio/virtio-balloon.c33
-rw-r--r--hw/virtio/virtio-pci.c11
-rw-r--r--hw/virtio/virtio-rng.c12
-rw-r--r--hw/virtio/virtio.c232
5 files changed, 218 insertions, 93 deletions
diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c
index 0df6a936a0..38e580642f 100644
--- a/hw/virtio/vhost-user.c
+++ b/hw/virtio/vhost-user.c
@@ -14,6 +14,7 @@
#include "sysemu/kvm.h"
#include "qemu/error-report.h"
#include "qemu/sockets.h"
+#include "exec/ram_addr.h"
#include <fcntl.h>
#include <unistd.h>
@@ -47,6 +48,7 @@ typedef struct VhostUserMemoryRegion {
uint64_t guest_phys_addr;
uint64_t memory_size;
uint64_t userspace_addr;
+ uint64_t mmap_offset;
} VhostUserMemoryRegion;
typedef struct VhostUserMemory {
@@ -183,10 +185,10 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
{
VhostUserMsg msg;
VhostUserRequest msg_request;
- RAMBlock *block = 0;
struct vhost_vring_file *file = 0;
int need_reply = 0;
int fds[VHOST_MEMORY_MAX_NREGIONS];
+ int i, fd;
size_t fd_num = 0;
assert(dev->vhost_ops->backend_type == VHOST_BACKEND_TYPE_USER);
@@ -212,14 +214,17 @@ static int vhost_user_call(struct vhost_dev *dev, unsigned long int request,
break;
case VHOST_SET_MEM_TABLE:
- QTAILQ_FOREACH(block, &ram_list.blocks, next)
- {
- if (block->fd > 0) {
- msg.memory.regions[fd_num].userspace_addr =
- (uintptr_t) block->host;
- msg.memory.regions[fd_num].memory_size = block->length;
- msg.memory.regions[fd_num].guest_phys_addr = block->offset;
- fds[fd_num++] = block->fd;
+ for (i = 0; i < dev->mem->nregions; ++i) {
+ struct vhost_memory_region *reg = dev->mem->regions + i;
+ fd = qemu_get_ram_fd(reg->guest_phys_addr);
+ if (fd > 0) {
+ msg.memory.regions[fd_num].userspace_addr = reg->userspace_addr;
+ msg.memory.regions[fd_num].memory_size = reg->memory_size;
+ msg.memory.regions[fd_num].guest_phys_addr = reg->guest_phys_addr;
+ msg.memory.regions[fd_num].mmap_offset = reg->userspace_addr -
+ (uintptr_t) qemu_get_ram_block_host_ptr(reg->guest_phys_addr);
+ assert(fd_num < VHOST_MEMORY_MAX_NREGIONS);
+ fds[fd_num++] = fd;
}
}
diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c
index 2a2e58a297..2c30b3d8bd 100644
--- a/hw/virtio/virtio-balloon.c
+++ b/hw/virtio/virtio-balloon.c
@@ -31,6 +31,7 @@
#endif
#include "hw/virtio/virtio-bus.h"
+#include "hw/virtio/virtio-access.h"
static void balloon_page(void *addr, int deflate)
{
@@ -206,8 +207,9 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq)
while (iov_to_buf(elem.out_sg, elem.out_num, offset, &pfn, 4) == 4) {
ram_addr_t pa;
ram_addr_t addr;
+ int p = virtio_ldl_p(vdev, &pfn);
- pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT;
+ pa = (ram_addr_t) p << VIRTIO_BALLOON_PFN_SHIFT;
offset += 4;
/* FIXME: remove get_system_memory(), but how? */
@@ -248,8 +250,8 @@ static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq)
while (iov_to_buf(elem->out_sg, elem->out_num, offset, &stat, sizeof(stat))
== sizeof(stat)) {
- uint16_t tag = tswap16(stat.tag);
- uint64_t val = tswap64(stat.val);
+ uint16_t tag = virtio_tswap16(vdev, stat.tag);
+ uint64_t val = virtio_tswap64(vdev, stat.val);
offset += sizeof(stat);
if (tag < VIRTIO_BALLOON_S_NR)
@@ -325,10 +327,12 @@ static void virtio_balloon_to_target(void *opaque, ram_addr_t target)
static void virtio_balloon_save(QEMUFile *f, void *opaque)
{
- VirtIOBalloon *s = VIRTIO_BALLOON(opaque);
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
+ virtio_save(VIRTIO_DEVICE(opaque), f);
+}
- virtio_save(vdev, f);
+static void virtio_balloon_save_device(VirtIODevice *vdev, QEMUFile *f)
+{
+ VirtIOBalloon *s = VIRTIO_BALLOON(vdev);
qemu_put_be32(f, s->num_pages);
qemu_put_be32(f, s->actual);
@@ -336,17 +340,16 @@ static void virtio_balloon_save(QEMUFile *f, void *opaque)
static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id)
{
- VirtIOBalloon *s = VIRTIO_BALLOON(opaque);
- VirtIODevice *vdev = VIRTIO_DEVICE(s);
- int ret;
-
if (version_id != 1)
return -EINVAL;
- ret = virtio_load(vdev, f);
- if (ret) {
- return ret;
- }
+ return virtio_load(VIRTIO_DEVICE(opaque), f, version_id);
+}
+
+static int virtio_balloon_load_device(VirtIODevice *vdev, QEMUFile *f,
+ int version_id)
+{
+ VirtIOBalloon *s = VIRTIO_BALLOON(vdev);
s->num_pages = qemu_get_be32(f);
s->actual = qemu_get_be32(f);
@@ -416,6 +419,8 @@ static void virtio_balloon_class_init(ObjectClass *klass, void *data)
vdc->get_config = virtio_balloon_get_config;
vdc->set_config = virtio_balloon_set_config;
vdc->get_features = virtio_balloon_get_features;
+ vdc->save = virtio_balloon_save_device;
+ vdc->load = virtio_balloon_load_device;
}
static const TypeInfo virtio_balloon_info = {
diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c
index 57e1e6141e..317324f23d 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);
@@ -409,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;
@@ -443,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-rng.c b/hw/virtio/virtio-rng.c
index b6ab3610cb..1356aca8d6 100644
--- a/hw/virtio/virtio-rng.c
+++ b/hw/virtio/virtio-rng.c
@@ -107,19 +107,20 @@ static void virtio_rng_save(QEMUFile *f, void *opaque)
static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
{
- VirtIORNG *vrng = opaque;
- VirtIODevice *vdev = VIRTIO_DEVICE(vrng);
-
if (version_id != 1) {
return -EINVAL;
}
- virtio_load(vdev, f);
+ return virtio_load(VIRTIO_DEVICE(opaque), f, version_id);
+}
+static int virtio_rng_load_device(VirtIODevice *vdev, QEMUFile *f,
+ int version_id)
+{
/* We may have an element ready but couldn't process it due to a quota
* limit. Make sure to try again after live migration when the quota may
* have been reset.
*/
- virtio_rng_process(vrng);
+ virtio_rng_process(VIRTIO_RNG(vdev));
return 0;
}
@@ -219,6 +220,7 @@ static void virtio_rng_class_init(ObjectClass *klass, void *data)
vdc->realize = virtio_rng_device_realize;
vdc->unrealize = virtio_rng_device_unrealize;
vdc->get_features = get_features;
+ vdc->load = virtio_rng_load_device;
}
static void virtio_rng_initfn(Object *obj)
diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c
index a3082d569d..5c981801f3 100644
--- a/hw/virtio/virtio.c
+++ b/hw/virtio/virtio.c
@@ -19,6 +19,8 @@
#include "hw/virtio/virtio.h"
#include "qemu/atomic.h"
#include "hw/virtio/virtio-bus.h"
+#include "migration/migration.h"
+#include "hw/virtio/virtio-access.h"
/*
* The alignment to use between consumer and producer parts of vring.
@@ -101,53 +103,56 @@ static void virtqueue_init(VirtQueue *vq)
vq->vring.align);
}
-static inline uint64_t vring_desc_addr(hwaddr desc_pa, int i)
+static inline uint64_t vring_desc_addr(VirtIODevice *vdev, hwaddr desc_pa,
+ int i)
{
hwaddr pa;
pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, addr);
- return ldq_phys(&address_space_memory, pa);
+ return virtio_ldq_phys(vdev, pa);
}
-static inline uint32_t vring_desc_len(hwaddr desc_pa, int i)
+static inline uint32_t vring_desc_len(VirtIODevice *vdev, hwaddr desc_pa, int i)
{
hwaddr pa;
pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, len);
- return ldl_phys(&address_space_memory, pa);
+ return virtio_ldl_phys(vdev, pa);
}
-static inline uint16_t vring_desc_flags(hwaddr desc_pa, int i)
+static inline uint16_t vring_desc_flags(VirtIODevice *vdev, hwaddr desc_pa,
+ int i)
{
hwaddr pa;
pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, flags);
- return lduw_phys(&address_space_memory, pa);
+ return virtio_lduw_phys(vdev, pa);
}
-static inline uint16_t vring_desc_next(hwaddr desc_pa, int i)
+static inline uint16_t vring_desc_next(VirtIODevice *vdev, hwaddr desc_pa,
+ int i)
{
hwaddr pa;
pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, next);
- return lduw_phys(&address_space_memory, pa);
+ return virtio_lduw_phys(vdev, pa);
}
static inline uint16_t vring_avail_flags(VirtQueue *vq)
{
hwaddr pa;
pa = vq->vring.avail + offsetof(VRingAvail, flags);
- return lduw_phys(&address_space_memory, pa);
+ return virtio_lduw_phys(vq->vdev, pa);
}
static inline uint16_t vring_avail_idx(VirtQueue *vq)
{
hwaddr pa;
pa = vq->vring.avail + offsetof(VRingAvail, idx);
- return lduw_phys(&address_space_memory, pa);
+ return virtio_lduw_phys(vq->vdev, pa);
}
static inline uint16_t vring_avail_ring(VirtQueue *vq, int i)
{
hwaddr pa;
pa = vq->vring.avail + offsetof(VRingAvail, ring[i]);
- return lduw_phys(&address_space_memory, pa);
+ return virtio_lduw_phys(vq->vdev, pa);
}
static inline uint16_t vring_used_event(VirtQueue *vq)
@@ -159,44 +164,44 @@ static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val)
{
hwaddr pa;
pa = vq->vring.used + offsetof(VRingUsed, ring[i].id);
- stl_phys(&address_space_memory, pa, val);
+ virtio_stl_phys(vq->vdev, pa, val);
}
static inline void vring_used_ring_len(VirtQueue *vq, int i, uint32_t val)
{
hwaddr pa;
pa = vq->vring.used + offsetof(VRingUsed, ring[i].len);
- stl_phys(&address_space_memory, pa, val);
+ virtio_stl_phys(vq->vdev, pa, val);
}
static uint16_t vring_used_idx(VirtQueue *vq)
{
hwaddr pa;
pa = vq->vring.used + offsetof(VRingUsed, idx);
- return lduw_phys(&address_space_memory, pa);
+ return virtio_lduw_phys(vq->vdev, pa);
}
static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val)
{
hwaddr pa;
pa = vq->vring.used + offsetof(VRingUsed, idx);
- stw_phys(&address_space_memory, pa, val);
+ virtio_stw_phys(vq->vdev, pa, val);
}
static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask)
{
+ VirtIODevice *vdev = vq->vdev;
hwaddr pa;
pa = vq->vring.used + offsetof(VRingUsed, flags);
- stw_phys(&address_space_memory,
- pa, lduw_phys(&address_space_memory, pa) | mask);
+ virtio_stw_phys(vdev, pa, virtio_lduw_phys(vdev, pa) | mask);
}
static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask)
{
+ VirtIODevice *vdev = vq->vdev;
hwaddr pa;
pa = vq->vring.used + offsetof(VRingUsed, flags);
- stw_phys(&address_space_memory,
- pa, lduw_phys(&address_space_memory, pa) & ~mask);
+ virtio_stw_phys(vdev, pa, virtio_lduw_phys(vdev, pa) & ~mask);
}
static inline void vring_avail_event(VirtQueue *vq, uint16_t val)
@@ -206,7 +211,7 @@ static inline void vring_avail_event(VirtQueue *vq, uint16_t val)
return;
}
pa = vq->vring.used + offsetof(VRingUsed, ring[vq->vring.num]);
- stw_phys(&address_space_memory, pa, val);
+ virtio_stw_phys(vq->vdev, pa, val);
}
void virtio_queue_set_notification(VirtQueue *vq, int enable)
@@ -323,17 +328,18 @@ static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx)
return head;
}
-static unsigned virtqueue_next_desc(hwaddr desc_pa,
+static unsigned virtqueue_next_desc(VirtIODevice *vdev, hwaddr desc_pa,
unsigned int i, unsigned int max)
{
unsigned int next;
/* If this descriptor says it doesn't chain, we're done. */
- if (!(vring_desc_flags(desc_pa, i) & VRING_DESC_F_NEXT))
+ if (!(vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_NEXT)) {
return max;
+ }
/* Check they're not leading us off end of descriptors. */
- next = vring_desc_next(desc_pa, i);
+ next = vring_desc_next(vdev, desc_pa, i);
/* Make sure compiler knows to grab that: we don't want it changing! */
smp_wmb();
@@ -356,6 +362,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
total_bufs = in_total = out_total = 0;
while (virtqueue_num_heads(vq, idx)) {
+ VirtIODevice *vdev = vq->vdev;
unsigned int max, num_bufs, indirect = 0;
hwaddr desc_pa;
int i;
@@ -365,8 +372,8 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
i = virtqueue_get_head(vq, idx++);
desc_pa = vq->vring.desc;
- if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) {
- if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) {
+ if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) {
+ if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) {
error_report("Invalid size for indirect buffer table");
exit(1);
}
@@ -379,8 +386,8 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
/* loop over the indirect descriptor table */
indirect = 1;
- max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc);
- desc_pa = vring_desc_addr(desc_pa, i);
+ max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc);
+ desc_pa = vring_desc_addr(vdev, desc_pa, i);
num_bufs = i = 0;
}
@@ -391,15 +398,15 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes,
exit(1);
}
- if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) {
- in_total += vring_desc_len(desc_pa, i);
+ if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) {
+ in_total += vring_desc_len(vdev, desc_pa, i);
} else {
- out_total += vring_desc_len(desc_pa, i);
+ out_total += vring_desc_len(vdev, desc_pa, i);
}
if (in_total >= max_in_bytes && out_total >= max_out_bytes) {
goto done;
}
- } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max);
+ } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max);
if (!indirect)
total_bufs = num_bufs;
@@ -450,6 +457,7 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
{
unsigned int i, head, max;
hwaddr desc_pa = vq->vring.desc;
+ VirtIODevice *vdev = vq->vdev;
if (!virtqueue_num_heads(vq, vq->last_avail_idx))
return 0;
@@ -460,19 +468,19 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
max = vq->vring.num;
i = head = virtqueue_get_head(vq, vq->last_avail_idx++);
- if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) {
+ if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) {
vring_avail_event(vq, vring_avail_idx(vq));
}
- if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_INDIRECT) {
- if (vring_desc_len(desc_pa, i) % sizeof(VRingDesc)) {
+ if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_INDIRECT) {
+ if (vring_desc_len(vdev, desc_pa, i) % sizeof(VRingDesc)) {
error_report("Invalid size for indirect buffer table");
exit(1);
}
/* loop over the indirect descriptor table */
- max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc);
- desc_pa = vring_desc_addr(desc_pa, i);
+ max = vring_desc_len(vdev, desc_pa, i) / sizeof(VRingDesc);
+ desc_pa = vring_desc_addr(vdev, desc_pa, i);
i = 0;
}
@@ -480,30 +488,30 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem)
do {
struct iovec *sg;
- if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) {
+ if (vring_desc_flags(vdev, desc_pa, i) & VRING_DESC_F_WRITE) {
if (elem->in_num >= ARRAY_SIZE(elem->in_sg)) {
error_report("Too many write descriptors in indirect table");
exit(1);
}
- elem->in_addr[elem->in_num] = vring_desc_addr(desc_pa, i);
+ elem->in_addr[elem->in_num] = vring_desc_addr(vdev, desc_pa, i);
sg = &elem->in_sg[elem->in_num++];
} else {
if (elem->out_num >= ARRAY_SIZE(elem->out_sg)) {
error_report("Too many read descriptors in indirect table");
exit(1);
}
- elem->out_addr[elem->out_num] = vring_desc_addr(desc_pa, i);
+ elem->out_addr[elem->out_num] = vring_desc_addr(vdev, desc_pa, i);
sg = &elem->out_sg[elem->out_num++];
}
- sg->iov_len = vring_desc_len(desc_pa, i);
+ sg->iov_len = vring_desc_len(vdev, desc_pa, i);
/* If we've got too many, that implies a descriptor loop. */
if ((elem->in_num + elem->out_num) > max) {
error_report("Looped descriptor");
exit(1);
}
- } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max);
+ } while ((i = virtqueue_next_desc(vdev, desc_pa, i, max)) != max);
/* Now map what we have collected */
virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1);
@@ -544,6 +552,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;
@@ -551,6 +580,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);
@@ -839,10 +875,46 @@ 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,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_END_OF_LIST()
+ },
+ .subsections = (VMStateSubsection[]) {
+ {
+ .vmsd = &vmstate_virtio_device_endian,
+ .needed = &virtio_device_endian_needed
+ },
+ { 0 }
+ }
+};
+
void virtio_save(VirtIODevice *vdev, QEMUFile *f)
{
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
+ VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
int i;
if (k->save_config) {
@@ -877,6 +949,13 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f)
k->save_queue(qbus->parent, i, f);
}
}
+
+ if (vdc->save != NULL) {
+ vdc->save(vdev, f);
+ }
+
+ /* Subsections */
+ vmstate_save_state(f, &vmstate_virtio, vdev);
}
int virtio_set_features(VirtIODevice *vdev, uint32_t val)
@@ -895,7 +974,7 @@ int virtio_set_features(VirtIODevice *vdev, uint32_t val)
return bad ? -1 : 0;
}
-int virtio_load(VirtIODevice *vdev, QEMUFile *f)
+int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id)
{
int i, ret;
int32_t config_len;
@@ -904,6 +983,13 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
uint32_t supported_features;
BusState *qbus = qdev_get_parent_bus(DEVICE(vdev));
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);
@@ -926,12 +1012,18 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
return -1;
}
config_len = qemu_get_be32(f);
- if (config_len != vdev->config_len) {
- error_report("Unexpected config length 0x%x. Expected 0x%zx",
- config_len, vdev->config_len);
- return -1;
+
+ /*
+ * There are cases where the incoming config can be bigger or smaller
+ * than what we have; so load what we have space for, and skip
+ * any excess that's in the stream.
+ */
+ qemu_get_buffer(f, vdev->config, MIN(config_len, vdev->config_len));
+
+ while (config_len > vdev->config_len) {
+ qemu_get_byte(f);
+ config_len--;
}
- qemu_get_buffer(f, vdev->config, vdev->config_len);
num = qemu_get_be32(f);
@@ -951,18 +1043,7 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
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",
@@ -977,6 +1058,40 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f)
}
virtio_notify_vector(vdev, VIRTIO_NO_VECTOR);
+
+ if (vdc->load != NULL) {
+ ret = vdc->load(vdev, f, version_id);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ /* 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;
}
@@ -1034,6 +1149,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)