Message ID | 1265031265-14717-5-git-send-email-ian.molton@collabora.co.uk |
---|---|
State | New |
Headers | show |
On 02/01/2010 07:34 AM, Ian Molton wrote: > 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<ian.molton@collabora.co.uk> > --- > 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 5c0ef1f..21d28f4 100644 > --- a/Makefile.target > +++ b/Makefile.target > @@ -172,7 +172,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-pci.o virtio-serial-bus.o > +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-pci.o virtio-serial-bus.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 8b511d2..77cb543 100644 > --- a/hw/pci.h > +++ b/hw/pci.h > @@ -69,6 +69,7 @@ > #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 709d13e..f057388 100644 > --- a/hw/virtio-pci.c > +++ b/hw/virtio-pci.c > @@ -22,6 +22,7 @@ > #include "sysemu.h" > #include "msix.h" > #include "net.h" > +#include "rng.h" > #include "loader.h" > > /* from Linux's linux/virtio_pci.h */ > @@ -94,6 +95,7 @@ typedef struct { > uint32_t nvectors; > DriveInfo *dinfo; > NICConf nic; > + RNGConf rng; > uint32_t host_features; > /* Max. number of ports we can have for a the virtio-serial device */ > uint32_t max_virtserial_ports; > @@ -550,6 +552,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, > Gerd (or the right person at Red Hat) needs to Ack the assignment of this PCI device id. > + PCI_CLASS_OTHERS, > + 0x00); > + > + return 0; > +} > + > static PCIDeviceInfo virtio_info[] = { > { > .qdev.name = "virtio-blk-pci", > @@ -603,6 +620,16 @@ static PCIDeviceInfo virtio_info[] = { > }, > .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), > I don't see any reason to use a define here. > + 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..d8cbb74 > --- /dev/null > +++ b/hw/virtio-rng.c > @@ -0,0 +1,202 @@ > +/* > + * Virtio RNG Device > + * > + * Copyright Collabora 2009 > + * > + * Authors: > + * Ian Molton<ian.molton@collabora.co.uk> > + * > + * 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<sys/time.h> > + > +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) { > Coding style is off here (newline between ) and { ). > + 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); > Can't call gettimeofday directly. You have to use qemu_gettimeofday(). But it would be better to not rely on gettimeofday and instead make use of the rt_clock. > +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; > +} > > This doesn't look correct to me. There is absolutely no state maintained by the virtio-rng backend? I find that hard to believe. Regards, Anthony Liguori
Hi, >> + PCI_VENDOR_ID_REDHAT_QUMRANET, >> + PCI_DEVICE_ID_VIRTIO_RNG, > > Gerd (or the right person at Red Hat) needs to Ack the assignment of > this PCI device id. Using the first unused one should be ok, I'll double check that though. pci-ids.txt must be updated too. cheers, Gerd
On 02/01/2010 10:03 AM, Gerd Hoffmann wrote: > Hi, > >>> + PCI_VENDOR_ID_REDHAT_QUMRANET, >>> + PCI_DEVICE_ID_VIRTIO_RNG, >> >> Gerd (or the right person at Red Hat) needs to Ack the assignment of >> this PCI device id. > > Using the first unused one should be ok, I'll double check that though. > pci-ids.txt must be updated too. Thanks. Regards, Anthony Liguori > cheers, > Gerd >
Gerd Hoffmann wrote: > Hi, > >>> + PCI_VENDOR_ID_REDHAT_QUMRANET, >>> + PCI_DEVICE_ID_VIRTIO_RNG, >> >> Gerd (or the right person at Red Hat) needs to Ack the assignment of >> this PCI device id. > > Using the first unused one should be ok, I'll double check that though. > pci-ids.txt must be updated too. Cheers Gerd :) -Ian
Anthony Liguori wrote: >> + DEFINE_RNG_PROPERTIES(VirtIOPCIProxy, rng), >> > > I don't see any reason to use a define here. Consistency with the other virtio code (at the time I wrote it) > Coding style is off here (newline between ) and { ). Fixed. > Can't call gettimeofday directly. You have to use qemu_gettimeofday(). > But it would be better to not rely on gettimeofday and instead make use > of the rt_clock. Hm, this I fixed before, I'll make sure its right in the next patch. Must have got an old revision mixed up there. >> +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; >> +} >> >> > > This doesn't look correct to me. There is absolutely no state > maintained by the virtio-rng backend? I find that hard to believe. What state needs maintaining? when it runs out of entropy, it simply reconnects. Unless I misunderstood what those functions are for... -Ian
Ian Molton wrote: >>> +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; >>> +} >>> >>> >> This doesn't look correct to me. There is absolutely no state >> maintained by the virtio-rng backend? I find that hard to believe. > > What state needs maintaining? when it runs out of entropy, it simply > reconnects. Unless I misunderstood what those functions are for... ping?
diff --git a/Makefile.target b/Makefile.target index 5c0ef1f..21d28f4 100644 --- a/Makefile.target +++ b/Makefile.target @@ -172,7 +172,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-pci.o virtio-serial-bus.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-pci.o virtio-serial-bus.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 8b511d2..77cb543 100644 --- a/hw/pci.h +++ b/hw/pci.h @@ -69,6 +69,7 @@ #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 709d13e..f057388 100644 --- a/hw/virtio-pci.c +++ b/hw/virtio-pci.c @@ -22,6 +22,7 @@ #include "sysemu.h" #include "msix.h" #include "net.h" +#include "rng.h" #include "loader.h" /* from Linux's linux/virtio_pci.h */ @@ -94,6 +95,7 @@ typedef struct { uint32_t nvectors; DriveInfo *dinfo; NICConf nic; + RNGConf rng; uint32_t host_features; /* Max. number of ports we can have for a the virtio-serial device */ uint32_t max_virtserial_ports; @@ -550,6 +552,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", @@ -603,6 +620,16 @@ static PCIDeviceInfo virtio_info[] = { }, .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..d8cbb74 --- /dev/null +++ b/hw/virtio-rng.c @@ -0,0 +1,202 @@ +/* + * Virtio RNG Device + * + * Copyright Collabora 2009 + * + * Authors: + * Ian Molton <ian.molton@collabora.co.uk> + * + * 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 <sys/time.h> + +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, uint32_t features) +{ + 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 <ian.molton@collabora.co.uk> + * + * 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 62e882b..04a6b39 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_serial_init(DeviceState *dev, uint32_t max_nr_ports); 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
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 <ian.molton@collabora.co.uk> --- 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