From patchwork Thu Dec 17 16:44:55 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Molton X-Patchwork-Id: 41331 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 2997DB6EFE for ; Fri, 18 Dec 2009 03:51:10 +1100 (EST) Received: from localhost ([127.0.0.1]:39494 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NLJZC-0000gq-UT for incoming@patchwork.ozlabs.org; Thu, 17 Dec 2009 11:51:06 -0500 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1NLJVD-000748-BL for qemu-devel@nongnu.org; Thu, 17 Dec 2009 11:46:59 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1NLJVB-00072W-Ty for qemu-devel@nongnu.org; Thu, 17 Dec 2009 11:46:58 -0500 Received: from [199.232.76.173] (port=44953 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NLJVB-00071y-1V for qemu-devel@nongnu.org; Thu, 17 Dec 2009 11:46:57 -0500 Received: from bhuna.collabora.co.uk ([93.93.128.226]:33986) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1NLJV8-0000Gd-EH for qemu-devel@nongnu.org; Thu, 17 Dec 2009 11:46:54 -0500 Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: ian) with ESMTPSA id D82FC6017F0 From: Ian Molton To: qemu-devel@nongnu.org Date: Thu, 17 Dec 2009 16:44:55 +0000 Message-Id: <1261068295-25831-5-git-send-email-ian.molton@collabora.co.uk> X-Mailer: git-send-email 1.6.5.4 In-Reply-To: <1261068295-25831-4-git-send-email-ian.molton@collabora.co.uk> References: <1261068295-25831-1-git-send-email-ian.molton@collabora.co.uk> <1261068295-25831-2-git-send-email-ian.molton@collabora.co.uk> <1261068295-25831-3-git-send-email-ian.molton@collabora.co.uk> <1261068295-25831-4-git-send-email-ian.molton@collabora.co.uk> X-detected-operating-system: by monty-python.gnu.org: GNU/Linux 2.6 (newer, 2) Cc: Ian Molton Subject: [Qemu-devel] [PATCH 4/4] virtio: Add virtio-rng driver X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch adds support for virtio-rng. Data is read from a chardev and can be either raw entropy or received via the EGD protocol. Signed-off-by: Ian Molton --- Makefile.target | 2 +- hw/pci.h | 1 + hw/virtio-pci.c | 27 +++++++ hw/virtio-rng.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/virtio-rng.h | 19 +++++ hw/virtio.h | 2 + rng.h | 18 +++++ 7 files changed, 270 insertions(+), 1 deletions(-) create mode 100644 hw/virtio-rng.c create mode 100644 hw/virtio-rng.h create mode 100644 rng.h diff --git a/Makefile.target b/Makefile.target index 7c1f30c..89f5b42 100644 --- a/Makefile.target +++ b/Makefile.target @@ -156,7 +156,7 @@ ifdef CONFIG_SOFTMMU obj-y = vl.o async.o monitor.o pci.o pci_host.o pcie_host.o machine.o gdbstub.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o virtio-rng.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o obj-$(CONFIG_ISA_MMIO) += isa_mmio.o LIBS+=-lz diff --git a/hw/pci.h b/hw/pci.h index d279e3f..98fc82c 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -70,6 +70,7 @@ extern target_phys_addr_t pci_mem_base; #define PCI_DEVICE_ID_VIRTIO_BLOCK 0x1001 #define PCI_DEVICE_ID_VIRTIO_BALLOON 0x1002 #define PCI_DEVICE_ID_VIRTIO_CONSOLE 0x1003 +#define PCI_DEVICE_ID_VIRTIO_RNG 0x1004 typedef uint64_t pcibus_t; #define FMT_PCIBUS PRIx64 diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c index 4500130..b4261e8 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -20,6 +20,7 @@ #include "sysemu.h" #include "msix.h" #include "net.h" +#include "rng.h" #include "loader.h" /* from Linux's linux/virtio_pci.h */ @@ -92,6 +93,7 @@ typedef struct { uint32_t nvectors; DriveInfo *dinfo; NICConf nic; + RNGConf rng; } VirtIOPCIProxy; /* virtio device */ @@ -551,6 +553,21 @@ static int virtio_balloon_init_pci(PCIDevice *pci_dev) return 0; } +static int virtio_rng_init_pci(PCIDevice *pci_dev) +{ + VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIODevice *vdev; + + vdev = virtio_rng_init(&pci_dev->qdev, &proxy->rng); + virtio_init_pci(proxy, vdev, + PCI_VENDOR_ID_REDHAT_QUMRANET, + PCI_DEVICE_ID_VIRTIO_RNG, + PCI_CLASS_OTHERS, + 0x00); + + return 0; +} + static PCIDeviceInfo virtio_info[] = { { .qdev.name = "virtio-blk-pci", @@ -592,6 +609,16 @@ static PCIDeviceInfo virtio_info[] = { .exit = virtio_exit_pci, .qdev.reset = virtio_pci_reset, },{ + .qdev.name = "virtio-rng-pci", + .qdev.size = sizeof(VirtIOPCIProxy), + .init = virtio_rng_init_pci, + .exit = virtio_exit_pci, + .qdev.props = (Property[]) { + DEFINE_RNG_PROPERTIES(VirtIOPCIProxy, rng), + DEFINE_PROP_END_OF_LIST(), + }, + .qdev.reset = virtio_pci_reset, + },{ /* end of list */ } }; diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c new file mode 100644 index 0000000..39d0833 --- /dev/null +++ b/hw/virtio-rng.c @@ -0,0 +1,202 @@ +/* + * Virtio RNG Device + * + * Copyright Collabora 2009 + * + * Authors: + * Ian Molton + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "hw.h" +#include "qemu-char.h" +#include "virtio.h" +#include "virtio-rng.h" +#include "rng.h" +#include + +typedef struct VirtIORng +{ + VirtIODevice vdev; + VirtQueue *vq; + CharDriverState *chr; + struct timeval last; + int rate; + int egd; + int entropy_remaining; + int pool; +} VirtIORng; + +/* Maximum size of the buffer the guest expects */ +#define BUFF_MAX 64 + +/* EGD protocol - we only use this command */ +#define EGD_READ_BLOCK 0x2 + +#define EGD_MAX_BLOCK_SIZE 255 +#define EGD_MAX_REQUESTS 3 +#define EGD_MAX_POOL_SIZE (EGD_MAX_BLOCK_SIZE * (EGD_MAX_REQUESTS-1)) + +static inline void req_entropy(VirtIORng *s) { + static const unsigned char entropy_rq[2] = { EGD_READ_BLOCK, + EGD_MAX_BLOCK_SIZE }; + if (s->egd) { + /* Let the socket buffer up the incoming data for us. Max block size + for EGD protocol is (stupidly) 255, so make sure we always have a + block pending for performance. We can have 3 outstanding buffers */ + if (s->pool <= EGD_MAX_POOL_SIZE) { + s->chr->chr_write(s->chr, entropy_rq, sizeof(entropy_rq)); + s->pool += EGD_MAX_BLOCK_SIZE; + } + } else { + s->pool = BUFF_MAX; + } +} + +static int vrng_can_read(void *opaque) +{ + VirtIORng *s = (VirtIORng *) opaque; + struct timeval now, d; + int max_entropy; + + if (!virtio_queue_ready(s->vq) || + !(s->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK) || + virtio_queue_empty(s->vq)) + return 0; + + req_entropy(s); + + if (s->rate) { + gettimeofday(&now, NULL); + timersub(&now, &s->last, &d); + if (d.tv_sec * 1000000 + d.tv_usec > 1000000) { + s->entropy_remaining = s->rate; + s->last = now; + } + max_entropy = MIN(s->pool, s->entropy_remaining); + } else { + max_entropy = s->pool; + } + + /* current implementations have a 64 byte buffer. + * We fall back to a one byte per read if there is not enough room. + */ + max_entropy = MIN(max_entropy, BUFF_MAX); + if (max_entropy) { + if (virtqueue_avail_bytes(s->vq, max_entropy, 0)) + return max_entropy; + if (virtqueue_avail_bytes(s->vq, 1, 0)) + return 1; + } + return 0; +} + +static void vrng_read(void *opaque, const uint8_t *buf, int size) +{ + VirtIORng *s = (VirtIORng *) opaque; + VirtQueueElement elem; + int offset = 0; + + /* The current kernel implementation has only one outstanding input + * buffer of 64 bytes. + */ + while (offset < size) { + int i = 0; + if (!virtqueue_pop(s->vq, &elem)) + break; + while (offset < size && i < elem.in_num) { + int len = MIN(elem.in_sg[i].iov_len, size - offset); + memcpy(elem.in_sg[i].iov_base, buf + offset, len); + offset += len; + i++; + } + virtqueue_push(s->vq, &elem, size); + } + + if (s->rate) + s->entropy_remaining -= size; + s->pool -= size; + + virtio_notify(&s->vdev, s->vq); +} + +static void vrng_event(void *opaque, int event) +{ + VirtIORng *s = opaque; + + /* + * If our connection has been interrupted we need to kick the entropy + * gathering process if we are using EGD. + */ + + if(s->egd && event == CHR_EVENT_RECONNECTED) + s->pool = 0; +} + + + +static void virtio_rng_handle(VirtIODevice *vdev, VirtQueue *vq) +{ + /* Nothing to do - we push data when its available */ +} + +static uint32_t virtio_rng_get_features(VirtIODevice *vdev) +{ + return 0; +} + +static void virtio_rng_save(QEMUFile *f, void *opaque) +{ + VirtIORng *s = opaque; + + virtio_save(&s->vdev, f); +} + +static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id) +{ + VirtIORng *s = opaque; + + if (version_id != 1) + return -EINVAL; + + virtio_load(&s->vdev, f); + return 0; +} + +VirtIODevice *virtio_rng_init(DeviceState *dev, RNGConf *rngdev) +{ + VirtIORng *s; + s = (VirtIORng *)virtio_common_init("virtio-rng", + VIRTIO_ID_RNG, + 0, sizeof(VirtIORng)); + + if (!s) + return NULL; + + s->vdev.get_features = virtio_rng_get_features; + + s->vq = virtio_add_queue(&s->vdev, 128, virtio_rng_handle); + s->chr = rngdev->chrdev; + s->rate = rngdev->rate; + gettimeofday(&s->last, NULL); + + if(rngdev->proto && !strncmp(rngdev->proto, "egd", 3)) + s->egd = 1; + +#ifdef DEBUG + printf("entropy being read from %s", rngdev->chrdev->label); + if(s->rate) + printf(" at %d bytes/sec max.", s->rate); + printf(" protocol: %s\n", s->egd?"egd":"raw"); +#endif + + qemu_chr_add_handlers(s->chr, vrng_can_read, vrng_read, vrng_event, s); + + register_savevm("virtio-rng", -1, 1, virtio_rng_save, virtio_rng_load, s); + + return &s->vdev; +} + diff --git a/hw/virtio-rng.h b/hw/virtio-rng.h new file mode 100644 index 0000000..bc4d2d8 --- /dev/null +++ b/hw/virtio-rng.h @@ -0,0 +1,19 @@ +/* + * Virtio RNG Support + * + * Copyright Collabora 2009 + * + * Authors: + * Ian Molton + * + * 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_RNG_H +#define _QEMU_VIRTIO_RNG_H + +/* The ID for virtio console */ +#define VIRTIO_ID_RNG 4 + +#endif diff --git a/hw/virtio.h b/hw/virtio.h index 35532a6..efdbb68 100644 --- a/hw/virtio.h +++ b/hw/virtio.h @@ -16,6 +16,7 @@ #include "hw.h" #include "net.h" +#include "rng.h" #include "qdev.h" #include "sysemu.h" @@ -173,6 +174,7 @@ VirtIODevice *virtio_blk_init(DeviceState *dev, DriveInfo *dinfo); VirtIODevice *virtio_net_init(DeviceState *dev, NICConf *conf); VirtIODevice *virtio_console_init(DeviceState *dev); VirtIODevice *virtio_balloon_init(DeviceState *dev); +VirtIODevice *virtio_rng_init(DeviceState *dev, RNGConf *rngdev); void virtio_net_exit(VirtIODevice *vdev); diff --git a/rng.h b/rng.h new file mode 100644 index 0000000..faf6880 --- /dev/null +++ b/rng.h @@ -0,0 +1,18 @@ +#ifndef QEMU_RNG_H +#define QEMU_RNG_H + +#include "qemu-option.h" + +/* qdev rng properties */ + +typedef struct RNGConf { + CharDriverState *chrdev; + uint64_t rate; + char *proto; +} RNGConf; + +#define DEFINE_RNG_PROPERTIES(_state, _conf) \ + DEFINE_PROP_CHR("chardev", _state, _conf.chrdev), \ + DEFINE_PROP_SIZE("rate", _state, _conf.rate, 0), \ + DEFINE_PROP_STRING("protocol", _state, _conf.proto) +#endif