+++ /dev/null
-/*
- * Virtio Balloon Device
- *
- * Copyright IBM, Corp. 2008
- * Copyright (C) 2011 Red Hat, Inc.
- * Copyright (C) 2011 Amit Shah <amit.shah@redhat.com>
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * 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 <sys/mman.h>
-#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)
+++ /dev/null
-/*
- * Virtio Support
- *
- * Copyright IBM, Corp. 2007-2008
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- * Rusty Russell <rusty@rustcorp.com.au>
- *
- * 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
+++ /dev/null
-/*
- * Virtio Block Device
- *
- * Copyright IBM, Corp. 2007
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * 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 <scsi/sg.h>
-#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)
+++ /dev/null
-/*
- * Virtio Block Device
- *
- * Copyright IBM, Corp. 2007
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * 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
+++ /dev/null
-/*
- * Virtio Network Device
- *
- * Copyright IBM, Corp. 2007
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * 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)
+++ /dev/null
-/*
- * Virtio Network Device
- *
- * Copyright IBM, Corp. 2007
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * 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
+++ /dev/null
-/*
- * Virtio PCI Bindings
- *
- * Copyright IBM, Corp. 2007
- * Copyright (c) 2009 CodeSourcery
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- * Paul Brook <paul@codesourcery.com>
- *
- * 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 <inttypes.h>
-
-#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)
+++ /dev/null
-/*
- * Virtio PCI Bindings
- *
- * Copyright IBM, Corp. 2007
- * Copyright (c) 2009 CodeSourcery
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- * Paul Brook <paul@codesourcery.com>
- *
- * 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 <inttypes.h>
-
-#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)
+++ /dev/null
-/*
- * Virtio PCI Bindings
- *
- * Copyright IBM, Corp. 2007
- * Copyright (c) 2009 CodeSourcery
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- * Paul Brook <paul@codesourcery.com>
- *
- * 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
+++ /dev/null
-/*
- * A bus for connecting virtio serial and console ports
- *
- * Copyright (C) 2009, 2010 Red Hat, Inc.
- *
- * Author(s):
- * Amit Shah <amit.shah@redhat.com>
- *
- * Some earlier parts are:
- * Copyright IBM, Corp. 2008
- * authored by
- * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
- *
- * 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)
+++ /dev/null
-/*
- * Virtio Serial / Console Support
- *
- * Copyright IBM, Corp. 2008
- * Copyright Red Hat, Inc. 2009, 2010
- *
- * Authors:
- * Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
- * Amit Shah <amit.shah@redhat.com>
- *
- * 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
+++ /dev/null
-/*
- * Virtio transport bindings
- *
- * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
- *
- * Author:
- * Evgeny Voevodin <e.voevodin@samsung.com>
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#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)
+++ /dev/null
-/*
- * Virtio transport header
- *
- * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd.
- *
- * Author:
- * Evgeny Voevodin <e.voevodin@samsung.com>
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#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_ */
+++ /dev/null
-/*
- * Virtio Support
- *
- * Copyright IBM, Corp. 2007
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * This work is licensed under the terms of the GNU GPL, version 2. See
- * the COPYING file in the top-level directory.
- *
- */
-
-#include <inttypes.h>
-
-#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;
-}
+++ /dev/null
-/*
- * Virtio Support
- *
- * Copyright IBM, Corp. 2007
- *
- * Authors:
- * Anthony Liguori <aliguori@us.ibm.com>
- *
- * 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