From patchwork Sat Nov 21 12:44:48 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ian Molton X-Patchwork-Id: 38982 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 C8885B6EF4 for ; Sat, 21 Nov 2009 23:48:04 +1100 (EST) Received: from localhost ([127.0.0.1]:50773 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NBpNi-00012Z-1n for incoming@patchwork.ozlabs.org; Sat, 21 Nov 2009 07:48:02 -0500 Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1NBpLx-0000Wm-Eq for qemu-devel@nongnu.org; Sat, 21 Nov 2009 07:46:13 -0500 Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1NBpLw-0000WI-GJ for qemu-devel@nongnu.org; Sat, 21 Nov 2009 07:46:13 -0500 Received: from [199.232.76.173] (port=60126 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1NBpLw-0000WD-89 for qemu-devel@nongnu.org; Sat, 21 Nov 2009 07:46:12 -0500 Received: from bhuna.collabora.co.uk ([93.93.128.226]:46475) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1NBpLv-0006yE-Ej for qemu-devel@nongnu.org; Sat, 21 Nov 2009 07:46:11 -0500 Received: from localhost.localdomain (94-192-117-31.zone6.bethere.co.uk [94.192.117.31]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by bhuna.collabora.co.uk (Postfix) with ESMTPSA id 1697F601275; Sat, 21 Nov 2009 12:46:10 +0000 (GMT) From: Ian Molton To: qemu-devel@nongnu.org Date: Sat, 21 Nov 2009 12:44:48 +0000 Message-Id: <1258807488-6341-2-git-send-email-ian.molton@collabora.co.uk> X-Mailer: git-send-email 1.6.5 In-Reply-To: <1258807488-6341-1-git-send-email-ian.molton@collabora.co.uk> References: <1258807488-6341-1-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] 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 | 194 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/virtio-rng.h | 19 ++++++ hw/virtio.h | 2 + rng.h | 18 +++++ 7 files changed, 262 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 91aa4a2..b4e4417 100644 --- a/Makefile.target +++ b/Makefile.target @@ -159,7 +159,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 9a56d0d..045ff9a 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 aebcf9d..a985a34 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 */ @@ -539,6 +541,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", @@ -580,6 +597,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..c08e3b5 --- /dev/null +++ b/hw/virtio-rng.c @@ -0,0 +1,194 @@ +/* + * 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) +{ + /* we will ignore any event for the time being */ +} + + + +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 15ad910..18dfe4e 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" @@ -167,6 +168,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