diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/9pfs/virtio-9p-device.c | 3 | ||||
-rw-r--r-- | hw/block/virtio-blk.c | 62 | ||||
-rw-r--r-- | hw/char/virtio-serial-bus.c | 94 | ||||
-rw-r--r-- | hw/i386/pc_piix.c | 10 | ||||
-rw-r--r-- | hw/i386/pc_q35.c | 10 | ||||
-rw-r--r-- | hw/net/vhost_net.c | 19 | ||||
-rw-r--r-- | hw/net/virtio-net.c | 56 | ||||
-rw-r--r-- | hw/scsi/virtio-scsi.c | 40 | ||||
-rw-r--r-- | hw/timer/mc146818rtc.c | 18 | ||||
-rw-r--r-- | hw/virtio/vhost-user.c | 23 | ||||
-rw-r--r-- | hw/virtio/virtio-balloon.c | 33 | ||||
-rw-r--r-- | hw/virtio/virtio-pci.c | 11 | ||||
-rw-r--r-- | hw/virtio/virtio-rng.c | 12 | ||||
-rw-r--r-- | hw/virtio/virtio.c | 232 |
14 files changed, 429 insertions, 194 deletions
diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 653762af1a..2572747629 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -19,6 +19,7 @@ #include "fsdev/qemu-fsdev.h" #include "virtio-9p-xattr.h" #include "virtio-9p-coth.h" +#include "hw/virtio/virtio-access.h" static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) { @@ -34,7 +35,7 @@ static void virtio_9p_get_config(VirtIODevice *vdev, uint8_t *config) len = strlen(s->tag); cfg = g_malloc0(sizeof(struct virtio_9p_config) + len); - stw_p(&cfg->tag_len, len); + virtio_stw_p(vdev, &cfg->tag_len, len); /* We don't copy the terminating null to config space */ memcpy(cfg->tag, s->tag, len); memcpy(config, cfg, s->config_size); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index a222e3f9a4..e59ebc962d 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -27,6 +27,7 @@ # include <scsi/sg.h> #endif #include "hw/virtio/virtio-bus.h" +#include "hw/virtio/virtio-access.h" static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) { @@ -88,7 +89,8 @@ static void virtio_blk_rw_complete(void *opaque, int ret) trace_virtio_blk_rw_complete(req, ret); if (ret) { - bool is_read = !(ldl_p(&req->out.type) & VIRTIO_BLK_T_OUT); + int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); + bool is_read = !(p & VIRTIO_BLK_T_OUT); if (virtio_blk_handle_rw_error(req, -ret, is_read)) return; } @@ -130,6 +132,8 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk, { int status = VIRTIO_BLK_S_OK; struct virtio_scsi_inhdr *scsi = NULL; + VirtIODevice *vdev = VIRTIO_DEVICE(blk); + #ifdef __linux__ int i; struct sg_io_hdr hdr; @@ -224,12 +228,12 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk, hdr.status = CHECK_CONDITION; } - stl_p(&scsi->errors, - hdr.status | (hdr.msg_status << 8) | - (hdr.host_status << 16) | (hdr.driver_status << 24)); - stl_p(&scsi->residual, hdr.resid); - stl_p(&scsi->sense_len, hdr.sb_len_wr); - stl_p(&scsi->data_len, hdr.dxfer_len); + virtio_stl_p(vdev, &scsi->errors, + hdr.status | (hdr.msg_status << 8) | + (hdr.host_status << 16) | (hdr.driver_status << 24)); + virtio_stl_p(vdev, &scsi->residual, hdr.resid); + virtio_stl_p(vdev, &scsi->sense_len, hdr.sb_len_wr); + virtio_stl_p(vdev, &scsi->data_len, hdr.dxfer_len); return status; #else @@ -239,7 +243,7 @@ int virtio_blk_handle_scsi_req(VirtIOBlock *blk, fail: /* Just put anything nonzero so that the ioctl fails in the guest. */ if (scsi) { - stl_p(&scsi->errors, 255); + virtio_stl_p(vdev, &scsi->errors, 255); } return status; } @@ -289,7 +293,7 @@ static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) BlockRequest *blkreq; uint64_t sector; - sector = ldq_p(&req->out.sector); + sector = virtio_ldq_p(VIRTIO_DEVICE(req->dev), &req->out.sector); bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_WRITE); @@ -323,7 +327,7 @@ static void virtio_blk_handle_read(VirtIOBlockReq *req) { uint64_t sector; - sector = ldq_p(&req->out.sector); + sector = virtio_ldq_p(VIRTIO_DEVICE(req->dev), &req->out.sector); bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_READ); @@ -374,7 +378,7 @@ void virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) - sizeof(struct virtio_blk_inhdr); iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); - type = ldl_p(&req->out.type); + type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); if (type & VIRTIO_BLK_T_FLUSH) { virtio_blk_handle_flush(req, mrb); @@ -504,12 +508,12 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) bdrv_get_geometry(s->bs, &capacity); memset(&blkcfg, 0, sizeof(blkcfg)); - stq_p(&blkcfg.capacity, capacity); - stl_p(&blkcfg.seg_max, 128 - 2); - stw_p(&blkcfg.cylinders, s->conf->cyls); - stl_p(&blkcfg.blk_size, blk_size); - stw_p(&blkcfg.min_io_size, s->conf->min_io_size / blk_size); - stw_p(&blkcfg.opt_io_size, s->conf->opt_io_size / blk_size); + virtio_stq_p(vdev, &blkcfg.capacity, capacity); + virtio_stl_p(vdev, &blkcfg.seg_max, 128 - 2); + virtio_stw_p(vdev, &blkcfg.cylinders, s->conf->cyls); + virtio_stl_p(vdev, &blkcfg.blk_size, blk_size); + virtio_stw_p(vdev, &blkcfg.min_io_size, s->conf->min_io_size / blk_size); + virtio_stw_p(vdev, &blkcfg.opt_io_size, s->conf->opt_io_size / blk_size); blkcfg.heads = s->conf->heads; /* * We must ensure that the block device capacity is a multiple of @@ -611,12 +615,16 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) static void virtio_blk_save(QEMUFile *f, void *opaque) { - VirtIOBlock *s = opaque; - VirtIODevice *vdev = VIRTIO_DEVICE(s); - VirtIOBlockReq *req = s->rq; + VirtIODevice *vdev = VIRTIO_DEVICE(opaque); virtio_save(vdev, f); +} +static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) +{ + VirtIOBlock *s = VIRTIO_BLK(vdev); + VirtIOBlockReq *req = s->rq; + while (req) { qemu_put_sbyte(f, 1); qemu_put_buffer(f, (unsigned char *)req->elem, @@ -630,15 +638,17 @@ static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) { VirtIOBlock *s = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(s); - int ret; if (version_id != 2) return -EINVAL; - ret = virtio_load(vdev, f); - if (ret) { - return ret; - } + return virtio_load(vdev, f, version_id); +} + +static int virtio_blk_load_device(VirtIODevice *vdev, QEMUFile *f, + int version_id) +{ + VirtIOBlock *s = VIRTIO_BLK(vdev); while (qemu_get_sbyte(f)) { VirtIOBlockReq *req = virtio_blk_alloc_request(s); @@ -799,6 +809,8 @@ static void virtio_blk_class_init(ObjectClass *klass, void *data) vdc->get_features = virtio_blk_get_features; vdc->set_status = virtio_blk_set_status; vdc->reset = virtio_blk_reset; + vdc->save = virtio_blk_save_device; + vdc->load = virtio_blk_load_device; } static const TypeInfo virtio_device_info = { diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index a2958ff02f..07bebc03ac 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -24,6 +24,7 @@ #include "hw/sysbus.h" #include "trace.h" #include "hw/virtio/virtio-serial.h" +#include "hw/virtio/virtio-access.h" static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id) { @@ -183,11 +184,12 @@ static size_t send_control_msg(VirtIOSerial *vser, void *buf, size_t len) static size_t send_control_event(VirtIOSerial *vser, uint32_t port_id, uint16_t event, uint16_t value) { + VirtIODevice *vdev = VIRTIO_DEVICE(vser); struct virtio_console_control cpkt; - stl_p(&cpkt.id, port_id); - stw_p(&cpkt.event, event); - stw_p(&cpkt.value, value); + virtio_stl_p(vdev, &cpkt.id, port_id); + virtio_stw_p(vdev, &cpkt.event, event); + virtio_stw_p(vdev, &cpkt.value, value); trace_virtio_serial_send_control_event(port_id, event, value); return send_control_msg(vser, &cpkt, sizeof(cpkt)); @@ -278,6 +280,7 @@ void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) /* Guest wants to notify us of some event */ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) { + VirtIODevice *vdev = VIRTIO_DEVICE(vser); struct VirtIOSerialPort *port; VirtIOSerialPortClass *vsc; struct virtio_console_control cpkt, *gcpkt; @@ -291,8 +294,8 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) return; } - cpkt.event = lduw_p(&gcpkt->event); - cpkt.value = lduw_p(&gcpkt->value); + cpkt.event = virtio_lduw_p(vdev, &gcpkt->event); + cpkt.value = virtio_lduw_p(vdev, &gcpkt->value); trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); @@ -312,10 +315,10 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) return; } - port = find_port_by_id(vser, ldl_p(&gcpkt->id)); + port = find_port_by_id(vser, virtio_ldl_p(vdev, &gcpkt->id)); if (!port) { error_report("virtio-serial-bus: Unexpected port id %u for device %s", - ldl_p(&gcpkt->id), vser->bus.qbus.name); + virtio_ldl_p(vdev, &gcpkt->id), vser->bus.qbus.name); return; } @@ -342,9 +345,9 @@ static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) } if (port->name) { - stl_p(&cpkt.id, port->id); - stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME); - stw_p(&cpkt.value, 1); + virtio_stl_p(vdev, &cpkt.id, port->id); + virtio_stw_p(vdev, &cpkt.event, VIRTIO_CONSOLE_PORT_NAME); + virtio_stw_p(vdev, &cpkt.value, 1); buffer_len = sizeof(cpkt) + strlen(port->name) + 1; buffer = g_malloc(buffer_len); @@ -510,18 +513,25 @@ static void vser_reset(VirtIODevice *vdev) vser = VIRTIO_SERIAL(vdev); guest_reset(vser); + + /* In case we have switched endianness */ + vser->config.max_nr_ports = + virtio_tswap32(vdev, vser->serial.max_virtserial_ports); } static void virtio_serial_save(QEMUFile *f, void *opaque) { - VirtIOSerial *s = VIRTIO_SERIAL(opaque); + /* The virtio device */ + virtio_save(VIRTIO_DEVICE(opaque), f); +} + +static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) +{ + VirtIOSerial *s = VIRTIO_SERIAL(vdev); VirtIOSerialPort *port; uint32_t nr_active_ports; unsigned int i, max_nr_ports; - /* The virtio device */ - virtio_save(VIRTIO_DEVICE(s), f); - /* The config space */ qemu_put_be16s(f, &s->config.cols); qemu_put_be16s(f, &s->config.rows); @@ -529,7 +539,7 @@ static void virtio_serial_save(QEMUFile *f, void *opaque) qemu_put_be32s(f, &s->config.max_nr_ports); /* The ports map */ - max_nr_ports = tswap32(s->config.max_nr_ports); + max_nr_ports = virtio_tswap32(vdev, s->config.max_nr_ports); for (i = 0; i < (max_nr_ports + 31) / 32; i++) { qemu_put_be32s(f, &s->ports_map[i]); } @@ -659,36 +669,39 @@ static int fetch_active_ports_list(QEMUFile *f, int version_id, static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) { - VirtIOSerial *s = VIRTIO_SERIAL(opaque); - uint32_t max_nr_ports, nr_active_ports, ports_map; - unsigned int i; - int ret; - if (version_id > 3) { return -EINVAL; } /* The virtio device */ - ret = virtio_load(VIRTIO_DEVICE(s), f); - if (ret) { - return ret; - } + return virtio_load(VIRTIO_DEVICE(opaque), f, version_id); +} + +static int virtio_serial_load_device(VirtIODevice *vdev, QEMUFile *f, + int version_id) +{ + VirtIOSerial *s = VIRTIO_SERIAL(vdev); + uint32_t max_nr_ports, nr_active_ports, ports_map; + unsigned int i; + int ret; + uint32_t tmp; if (version_id < 2) { return 0; } - /* The config space */ - qemu_get_be16s(f, &s->config.cols); - qemu_get_be16s(f, &s->config.rows); - - qemu_get_be32s(f, &max_nr_ports); - tswap32s(&max_nr_ports); - if (max_nr_ports > tswap32(s->config.max_nr_ports)) { - /* Source could have had more ports than us. Fail migration. */ - return -EINVAL; - } + /* Unused */ + qemu_get_be16s(f, (uint16_t *) &tmp); + qemu_get_be16s(f, (uint16_t *) &tmp); + qemu_get_be32s(f, &tmp); + /* Note: this is the only location where we use tswap32() instead of + * virtio_tswap32() because: + * - virtio_tswap32() only makes sense when the device is fully restored + * - the target endianness that was used to populate s->config is + * necessarly the default one + */ + max_nr_ports = tswap32(s->config.max_nr_ports); for (i = 0; i < (max_nr_ports + 31) / 32; i++) { qemu_get_be32s(f, &ports_map); @@ -751,9 +764,10 @@ static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) /* This function is only used if a port id is not provided by the user */ static uint32_t find_free_port_id(VirtIOSerial *vser) { + VirtIODevice *vdev = VIRTIO_DEVICE(vser); unsigned int i, max_nr_ports; - max_nr_ports = tswap32(vser->config.max_nr_ports); + max_nr_ports = virtio_tswap32(vdev, vser->config.max_nr_ports); for (i = 0; i < (max_nr_ports + 31) / 32; i++) { uint32_t map, bit; @@ -806,6 +820,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) VirtIOSerialPort *port = VIRTIO_SERIAL_PORT(dev); VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); VirtIOSerialBus *bus = VIRTIO_SERIAL_BUS(qdev_get_parent_bus(dev)); + VirtIODevice *vdev = VIRTIO_DEVICE(bus->vser); int max_nr_ports; bool plugging_port0; Error *err = NULL; @@ -841,7 +856,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) } } - max_nr_ports = tswap32(port->vser->config.max_nr_ports); + max_nr_ports = virtio_tswap32(vdev, port->vser->config.max_nr_ports); if (port->id >= max_nr_ports) { error_setg(errp, "virtio-serial-bus: Out-of-range port id specified, " "max. allowed: %u", max_nr_ports - 1); @@ -863,7 +878,7 @@ static void virtser_port_device_realize(DeviceState *dev, Error **errp) add_port(port->vser, port->id); /* Send an update to the guest about this new port added */ - virtio_notify_config(VIRTIO_DEVICE(port->vser)); + virtio_notify_config(vdev); } static void virtser_port_device_unrealize(DeviceState *dev, Error **errp) @@ -942,7 +957,8 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output); } - vser->config.max_nr_ports = tswap32(vser->serial.max_virtserial_ports); + vser->config.max_nr_ports = + virtio_tswap32(vdev, vser->serial.max_virtserial_ports); vser->ports_map = g_malloc0(((vser->serial.max_virtserial_ports + 31) / 32) * sizeof(vser->ports_map[0])); /* @@ -1019,6 +1035,8 @@ static void virtio_serial_class_init(ObjectClass *klass, void *data) vdc->get_config = get_config; vdc->set_status = set_status; vdc->reset = vser_reset; + vdc->save = virtio_serial_save_device; + vdc->load = virtio_serial_load_device; } static const TypeInfo virtio_device_info = { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 47546b72ae..2dccb3401b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -392,6 +392,11 @@ static void pc_init_pci_no_kvmclock(MachineState *machine) has_pci_info = false; has_acpi_build = false; smbios_defaults = false; + gigabyte_align = false; + smbios_legacy_mode = true; + has_reserved_memory = false; + option_rom_has_mr = true; + rom_file_has_mr = false; x86_cpu_compat_disable_kvm_features(FEAT_KVM, KVM_FEATURE_PV_EOI); enable_compat_apic_id_mode(); pc_init1(machine, 1, 0); @@ -402,6 +407,11 @@ static void pc_init_isa(MachineState *machine) has_pci_info = false; has_acpi_build = false; smbios_defaults = false; + gigabyte_align = false; + smbios_legacy_mode = true; + has_reserved_memory = false; + option_rom_has_mr = true; + rom_file_has_mr = false; if (!machine->cpu_model) { machine->cpu_model = "486"; } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 155db99f63..36b6ab0bce 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -361,7 +361,7 @@ static QEMUMachine pc_q35_machine_v2_0 = { .name = "pc-q35-2.0", .init = pc_q35_init_2_0, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_2_0, + PC_COMPAT_2_0, { /* end of list */ } }, }; @@ -373,7 +373,7 @@ static QEMUMachine pc_q35_machine_v1_7 = { .name = "pc-q35-1.7", .init = pc_q35_init_1_7, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_1_7, + PC_COMPAT_1_7, { /* end of list */ } }, }; @@ -385,7 +385,7 @@ static QEMUMachine pc_q35_machine_v1_6 = { .name = "pc-q35-1.6", .init = pc_q35_init_1_6, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_1_6, + PC_COMPAT_1_6, { /* end of list */ } }, }; @@ -395,7 +395,7 @@ static QEMUMachine pc_q35_machine_v1_5 = { .name = "pc-q35-1.5", .init = pc_q35_init_1_5, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_1_5, + PC_COMPAT_1_5, { /* end of list */ } }, }; @@ -409,7 +409,7 @@ static QEMUMachine pc_q35_machine_v1_4 = { .name = "pc-q35-1.4", .init = pc_q35_init_1_4, .compat_props = (GlobalProperty[]) { - PC_Q35_COMPAT_1_4, + PC_COMPAT_1_4, { /* end of list */ } }, }; diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 7ac7c21bdb..f87c79824b 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -275,6 +275,19 @@ static void vhost_net_stop_one(struct vhost_net *net, vhost_dev_disable_notifiers(&net->dev, dev); } +static bool vhost_net_device_endian_ok(VirtIODevice *vdev) +{ +#ifdef TARGET_IS_BIENDIAN +#ifdef HOST_WORDS_BIGENDIAN + return virtio_is_big_endian(vdev); +#else + return !virtio_is_big_endian(vdev); +#endif +#else + return true; +#endif +} + int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, int total_queues) { @@ -283,6 +296,12 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); int r, i = 0; + if (!vhost_net_device_endian_ok(dev)) { + error_report("vhost-net does not support cross-endian"); + r = -ENOSYS; + goto err; + } + if (!k->set_guest_notifiers) { error_report("binding does not support guest notifiers"); r = -ENOSYS; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index e51d753cee..268eff9df8 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -23,6 +23,7 @@ #include "hw/virtio/virtio-bus.h" #include "qapi/qmp/qjson.h" #include "qapi-event.h" +#include "hw/virtio/virtio-access.h" #define VIRTIO_NET_VM_VERSION 11 @@ -72,8 +73,8 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) VirtIONet *n = VIRTIO_NET(vdev); struct virtio_net_config netcfg; - stw_p(&netcfg.status, n->status); - stw_p(&netcfg.max_virtqueue_pairs, n->max_queues); + virtio_stw_p(vdev, &netcfg.status, n->status); + virtio_stw_p(vdev, &netcfg.max_virtqueue_pairs, n->max_queues); memcpy(netcfg.mac, n->mac, ETH_ALEN); memcpy(config, &netcfg, n->config_size); } @@ -604,6 +605,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { + VirtIODevice *vdev = VIRTIO_DEVICE(n); struct virtio_net_ctrl_mac mac_data; size_t s; NetClientState *nc = qemu_get_queue(n->nic); @@ -632,7 +634,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); - mac_data.entries = ldl_p(&mac_data.entries); + mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); if (s != sizeof(mac_data.entries)) { goto error; } @@ -659,7 +661,7 @@ static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, s = iov_to_buf(iov, iov_cnt, 0, &mac_data.entries, sizeof(mac_data.entries)); - mac_data.entries = ldl_p(&mac_data.entries); + mac_data.entries = virtio_ldl_p(vdev, &mac_data.entries); if (s != sizeof(mac_data.entries)) { goto error; } @@ -699,12 +701,13 @@ error: static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { + VirtIODevice *vdev = VIRTIO_DEVICE(n); uint16_t vid; size_t s; NetClientState *nc = qemu_get_queue(n->nic); s = iov_to_buf(iov, iov_cnt, 0, &vid, sizeof(vid)); - vid = lduw_p(&vid); + vid = virtio_lduw_p(vdev, &vid); if (s != sizeof(vid)) { return VIRTIO_NET_ERR; } @@ -758,7 +761,7 @@ static int virtio_net_handle_mq(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_ERR; } - queues = lduw_p(&mq.virtqueue_pairs); + queues = virtio_lduw_p(vdev, &mq.virtqueue_pairs); if (queues < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || queues > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || @@ -875,6 +878,14 @@ static int virtio_net_has_buffers(VirtIONetQueue *q, int bufsize) return 1; } +static void virtio_net_hdr_swap(VirtIODevice *vdev, struct virtio_net_hdr *hdr) +{ + virtio_tswap16s(vdev, &hdr->hdr_len); + virtio_tswap16s(vdev, &hdr->gso_size); + virtio_tswap16s(vdev, &hdr->csum_start); + virtio_tswap16s(vdev, &hdr->csum_offset); +} + /* dhclient uses AF_PACKET but doesn't pass auxdata to the kernel so * it never finds out that the packets don't have valid checksums. This * causes dhclient to get upset. Fedora's carried a patch for ages to @@ -910,6 +921,7 @@ static void receive_header(VirtIONet *n, const struct iovec *iov, int iov_cnt, void *wbuf = (void *)buf; work_around_broken_dhclient(wbuf, wbuf + n->host_hdr_len, size - n->host_hdr_len); + virtio_net_hdr_swap(VIRTIO_DEVICE(n), wbuf); iov_from_buf(iov, iov_cnt, 0, buf, sizeof(struct virtio_net_hdr)); } else { struct virtio_net_hdr hdr = { @@ -1059,7 +1071,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t } if (mhdr_cnt) { - stw_p(&mhdr.num_buffers, i); + virtio_stw_p(vdev, &mhdr.num_buffers, i); iov_from_buf(mhdr_sg, mhdr_cnt, 0, &mhdr.num_buffers, sizeof mhdr.num_buffers); @@ -1118,6 +1130,14 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) exit(1); } + if (n->has_vnet_hdr) { + if (out_sg[0].iov_len < n->guest_hdr_len) { + error_report("virtio-net header incorrect"); + exit(1); + } + virtio_net_hdr_swap(vdev, (void *) out_sg[0].iov_base); + } + /* * If host wants to see the guest header as is, we can * pass it on unchanged. Otherwise, copy just the parts @@ -1297,7 +1317,6 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) static void virtio_net_save(QEMUFile *f, void *opaque) { - int i; VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); @@ -1305,6 +1324,12 @@ static void virtio_net_save(QEMUFile *f, void *opaque) * it might keep writing to memory. */ assert(!n->vhost_started); virtio_save(vdev, f); +} + +static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) +{ + VirtIONet *n = VIRTIO_NET(vdev); + int i; qemu_put_buffer(f, n->mac, ETH_ALEN); qemu_put_be32(f, n->vqs[0].tx_waiting); @@ -1340,15 +1365,18 @@ static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) { VirtIONet *n = opaque; VirtIODevice *vdev = VIRTIO_DEVICE(n); - int ret, i, link_down; if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) return -EINVAL; - ret = virtio_load(vdev, f); - if (ret) { - return ret; - } + return virtio_load(vdev, f, version_id); +} + +static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, + int version_id) +{ + VirtIONet *n = VIRTIO_NET(vdev); + int i, link_down; qemu_get_buffer(f, n->mac, ETH_ALEN); n->vqs[0].tx_waiting = qemu_get_be32(f); @@ -1694,6 +1722,8 @@ static void virtio_net_class_init(ObjectClass *klass, void *data) vdc->set_status = virtio_net_set_status; vdc->guest_notifier_mask = virtio_net_guest_notifier_mask; vdc->guest_notifier_pending = virtio_net_guest_notifier_pending; + vdc->load = virtio_net_load_device; + vdc->save = virtio_net_save_device; } static const TypeInfo virtio_net_info = { diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 8c8c9d1f61..04ecfa7e9a 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -19,6 +19,7 @@ #include <hw/scsi/scsi.h> #include <block/scsi.h> #include <hw/virtio/virtio-bus.h> +#include "hw/virtio/virtio-access.h" typedef struct VirtIOSCSIReq { VirtIOSCSI *dev; @@ -235,7 +236,7 @@ static void virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ req->resp.tmf.response = VIRTIO_SCSI_S_OK; - tswap32s(&req->req.tmf.subtype); + virtio_tswap32s(VIRTIO_DEVICE(s), &req->req.tmf.subtype); switch (req->req.tmf.subtype) { case VIRTIO_SCSI_T_TMF_ABORT_TASK: case VIRTIO_SCSI_T_TMF_QUERY_TASK: @@ -346,7 +347,7 @@ static void virtio_scsi_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) continue; } - tswap32s(&req->req.tmf.type); + virtio_tswap32s(vdev, &req->req.tmf.type); if (req->req.tmf.type == VIRTIO_SCSI_T_TMF) { if (virtio_scsi_parse_req(req, sizeof(VirtIOSCSICtrlTMFReq), sizeof(VirtIOSCSICtrlTMFResp)) < 0) { @@ -384,6 +385,7 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, VirtIOSCSIReq *req = r->hba_private; uint8_t sense[SCSI_SENSE_BUF_SIZE]; uint32_t sense_len; + VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); if (r->io_canceled) { return; @@ -392,14 +394,14 @@ static void virtio_scsi_command_complete(SCSIRequest *r, uint32_t status, req->resp.cmd.response = VIRTIO_SCSI_S_OK; req->resp.cmd.status = status; if (req->resp.cmd.status == GOOD) { - req->resp.cmd.resid = tswap32(resid); + req->resp.cmd.resid = virtio_tswap32(vdev, resid); } else { req->resp.cmd.resid = 0; sense_len = scsi_req_get_sense(r, sense, sizeof(sense)); sense_len = MIN(sense_len, req->resp_iov.size - sizeof(req->resp.cmd)); qemu_iovec_from_buf(&req->resp_iov, sizeof(req->resp.cmd), &req->resp, sense_len); - req->resp.cmd.sense_len = tswap32(sense_len); + req->resp.cmd.sense_len = virtio_tswap32(vdev, sense_len); } virtio_scsi_complete_cmd_req(req); } @@ -487,16 +489,16 @@ static void virtio_scsi_get_config(VirtIODevice *vdev, VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; VirtIOSCSICommon *s = VIRTIO_SCSI_COMMON(vdev); - stl_p(&scsiconf->num_queues, s->conf.num_queues); - stl_p(&scsiconf->seg_max, 128 - 2); - stl_p(&scsiconf->max_sectors, s->conf.max_sectors); - stl_p(&scsiconf->cmd_per_lun, s->conf.cmd_per_lun); - stl_p(&scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); - stl_p(&scsiconf->sense_size, s->sense_size); - stl_p(&scsiconf->cdb_size, s->cdb_size); - stw_p(&scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); - stw_p(&scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); - stl_p(&scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN); + virtio_stl_p(vdev, &scsiconf->num_queues, s->conf.num_queues); + virtio_stl_p(vdev, &scsiconf->seg_max, 128 - 2); + virtio_stl_p(vdev, &scsiconf->max_sectors, s->conf.max_sectors); + virtio_stl_p(vdev, &scsiconf->cmd_per_lun, s->conf.cmd_per_lun); + virtio_stl_p(vdev, &scsiconf->event_info_size, sizeof(VirtIOSCSIEvent)); + virtio_stl_p(vdev, &scsiconf->sense_size, s->sense_size); + virtio_stl_p(vdev, &scsiconf->cdb_size, s->cdb_size); + virtio_stw_p(vdev, &scsiconf->max_channel, VIRTIO_SCSI_MAX_CHANNEL); + virtio_stw_p(vdev, &scsiconf->max_target, VIRTIO_SCSI_MAX_TARGET); + virtio_stl_p(vdev, &scsiconf->max_lun, VIRTIO_SCSI_MAX_LUN); } static void virtio_scsi_set_config(VirtIODevice *vdev, @@ -505,14 +507,14 @@ static void virtio_scsi_set_config(VirtIODevice *vdev, VirtIOSCSIConfig *scsiconf = (VirtIOSCSIConfig *)config; VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - if ((uint32_t) ldl_p(&scsiconf->sense_size) >= 65536 || - (uint32_t) ldl_p(&scsiconf->cdb_size) >= 256) { + if ((uint32_t) virtio_ldl_p(vdev, &scsiconf->sense_size) >= 65536 || + (uint32_t) virtio_ldl_p(vdev, &scsiconf->cdb_size) >= 256) { error_report("bad data written to virtio-scsi configuration space"); exit(1); } - vs->sense_size = ldl_p(&scsiconf->sense_size); - vs->cdb_size = ldl_p(&scsiconf->cdb_size); + vs->sense_size = virtio_ldl_p(vdev, &scsiconf->sense_size); + vs->cdb_size = virtio_ldl_p(vdev, &scsiconf->cdb_size); } static uint32_t virtio_scsi_get_features(VirtIODevice *vdev, @@ -549,7 +551,7 @@ static int virtio_scsi_load(QEMUFile *f, void *opaque, int version_id) VirtIODevice *vdev = VIRTIO_DEVICE(opaque); int ret; - ret = virtio_load(vdev, f); + ret = virtio_load(vdev, f, version_id); if (ret) { return ret; } diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index 05002bf9d6..307732c744 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -27,6 +27,7 @@ #include "hw/timer/mc146818rtc.h" #include "qapi/visitor.h" #include "qapi-event.h" +#include "qmp-commands.h" #ifdef TARGET_I386 #include "hw/i386/apic.h" @@ -85,6 +86,7 @@ typedef struct RTCState { Notifier clock_reset_notifier; LostTickPolicy lost_tick_policy; Notifier suspend_notifier; + QLIST_ENTRY(RTCState) link; } RTCState; static void rtc_set_time(RTCState *s); @@ -523,6 +525,20 @@ static void rtc_get_time(RTCState *s, struct tm *tm) rtc_from_bcd(s, s->cmos_data[RTC_CENTURY]) * 100 - 1900; } +static QLIST_HEAD(, RTCState) rtc_devices = + QLIST_HEAD_INITIALIZER(rtc_devices); + +#ifdef TARGET_I386 +void qmp_rtc_reset_reinjection(Error **errp) +{ + RTCState *s; + + QLIST_FOREACH(s, &rtc_devices, link) { + s->irq_coalesced = 0; + } +} +#endif + static void rtc_set_time(RTCState *s) { struct tm tm; @@ -911,6 +927,8 @@ ISADevice *rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) } else { isa_init_irq(isadev, &s->irq, RTC_ISA_IRQ); } + QLIST_INSERT_HEAD(&rtc_devices, s, link); + return isadev; } 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) |