From patchwork Wed Apr 25 05:54:12 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Evgeny Voevodin X-Patchwork-Id: 154803 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 04832B6FAA for ; Wed, 25 Apr 2012 15:55:46 +1000 (EST) Received: from localhost ([::1]:45962 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SMvCZ-0005Ck-Uh for incoming@patchwork.ozlabs.org; Wed, 25 Apr 2012 01:55:43 -0400 Received: from eggs.gnu.org ([208.118.235.92]:32808) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SMvBV-0002qQ-HU for qemu-devel@nongnu.org; Wed, 25 Apr 2012 01:54:40 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SMvBP-0006d1-Pk for qemu-devel@nongnu.org; Wed, 25 Apr 2012 01:54:37 -0400 Received: from mailout1.w1.samsung.com ([210.118.77.11]:28625) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SMvBP-0006WG-GU for qemu-devel@nongnu.org; Wed, 25 Apr 2012 01:54:31 -0400 Received: from euspt2 (mailout1.w1.samsung.com [210.118.77.11]) by mailout1.w1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTP id <0M3000B46TOF3Z@mailout1.w1.samsung.com> for qemu-devel@nongnu.org; Wed, 25 Apr 2012 06:53:03 +0100 (BST) Received: from evvoevodinPC.rnd.samsung.ru ([106.109.9.191]) by spt2.w1.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTPA id <0M300064STQJKX@spt2.w1.samsung.com> for qemu-devel@nongnu.org; Wed, 25 Apr 2012 06:54:27 +0100 (BST) Date: Wed, 25 Apr 2012 09:54:12 +0400 From: Evgeny Voevodin In-reply-to: <1335333257-5128-1-git-send-email-e.voevodin@samsung.com> To: qemu-devel@nongnu.org Message-id: <1335333257-5128-5-git-send-email-e.voevodin@samsung.com> MIME-version: 1.0 X-Mailer: git-send-email 1.7.5.4 Content-type: TEXT/PLAIN Content-transfer-encoding: 7BIT References: <1335333257-5128-1-git-send-email-e.voevodin@samsung.com> X-detected-operating-system: by eggs.gnu.org: Solaris 9.1 X-Received-From: 210.118.77.11 Cc: peter.maydell@linaro.org, aliguori@us.ibm.com, i.mitsyanko@samsung.com, Evgeny Voevodin , kyungmin.park@samsung.com, d.solodkiy@samsung.com, m.kozlov@samsung.com Subject: [Qemu-devel] [RFC 4/9] Add MMIO based virtio transport X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Peter Maydell Add support for the generic MMIO based virtio transport. This patch is a modyfied patch of Peter Maydell . Changes are to have virtio-mmio bridge device which provides virtio-mmio bus. To this bus virtio-mmio-transport device is connected and in turn provides virtio-transport bus. Then virtio backends could be connected to this bus. Also this patch includes some fixes for bugs spotted by Ying-Shiuan Pan . Signed-off-by: Evgeny Voevodin --- Makefile.objs | 1 + hw/virtio-mmio.c | 480 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/virtio-mmio.h | 31 ++++ 3 files changed, 512 insertions(+), 0 deletions(-) create mode 100644 hw/virtio-mmio.c create mode 100644 hw/virtio-mmio.h diff --git a/Makefile.objs b/Makefile.objs index 5a648bc..fe95bdd 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -213,6 +213,7 @@ 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-mmio.c b/hw/virtio-mmio.c new file mode 100644 index 0000000..932b3f3 --- /dev/null +++ b/hw/virtio-mmio.c @@ -0,0 +1,480 @@ +/* + * 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" +#include "virtio-mmio.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_BRIDGE "virtio-mmio-bridge" +#define VIRTIO_MMIO_TRANSPORT "virtio-mmio-transport" +#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; + +BusState *virtio_mmio_bus_init(void) +{ + DeviceState *dev; + + /* Create bridge device */ + dev = qdev_create(NULL, VIRTIO_MMIO_BRIDGE); + qdev_init_nofail(dev); + + return qbus_create(&virtio_mmio_bus_info, dev, virtio_mmio_bus_info.name); +} + +void virtio_mmio_create_transport(BusState *bus, target_phys_addr_t addr, + qemu_irq irq) +{ + DeviceState *dev; + SysBusDevice *busdev; + + dev = qdev_create(bus, VIRTIO_MMIO_TRANSPORT); + qdev_init_nofail(dev); + busdev = sysbus_from_qdev(dev); + sysbus_connect_irq(busdev, 0, irq); + sysbus_mmio_map(busdev, VIRTIO_MMIO_IOMAP, addr); +} + +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", 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 *mmio_bus = qdev_get_parent_bus(&busdev->qdev); + BusState *transport_bus; + VirtIOTransportBusState *virtio_transport_bus; + DeviceState *dev; + char *buf; + int len; + uint32_t i = 0; + + /* Count siblings */ + QTAILQ_FOREACH(dev, &mmio_bus->children, sibling) { + i++; + } + + len = strlen(VIRTIO_MMIO) + 16; + buf = g_malloc(len); + snprintf(buf, len, "%s.%d", VIRTIO_MMIO, i - 1); + transport_bus = qbus_create(&virtio_transport_bus_info, &busdev->qdev, buf); + + /* Populate mmio init transport function */ + virtio_transport_bus = + DO_UPCAST(VirtIOTransportBusState, bus, transport_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-bridge", 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, +}; + +/******************** VirtIOMMIO Bus Bridge Device **********************/ +/* Only required to have the virtio-mmio bus as child in the system bus. + * Borrowed from s390. */ + +static int virtio_mmio_bridge_init(SysBusDevice *dev) +{ + /* nothing */ + return 0; +} + +static void virtio_mmio_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + + k->init = virtio_mmio_bridge_init; + dc->no_user = 1; +} + +static TypeInfo virtio_mmio_bridge_info = { + .name = VIRTIO_MMIO_BRIDGE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SysBusDevice), + .class_init = virtio_mmio_bridge_class_init, +}; + +static void virtio_mmio_register_types(void) +{ + type_register_static(&virtio_mmio_bridge_info); + type_register_static(&virtio_mmio_transport_info); +} + +type_init(virtio_mmio_register_types) diff --git a/hw/virtio-mmio.h b/hw/virtio-mmio.h new file mode 100644 index 0000000..9a08371 --- /dev/null +++ b/hw/virtio-mmio.h @@ -0,0 +1,31 @@ +/* + * Virtio MMIO 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_MMIO_H_ +#define VIRTIO_MMIO_H_ + +#include "sysbus.h" + +BusState *virtio_mmio_bus_init(void); +void virtio_mmio_create_transport(BusState *bus, target_phys_addr_t addr, + qemu_irq irq); + +#endif /* VIRTIO_MMIO_H_ */