From f0bbdaa7613f4254fc8901bb1175198d04b3fa88 Mon Sep 17 00:00:00 2001 From: Kitae Kim Date: Tue, 9 Jul 2013 16:41:16 +0900 Subject: [PATCH] virtio: removed previous virtio modules. Change-Id: Ie34a7dcea9a0f607a05437ed30f9b6f9d5fb1c23 Signed-off-by: Kitae Kim --- hw/virtio-balloon.c | 316 -------- hw/virtio-balloon.h | 64 -- hw/virtio-blk.c | 732 ------------------ hw/virtio-blk.h | 129 ---- hw/virtio-net.c | 1141 --------------------------- hw/virtio-net.h | 206 ----- hw/virtio-pci-new.c | 925 ---------------------- hw/virtio-pci.c | 1356 --------------------------------- hw/virtio-pci.h | 78 -- hw/virtio-serial-bus.c | 1060 -------------------------- hw/virtio-serial.h | 219 ------ hw/virtio-transport.c | 147 ---- hw/virtio-transport.h | 74 -- hw/virtio.c | 1059 ------------------------- hw/virtio.h | 254 ------ hw/{ => virtio}/virtio-mmio.c | 0 16 files changed, 7760 deletions(-) delete mode 100644 hw/virtio-balloon.c delete mode 100644 hw/virtio-balloon.h delete mode 100644 hw/virtio-blk.c delete mode 100644 hw/virtio-blk.h delete mode 100644 hw/virtio-net.c delete mode 100644 hw/virtio-net.h delete mode 100644 hw/virtio-pci-new.c delete mode 100755 hw/virtio-pci.c delete mode 100644 hw/virtio-pci.h delete mode 100644 hw/virtio-serial-bus.c delete mode 100644 hw/virtio-serial.h delete mode 100644 hw/virtio-transport.c delete mode 100644 hw/virtio-transport.h delete mode 100644 hw/virtio.c delete mode 100755 hw/virtio.h rename hw/{ => virtio}/virtio-mmio.c (100%) diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c deleted file mode 100644 index d6fe2aa042..0000000000 --- a/hw/virtio-balloon.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Virtio Balloon Device - * - * Copyright IBM, Corp. 2008 - * Copyright (C) 2011 Red Hat, Inc. - * Copyright (C) 2011 Amit Shah - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "iov.h" -#include "qemu-common.h" -#include "virtio.h" -#include "pc.h" -#include "cpu.h" -#include "balloon.h" -#include "virtio-balloon.h" -#include "virtio-transport.h" -#include "kvm.h" -#include "exec-memory.h" - -#if defined(__linux__) -#include -#endif - -typedef struct VirtIOBalloon -{ - VirtIODevice vdev; - VirtQueue *ivq, *dvq, *svq; - uint32_t num_pages; - uint32_t actual; - uint64_t stats[VIRTIO_BALLOON_S_NR]; - VirtQueueElement stats_vq_elem; - size_t stats_vq_offset; - DeviceState *qdev; -} VirtIOBalloon; - -static VirtIOBalloon *to_virtio_balloon(VirtIODevice *vdev) -{ - return (VirtIOBalloon *)vdev; -} - -static void balloon_page(void *addr, int deflate) -{ -#if defined(__linux__) - if (!kvm_enabled() || kvm_has_sync_mmu()) - qemu_madvise(addr, TARGET_PAGE_SIZE, - deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED); -#endif -} - -/* - * reset_stats - Mark all items in the stats array as unset - * - * This function needs to be called at device intialization and before - * before updating to a set of newly-generated stats. This will ensure that no - * stale values stick around in case the guest reports a subset of the supported - * statistics. - */ -static inline void reset_stats(VirtIOBalloon *dev) -{ - int i; - for (i = 0; i < VIRTIO_BALLOON_S_NR; dev->stats[i++] = -1); -} - -static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOBalloon *s = to_virtio_balloon(vdev); - VirtQueueElement elem; - MemoryRegionSection section; - - while (virtqueue_pop(vq, &elem)) { - size_t offset = 0; - uint32_t pfn; - - while (iov_to_buf(elem.out_sg, elem.out_num, offset, &pfn, 4) == 4) { - ram_addr_t pa; - ram_addr_t addr; - - pa = (ram_addr_t)ldl_p(&pfn) << VIRTIO_BALLOON_PFN_SHIFT; - offset += 4; - - /* FIXME: remove get_system_memory(), but how? */ - section = memory_region_find(get_system_memory(), pa, 1); - if (!section.size || !memory_region_is_ram(section.mr)) - continue; - - /* Using memory_region_get_ram_ptr is bending the rules a bit, but - should be OK because we only want a single page. */ - addr = section.offset_within_region; - balloon_page(memory_region_get_ram_ptr(section.mr) + addr, - !!(vq == s->dvq)); - } - - virtqueue_push(vq, &elem, offset); - virtio_notify(vdev, vq); - } -} - -static void virtio_balloon_receive_stats(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); - VirtQueueElement *elem = &s->stats_vq_elem; - VirtIOBalloonStat stat; - size_t offset = 0; - - if (!virtqueue_pop(vq, elem)) { - return; - } - - /* Initialize the stats to get rid of any stale values. This is only - * needed to handle the case where a guest supports fewer stats than it - * used to (ie. it has booted into an old kernel). - */ - reset_stats(s); - - 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); - - offset += sizeof(stat); - if (tag < VIRTIO_BALLOON_S_NR) - s->stats[tag] = val; - } - s->stats_vq_offset = offset; -} - -static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) -{ - VirtIOBalloon *dev = to_virtio_balloon(vdev); - struct virtio_balloon_config config; - - config.num_pages = cpu_to_le32(dev->num_pages); - config.actual = cpu_to_le32(dev->actual); - - memcpy(config_data, &config, 8); -} - -static void virtio_balloon_set_config(VirtIODevice *vdev, - const uint8_t *config_data) -{ - VirtIOBalloon *dev = to_virtio_balloon(vdev); - struct virtio_balloon_config config; - uint32_t oldactual = dev->actual; - memcpy(&config, config_data, 8); - dev->actual = le32_to_cpu(config.actual); - if (dev->actual != oldactual) { - qemu_balloon_changed(ram_size - - (dev->actual << VIRTIO_BALLOON_PFN_SHIFT)); - } -} - -static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) -{ - f |= (1 << VIRTIO_BALLOON_F_STATS_VQ); - return f; -} - -static void virtio_balloon_stat(void *opaque, BalloonInfo *info) -{ - VirtIOBalloon *dev = opaque; - -#if 0 - /* Disable guest-provided stats for now. For more details please check: - * https://bugzilla.redhat.com/show_bug.cgi?id=623903 - * - * If you do enable it (which is probably not going to happen as we - * need a new command for it), remember that you also need to fill the - * appropriate members of the BalloonInfo structure so that the stats - * are returned to the client. - */ - if (dev->vdev.guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ)) { - virtqueue_push(dev->svq, &dev->stats_vq_elem, dev->stats_vq_offset); - virtio_notify(&dev->vdev, dev->svq); - return; - } -#endif - - /* Stats are not supported. Clear out any stale values that might - * have been set by a more featureful guest kernel. - */ - reset_stats(dev); - - info->actual = ram_size - ((uint64_t) dev->actual << - VIRTIO_BALLOON_PFN_SHIFT); -} - -static void virtio_balloon_to_target(void *opaque, ram_addr_t target) -{ - VirtIOBalloon *dev = opaque; - - if (target > ram_size) { - target = ram_size; - } - if (target) { - dev->num_pages = (ram_size - target) >> VIRTIO_BALLOON_PFN_SHIFT; - virtio_notify_config(&dev->vdev); - } -} - -static void virtio_balloon_save(QEMUFile *f, void *opaque) -{ - VirtIOBalloon *s = opaque; - - virtio_save(&s->vdev, f); - - qemu_put_be32(f, s->num_pages); - qemu_put_be32(f, s->actual); -} - -static int virtio_balloon_load(QEMUFile *f, void *opaque, int version_id) -{ - VirtIOBalloon *s = opaque; - int ret; - - if (version_id != 1) - return -EINVAL; - - ret = virtio_load(&s->vdev, f); - if (ret) { - return ret; - } - - s->num_pages = qemu_get_be32(f); - s->actual = qemu_get_be32(f); - return 0; -} - -VirtIODevice *virtio_balloon_init(DeviceState *dev) -{ - VirtIOBalloon *s; - int ret; - - s = (VirtIOBalloon *)virtio_common_init("virtio-balloon", - VIRTIO_ID_BALLOON, - 8, sizeof(VirtIOBalloon)); - - s->vdev.get_config = virtio_balloon_get_config; - s->vdev.set_config = virtio_balloon_set_config; - s->vdev.get_features = virtio_balloon_get_features; - - ret = qemu_add_balloon_handler(virtio_balloon_to_target, - virtio_balloon_stat, s); - if (ret < 0) { - virtio_cleanup(&s->vdev); - return NULL; - } - - s->ivq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); - s->dvq = virtio_add_queue(&s->vdev, 128, virtio_balloon_handle_output); - s->svq = virtio_add_queue(&s->vdev, 128, virtio_balloon_receive_stats); - - reset_stats(s); - - s->qdev = dev; - register_savevm(dev, "virtio-balloon", -1, 1, - virtio_balloon_save, virtio_balloon_load, s); - - return &s->vdev; -} - -void virtio_balloon_exit(VirtIODevice *vdev) -{ - VirtIOBalloon *s = DO_UPCAST(VirtIOBalloon, vdev, vdev); - - qemu_remove_balloon_handler(s); - unregister_savevm(s->qdev, "virtio-balloon", s); - virtio_cleanup(vdev); -} - -/******************** VirtIOBaloon Device **********************/ - -static int virtio_balloondev_init(DeviceState *dev) -{ - VirtIODevice *vdev; - VirtIOBaloonState *s = VIRTIO_BALLOON_FROM_QDEV(dev); - vdev = virtio_balloon_init(dev); - if (!vdev) { - return -1; - } - - assert(s->trl != NULL); - - return virtio_call_backend_init_cb(dev, s->trl, vdev); -} - -static Property virtio_balloon_properties[] = { - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_balloon_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - dc->init = virtio_balloondev_init; - dc->props = virtio_balloon_properties; -} - -static TypeInfo virtio_balloon_info = { - .name = "virtio-balloon", - .parent = TYPE_DEVICE, - .instance_size = sizeof(VirtIOBaloonState), - .class_init = virtio_balloon_class_init, -}; - -static void virtio_baloon_register_types(void) -{ - type_register_static(&virtio_balloon_info); -} - -type_init(virtio_baloon_register_types) diff --git a/hw/virtio-balloon.h b/hw/virtio-balloon.h deleted file mode 100644 index b925186f1d..0000000000 --- a/hw/virtio-balloon.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Virtio Support - * - * Copyright IBM, Corp. 2007-2008 - * - * Authors: - * Anthony Liguori - * Rusty Russell - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef _QEMU_VIRTIO_BALLOON_H -#define _QEMU_VIRTIO_BALLOON_H - -#include "sysbus.h" -#include "virtio.h" -#include "pci.h" -#include "virtio-transport.h" - -/* from Linux's linux/virtio_balloon.h */ - -/* The ID for virtio_balloon */ -#define VIRTIO_ID_BALLOON 5 - -/* The feature bitmap for virtio balloon */ -#define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ -#define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory stats virtqueue */ - -/* Size of a PFN in the balloon interface. */ -#define VIRTIO_BALLOON_PFN_SHIFT 12 - -struct virtio_balloon_config -{ - /* Number of pages host wants Guest to give up. */ - uint32_t num_pages; - /* Number of pages we've actually got in balloon. */ - uint32_t actual; -}; - -/* Memory Statistics */ -#define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ -#define VIRTIO_BALLOON_S_SWAP_OUT 1 /* Amount of memory swapped out */ -#define VIRTIO_BALLOON_S_MAJFLT 2 /* Number of major faults */ -#define VIRTIO_BALLOON_S_MINFLT 3 /* Number of minor faults */ -#define VIRTIO_BALLOON_S_MEMFREE 4 /* Total amount of free memory */ -#define VIRTIO_BALLOON_S_MEMTOT 5 /* Total amount of memory */ -#define VIRTIO_BALLOON_S_NR 6 - -typedef struct VirtIOBalloonStat { - uint16_t tag; - uint64_t val; -} QEMU_PACKED VirtIOBalloonStat; - -typedef struct { - DeviceState qdev; - VirtIOTransportLink *trl; -} VirtIOBaloonState; - -#define VIRTIO_BALLOON_FROM_QDEV(dev) DO_UPCAST(VirtIOBaloonState, qdev, dev) - -#endif diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c deleted file mode 100644 index 0a233528ea..0000000000 --- a/hw/virtio-blk.c +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Virtio Block Device - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu-common.h" -#include "qemu-error.h" -#include "trace.h" -#include "hw/block-common.h" -#include "blockdev.h" -#include "virtio-transport.h" -#include "virtio-pci.h" -#include "virtio-blk.h" -#include "scsi-defs.h" -#ifdef __linux__ -# include -#endif - -typedef struct VirtIOBlock -{ - VirtIODevice vdev; - BlockDriverState *bs; - VirtQueue *vq; - void *rq; - QEMUBH *bh; - BlockConf *conf; - VirtIOBlkConf *blk; - unsigned short sector_mask; - DeviceState *qdev; -} VirtIOBlock; - -static VirtIOBlock *to_virtio_blk(VirtIODevice *vdev) -{ - return (VirtIOBlock *)vdev; -} - -typedef struct VirtIOBlockReq -{ - VirtIOBlock *dev; - VirtQueueElement elem; - struct virtio_blk_inhdr *in; - struct virtio_blk_outhdr *out; - struct virtio_scsi_inhdr *scsi; - QEMUIOVector qiov; - struct VirtIOBlockReq *next; - BlockAcctCookie acct; -} VirtIOBlockReq; - -static void virtio_blk_req_complete(VirtIOBlockReq *req, int status) -{ - VirtIOBlock *s = req->dev; - - trace_virtio_blk_req_complete(req, status); - - stb_p(&req->in->status, status); - virtqueue_push(s->vq, &req->elem, req->qiov.size + sizeof(*req->in)); - virtio_notify(&s->vdev, s->vq); -} - -static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, - int is_read) -{ - BlockErrorAction action = bdrv_get_on_error(req->dev->bs, is_read); - VirtIOBlock *s = req->dev; - - if (action == BLOCK_ERR_IGNORE) { - bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_IGNORE, is_read); - return 0; - } - - if ((error == ENOSPC && action == BLOCK_ERR_STOP_ENOSPC) - || action == BLOCK_ERR_STOP_ANY) { - req->next = s->rq; - s->rq = req; - bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_STOP, is_read); - vm_stop(RUN_STATE_IO_ERROR); - bdrv_iostatus_set_err(s->bs, error); - } else { - virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - bdrv_acct_done(s->bs, &req->acct); - g_free(req); - bdrv_emit_qmp_error_event(s->bs, BDRV_ACTION_REPORT, is_read); - } - - return 1; -} - -static void virtio_blk_rw_complete(void *opaque, int ret) -{ - VirtIOBlockReq *req = opaque; - - trace_virtio_blk_rw_complete(req, ret); - - if (ret) { - int is_read = !(ldl_p(&req->out->type) & VIRTIO_BLK_T_OUT); - if (virtio_blk_handle_rw_error(req, -ret, is_read)) - return; - } - - virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - bdrv_acct_done(req->dev->bs, &req->acct); - g_free(req); -} - -static void virtio_blk_flush_complete(void *opaque, int ret) -{ - VirtIOBlockReq *req = opaque; - - if (ret) { - if (virtio_blk_handle_rw_error(req, -ret, 0)) { - return; - } - } - - virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - bdrv_acct_done(req->dev->bs, &req->acct); - g_free(req); -} - -static VirtIOBlockReq *virtio_blk_alloc_request(VirtIOBlock *s) -{ - VirtIOBlockReq *req = g_malloc(sizeof(*req)); - req->dev = s; - req->qiov.size = 0; - req->next = NULL; - return req; -} - -static VirtIOBlockReq *virtio_blk_get_request(VirtIOBlock *s) -{ - VirtIOBlockReq *req = virtio_blk_alloc_request(s); - - if (req != NULL) { - if (!virtqueue_pop(s->vq, &req->elem)) { - g_free(req); - return NULL; - } - } - - return req; -} - -static void virtio_blk_handle_scsi(VirtIOBlockReq *req) -{ -#ifdef __linux__ - int ret; - int i; -#endif - int status = VIRTIO_BLK_S_OK; - - /* - * We require at least one output segment each for the virtio_blk_outhdr - * and the SCSI command block. - * - * We also at least require the virtio_blk_inhdr, the virtio_scsi_inhdr - * and the sense buffer pointer in the input segments. - */ - if (req->elem.out_num < 2 || req->elem.in_num < 3) { - virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - g_free(req); - return; - } - - /* - * The scsi inhdr is placed in the second-to-last input segment, just - * before the regular inhdr. - */ - req->scsi = (void *)req->elem.in_sg[req->elem.in_num - 2].iov_base; - - if (!req->dev->blk->scsi) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - - /* - * No support for bidirection commands yet. - */ - if (req->elem.out_num > 2 && req->elem.in_num > 3) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - -#ifdef __linux__ - struct sg_io_hdr hdr; - memset(&hdr, 0, sizeof(struct sg_io_hdr)); - hdr.interface_id = 'S'; - hdr.cmd_len = req->elem.out_sg[1].iov_len; - hdr.cmdp = req->elem.out_sg[1].iov_base; - hdr.dxfer_len = 0; - - if (req->elem.out_num > 2) { - /* - * If there are more than the minimally required 2 output segments - * there is write payload starting from the third iovec. - */ - hdr.dxfer_direction = SG_DXFER_TO_DEV; - hdr.iovec_count = req->elem.out_num - 2; - - for (i = 0; i < hdr.iovec_count; i++) - hdr.dxfer_len += req->elem.out_sg[i + 2].iov_len; - - hdr.dxferp = req->elem.out_sg + 2; - - } else if (req->elem.in_num > 3) { - /* - * If we have more than 3 input segments the guest wants to actually - * read data. - */ - hdr.dxfer_direction = SG_DXFER_FROM_DEV; - hdr.iovec_count = req->elem.in_num - 3; - for (i = 0; i < hdr.iovec_count; i++) - hdr.dxfer_len += req->elem.in_sg[i].iov_len; - - hdr.dxferp = req->elem.in_sg; - } else { - /* - * Some SCSI commands don't actually transfer any data. - */ - hdr.dxfer_direction = SG_DXFER_NONE; - } - - hdr.sbp = req->elem.in_sg[req->elem.in_num - 3].iov_base; - hdr.mx_sb_len = req->elem.in_sg[req->elem.in_num - 3].iov_len; - - ret = bdrv_ioctl(req->dev->bs, SG_IO, &hdr); - if (ret) { - status = VIRTIO_BLK_S_UNSUPP; - goto fail; - } - - /* - * From SCSI-Generic-HOWTO: "Some lower level drivers (e.g. ide-scsi) - * clear the masked_status field [hence status gets cleared too, see - * block/scsi_ioctl.c] even when a CHECK_CONDITION or COMMAND_TERMINATED - * status has occurred. However they do set DRIVER_SENSE in driver_status - * field. Also a (sb_len_wr > 0) indicates there is a sense buffer. - */ - if (hdr.status == 0 && hdr.sb_len_wr > 0) { - hdr.status = CHECK_CONDITION; - } - - stl_p(&req->scsi->errors, - hdr.status | (hdr.msg_status << 8) | - (hdr.host_status << 16) | (hdr.driver_status << 24)); - stl_p(&req->scsi->residual, hdr.resid); - stl_p(&req->scsi->sense_len, hdr.sb_len_wr); - stl_p(&req->scsi->data_len, hdr.dxfer_len); - - virtio_blk_req_complete(req, status); - g_free(req); - return; -#else - abort(); -#endif - -fail: - /* Just put anything nonzero so that the ioctl fails in the guest. */ - stl_p(&req->scsi->errors, 255); - virtio_blk_req_complete(req, status); - g_free(req); -} - -typedef struct MultiReqBuffer { - BlockRequest blkreq[32]; - unsigned int num_writes; -} MultiReqBuffer; - -static void virtio_submit_multiwrite(BlockDriverState *bs, MultiReqBuffer *mrb) -{ - int i, ret; - - if (!mrb->num_writes) { - return; - } - - ret = bdrv_aio_multiwrite(bs, mrb->blkreq, mrb->num_writes); - if (ret != 0) { - for (i = 0; i < mrb->num_writes; i++) { - if (mrb->blkreq[i].error) { - virtio_blk_rw_complete(mrb->blkreq[i].opaque, -EIO); - } - } - } - - mrb->num_writes = 0; -} - -static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) -{ - bdrv_acct_start(req->dev->bs, &req->acct, 0, BDRV_ACCT_FLUSH); - - /* - * Make sure all outstanding writes are posted to the backing device. - */ - virtio_submit_multiwrite(req->dev->bs, mrb); - bdrv_aio_flush(req->dev->bs, virtio_blk_flush_complete, req); -} - -static void virtio_blk_handle_write(VirtIOBlockReq *req, MultiReqBuffer *mrb) -{ - BlockRequest *blkreq; - uint64_t sector; - - sector = ldq_p(&req->out->sector); - - bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_WRITE); - - trace_virtio_blk_handle_write(req, sector, req->qiov.size / 512); - - if (sector & req->dev->sector_mask) { - virtio_blk_rw_complete(req, -EIO); - return; - } - if (req->qiov.size % req->dev->conf->logical_block_size) { - virtio_blk_rw_complete(req, -EIO); - return; - } - - if (mrb->num_writes == 32) { - virtio_submit_multiwrite(req->dev->bs, mrb); - } - - blkreq = &mrb->blkreq[mrb->num_writes]; - blkreq->sector = sector; - blkreq->nb_sectors = req->qiov.size / BDRV_SECTOR_SIZE; - blkreq->qiov = &req->qiov; - blkreq->cb = virtio_blk_rw_complete; - blkreq->opaque = req; - blkreq->error = 0; - - mrb->num_writes++; -} - -static void virtio_blk_handle_read(VirtIOBlockReq *req) -{ - uint64_t sector; - - sector = ldq_p(&req->out->sector); - - bdrv_acct_start(req->dev->bs, &req->acct, req->qiov.size, BDRV_ACCT_READ); - - trace_virtio_blk_handle_read(req, sector, req->qiov.size / 512); - - if (sector & req->dev->sector_mask) { - virtio_blk_rw_complete(req, -EIO); - return; - } - if (req->qiov.size % req->dev->conf->logical_block_size) { - virtio_blk_rw_complete(req, -EIO); - return; - } - bdrv_aio_readv(req->dev->bs, sector, &req->qiov, - req->qiov.size / BDRV_SECTOR_SIZE, - virtio_blk_rw_complete, req); -} - -static void virtio_blk_handle_request(VirtIOBlockReq *req, - MultiReqBuffer *mrb) -{ - uint32_t type; - - if (req->elem.out_num < 1 || req->elem.in_num < 1) { - error_report("virtio-blk missing headers"); - exit(1); - } - - if (req->elem.out_sg[0].iov_len < sizeof(*req->out) || - req->elem.in_sg[req->elem.in_num - 1].iov_len < sizeof(*req->in)) { - error_report("virtio-blk header not in correct element"); - exit(1); - } - - req->out = (void *)req->elem.out_sg[0].iov_base; - req->in = (void *)req->elem.in_sg[req->elem.in_num - 1].iov_base; - - type = ldl_p(&req->out->type); - - if (type & VIRTIO_BLK_T_FLUSH) { - virtio_blk_handle_flush(req, mrb); - } else if (type & VIRTIO_BLK_T_SCSI_CMD) { - virtio_blk_handle_scsi(req); - } else if (type & VIRTIO_BLK_T_GET_ID) { - VirtIOBlock *s = req->dev; - - /* - * NB: per existing s/n string convention the string is - * terminated by '\0' only when shorter than buffer. - */ - strncpy(req->elem.in_sg[0].iov_base, - s->blk->serial ? s->blk->serial : "", - MIN(req->elem.in_sg[0].iov_len, VIRTIO_BLK_ID_BYTES)); - virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - g_free(req); - } else if (type & VIRTIO_BLK_T_OUT) { - qemu_iovec_init_external(&req->qiov, &req->elem.out_sg[1], - req->elem.out_num - 1); - virtio_blk_handle_write(req, mrb); - } else { - qemu_iovec_init_external(&req->qiov, &req->elem.in_sg[0], - req->elem.in_num - 1); - virtio_blk_handle_read(req); - } -} - -static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOBlock *s = to_virtio_blk(vdev); - VirtIOBlockReq *req; - MultiReqBuffer mrb = { - .num_writes = 0, - }; - - while ((req = virtio_blk_get_request(s))) { - virtio_blk_handle_request(req, &mrb); - } - - virtio_submit_multiwrite(s->bs, &mrb); - - /* - * FIXME: Want to check for completions before returning to guest mode, - * so cached reads and writes are reported as quickly as possible. But - * that should be done in the generic block layer. - */ -} - -static void virtio_blk_dma_restart_bh(void *opaque) -{ - VirtIOBlock *s = opaque; - VirtIOBlockReq *req = s->rq; - MultiReqBuffer mrb = { - .num_writes = 0, - }; - - qemu_bh_delete(s->bh); - s->bh = NULL; - - s->rq = NULL; - - while (req) { - virtio_blk_handle_request(req, &mrb); - req = req->next; - } - - virtio_submit_multiwrite(s->bs, &mrb); -} - -static void virtio_blk_dma_restart_cb(void *opaque, int running, - RunState state) -{ - VirtIOBlock *s = opaque; - - if (!running) - return; - - if (!s->bh) { - s->bh = qemu_bh_new(virtio_blk_dma_restart_bh, s); - qemu_bh_schedule(s->bh); - } -} - -static void virtio_blk_reset(VirtIODevice *vdev) -{ - /* - * This should cancel pending requests, but can't do nicely until there - * are per-device request lists. - */ - bdrv_drain_all(); -} - -/* coalesce internal state, copy to pci i/o region 0 - */ -static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) -{ - VirtIOBlock *s = to_virtio_blk(vdev); - struct virtio_blk_config blkcfg; - uint64_t capacity; - int blk_size = s->conf->logical_block_size; - - bdrv_get_geometry(s->bs, &capacity); - memset(&blkcfg, 0, sizeof(blkcfg)); - stq_raw(&blkcfg.capacity, capacity); - stl_raw(&blkcfg.seg_max, 128 - 2); - stw_raw(&blkcfg.cylinders, s->conf->cyls); - stl_raw(&blkcfg.blk_size, blk_size); - stw_raw(&blkcfg.min_io_size, s->conf->min_io_size / blk_size); - stw_raw(&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 - * the logical block size. If that is not the case, lets use - * sector_mask to adopt the geometry to have a correct picture. - * For those devices where the capacity is ok for the given geometry - * we dont touch the sector value of the geometry, since some devices - * (like s390 dasd) need a specific value. Here the capacity is already - * cyls*heads*secs*blk_size and the sector value is not block size - * divided by 512 - instead it is the amount of blk_size blocks - * per track (cylinder). - */ - if (bdrv_getlength(s->bs) / s->conf->heads / s->conf->secs % blk_size) { - blkcfg.sectors = s->conf->secs & ~s->sector_mask; - } else { - blkcfg.sectors = s->conf->secs; - } - blkcfg.size_max = 0; - blkcfg.physical_block_exp = get_physical_block_exp(s->conf); - blkcfg.alignment_offset = 0; - blkcfg.wce = bdrv_enable_write_cache(s->bs); - memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); -} - -static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) -{ - VirtIOBlock *s = to_virtio_blk(vdev); - struct virtio_blk_config blkcfg; - - memcpy(&blkcfg, config, sizeof(blkcfg)); - bdrv_set_enable_write_cache(s->bs, blkcfg.wce != 0); -} - -static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) -{ - VirtIOBlock *s = to_virtio_blk(vdev); - - features |= (1 << VIRTIO_BLK_F_SEG_MAX); - features |= (1 << VIRTIO_BLK_F_GEOMETRY); - features |= (1 << VIRTIO_BLK_F_TOPOLOGY); - features |= (1 << VIRTIO_BLK_F_BLK_SIZE); - features |= (1 << VIRTIO_BLK_F_SCSI); - - if (bdrv_enable_write_cache(s->bs)) - features |= (1 << VIRTIO_BLK_F_WCE); - - if (bdrv_is_read_only(s->bs)) - features |= 1 << VIRTIO_BLK_F_RO; - - return features; -} - -static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) -{ - VirtIOBlock *s = to_virtio_blk(vdev); - uint32_t features; - - if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return; - } - - features = vdev->guest_features; - bdrv_set_enable_write_cache(s->bs, !!(features & (1 << VIRTIO_BLK_F_WCE))); -} - -static void virtio_blk_save(QEMUFile *f, void *opaque) -{ - VirtIOBlock *s = opaque; - VirtIOBlockReq *req = s->rq; - - virtio_save(&s->vdev, f); - - while (req) { - qemu_put_sbyte(f, 1); - qemu_put_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem)); - req = req->next; - } - qemu_put_sbyte(f, 0); -} - -static int virtio_blk_load(QEMUFile *f, void *opaque, int version_id) -{ - VirtIOBlock *s = opaque; - int ret; - - if (version_id != 2) - return -EINVAL; - - ret = virtio_load(&s->vdev, f); - if (ret) { - return ret; - } - - while (qemu_get_sbyte(f)) { - VirtIOBlockReq *req = virtio_blk_alloc_request(s); - qemu_get_buffer(f, (unsigned char*)&req->elem, sizeof(req->elem)); - req->next = s->rq; - s->rq = req; - - virtqueue_map_sg(req->elem.in_sg, req->elem.in_addr, - req->elem.in_num, 1); - virtqueue_map_sg(req->elem.out_sg, req->elem.out_addr, - req->elem.out_num, 0); - } - - return 0; -} - -static void virtio_blk_resize(void *opaque) -{ - VirtIOBlock *s = opaque; - - virtio_notify_config(&s->vdev); -} - -static const BlockDevOps virtio_block_ops = { - .resize_cb = virtio_blk_resize, -}; - -VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk) -{ - VirtIOBlock *s; - static int virtio_blk_id; - - if (!blk->conf.bs) { - error_report("drive property not set"); - return NULL; - } - if (!bdrv_is_inserted(blk->conf.bs)) { - error_report("Device needs media, but drive is empty"); - return NULL; - } - - blkconf_serial(&blk->conf, &blk->serial); - if (blkconf_geometry(&blk->conf, NULL, 65535, 255, 255) < 0) { - return NULL; - } - - s = (VirtIOBlock *)virtio_common_init("virtio-blk", VIRTIO_ID_BLOCK, - sizeof(struct virtio_blk_config), - sizeof(VirtIOBlock)); - - s->vdev.get_config = virtio_blk_update_config; - s->vdev.set_config = virtio_blk_set_config; - s->vdev.get_features = virtio_blk_get_features; - s->vdev.set_status = virtio_blk_set_status; - s->vdev.reset = virtio_blk_reset; - s->bs = blk->conf.bs; - s->conf = &blk->conf; - s->blk = blk; - s->rq = NULL; - s->sector_mask = (s->conf->logical_block_size / BDRV_SECTOR_SIZE) - 1; - - s->vq = virtio_add_queue(&s->vdev, 128, virtio_blk_handle_output); - - qemu_add_vm_change_state_handler(virtio_blk_dma_restart_cb, s); - s->qdev = dev; - register_savevm(dev, "virtio-blk", virtio_blk_id++, 2, - virtio_blk_save, virtio_blk_load, s); - bdrv_set_dev_ops(s->bs, &virtio_block_ops, s); - bdrv_set_buffer_alignment(s->bs, s->conf->logical_block_size); - - bdrv_iostatus_enable(s->bs); - add_boot_device_path(s->conf->bootindex, dev, "/disk@0,0"); - - return &s->vdev; -} - -void virtio_blk_exit(VirtIODevice *vdev) -{ - VirtIOBlock *s = to_virtio_blk(vdev); - unregister_savevm(s->qdev, "virtio-blk", s); - blockdev_mark_auto_del(s->bs); - virtio_cleanup(vdev); -} - -/******************** VirtIOBlk Device **********************/ - -static int virtio_blkdev_init(DeviceState *dev) -{ - VirtIODevice *vdev; - VirtIOBlockState *s = VIRTIO_BLK_FROM_QDEV(dev); - - assert(s->trl != NULL); - - vdev = virtio_blk_init(dev, &s->blk); - if (!vdev) { - return -1; - } - - /* Pass default host_features to transport */ - s->trl->host_features = s->host_features; - - if (virtio_call_backend_init_cb(dev, s->trl, vdev) != 0) { - return -1; - } - - /* Binding should be ready here, let's get final features */ - if (vdev->binding->get_features) { - s->host_features = vdev->binding->get_features(vdev->binding_opaque); - } - return 0; -} - -static Property virtio_blkdev_properties[] = { - DEFINE_BLOCK_PROPERTIES(VirtIOBlockState, blk.conf), - DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlockState, blk.conf), - DEFINE_PROP_STRING("serial", VirtIOBlockState, blk.serial), -#ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtIOBlockState, blk.scsi, 0, true), -#endif - DEFINE_PROP_BIT("config-wce", VirtIOBlockState, blk.config_wce, 0, true), - DEFINE_VIRTIO_BLK_FEATURES(VirtIOBlockState, host_features), - - DEFINE_PROP_TRANSPORT("transport", VirtIOBlockState, trl), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_blkdev_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - dc->init = virtio_blkdev_init; - dc->props = virtio_blkdev_properties; -} - -static TypeInfo virtio_blkdev_info = { - .name = "virtio-blk", - .parent = TYPE_DEVICE, - .instance_size = sizeof(VirtIOBlockState), - .class_init = virtio_blkdev_class_init, -}; - -static void virtio_blk_register_types(void) -{ - type_register_static(&virtio_blkdev_info); -} - -type_init(virtio_blk_register_types) diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h deleted file mode 100644 index 0886818ac9..0000000000 --- a/hw/virtio-blk.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Virtio Block Device - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef _QEMU_VIRTIO_BLK_H -#define _QEMU_VIRTIO_BLK_H - -#include "sysbus.h" -#include "virtio.h" -#include "virtio-transport.h" -#include "hw/block-common.h" - -/* from Linux's linux/virtio_blk.h */ - -/* The ID for virtio_block */ -#define VIRTIO_ID_BLOCK 2 - -/* Feature bits */ -#define VIRTIO_BLK_F_BARRIER 0 /* Does host support barriers? */ -#define VIRTIO_BLK_F_SIZE_MAX 1 /* Indicates maximum segment size */ -#define VIRTIO_BLK_F_SEG_MAX 2 /* Indicates maximum # of segments */ -#define VIRTIO_BLK_F_GEOMETRY 4 /* Indicates support of legacy geometry */ -#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ -#define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ -#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ -/* #define VIRTIO_BLK_F_IDENTIFY 8 ATA IDENTIFY supported, DEPRECATED */ -#define VIRTIO_BLK_F_WCE 9 /* write cache enabled */ -#define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ -#define VIRTIO_BLK_F_CONFIG_WCE 11 /* write cache configurable */ - -#define VIRTIO_BLK_ID_BYTES 20 /* ID string length */ - -struct virtio_blk_config -{ - uint64_t capacity; - uint32_t size_max; - uint32_t seg_max; - uint16_t cylinders; - uint8_t heads; - uint8_t sectors; - uint32_t blk_size; - uint8_t physical_block_exp; - uint8_t alignment_offset; - uint16_t min_io_size; - uint32_t opt_io_size; - uint8_t wce; -} QEMU_PACKED; - -/* These two define direction. */ -#define VIRTIO_BLK_T_IN 0 -#define VIRTIO_BLK_T_OUT 1 - -/* This bit says it's a scsi command, not an actual read or write. */ -#define VIRTIO_BLK_T_SCSI_CMD 2 - -/* Flush the volatile write cache */ -#define VIRTIO_BLK_T_FLUSH 4 - -/* return the device ID string */ -#define VIRTIO_BLK_T_GET_ID 8 - -/* Barrier before this op. */ -#define VIRTIO_BLK_T_BARRIER 0x80000000 - -/* This is the first element of the read scatter-gather list. */ -struct virtio_blk_outhdr -{ - /* VIRTIO_BLK_T* */ - uint32_t type; - /* io priority. */ - uint32_t ioprio; - /* Sector (ie. 512 byte offset) */ - uint64_t sector; -}; - -#define VIRTIO_BLK_S_OK 0 -#define VIRTIO_BLK_S_IOERR 1 -#define VIRTIO_BLK_S_UNSUPP 2 - -/* This is the last element of the write scatter-gather list */ -struct virtio_blk_inhdr -{ - unsigned char status; -}; - -/* SCSI pass-through header */ -struct virtio_scsi_inhdr -{ - uint32_t errors; - uint32_t data_len; - uint32_t sense_len; - uint32_t residual; -}; - -struct VirtIOBlkConf -{ - BlockConf conf; - char *serial; - uint32_t scsi; - uint32_t config_wce; -}; - -#define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ - DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ - DEFINE_PROP_BIT("config-wce", _state, _field, VIRTIO_BLK_F_CONFIG_WCE, true) - - -typedef struct { - DeviceState qdev; - /* virtio-blk */ - VirtIOBlkConf blk; - - uint32_t host_features; - - VirtIOTransportLink *trl; -} VirtIOBlockState; - -#define VIRTIO_BLK_FROM_QDEV(dev) DO_UPCAST(VirtIOBlockState, qdev, dev) - -#endif diff --git a/hw/virtio-net.c b/hw/virtio-net.c deleted file mode 100644 index b7cfb1c07d..0000000000 --- a/hw/virtio-net.c +++ /dev/null @@ -1,1141 +0,0 @@ -/* - * Virtio Network Device - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "iov.h" -#include "virtio.h" -#include "virtio-transport.h" -#include "virtio-pci.h" -#include "net.h" -#include "net/checksum.h" -#include "net/tap.h" -#include "qemu-error.h" -#include "qemu-timer.h" -#include "virtio-net.h" -#include "vhost_net.h" - -#define VIRTIO_NET_VM_VERSION 11 - -#define MAC_TABLE_ENTRIES 64 -#define MAX_VLAN (1 << 12) /* Per 802.1Q definition */ - -typedef struct VirtIONet -{ - VirtIODevice vdev; - uint8_t mac[ETH_ALEN]; - uint16_t status; - VirtQueue *rx_vq; - VirtQueue *tx_vq; - VirtQueue *ctrl_vq; - NICState *nic; - QEMUTimer *tx_timer; - QEMUBH *tx_bh; - uint32_t tx_timeout; - int32_t tx_burst; - int tx_waiting; - uint32_t has_vnet_hdr; - uint8_t has_ufo; - struct { - VirtQueueElement elem; - ssize_t len; - } async_tx; - int mergeable_rx_bufs; - uint8_t promisc; - uint8_t allmulti; - uint8_t alluni; - uint8_t nomulti; - uint8_t nouni; - uint8_t nobcast; - uint8_t vhost_started; - struct { - int in_use; - int first_multi; - uint8_t multi_overflow; - uint8_t uni_overflow; - uint8_t *macs; - } mac_table; - uint32_t *vlans; - DeviceState *qdev; -} VirtIONet; - -/* TODO - * - we could suppress RX interrupt if we were so inclined. - */ - -static VirtIONet *to_virtio_net(VirtIODevice *vdev) -{ - return (VirtIONet *)vdev; -} - -static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) -{ - VirtIONet *n = to_virtio_net(vdev); - struct virtio_net_config netcfg; - - stw_p(&netcfg.status, n->status); - memcpy(netcfg.mac, n->mac, ETH_ALEN); - memcpy(config, &netcfg, sizeof(netcfg)); -} - -static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) -{ - VirtIONet *n = to_virtio_net(vdev); - struct virtio_net_config netcfg; - - memcpy(&netcfg, config, sizeof(netcfg)); - - if (memcmp(netcfg.mac, n->mac, ETH_ALEN)) { - memcpy(n->mac, netcfg.mac, ETH_ALEN); - qemu_format_nic_info_str(&n->nic->nc, n->mac); - } -} - -static bool virtio_net_started(VirtIONet *n, uint8_t status) -{ - return (status & VIRTIO_CONFIG_S_DRIVER_OK) && - (n->status & VIRTIO_NET_S_LINK_UP) && n->vdev.vm_running; -} - -static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) -{ - if (!n->nic->nc.peer) { - return; - } - if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return; - } - - if (!tap_get_vhost_net(n->nic->nc.peer)) { - return; - } - if (!!n->vhost_started == virtio_net_started(n, status) && - !n->nic->nc.peer->link_down) { - return; - } - if (!n->vhost_started) { - int r; - if (!vhost_net_query(tap_get_vhost_net(n->nic->nc.peer), &n->vdev)) { - return; - } - r = vhost_net_start(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); - if (r < 0) { - error_report("unable to start vhost net: %d: " - "falling back on userspace virtio", -r); - } else { - n->vhost_started = 1; - } - } else { - vhost_net_stop(tap_get_vhost_net(n->nic->nc.peer), &n->vdev); - n->vhost_started = 0; - } -} - -static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) -{ - VirtIONet *n = to_virtio_net(vdev); - - virtio_net_vhost_status(n, status); - - if (!n->tx_waiting) { - return; - } - - if (virtio_net_started(n, status) && !n->vhost_started) { - if (n->tx_timer) { - qemu_mod_timer(n->tx_timer, - qemu_get_clock_ns(vm_clock) + n->tx_timeout); - } else { - qemu_bh_schedule(n->tx_bh); - } - } else { - if (n->tx_timer) { - qemu_del_timer(n->tx_timer); - } else { - qemu_bh_cancel(n->tx_bh); - } - } -} - -static void virtio_net_set_link_status(NetClientState *nc) -{ - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - uint16_t old_status = n->status; - - if (nc->link_down) - n->status &= ~VIRTIO_NET_S_LINK_UP; - else - n->status |= VIRTIO_NET_S_LINK_UP; - - if (n->status != old_status) - virtio_notify_config(&n->vdev); - - virtio_net_set_status(&n->vdev, n->vdev.status); -} - -static void virtio_net_reset(VirtIODevice *vdev) -{ - VirtIONet *n = to_virtio_net(vdev); - - /* Reset back to compatibility mode */ - n->promisc = 1; - n->allmulti = 0; - n->alluni = 0; - n->nomulti = 0; - n->nouni = 0; - n->nobcast = 0; - - /* Flush any MAC and VLAN filter table state */ - n->mac_table.in_use = 0; - n->mac_table.first_multi = 0; - n->mac_table.multi_overflow = 0; - n->mac_table.uni_overflow = 0; - memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); - memset(n->vlans, 0, MAX_VLAN >> 3); -} - -static int peer_has_vnet_hdr(VirtIONet *n) -{ - if (!n->nic->nc.peer) - return 0; - - if (n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) - return 0; - - n->has_vnet_hdr = tap_has_vnet_hdr(n->nic->nc.peer); - - return n->has_vnet_hdr; -} - -static int peer_has_ufo(VirtIONet *n) -{ - if (!peer_has_vnet_hdr(n)) - return 0; - - n->has_ufo = tap_has_ufo(n->nic->nc.peer); - - return n->has_ufo; -} - -static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) -{ - VirtIONet *n = to_virtio_net(vdev); - - features |= (1 << VIRTIO_NET_F_MAC); - - if (peer_has_vnet_hdr(n)) { - tap_using_vnet_hdr(n->nic->nc.peer, 1); - } else { - features &= ~(0x1 << VIRTIO_NET_F_CSUM); - features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO4); - features &= ~(0x1 << VIRTIO_NET_F_HOST_TSO6); - features &= ~(0x1 << VIRTIO_NET_F_HOST_ECN); - - features &= ~(0x1 << VIRTIO_NET_F_GUEST_CSUM); - features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO4); - features &= ~(0x1 << VIRTIO_NET_F_GUEST_TSO6); - features &= ~(0x1 << VIRTIO_NET_F_GUEST_ECN); - } - - if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { - features &= ~(0x1 << VIRTIO_NET_F_GUEST_UFO); - features &= ~(0x1 << VIRTIO_NET_F_HOST_UFO); - } - - if (!n->nic->nc.peer || - n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return features; - } - if (!tap_get_vhost_net(n->nic->nc.peer)) { - return features; - } - return vhost_net_get_features(tap_get_vhost_net(n->nic->nc.peer), features); -} - -static uint32_t virtio_net_bad_features(VirtIODevice *vdev) -{ - uint32_t features = 0; - - /* Linux kernel 2.6.25. It understood MAC (as everyone must), - * but also these: */ - features |= (1 << VIRTIO_NET_F_MAC); - features |= (1 << VIRTIO_NET_F_CSUM); - features |= (1 << VIRTIO_NET_F_HOST_TSO4); - features |= (1 << VIRTIO_NET_F_HOST_TSO6); - features |= (1 << VIRTIO_NET_F_HOST_ECN); - - return features; -} - -static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) -{ - VirtIONet *n = to_virtio_net(vdev); - - n->mergeable_rx_bufs = !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF)); - - if (n->has_vnet_hdr) { - tap_set_offload(n->nic->nc.peer, - (features >> VIRTIO_NET_F_GUEST_CSUM) & 1, - (features >> VIRTIO_NET_F_GUEST_TSO4) & 1, - (features >> VIRTIO_NET_F_GUEST_TSO6) & 1, - (features >> VIRTIO_NET_F_GUEST_ECN) & 1, - (features >> VIRTIO_NET_F_GUEST_UFO) & 1); - } - if (!n->nic->nc.peer || - n->nic->nc.peer->info->type != NET_CLIENT_OPTIONS_KIND_TAP) { - return; - } - if (!tap_get_vhost_net(n->nic->nc.peer)) { - return; - } - vhost_net_ack_features(tap_get_vhost_net(n->nic->nc.peer), features); -} - -static int virtio_net_handle_rx_mode(VirtIONet *n, uint8_t cmd, - VirtQueueElement *elem) -{ - uint8_t on; - - if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(on)) { - error_report("virtio-net ctrl invalid rx mode command"); - exit(1); - } - - on = ldub_p(elem->out_sg[1].iov_base); - - if (cmd == VIRTIO_NET_CTRL_RX_MODE_PROMISC) - n->promisc = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLMULTI) - n->allmulti = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_ALLUNI) - n->alluni = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOMULTI) - n->nomulti = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOUNI) - n->nouni = on; - else if (cmd == VIRTIO_NET_CTRL_RX_MODE_NOBCAST) - n->nobcast = on; - else - return VIRTIO_NET_ERR; - - return VIRTIO_NET_OK; -} - -static int virtio_net_handle_mac(VirtIONet *n, uint8_t cmd, - VirtQueueElement *elem) -{ - struct virtio_net_ctrl_mac mac_data; - - if (cmd != VIRTIO_NET_CTRL_MAC_TABLE_SET || elem->out_num != 3 || - elem->out_sg[1].iov_len < sizeof(mac_data) || - elem->out_sg[2].iov_len < sizeof(mac_data)) - return VIRTIO_NET_ERR; - - n->mac_table.in_use = 0; - n->mac_table.first_multi = 0; - n->mac_table.uni_overflow = 0; - n->mac_table.multi_overflow = 0; - memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); - - mac_data.entries = ldl_p(elem->out_sg[1].iov_base); - - if (sizeof(mac_data.entries) + - (mac_data.entries * ETH_ALEN) > elem->out_sg[1].iov_len) - return VIRTIO_NET_ERR; - - if (mac_data.entries <= MAC_TABLE_ENTRIES) { - memcpy(n->mac_table.macs, elem->out_sg[1].iov_base + sizeof(mac_data), - mac_data.entries * ETH_ALEN); - n->mac_table.in_use += mac_data.entries; - } else { - n->mac_table.uni_overflow = 1; - } - - n->mac_table.first_multi = n->mac_table.in_use; - - mac_data.entries = ldl_p(elem->out_sg[2].iov_base); - - if (sizeof(mac_data.entries) + - (mac_data.entries * ETH_ALEN) > elem->out_sg[2].iov_len) - return VIRTIO_NET_ERR; - - if (mac_data.entries) { - if (n->mac_table.in_use + mac_data.entries <= MAC_TABLE_ENTRIES) { - memcpy(n->mac_table.macs + (n->mac_table.in_use * ETH_ALEN), - elem->out_sg[2].iov_base + sizeof(mac_data), - mac_data.entries * ETH_ALEN); - n->mac_table.in_use += mac_data.entries; - } else { - n->mac_table.multi_overflow = 1; - } - } - - return VIRTIO_NET_OK; -} - -static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, - VirtQueueElement *elem) -{ - uint16_t vid; - - if (elem->out_num != 2 || elem->out_sg[1].iov_len != sizeof(vid)) { - error_report("virtio-net ctrl invalid vlan command"); - return VIRTIO_NET_ERR; - } - - vid = lduw_p(elem->out_sg[1].iov_base); - - if (vid >= MAX_VLAN) - return VIRTIO_NET_ERR; - - if (cmd == VIRTIO_NET_CTRL_VLAN_ADD) - n->vlans[vid >> 5] |= (1U << (vid & 0x1f)); - else if (cmd == VIRTIO_NET_CTRL_VLAN_DEL) - n->vlans[vid >> 5] &= ~(1U << (vid & 0x1f)); - else - return VIRTIO_NET_ERR; - - return VIRTIO_NET_OK; -} - -static void virtio_net_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIONet *n = to_virtio_net(vdev); - struct virtio_net_ctrl_hdr ctrl; - virtio_net_ctrl_ack status = VIRTIO_NET_ERR; - VirtQueueElement elem; - - while (virtqueue_pop(vq, &elem)) { - if ((elem.in_num < 1) || (elem.out_num < 1)) { - error_report("virtio-net ctrl missing headers"); - exit(1); - } - - if (elem.out_sg[0].iov_len < sizeof(ctrl) || - elem.in_sg[elem.in_num - 1].iov_len < sizeof(status)) { - error_report("virtio-net ctrl header not in correct element"); - exit(1); - } - - ctrl.class = ldub_p(elem.out_sg[0].iov_base); - ctrl.cmd = ldub_p(elem.out_sg[0].iov_base + sizeof(ctrl.class)); - - if (ctrl.class == VIRTIO_NET_CTRL_RX_MODE) - status = virtio_net_handle_rx_mode(n, ctrl.cmd, &elem); - else if (ctrl.class == VIRTIO_NET_CTRL_MAC) - status = virtio_net_handle_mac(n, ctrl.cmd, &elem); - else if (ctrl.class == VIRTIO_NET_CTRL_VLAN) - status = virtio_net_handle_vlan_table(n, ctrl.cmd, &elem); - - stb_p(elem.in_sg[elem.in_num - 1].iov_base, status); - - virtqueue_push(vq, &elem, sizeof(status)); - virtio_notify(vdev, vq); - } -} - -/* RX */ - -static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIONet *n = to_virtio_net(vdev); - - qemu_flush_queued_packets(&n->nic->nc); - - /* We now have RX buffers, signal to the IO thread to break out of the - * select to re-poll the tap file descriptor */ - qemu_notify_event(); -} - -static int virtio_net_can_receive(NetClientState *nc) -{ - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - if (!n->vdev.vm_running) { - return 0; - } - - if (!virtio_queue_ready(n->rx_vq) || - !(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) - return 0; - - return 1; -} - -static int virtio_net_has_buffers(VirtIONet *n, int bufsize) -{ - if (virtio_queue_empty(n->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) { - virtio_queue_set_notification(n->rx_vq, 1); - - /* To avoid a race condition where the guest has made some buffers - * available after the above check but before notification was - * enabled, check for available buffers again. - */ - if (virtio_queue_empty(n->rx_vq) || - (n->mergeable_rx_bufs && - !virtqueue_avail_bytes(n->rx_vq, bufsize, 0))) - return 0; - } - - virtio_queue_set_notification(n->rx_vq, 0); - return 1; -} - -/* 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 - * fix this with Xen but it hasn't appeared in an upstream release of - * dhclient yet. - * - * To avoid breaking existing guests, we catch udp packets and add - * checksums. This is terrible but it's better than hacking the guest - * kernels. - * - * N.B. if we introduce a zero-copy API, this operation is no longer free so - * we should provide a mechanism to disable it to avoid polluting the host - * cache. - */ -static void work_around_broken_dhclient(struct virtio_net_hdr *hdr, - const uint8_t *buf, size_t size) -{ - if ((hdr->flags & VIRTIO_NET_HDR_F_NEEDS_CSUM) && /* missing csum */ - (size > 27 && size < 1500) && /* normal sized MTU */ - (buf[12] == 0x08 && buf[13] == 0x00) && /* ethertype == IPv4 */ - (buf[23] == 17) && /* ip.protocol == UDP */ - (buf[34] == 0 && buf[35] == 67)) { /* udp.srcport == bootps */ - /* FIXME this cast is evil */ - net_checksum_calculate((uint8_t *)buf, size); - hdr->flags &= ~VIRTIO_NET_HDR_F_NEEDS_CSUM; - } -} - -static int receive_header(VirtIONet *n, struct iovec *iov, int iovcnt, - const void *buf, size_t size, size_t hdr_len) -{ - struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)iov[0].iov_base; - int offset = 0; - - hdr->flags = 0; - hdr->gso_type = VIRTIO_NET_HDR_GSO_NONE; - - if (n->has_vnet_hdr) { - memcpy(hdr, buf, sizeof(*hdr)); - offset = sizeof(*hdr); - work_around_broken_dhclient(hdr, buf + offset, size - offset); - } - - /* We only ever receive a struct virtio_net_hdr from the tapfd, - * but we may be passing along a larger header to the guest. - */ - iov[0].iov_base += hdr_len; - iov[0].iov_len -= hdr_len; - - return offset; -} - -static int receive_filter(VirtIONet *n, const uint8_t *buf, int size) -{ - static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - static const uint8_t vlan[] = {0x81, 0x00}; - uint8_t *ptr = (uint8_t *)buf; - int i; - - if (n->promisc) - return 1; - - if (n->has_vnet_hdr) { - ptr += sizeof(struct virtio_net_hdr); - } - - if (!memcmp(&ptr[12], vlan, sizeof(vlan))) { - int vid = be16_to_cpup((uint16_t *)(ptr + 14)) & 0xfff; - if (!(n->vlans[vid >> 5] & (1U << (vid & 0x1f)))) - return 0; - } - - if (ptr[0] & 1) { // multicast - if (!memcmp(ptr, bcast, sizeof(bcast))) { - return !n->nobcast; - } else if (n->nomulti) { - return 0; - } else if (n->allmulti || n->mac_table.multi_overflow) { - return 1; - } - - for (i = n->mac_table.first_multi; i < n->mac_table.in_use; i++) { - if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { - return 1; - } - } - } else { // unicast - if (n->nouni) { - return 0; - } else if (n->alluni || n->mac_table.uni_overflow) { - return 1; - } else if (!memcmp(ptr, n->mac, ETH_ALEN)) { - return 1; - } - - for (i = 0; i < n->mac_table.first_multi; i++) { - if (!memcmp(ptr, &n->mac_table.macs[i * ETH_ALEN], ETH_ALEN)) { - return 1; - } - } - } - - return 0; -} - -static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t size) -{ - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - struct virtio_net_hdr_mrg_rxbuf *mhdr = NULL; - size_t guest_hdr_len, offset, i, host_hdr_len; - - if (!virtio_net_can_receive(&n->nic->nc)) - return -1; - - /* hdr_len refers to the header we supply to the guest */ - guest_hdr_len = n->mergeable_rx_bufs ? - sizeof(struct virtio_net_hdr_mrg_rxbuf) : sizeof(struct virtio_net_hdr); - - - host_hdr_len = n->has_vnet_hdr ? sizeof(struct virtio_net_hdr) : 0; - if (!virtio_net_has_buffers(n, size + guest_hdr_len - host_hdr_len)) - return 0; - - if (!receive_filter(n, buf, size)) - return size; - - offset = i = 0; - - while (offset < size) { - VirtQueueElement elem; - int len, total; - struct iovec sg[VIRTQUEUE_MAX_SIZE]; - - total = 0; - - if (virtqueue_pop(n->rx_vq, &elem) == 0) { - if (i == 0) - return -1; - error_report("virtio-net unexpected empty queue: " - "i %zd mergeable %d offset %zd, size %zd, " - "guest hdr len %zd, host hdr len %zd guest features 0x%x", - i, n->mergeable_rx_bufs, offset, size, - guest_hdr_len, host_hdr_len, n->vdev.guest_features); - exit(1); - } - - if (elem.in_num < 1) { - error_report("virtio-net receive queue contains no in buffers"); - exit(1); - } - - if (!n->mergeable_rx_bufs && elem.in_sg[0].iov_len != guest_hdr_len) { - error_report("virtio-net header not in first element"); - exit(1); - } - - memcpy(&sg, &elem.in_sg[0], sizeof(sg[0]) * elem.in_num); - - if (i == 0) { - if (n->mergeable_rx_bufs) - mhdr = (struct virtio_net_hdr_mrg_rxbuf *)sg[0].iov_base; - - offset += receive_header(n, sg, elem.in_num, - buf + offset, size - offset, guest_hdr_len); - total += guest_hdr_len; - } - - /* copy in packet. ugh */ - len = iov_from_buf(sg, elem.in_num, 0, - buf + offset, size - offset); - total += len; - offset += len; - /* If buffers can't be merged, at this point we - * must have consumed the complete packet. - * Otherwise, drop it. */ - if (!n->mergeable_rx_bufs && offset < size) { -#if 0 - error_report("virtio-net truncated non-mergeable packet: " - "i %zd mergeable %d offset %zd, size %zd, " - "guest hdr len %zd, host hdr len %zd", - i, n->mergeable_rx_bufs, - offset, size, guest_hdr_len, host_hdr_len); -#endif - return size; - } - - /* signal other side */ - virtqueue_fill(n->rx_vq, &elem, total, i++); - } - - if (mhdr) { - stw_p(&mhdr->num_buffers, i); - } - - virtqueue_flush(n->rx_vq, i); - virtio_notify(&n->vdev, n->rx_vq); - - return size; -} - -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq); - -static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) -{ - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - - virtqueue_push(n->tx_vq, &n->async_tx.elem, n->async_tx.len); - virtio_notify(&n->vdev, n->tx_vq); - - n->async_tx.elem.out_num = n->async_tx.len = 0; - - virtio_queue_set_notification(n->tx_vq, 1); - virtio_net_flush_tx(n, n->tx_vq); -} - -/* TX */ -static int32_t virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq) -{ - VirtQueueElement elem; - int32_t num_packets = 0; - if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return num_packets; - } - - assert(n->vdev.vm_running); - - if (n->async_tx.elem.out_num) { - virtio_queue_set_notification(n->tx_vq, 0); - return num_packets; - } - - while (virtqueue_pop(vq, &elem)) { - ssize_t ret, len = 0; - unsigned int out_num = elem.out_num; - struct iovec *out_sg = &elem.out_sg[0]; - unsigned hdr_len; - - /* hdr_len refers to the header received from the guest */ - hdr_len = n->mergeable_rx_bufs ? - sizeof(struct virtio_net_hdr_mrg_rxbuf) : - sizeof(struct virtio_net_hdr); - - if (out_num < 1 || out_sg->iov_len != hdr_len) { - error_report("virtio-net header not in first element"); - exit(1); - } - - /* ignore the header if GSO is not supported */ - if (!n->has_vnet_hdr) { - out_num--; - out_sg++; - len += hdr_len; - } else if (n->mergeable_rx_bufs) { - /* tapfd expects a struct virtio_net_hdr */ - hdr_len -= sizeof(struct virtio_net_hdr); - out_sg->iov_len -= hdr_len; - len += hdr_len; - } - - ret = qemu_sendv_packet_async(&n->nic->nc, out_sg, out_num, - virtio_net_tx_complete); - if (ret == 0) { - virtio_queue_set_notification(n->tx_vq, 0); - n->async_tx.elem = elem; - n->async_tx.len = len; - return -EBUSY; - } - - len += ret; - - virtqueue_push(vq, &elem, len); - virtio_notify(&n->vdev, vq); - - if (++num_packets >= n->tx_burst) { - break; - } - } - return num_packets; -} - -static void virtio_net_handle_tx_timer(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIONet *n = to_virtio_net(vdev); - - /* This happens when device was stopped but VCPU wasn't. */ - if (!n->vdev.vm_running) { - n->tx_waiting = 1; - return; - } - - if (n->tx_waiting) { - virtio_queue_set_notification(vq, 1); - qemu_del_timer(n->tx_timer); - n->tx_waiting = 0; - virtio_net_flush_tx(n, vq); - } else { - qemu_mod_timer(n->tx_timer, - qemu_get_clock_ns(vm_clock) + n->tx_timeout); - n->tx_waiting = 1; - virtio_queue_set_notification(vq, 0); - } -} - -static void virtio_net_handle_tx_bh(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIONet *n = to_virtio_net(vdev); - - if (unlikely(n->tx_waiting)) { - return; - } - n->tx_waiting = 1; - /* This happens when device was stopped but VCPU wasn't. */ - if (!n->vdev.vm_running) { - return; - } - virtio_queue_set_notification(vq, 0); - qemu_bh_schedule(n->tx_bh); -} - -static void virtio_net_tx_timer(void *opaque) -{ - VirtIONet *n = opaque; - assert(n->vdev.vm_running); - - n->tx_waiting = 0; - - /* Just in case the driver is not ready on more */ - if (!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) - return; - - virtio_queue_set_notification(n->tx_vq, 1); - virtio_net_flush_tx(n, n->tx_vq); -} - -static void virtio_net_tx_bh(void *opaque) -{ - VirtIONet *n = opaque; - int32_t ret; - - assert(n->vdev.vm_running); - - n->tx_waiting = 0; - - /* Just in case the driver is not ready on more */ - if (unlikely(!(n->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK))) - return; - - ret = virtio_net_flush_tx(n, n->tx_vq); - if (ret == -EBUSY) { - return; /* Notification re-enable handled by tx_complete */ - } - - /* If we flush a full burst of packets, assume there are - * more coming and immediately reschedule */ - if (ret >= n->tx_burst) { - qemu_bh_schedule(n->tx_bh); - n->tx_waiting = 1; - return; - } - - /* If less than a full burst, re-enable notification and flush - * anything that may have come in while we weren't looking. If - * we find something, assume the guest is still active and reschedule */ - virtio_queue_set_notification(n->tx_vq, 1); - if (virtio_net_flush_tx(n, n->tx_vq) > 0) { - virtio_queue_set_notification(n->tx_vq, 0); - qemu_bh_schedule(n->tx_bh); - n->tx_waiting = 1; - } -} - -static void virtio_net_save(QEMUFile *f, void *opaque) -{ - VirtIONet *n = opaque; - - /* At this point, backend must be stopped, otherwise - * it might keep writing to memory. */ - assert(!n->vhost_started); - virtio_save(&n->vdev, f); - - qemu_put_buffer(f, n->mac, ETH_ALEN); - qemu_put_be32(f, n->tx_waiting); - qemu_put_be32(f, n->mergeable_rx_bufs); - qemu_put_be16(f, n->status); - qemu_put_byte(f, n->promisc); - qemu_put_byte(f, n->allmulti); - qemu_put_be32(f, n->mac_table.in_use); - qemu_put_buffer(f, n->mac_table.macs, n->mac_table.in_use * ETH_ALEN); - qemu_put_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); - qemu_put_be32(f, n->has_vnet_hdr); - qemu_put_byte(f, n->mac_table.multi_overflow); - qemu_put_byte(f, n->mac_table.uni_overflow); - qemu_put_byte(f, n->alluni); - qemu_put_byte(f, n->nomulti); - qemu_put_byte(f, n->nouni); - qemu_put_byte(f, n->nobcast); - qemu_put_byte(f, n->has_ufo); -} - -static int virtio_net_load(QEMUFile *f, void *opaque, int version_id) -{ - VirtIONet *n = opaque; - int i; - int ret; - - if (version_id < 2 || version_id > VIRTIO_NET_VM_VERSION) - return -EINVAL; - - ret = virtio_load(&n->vdev, f); - if (ret) { - return ret; - } - - qemu_get_buffer(f, n->mac, ETH_ALEN); - n->tx_waiting = qemu_get_be32(f); - n->mergeable_rx_bufs = qemu_get_be32(f); - - if (version_id >= 3) - n->status = qemu_get_be16(f); - - if (version_id >= 4) { - if (version_id < 8) { - n->promisc = qemu_get_be32(f); - n->allmulti = qemu_get_be32(f); - } else { - n->promisc = qemu_get_byte(f); - n->allmulti = qemu_get_byte(f); - } - } - - if (version_id >= 5) { - n->mac_table.in_use = qemu_get_be32(f); - /* MAC_TABLE_ENTRIES may be different from the saved image */ - if (n->mac_table.in_use <= MAC_TABLE_ENTRIES) { - qemu_get_buffer(f, n->mac_table.macs, - n->mac_table.in_use * ETH_ALEN); - } else if (n->mac_table.in_use) { - qemu_fseek(f, n->mac_table.in_use * ETH_ALEN, SEEK_CUR); - n->mac_table.multi_overflow = n->mac_table.uni_overflow = 1; - n->mac_table.in_use = 0; - } - } - - if (version_id >= 6) - qemu_get_buffer(f, (uint8_t *)n->vlans, MAX_VLAN >> 3); - - if (version_id >= 7) { - if (qemu_get_be32(f) && !peer_has_vnet_hdr(n)) { - error_report("virtio-net: saved image requires vnet_hdr=on"); - return -1; - } - - if (n->has_vnet_hdr) { - tap_using_vnet_hdr(n->nic->nc.peer, 1); - tap_set_offload(n->nic->nc.peer, - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_CSUM) & 1, - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO4) & 1, - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_TSO6) & 1, - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_ECN) & 1, - (n->vdev.guest_features >> VIRTIO_NET_F_GUEST_UFO) & 1); - } - } - - if (version_id >= 9) { - n->mac_table.multi_overflow = qemu_get_byte(f); - n->mac_table.uni_overflow = qemu_get_byte(f); - } - - if (version_id >= 10) { - n->alluni = qemu_get_byte(f); - n->nomulti = qemu_get_byte(f); - n->nouni = qemu_get_byte(f); - n->nobcast = qemu_get_byte(f); - } - - if (version_id >= 11) { - if (qemu_get_byte(f) && !peer_has_ufo(n)) { - error_report("virtio-net: saved image requires TUN_F_UFO support"); - return -1; - } - } - - /* Find the first multicast entry in the saved MAC filter */ - for (i = 0; i < n->mac_table.in_use; i++) { - if (n->mac_table.macs[i * ETH_ALEN] & 1) { - break; - } - } - n->mac_table.first_multi = i; - return 0; -} - -static void virtio_net_cleanup(NetClientState *nc) -{ - VirtIONet *n = DO_UPCAST(NICState, nc, nc)->opaque; - - n->nic = NULL; -} - -static NetClientInfo net_virtio_info = { - .type = NET_CLIENT_OPTIONS_KIND_NIC, - .size = sizeof(NICState), - .can_receive = virtio_net_can_receive, - .receive = virtio_net_receive, - .cleanup = virtio_net_cleanup, - .link_status_changed = virtio_net_set_link_status, -}; - -VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, - virtio_net_conf *net) -{ - VirtIONet *n; - - n = (VirtIONet *)virtio_common_init("virtio-net", VIRTIO_ID_NET, - sizeof(struct virtio_net_config), - sizeof(VirtIONet)); - - n->vdev.get_config = virtio_net_get_config; - n->vdev.set_config = virtio_net_set_config; - n->vdev.get_features = virtio_net_get_features; - n->vdev.set_features = virtio_net_set_features; - n->vdev.bad_features = virtio_net_bad_features; - n->vdev.reset = virtio_net_reset; - n->vdev.set_status = virtio_net_set_status; - n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); - - if (net->tx && strcmp(net->tx, "timer") && strcmp(net->tx, "bh")) { - error_report("virtio-net: " - "Unknown option tx=%s, valid options: \"timer\" \"bh\"", - net->tx); - error_report("Defaulting to \"bh\""); - } - - if (net->tx && !strcmp(net->tx, "timer")) { - n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_timer); - n->tx_timer = qemu_new_timer_ns(vm_clock, virtio_net_tx_timer, n); - n->tx_timeout = net->txtimer; - } else { - n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx_bh); - n->tx_bh = qemu_bh_new(virtio_net_tx_bh, n); - } - n->ctrl_vq = virtio_add_queue(&n->vdev, 64, virtio_net_handle_ctrl); - qemu_macaddr_default_if_unset(&conf->macaddr); - memcpy(&n->mac[0], &conf->macaddr, sizeof(n->mac)); - n->status = VIRTIO_NET_S_LINK_UP; - - n->nic = qemu_new_nic(&net_virtio_info, conf, object_get_typename(OBJECT(dev)), dev->id, n); - - qemu_format_nic_info_str(&n->nic->nc, conf->macaddr.a); - - n->tx_waiting = 0; - n->tx_burst = net->txburst; - n->mergeable_rx_bufs = 0; - n->promisc = 1; /* for compatibility */ - - n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); - - n->vlans = g_malloc0(MAX_VLAN >> 3); - - n->qdev = dev; - register_savevm(dev, "virtio-net", -1, VIRTIO_NET_VM_VERSION, - virtio_net_save, virtio_net_load, n); - - add_boot_device_path(conf->bootindex, dev, "/ethernet-phy@0"); - - return &n->vdev; -} - -void virtio_net_exit(VirtIODevice *vdev) -{ - VirtIONet *n = DO_UPCAST(VirtIONet, vdev, vdev); - - /* This will stop vhost backend if appropriate. */ - virtio_net_set_status(vdev, 0); - - qemu_purge_queued_packets(&n->nic->nc); - - unregister_savevm(n->qdev, "virtio-net", n); - - g_free(n->mac_table.macs); - g_free(n->vlans); - - if (n->tx_timer) { - qemu_del_timer(n->tx_timer); - qemu_free_timer(n->tx_timer); - } else { - qemu_bh_delete(n->tx_bh); - } - - qemu_del_net_client(&n->nic->nc); - virtio_cleanup(&n->vdev); -} - -/******************** VirtIONet Device **********************/ - -static int virtio_netdev_init(DeviceState *dev) -{ - VirtIODevice *vdev; - VirtIONetState *s = VIRTIO_NET_FROM_QDEV(dev); - - assert(s->trl != NULL); - - vdev = virtio_net_init(dev, &s->nic, &s->net); - - /* Pass default host_features to transport */ - s->trl->host_features = s->host_features; - - if (virtio_call_backend_init_cb(dev, s->trl, vdev) != 0) { - return -1; - } - - /* Binding should be ready here, let's get final features */ - if (vdev->binding->get_features) { - s->host_features = vdev->binding->get_features(vdev->binding_opaque); - } - return 0; -} - -static Property virtio_net_properties[] = { - DEFINE_VIRTIO_NET_FEATURES(VirtIONetState, host_features), - DEFINE_NIC_PROPERTIES(VirtIONetState, nic), - DEFINE_PROP_UINT32("x-txtimer", VirtIONetState, net.txtimer, - TX_TIMER_INTERVAL), - DEFINE_PROP_INT32("x-txburst", VirtIONetState, net.txburst, TX_BURST), - DEFINE_PROP_STRING("tx", VirtIONetState, net.tx), - DEFINE_PROP_TRANSPORT("transport", VirtIONetState, trl), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_net_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - dc->init = virtio_netdev_init; - dc->props = virtio_net_properties; -} - -static TypeInfo virtio_net_info = { - .name = "virtio-net", - .parent = TYPE_DEVICE, - .instance_size = sizeof(VirtIONetState), - .class_init = virtio_net_class_init, -}; - -static void virtio_net_register_types(void) -{ - type_register_static(&virtio_net_info); -} - -type_init(virtio_net_register_types) diff --git a/hw/virtio-net.h b/hw/virtio-net.h deleted file mode 100644 index 8dd49d3c6b..0000000000 --- a/hw/virtio-net.h +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Virtio Network Device - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef _QEMU_VIRTIO_NET_H -#define _QEMU_VIRTIO_NET_H - -#include "sysbus.h" -#include "virtio.h" -#include "virtio-transport.h" -#include "net.h" -#include "pci.h" - -#define ETH_ALEN 6 - -/* from Linux's virtio_net.h */ - -/* The ID for virtio_net */ -#define VIRTIO_ID_NET 1 - -/* The feature bitmap for virtio net */ -#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ -#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ -#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address. */ -#define VIRTIO_NET_F_GSO 6 /* Host handles pkts w/ any GSO type */ -#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in. */ -#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in. */ -#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in. */ -#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in. */ -#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in. */ -#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in. */ -#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in. */ -#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in. */ -#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ -#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ -#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ -#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ -#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ -#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ - -#define VIRTIO_NET_S_LINK_UP 1 /* Link is up */ - -#define TX_TIMER_INTERVAL 150000 /* 150 us */ - -/* Limit the number of packets that can be sent via a single flush - * of the TX queue. This gives us a guaranteed exit condition and - * ensures fairness in the io path. 256 conveniently matches the - * length of the TX queue and shows a good balance of performance - * and latency. */ -#define TX_BURST 256 - -typedef struct virtio_net_conf -{ - uint32_t txtimer; - int32_t txburst; - char *tx; -} virtio_net_conf; - -/* Maximum packet size we can receive from tap device: header + 64k */ -#define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 << 10)) - -struct virtio_net_config -{ - /* The config defining mac address ($ETH_ALEN bytes) */ - uint8_t mac[ETH_ALEN]; - /* See VIRTIO_NET_F_STATUS and VIRTIO_NET_S_* above */ - uint16_t status; -} QEMU_PACKED; - -/* This is the first element of the scatter-gather list. If you don't - * specify GSO or CSUM features, you can simply ignore the header. */ -struct virtio_net_hdr -{ -#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 // Use csum_start, csum_offset -#define VIRTIO_NET_HDR_F_DATA_VALID 2 // Csum is valid - uint8_t flags; -#define VIRTIO_NET_HDR_GSO_NONE 0 // Not a GSO frame -#define VIRTIO_NET_HDR_GSO_TCPV4 1 // GSO frame, IPv4 TCP (TSO) -#define VIRTIO_NET_HDR_GSO_UDP 3 // GSO frame, IPv4 UDP (UFO) -#define VIRTIO_NET_HDR_GSO_TCPV6 4 // GSO frame, IPv6 TCP -#define VIRTIO_NET_HDR_GSO_ECN 0x80 // TCP has ECN set - uint8_t gso_type; - uint16_t hdr_len; - uint16_t gso_size; - uint16_t csum_start; - uint16_t csum_offset; -}; - -/* This is the version of the header to use when the MRG_RXBUF - * feature has been negotiated. */ -struct virtio_net_hdr_mrg_rxbuf -{ - struct virtio_net_hdr hdr; - uint16_t num_buffers; /* Number of merged rx buffers */ -}; - -/* - * Control virtqueue data structures - * - * The control virtqueue expects a header in the first sg entry - * and an ack/status response in the last entry. Data for the - * command goes in between. - */ -struct virtio_net_ctrl_hdr { - uint8_t class; - uint8_t cmd; -}; - -typedef uint8_t virtio_net_ctrl_ack; - -#define VIRTIO_NET_OK 0 -#define VIRTIO_NET_ERR 1 - -/* - * Control the RX mode, ie. promisucous, allmulti, etc... - * All commands require an "out" sg entry containing a 1 byte - * state value, zero = disable, non-zero = enable. Commands - * 0 and 1 are supported with the VIRTIO_NET_F_CTRL_RX feature. - * Commands 2-5 are added with VIRTIO_NET_F_CTRL_RX_EXTRA. - */ -#define VIRTIO_NET_CTRL_RX_MODE 0 - #define VIRTIO_NET_CTRL_RX_MODE_PROMISC 0 - #define VIRTIO_NET_CTRL_RX_MODE_ALLMULTI 1 - #define VIRTIO_NET_CTRL_RX_MODE_ALLUNI 2 - #define VIRTIO_NET_CTRL_RX_MODE_NOMULTI 3 - #define VIRTIO_NET_CTRL_RX_MODE_NOUNI 4 - #define VIRTIO_NET_CTRL_RX_MODE_NOBCAST 5 - -/* - * Control the MAC filter table. - * - * The MAC filter table is managed by the hypervisor, the guest should - * assume the size is infinite. Filtering should be considered - * non-perfect, ie. based on hypervisor resources, the guest may - * received packets from sources not specified in the filter list. - * - * In addition to the class/cmd header, the TABLE_SET command requires - * two out scatterlists. Each contains a 4 byte count of entries followed - * by a concatenated byte stream of the ETH_ALEN MAC addresses. The - * first sg list contains unicast addresses, the second is for multicast. - * This functionality is present if the VIRTIO_NET_F_CTRL_RX feature - * is available. - */ -struct virtio_net_ctrl_mac { - uint32_t entries; - uint8_t macs[][ETH_ALEN]; -}; -#define VIRTIO_NET_CTRL_MAC 1 - #define VIRTIO_NET_CTRL_MAC_TABLE_SET 0 - -/* - * Control VLAN filtering - * - * The VLAN filter table is controlled via a simple ADD/DEL interface. - * VLAN IDs not added may be filterd by the hypervisor. Del is the - * opposite of add. Both commands expect an out entry containing a 2 - * byte VLAN ID. VLAN filterting is available with the - * VIRTIO_NET_F_CTRL_VLAN feature bit. - */ -#define VIRTIO_NET_CTRL_VLAN 2 - #define VIRTIO_NET_CTRL_VLAN_ADD 0 - #define VIRTIO_NET_CTRL_VLAN_DEL 1 - -#define DEFINE_VIRTIO_NET_FEATURES(_state, _field) \ - DEFINE_VIRTIO_COMMON_FEATURES(_state, _field), \ - DEFINE_PROP_BIT("csum", _state, _field, VIRTIO_NET_F_CSUM, true), \ - DEFINE_PROP_BIT("guest_csum", _state, _field, VIRTIO_NET_F_GUEST_CSUM, true), \ - DEFINE_PROP_BIT("gso", _state, _field, VIRTIO_NET_F_GSO, true), \ - DEFINE_PROP_BIT("guest_tso4", _state, _field, VIRTIO_NET_F_GUEST_TSO4, true), \ - DEFINE_PROP_BIT("guest_tso6", _state, _field, VIRTIO_NET_F_GUEST_TSO6, true), \ - DEFINE_PROP_BIT("guest_ecn", _state, _field, VIRTIO_NET_F_GUEST_ECN, true), \ - DEFINE_PROP_BIT("guest_ufo", _state, _field, VIRTIO_NET_F_GUEST_UFO, true), \ - DEFINE_PROP_BIT("host_tso4", _state, _field, VIRTIO_NET_F_HOST_TSO4, true), \ - DEFINE_PROP_BIT("host_tso6", _state, _field, VIRTIO_NET_F_HOST_TSO6, true), \ - DEFINE_PROP_BIT("host_ecn", _state, _field, VIRTIO_NET_F_HOST_ECN, true), \ - DEFINE_PROP_BIT("host_ufo", _state, _field, VIRTIO_NET_F_HOST_UFO, true), \ - DEFINE_PROP_BIT("mrg_rxbuf", _state, _field, VIRTIO_NET_F_MRG_RXBUF, true), \ - DEFINE_PROP_BIT("status", _state, _field, VIRTIO_NET_F_STATUS, true), \ - DEFINE_PROP_BIT("ctrl_vq", _state, _field, VIRTIO_NET_F_CTRL_VQ, true), \ - DEFINE_PROP_BIT("ctrl_rx", _state, _field, VIRTIO_NET_F_CTRL_RX, true), \ - DEFINE_PROP_BIT("ctrl_vlan", _state, _field, VIRTIO_NET_F_CTRL_VLAN, true), \ - DEFINE_PROP_BIT("ctrl_rx_extra", _state, _field, VIRTIO_NET_F_CTRL_RX_EXTRA, true) - -typedef struct { - DeviceState qdev; - /* virtio-net */ - NICConf nic; - virtio_net_conf net; - - uint32_t host_features; - - VirtIOTransportLink *trl; -} VirtIONetState; - -#define VIRTIO_NET_FROM_QDEV(dev) DO_UPCAST(VirtIONetState, qdev, dev) - -#endif diff --git a/hw/virtio-pci-new.c b/hw/virtio-pci-new.c deleted file mode 100644 index 04f0da46e8..0000000000 --- a/hw/virtio-pci-new.c +++ /dev/null @@ -1,925 +0,0 @@ -/* - * Virtio PCI Bindings - * - * Copyright IBM, Corp. 2007 - * Copyright (c) 2009 CodeSourcery - * - * Authors: - * Anthony Liguori - * Paul Brook - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include - -#include "virtio.h" -#include "virtio-transport.h" -#include "virtio-blk.h" -#include "virtio-net.h" -#include "virtio-serial.h" -#include "virtio-scsi.h" -#include "virtio-balloon.h" -#include "pci.h" -#include "qemu-error.h" -#include "msi.h" -#include "msix.h" -#include "net.h" -#include "loader.h" -#include "kvm.h" -#include "blockdev.h" -#include "virtio-pci.h" -#include "range.h" - -/* from Linux's linux/virtio_pci.h */ - -/* A 32-bit r/o bitmask of the features supported by the host */ -#define VIRTIO_PCI_HOST_FEATURES 0 - -/* A 32-bit r/w bitmask of features activated by the guest */ -#define VIRTIO_PCI_GUEST_FEATURES 4 - -/* A 32-bit r/w PFN for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_PFN 8 - -/* A 16-bit r/o queue size for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_NUM 12 - -/* A 16-bit r/w queue selector */ -#define VIRTIO_PCI_QUEUE_SEL 14 - -/* A 16-bit r/w queue notifier */ -#define VIRTIO_PCI_QUEUE_NOTIFY 16 - -/* An 8-bit device status register. */ -#define VIRTIO_PCI_STATUS 18 - -/* An 8-bit r/o interrupt status register. Reading the value will return the - * current contents of the ISR and will also clear it. This is effectively - * a read-and-acknowledge. */ -#define VIRTIO_PCI_ISR 19 - -/* MSI-X registers: only enabled if MSI-X is enabled. */ -/* A 16-bit vector for configuration changes. */ -#define VIRTIO_MSI_CONFIG_VECTOR 20 -/* A 16-bit vector for selected queue notifications. */ -#define VIRTIO_MSI_QUEUE_VECTOR 22 - -/* Config space size */ -#define VIRTIO_PCI_CONFIG_NOMSI 20 -#define VIRTIO_PCI_CONFIG_MSI 24 -#define VIRTIO_PCI_REGION_SIZE(dev) (msix_present(dev) ? \ - VIRTIO_PCI_CONFIG_MSI : \ - VIRTIO_PCI_CONFIG_NOMSI) - -/* The remaining space is defined by each driver as the per-driver - * configuration space */ -#define VIRTIO_PCI_CONFIG(dev) (msix_enabled(dev) ? \ - VIRTIO_PCI_CONFIG_MSI : \ - VIRTIO_PCI_CONFIG_NOMSI) - -/* How many bits to shift physical queue address written to QUEUE_PFN. - * 12 is historical, and due to x86 page size. */ -#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 - -/* Flags track per-device state like workarounds for quirks in older guests. */ -#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0) - -/* QEMU doesn't strictly need write barriers since everything runs in - * lock-step. We'll leave the calls to wmb() in though to make it obvious for - * KVM or if kqemu gets SMP support. - */ -#define wmb() do { } while (0) - -/* HACK for virtio to determine if it's running a big endian guest */ -bool virtio_is_big_endian(void); - -/* virtio device */ - -static void virtio_pci_notify(void *opaque, uint16_t vector) -{ - VirtIOPCI *s = opaque; - if (msix_enabled(&s->pci_dev)) { - msix_notify(&s->pci_dev, vector); - } - else { - qemu_set_irq(s->pci_dev.irq[0], s->vdev->isr & 1); - } -} - -static void virtio_pci_save_config(void * opaque, QEMUFile *f) -{ - VirtIOPCI *s = opaque; - pci_device_save(&s->pci_dev, f); - msix_save(&s->pci_dev, f); - if (msix_present(&s->pci_dev)) { - qemu_put_be16(f, s->vdev->config_vector); - } -} - -static void virtio_pci_save_queue(void * opaque, int n, QEMUFile *f) -{ - VirtIOPCI *s = opaque; - if (msix_present(&s->pci_dev)) { - qemu_put_be16(f, virtio_queue_vector(s->vdev, n)); - } -} - -static int virtio_pci_load_config(void * opaque, QEMUFile *f) -{ - VirtIOPCI *s = opaque; - int ret; - ret = pci_device_load(&s->pci_dev, f); - if (ret) { - return ret; - } - msix_unuse_all_vectors(&s->pci_dev); - msix_load(&s->pci_dev, f); - if (msix_present(&s->pci_dev)) { - qemu_get_be16s(f, &s->vdev->config_vector); - } else { - s->vdev->config_vector = VIRTIO_NO_VECTOR; - } - if (s->vdev->config_vector != VIRTIO_NO_VECTOR) { - return msix_vector_use(&s->pci_dev, s->vdev->config_vector); - } - return 0; -} - -static int virtio_pci_load_queue(void * opaque, int n, QEMUFile *f) -{ - VirtIOPCI *s = opaque; - uint16_t vector; - if (msix_present(&s->pci_dev)) { - qemu_get_be16s(f, &vector); - } else { - vector = VIRTIO_NO_VECTOR; - } - virtio_queue_set_vector(s->vdev, n, vector); - if (vector != VIRTIO_NO_VECTOR) { - return msix_vector_use(&s->pci_dev, vector); - } - return 0; -} - -static int virtio_pci_set_host_notifier_internal(VirtIOPCI *s, int n, - bool assign, bool set_handler) -{ - VirtQueue *vq = virtio_get_queue(s->vdev, n); - EventNotifier *notifier = virtio_queue_get_host_notifier(vq); - int r = 0; - - if (assign) { - r = event_notifier_init(notifier, 1); - if (r < 0) { - error_report("%s: unable to init event notifier: %d", __func__, r); - return r; - } - virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); - memory_region_add_eventfd(&s->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2, true, n, - notifier); - } else { - memory_region_del_eventfd(&s->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2, true, n, - notifier); - virtio_queue_set_host_notifier_fd_handler(vq, false, false); - event_notifier_cleanup(notifier); - } - return r; -} - -static void virtio_pci_start_ioeventfd(VirtIOPCI *s) -{ - int n, r; - - if (!(s->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) || - s->ioeventfd_disabled || - s->ioeventfd_started) { - return; - } - - for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(s->vdev, n)) { - continue; - } - - r = virtio_pci_set_host_notifier_internal(s, n, true, true); - if (r < 0) { - goto assign_error; - } - } - s->ioeventfd_started = true; - return; - -assign_error: - while (--n >= 0) { - if (!virtio_queue_get_num(s->vdev, n)) { - continue; - } - - r = virtio_pci_set_host_notifier_internal(s, n, false, false); - assert(r >= 0); - } - s->ioeventfd_started = false; - error_report("%s: failed. Fallback to a userspace (slower).", __func__); -} - -static void virtio_pci_stop_ioeventfd(VirtIOPCI *s) -{ - int r; - int n; - - if (!s->ioeventfd_started) { - return; - } - - for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(s->vdev, n)) { - continue; - } - - r = virtio_pci_set_host_notifier_internal(s, n, false, false); - assert(r >= 0); - } - s->ioeventfd_started = false; -} - -void virtio_pci_reset_(DeviceState *d) -{ - VirtIOPCI *s = - container_of(d, VirtIOPCI, pci_dev.qdev); - virtio_pci_stop_ioeventfd(s); - if (s->vdev) { - virtio_reset(s->vdev); - } - msix_unuse_all_vectors(&s->pci_dev); - s->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG; -} - -static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - VirtIOPCI *s = opaque; - VirtIODevice *vdev = s->vdev; - target_phys_addr_t pa; - - switch (addr) { - case VIRTIO_PCI_GUEST_FEATURES: - /* Guest does not negotiate properly? We have to assume nothing. */ - if (val & (1 << VIRTIO_F_BAD_FEATURE)) { - val = vdev->bad_features ? vdev->bad_features(vdev) : 0; - } - virtio_set_features(vdev, val); - break; - case VIRTIO_PCI_QUEUE_PFN: - pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; - if (pa == 0) { - virtio_pci_stop_ioeventfd(s); - virtio_reset(s->vdev); - msix_unuse_all_vectors(&s->pci_dev); - } - else - virtio_queue_set_addr(vdev, vdev->queue_sel, pa); - break; - case VIRTIO_PCI_QUEUE_SEL: - if (val < VIRTIO_PCI_QUEUE_MAX) - vdev->queue_sel = val; - break; - case VIRTIO_PCI_QUEUE_NOTIFY: - if (val < VIRTIO_PCI_QUEUE_MAX) { - virtio_queue_notify(vdev, val); - } - break; - case VIRTIO_PCI_STATUS: - if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) { - virtio_pci_stop_ioeventfd(s); - } - - virtio_set_status(vdev, val & 0xFF); - - if (val & VIRTIO_CONFIG_S_DRIVER_OK) { - virtio_pci_start_ioeventfd(s); - } - - if (vdev->status == 0) { - virtio_reset(s->vdev); - msix_unuse_all_vectors(&s->pci_dev); - } - - /* Linux before 2.6.34 sets the device as OK without enabling - the PCI device bus master bit. In this case we need to disable - some safety checks. */ - if ((val & VIRTIO_CONFIG_S_DRIVER_OK) && - !(s->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - s->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; - } - break; - case VIRTIO_MSI_CONFIG_VECTOR: - msix_vector_unuse(&s->pci_dev, vdev->config_vector); - /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&s->pci_dev, val) < 0) - val = VIRTIO_NO_VECTOR; - vdev->config_vector = val; - break; - case VIRTIO_MSI_QUEUE_VECTOR: - msix_vector_unuse(&s->pci_dev, - virtio_queue_vector(vdev, vdev->queue_sel)); - /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&s->pci_dev, val) < 0) - val = VIRTIO_NO_VECTOR; - virtio_queue_set_vector(vdev, vdev->queue_sel, val); - break; - default: - error_report("%s: unexpected address 0x%x value 0x%x", - __func__, addr, val); - break; - } -} - -static uint32_t virtio_ioport_read(VirtIOPCI *s, uint32_t addr) -{ - VirtIODevice *vdev = s->vdev; - uint32_t ret = 0xFFFFFFFF; - - switch (addr) { - case VIRTIO_PCI_HOST_FEATURES: - ret = s->host_features; - break; - case VIRTIO_PCI_GUEST_FEATURES: - ret = vdev->guest_features; - break; - case VIRTIO_PCI_QUEUE_PFN: - ret = virtio_queue_get_addr(vdev, vdev->queue_sel) - >> VIRTIO_PCI_QUEUE_ADDR_SHIFT; - break; - case VIRTIO_PCI_QUEUE_NUM: - ret = virtio_queue_get_num(vdev, vdev->queue_sel); - break; - case VIRTIO_PCI_QUEUE_SEL: - ret = vdev->queue_sel; - break; - case VIRTIO_PCI_STATUS: - ret = vdev->status; - break; - case VIRTIO_PCI_ISR: - /* reading from the ISR also clears it. */ - ret = vdev->isr; - vdev->isr = 0; - qemu_set_irq(s->pci_dev.irq[0], 0); - break; - case VIRTIO_MSI_CONFIG_VECTOR: - ret = vdev->config_vector; - break; - case VIRTIO_MSI_QUEUE_VECTOR: - ret = virtio_queue_vector(vdev, vdev->queue_sel); - break; - default: - break; - } - - return ret; -} - -static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr) -{ - VirtIOPCI *s = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&s->pci_dev); - if (addr < config) - return virtio_ioport_read(s, addr); - addr -= config; - return virtio_config_readb(s->vdev, addr); -} - -static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr) -{ - VirtIOPCI *s = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&s->pci_dev); - uint16_t val; - if (addr < config) - return virtio_ioport_read(s, addr); - addr -= config; - val = virtio_config_readw(s->vdev, addr); - if (virtio_is_big_endian()) { - /* - * virtio is odd, ioports are LE but config space is target native - * endian. However, in qemu, all PIO is LE, so we need to re-swap - * on BE targets - */ - val = bswap16(val); - } - return val; -} - -static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr) -{ - VirtIOPCI *s = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&s->pci_dev); - uint32_t val; - if (addr < config) - return virtio_ioport_read(s, addr); - addr -= config; - val = virtio_config_readl(s->vdev, addr); - if (virtio_is_big_endian()) { - val = bswap32(val); - } - return val; -} - -static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - VirtIOPCI *s = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&s->pci_dev); - if (addr < config) { - virtio_ioport_write(s, addr, val); - return; - } - addr -= config; - virtio_config_writeb(s->vdev, addr, val); -} - -static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val) -{ - VirtIOPCI *s = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&s->pci_dev); - if (addr < config) { - virtio_ioport_write(s, addr, val); - return; - } - addr -= config; - if (virtio_is_big_endian()) { - val = bswap16(val); - } - virtio_config_writew(s->vdev, addr, val); -} - -static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val) -{ - VirtIOPCI *s = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&s->pci_dev); - if (addr < config) { - virtio_ioport_write(s, addr, val); - return; - } - addr -= config; - if (virtio_is_big_endian()) { - val = bswap32(val); - } - virtio_config_writel(s->vdev, addr, val); -} - -static const MemoryRegionPortio virtio_portio[] = { - { 0, 0x10000, 1, .write = virtio_pci_config_writeb, }, - { 0, 0x10000, 2, .write = virtio_pci_config_writew, }, - { 0, 0x10000, 4, .write = virtio_pci_config_writel, }, - { 0, 0x10000, 1, .read = virtio_pci_config_readb, }, - { 0, 0x10000, 2, .read = virtio_pci_config_readw, }, - { 0, 0x10000, 4, .read = virtio_pci_config_readl, }, - PORTIO_END_OF_LIST() -}; - -static const MemoryRegionOps virtio_pci_config_ops = { - .old_portio = virtio_portio, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, - uint32_t val, int len) -{ - VirtIOPCI *s = DO_UPCAST(VirtIOPCI, - pci_dev, pci_dev); - - pci_default_write_config(pci_dev, address, val, len); - - if (range_covers_byte(address, len, PCI_COMMAND) && - !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) && - !(s->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) { - virtio_pci_stop_ioeventfd(s); - virtio_set_status(s->vdev, - s->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); - } -} - -static unsigned virtio_pci_get_features(void *opaque) -{ - VirtIOPCI *s = opaque; - return s->host_features; -} - -static int kvm_virtio_pci_vq_vector_use(VirtIOPCI *s, - unsigned int queue_no, - unsigned int vector, - MSIMessage msg) -{ - VirtQueue *vq = virtio_get_queue(s->vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); - VirtIOIRQFD *irqfd = &s->vector_irqfd[vector]; - int ret; - - if (irqfd->users == 0) { - ret = kvm_irqchip_add_msi_route(kvm_state, msg); - if (ret < 0) { - return ret; - } - irqfd->virq = ret; - } - irqfd->users++; - - ret = kvm_irqchip_add_irq_notifier(kvm_state, n, irqfd->virq); - if (ret < 0) { - if (--irqfd->users == 0) { - kvm_irqchip_release_virq(kvm_state, irqfd->virq); - } - return ret; - } - - virtio_queue_set_guest_notifier_fd_handler(vq, true, true); - return 0; -} - -static void kvm_virtio_pci_vq_vector_release(VirtIOPCI *s, - unsigned int queue_no, - unsigned int vector) -{ - VirtQueue *vq = virtio_get_queue(s->vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); - VirtIOIRQFD *irqfd = &s->vector_irqfd[vector]; - int ret; - - ret = kvm_irqchip_remove_irq_notifier(kvm_state, n, irqfd->virq); - assert(ret == 0); - - if (--irqfd->users == 0) { - kvm_irqchip_release_virq(kvm_state, irqfd->virq); - } - - virtio_queue_set_guest_notifier_fd_handler(vq, true, false); -} - -static int kvm_virtio_pci_vector_use(PCIDevice *dev, unsigned vector, - MSIMessage msg) -{ - VirtIOPCI *s = container_of(dev, VirtIOPCI, pci_dev); - VirtIODevice *vdev = s->vdev; - int ret, queue_no; - - for (queue_no = 0; queue_no < VIRTIO_PCI_QUEUE_MAX; queue_no++) { - if (!virtio_queue_get_num(vdev, queue_no)) { - break; - } - if (virtio_queue_vector(vdev, queue_no) != vector) { - continue; - } - ret = kvm_virtio_pci_vq_vector_use(s, queue_no, vector, msg); - if (ret < 0) { - goto undo; - } - } - return 0; - -undo: - while (--queue_no >= 0) { - if (virtio_queue_vector(vdev, queue_no) != vector) { - continue; - } - kvm_virtio_pci_vq_vector_release(s, queue_no, vector); - } - return ret; -} - -static void kvm_virtio_pci_vector_release(PCIDevice *dev, unsigned vector) -{ - VirtIOPCI *s = container_of(dev, VirtIOPCI, pci_dev); - VirtIODevice *vdev = s->vdev; - int queue_no; - - for (queue_no = 0; queue_no < VIRTIO_PCI_QUEUE_MAX; queue_no++) { - if (!virtio_queue_get_num(vdev, queue_no)) { - break; - } - if (virtio_queue_vector(vdev, queue_no) != vector) { - continue; - } - kvm_virtio_pci_vq_vector_release(s, queue_no, vector); - } -} - -static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign) -{ - VirtIOPCI *s = opaque; - VirtQueue *vq = virtio_get_queue(s->vdev, n); - EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); - - if (assign) { - int r = event_notifier_init(notifier, 0); - if (r < 0) { - return r; - } - virtio_queue_set_guest_notifier_fd_handler(vq, true, false); - } else { - virtio_queue_set_guest_notifier_fd_handler(vq, false, false); - event_notifier_cleanup(notifier); - } - - return 0; -} - -static bool virtio_pci_query_guest_notifiers(void *opaque) -{ - VirtIOPCI *s = opaque; - return msix_enabled(&s->pci_dev); -} - -static int virtio_pci_set_guest_notifiers(void *opaque, bool assign) -{ - VirtIOPCI *s = opaque; - VirtIODevice *vdev = s->vdev; - int r, n; - - /* Must unset vector notifier while guest notifier is still assigned */ - if (kvm_msi_via_irqfd_enabled() && !assign) { - msix_unset_vector_notifiers(&s->pci_dev); - g_free(s->vector_irqfd); - s->vector_irqfd = NULL; - } - - for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(vdev, n)) { - break; - } - - r = virtio_pci_set_guest_notifier(opaque, n, assign); - if (r < 0) { - goto assign_error; - } - } - - /* Must set vector notifier after guest notifier has been assigned */ - if (kvm_msi_via_irqfd_enabled() && assign) { - s->vector_irqfd = - g_malloc0(sizeof(*s->vector_irqfd) * - msix_nr_vectors_allocated(&s->pci_dev)); - r = msix_set_vector_notifiers(&s->pci_dev, - kvm_virtio_pci_vector_use, - kvm_virtio_pci_vector_release); - if (r < 0) { - goto assign_error; - } - } - - return 0; - -assign_error: - /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ - assert(assign); - while (--n >= 0) { - virtio_pci_set_guest_notifier(opaque, n, !assign); - } - return r; -} - -static int virtio_pci_set_host_notifier(void *opaque, int n, bool assign) -{ - VirtIOPCI *s = opaque; - - /* Stop using ioeventfd for virtqueue kick if the device starts using host - * notifiers. This makes it easy to avoid stepping on each others' toes. - */ - s->ioeventfd_disabled = assign; - if (assign) { - virtio_pci_stop_ioeventfd(s); - } - /* We don't need to start here: it's not needed because backend - * currently only stops on status change away from ok, - * reset, vmstop and such. If we do add code to start here, - * need to check vmstate, device state etc. */ - return virtio_pci_set_host_notifier_internal(s, n, assign, false); -} - -static void virtio_pci_vmstate_change(void *opaque, bool running) -{ - VirtIOPCI *s = opaque; - - if (running) { - /* Try to find out if the guest has bus master disabled, but is - in ready state. Then we have a buggy guest OS. */ - if ((s->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && - !(s->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - s->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; - } - virtio_pci_start_ioeventfd(s); - } else { - virtio_pci_stop_ioeventfd(s); - } -} - -static const VirtIOBindings virtio_pci_bindings = { - .notify = virtio_pci_notify, - .save_config = virtio_pci_save_config, - .load_config = virtio_pci_load_config, - .save_queue = virtio_pci_save_queue, - .load_queue = virtio_pci_load_queue, - .get_features = virtio_pci_get_features, - .query_guest_notifiers = virtio_pci_query_guest_notifiers, - .set_host_notifier = virtio_pci_set_host_notifier, - .set_guest_notifiers = virtio_pci_set_guest_notifiers, - .vmstate_change = virtio_pci_vmstate_change, -}; - -static void virtio_init_pci_(VirtIOPCI *s, VirtIODevice *vdev) -{ - uint8_t *config; - uint32_t size; - - s->vdev = vdev; - - config = s->pci_dev.config; - - if (s->class_code) { - pci_config_set_class(config, s->class_code); - } - pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID, - pci_get_word(config + PCI_VENDOR_ID)); - pci_set_word(config + PCI_SUBSYSTEM_ID, vdev->device_id); - config[PCI_INTERRUPT_PIN] = 1; - - if (vdev->nvectors && - msix_init_exclusive_bar(&s->pci_dev, vdev->nvectors, 1)) { - vdev->nvectors = 0; - } - - s->pci_dev.config_write = virtio_write_config; - - size = VIRTIO_PCI_REGION_SIZE(&s->pci_dev) + vdev->config_len; - if (size & (size-1)) - size = 1 << qemu_fls(size); - - memory_region_init_io(&s->bar, &virtio_pci_config_ops, s, - "virtio-pci", size); - pci_register_bar(&s->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, - &s->bar); - - if (!kvm_has_many_ioeventfds()) { - s->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; - } - - virtio_bind_device(vdev, &virtio_pci_bindings, s); - s->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; - s->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; - s->host_features = vdev->get_features(vdev, s->host_features); -} - -static void virtio_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCI *s = DO_UPCAST(VirtIOPCI, pci_dev, pci_dev); - - memory_region_destroy(&s->bar); - msix_uninit_exclusive_bar(pci_dev); -} - -static int virtio_pci_transport_cb(DeviceState *dev, VirtIODevice *vdev, - VirtIOTransportLink *trl) -{ - PCIDevice *pci_dev = DO_UPCAST(PCIDevice, qdev, trl->tr); - VirtIOPCI *s = DO_UPCAST(VirtIOPCI, pci_dev, pci_dev); - - virtio_plug_into_transport(dev, trl); - - // TODO: Figure out if props were explicitly set before - - /* Get default host_features passed from back-end */ - s->host_features = s->trl->host_features; - - switch (vdev->device_id) { - case VIRTIO_ID_BLOCK: - s->flags |= VIRTIO_PCI_FLAG_USE_IOEVENTFD; - s->nvectors = 2; - - if (s->class_code != PCI_CLASS_STORAGE_SCSI && - s->class_code != PCI_CLASS_STORAGE_OTHER) { - s->class_code = PCI_CLASS_STORAGE_SCSI; - } - - vdev->nvectors = s->nvectors; - pci_config_set_device_id(s->pci_dev.config, PCI_DEVICE_ID_VIRTIO_BLOCK); - pci_config_set_class(s->pci_dev.config, PCI_CLASS_STORAGE_SCSI); - virtio_init_pci_(s, vdev); - s->nvectors = vdev->nvectors; - break; - case VIRTIO_ID_NET: - s->nvectors = 3; - - /* load rom */ - pci_dev->romfile = g_strdup("pxe-virtio.rom"); - pci_add_option_rom(pci_dev, false); - - vdev->nvectors = s->nvectors; - pci_config_set_device_id(s->pci_dev.config, PCI_DEVICE_ID_VIRTIO_NET); - pci_config_set_class(s->pci_dev.config, PCI_CLASS_NETWORK_ETHERNET); - virtio_init_pci_(s, vdev); - s->nvectors = vdev->nvectors; - break; - case VIRTIO_ID_BALLOON: - break; - case VIRTIO_ID_SCSI: - break; - case VIRTIO_ID_CONSOLE: - { - break; - } - default: - fprintf(stderr, - "Unknown back-end device id: 0x%" PRIx16 "\n", vdev->device_id); - return -1; - } - - return 0; -} - -static int virtio_pci_device_init(PCIDevice *pci_dev) -{ - VirtIOPCI *s = - DO_UPCAST(VirtIOPCI, pci_dev, pci_dev); - - virtio_init_transport(&pci_dev->qdev, &s->trl, VIRTIO_PCI, - virtio_pci_transport_cb); - - return 0; -} - -static void virtio_pci_device_exit(PCIDevice *pci_dev) -{ - VirtIOPCI *s = - DO_UPCAST(VirtIOPCI, pci_dev, pci_dev); - - switch (s->vdev->device_id) { - case VIRTIO_ID_BLOCK: - virtio_pci_stop_ioeventfd(s); - virtio_blk_exit(s->vdev); - break; - case VIRTIO_ID_NET: - virtio_pci_stop_ioeventfd(s); - virtio_net_exit(s->vdev); - break; - case VIRTIO_ID_BALLOON: - virtio_pci_stop_ioeventfd(s); - virtio_balloon_exit(s->vdev); - break; - case VIRTIO_ID_SCSI: - virtio_scsi_exit(s->vdev); - break; - case VIRTIO_ID_CONSOLE: - virtio_pci_stop_ioeventfd(s); - virtio_serial_exit(s->vdev); - break; - default: - fprintf(stderr, - "Unknown back-end device id: 0x%" PRIx16 "\n", - s->vdev->device_id); - return; - } - - virtio_exit_pci(pci_dev); - - return; -} - -/******************** VirtIOPCI Device **********************/ - -static Property virtio_pci_properties[] = { - DEFINE_PROP_HEX32("class", VirtIOPCI, class_code, 0), - DEFINE_PROP_BIT("ioeventfd", VirtIOPCI, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), - DEFINE_PROP_UINT32("vectors", VirtIOPCI, nvectors, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->init = virtio_pci_device_init; - k->exit = virtio_pci_device_exit; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_OTHERS; - dc->reset = virtio_pci_reset_; - dc->props = virtio_pci_properties; -} - -static TypeInfo virtio_pci_info = { - .name = VIRTIO_PCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCI), - .class_init = virtio_pci_class_init, -}; - -/************************************************************/ - -static void virtio_pci_register_types(void) -{ - type_register_static(&virtio_pci_info); -} - -type_init(virtio_pci_register_types) diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c deleted file mode 100755 index d087b9958e..0000000000 --- a/hw/virtio-pci.c +++ /dev/null @@ -1,1356 +0,0 @@ -/* - * Virtio PCI Bindings - * - * Copyright IBM, Corp. 2007 - * Copyright (c) 2009 CodeSourcery - * - * Authors: - * Anthony Liguori - * Paul Brook - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - - -#include - -#include "virtio.h" -#include "virtio-blk.h" -#include "virtio-net.h" -#include "virtio-serial.h" -#include "virtio-scsi.h" -#include "pci.h" -#include "qemu-error.h" -#include "msi.h" -#include "msix.h" -#include "net.h" -#include "loader.h" -#include "kvm.h" -#include "blockdev.h" -#include "virtio-pci.h" -#include "range.h" -#ifdef CONFIG_MARU -#include "../tizen/src/hw/maru_device_ids.h" -#include "../tizen/src/hw/maru_virtio_evdi.h" -#include "../tizen/src/mloop_event.h" -#endif - -/* from Linux's linux/virtio_pci.h */ - -/* A 32-bit r/o bitmask of the features supported by the host */ -#define VIRTIO_PCI_HOST_FEATURES 0 - -/* A 32-bit r/w bitmask of features activated by the guest */ -#define VIRTIO_PCI_GUEST_FEATURES 4 - -/* A 32-bit r/w PFN for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_PFN 8 - -/* A 16-bit r/o queue size for the currently selected queue */ -#define VIRTIO_PCI_QUEUE_NUM 12 - -/* A 16-bit r/w queue selector */ -#define VIRTIO_PCI_QUEUE_SEL 14 - -/* A 16-bit r/w queue notifier */ -#define VIRTIO_PCI_QUEUE_NOTIFY 16 - -/* An 8-bit device status register. */ -#define VIRTIO_PCI_STATUS 18 - -/* An 8-bit r/o interrupt status register. Reading the value will return the - * current contents of the ISR and will also clear it. This is effectively - * a read-and-acknowledge. */ -#define VIRTIO_PCI_ISR 19 - -/* MSI-X registers: only enabled if MSI-X is enabled. */ -/* A 16-bit vector for configuration changes. */ -#define VIRTIO_MSI_CONFIG_VECTOR 20 -/* A 16-bit vector for selected queue notifications. */ -#define VIRTIO_MSI_QUEUE_VECTOR 22 - -/* Config space size */ -#define VIRTIO_PCI_CONFIG_NOMSI 20 -#define VIRTIO_PCI_CONFIG_MSI 24 -#define VIRTIO_PCI_REGION_SIZE(dev) (msix_present(dev) ? \ - VIRTIO_PCI_CONFIG_MSI : \ - VIRTIO_PCI_CONFIG_NOMSI) - -/* The remaining space is defined by each driver as the per-driver - * configuration space */ -#define VIRTIO_PCI_CONFIG(dev) (msix_enabled(dev) ? \ - VIRTIO_PCI_CONFIG_MSI : \ - VIRTIO_PCI_CONFIG_NOMSI) - -/* How many bits to shift physical queue address written to QUEUE_PFN. - * 12 is historical, and due to x86 page size. */ -#define VIRTIO_PCI_QUEUE_ADDR_SHIFT 12 - -/* Flags track per-device state like workarounds for quirks in older guests. */ -#define VIRTIO_PCI_FLAG_BUS_MASTER_BUG (1 << 0) - -/* QEMU doesn't strictly need write barriers since everything runs in - * lock-step. We'll leave the calls to wmb() in though to make it obvious for - * KVM or if kqemu gets SMP support. - */ -#define wmb() do { } while (0) - -/* HACK for virtio to determine if it's running a big endian guest */ -bool virtio_is_big_endian(void); - -/* virtio device */ - -static void virtio_pci_notify(void *opaque, uint16_t vector) -{ - VirtIOPCIProxy *proxy = opaque; - if (msix_enabled(&proxy->pci_dev)) - msix_notify(&proxy->pci_dev, vector); - else - qemu_set_irq(proxy->pci_dev.irq[0], proxy->vdev->isr & 1); -} - -static void virtio_pci_save_config(void * opaque, QEMUFile *f) -{ - VirtIOPCIProxy *proxy = opaque; - pci_device_save(&proxy->pci_dev, f); - msix_save(&proxy->pci_dev, f); - if (msix_present(&proxy->pci_dev)) - qemu_put_be16(f, proxy->vdev->config_vector); -} - -static void virtio_pci_save_queue(void * opaque, int n, QEMUFile *f) -{ - VirtIOPCIProxy *proxy = opaque; - if (msix_present(&proxy->pci_dev)) - qemu_put_be16(f, virtio_queue_vector(proxy->vdev, n)); -} - -static int virtio_pci_load_config(void * opaque, QEMUFile *f) -{ - VirtIOPCIProxy *proxy = opaque; - int ret; - ret = pci_device_load(&proxy->pci_dev, f); - if (ret) { - return ret; - } - msix_unuse_all_vectors(&proxy->pci_dev); - msix_load(&proxy->pci_dev, f); - if (msix_present(&proxy->pci_dev)) { - qemu_get_be16s(f, &proxy->vdev->config_vector); - } else { - proxy->vdev->config_vector = VIRTIO_NO_VECTOR; - } - if (proxy->vdev->config_vector != VIRTIO_NO_VECTOR) { - return msix_vector_use(&proxy->pci_dev, proxy->vdev->config_vector); - } - return 0; -} - -static int virtio_pci_load_queue(void * opaque, int n, QEMUFile *f) -{ - VirtIOPCIProxy *proxy = opaque; - uint16_t vector; - if (msix_present(&proxy->pci_dev)) { - qemu_get_be16s(f, &vector); - } else { - vector = VIRTIO_NO_VECTOR; - } - virtio_queue_set_vector(proxy->vdev, n, vector); - if (vector != VIRTIO_NO_VECTOR) { - return msix_vector_use(&proxy->pci_dev, vector); - } - return 0; -} - -static int virtio_pci_set_host_notifier_internal(VirtIOPCIProxy *proxy, - int n, bool assign, bool set_handler) -{ - VirtQueue *vq = virtio_get_queue(proxy->vdev, n); - EventNotifier *notifier = virtio_queue_get_host_notifier(vq); - int r = 0; - - if (assign) { - r = event_notifier_init(notifier, 1); - if (r < 0) { - error_report("%s: unable to init event notifier: %d", - __func__, r); - return r; - } - virtio_queue_set_host_notifier_fd_handler(vq, true, set_handler); - memory_region_add_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2, - true, n, notifier); - } else { - memory_region_del_eventfd(&proxy->bar, VIRTIO_PCI_QUEUE_NOTIFY, 2, - true, n, notifier); - virtio_queue_set_host_notifier_fd_handler(vq, false, false); - event_notifier_cleanup(notifier); - } - return r; -} - -static void virtio_pci_start_ioeventfd(VirtIOPCIProxy *proxy) -{ - int n, r; - - if (!(proxy->flags & VIRTIO_PCI_FLAG_USE_IOEVENTFD) || - proxy->ioeventfd_disabled || - proxy->ioeventfd_started) { - return; - } - - for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(proxy->vdev, n)) { - continue; - } - - r = virtio_pci_set_host_notifier_internal(proxy, n, true, true); - if (r < 0) { - goto assign_error; - } - } - proxy->ioeventfd_started = true; - return; - -assign_error: - while (--n >= 0) { - if (!virtio_queue_get_num(proxy->vdev, n)) { - continue; - } - - r = virtio_pci_set_host_notifier_internal(proxy, n, false, false); - assert(r >= 0); - } - proxy->ioeventfd_started = false; - error_report("%s: failed. Fallback to a userspace (slower).", __func__); -} - -static void virtio_pci_stop_ioeventfd(VirtIOPCIProxy *proxy) -{ - int r; - int n; - - if (!proxy->ioeventfd_started) { - return; - } - - for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(proxy->vdev, n)) { - continue; - } - - r = virtio_pci_set_host_notifier_internal(proxy, n, false, false); - assert(r >= 0); - } - proxy->ioeventfd_started = false; -} - -void virtio_pci_reset(DeviceState *d) -{ - VirtIOPCIProxy *proxy = container_of(d, VirtIOPCIProxy, pci_dev.qdev); - virtio_pci_stop_ioeventfd(proxy); - virtio_reset(proxy->vdev); - msix_unuse_all_vectors(&proxy->pci_dev); - proxy->flags &= ~VIRTIO_PCI_FLAG_BUS_MASTER_BUG; -} - -static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) -{ - VirtIOPCIProxy *proxy = opaque; - VirtIODevice *vdev = proxy->vdev; - target_phys_addr_t pa; - - switch (addr) { - case VIRTIO_PCI_GUEST_FEATURES: - /* Guest does not negotiate properly? We have to assume nothing. */ - if (val & (1 << VIRTIO_F_BAD_FEATURE)) { - val = vdev->bad_features ? vdev->bad_features(vdev) : 0; - } - virtio_set_features(vdev, val); - break; - case VIRTIO_PCI_QUEUE_PFN: - pa = (target_phys_addr_t)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; - if (pa == 0) { - virtio_pci_stop_ioeventfd(proxy); - virtio_reset(proxy->vdev); - msix_unuse_all_vectors(&proxy->pci_dev); - } - else - virtio_queue_set_addr(vdev, vdev->queue_sel, pa); - break; - case VIRTIO_PCI_QUEUE_SEL: - if (val < VIRTIO_PCI_QUEUE_MAX) - vdev->queue_sel = val; - break; - case VIRTIO_PCI_QUEUE_NOTIFY: - if (val < VIRTIO_PCI_QUEUE_MAX) { - virtio_queue_notify(vdev, val); - } - break; - case VIRTIO_PCI_STATUS: - if (!(val & VIRTIO_CONFIG_S_DRIVER_OK)) { - virtio_pci_stop_ioeventfd(proxy); - } - - virtio_set_status(vdev, val & 0xFF); - - if (val & VIRTIO_CONFIG_S_DRIVER_OK) { - virtio_pci_start_ioeventfd(proxy); - } - - if (vdev->status == 0) { - virtio_reset(proxy->vdev); - msix_unuse_all_vectors(&proxy->pci_dev); - } - - /* Linux before 2.6.34 sets the device as OK without enabling - the PCI device bus master bit. In this case we need to disable - some safety checks. */ - if ((val & VIRTIO_CONFIG_S_DRIVER_OK) && - !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; - } - break; - case VIRTIO_MSI_CONFIG_VECTOR: - msix_vector_unuse(&proxy->pci_dev, vdev->config_vector); - /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) - val = VIRTIO_NO_VECTOR; - vdev->config_vector = val; - break; - case VIRTIO_MSI_QUEUE_VECTOR: - msix_vector_unuse(&proxy->pci_dev, - virtio_queue_vector(vdev, vdev->queue_sel)); - /* Make it possible for guest to discover an error took place. */ - if (msix_vector_use(&proxy->pci_dev, val) < 0) - val = VIRTIO_NO_VECTOR; - virtio_queue_set_vector(vdev, vdev->queue_sel, val); - break; - default: - error_report("%s: unexpected address 0x%x value 0x%x", - __func__, addr, val); - break; - } -} - -static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr) -{ - VirtIODevice *vdev = proxy->vdev; - uint32_t ret = 0xFFFFFFFF; - - switch (addr) { - case VIRTIO_PCI_HOST_FEATURES: - ret = proxy->host_features; - break; - case VIRTIO_PCI_GUEST_FEATURES: - ret = vdev->guest_features; - break; - case VIRTIO_PCI_QUEUE_PFN: - ret = virtio_queue_get_addr(vdev, vdev->queue_sel) - >> VIRTIO_PCI_QUEUE_ADDR_SHIFT; - break; - case VIRTIO_PCI_QUEUE_NUM: - ret = virtio_queue_get_num(vdev, vdev->queue_sel); - break; - case VIRTIO_PCI_QUEUE_SEL: - ret = vdev->queue_sel; - break; - case VIRTIO_PCI_STATUS: - ret = vdev->status; - break; - case VIRTIO_PCI_ISR: - /* reading from the ISR also clears it. */ - ret = vdev->isr; - vdev->isr = 0; - qemu_set_irq(proxy->pci_dev.irq[0], 0); - break; - case VIRTIO_MSI_CONFIG_VECTOR: - ret = vdev->config_vector; - break; - case VIRTIO_MSI_QUEUE_VECTOR: - ret = virtio_queue_vector(vdev, vdev->queue_sel); - break; - default: - break; - } - - return ret; -} - -static uint32_t virtio_pci_config_readb(void *opaque, uint32_t addr) -{ - VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); - if (addr < config) - return virtio_ioport_read(proxy, addr); - addr -= config; - return virtio_config_readb(proxy->vdev, addr); -} - -static uint32_t virtio_pci_config_readw(void *opaque, uint32_t addr) -{ - VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); - uint16_t val; - if (addr < config) - return virtio_ioport_read(proxy, addr); - addr -= config; - val = virtio_config_readw(proxy->vdev, addr); - if (virtio_is_big_endian()) { - /* - * virtio is odd, ioports are LE but config space is target native - * endian. However, in qemu, all PIO is LE, so we need to re-swap - * on BE targets - */ - val = bswap16(val); - } - return val; -} - -static uint32_t virtio_pci_config_readl(void *opaque, uint32_t addr) -{ - VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); - uint32_t val; - if (addr < config) - return virtio_ioport_read(proxy, addr); - addr -= config; - val = virtio_config_readl(proxy->vdev, addr); - if (virtio_is_big_endian()) { - val = bswap32(val); - } - return val; -} - -static void virtio_pci_config_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); - if (addr < config) { - virtio_ioport_write(proxy, addr, val); - return; - } - addr -= config; - virtio_config_writeb(proxy->vdev, addr, val); -} - -static void virtio_pci_config_writew(void *opaque, uint32_t addr, uint32_t val) -{ - VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); - if (addr < config) { - virtio_ioport_write(proxy, addr, val); - return; - } - addr -= config; - if (virtio_is_big_endian()) { - val = bswap16(val); - } - virtio_config_writew(proxy->vdev, addr, val); -} - -static void virtio_pci_config_writel(void *opaque, uint32_t addr, uint32_t val) -{ - VirtIOPCIProxy *proxy = opaque; - uint32_t config = VIRTIO_PCI_CONFIG(&proxy->pci_dev); - if (addr < config) { - virtio_ioport_write(proxy, addr, val); - return; - } - addr -= config; - if (virtio_is_big_endian()) { - val = bswap32(val); - } - virtio_config_writel(proxy->vdev, addr, val); -} - -static const MemoryRegionPortio virtio_portio[] = { - { 0, 0x10000, 1, .write = virtio_pci_config_writeb, }, - { 0, 0x10000, 2, .write = virtio_pci_config_writew, }, - { 0, 0x10000, 4, .write = virtio_pci_config_writel, }, - { 0, 0x10000, 1, .read = virtio_pci_config_readb, }, - { 0, 0x10000, 2, .read = virtio_pci_config_readw, }, - { 0, 0x10000, 4, .read = virtio_pci_config_readl, }, - PORTIO_END_OF_LIST() -}; - -static const MemoryRegionOps virtio_pci_config_ops = { - .old_portio = virtio_portio, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, - uint32_t val, int len) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - pci_default_write_config(pci_dev, address, val, len); - - if (range_covers_byte(address, len, PCI_COMMAND) && - !(pci_dev->config[PCI_COMMAND] & PCI_COMMAND_MASTER) && - !(proxy->flags & VIRTIO_PCI_FLAG_BUS_MASTER_BUG)) { - virtio_pci_stop_ioeventfd(proxy); - virtio_set_status(proxy->vdev, - proxy->vdev->status & ~VIRTIO_CONFIG_S_DRIVER_OK); - } -} - -static unsigned virtio_pci_get_features(void *opaque) -{ - VirtIOPCIProxy *proxy = opaque; - return proxy->host_features; -} - -static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, - unsigned int queue_no, - unsigned int vector, - MSIMessage msg) -{ - VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); - VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - int ret; - - if (irqfd->users == 0) { - ret = kvm_irqchip_add_msi_route(kvm_state, msg); - if (ret < 0) { - return ret; - } - irqfd->virq = ret; - } - irqfd->users++; - - ret = kvm_irqchip_add_irq_notifier(kvm_state, n, irqfd->virq); - if (ret < 0) { - if (--irqfd->users == 0) { - kvm_irqchip_release_virq(kvm_state, irqfd->virq); - } - return ret; - } - - virtio_queue_set_guest_notifier_fd_handler(vq, true, true); - return 0; -} - -static void kvm_virtio_pci_vq_vector_release(VirtIOPCIProxy *proxy, - unsigned int queue_no, - unsigned int vector) -{ - VirtQueue *vq = virtio_get_queue(proxy->vdev, queue_no); - EventNotifier *n = virtio_queue_get_guest_notifier(vq); - VirtIOIRQFD *irqfd = &proxy->vector_irqfd[vector]; - int ret; - - ret = kvm_irqchip_remove_irq_notifier(kvm_state, n, irqfd->virq); - assert(ret == 0); - - if (--irqfd->users == 0) { - kvm_irqchip_release_virq(kvm_state, irqfd->virq); - } - - virtio_queue_set_guest_notifier_fd_handler(vq, true, false); -} - -static int kvm_virtio_pci_vector_use(PCIDevice *dev, unsigned vector, - MSIMessage msg) -{ - VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); - VirtIODevice *vdev = proxy->vdev; - int ret, queue_no; - - for (queue_no = 0; queue_no < VIRTIO_PCI_QUEUE_MAX; queue_no++) { - if (!virtio_queue_get_num(vdev, queue_no)) { - break; - } - if (virtio_queue_vector(vdev, queue_no) != vector) { - continue; - } - ret = kvm_virtio_pci_vq_vector_use(proxy, queue_no, vector, msg); - if (ret < 0) { - goto undo; - } - } - return 0; - -undo: - while (--queue_no >= 0) { - if (virtio_queue_vector(vdev, queue_no) != vector) { - continue; - } - kvm_virtio_pci_vq_vector_release(proxy, queue_no, vector); - } - return ret; -} - -static void kvm_virtio_pci_vector_release(PCIDevice *dev, unsigned vector) -{ - VirtIOPCIProxy *proxy = container_of(dev, VirtIOPCIProxy, pci_dev); - VirtIODevice *vdev = proxy->vdev; - int queue_no; - - for (queue_no = 0; queue_no < VIRTIO_PCI_QUEUE_MAX; queue_no++) { - if (!virtio_queue_get_num(vdev, queue_no)) { - break; - } - if (virtio_queue_vector(vdev, queue_no) != vector) { - continue; - } - kvm_virtio_pci_vq_vector_release(proxy, queue_no, vector); - } -} - -static int virtio_pci_set_guest_notifier(void *opaque, int n, bool assign) -{ - VirtIOPCIProxy *proxy = opaque; - VirtQueue *vq = virtio_get_queue(proxy->vdev, n); - EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); - - if (assign) { - int r = event_notifier_init(notifier, 0); - if (r < 0) { - return r; - } - virtio_queue_set_guest_notifier_fd_handler(vq, true, false); - } else { - virtio_queue_set_guest_notifier_fd_handler(vq, false, false); - event_notifier_cleanup(notifier); - } - - return 0; -} - -static bool virtio_pci_query_guest_notifiers(void *opaque) -{ - VirtIOPCIProxy *proxy = opaque; - return msix_enabled(&proxy->pci_dev); -} - -static int virtio_pci_set_guest_notifiers(void *opaque, bool assign) -{ - VirtIOPCIProxy *proxy = opaque; - VirtIODevice *vdev = proxy->vdev; - int r, n; - - /* Must unset vector notifier while guest notifier is still assigned */ - if (kvm_msi_via_irqfd_enabled() && !assign) { - msix_unset_vector_notifiers(&proxy->pci_dev); - g_free(proxy->vector_irqfd); - proxy->vector_irqfd = NULL; - } - - for (n = 0; n < VIRTIO_PCI_QUEUE_MAX; n++) { - if (!virtio_queue_get_num(vdev, n)) { - break; - } - - r = virtio_pci_set_guest_notifier(opaque, n, assign); - if (r < 0) { - goto assign_error; - } - } - - /* Must set vector notifier after guest notifier has been assigned */ - if (kvm_msi_via_irqfd_enabled() && assign) { - proxy->vector_irqfd = - g_malloc0(sizeof(*proxy->vector_irqfd) * - msix_nr_vectors_allocated(&proxy->pci_dev)); - r = msix_set_vector_notifiers(&proxy->pci_dev, - kvm_virtio_pci_vector_use, - kvm_virtio_pci_vector_release); - if (r < 0) { - goto assign_error; - } - } - - return 0; - -assign_error: - /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */ - assert(assign); - while (--n >= 0) { - virtio_pci_set_guest_notifier(opaque, n, !assign); - } - return r; -} - -static int virtio_pci_set_host_notifier(void *opaque, int n, bool assign) -{ - VirtIOPCIProxy *proxy = opaque; - - /* Stop using ioeventfd for virtqueue kick if the device starts using host - * notifiers. This makes it easy to avoid stepping on each others' toes. - */ - proxy->ioeventfd_disabled = assign; - if (assign) { - virtio_pci_stop_ioeventfd(proxy); - } - /* We don't need to start here: it's not needed because backend - * currently only stops on status change away from ok, - * reset, vmstop and such. If we do add code to start here, - * need to check vmstate, device state etc. */ - return virtio_pci_set_host_notifier_internal(proxy, n, assign, false); -} - -static void virtio_pci_vmstate_change(void *opaque, bool running) -{ - VirtIOPCIProxy *proxy = opaque; - - if (running) { - /* Try to find out if the guest has bus master disabled, but is - in ready state. Then we have a buggy guest OS. */ - if ((proxy->vdev->status & VIRTIO_CONFIG_S_DRIVER_OK) && - !(proxy->pci_dev.config[PCI_COMMAND] & PCI_COMMAND_MASTER)) { - proxy->flags |= VIRTIO_PCI_FLAG_BUS_MASTER_BUG; - } - virtio_pci_start_ioeventfd(proxy); - } else { - virtio_pci_stop_ioeventfd(proxy); - } -} - -static const VirtIOBindings virtio_pci_bindings = { - .notify = virtio_pci_notify, - .save_config = virtio_pci_save_config, - .load_config = virtio_pci_load_config, - .save_queue = virtio_pci_save_queue, - .load_queue = virtio_pci_load_queue, - .get_features = virtio_pci_get_features, - .query_guest_notifiers = virtio_pci_query_guest_notifiers, - .set_host_notifier = virtio_pci_set_host_notifier, - .set_guest_notifiers = virtio_pci_set_guest_notifiers, - .vmstate_change = virtio_pci_vmstate_change, -}; - -void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev) -{ - uint8_t *config; - uint32_t size; - - proxy->vdev = vdev; - - config = proxy->pci_dev.config; - - if (proxy->class_code) { - pci_config_set_class(config, proxy->class_code); - } - pci_set_word(config + PCI_SUBSYSTEM_VENDOR_ID, - pci_get_word(config + PCI_VENDOR_ID)); - pci_set_word(config + PCI_SUBSYSTEM_ID, vdev->device_id); - config[PCI_INTERRUPT_PIN] = 1; - - if (vdev->nvectors && - msix_init_exclusive_bar(&proxy->pci_dev, vdev->nvectors, 1)) { - vdev->nvectors = 0; - } - - proxy->pci_dev.config_write = virtio_write_config; - - size = VIRTIO_PCI_REGION_SIZE(&proxy->pci_dev) + vdev->config_len; - if (size & (size-1)) - size = 1 << qemu_fls(size); - - memory_region_init_io(&proxy->bar, &virtio_pci_config_ops, proxy, - "virtio-pci", size); - pci_register_bar(&proxy->pci_dev, 0, PCI_BASE_ADDRESS_SPACE_IO, - &proxy->bar); - - if (!kvm_has_many_ioeventfds()) { - proxy->flags &= ~VIRTIO_PCI_FLAG_USE_IOEVENTFD; - } - - virtio_bind_device(vdev, &virtio_pci_bindings, proxy); - proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; - proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; - proxy->host_features = vdev->get_features(vdev, proxy->host_features); -} - -static int virtio_blk_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - if (proxy->class_code != PCI_CLASS_STORAGE_SCSI && - proxy->class_code != PCI_CLASS_STORAGE_OTHER) - proxy->class_code = PCI_CLASS_STORAGE_SCSI; - - vdev = virtio_blk_init(&pci_dev->qdev, &proxy->blk); - if (!vdev) { - return -1; - } - vdev->nvectors = proxy->nvectors; - virtio_init_pci(proxy, vdev); - /* make the actual value visible */ - proxy->nvectors = vdev->nvectors; - return 0; -} - -static void virtio_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - memory_region_destroy(&proxy->bar); - msix_uninit_exclusive_bar(pci_dev); -} - -static void virtio_blk_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - virtio_blk_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -static int virtio_serial_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - if (proxy->class_code != PCI_CLASS_COMMUNICATION_OTHER && - proxy->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */ - proxy->class_code != PCI_CLASS_OTHERS) /* qemu-kvm */ - proxy->class_code = PCI_CLASS_COMMUNICATION_OTHER; - - vdev = virtio_serial_init(&pci_dev->qdev, &proxy->serial); - if (!vdev) { - return -1; - } - vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED - ? proxy->serial.max_virtserial_ports + 1 - : proxy->nvectors; - virtio_init_pci(proxy, vdev); - proxy->nvectors = vdev->nvectors; - return 0; -} - -static void virtio_serial_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - virtio_serial_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -static int virtio_net_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = virtio_net_init(&pci_dev->qdev, &proxy->nic, &proxy->net); - - vdev->nvectors = proxy->nvectors; - virtio_init_pci(proxy, vdev); - - /* make the actual value visible */ - proxy->nvectors = vdev->nvectors; - return 0; -} - -static void virtio_net_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - virtio_net_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -static int virtio_balloon_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - if (proxy->class_code != PCI_CLASS_OTHERS && - proxy->class_code != PCI_CLASS_MEMORY_RAM) { /* qemu < 1.1 */ - proxy->class_code = PCI_CLASS_OTHERS; - } - - vdev = virtio_balloon_init(&pci_dev->qdev); - if (!vdev) { - return -1; - } - virtio_init_pci(proxy, vdev); - return 0; -} - -static void virtio_balloon_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - virtio_balloon_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -#ifdef CONFIG_GL_BACKEND -#if defined(CONFIG_MARU) -static int virtio_gl_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = virtio_gl_init(&pci_dev->qdev); - if (!vdev) { - return -1; - } - virtio_init_pci(proxy, vdev); - return 0; -} -#endif -#endif - -#ifdef CONFIG_MARU -static int maru_virtio_touchscreen_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = maru_virtio_touchscreen_init(&pci_dev->qdev); - if (!vdev) { - return -1; - } - virtio_init_pci(proxy, vdev); - return 0; -} - -static void maru_virtio_touchscreen_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - maru_virtio_touchscreen_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -static int virtio_keyboard_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = virtio_keyboard_init(&pci_dev->qdev); - if (!vdev) { - return -1; - } - virtio_init_pci(proxy, vdev); - return 0; -} - -static void virtio_keyboard_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - virtio_keyboard_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -static int virtio_esm_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = virtio_esm_init(&pci_dev->qdev); - if (!vdev) { - return -1; - } - virtio_init_pci(proxy, vdev); - return 0; -} - -static void virtio_esm_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - virtio_esm_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -static int virtio_hwkey_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = maru_virtio_hwkey_init(&pci_dev->qdev); - if (!vdev) { - return -1; - } - virtio_init_pci(proxy, vdev); - return 0; -} - -static void virtio_hwkey_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - maru_virtio_hwkey_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -static int virtio_evdi_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = virtio_evdi_init(&pci_dev->qdev); - if (!vdev) { - return -1; - } - virtio_init_pci(proxy, vdev); - return 0; -} - -static void virtio_evdi_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_pci_stop_ioeventfd(proxy); - virtio_evdi_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -#endif - -static Property virtio_blk_properties[] = { - DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, blk.conf), - DEFINE_BLOCK_CHS_PROPERTIES(VirtIOPCIProxy, blk.conf), - DEFINE_PROP_STRING("serial", VirtIOPCIProxy, blk.serial), -#ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtIOPCIProxy, blk.scsi, 0, true), -#endif - DEFINE_PROP_BIT("config-wce", VirtIOPCIProxy, blk.config_wce, 0, true), - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_VIRTIO_BLK_FEATURES(VirtIOPCIProxy, host_features), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_blk_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_blk_init_pci; - k->exit = virtio_blk_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_STORAGE_SCSI; - dc->reset = virtio_pci_reset; - dc->props = virtio_blk_properties; -} - -static TypeInfo virtio_blk_info = { - .name = "virtio-blk-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_blk_class_init, -}; - -static Property virtio_net_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, false), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), - DEFINE_VIRTIO_NET_FEATURES(VirtIOPCIProxy, host_features), - DEFINE_NIC_PROPERTIES(VirtIOPCIProxy, nic), - DEFINE_PROP_UINT32("x-txtimer", VirtIOPCIProxy, net.txtimer, TX_TIMER_INTERVAL), - DEFINE_PROP_INT32("x-txburst", VirtIOPCIProxy, net.txburst, TX_BURST), - DEFINE_PROP_STRING("tx", VirtIOPCIProxy, net.tx), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_net_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_net_init_pci; - k->exit = virtio_net_exit_pci; - k->romfile = "pxe-virtio.rom"; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_NET; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_NETWORK_ETHERNET; - dc->reset = virtio_pci_reset; - dc->props = virtio_net_properties; -} - -static TypeInfo virtio_net_info = { - .name = "virtio-net-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_net_class_init, -}; - -static Property virtio_serial_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), - DEFINE_PROP_UINT32("max_ports", VirtIOPCIProxy, serial.max_virtserial_ports, 31), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_serial_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_serial_init_pci; - k->exit = virtio_serial_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_COMMUNICATION_OTHER; - dc->reset = virtio_pci_reset; - dc->props = virtio_serial_properties; -} - -static TypeInfo virtio_serial_info = { - .name = "virtio-serial-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_serial_class_init, -}; - -static Property virtio_balloon_properties[] = { - DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features), - DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_balloon_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_balloon_init_pci; - k->exit = virtio_balloon_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_OTHERS; - dc->reset = virtio_pci_reset; - dc->props = virtio_balloon_properties; -} - -static TypeInfo virtio_balloon_info = { - .name = "virtio-balloon-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_balloon_class_init, -}; - -static int virtio_scsi_init_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - VirtIODevice *vdev; - - vdev = virtio_scsi_init(&pci_dev->qdev, &proxy->scsi); - if (!vdev) { - return -EINVAL; - } - - vdev->nvectors = proxy->nvectors == DEV_NVECTORS_UNSPECIFIED - ? proxy->scsi.num_queues + 3 - : proxy->nvectors; - virtio_init_pci(proxy, vdev); - - /* make the actual value visible */ - proxy->nvectors = vdev->nvectors; - return 0; -} - -static void virtio_scsi_exit_pci(PCIDevice *pci_dev) -{ - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); - - virtio_scsi_exit(proxy->vdev); - virtio_exit_pci(pci_dev); -} - -static Property virtio_scsi_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, DEV_NVECTORS_UNSPECIFIED), - DEFINE_VIRTIO_SCSI_PROPERTIES(VirtIOPCIProxy, host_features, scsi), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_scsi_init_pci; - k->exit = virtio_scsi_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; - k->revision = 0x00; - k->class_id = PCI_CLASS_STORAGE_SCSI; - dc->reset = virtio_pci_reset; - dc->props = virtio_scsi_properties; -} - -static TypeInfo virtio_scsi_info = { - .name = "virtio-scsi-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_scsi_class_init, -}; - -#if defined(CONFIG_MARU) -#ifdef CONFIG_GL_BACKEND -static void virtio_gl_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_gl_init_pci; - k->exit = virtio_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_GL; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_OTHERS; - dc->reset = virtio_pci_reset; -} - -static TypeInfo virtio_gl_info = { - .name = "virtio-gl-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_gl_class_init, -}; -#endif -#endif - -#ifdef CONFIG_MARU -static void maru_virtio_touchscreen_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = maru_virtio_touchscreen_init_pci; - k->exit = maru_virtio_touchscreen_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_TOUCHSCREEN; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_OTHERS; - dc->reset = virtio_pci_reset; -} - -static TypeInfo maru_virtio_touchscreen_info = { - .name = "virtio-touchscreen-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = maru_virtio_touchscreen_class_init, -}; - -static void virtio_keyboard_class_init(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_keyboard_init_pci; - k->exit = virtio_keyboard_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_KEYBOARD; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_OTHERS; - dc->reset = virtio_pci_reset; -} - -static TypeInfo virtio_keyboard_info = { - .name = "virtio-keyboard-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_keyboard_class_init, -}; - -static void virtio_esm_class_init(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_esm_init_pci; - k->exit = virtio_esm_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_ESM; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_OTHERS; - dc->reset = virtio_pci_reset; -} - -static TypeInfo virtio_esm_info = { - .name = "virtio-esm-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_esm_class_init, -}; - -static void virtio_hwkey_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_hwkey_init_pci; - k->exit = virtio_hwkey_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_HWKEY; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_OTHERS; - dc->reset = virtio_pci_reset; -} - -static TypeInfo virtio_hwkey_info = { - .name = "virtio-hwkey-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_hwkey_class_init, -}; - -static void virtio_evdi_class_init(ObjectClass *klass, void *data) { - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->init = virtio_evdi_init_pci; - k->exit = virtio_evdi_exit_pci; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_EVDI; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_OTHERS; - dc->reset = virtio_pci_reset; -} - -static TypeInfo virtio_evdi_info = { - .name = "virtio-evdi-pci", - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(VirtIOPCIProxy), - .class_init = virtio_evdi_class_init, -}; - - -#endif /* CONFIG_MARU */ - -static void virtio_pci_register_types(void) -{ - type_register_static(&virtio_blk_info); - type_register_static(&virtio_net_info); - type_register_static(&virtio_serial_info); - type_register_static(&virtio_balloon_info); - type_register_static(&virtio_scsi_info); -#ifdef CONFIG_GL_BACKEND - type_register_static(&virtio_gl_info); -#endif -#ifdef CONFIG_MARU - type_register_static(&maru_virtio_touchscreen_info); - type_register_static(&virtio_keyboard_info); - type_register_static(&virtio_esm_info); - type_register_static(&virtio_hwkey_info); - type_register_static(&virtio_evdi_info); -#endif -} - -type_init(virtio_pci_register_types) diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h deleted file mode 100644 index 2cddd67808..0000000000 --- a/hw/virtio-pci.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Virtio PCI Bindings - * - * Copyright IBM, Corp. 2007 - * Copyright (c) 2009 CodeSourcery - * - * Authors: - * Anthony Liguori - * Paul Brook - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - */ - -#ifndef QEMU_VIRTIO_PCI_H -#define QEMU_VIRTIO_PCI_H - -#include "virtio-blk.h" -#include "virtio-net.h" -#include "virtio-serial.h" -#include "virtio-scsi.h" - -/* Performance improves when virtqueue kick processing is decoupled from the - * vcpu thread using ioeventfd for some devices. */ -#define VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT 1 -#define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) - -typedef struct { - int virq; - unsigned int users; -} VirtIOIRQFD; - -typedef struct { - PCIDevice pci_dev; - VirtIODevice *vdev; - MemoryRegion bar; - uint32_t flags; - uint32_t class_code; - uint32_t nvectors; - VirtIOBlkConf blk; - NICConf nic; - uint32_t host_features; -#ifdef CONFIG_LINUX - V9fsConf fsconf; -#endif - virtio_serial_conf serial; - virtio_net_conf net; - VirtIOSCSIConf scsi; - bool ioeventfd_disabled; - bool ioeventfd_started; - VirtIOIRQFD *vector_irqfd; -} VirtIOPCIProxy; - -typedef struct { - PCIDevice pci_dev; - VirtIODevice *vdev; - MemoryRegion bar; - - uint32_t flags; - uint32_t class_code; - uint32_t nvectors; - uint32_t host_features; - - VirtIOTransportLink *trl; - - bool ioeventfd_disabled; - bool ioeventfd_started; - VirtIOIRQFD *vector_irqfd; -} VirtIOPCI; - -void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev); -void virtio_pci_reset(DeviceState *d); -void virtio_pci_reset_(DeviceState *d); - -/* Virtio ABI version, if we increment this, we break the guest driver. */ -#define VIRTIO_PCI_ABI_VERSION 0 - -#endif diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c deleted file mode 100644 index 699a48503a..0000000000 --- a/hw/virtio-serial-bus.c +++ /dev/null @@ -1,1060 +0,0 @@ -/* - * A bus for connecting virtio serial and console ports - * - * Copyright (C) 2009, 2010 Red Hat, Inc. - * - * Author(s): - * Amit Shah - * - * Some earlier parts are: - * Copyright IBM, Corp. 2008 - * authored by - * Christian Ehrhardt - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#include "iov.h" -#include "monitor.h" -#include "qemu-queue.h" -#include "sysbus.h" -#include "trace.h" -#include "virtio-serial.h" -#include "virtio-transport.h" - -/* The virtio-serial bus on top of which the ports will ride as devices */ -struct VirtIOSerialBus { - BusState qbus; - - /* This is the parent device that provides the bus for ports. */ - VirtIOSerial *vser; - - /* The maximum number of ports that can ride on top of this bus */ - uint32_t max_nr_ports; -}; - -struct VirtIOSerial { - VirtIODevice vdev; - - VirtQueue *c_ivq, *c_ovq; - /* Arrays of ivqs and ovqs: one per port */ - VirtQueue **ivqs, **ovqs; - - VirtIOSerialBus bus; - - DeviceState *qdev; - - QTAILQ_HEAD(, VirtIOSerialPort) ports; - - /* bitmap for identifying active ports */ - uint32_t *ports_map; - - struct virtio_console_config config; -}; - -static VirtIOSerialPort *find_port_by_id(VirtIOSerial *vser, uint32_t id) -{ - VirtIOSerialPort *port; - - if (id == VIRTIO_CONSOLE_BAD_ID) { - return NULL; - } - - QTAILQ_FOREACH(port, &vser->ports, next) { - if (port->id == id) - return port; - } - return NULL; -} - -static VirtIOSerialPort *find_port_by_vq(VirtIOSerial *vser, VirtQueue *vq) -{ - VirtIOSerialPort *port; - - QTAILQ_FOREACH(port, &vser->ports, next) { - if (port->ivq == vq || port->ovq == vq) - return port; - } - return NULL; -} - -static bool use_multiport(VirtIOSerial *vser) -{ - return vser->vdev.guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT); -} - -static size_t write_to_port(VirtIOSerialPort *port, - const uint8_t *buf, size_t size) -{ - VirtQueueElement elem; - VirtQueue *vq; - size_t offset; - - vq = port->ivq; - if (!virtio_queue_ready(vq)) { - return 0; - } - - offset = 0; - while (offset < size) { - size_t len; - - if (!virtqueue_pop(vq, &elem)) { - break; - } - - len = iov_from_buf(elem.in_sg, elem.in_num, 0, - buf + offset, size - offset); - offset += len; - - virtqueue_push(vq, &elem, len); - } - - virtio_notify(&port->vser->vdev, vq); - return offset; -} - -static void discard_vq_data(VirtQueue *vq, VirtIODevice *vdev) -{ - VirtQueueElement elem; - - if (!virtio_queue_ready(vq)) { - return; - } - while (virtqueue_pop(vq, &elem)) { - virtqueue_push(vq, &elem, 0); - } - virtio_notify(vdev, vq); -} - -static void do_flush_queued_data(VirtIOSerialPort *port, VirtQueue *vq, - VirtIODevice *vdev) -{ - VirtIOSerialPortClass *vsc; - - assert(port); - assert(virtio_queue_ready(vq)); - - vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - - while (!port->throttled) { - unsigned int i; - - /* Pop an elem only if we haven't left off a previous one mid-way */ - if (!port->elem.out_num) { - if (!virtqueue_pop(vq, &port->elem)) { - break; - } - port->iov_idx = 0; - port->iov_offset = 0; - } - - for (i = port->iov_idx; i < port->elem.out_num; i++) { - size_t buf_size; - ssize_t ret; - - buf_size = port->elem.out_sg[i].iov_len - port->iov_offset; - ret = vsc->have_data(port, - port->elem.out_sg[i].iov_base - + port->iov_offset, - buf_size); - if (ret < 0 && ret != -EAGAIN) { - /* We don't handle any other type of errors here */ - abort(); - } - if (ret == -EAGAIN || (ret >= 0 && ret < buf_size)) { - /* - * this is a temporary check until chardevs can signal to - * frontends that they are writable again. This prevents - * the console from going into throttled mode (forever) - * if virtio-console is connected to a pty without a - * listener. Otherwise the guest spins forever. - * We can revert this if - * 1: chardevs can notify frondends - * 2: the guest driver does not spin in these cases - */ - if (!vsc->is_console) { - virtio_serial_throttle_port(port, true); - } - port->iov_idx = i; - if (ret > 0) { - port->iov_offset += ret; - } - break; - } - port->iov_offset = 0; - } - if (port->throttled) { - break; - } - virtqueue_push(vq, &port->elem, 0); - port->elem.out_num = 0; - } - virtio_notify(vdev, vq); -} - -static void flush_queued_data(VirtIOSerialPort *port) -{ - assert(port); - - if (!virtio_queue_ready(port->ovq)) { - return; - } - do_flush_queued_data(port, port->ovq, &port->vser->vdev); -} - -static size_t send_control_msg(VirtIOSerialPort *port, void *buf, size_t len) -{ - VirtQueueElement elem; - VirtQueue *vq; - struct virtio_console_control *cpkt; - - vq = port->vser->c_ivq; - if (!virtio_queue_ready(vq)) { - return 0; - } - if (!virtqueue_pop(vq, &elem)) { - return 0; - } - - cpkt = (struct virtio_console_control *)buf; - stl_p(&cpkt->id, port->id); - memcpy(elem.in_sg[0].iov_base, buf, len); - - virtqueue_push(vq, &elem, len); - virtio_notify(&port->vser->vdev, vq); - return len; -} - -static size_t send_control_event(VirtIOSerialPort *port, uint16_t event, - uint16_t value) -{ - struct virtio_console_control cpkt; - - stw_p(&cpkt.event, event); - stw_p(&cpkt.value, value); - - trace_virtio_serial_send_control_event(port->id, event, value); - return send_control_msg(port, &cpkt, sizeof(cpkt)); -} - -/* Functions for use inside qemu to open and read from/write to ports */ -int virtio_serial_open(VirtIOSerialPort *port) -{ - /* Don't allow opening an already-open port */ - if (port->host_connected) { - return 0; - } - /* Send port open notification to the guest */ - port->host_connected = true; - send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); - - return 0; -} - -int virtio_serial_close(VirtIOSerialPort *port) -{ - port->host_connected = false; - /* - * If there's any data the guest sent which the app didn't - * consume, reset the throttling flag and discard the data. - */ - port->throttled = false; - discard_vq_data(port->ovq, &port->vser->vdev); - - send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0); - - return 0; -} - -/* Individual ports/apps call this function to write to the guest. */ -ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, - size_t size) -{ - if (!port || !port->host_connected || !port->guest_connected) { - return 0; - } - return write_to_port(port, buf, size); -} - -/* - * Readiness of the guest to accept data on a port. - * Returns max. data the guest can receive - */ -size_t virtio_serial_guest_ready(VirtIOSerialPort *port) -{ - VirtQueue *vq = port->ivq; - - if (!virtio_queue_ready(vq) || - !(port->vser->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || - virtio_queue_empty(vq)) { - return 0; - } - if (use_multiport(port->vser) && !port->guest_connected) { - return 0; - } - - if (virtqueue_avail_bytes(vq, 4096, 0)) { - return 4096; - } - if (virtqueue_avail_bytes(vq, 1, 0)) { - return 1; - } - return 0; -} - -static void flush_queued_data_bh(void *opaque) -{ - VirtIOSerialPort *port = opaque; - - flush_queued_data(port); -} - -void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle) -{ - if (!port) { - return; - } - - trace_virtio_serial_throttle_port(port->id, throttle); - port->throttled = throttle; - if (throttle) { - return; - } - qemu_bh_schedule(port->bh); -} - -/* Guest wants to notify us of some event */ -static void handle_control_message(VirtIOSerial *vser, void *buf, size_t len) -{ - struct VirtIOSerialPort *port; - VirtIOSerialPortClass *vsc; - struct virtio_console_control cpkt, *gcpkt; - uint8_t *buffer; - size_t buffer_len; - - gcpkt = buf; - - if (len < sizeof(cpkt)) { - /* The guest sent an invalid control packet */ - return; - } - - cpkt.event = lduw_p(&gcpkt->event); - cpkt.value = lduw_p(&gcpkt->value); - - trace_virtio_serial_handle_control_message(cpkt.event, cpkt.value); - - if (cpkt.event == VIRTIO_CONSOLE_DEVICE_READY) { - if (!cpkt.value) { - error_report("virtio-serial-bus: Guest failure in adding device %s", - vser->bus.qbus.name); - return; - } - /* - * The device is up, we can now tell the device about all the - * ports we have here. - */ - QTAILQ_FOREACH(port, &vser->ports, next) { - send_control_event(port, VIRTIO_CONSOLE_PORT_ADD, 1); - } - return; - } - - port = find_port_by_id(vser, ldl_p(&gcpkt->id)); - if (!port) { - error_report("virtio-serial-bus: Unexpected port id %u for device %s", - ldl_p(&gcpkt->id), vser->bus.qbus.name); - return; - } - - trace_virtio_serial_handle_control_message_port(port->id); - - vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - - switch(cpkt.event) { - case VIRTIO_CONSOLE_PORT_READY: - if (!cpkt.value) { - error_report("virtio-serial-bus: Guest failure in adding port %u for device %s", - port->id, vser->bus.qbus.name); - break; - } - /* - * Now that we know the guest asked for the port name, we're - * sure the guest has initialised whatever state is necessary - * for this port. Now's a good time to let the guest know if - * this port is a console port so that the guest can hook it - * up to hvc. - */ - if (vsc->is_console) { - send_control_event(port, VIRTIO_CONSOLE_CONSOLE_PORT, 1); - } - - if (port->name) { - stw_p(&cpkt.event, VIRTIO_CONSOLE_PORT_NAME); - stw_p(&cpkt.value, 1); - - buffer_len = sizeof(cpkt) + strlen(port->name) + 1; - buffer = g_malloc(buffer_len); - - memcpy(buffer, &cpkt, sizeof(cpkt)); - memcpy(buffer + sizeof(cpkt), port->name, strlen(port->name)); - buffer[buffer_len - 1] = 0; - - send_control_msg(port, buffer, buffer_len); - g_free(buffer); - } - - if (port->host_connected) { - send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1); - } - - /* - * When the guest has asked us for this information it means - * the guest is all setup and has its virtqueues - * initialised. If some app is interested in knowing about - * this event, let it know. - */ - if (vsc->guest_ready) { - vsc->guest_ready(port); - } - break; - - case VIRTIO_CONSOLE_PORT_OPEN: - port->guest_connected = cpkt.value; - if (cpkt.value && vsc->guest_open) { - /* Send the guest opened notification if an app is interested */ - vsc->guest_open(port); - } - - if (!cpkt.value && vsc->guest_close) { - /* Send the guest closed notification if an app is interested */ - vsc->guest_close(port); - } - break; - } -} - -static void control_in(VirtIODevice *vdev, VirtQueue *vq) -{ -} - -static void control_out(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtQueueElement elem; - VirtIOSerial *vser; - uint8_t *buf; - size_t len; - - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - - len = 0; - buf = NULL; - while (virtqueue_pop(vq, &elem)) { - size_t cur_len; - - cur_len = iov_size(elem.out_sg, elem.out_num); - /* - * Allocate a new buf only if we didn't have one previously or - * if the size of the buf differs - */ - if (cur_len > len) { - g_free(buf); - - buf = g_malloc(cur_len); - len = cur_len; - } - iov_to_buf(elem.out_sg, elem.out_num, 0, buf, cur_len); - - handle_control_message(vser, buf, cur_len); - virtqueue_push(vq, &elem, 0); - } - g_free(buf); - virtio_notify(vdev, vq); -} - -/* Guest wrote something to some port. */ -static void handle_output(VirtIODevice *vdev, VirtQueue *vq) -{ - VirtIOSerial *vser; - VirtIOSerialPort *port; - - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - port = find_port_by_vq(vser, vq); - - if (!port || !port->host_connected) { - discard_vq_data(vq, vdev); - return; - } - - if (!port->throttled) { - do_flush_queued_data(port, vq, vdev); - return; - } -} - -static void handle_input(VirtIODevice *vdev, VirtQueue *vq) -{ -} - -static uint32_t get_features(VirtIODevice *vdev, uint32_t features) -{ - VirtIOSerial *vser; - - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - - if (vser->bus.max_nr_ports > 1) { - features |= (1 << VIRTIO_CONSOLE_F_MULTIPORT); - } - return features; -} - -/* Guest requested config info */ -static void get_config(VirtIODevice *vdev, uint8_t *config_data) -{ - VirtIOSerial *vser; - - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - memcpy(config_data, &vser->config, sizeof(struct virtio_console_config)); -} - -static void set_config(VirtIODevice *vdev, const uint8_t *config_data) -{ - struct virtio_console_config config; - - memcpy(&config, config_data, sizeof(config)); -} - -static void guest_reset(VirtIOSerial *vser) -{ - VirtIOSerialPort *port; - VirtIOSerialPortClass *vsc; - - QTAILQ_FOREACH(port, &vser->ports, next) { - vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - if (port->guest_connected) { - port->guest_connected = false; - - if (vsc->guest_close) - vsc->guest_close(port); - } - } -} - -static void set_status(VirtIODevice *vdev, uint8_t status) -{ - VirtIOSerial *vser; - VirtIOSerialPort *port; - - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - port = find_port_by_id(vser, 0); - - if (port && !use_multiport(port->vser) - && (status & VIRTIO_CONFIG_S_DRIVER_OK)) { - /* - * Non-multiport guests won't be able to tell us guest - * open/close status. Such guests can only have a port at id - * 0, so set guest_connected for such ports as soon as guest - * is up. - */ - port->guest_connected = true; - } - if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { - guest_reset(vser); - } -} - -static void vser_reset(VirtIODevice *vdev) -{ - VirtIOSerial *vser; - - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - guest_reset(vser); -} - -static void virtio_serial_save(QEMUFile *f, void *opaque) -{ - VirtIOSerial *s = opaque; - VirtIOSerialPort *port; - uint32_t nr_active_ports; - unsigned int i, max_nr_ports; - - /* The virtio device */ - virtio_save(&s->vdev, f); - - /* The config space */ - qemu_put_be16s(f, &s->config.cols); - qemu_put_be16s(f, &s->config.rows); - - qemu_put_be32s(f, &s->config.max_nr_ports); - - /* The ports map */ - max_nr_ports = tswap32(s->config.max_nr_ports); - for (i = 0; i < (max_nr_ports + 31) / 32; i++) { - qemu_put_be32s(f, &s->ports_map[i]); - } - - /* Ports */ - - nr_active_ports = 0; - QTAILQ_FOREACH(port, &s->ports, next) { - nr_active_ports++; - } - - qemu_put_be32s(f, &nr_active_ports); - - /* - * Items in struct VirtIOSerialPort. - */ - QTAILQ_FOREACH(port, &s->ports, next) { - uint32_t elem_popped; - - qemu_put_be32s(f, &port->id); - qemu_put_byte(f, port->guest_connected); - qemu_put_byte(f, port->host_connected); - - elem_popped = 0; - if (port->elem.out_num) { - elem_popped = 1; - } - qemu_put_be32s(f, &elem_popped); - if (elem_popped) { - qemu_put_be32s(f, &port->iov_idx); - qemu_put_be64s(f, &port->iov_offset); - - qemu_put_buffer(f, (unsigned char *)&port->elem, - sizeof(port->elem)); - } - } -} - -static int virtio_serial_load(QEMUFile *f, void *opaque, int version_id) -{ - VirtIOSerial *s = opaque; - VirtIOSerialPort *port; - 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(&s->vdev, f); - if (ret) { - return ret; - } - - 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; - } - - for (i = 0; i < (max_nr_ports + 31) / 32; i++) { - qemu_get_be32s(f, &ports_map); - - if (ports_map != s->ports_map[i]) { - /* - * Ports active on source and destination don't - * match. Fail migration. - */ - return -EINVAL; - } - } - - qemu_get_be32s(f, &nr_active_ports); - - /* Items in struct VirtIOSerialPort */ - for (i = 0; i < nr_active_ports; i++) { - uint32_t id; - bool host_connected; - - id = qemu_get_be32(f); - port = find_port_by_id(s, id); - if (!port) { - return -EINVAL; - } - - port->guest_connected = qemu_get_byte(f); - host_connected = qemu_get_byte(f); - if (host_connected != port->host_connected) { - /* - * We have to let the guest know of the host connection - * status change - */ - send_control_event(port, VIRTIO_CONSOLE_PORT_OPEN, - port->host_connected); - } - - if (version_id > 2) { - uint32_t elem_popped; - - qemu_get_be32s(f, &elem_popped); - if (elem_popped) { - qemu_get_be32s(f, &port->iov_idx); - qemu_get_be64s(f, &port->iov_offset); - - qemu_get_buffer(f, (unsigned char *)&port->elem, - sizeof(port->elem)); - virtqueue_map_sg(port->elem.in_sg, port->elem.in_addr, - port->elem.in_num, 1); - virtqueue_map_sg(port->elem.out_sg, port->elem.out_addr, - port->elem.out_num, 1); - - /* - * Port was throttled on source machine. Let's - * unthrottle it here so data starts flowing again. - */ - virtio_serial_throttle_port(port, false); - } - } - } - return 0; -} - -static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent); - -static Property virtser_props[] = { - DEFINE_PROP_UINT32("nr", VirtIOSerialPort, id, VIRTIO_CONSOLE_BAD_ID), - DEFINE_PROP_STRING("name", VirtIOSerialPort, name), - DEFINE_PROP_END_OF_LIST() -}; - -#define TYPE_VIRTIO_SERIAL_BUS "virtio-serial-bus" -#define VIRTIO_SERIAL_BUS(obj) \ - OBJECT_CHECK(VirtIOSerialBus, (obj), TYPE_VIRTIO_SERIAL_BUS) - -static void virtser_bus_class_init(ObjectClass *klass, void *data) -{ - BusClass *k = BUS_CLASS(klass); - k->print_dev = virtser_bus_dev_print; -} - -static const TypeInfo virtser_bus_info = { - .name = TYPE_VIRTIO_SERIAL_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(VirtIOSerialBus), - .class_init = virtser_bus_class_init, -}; - -static void virtser_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent) -{ - VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); - - monitor_printf(mon, "%*sport %d, guest %s, host %s, throttle %s\n", - indent, "", port->id, - port->guest_connected ? "on" : "off", - port->host_connected ? "on" : "off", - port->throttled ? "on" : "off"); -} - -/* This function is only used if a port id is not provided by the user */ -static uint32_t find_free_port_id(VirtIOSerial *vser) -{ - unsigned int i, max_nr_ports; - - max_nr_ports = tswap32(vser->config.max_nr_ports); - for (i = 0; i < (max_nr_ports + 31) / 32; i++) { - uint32_t map, bit; - - map = vser->ports_map[i]; - bit = ffs(~map); - if (bit) { - return (bit - 1) + i * 32; - } - } - return VIRTIO_CONSOLE_BAD_ID; -} - -static void mark_port_added(VirtIOSerial *vser, uint32_t port_id) -{ - unsigned int i; - - i = port_id / 32; - vser->ports_map[i] |= 1U << (port_id % 32); -} - -static void add_port(VirtIOSerial *vser, uint32_t port_id) -{ - mark_port_added(vser, port_id); - - send_control_event(find_port_by_id(vser, port_id), - VIRTIO_CONSOLE_PORT_ADD, 1); -} - -static void remove_port(VirtIOSerial *vser, uint32_t port_id) -{ - VirtIOSerialPort *port; - unsigned int i; - - i = port_id / 32; - vser->ports_map[i] &= ~(1U << (port_id % 32)); - - port = find_port_by_id(vser, port_id); - /* Flush out any unconsumed buffers first */ - discard_vq_data(port->ovq, &port->vser->vdev); - - send_control_event(port, VIRTIO_CONSOLE_PORT_REMOVE, 1); -} - -static int virtser_port_qdev_init(DeviceState *qdev) -{ - VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); - VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - VirtIOSerialBus *bus = DO_UPCAST(VirtIOSerialBus, qbus, qdev->parent_bus); - int ret, max_nr_ports; - bool plugging_port0; - - port->vser = bus->vser; - port->bh = qemu_bh_new(flush_queued_data_bh, port); - - assert(vsc->have_data); - - /* - * Is the first console port we're seeing? If so, put it up at - * location 0. This is done for backward compatibility (old - * kernel, new qemu). - */ - plugging_port0 = vsc->is_console && !find_port_by_id(port->vser, 0); - - if (find_port_by_id(port->vser, port->id)) { - error_report("virtio-serial-bus: A port already exists at id %u", - port->id); - return -1; - } - - if (port->id == VIRTIO_CONSOLE_BAD_ID) { - if (plugging_port0) { - port->id = 0; - } else { - port->id = find_free_port_id(port->vser); - if (port->id == VIRTIO_CONSOLE_BAD_ID) { - error_report("virtio-serial-bus: Maximum port limit for this device reached"); - return -1; - } - } - } - - max_nr_ports = tswap32(port->vser->config.max_nr_ports); - if (port->id >= max_nr_ports) { - error_report("virtio-serial-bus: Out-of-range port id specified, max. allowed: %u", - max_nr_ports - 1); - return -1; - } - - ret = vsc->init(port); - if (ret) { - return ret; - } - - port->elem.out_num = 0; - - QTAILQ_INSERT_TAIL(&port->vser->ports, port, next); - port->ivq = port->vser->ivqs[port->id]; - port->ovq = port->vser->ovqs[port->id]; - - add_port(port->vser, port->id); - - /* Send an update to the guest about this new port added */ - virtio_notify_config(&port->vser->vdev); - - return ret; -} - -static int virtser_port_qdev_exit(DeviceState *qdev) -{ - VirtIOSerialPort *port = DO_UPCAST(VirtIOSerialPort, dev, qdev); - VirtIOSerialPortClass *vsc = VIRTIO_SERIAL_PORT_GET_CLASS(port); - VirtIOSerial *vser = port->vser; - - qemu_bh_delete(port->bh); - remove_port(port->vser, port->id); - - QTAILQ_REMOVE(&vser->ports, port, next); - - if (vsc->exit) { - vsc->exit(port); - } - return 0; -} - -VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *conf) -{ - VirtIOSerial *vser; - VirtIODevice *vdev; - uint32_t i, max_supported_ports; - - if (!conf->max_virtserial_ports) - return NULL; - - /* Each port takes 2 queues, and one pair is for the control queue */ - max_supported_ports = VIRTIO_PCI_QUEUE_MAX / 2 - 1; - - if (conf->max_virtserial_ports > max_supported_ports) { - error_report("maximum ports supported: %u", max_supported_ports); - return NULL; - } - - vdev = virtio_common_init("virtio-serial", VIRTIO_ID_CONSOLE, - sizeof(struct virtio_console_config), - sizeof(VirtIOSerial)); - - vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - - /* Spawn a new virtio-serial bus on which the ports will ride as devices */ - qbus_create_inplace(&vser->bus.qbus, TYPE_VIRTIO_SERIAL_BUS, dev, NULL); - vser->bus.qbus.allow_hotplug = 1; - vser->bus.vser = vser; - QTAILQ_INIT(&vser->ports); - - vser->bus.max_nr_ports = conf->max_virtserial_ports; - vser->ivqs = g_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *)); - vser->ovqs = g_malloc(conf->max_virtserial_ports * sizeof(VirtQueue *)); - - /* Add a queue for host to guest transfers for port 0 (backward compat) */ - vser->ivqs[0] = virtio_add_queue(vdev, 128, handle_input); - /* Add a queue for guest to host transfers for port 0 (backward compat) */ - vser->ovqs[0] = virtio_add_queue(vdev, 128, handle_output); - - /* TODO: host to guest notifications can get dropped - * if the queue fills up. Implement queueing in host, - * this might also make it possible to reduce the control - * queue size: as guest preposts buffers there, - * this will save 4Kbyte of guest memory per entry. */ - - /* control queue: host to guest */ - vser->c_ivq = virtio_add_queue(vdev, 32, control_in); - /* control queue: guest to host */ - vser->c_ovq = virtio_add_queue(vdev, 32, control_out); - - for (i = 1; i < vser->bus.max_nr_ports; i++) { - /* Add a per-port queue for host to guest transfers */ - vser->ivqs[i] = virtio_add_queue(vdev, 128, handle_input); - /* Add a per-per queue for guest to host transfers */ - vser->ovqs[i] = virtio_add_queue(vdev, 128, handle_output); - } - - vser->config.max_nr_ports = tswap32(conf->max_virtserial_ports); - vser->ports_map = g_malloc0(((conf->max_virtserial_ports + 31) / 32) - * sizeof(vser->ports_map[0])); - /* - * Reserve location 0 for a console port for backward compat - * (old kernel, new qemu) - */ - mark_port_added(vser, 0); - - vser->vdev.get_features = get_features; - vser->vdev.get_config = get_config; - vser->vdev.set_config = set_config; - vser->vdev.set_status = set_status; - vser->vdev.reset = vser_reset; - - vser->qdev = dev; - - /* - * Register for the savevm section with the virtio-console name - * to preserve backward compat - */ - register_savevm(dev, "virtio-console", -1, 3, virtio_serial_save, - virtio_serial_load, vser); - - return vdev; -} - -void virtio_serial_exit(VirtIODevice *vdev) -{ - VirtIOSerial *vser = DO_UPCAST(VirtIOSerial, vdev, vdev); - - unregister_savevm(vser->qdev, "virtio-console", vser); - - g_free(vser->ivqs); - g_free(vser->ovqs); - g_free(vser->ports_map); - - virtio_cleanup(vdev); -} - -static void virtio_serial_port_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *k = DEVICE_CLASS(klass); - k->init = virtser_port_qdev_init; - k->bus_type = TYPE_VIRTIO_SERIAL_BUS; - k->exit = virtser_port_qdev_exit; - k->unplug = qdev_simple_unplug_cb; - k->props = virtser_props; -} - -static TypeInfo virtio_serial_port_type_info = { - .name = TYPE_VIRTIO_SERIAL_PORT, - .parent = TYPE_DEVICE, - .instance_size = sizeof(VirtIOSerialPort), - .abstract = true, - .class_size = sizeof(VirtIOSerialPortClass), - .class_init = virtio_serial_port_class_init, -}; - -static void virtio_serial_register_types(void) -{ - type_register_static(&virtser_bus_info); - type_register_static(&virtio_serial_port_type_info); -} - -type_init(virtio_serial_register_types) - -/******************** VirtIOSer Device **********************/ - -static int virtio_serialdev_init(DeviceState *dev) -{ - VirtIODevice *vdev; - VirtIOSerState *s = VIRTIO_SERIAL_FROM_QDEV(dev); - vdev = virtio_serial_init(dev, &s->serial); - if (!vdev) { - return -1; - } - - assert(s->trl != NULL); - - return virtio_call_backend_init_cb(dev, s->trl, vdev); -} - -static Property virtio_serial_properties[] = { - DEFINE_PROP_UINT32("max_ports", VirtIOSerState, - serial.max_virtserial_ports, 31), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_serial_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - dc->init = virtio_serialdev_init; - dc->props = virtio_serial_properties; -} - -static TypeInfo virtio_serial_info = { - .name = "virtio-serial", - .parent = TYPE_DEVICE, - .instance_size = sizeof(VirtIOSerState), - .class_init = virtio_serial_class_init, -}; - -static void virtio_ser_register_types(void) -{ - type_register_static(&virtio_serial_info); -} - -type_init(virtio_ser_register_types) diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h deleted file mode 100644 index c6b916a1a7..0000000000 --- a/hw/virtio-serial.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Virtio Serial / Console Support - * - * Copyright IBM, Corp. 2008 - * Copyright Red Hat, Inc. 2009, 2010 - * - * Authors: - * Christian Ehrhardt - * Amit Shah - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ -#ifndef _QEMU_VIRTIO_SERIAL_H -#define _QEMU_VIRTIO_SERIAL_H - -#include "sysbus.h" -#include "qdev.h" -#include "virtio.h" -#include "virtio-transport.h" - -/* == Interface shared between the guest kernel and qemu == */ - -/* The Virtio ID for virtio console / serial ports */ -#define VIRTIO_ID_CONSOLE 3 - -/* Features supported */ -#define VIRTIO_CONSOLE_F_MULTIPORT 1 - -#define VIRTIO_CONSOLE_BAD_ID (~(uint32_t)0) - -struct virtio_console_config { - /* - * These two fields are used by VIRTIO_CONSOLE_F_SIZE which - * isn't implemented here yet - */ - uint16_t cols; - uint16_t rows; - - uint32_t max_nr_ports; -} QEMU_PACKED; - -struct virtio_console_control { - uint32_t id; /* Port number */ - uint16_t event; /* The kind of control event (see below) */ - uint16_t value; /* Extra information for the key */ -}; - -struct virtio_serial_conf { - /* Max. number of ports we can have for a virtio-serial device */ - uint32_t max_virtserial_ports; -}; - -/* Some events for the internal messages (control packets) */ -#define VIRTIO_CONSOLE_DEVICE_READY 0 -#define VIRTIO_CONSOLE_PORT_ADD 1 -#define VIRTIO_CONSOLE_PORT_REMOVE 2 -#define VIRTIO_CONSOLE_PORT_READY 3 -#define VIRTIO_CONSOLE_CONSOLE_PORT 4 -#define VIRTIO_CONSOLE_RESIZE 5 -#define VIRTIO_CONSOLE_PORT_OPEN 6 -#define VIRTIO_CONSOLE_PORT_NAME 7 - -/* == In-qemu interface == */ - -#define TYPE_VIRTIO_SERIAL_PORT "virtio-serial-port" -#define VIRTIO_SERIAL_PORT(obj) \ - OBJECT_CHECK(VirtIOSerialPort, (obj), TYPE_VIRTIO_SERIAL_PORT) -#define VIRTIO_SERIAL_PORT_CLASS(klass) \ - OBJECT_CLASS_CHECK(VirtIOSerialPortClass, (klass), TYPE_VIRTIO_SERIAL_PORT) -#define VIRTIO_SERIAL_PORT_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VirtIOSerialPortClass, (obj), TYPE_VIRTIO_SERIAL_PORT) - -typedef struct VirtIOSerial VirtIOSerial; -typedef struct VirtIOSerialBus VirtIOSerialBus; -typedef struct VirtIOSerialPort VirtIOSerialPort; - -typedef struct VirtIOSerialPortClass { - DeviceClass parent_class; - - /* Is this a device that binds with hvc in the guest? */ - bool is_console; - - /* - * The per-port (or per-app) init function that's called when a - * new device is found on the bus. - */ - int (*init)(VirtIOSerialPort *port); - /* - * Per-port exit function that's called when a port gets - * hot-unplugged or removed. - */ - int (*exit)(VirtIOSerialPort *port); - - /* Callbacks for guest events */ - /* Guest opened device. */ - void (*guest_open)(VirtIOSerialPort *port); - /* Guest closed device. */ - void (*guest_close)(VirtIOSerialPort *port); - - /* Guest is now ready to accept data (virtqueues set up). */ - void (*guest_ready)(VirtIOSerialPort *port); - - /* - * Guest wrote some data to the port. This data is handed over to - * the app via this callback. The app can return a size less than - * 'len'. In this case, throttling will be enabled for this port. - */ - ssize_t (*have_data)(VirtIOSerialPort *port, const uint8_t *buf, - size_t len); -} VirtIOSerialPortClass; - -/* - * This is the state that's shared between all the ports. Some of the - * state is configurable via command-line options. Some of it can be - * set by individual devices in their initfn routines. Some of the - * state is set by the generic qdev device init routine. - */ -struct VirtIOSerialPort { - DeviceState dev; - - QTAILQ_ENTRY(VirtIOSerialPort) next; - - /* - * This field gives us the virtio device as well as the qdev bus - * that we are associated with - */ - VirtIOSerial *vser; - - VirtQueue *ivq, *ovq; - - /* - * This name is sent to the guest and exported via sysfs. - * The guest could create symlinks based on this information. - * The name is in the reverse fqdn format, like org.qemu.console.0 - */ - char *name; - - /* - * This id helps identify ports between the guest and the host. - * The guest sends a "header" with this id with each data packet - * that it sends and the host can then find out which associated - * device to send out this data to - */ - uint32_t id; - - /* - * This is the elem that we pop from the virtqueue. A slow - * backend that consumes guest data (e.g. the file backend for - * qemu chardevs) can cause the guest to block till all the output - * is flushed. This isn't desired, so we keep a note of the last - * element popped and continue consuming it once the backend - * becomes writable again. - */ - VirtQueueElement elem; - - /* - * The index and the offset into the iov buffer that was popped in - * elem above. - */ - uint32_t iov_idx; - uint64_t iov_offset; - - /* - * When unthrottling we use a bottom-half to call flush_queued_data. - */ - QEMUBH *bh; - - /* Is the corresponding guest device open? */ - bool guest_connected; - /* Is this device open for IO on the host? */ - bool host_connected; - /* Do apps not want to receive data? */ - bool throttled; -}; - -typedef struct { - DeviceState qdev; - /* virtio-serial */ - virtio_serial_conf serial; - VirtIOTransportLink *trl; -} VirtIOSerState; - -#define VIRTIO_SERIAL_FROM_QDEV(dev) DO_UPCAST(VirtIOSerState, qdev, dev) - -/* Interface to the virtio-serial bus */ - -/* - * Open a connection to the port - * Returns 0 on success (always). - */ -int virtio_serial_open(VirtIOSerialPort *port); - -/* - * Close the connection to the port - * Returns 0 on success (always). - */ -int virtio_serial_close(VirtIOSerialPort *port); - -/* - * Send data to Guest - */ -ssize_t virtio_serial_write(VirtIOSerialPort *port, const uint8_t *buf, - size_t size); - -/* - * Query whether a guest is ready to receive data. - */ -size_t virtio_serial_guest_ready(VirtIOSerialPort *port); - -/* - * Flow control: Ports can signal to the virtio-serial core to stop - * sending data or re-start sending data, depending on the 'throttle' - * value here. - */ -void virtio_serial_throttle_port(VirtIOSerialPort *port, bool throttle); - -#endif diff --git a/hw/virtio-transport.c b/hw/virtio-transport.c deleted file mode 100644 index 76360ba9fb..0000000000 --- a/hw/virtio-transport.c +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Virtio transport bindings - * - * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. - * - * Author: - * Evgeny Voevodin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#include "virtio-transport.h" - -#define VIRTIO_TRANSPORT_BUS "virtio-transport" - -static QTAILQ_HEAD(, VirtIOTransportLink) transport_links = - QTAILQ_HEAD_INITIALIZER(transport_links); - -/* - * Find transport device by its ID. - */ -VirtIOTransportLink* virtio_find_transport(const char *name) -{ - VirtIOTransportLink *trl; - - assert(name != NULL); - - QTAILQ_FOREACH(trl, &transport_links, sibling) { - if (trl->tr->id != NULL) { - if (!strcmp(name, trl->tr->id)) { - return trl; - } - } - } - - return NULL; -} - -/* - * Count transport devices by ID. - */ -uint32_t virtio_count_transports(const char *name) -{ - VirtIOTransportLink *trl; - uint32_t i = 0; - - QTAILQ_FOREACH(trl, &transport_links, sibling) { - if (name == NULL) { - i++; - continue; - } - - if (trl->tr->id != NULL) { - if (!strncmp(name, trl->tr->id,strlen(name))) { - i++; - } - } - } - return i; -} - -/* - * Initialize new transport device - */ -char* virtio_init_transport(DeviceState *dev, VirtIOTransportLink **trl, - const char* name, virtio_backend_init_cb cb) -{ - VirtIOTransportLink *link = g_malloc0(sizeof(VirtIOTransportLink)); - char *buf; - size_t len; - uint32_t i; - - assert(dev != NULL); - assert(name != NULL); - assert(trl != NULL); - - i = virtio_count_transports(name); - len = strlen(name) + 16; - buf = g_malloc(len); - snprintf(buf, len, "%s.%d", name, i); - qbus_create(TYPE_VIRTIO_BUS, dev, buf); - - /* Add new transport */ - QTAILQ_INSERT_TAIL(&transport_links, link, sibling); - link->tr = dev; - link->cb = cb; - // TODO: Add a link property - *trl = link; - return buf; -} - -/* - * Unplug back-end from system bus and plug it into transport bus. - */ -void virtio_plug_into_transport(DeviceState *dev, VirtIOTransportLink *trl) -{ - BusChild *kid; - - /* Unplug back-end from system bus */ - QTAILQ_FOREACH(kid, &qdev_get_parent_bus(dev)->children, sibling) { - if (kid->child == dev) { - QTAILQ_REMOVE(&qdev_get_parent_bus(dev)->children, kid, sibling); - break; - } - } - - /* Plug back-end into transport's bus */ - qdev_set_parent_bus(dev, QLIST_FIRST(&trl->tr->child_bus)); - -} - -/* - * Execute call-back on back-end initialization. - * Performs initialization of MMIO or PCI transport. - */ -int virtio_call_backend_init_cb(DeviceState *dev, VirtIOTransportLink *trl, - VirtIODevice *vdev) -{ - if (trl->cb) { - return trl->cb(dev, vdev, trl); - } - - return 0; -} - -static const TypeInfo virtio_bus_info = { - .name = TYPE_VIRTIO_BUS, - .parent = TYPE_BUS, - .instance_size = sizeof(BusState), -}; - -static void virtio_register_types(void) -{ - type_register_static(&virtio_bus_info); -} - -type_init(virtio_register_types) diff --git a/hw/virtio-transport.h b/hw/virtio-transport.h deleted file mode 100644 index 43200dc6ba..0000000000 --- a/hw/virtio-transport.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Virtio transport header - * - * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. - * - * Author: - * Evgeny Voevodin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see . - */ - -#ifndef VIRTIO_TRANSPORT_H_ -#define VIRTIO_TRANSPORT_H_ - -#include "qdev.h" -#include "qemu-common.h" - -#define VIRTIO_MMIO "virtio-mmio" -#define VIRTIO_PCI "virtio-pci" - -#define TYPE_VIRTIO_BUS "virtio-bus" -#define VIRTIO_BUS(obj) OBJECT_CHECK(virtio_bus, (obj), TYPE_VIRTIO_BUS) - -struct VirtIOTransportLink; - -typedef int (*virtio_backend_init_cb)(DeviceState *dev, VirtIODevice *vdev, - struct VirtIOTransportLink *trl); - -typedef struct VirtIOTransportLink { - DeviceState *tr; - virtio_backend_init_cb cb; - uint32_t host_features; - QTAILQ_ENTRY(VirtIOTransportLink) sibling; -} VirtIOTransportLink; - -/* - * Find transport device by its ID. - */ -VirtIOTransportLink* virtio_find_transport(const char *name); - -/* - * Count transport devices by ID. - */ -uint32_t virtio_count_transports(const char *name); - -/* - * Initialize new transport device - */ -char* virtio_init_transport(DeviceState *dev, VirtIOTransportLink **trl, - const char* name, virtio_backend_init_cb cb); - -/* - * Unplug back-end from system bus and plug it into transport bus. - */ -void virtio_plug_into_transport(DeviceState *dev, VirtIOTransportLink *trl); - -/* - * Execute call-back on back-end initialization. - * Performs initialization of MMIO or PCI transport. - */ -int virtio_call_backend_init_cb(DeviceState *dev, VirtIOTransportLink *trl, - VirtIODevice *vdev); - -#endif /* VIRTIO_TRANSPORT_H_ */ diff --git a/hw/virtio.c b/hw/virtio.c deleted file mode 100644 index 4f47d4e602..0000000000 --- a/hw/virtio.c +++ /dev/null @@ -1,1059 +0,0 @@ -/* - * Virtio Support - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include - -#include "trace.h" -#include "qemu-error.h" -#include "virtio.h" -#include "qemu-barrier.h" - -/* The alignment to use between consumer and producer parts of vring. - * x86 pagesize again. This is the default, used by transports like PCI - * which don't provide a means for the guest to tell the host the alignment. - */ -#define VIRTIO_PCI_VRING_ALIGN 4096 - -typedef struct VRingDesc -{ - uint64_t addr; - uint32_t len; - uint16_t flags; - uint16_t next; -} VRingDesc; - -typedef struct VRingAvail -{ - uint16_t flags; - uint16_t idx; - uint16_t ring[0]; -} VRingAvail; - -typedef struct VRingUsedElem -{ - uint32_t id; - uint32_t len; -} VRingUsedElem; - -typedef struct VRingUsed -{ - uint16_t flags; - uint16_t idx; - VRingUsedElem ring[0]; -} VRingUsed; - -typedef struct VRing -{ - unsigned int num; - unsigned int align; - target_phys_addr_t desc; - target_phys_addr_t avail; - target_phys_addr_t used; -} VRing; - -struct VirtQueue -{ - VRing vring; - target_phys_addr_t pa; - uint16_t last_avail_idx; - /* Last used index value we have signalled on */ - uint16_t signalled_used; - - /* Last used index value we have signalled on */ - bool signalled_used_valid; - - /* Notification enabled? */ - bool notification; - - int inuse; - - uint16_t vector; - void (*handle_output)(VirtIODevice *vdev, VirtQueue *vq); - VirtIODevice *vdev; - EventNotifier guest_notifier; - EventNotifier host_notifier; -}; - -/* virt queue functions */ -static void virtqueue_init(VirtQueue *vq) -{ - target_phys_addr_t pa = vq->pa; - - vq->vring.desc = pa; - vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc); - vq->vring.used = vring_align(vq->vring.avail + - offsetof(VRingAvail, ring[vq->vring.num]), - vq->vring.align); -} - -static inline uint64_t vring_desc_addr(target_phys_addr_t desc_pa, int i) -{ - target_phys_addr_t pa; - pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, addr); - return ldq_phys(pa); -} - -static inline uint32_t vring_desc_len(target_phys_addr_t desc_pa, int i) -{ - target_phys_addr_t pa; - pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, len); - return ldl_phys(pa); -} - -static inline uint16_t vring_desc_flags(target_phys_addr_t desc_pa, int i) -{ - target_phys_addr_t pa; - pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, flags); - return lduw_phys(pa); -} - -static inline uint16_t vring_desc_next(target_phys_addr_t desc_pa, int i) -{ - target_phys_addr_t pa; - pa = desc_pa + sizeof(VRingDesc) * i + offsetof(VRingDesc, next); - return lduw_phys(pa); -} - -static inline uint16_t vring_avail_flags(VirtQueue *vq) -{ - target_phys_addr_t pa; - pa = vq->vring.avail + offsetof(VRingAvail, flags); - return lduw_phys(pa); -} - -static inline uint16_t vring_avail_idx(VirtQueue *vq) -{ - target_phys_addr_t pa; - pa = vq->vring.avail + offsetof(VRingAvail, idx); - return lduw_phys(pa); -} - -static inline uint16_t vring_avail_ring(VirtQueue *vq, int i) -{ - target_phys_addr_t pa; - pa = vq->vring.avail + offsetof(VRingAvail, ring[i]); - return lduw_phys(pa); -} - -static inline uint16_t vring_used_event(VirtQueue *vq) -{ - return vring_avail_ring(vq, vq->vring.num); -} - -static inline void vring_used_ring_id(VirtQueue *vq, int i, uint32_t val) -{ - target_phys_addr_t pa; - pa = vq->vring.used + offsetof(VRingUsed, ring[i].id); - stl_phys(pa, val); -} - -static inline void vring_used_ring_len(VirtQueue *vq, int i, uint32_t val) -{ - target_phys_addr_t pa; - pa = vq->vring.used + offsetof(VRingUsed, ring[i].len); - stl_phys(pa, val); -} - -static uint16_t vring_used_idx(VirtQueue *vq) -{ - target_phys_addr_t pa; - pa = vq->vring.used + offsetof(VRingUsed, idx); - return lduw_phys(pa); -} - -static inline void vring_used_idx_set(VirtQueue *vq, uint16_t val) -{ - target_phys_addr_t pa; - pa = vq->vring.used + offsetof(VRingUsed, idx); - stw_phys(pa, val); -} - -static inline void vring_used_flags_set_bit(VirtQueue *vq, int mask) -{ - target_phys_addr_t pa; - pa = vq->vring.used + offsetof(VRingUsed, flags); - stw_phys(pa, lduw_phys(pa) | mask); -} - -static inline void vring_used_flags_unset_bit(VirtQueue *vq, int mask) -{ - target_phys_addr_t pa; - pa = vq->vring.used + offsetof(VRingUsed, flags); - stw_phys(pa, lduw_phys(pa) & ~mask); -} - -static inline void vring_avail_event(VirtQueue *vq, uint16_t val) -{ - target_phys_addr_t pa; - if (!vq->notification) { - return; - } - pa = vq->vring.used + offsetof(VRingUsed, ring[vq->vring.num]); - stw_phys(pa, val); -} - -void virtio_queue_set_notification(VirtQueue *vq, int enable) -{ - vq->notification = enable; - if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { - vring_avail_event(vq, vring_avail_idx(vq)); - } else if (enable) { - vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY); - } else { - vring_used_flags_set_bit(vq, VRING_USED_F_NO_NOTIFY); - } - if (enable) { - /* Expose avail event/used flags before caller checks the avail idx. */ - smp_mb(); - } -} - -int virtio_queue_ready(VirtQueue *vq) -{ - return vq->vring.avail != 0; -} - -int virtio_queue_empty(VirtQueue *vq) -{ - return vring_avail_idx(vq) == vq->last_avail_idx; -} - -void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len, unsigned int idx) -{ - unsigned int offset; - int i; - - trace_virtqueue_fill(vq, elem, len, idx); - - offset = 0; - for (i = 0; i < elem->in_num; i++) { - size_t size = MIN(len - offset, elem->in_sg[i].iov_len); - - cpu_physical_memory_unmap(elem->in_sg[i].iov_base, - elem->in_sg[i].iov_len, - 1, size); - - offset += elem->in_sg[i].iov_len; - } - - for (i = 0; i < elem->out_num; i++) - cpu_physical_memory_unmap(elem->out_sg[i].iov_base, - elem->out_sg[i].iov_len, - 0, elem->out_sg[i].iov_len); - - idx = (idx + vring_used_idx(vq)) % vq->vring.num; - - /* Get a pointer to the next entry in the used ring. */ - vring_used_ring_id(vq, idx, elem->index); - vring_used_ring_len(vq, idx, len); -} - -void virtqueue_flush(VirtQueue *vq, unsigned int count) -{ - uint16_t old, new; - /* Make sure buffer is written before we update index. */ - smp_wmb(); - trace_virtqueue_flush(vq, count); - old = vring_used_idx(vq); - new = old + count; - vring_used_idx_set(vq, new); - vq->inuse -= count; - if (unlikely((int16_t)(new - vq->signalled_used) < (uint16_t)(new - old))) - vq->signalled_used_valid = false; -} - -void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len) -{ - virtqueue_fill(vq, elem, len, 0); - virtqueue_flush(vq, 1); -} - -static int virtqueue_num_heads(VirtQueue *vq, unsigned int idx) -{ - uint16_t num_heads = vring_avail_idx(vq) - idx; - - /* Check it isn't doing very strange things with descriptor numbers. */ - if (num_heads > vq->vring.num) { - error_report("Guest moved used index from %u to %u", - idx, vring_avail_idx(vq)); - exit(1); - } - /* On success, callers read a descriptor at vq->last_avail_idx. - * Make sure descriptor read does not bypass avail index read. */ - if (num_heads) { - smp_rmb(); - } - - return num_heads; -} - -static unsigned int virtqueue_get_head(VirtQueue *vq, unsigned int idx) -{ - unsigned int head; - - /* Grab the next descriptor number they're advertising, and increment - * the index we've seen. */ - head = vring_avail_ring(vq, idx % vq->vring.num); - - /* If their number is silly, that's a fatal mistake. */ - if (head >= vq->vring.num) { - error_report("Guest says index %u is available", head); - exit(1); - } - - return head; -} - -static unsigned virtqueue_next_desc(target_phys_addr_t 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)) - return max; - - /* Check they're not leading us off end of descriptors. */ - next = vring_desc_next(desc_pa, i); - /* Make sure compiler knows to grab that: we don't want it changing! */ - smp_wmb(); - - if (next >= max) { - error_report("Desc next is %u", next); - exit(1); - } - - return next; -} - -int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes) -{ - unsigned int idx; - int total_bufs, in_total, out_total; - - idx = vq->last_avail_idx; - - total_bufs = in_total = out_total = 0; - while (virtqueue_num_heads(vq, idx)) { - unsigned int max, num_bufs, indirect = 0; - target_phys_addr_t desc_pa; - int i; - - max = vq->vring.num; - num_bufs = total_bufs; - 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)) { - error_report("Invalid size for indirect buffer table"); - exit(1); - } - - /* If we've got too many, that implies a descriptor loop. */ - if (num_bufs >= max) { - error_report("Looped descriptor"); - exit(1); - } - - /* loop over the indirect descriptor table */ - indirect = 1; - max = vring_desc_len(desc_pa, i) / sizeof(VRingDesc); - num_bufs = i = 0; - desc_pa = vring_desc_addr(desc_pa, i); - } - - do { - /* If we've got too many, that implies a descriptor loop. */ - if (++num_bufs > max) { - error_report("Looped descriptor"); - exit(1); - } - - if (vring_desc_flags(desc_pa, i) & VRING_DESC_F_WRITE) { - if (in_bytes > 0 && - (in_total += vring_desc_len(desc_pa, i)) >= in_bytes) - return 1; - } else { - if (out_bytes > 0 && - (out_total += vring_desc_len(desc_pa, i)) >= out_bytes) - return 1; - } - } while ((i = virtqueue_next_desc(desc_pa, i, max)) != max); - - if (!indirect) - total_bufs = num_bufs; - else - total_bufs++; - } - - return 0; -} - -void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr, - size_t num_sg, int is_write) -{ - unsigned int i; - target_phys_addr_t len; - - for (i = 0; i < num_sg; i++) { - len = sg[i].iov_len; - sg[i].iov_base = cpu_physical_memory_map(addr[i], &len, is_write); - if (sg[i].iov_base == NULL || len != sg[i].iov_len) { - error_report("virtio: trying to map MMIO memory"); - exit(1); - } - } -} - -int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) -{ - unsigned int i, head, max; - target_phys_addr_t desc_pa = vq->vring.desc; - - if (!virtqueue_num_heads(vq, vq->last_avail_idx)) - return 0; - - /* When we start there are none of either input nor output. */ - elem->out_num = elem->in_num = 0; - - 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)) { - 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)) { - 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); - i = 0; - } - - /* Collect all the descriptors */ - do { - struct iovec *sg; - - if (vring_desc_flags(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); - 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); - sg = &elem->out_sg[elem->out_num++]; - } - - sg->iov_len = vring_desc_len(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); - - /* Now map what we have collected */ - virtqueue_map_sg(elem->in_sg, elem->in_addr, elem->in_num, 1); - virtqueue_map_sg(elem->out_sg, elem->out_addr, elem->out_num, 0); - - elem->index = head; - - vq->inuse++; - - trace_virtqueue_pop(vq, elem, elem->in_num, elem->out_num); - return elem->in_num + elem->out_num; -} - -/* virtio device */ -static void virtio_notify_vector(VirtIODevice *vdev, uint16_t vector) -{ - if (vdev->binding->notify) { - vdev->binding->notify(vdev->binding_opaque, vector); - } -} - -void virtio_update_irq(VirtIODevice *vdev) -{ - virtio_notify_vector(vdev, VIRTIO_NO_VECTOR); -} - -void virtio_set_status(VirtIODevice *vdev, uint8_t val) -{ - trace_virtio_set_status(vdev, val); - - if (vdev->set_status) { - vdev->set_status(vdev, val); - } - vdev->status = val; -} - -void virtio_reset(void *opaque) -{ - VirtIODevice *vdev = opaque; - int i; - - virtio_set_status(vdev, 0); - - if (vdev->reset) - vdev->reset(vdev); - - vdev->guest_features = 0; - vdev->queue_sel = 0; - vdev->status = 0; - vdev->isr = 0; - vdev->config_vector = VIRTIO_NO_VECTOR; - virtio_notify_vector(vdev, vdev->config_vector); - - for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { - vdev->vq[i].vring.desc = 0; - vdev->vq[i].vring.avail = 0; - vdev->vq[i].vring.used = 0; - vdev->vq[i].last_avail_idx = 0; - vdev->vq[i].pa = 0; - vdev->vq[i].vector = VIRTIO_NO_VECTOR; - vdev->vq[i].signalled_used = 0; - vdev->vq[i].signalled_used_valid = false; - vdev->vq[i].notification = true; - } -} - -uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr) -{ - uint8_t val; - - vdev->get_config(vdev, vdev->config); - - if (addr > (vdev->config_len - sizeof(val))) - return (uint32_t)-1; - - val = ldub_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr) -{ - uint16_t val; - - vdev->get_config(vdev, vdev->config); - - if (addr > (vdev->config_len - sizeof(val))) - return (uint32_t)-1; - - val = lduw_p(vdev->config + addr); - return val; -} - -uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr) -{ - uint32_t val; - - vdev->get_config(vdev, vdev->config); - - if (addr > (vdev->config_len - sizeof(val))) - return (uint32_t)-1; - - val = ldl_p(vdev->config + addr); - return val; -} - -void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data) -{ - uint8_t val = data; - - if (addr > (vdev->config_len - sizeof(val))) - return; - - stb_p(vdev->config + addr, val); - - if (vdev->set_config) - vdev->set_config(vdev, vdev->config); -} - -void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data) -{ - uint16_t val = data; - - if (addr > (vdev->config_len - sizeof(val))) - return; - - stw_p(vdev->config + addr, val); - - if (vdev->set_config) - vdev->set_config(vdev, vdev->config); -} - -void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data) -{ - uint32_t val = data; - - if (addr > (vdev->config_len - sizeof(val))) - return; - - stl_p(vdev->config + addr, val); - - if (vdev->set_config) - vdev->set_config(vdev, vdev->config); -} - -void virtio_queue_set_addr(VirtIODevice *vdev, int n, target_phys_addr_t addr) -{ - vdev->vq[n].pa = addr; - virtqueue_init(&vdev->vq[n]); -} - -target_phys_addr_t virtio_queue_get_addr(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].pa; -} - -void virtio_queue_set_num(VirtIODevice *vdev, int n, int num) -{ - vdev->vq[n].vring.num = num; - virtqueue_init(&vdev->vq[n]); -} - -int virtio_queue_get_num(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.num; -} - -int virtio_queue_get_id(VirtQueue *vq) -{ - VirtIODevice *vdev = vq->vdev; - assert(vq >= &vdev->vq[0] && vq < &vdev->vq[VIRTIO_PCI_QUEUE_MAX]); - return vq - &vdev->vq[0]; -} - -void virtio_queue_set_align(VirtIODevice *vdev, int n, int align) -{ - vdev->vq[n].vring.align = align; - virtqueue_init(&vdev->vq[n]); -} - -void virtio_queue_notify_vq(VirtQueue *vq) -{ - if (vq->vring.desc) { - VirtIODevice *vdev = vq->vdev; - trace_virtio_queue_notify(vdev, vq - vdev->vq, vq); - vq->handle_output(vdev, vq); - } -} - -void virtio_queue_notify(VirtIODevice *vdev, int n) -{ - virtio_queue_notify_vq(&vdev->vq[n]); -} - -uint16_t virtio_queue_vector(VirtIODevice *vdev, int n) -{ - return n < VIRTIO_PCI_QUEUE_MAX ? vdev->vq[n].vector : - VIRTIO_NO_VECTOR; -} - -void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector) -{ - if (n < VIRTIO_PCI_QUEUE_MAX) - vdev->vq[n].vector = vector; -} - -VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, - void (*handle_output)(VirtIODevice *, VirtQueue *)) -{ - int i; - - for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num == 0) - break; - } - - if (i == VIRTIO_PCI_QUEUE_MAX || queue_size > VIRTQUEUE_MAX_SIZE) - abort(); - - vdev->vq[i].vring.num = queue_size; - vdev->vq[i].vring.align = VIRTIO_PCI_VRING_ALIGN; - vdev->vq[i].handle_output = handle_output; - - return &vdev->vq[i]; -} - -void virtio_irq(VirtQueue *vq) -{ - trace_virtio_irq(vq); - vq->vdev->isr |= 0x01; - virtio_notify_vector(vq->vdev, vq->vector); -} - -/* Assuming a given event_idx value from the other size, if - * we have just incremented index from old to new_idx, - * should we trigger an event? */ -static inline int vring_need_event(uint16_t event, uint16_t new, uint16_t old) -{ - /* Note: Xen has similar logic for notification hold-off - * in include/xen/interface/io/ring.h with req_event and req_prod - * corresponding to event_idx + 1 and new respectively. - * Note also that req_event and req_prod in Xen start at 1, - * event indexes in virtio start at 0. */ - return (uint16_t)(new - event - 1) < (uint16_t)(new - old); -} - -static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq) -{ - uint16_t old, new; - bool v; - /* We need to expose used array entries before checking used event. */ - smp_mb(); - /* Always notify when queue is empty (when feature acknowledge) */ - if (((vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) && - !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx)) { - return true; - } - - if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { - return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT); - } - - v = vq->signalled_used_valid; - vq->signalled_used_valid = true; - old = vq->signalled_used; - new = vq->signalled_used = vring_used_idx(vq); - return !v || vring_need_event(vring_used_event(vq), new, old); -} - -void virtio_notify(VirtIODevice *vdev, VirtQueue *vq) -{ - if (!vring_notify(vdev, vq)) { - return; - } - - trace_virtio_notify(vdev, vq); - vdev->isr |= 0x01; - virtio_notify_vector(vdev, vq->vector); -} - -void virtio_notify_config(VirtIODevice *vdev) -{ - if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) - return; - - vdev->isr |= 0x03; - virtio_notify_vector(vdev, vdev->config_vector); -} - -void virtio_save(VirtIODevice *vdev, QEMUFile *f) -{ - int i; - - if (vdev->binding->save_config) - vdev->binding->save_config(vdev->binding_opaque, f); - - qemu_put_8s(f, &vdev->status); - qemu_put_8s(f, &vdev->isr); - qemu_put_be16s(f, &vdev->queue_sel); - qemu_put_be32s(f, &vdev->guest_features); - qemu_put_be32(f, vdev->config_len); - qemu_put_buffer(f, vdev->config, vdev->config_len); - - for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num == 0) - break; - } - - qemu_put_be32(f, i); - - for (i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { - if (vdev->vq[i].vring.num == 0) - break; - - qemu_put_be32(f, vdev->vq[i].vring.num); - qemu_put_be64(f, vdev->vq[i].pa); - qemu_put_be16s(f, &vdev->vq[i].last_avail_idx); - if (vdev->binding->save_queue) - vdev->binding->save_queue(vdev->binding_opaque, i, f); - } -} - -int virtio_set_features(VirtIODevice *vdev, uint32_t val) -{ - uint32_t supported_features = - vdev->binding->get_features(vdev->binding_opaque); - bool bad = (val & ~supported_features) != 0; - - val &= supported_features; - if (vdev->set_features) { - vdev->set_features(vdev, val); - } - vdev->guest_features = val; - return bad ? -1 : 0; -} - -int virtio_load(VirtIODevice *vdev, QEMUFile *f) -{ - int num, i, ret; - uint32_t features; - uint32_t supported_features; - - if (vdev->binding->load_config) { - ret = vdev->binding->load_config(vdev->binding_opaque, f); - if (ret) - return ret; - } - - qemu_get_8s(f, &vdev->status); - qemu_get_8s(f, &vdev->isr); - qemu_get_be16s(f, &vdev->queue_sel); - qemu_get_be32s(f, &features); - - if (virtio_set_features(vdev, features) < 0) { - supported_features = vdev->binding->get_features(vdev->binding_opaque); - error_report("Features 0x%x unsupported. Allowed features: 0x%x", - features, supported_features); - return -1; - } - vdev->config_len = qemu_get_be32(f); - qemu_get_buffer(f, vdev->config, vdev->config_len); - - num = qemu_get_be32(f); - - for (i = 0; i < num; i++) { - vdev->vq[i].vring.num = qemu_get_be32(f); - vdev->vq[i].pa = qemu_get_be64(f); - qemu_get_be16s(f, &vdev->vq[i].last_avail_idx); - vdev->vq[i].signalled_used_valid = false; - 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", - i, vdev->vq[i].last_avail_idx); - return -1; - } - if (vdev->binding->load_queue) { - ret = vdev->binding->load_queue(vdev->binding_opaque, i, f); - if (ret) - return ret; - } - } - - virtio_notify_vector(vdev, VIRTIO_NO_VECTOR); - return 0; -} - -void virtio_cleanup(VirtIODevice *vdev) -{ - qemu_del_vm_change_state_handler(vdev->vmstate); - g_free(vdev->config); - g_free(vdev->vq); - g_free(vdev); -} - -static void virtio_vmstate_change(void *opaque, int running, RunState state) -{ - VirtIODevice *vdev = opaque; - bool backend_run = running && (vdev->status & VIRTIO_CONFIG_S_DRIVER_OK); - vdev->vm_running = running; - - if (backend_run) { - virtio_set_status(vdev, vdev->status); - } - - if (vdev->binding->vmstate_change) { - vdev->binding->vmstate_change(vdev->binding_opaque, backend_run); - } - - if (!backend_run) { - virtio_set_status(vdev, vdev->status); - } -} - -VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, - size_t config_size, size_t struct_size) -{ - VirtIODevice *vdev; - int i; - - vdev = g_malloc0(struct_size); - - vdev->device_id = device_id; - vdev->status = 0; - vdev->isr = 0; - vdev->queue_sel = 0; - vdev->config_vector = VIRTIO_NO_VECTOR; - vdev->vq = g_malloc0(sizeof(VirtQueue) * VIRTIO_PCI_QUEUE_MAX); - vdev->vm_running = runstate_is_running(); - for(i = 0; i < VIRTIO_PCI_QUEUE_MAX; i++) { - vdev->vq[i].vector = VIRTIO_NO_VECTOR; - vdev->vq[i].vdev = vdev; - } - - vdev->name = name; - vdev->config_len = config_size; - if (vdev->config_len) - vdev->config = g_malloc0(config_size); - else - vdev->config = NULL; - - vdev->vmstate = qemu_add_vm_change_state_handler(virtio_vmstate_change, vdev); - - return vdev; -} - -void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding, - void *opaque) -{ - vdev->binding = binding; - vdev->binding_opaque = opaque; -} - -target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.desc; -} - -target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.avail; -} - -target_phys_addr_t virtio_queue_get_used_addr(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.used; -} - -target_phys_addr_t virtio_queue_get_ring_addr(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.desc; -} - -target_phys_addr_t virtio_queue_get_desc_size(VirtIODevice *vdev, int n) -{ - return sizeof(VRingDesc) * vdev->vq[n].vring.num; -} - -target_phys_addr_t virtio_queue_get_avail_size(VirtIODevice *vdev, int n) -{ - return offsetof(VRingAvail, ring) + - sizeof(uint64_t) * vdev->vq[n].vring.num; -} - -target_phys_addr_t virtio_queue_get_used_size(VirtIODevice *vdev, int n) -{ - return offsetof(VRingUsed, ring) + - sizeof(VRingUsedElem) * vdev->vq[n].vring.num; -} - -target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].vring.used - vdev->vq[n].vring.desc + - virtio_queue_get_used_size(vdev, n); -} - -uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n) -{ - return vdev->vq[n].last_avail_idx; -} - -void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx) -{ - vdev->vq[n].last_avail_idx = idx; -} - -VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n) -{ - return vdev->vq + n; -} - -static void virtio_queue_guest_notifier_read(EventNotifier *n) -{ - VirtQueue *vq = container_of(n, VirtQueue, guest_notifier); - if (event_notifier_test_and_clear(n)) { - virtio_irq(vq); - } -} - -void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign, - bool with_irqfd) -{ - if (assign && !with_irqfd) { - event_notifier_set_handler(&vq->guest_notifier, - virtio_queue_guest_notifier_read); - } else { - event_notifier_set_handler(&vq->guest_notifier, NULL); - } - if (!assign) { - /* Test and clear notifier before closing it, - * in case poll callback didn't have time to run. */ - virtio_queue_guest_notifier_read(&vq->guest_notifier); - } -} - -EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq) -{ - return &vq->guest_notifier; -} - -static void virtio_queue_host_notifier_read(EventNotifier *n) -{ - VirtQueue *vq = container_of(n, VirtQueue, host_notifier); - if (event_notifier_test_and_clear(n)) { - virtio_queue_notify_vq(vq); - } -} - -void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, - bool set_handler) -{ - if (assign && set_handler) { - event_notifier_set_handler(&vq->host_notifier, - virtio_queue_host_notifier_read); - } else { - event_notifier_set_handler(&vq->host_notifier, NULL); - } - if (!assign) { - /* Test and clear notifier before after disabling event, - * in case poll callback didn't have time to run. */ - virtio_queue_host_notifier_read(&vq->host_notifier); - } -} - -EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq) -{ - return &vq->host_notifier; -} diff --git a/hw/virtio.h b/hw/virtio.h deleted file mode 100755 index 0b035618cf..0000000000 --- a/hw/virtio.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Virtio Support - * - * Copyright IBM, Corp. 2007 - * - * Authors: - * Anthony Liguori - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef _QEMU_VIRTIO_H -#define _QEMU_VIRTIO_H - -#include "hw.h" -#include "net.h" -#include "qdev.h" -#include "sysemu.h" -#include "event_notifier.h" -#ifdef CONFIG_LINUX -#include "9p.h" -#endif -#define VIRTIO_ID_GL 6 - -/* from Linux's linux/virtio_config.h */ - -/* Status byte for guest to report progress, and synchronize features. */ -/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */ -#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 -/* We have found a driver for the device. */ -#define VIRTIO_CONFIG_S_DRIVER 2 -/* Driver has used its parts of the config, and is happy */ -#define VIRTIO_CONFIG_S_DRIVER_OK 4 -/* We've given up on this device. */ -#define VIRTIO_CONFIG_S_FAILED 0x80 - -/* Some virtio feature bits (currently bits 28 through 31) are reserved for the - * transport being used (eg. virtio_ring), the rest are per-device feature bits. */ -#define VIRTIO_TRANSPORT_F_START 28 -#define VIRTIO_TRANSPORT_F_END 32 - -/* We notify when the ring is completely used, even if the guest is suppressing - * callbacks */ -#define VIRTIO_F_NOTIFY_ON_EMPTY 24 -/* We support indirect buffer descriptors */ -#define VIRTIO_RING_F_INDIRECT_DESC 28 -/* The Guest publishes the used index for which it expects an interrupt - * at the end of the avail ring. Host should ignore the avail->flags field. */ -/* The Host publishes the avail index for which it expects a kick - * at the end of the used ring. Guest should ignore the used->flags field. */ -#define VIRTIO_RING_F_EVENT_IDX 29 -/* A guest should never accept this. It implies negotiation is broken. */ -#define VIRTIO_F_BAD_FEATURE 30 - -/* from Linux's linux/virtio_ring.h */ - -/* This marks a buffer as continuing via the next field. */ -#define VRING_DESC_F_NEXT 1 -/* This marks a buffer as write-only (otherwise read-only). */ -#define VRING_DESC_F_WRITE 2 -/* This means the buffer contains a list of buffer descriptors. */ -#define VRING_DESC_F_INDIRECT 4 - -/* This means don't notify other side when buffer added. */ -#define VRING_USED_F_NO_NOTIFY 1 -/* This means don't interrupt guest when buffer consumed. */ -#define VRING_AVAIL_F_NO_INTERRUPT 1 - -struct VirtQueue; - -static inline target_phys_addr_t vring_align(target_phys_addr_t addr, - unsigned long align) -{ - return (addr + align - 1) & ~(align - 1); -} - -typedef struct VirtQueue VirtQueue; - -#define VIRTQUEUE_MAX_SIZE 1024 - -typedef struct VirtQueueElement -{ - unsigned int index; - unsigned int out_num; - unsigned int in_num; - target_phys_addr_t in_addr[VIRTQUEUE_MAX_SIZE]; - target_phys_addr_t out_addr[VIRTQUEUE_MAX_SIZE]; - struct iovec in_sg[VIRTQUEUE_MAX_SIZE]; - struct iovec out_sg[VIRTQUEUE_MAX_SIZE]; -} VirtQueueElement; - -typedef struct { - void (*notify)(void * opaque, uint16_t vector); - void (*save_config)(void * opaque, QEMUFile *f); - void (*save_queue)(void * opaque, int n, QEMUFile *f); - int (*load_config)(void * opaque, QEMUFile *f); - int (*load_queue)(void * opaque, int n, QEMUFile *f); - int (*load_done)(void * opaque, QEMUFile *f); - unsigned (*get_features)(void * opaque); - bool (*query_guest_notifiers)(void * opaque); - int (*set_guest_notifiers)(void * opaque, bool assigned); - int (*set_host_notifier)(void * opaque, int n, bool assigned); - void (*vmstate_change)(void * opaque, bool running); -} VirtIOBindings; - -#define VIRTIO_PCI_QUEUE_MAX 64 - -#define VIRTIO_NO_VECTOR 0xffff - -struct VirtIODevice -{ - const char *name; - uint8_t status; - uint8_t isr; - uint16_t queue_sel; - uint32_t guest_features; - size_t config_len; - void *config; - uint16_t config_vector; - int nvectors; - uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features); - uint32_t (*bad_features)(VirtIODevice *vdev); - void (*set_features)(VirtIODevice *vdev, uint32_t val); - void (*get_config)(VirtIODevice *vdev, uint8_t *config); - void (*set_config)(VirtIODevice *vdev, const uint8_t *config); - void (*reset)(VirtIODevice *vdev); - void (*set_status)(VirtIODevice *vdev, uint8_t val); - VirtQueue *vq; - const VirtIOBindings *binding; - void *binding_opaque; - uint16_t device_id; - bool vm_running; - VMChangeStateEntry *vmstate; -}; - -VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_size, - void (*handle_output)(VirtIODevice *, - VirtQueue *)); - -void virtqueue_push(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len); -void virtqueue_flush(VirtQueue *vq, unsigned int count); -void virtqueue_fill(VirtQueue *vq, const VirtQueueElement *elem, - unsigned int len, unsigned int idx); - -void virtqueue_map_sg(struct iovec *sg, target_phys_addr_t *addr, - size_t num_sg, int is_write); -int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem); -int virtqueue_avail_bytes(VirtQueue *vq, int in_bytes, int out_bytes); - -void virtio_notify(VirtIODevice *vdev, VirtQueue *vq); - -void virtio_save(VirtIODevice *vdev, QEMUFile *f); - -int virtio_load(VirtIODevice *vdev, QEMUFile *f); - -void virtio_cleanup(VirtIODevice *vdev); - -void virtio_notify_config(VirtIODevice *vdev); - -void virtio_queue_set_notification(VirtQueue *vq, int enable); - -int virtio_queue_ready(VirtQueue *vq); - -int virtio_queue_empty(VirtQueue *vq); - -/* Host binding interface. */ - -VirtIODevice *virtio_common_init(const char *name, uint16_t device_id, - size_t config_size, size_t struct_size); -uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr); -uint32_t virtio_config_readw(VirtIODevice *vdev, uint32_t addr); -uint32_t virtio_config_readl(VirtIODevice *vdev, uint32_t addr); -void virtio_config_writeb(VirtIODevice *vdev, uint32_t addr, uint32_t data); -void virtio_config_writew(VirtIODevice *vdev, uint32_t addr, uint32_t data); -void virtio_config_writel(VirtIODevice *vdev, uint32_t addr, uint32_t data); -void virtio_queue_set_addr(VirtIODevice *vdev, int n, target_phys_addr_t addr); -target_phys_addr_t virtio_queue_get_addr(VirtIODevice *vdev, int n); -void virtio_queue_set_num(VirtIODevice *vdev, int n, int num); -int virtio_queue_get_num(VirtIODevice *vdev, int n); -void virtio_queue_set_align(VirtIODevice *vdev, int n, int align); -void virtio_queue_notify(VirtIODevice *vdev, int n); -uint16_t virtio_queue_vector(VirtIODevice *vdev, int n); -void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector); -void virtio_set_status(VirtIODevice *vdev, uint8_t val); -void virtio_reset(void *opaque); -void virtio_update_irq(VirtIODevice *vdev); -int virtio_set_features(VirtIODevice *vdev, uint32_t val); - -void virtio_bind_device(VirtIODevice *vdev, const VirtIOBindings *binding, - void *opaque); - -/* Base devices. */ -typedef struct VirtIOBlkConf VirtIOBlkConf; -VirtIODevice *virtio_blk_init(DeviceState *dev, VirtIOBlkConf *blk); -struct virtio_net_conf; -VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf, - struct virtio_net_conf *net); -typedef struct virtio_serial_conf virtio_serial_conf; -VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial); -VirtIODevice *virtio_balloon_init(DeviceState *dev); -typedef struct VirtIOSCSIConf VirtIOSCSIConf; -VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf); -#ifdef CONFIG_LINUX -VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf); -#endif -#ifdef CONFIG_GL_BACKEND -VirtIODevice *virtio_gl_init(DeviceState *dev); -#endif - -void virtio_net_exit(VirtIODevice *vdev); -void virtio_blk_exit(VirtIODevice *vdev); -void virtio_serial_exit(VirtIODevice *vdev); -void virtio_balloon_exit(VirtIODevice *vdev); -void virtio_scsi_exit(VirtIODevice *vdev); - -/* Maru devices */ -#ifdef CONFIG_MARU -#include "tizen/src/hw/maru_virtio_touchscreen.h" -#include "tizen/src/hw/maru_virtio_keyboard.h" -#include "tizen/src/hw/maru_virtio_esm.h" -#include "tizen/src/hw/maru_virtio_hwkey.h" -#endif - - -#define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \ - DEFINE_PROP_BIT("indirect_desc", _state, _field, \ - VIRTIO_RING_F_INDIRECT_DESC, true), \ - DEFINE_PROP_BIT("event_idx", _state, _field, \ - VIRTIO_RING_F_EVENT_IDX, true) - -target_phys_addr_t virtio_queue_get_desc_addr(VirtIODevice *vdev, int n); -target_phys_addr_t virtio_queue_get_avail_addr(VirtIODevice *vdev, int n); -target_phys_addr_t virtio_queue_get_used_addr(VirtIODevice *vdev, int n); -target_phys_addr_t virtio_queue_get_ring_addr(VirtIODevice *vdev, int n); -target_phys_addr_t virtio_queue_get_desc_size(VirtIODevice *vdev, int n); -target_phys_addr_t virtio_queue_get_avail_size(VirtIODevice *vdev, int n); -target_phys_addr_t virtio_queue_get_used_size(VirtIODevice *vdev, int n); -target_phys_addr_t virtio_queue_get_ring_size(VirtIODevice *vdev, int n); -uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n); -void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx); -VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n); -int virtio_queue_get_id(VirtQueue *vq); -EventNotifier *virtio_queue_get_guest_notifier(VirtQueue *vq); -void virtio_queue_set_guest_notifier_fd_handler(VirtQueue *vq, bool assign, - bool with_irqfd); -EventNotifier *virtio_queue_get_host_notifier(VirtQueue *vq); -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); -#endif diff --git a/hw/virtio-mmio.c b/hw/virtio/virtio-mmio.c similarity index 100% rename from hw/virtio-mmio.c rename to hw/virtio/virtio-mmio.c -- 2.34.1