From 0fba2c3db55f183f5a1727c749d1b6ab164d68a5 Mon Sep 17 00:00:00 2001 From: Evgeny Voevodin Date: Wed, 13 Jun 2012 11:40:54 +0400 Subject: [PATCH] Virtio: Add virtio-mmio support. We need this patch-set be included to let ARM board use virtio interface to speed up execution. Signed-off-by: Evgeny Voevodin --- Makefile.objs | 2 + hw/virtio-balloon.c | 34 ++++ hw/virtio-balloon.h | 7 + hw/virtio-blk.c | 48 ++++- hw/virtio-blk.h | 10 + hw/virtio-mmio.c | 421 +++++++++++++++++++++++++++++++++++++++++ hw/virtio-net.c | 46 +++++ hw/virtio-net.h | 11 ++ hw/virtio-serial-bus.c | 42 ++++ hw/virtio-serial.h | 9 + hw/virtio-transport.c | 69 +++++++ hw/virtio-transport.h | 42 ++++ hw/virtio.c | 20 +- hw/virtio.h | 2 + 14 files changed, 755 insertions(+), 8 deletions(-) create mode 100644 hw/virtio-mmio.c create mode 100644 hw/virtio-transport.c create mode 100644 hw/virtio-transport.h diff --git a/Makefile.objs b/Makefile.objs index 70c5c79a6e..6c9efd3218 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -212,6 +212,8 @@ hw-obj-y += vl.o loader.o hw-obj-$(CONFIG_VIRTIO) += virtio-console.o hw-obj-y += usb/libhw.o hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o +hw-obj-$(CONFIG_VIRTIO) += virtio-transport.o +hw-obj-$(CONFIG_VIRTIO) += virtio-mmio.o hw-obj-y += fw_cfg.o hw-obj-$(CONFIG_PCI) += pci.o pci_bridge.o pci_bridge_dev.o hw-obj-$(CONFIG_PCI) += msix.o msi.o diff --git a/hw/virtio-balloon.c b/hw/virtio-balloon.c index 075ed87e37..699ed8956c 100644 --- a/hw/virtio-balloon.c +++ b/hw/virtio-balloon.c @@ -20,6 +20,7 @@ #include "cpu.h" #include "balloon.h" #include "virtio-balloon.h" +#include "virtio-transport.h" #include "kvm.h" #include "exec-memory.h" @@ -267,3 +268,36 @@ void virtio_balloon_exit(VirtIODevice *vdev) unregister_savevm(s->qdev, "virtio-balloon", s); virtio_cleanup(vdev); } + +/******************** VirtIOBaloon Device **********************/ + +static int virtio_balloondev_init(DeviceState *dev) +{ + VirtIODevice *vdev; + vdev = virtio_balloon_init(dev); + if (!vdev) { + return -1; + } + return virtio_init_transport(dev, vdev); +} + +static void virtio_balloon_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + dc->init = virtio_balloondev_init; + dc->bus_info = &virtio_transport_bus_info; +} + +static TypeInfo virtio_balloon_info = { + .name = "virtio-balloon", + .parent = TYPE_DEVICE, + .instance_size = sizeof(VirtIOBaloonState), + .class_init = virtio_balloon_class_init, +}; + +static void virtio_baloon_register_types(void) +{ + type_register_static(&virtio_balloon_info); +} + +type_init(virtio_baloon_register_types) diff --git a/hw/virtio-balloon.h b/hw/virtio-balloon.h index 73300ddc86..e1342269a2 100644 --- a/hw/virtio-balloon.h +++ b/hw/virtio-balloon.h @@ -15,6 +15,7 @@ #ifndef _QEMU_VIRTIO_BALLOON_H #define _QEMU_VIRTIO_BALLOON_H +#include "sysbus.h" #include "virtio.h" #include "pci.h" @@ -52,4 +53,10 @@ typedef struct VirtIOBalloonStat { uint64_t val; } QEMU_PACKED VirtIOBalloonStat; +typedef struct { + DeviceState qdev; +} VirtIOBaloonState; + +#define VIRTIO_BALLOON_FROM_QDEV(dev) DO_UPCAST(VirtIOBaloonState, qdev, dev) + #endif diff --git a/hw/virtio-blk.c b/hw/virtio-blk.c index 3c280f5aa0..0a62a192c1 100644 --- a/hw/virtio-blk.c +++ b/hw/virtio-blk.c @@ -15,6 +15,7 @@ #include "qemu-error.h" #include "trace.h" #include "blockdev.h" +#include "virtio-transport.h" #include "virtio-blk.h" #include "scsi-defs.h" #ifdef __linux__ @@ -151,12 +152,6 @@ static void virtio_blk_handle_scsi(VirtIOBlockReq *req) int status = VIRTIO_BLK_S_OK; int i; - if ((req->dev->vdev.guest_features & (1 << VIRTIO_BLK_F_SCSI)) == 0) { - virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); - g_free(req); - return; - } - /* * We require at least one output segment each for the virtio_blk_outhdr * and the SCSI command block. @@ -635,3 +630,44 @@ void virtio_blk_exit(VirtIODevice *vdev) blockdev_mark_auto_del(s->bs); virtio_cleanup(vdev); } + +/******************** VirtIOBlk Device **********************/ + +static int virtio_blkdev_init(DeviceState *dev) +{ + VirtIODevice *vdev; + VirtIOBlockState *proxy = VIRTIO_BLK_FROM_QDEV(dev); + vdev = virtio_blk_init(dev, &proxy->block); + if (!vdev) { + return -1; + } + return virtio_init_transport(dev, vdev); +} + +static Property virtio_blkdev_properties[] = { + DEFINE_BLOCK_PROPERTIES(VirtIOBlockState, block.conf), + DEFINE_PROP_STRING("serial", VirtIOBlockState, block.serial), + 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; + dc->bus_info = &virtio_transport_bus_info; +} + +static TypeInfo virtio_blkdev_info = { + .name = "virtio-blk", + .parent = TYPE_DEVICE, + .instance_size = sizeof(VirtIOBlockState), + .class_init = virtio_blkdev_class_init, +}; + +static void virtio_blk_register_types(void) +{ + type_register_static(&virtio_blkdev_info); +} + +type_init(virtio_blk_register_types) diff --git a/hw/virtio-blk.h b/hw/virtio-blk.h index d7850012bd..e3bd8af01d 100644 --- a/hw/virtio-blk.h +++ b/hw/virtio-blk.h @@ -14,6 +14,7 @@ #ifndef _QEMU_VIRTIO_BLK_H #define _QEMU_VIRTIO_BLK_H +#include "sysbus.h" #include "virtio.h" #include "block.h" @@ -107,4 +108,13 @@ struct VirtIOBlkConf #define DEFINE_VIRTIO_BLK_FEATURES(_state, _field) \ DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) + +typedef struct { + DeviceState qdev; + /* virtio-blk */ + VirtIOBlkConf block; +} VirtIOBlockState; + +#define VIRTIO_BLK_FROM_QDEV(dev) DO_UPCAST(VirtIOBlockState, qdev, dev) + #endif diff --git a/hw/virtio-mmio.c b/hw/virtio-mmio.c new file mode 100644 index 0000000000..1598e04cd1 --- /dev/null +++ b/hw/virtio-mmio.c @@ -0,0 +1,421 @@ +/* + * Virtio MMIO bindings + * + * Copyright (c) 2011 Linaro Limited + * + * Authors: + * Peter Maydell + * Evgeny Voevodin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +/* TODO: + * * save/load support + * * test net, serial, balloon + */ + +#include "sysbus.h" +#include "virtio.h" +#include "virtio-transport.h" +#include "virtio-blk.h" +#include "virtio-net.h" +#include "virtio-serial.h" +#include "host-utils.h" + +/* #define DEBUG_VIRTIO_MMIO */ + +#ifdef DEBUG_VIRTIO_MMIO + +#define DPRINTF(fmt, ...) \ +do { printf("virtio_mmio: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +/* Memory mapped register offsets */ +#define VIRTIO_MMIO_MAGIC 0x0 +#define VIRTIO_MMIO_VERSION 0x4 +#define VIRTIO_MMIO_DEVICEID 0x8 +#define VIRTIO_MMIO_VENDORID 0xc +#define VIRTIO_MMIO_HOSTFEATURES 0x10 +#define VIRTIO_MMIO_HOSTFEATURESSEL 0x14 +#define VIRTIO_MMIO_GUESTFEATURES 0x20 +#define VIRTIO_MMIO_GUESTFEATURESSEL 0x24 +#define VIRTIO_MMIO_GUESTPAGESIZE 0x28 +#define VIRTIO_MMIO_QUEUESEL 0x30 +#define VIRTIO_MMIO_QUEUENUMMAX 0x34 +#define VIRTIO_MMIO_QUEUENUM 0x38 +#define VIRTIO_MMIO_QUEUEALIGN 0x3c +#define VIRTIO_MMIO_QUEUEPFN 0x40 +#define VIRTIO_MMIO_QUEUENOTIFY 0x50 +#define VIRTIO_MMIO_INTERRUPTSTATUS 0x60 +#define VIRTIO_MMIO_INTERRUPTACK 0x64 +#define VIRTIO_MMIO_STATUS 0x70 +/* Device specific config space starts here */ +#define VIRTIO_MMIO_CONFIG 0x100 + +#define VIRT_MAGIC 0x74726976 /* 'virt' */ +#define VIRT_VERSION 1 +#define VIRT_VENDOR 0x554D4551 /* 'QEMU' */ + +#define VIRTIO_MMIO "virtio-mmio" +#define VIRTIO_MMIO_BUS "virtio-mmio-bus" + +struct BusInfo virtio_mmio_bus_info = { + .name = VIRTIO_MMIO_BUS, + .size = sizeof(BusState), +}; + +enum VIRTIO_MMIO_MAPPINGS { + VIRTIO_MMIO_IOMAP, + VIRTIO_MMIO_IOMEM, +}; + +typedef struct { + SysBusDevice busdev; + VirtIODevice *vdev; + MemoryRegion iomap; /* hold base address */ + MemoryRegion iomem; /* hold io funcs */ + MemoryRegion alias; + qemu_irq irq; + uint32_t int_enable; + uint32_t host_features; + uint32_t host_features_sel; + uint32_t guest_features_sel; + uint32_t guest_page_shift; +} VirtIOMMIOTransportState; + +static uint64_t virtio_mmio_read(void *opaque, target_phys_addr_t offset, + unsigned size) +{ + VirtIOMMIOTransportState *transport = (VirtIOMMIOTransportState *)opaque; + VirtIODevice *vdev = transport->vdev; + DPRINTF("virtio_mmio_read offset 0x%x\n", (int)offset); + if (offset >= VIRTIO_MMIO_CONFIG) { + offset -= VIRTIO_MMIO_CONFIG; + switch (size) { + case 1: + return virtio_config_readb(vdev, offset); + case 2: + return virtio_config_readw(vdev, offset); + case 4: + return virtio_config_readl(vdev, offset); + default: + abort(); + } + } + if (size != 4) { + DPRINTF("wrong size access to register!\n"); + return 0; + } + switch (offset) { + case VIRTIO_MMIO_MAGIC: + return VIRT_MAGIC; + case VIRTIO_MMIO_VERSION: + return VIRT_VERSION; + case VIRTIO_MMIO_DEVICEID: + return vdev->device_id; + case VIRTIO_MMIO_VENDORID: + return VIRT_VENDOR; + case VIRTIO_MMIO_HOSTFEATURES: + if (transport->host_features_sel) { + return 0; + } + return transport->host_features; + case VIRTIO_MMIO_QUEUENUMMAX: + return VIRTQUEUE_MAX_SIZE; + case VIRTIO_MMIO_QUEUEPFN: + return virtio_queue_get_addr(vdev, vdev->queue_sel) + >> transport->guest_page_shift; + case VIRTIO_MMIO_INTERRUPTSTATUS: + return vdev->isr; + case VIRTIO_MMIO_STATUS: + return vdev->status; + case VIRTIO_MMIO_HOSTFEATURESSEL: + case VIRTIO_MMIO_GUESTFEATURES: + case VIRTIO_MMIO_GUESTFEATURESSEL: + case VIRTIO_MMIO_GUESTPAGESIZE: + case VIRTIO_MMIO_QUEUESEL: + case VIRTIO_MMIO_QUEUENUM: + case VIRTIO_MMIO_QUEUEALIGN: + case VIRTIO_MMIO_QUEUENOTIFY: + case VIRTIO_MMIO_INTERRUPTACK: + DPRINTF("read of write-only register\n"); + return 0; + default: + DPRINTF("bad register offset\n"); + return 0; + } + return 0; +} + +static void virtio_mmio_write(void *opaque, target_phys_addr_t offset, + uint64_t value, unsigned size) +{ + VirtIOMMIOTransportState *transport = (VirtIOMMIOTransportState *)opaque; + VirtIODevice *vdev = transport->vdev; + DPRINTF("virtio_mmio_write offset 0x%x value 0x%" PRIx64 "\n", + (int)offset, value); + if (offset >= VIRTIO_MMIO_CONFIG) { + offset -= VIRTIO_MMIO_CONFIG; + switch (size) { + case 1: + virtio_config_writeb(vdev, offset, value); + break; + case 2: + virtio_config_writew(vdev, offset, value); + break; + case 4: + virtio_config_writel(vdev, offset, value); + break; + default: + abort(); + } + return; + } + if (size != 4) { + DPRINTF("wrong size access to register!\n"); + return; + } + switch (offset) { + case VIRTIO_MMIO_HOSTFEATURESSEL: + transport->host_features_sel = value; + break; + case VIRTIO_MMIO_GUESTFEATURES: + if (!transport->guest_features_sel) { + virtio_set_features(vdev, value); + } + break; + case VIRTIO_MMIO_GUESTFEATURESSEL: + transport->guest_features_sel = value; + break; + case VIRTIO_MMIO_GUESTPAGESIZE: + transport->guest_page_shift = ctz32(value); + if (transport->guest_page_shift > 31) { + transport->guest_page_shift = 0; + } + DPRINTF("guest page size %" PRIx64 " shift %d\n", value, + transport->guest_page_shift); + break; + case VIRTIO_MMIO_QUEUESEL: + if (value < VIRTIO_PCI_QUEUE_MAX) { + vdev->queue_sel = value; + } + break; + case VIRTIO_MMIO_QUEUENUM: + DPRINTF("mmio_queue write %d max %d\n", (int)value, VIRTQUEUE_MAX_SIZE); + if (value <= VIRTQUEUE_MAX_SIZE) { + DPRINTF("calling virtio_queue_set_num\n"); + virtio_queue_set_num(vdev, vdev->queue_sel, value); + } + break; + case VIRTIO_MMIO_QUEUEALIGN: + virtio_queue_set_align(vdev, vdev->queue_sel, value); + break; + case VIRTIO_MMIO_QUEUEPFN: + if (value == 0) { + virtio_reset(vdev); + } else { + virtio_queue_set_addr(vdev, vdev->queue_sel, + value << transport->guest_page_shift); + } + break; + case VIRTIO_MMIO_QUEUENOTIFY: + if (value < VIRTIO_PCI_QUEUE_MAX) { + virtio_queue_notify(vdev, value); + } + break; + case VIRTIO_MMIO_INTERRUPTACK: + vdev->isr &= ~value; + virtio_update_irq(vdev); + break; + case VIRTIO_MMIO_STATUS: + virtio_set_status(vdev, value & 0xff); + if (vdev->status == 0) { + virtio_reset(vdev); + } + break; + case VIRTIO_MMIO_MAGIC: + case VIRTIO_MMIO_VERSION: + case VIRTIO_MMIO_DEVICEID: + case VIRTIO_MMIO_VENDORID: + case VIRTIO_MMIO_HOSTFEATURES: + case VIRTIO_MMIO_QUEUENUMMAX: + case VIRTIO_MMIO_INTERRUPTSTATUS: + DPRINTF("write to readonly register\n"); + break; + + default: + DPRINTF("bad register offset\n"); + } +} + +static const MemoryRegionOps virtio_mem_ops = { + .read = virtio_mmio_read, + .write = virtio_mmio_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static void virtio_mmio_update_irq(void *opaque, uint16_t vector) +{ + VirtIOMMIOTransportState *transport = opaque; + int level = (transport->vdev->isr != 0); + DPRINTF("virtio_mmio setting IRQ %d\n", level); + qemu_set_irq(transport->irq, level); +} + +static unsigned int virtio_mmio_get_features(void *opaque) +{ + VirtIOMMIOTransportState *transport = opaque; + return transport->host_features; +} + +static int virtio_mmio_load_config(void *opaque, QEMUFile *f) +{ + VirtIOMMIOTransportState *transport = opaque; + transport->int_enable = qemu_get_be32(f); + transport->host_features = qemu_get_be32(f); + transport->host_features_sel = qemu_get_be32(f); + transport->guest_features_sel = qemu_get_be32(f); + transport->guest_page_shift = qemu_get_be32(f); + return 0; +} + +static void virtio_mmio_save_config(void *opaque, QEMUFile *f) +{ + VirtIOMMIOTransportState *transport = opaque; + qemu_put_be32(f, transport->int_enable); + qemu_put_be32(f, transport->host_features); + qemu_put_be32(f, transport->host_features_sel); + qemu_put_be32(f, transport->guest_features_sel); + qemu_put_be32(f, transport->guest_page_shift); +} + +static VirtIOBindings virtio_mmio_bindings = { + .notify = virtio_mmio_update_irq, + .get_features = virtio_mmio_get_features, + .save_config = virtio_mmio_save_config, + .load_config = virtio_mmio_load_config, +}; + +static void virtio_mmio_attach_to_transport(DeviceState *dev) +{ + DeviceState *transport_dev = qdev_get_parent_bus(dev)->parent; + SysBusDevice *transport_sysbus = sysbus_from_qdev(transport_dev); + VirtIOMMIOTransportState *transport = + FROM_SYSBUS(VirtIOMMIOTransportState, transport_sysbus); + + /* Create alias and add it as subregion to transport iomem */ + memory_region_init_alias(&transport->alias, + "virtio-mmio-alias", + &transport->iomem, + 0, + 0x1000); + /* add alias as subregion to transport iomap */ + memory_region_add_subregion(&transport->iomap, + 0, + &transport->alias); +} + +static int virtio_init_mmio_transport(DeviceState *dev, VirtIODevice *vdev) +{ + DeviceState *transport_dev = qdev_get_parent_bus(dev)->parent; + SysBusDevice *transport_sysbus = sysbus_from_qdev(transport_dev); + VirtIOMMIOTransportState *transport = + FROM_SYSBUS(VirtIOMMIOTransportState, transport_sysbus); + + transport->vdev = vdev; + transport->vdev->nvectors = 0; + sysbus_init_irq(&transport->busdev, &transport->irq); + memory_region_init_io(&transport->iomem, &virtio_mem_ops, transport, + "virtio-mmio-iomem", 0x1000); + sysbus_init_mmio(&transport->busdev, &transport->iomem); + virtio_bind_device(vdev, &virtio_mmio_bindings, transport); + transport->host_features |= (0x1 << VIRTIO_F_NOTIFY_ON_EMPTY); + transport->host_features = + vdev->get_features(vdev, transport->host_features); + + virtio_mmio_attach_to_transport(dev); + return 0; +} + +static void virtio_mmio_transport_handler(void *opaque, int irq, int level) +{ + VirtIOMMIOTransportState *s = (VirtIOMMIOTransportState *)opaque; + + qemu_set_irq(s->irq, level); + + return; +} + +static int virtio_mmio_transport_device_init(SysBusDevice *busdev) +{ + VirtIOMMIOTransportState *s = + DO_UPCAST(VirtIOMMIOTransportState, busdev, busdev); + BusState *parent_bus = qdev_get_parent_bus(&busdev->qdev); + BusState *child_bus; + VirtIOTransportBusState *virtio_transport_bus; + char *buf; + int len; + uint32_t i; + + i = virtio_count_siblings(parent_bus, VIRTIO_MMIO); + + len = strlen(VIRTIO_MMIO) + 16; + buf = g_malloc(len); + snprintf(buf, len, "%s.%d", VIRTIO_MMIO, i); + child_bus = qbus_create(&virtio_transport_bus_info, &busdev->qdev, buf); + + /* Populate mmio init transport function */ + virtio_transport_bus = DO_UPCAST(VirtIOTransportBusState, bus, child_bus); + virtio_transport_bus->init_fn = virtio_init_mmio_transport; + + qdev_init_gpio_in(&s->busdev.qdev, virtio_mmio_transport_handler, 1); + + sysbus_init_irq(busdev, &s->irq); + memory_region_init(&s->iomap, "virtio-mmio-iomap", 0x1000); + sysbus_init_mmio(busdev, &s->iomap); + return 0; +} + +static void virtio_mmio_reset(DeviceState *d) +{ + VirtIOMMIOTransportState *transport = + FROM_SYSBUS(VirtIOMMIOTransportState, sysbus_from_qdev(d)); + if (transport->vdev) { + virtio_reset(transport->vdev); + } +} + +static void virtio_mmio_transport_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + k->init = virtio_mmio_transport_device_init; + dc->reset = virtio_mmio_reset; +} + +static TypeInfo virtio_mmio_transport_info = { + .name = VIRTIO_MMIO_TRANSPORT, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(VirtIOMMIOTransportState), + .class_init = virtio_mmio_transport_class_init, +}; + +static void virtio_mmio_register_types(void) +{ + type_register_static(&virtio_mmio_transport_info); +} + +type_init(virtio_mmio_register_types) diff --git a/hw/virtio-net.c b/hw/virtio-net.c index 3f190d417e..350fbb6948 100644 --- a/hw/virtio-net.c +++ b/hw/virtio-net.c @@ -13,6 +13,7 @@ #include "iov.h" #include "virtio.h" +#include "virtio-transport.h" #include "net.h" #include "net/checksum.h" #include "net/tap.h" @@ -1080,3 +1081,48 @@ void virtio_net_exit(VirtIODevice *vdev) qemu_del_vlan_client(&n->nic->nc); virtio_cleanup(&n->vdev); } + +/******************** VirtIONet Device **********************/ + +static int virtio_netdev_init(DeviceState *dev) +{ + VirtIODevice *vdev; + VirtIONetState *proxy = VIRTIO_NET_FROM_QDEV(dev); + vdev = virtio_net_init(dev, &proxy->nic, &proxy->net); + if (!vdev) { + return -1; + } + return virtio_init_transport(dev, vdev); +} + +static Property virtio_net_properties[] = { + 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_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; + dc->bus_info = &virtio_transport_bus_info; +} + +static TypeInfo virtio_net_info = { + .name = "virtio-net", + .parent = TYPE_DEVICE, + .instance_size = sizeof(VirtIONetState), + .class_init = virtio_net_class_init, +}; + +static void virtio_net_register_types(void) +{ + type_register_static(&virtio_net_info); +} + +type_init(virtio_net_register_types) diff --git a/hw/virtio-net.h b/hw/virtio-net.h index 36aa463812..74a55c854e 100644 --- a/hw/virtio-net.h +++ b/hw/virtio-net.h @@ -14,6 +14,7 @@ #ifndef _QEMU_VIRTIO_NET_H #define _QEMU_VIRTIO_NET_H +#include "sysbus.h" #include "virtio.h" #include "net.h" #include "pci.h" @@ -187,4 +188,14 @@ struct virtio_net_ctrl_mac { 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; +} VirtIONetState; + +#define VIRTIO_NET_FROM_QDEV(dev) DO_UPCAST(VirtIONetState, qdev, dev) + #endif diff --git a/hw/virtio-serial-bus.c b/hw/virtio-serial-bus.c index 72287d10ce..af63edbbed 100644 --- a/hw/virtio-serial-bus.c +++ b/hw/virtio-serial-bus.c @@ -24,6 +24,7 @@ #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 { @@ -1000,3 +1001,44 @@ static void virtio_serial_register_types(void) } type_init(virtio_serial_register_types) + +/******************** VirtIOSer Device **********************/ + +static int virtio_serialdev_init(DeviceState *dev) +{ + VirtIODevice *vdev; + VirtIOSerState *proxy = VIRTIO_SERIAL_FROM_QDEV(dev); + vdev = virtio_serial_init(dev, &proxy->serial); + if (!vdev) { + return -1; + } + return virtio_init_transport(dev, 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; + dc->bus_info = &virtio_transport_bus_info; +} + +static TypeInfo virtio_serial_info = { + .name = "virtio-serial", + .parent = TYPE_DEVICE, + .instance_size = sizeof(VirtIOSerState), + .class_init = virtio_serial_class_init, +}; + +static void virtio_ser_register_types(void) +{ + type_register_static(&virtio_serial_info); +} + +type_init(virtio_ser_register_types) diff --git a/hw/virtio-serial.h b/hw/virtio-serial.h index 16e39820a2..2290aac493 100644 --- a/hw/virtio-serial.h +++ b/hw/virtio-serial.h @@ -15,6 +15,7 @@ #ifndef _QEMU_VIRTIO_SERIAL_H #define _QEMU_VIRTIO_SERIAL_H +#include "sysbus.h" #include "qdev.h" #include "virtio.h" @@ -173,6 +174,14 @@ struct VirtIOSerialPort { bool throttled; }; +typedef struct { + DeviceState qdev; + /* virtio-serial */ + virtio_serial_conf serial; +} VirtIOSerState; + +#define VIRTIO_SERIAL_FROM_QDEV(dev) DO_UPCAST(VirtIOSerState, qdev, dev) + /* Interface to the virtio-serial bus */ /* diff --git a/hw/virtio-transport.c b/hw/virtio-transport.c new file mode 100644 index 0000000000..de9edc9a5f --- /dev/null +++ b/hw/virtio-transport.c @@ -0,0 +1,69 @@ +/* + * Virtio transport bindings + * + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. + * + * Author: + * Evgeny Voevodin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "virtio-transport.h" + +#define VIRTIO_TRANSPORT_BUS "virtio-transport" + +struct BusInfo virtio_transport_bus_info = { + .name = VIRTIO_TRANSPORT_BUS, + .size = sizeof(VirtIOTransportBusState), +}; + +int virtio_init_transport(DeviceState *dev, VirtIODevice *vdev) +{ + DeviceState *transport_dev = qdev_get_parent_bus(dev)->parent; + BusState *bus; + VirtIOTransportBusState *virtio_transport_bus; + + /* transport device has single child bus */ + bus = QLIST_FIRST(&transport_dev->child_bus); + virtio_transport_bus = DO_UPCAST(VirtIOTransportBusState, bus, bus); + + if (virtio_transport_bus->init_fn) { + return virtio_transport_bus->init_fn(dev, vdev); + } + + return 0; +} + +uint32_t virtio_count_siblings(BusState *parent_bus, const char *child_bus) +{ + DeviceState *dev; + BusState *bus; + uint32_t i = 0; + char *buf; + int len; + + len = strlen(child_bus) + 1; + buf = g_malloc(len); + snprintf(buf, len, "%s.", child_bus); + + QTAILQ_FOREACH(dev, &parent_bus->children, sibling) { + QLIST_FOREACH(bus, &dev->child_bus, sibling) { + if (strncmp(buf, bus->name, strlen(buf)) == 0) { + i++; + } + } + } + + return i; +} diff --git a/hw/virtio-transport.h b/hw/virtio-transport.h new file mode 100644 index 0000000000..8930116496 --- /dev/null +++ b/hw/virtio-transport.h @@ -0,0 +1,42 @@ +/* + * Virtio transport header + * + * Copyright (c) 2011 - 2012 Samsung Electronics Co., Ltd. + * + * Author: + * Evgeny Voevodin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#ifndef VIRTIO_TRANSPORT_H_ +#define VIRTIO_TRANSPORT_H_ + +#include "sysbus.h" +#include "virtio.h" + +#define VIRTIO_MMIO_TRANSPORT "virtio-mmio-transport" + +extern struct BusInfo virtio_transport_bus_info; + +typedef int (*virtio_init_transport_fn)(DeviceState *dev, VirtIODevice *vdev); + +typedef struct VirtIOTransportBusState { + BusState bus; + virtio_init_transport_fn init_fn; +} VirtIOTransportBusState; + +int virtio_init_transport(DeviceState *dev, VirtIODevice *vdev); +uint32_t virtio_count_siblings(BusState *parent_bus, const char *child_bus); + +#endif /* VIRTIO_TRANSPORT_H_ */ diff --git a/hw/virtio.c b/hw/virtio.c index 168abe4864..38c59f6949 100644 --- a/hw/virtio.c +++ b/hw/virtio.c @@ -19,7 +19,9 @@ #include "qemu-barrier.h" /* The alignment to use between consumer and producer parts of vring. - * x86 pagesize again. */ + * 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 @@ -53,6 +55,7 @@ typedef struct 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; @@ -90,7 +93,7 @@ static void virtqueue_init(VirtQueue *vq) vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc); vq->vring.used = vring_align(vq->vring.avail + offsetof(VRingAvail, ring[vq->vring.num]), - VIRTIO_PCI_VRING_ALIGN); + vq->vring.align); } static inline uint64_t vring_desc_addr(target_phys_addr_t desc_pa, int i) @@ -628,6 +631,12 @@ 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; @@ -640,6 +649,12 @@ int virtio_queue_get_id(VirtQueue *vq) 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) { @@ -680,6 +695,7 @@ VirtQueue *virtio_add_queue(VirtIODevice *vdev, int queue_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]; diff --git a/hw/virtio.h b/hw/virtio.h index 5218ebed1a..f5ae77fa2d 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -179,7 +179,9 @@ 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); -- 2.34.1