diff mbox

[v2,1/1] virtio-rng: hardware random number generator device

Message ID e04e5a79dc6c5143c53aba66cf226c97c8300343.1337972540.git.amit.shah@redhat.com
State New
Headers show

Commit Message

Amit Shah May 25, 2012, 7:32 p.m. UTC
The Linux kernel already has a virtio-rng driver, this is the device
implementation.

When the guest asks for entropy from the virtio hwrng, it puts a buffer
in the vq.  We then put entropy into that buffer, and push it back to
the guest.

The chardev connected to this device is fed the data to be sent to the
guest.

Invocation is simple:

  $ qemu ... -device virtio-rng-pci,chardev=foo

In the guest, we see

  $ cat /sys/devices/virtual/misc/hw_random/rng_available
  virtio

  $ cat /sys/devices/virtual/misc/hw_random/rng_current
  virtio

  # cat /dev/hwrng

Simply feeding /dev/urandom from the host to the chardev is sufficient:

  $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
             -device virtio-rng,chardev=foo

  $ nc -U /tmp/foo < /dev/urandom

A QMP event is sent for interested apps to monitor activity and send the
appropriate number of bytes that get asked by the guest:

  {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
   "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}

Signed-off-by: Amit Shah <amit.shah@redhat.com>
---
 Makefile.objs        |    1 +
 hw/pci.h             |    1 +
 hw/s390-virtio-bus.c |   35 +++++++++
 hw/s390-virtio-bus.h |    2 +
 hw/virtio-pci.c      |   51 +++++++++++++
 hw/virtio-pci.h      |    2 +
 hw/virtio-rng.c      |  199 ++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/virtio-rng.h      |   24 ++++++
 hw/virtio.h          |    3 +
 monitor.c            |    3 +
 monitor.h            |    1 +
 11 files changed, 322 insertions(+), 0 deletions(-)
 create mode 100644 hw/virtio-rng.c
 create mode 100644 hw/virtio-rng.h

Comments

Anthony Liguori May 25, 2012, 8 p.m. UTC | #1
On 05/25/2012 02:32 PM, Amit Shah wrote:
> The Linux kernel already has a virtio-rng driver, this is the device
> implementation.
>
> When the guest asks for entropy from the virtio hwrng, it puts a buffer
> in the vq.  We then put entropy into that buffer, and push it back to
> the guest.
>
> The chardev connected to this device is fed the data to be sent to the
> guest.
>
> Invocation is simple:
>
>    $ qemu ... -device virtio-rng-pci,chardev=foo
>
> In the guest, we see
>
>    $ cat /sys/devices/virtual/misc/hw_random/rng_available
>    virtio
>
>    $ cat /sys/devices/virtual/misc/hw_random/rng_current
>    virtio
>
>    # cat /dev/hwrng
>
> Simply feeding /dev/urandom from the host to the chardev is sufficient:
>
>    $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
>               -device virtio-rng,chardev=foo
>
>    $ nc -U /tmp/foo<  /dev/urandom
>
> A QMP event is sent for interested apps to monitor activity and send the
> appropriate number of bytes that get asked by the guest:
>
>    {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
>     "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}

I don't understand the point of this event.  Can't a management app just create 
a socket and then it can see all the requests the guest makes?

Regards,

Anthony Liguori

>
> Signed-off-by: Amit Shah<amit.shah@redhat.com>
> ---
>   Makefile.objs        |    1 +
>   hw/pci.h             |    1 +
>   hw/s390-virtio-bus.c |   35 +++++++++
>   hw/s390-virtio-bus.h |    2 +
>   hw/virtio-pci.c      |   51 +++++++++++++
>   hw/virtio-pci.h      |    2 +
>   hw/virtio-rng.c      |  199 ++++++++++++++++++++++++++++++++++++++++++++++++++
>   hw/virtio-rng.h      |   24 ++++++
>   hw/virtio.h          |    3 +
>   monitor.c            |    3 +
>   monitor.h            |    1 +
>   11 files changed, 322 insertions(+), 0 deletions(-)
>   create mode 100644 hw/virtio-rng.c
>   create mode 100644 hw/virtio-rng.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 70c5c79..5850762 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -210,6 +210,7 @@ user-obj-y += $(qom-obj-twice-y)
>   hw-obj-y =
>   hw-obj-y += vl.o loader.o
>   hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
> +hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o
>   hw-obj-y += usb/libhw.o
>   hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
>   hw-obj-y += fw_cfg.o
> diff --git a/hw/pci.h b/hw/pci.h
> index 8d0aa49..0a22f91 100644
> --- a/hw/pci.h
> +++ b/hw/pci.h
> @@ -76,6 +76,7 @@
>   #define PCI_DEVICE_ID_VIRTIO_BALLOON     0x1002
>   #define PCI_DEVICE_ID_VIRTIO_CONSOLE     0x1003
>   #define PCI_DEVICE_ID_VIRTIO_SCSI        0x1004
> +#define PCI_DEVICE_ID_VIRTIO_RNG         0x1005
>
>   #define FMT_PCIBUS                      PRIx64
>
> diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
> index 1d38a8f..e75711e 100644
> --- a/hw/s390-virtio-bus.c
> +++ b/hw/s390-virtio-bus.c
> @@ -26,6 +26,7 @@
>   #include "loader.h"
>   #include "elf.h"
>   #include "hw/virtio.h"
> +#include "hw/virtio-rng.h"
>   #include "hw/virtio-serial.h"
>   #include "hw/virtio-net.h"
>   #include "hw/sysbus.h"
> @@ -204,6 +205,18 @@ static int s390_virtio_scsi_init(VirtIOS390Device *dev)
>       return s390_virtio_device_init(dev, vdev);
>   }
>
> +static int s390_virtio_rng_init(VirtIOS390Device *dev)
> +{
> +    VirtIODevice *vdev;
> +
> +    vdev = virtio_rng_init((DeviceState *)dev,&dev->rng);
> +    if (!vdev) {
> +        return -1;
> +    }
> +
> +    return s390_virtio_device_init(dev, vdev);
> +}
> +
>   static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
>   {
>       ram_addr_t token_off;
> @@ -445,6 +458,27 @@ static TypeInfo s390_virtio_serial = {
>       .class_init    = s390_virtio_serial_class_init,
>   };
>
> +static Property s390_virtio_rng_properties[] = {
> +    DEFINE_PROP_CHR("chardev", VirtIOS390Device, rng.chr),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void s390_virtio_rng_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
> +
> +    k->init = s390_virtio_rng_init;
> +    dc->props = s390_virtio_rng_properties;
> +}
> +
> +static TypeInfo s390_virtio_rng = {
> +    .name          = "virtio-rng-s390",
> +    .parent        = TYPE_VIRTIO_S390_DEVICE,
> +    .instance_size = sizeof(VirtIOS390Device),
> +    .class_init    = s390_virtio_rng_class_init,
> +};
> +
>   static int s390_virtio_busdev_init(DeviceState *dev)
>   {
>       VirtIOS390Device *_dev = (VirtIOS390Device *)dev;
> @@ -524,6 +558,7 @@ static void s390_virtio_register_types(void)
>       type_register_static(&s390_virtio_blk);
>       type_register_static(&s390_virtio_net);
>       type_register_static(&s390_virtio_scsi);
> +    type_register_static(&s390_virtio_rng);
>       type_register_static(&s390_virtio_bridge_info);
>   }
>
> diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h
> index 4b99d02..5fc53b3 100644
> --- a/hw/s390-virtio-bus.h
> +++ b/hw/s390-virtio-bus.h
> @@ -19,6 +19,7 @@
>
>   #include "virtio-blk.h"
>   #include "virtio-net.h"
> +#include "virtio-rng.h"
>   #include "virtio-serial.h"
>   #include "virtio-scsi.h"
>
> @@ -71,6 +72,7 @@ struct VirtIOS390Device {
>       virtio_serial_conf serial;
>       virtio_net_conf net;
>       VirtIOSCSIConf scsi;
> +    VirtIORNGConf rng;
>   };
>
>   typedef struct VirtIOS390Bus {
> diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
> index 79b86f1..bd5851e 100644
> --- a/hw/virtio-pci.c
> +++ b/hw/virtio-pci.c
> @@ -810,6 +810,28 @@ static int virtio_balloon_exit_pci(PCIDevice *pci_dev)
>       return virtio_exit_pci(pci_dev);
>   }
>
> +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);
> +    if (!vdev) {
> +        return -1;
> +    }
> +    virtio_init_pci(proxy, vdev);
> +    return 0;
> +}
> +
> +static int virtio_rng_exit_pci(PCIDevice *pci_dev)
> +{
> +    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
> +
> +    virtio_pci_stop_ioeventfd(proxy);
> +    virtio_rng_exit(proxy->vdev);
> +    return virtio_exit_pci(pci_dev);
> +}
> +
>   static Property virtio_blk_properties[] = {
>       DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
>       DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, blk.conf),
> @@ -938,6 +960,34 @@ static TypeInfo virtio_balloon_info = {
>       .class_init    = virtio_balloon_class_init,
>   };
>
> +static Property virtio_rng_properties[] = {
> +    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
> +    DEFINE_PROP_CHR("chardev", VirtIOPCIProxy, rng.chr),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void virtio_rng_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
> +
> +    k->init = virtio_rng_init_pci;
> +    k->exit = virtio_rng_exit_pci;
> +    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
> +    k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
> +    k->revision = VIRTIO_PCI_ABI_VERSION;
> +    k->class_id = PCI_CLASS_OTHERS;
> +    dc->reset = virtio_pci_reset;
> +    dc->props = virtio_rng_properties;
> +}
> +
> +static TypeInfo virtio_rng_info = {
> +    .name          = "virtio-rng-pci",
> +    .parent        = TYPE_PCI_DEVICE,
> +    .instance_size = sizeof(VirtIOPCIProxy),
> +    .class_init    = virtio_rng_class_init,
> +};
> +
>   static int virtio_scsi_init_pci(PCIDevice *pci_dev)
>   {
>       VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
> @@ -999,6 +1049,7 @@ static void virtio_pci_register_types(void)
>       type_register_static(&virtio_serial_info);
>       type_register_static(&virtio_balloon_info);
>       type_register_static(&virtio_scsi_info);
> +    type_register_static(&virtio_rng_info);
>   }
>
>   type_init(virtio_pci_register_types)
> diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h
> index 889e59e..cc3f828 100644
> --- a/hw/virtio-pci.h
> +++ b/hw/virtio-pci.h
> @@ -17,6 +17,7 @@
>
>   #include "virtio-blk.h"
>   #include "virtio-net.h"
> +#include "virtio-rng.h"
>   #include "virtio-serial.h"
>   #include "virtio-scsi.h"
>
> @@ -42,6 +43,7 @@ typedef struct {
>       virtio_serial_conf serial;
>       virtio_net_conf net;
>       VirtIOSCSIConf scsi;
> +    VirtIORNGConf rng;
>       bool ioeventfd_disabled;
>       bool ioeventfd_started;
>   } VirtIOPCIProxy;
> diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c
> new file mode 100644
> index 0000000..f4f9078
> --- /dev/null
> +++ b/hw/virtio-rng.c
> @@ -0,0 +1,199 @@
> +/*
> + * A virtio device implementing a hardware random number generator.
> + *
> + * Copyright 2012 Red Hat, Inc.
> + * Copyright 2012 Amit Shah<amit.shah@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or
> + * (at your option) any later version.  See the COPYING file in the
> + * top-level directory.
> + */
> +
> +#include "iov.h"
> +#include "qdev.h"
> +#include "qjson.h"
> +#include "virtio.h"
> +#include "virtio-rng.h"
> +
> +typedef struct VirtIORNG {
> +    VirtIODevice vdev;
> +
> +    DeviceState *qdev;
> +
> +    /* Only one vq - guest puts buffer(s) on it when it needs entropy */
> +    VirtQueue *vq;
> +    VirtQueueElement elem;
> +
> +    /* Config data for the device -- currently only chardev */
> +    VirtIORNGConf *conf;
> +
> +    /* Whether we've popped a vq element into 'elem' above */
> +    bool popped;
> +} VirtIORNG;
> +
> +static bool is_guest_ready(VirtIORNG *vrng)
> +{
> +    if (virtio_queue_ready(vrng->vq)
> +&&  (vrng->vdev.status&  VIRTIO_CONFIG_S_DRIVER_OK)) {
> +        return true;
> +    }
> +    return false;
> +}
> +
> +static void send_qevent_for_entropy(size_t size)
> +{
> +    QObject *data;
> +
> +    data = qobject_from_jsonf("{ 'bytes': %" PRId64 " }",
> +                              size);
> +    monitor_protocol_event(QEVENT_ENTROPY_NEEDED, data);
> +    qobject_decref(data);
> +}
> +
> +static size_t pop_an_elem(VirtIORNG *vrng)
> +{
> +    size_t size;
> +
> +    if (!vrng->popped&&  !virtqueue_pop(vrng->vq,&vrng->elem)) {
> +        return 0;
> +    }
> +    vrng->popped = true;
> +
> +    size = iov_size(vrng->elem.in_sg, vrng->elem.in_num);
> +    return size;
> +}
> +
> +static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
> +{
> +    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
> +    size_t size;
> +
> +    size = pop_an_elem(vrng);
> +    if (size) {
> +        send_qevent_for_entropy(size);
> +    }
> +}
> +
> +static int chr_can_read(void *opaque)
> +{
> +    VirtIORNG *vrng = opaque;
> +
> +    if (!is_guest_ready(vrng)) {
> +        return 0;
> +    }
> +    return pop_an_elem(vrng);
> +}
> +
> +/* Send data from a char device over to the guest */
> +static void chr_read(void *opaque, const uint8_t *buf, int size)
> +{
> +    VirtIORNG *vrng = opaque;
> +    size_t len;
> +    int offset;
> +
> +    if (!is_guest_ready(vrng)) {
> +        return;
> +    }
> +
> +    offset = 0;
> +    while (offset<  size) {
> +        if (!pop_an_elem(vrng)) {
> +            break;
> +        }
> +        len = iov_from_buf(vrng->elem.in_sg, vrng->elem.in_num,
> +                           buf + offset, 0, size - offset);
> +        offset += len;
> +
> +        virtqueue_push(vrng->vq,&vrng->elem, len);
> +        vrng->popped = false;
> +    }
> +    virtio_notify(&vrng->vdev, vrng->vq);
> +
> +    /*
> +     * Lastly, if we had multiple elems queued by the guest, and we
> +     * didn't have enough data to fill them all, indicate we want more
> +     * data.  We can't stick this into chr_can_read(), as it'll just
> +     * end up spamming the management app.
> +     */
> +    len = pop_an_elem(vrng);
> +    if (len) {
> +        send_qevent_for_entropy(len);
> +    }
> +}
> +
> +static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
> +{
> +    return f;
> +}
> +
> +static void virtio_rng_save(QEMUFile *f, void *opaque)
> +{
> +    VirtIORNG *vrng = opaque;
> +
> +    virtio_save(&vrng->vdev, f);
> +
> +    qemu_put_byte(f, vrng->popped);
> +    if (vrng->popped) {
> +        qemu_put_buffer(f, (unsigned char *)&vrng->elem,
> +                        sizeof(vrng->elem));
> +    }
> +}
> +
> +static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
> +{
> +    VirtIORNG *vrng = opaque;
> +
> +    if (version_id != 1) {
> +        return -EINVAL;
> +    }
> +    virtio_load(&vrng->vdev, f);
> +
> +    vrng->popped = qemu_get_byte(f);
> +    if (vrng->popped) {
> +        qemu_get_buffer(f, (unsigned char *)&vrng->elem,
> +                        sizeof(vrng->elem));
> +        virtqueue_map_sg(vrng->elem.in_sg, vrng->elem.in_addr,
> +                         vrng->elem.in_num, 1);
> +        virtqueue_map_sg(vrng->elem.out_sg, vrng->elem.out_addr,
> +                         vrng->elem.out_num, 0);
> +    }
> +    return 0;
> +}
> +
> +VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
> +{
> +    VirtIORNG *vrng;
> +    VirtIODevice *vdev;
> +
> +    if (!conf->chr) {
> +        error_report("chardev property expected");
> +        return NULL;
> +    }
> +
> +    vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
> +                              sizeof(VirtIORNG));
> +
> +    vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
> +
> +    vrng->vq = virtio_add_queue(vdev, 8, handle_input);
> +    vrng->vdev.get_features = get_features;
> +
> +    vrng->qdev = dev;
> +    vrng->conf = conf;
> +    vrng->popped = false;
> +    register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
> +                    virtio_rng_load, vrng);
> +
> +    qemu_chr_add_handlers(conf->chr, chr_can_read, chr_read, NULL, vrng);
> +
> +    return vdev;
> +}
> +
> +void virtio_rng_exit(VirtIODevice *vdev)
> +{
> +    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
> +
> +    qemu_chr_add_handlers(vrng->conf->chr, NULL, NULL, NULL, NULL);
> +    unregister_savevm(vrng->qdev, "virtio-rng", vrng);
> +    virtio_cleanup(vdev);
> +}
> diff --git a/hw/virtio-rng.h b/hw/virtio-rng.h
> new file mode 100644
> index 0000000..b132acd
> --- /dev/null
> +++ b/hw/virtio-rng.h
> @@ -0,0 +1,24 @@
> +/*
> + * Virtio RNG Support
> + *
> + * Copyright Red Hat, Inc. 2012
> + * Copyright Amit Shah<amit.shah@redhat.com>
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or
> + * (at your option) any later version.  See the COPYING file in the
> + * top-level directory.
> + */
> +
> +#ifndef _QEMU_VIRTIO_RNG_H
> +#define _QEMU_VIRTIO_RNG_H
> +
> +#include "qemu-char.h"
> +
> +/* The Virtio ID for the virtio rng device */
> +#define VIRTIO_ID_RNG    4
> +
> +struct VirtIORNGConf {
> +    CharDriverState *chr;
> +};
> +
> +#endif
> diff --git a/hw/virtio.h b/hw/virtio.h
> index 85aabe5..b4b5bf6 100644
> --- a/hw/virtio.h
> +++ b/hw/virtio.h
> @@ -201,6 +201,8 @@ VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
>   VirtIODevice *virtio_balloon_init(DeviceState *dev);
>   typedef struct VirtIOSCSIConf VirtIOSCSIConf;
>   VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
> +typedef struct VirtIORNGConf VirtIORNGConf;
> +VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf);
>   #ifdef CONFIG_LINUX
>   VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
>   #endif
> @@ -211,6 +213,7 @@ void virtio_blk_exit(VirtIODevice *vdev);
>   void virtio_serial_exit(VirtIODevice *vdev);
>   void virtio_balloon_exit(VirtIODevice *vdev);
>   void virtio_scsi_exit(VirtIODevice *vdev);
> +void virtio_rng_exit(VirtIODevice *vdev);
>
>   #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
>   	DEFINE_PROP_BIT("indirect_desc", _state, _field, \
> diff --git a/monitor.c b/monitor.c
> index 12a6fe2..177b73e 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -493,6 +493,9 @@ void monitor_protocol_event(MonitorEvent event, QObject *data)
>           case QEVENT_WAKEUP:
>               event_name = "WAKEUP";
>               break;
> +        case QEVENT_ENTROPY_NEEDED:
> +            event_name = "ENTROPY_NEEDED";
> +            break;
>           default:
>               abort();
>               break;
> diff --git a/monitor.h b/monitor.h
> index 0d49800..731d599 100644
> --- a/monitor.h
> +++ b/monitor.h
> @@ -41,6 +41,7 @@ typedef enum MonitorEvent {
>       QEVENT_DEVICE_TRAY_MOVED,
>       QEVENT_SUSPEND,
>       QEVENT_WAKEUP,
> +    QEVENT_ENTROPY_NEEDED,
>       QEVENT_MAX,
>   } MonitorEvent;
>
Amit Shah May 25, 2012, 8:20 p.m. UTC | #2
On (Fri) 25 May 2012 [15:00:53], Anthony Liguori wrote:
> On 05/25/2012 02:32 PM, Amit Shah wrote:
> >The Linux kernel already has a virtio-rng driver, this is the device
> >implementation.
> >
> >When the guest asks for entropy from the virtio hwrng, it puts a buffer
> >in the vq.  We then put entropy into that buffer, and push it back to
> >the guest.
> >
> >The chardev connected to this device is fed the data to be sent to the
> >guest.
> >
> >Invocation is simple:
> >
> >   $ qemu ... -device virtio-rng-pci,chardev=foo
> >
> >In the guest, we see
> >
> >   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >   virtio
> >
> >   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >   virtio
> >
> >   # cat /dev/hwrng
> >
> >Simply feeding /dev/urandom from the host to the chardev is sufficient:
> >
> >   $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
> >              -device virtio-rng,chardev=foo
> >
> >   $ nc -U /tmp/foo<  /dev/urandom
> >
> >A QMP event is sent for interested apps to monitor activity and send the
> >appropriate number of bytes that get asked by the guest:
> >
> >   {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
> >    "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}
> 
> I don't understand the point of this event.  Can't a management app
> just create a socket and then it can see all the requests the guest
> makes?

How?  With the chardev, it can only keep feeding data, and that data
will be consumed when chr_can_read() returns > 0.  And even then the
mgmt app has no idea how much data was asked for, and how much was
consumed.

		Amit
Daniel P. Berrangé May 28, 2012, 8:33 a.m. UTC | #3
On Sat, May 26, 2012 at 01:02:49AM +0530, Amit Shah wrote:
> The Linux kernel already has a virtio-rng driver, this is the device
> implementation.
> 
> When the guest asks for entropy from the virtio hwrng, it puts a buffer
> in the vq.  We then put entropy into that buffer, and push it back to
> the guest.
> 
> The chardev connected to this device is fed the data to be sent to the
> guest.
> 
> Invocation is simple:
> 
>   $ qemu ... -device virtio-rng-pci,chardev=foo
> 
> In the guest, we see
> 
>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
>   virtio
> 
>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
>   virtio
> 
>   # cat /dev/hwrng
> 
> Simply feeding /dev/urandom from the host to the chardev is sufficient:
> 
>   $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
>              -device virtio-rng,chardev=foo
> 
>   $ nc -U /tmp/foo < /dev/urandom

ACK to this ARGV design from a libvirt POV.

> A QMP event is sent for interested apps to monitor activity and send the
> appropriate number of bytes that get asked by the guest:
> 
>   {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
>    "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}

IIUC, there are three ways mgmt apps can use the RNG with the
chardev

 - Wire it up to a source that just blindly provides all the
   entropy QEMU desires (as you /dev/urandom example above)

 - Feed in a fixed amount of entropy every minute, regardless
   of how much QEMU desires

 - Feed in entropy on demand, in response to the ENTROPY_NEEDED
   event notification (possibly throttling)

These options sounds like they should cover all reasonable needs,
so gets my vote. Probably want to include the ENTROPY_NEEDED
event in my patch which adds rate limiting to guest initiated
events.

Daniel
Amit Shah May 28, 2012, 9:17 a.m. UTC | #4
On (Mon) 28 May 2012 [09:33:57], Daniel P. Berrange wrote:
> On Sat, May 26, 2012 at 01:02:49AM +0530, Amit Shah wrote:
> > The Linux kernel already has a virtio-rng driver, this is the device
> > implementation.
> > 
> > When the guest asks for entropy from the virtio hwrng, it puts a buffer
> > in the vq.  We then put entropy into that buffer, and push it back to
> > the guest.
> > 
> > The chardev connected to this device is fed the data to be sent to the
> > guest.
> > 
> > Invocation is simple:
> > 
> >   $ qemu ... -device virtio-rng-pci,chardev=foo
> > 
> > In the guest, we see
> > 
> >   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >   virtio
> > 
> >   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >   virtio
> > 
> >   # cat /dev/hwrng
> > 
> > Simply feeding /dev/urandom from the host to the chardev is sufficient:
> > 
> >   $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
> >              -device virtio-rng,chardev=foo
> > 
> >   $ nc -U /tmp/foo < /dev/urandom
> 
> ACK to this ARGV design from a libvirt POV.
> 
> > A QMP event is sent for interested apps to monitor activity and send the
> > appropriate number of bytes that get asked by the guest:
> > 
> >   {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
> >    "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}
> 
> IIUC, there are three ways mgmt apps can use the RNG with the
> chardev
> 
>  - Wire it up to a source that just blindly provides all the
>    entropy QEMU desires (as you /dev/urandom example above)
> 
>  - Feed in a fixed amount of entropy every minute, regardless
>    of how much QEMU desires

This option currently won't do anything -- i.e. till the guest sends
across a buffer to be filled in, nothing will go to the guest, and the
data will just be buffered in the chardev layer till such a buffer
comes along.  It can be debatable on feeding entropy by pushing every
particular timeout, or just providing the freshest on-demand.
Advantage could be we have more random bits, disadvantage is this
could be throttled as the host went out of enough entropy.

>  - Feed in entropy on demand, in response to the ENTROPY_NEEDED
>    event notification (possibly throttling)
> 
> These options sounds like they should cover all reasonable needs,
> so gets my vote.

Great!

> Probably want to include the ENTROPY_NEEDED
> event in my patch which adds rate limiting to guest initiated
> events.

Yes, just depends in which order the patches go in.

Thanks,
		Amit
Anthony Liguori June 4, 2012, 11:04 a.m. UTC | #5
On 05/26/2012 04:20 AM, Amit Shah wrote:
> On (Fri) 25 May 2012 [15:00:53], Anthony Liguori wrote:
>> On 05/25/2012 02:32 PM, Amit Shah wrote:
>>> The Linux kernel already has a virtio-rng driver, this is the device
>>> implementation.
>>>
>>> When the guest asks for entropy from the virtio hwrng, it puts a buffer
>>> in the vq.  We then put entropy into that buffer, and push it back to
>>> the guest.
>>>
>>> The chardev connected to this device is fed the data to be sent to the
>>> guest.
>>>
>>> Invocation is simple:
>>>
>>>    $ qemu ... -device virtio-rng-pci,chardev=foo
>>>
>>> In the guest, we see
>>>
>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_available
>>>    virtio
>>>
>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_current
>>>    virtio
>>>
>>>    # cat /dev/hwrng
>>>
>>> Simply feeding /dev/urandom from the host to the chardev is sufficient:
>>>
>>>    $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
>>>               -device virtio-rng,chardev=foo
>>>
>>>    $ nc -U /tmp/foo<   /dev/urandom
>>>
>>> A QMP event is sent for interested apps to monitor activity and send the
>>> appropriate number of bytes that get asked by the guest:
>>>
>>>    {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
>>>     "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}
>>
>> I don't understand the point of this event.  Can't a management app
>> just create a socket and then it can see all the requests the guest
>> makes?
>
> How?  With the chardev, it can only keep feeding data, and that data
> will be consumed when chr_can_read() returns>  0.  And even then the
> mgmt app has no idea how much data was asked for, and how much was
> consumed.

Okay, then the right approach is to use a message protocol where QEMU asks for a 
certain amount of data and then the daemon sends it back.

I think this is pretty much why the egd protocol exists, no?  Why not just 
implement egd protocol support?

Once we introduce a protocol of any form (even raw), we have to support it 
forever so let's not do it carelessly.

Regards,

Anthony Liguori

>
> 		Amit
>
>
Amit Shah June 5, 2012, 9:41 a.m. UTC | #6
On (Mon) 04 Jun 2012 [19:04:41], Anthony Liguori wrote:
> On 05/26/2012 04:20 AM, Amit Shah wrote:
> >On (Fri) 25 May 2012 [15:00:53], Anthony Liguori wrote:
> >>On 05/25/2012 02:32 PM, Amit Shah wrote:
> >>>The Linux kernel already has a virtio-rng driver, this is the device
> >>>implementation.
> >>>
> >>>When the guest asks for entropy from the virtio hwrng, it puts a buffer
> >>>in the vq.  We then put entropy into that buffer, and push it back to
> >>>the guest.
> >>>
> >>>The chardev connected to this device is fed the data to be sent to the
> >>>guest.
> >>>
> >>>Invocation is simple:
> >>>
> >>>   $ qemu ... -device virtio-rng-pci,chardev=foo
> >>>
> >>>In the guest, we see
> >>>
> >>>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >>>   virtio
> >>>
> >>>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >>>   virtio
> >>>
> >>>   # cat /dev/hwrng
> >>>
> >>>Simply feeding /dev/urandom from the host to the chardev is sufficient:
> >>>
> >>>   $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
> >>>              -device virtio-rng,chardev=foo
> >>>
> >>>   $ nc -U /tmp/foo<   /dev/urandom
> >>>
> >>>A QMP event is sent for interested apps to monitor activity and send the
> >>>appropriate number of bytes that get asked by the guest:
> >>>
> >>>   {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
> >>>    "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}
> >>
> >>I don't understand the point of this event.  Can't a management app
> >>just create a socket and then it can see all the requests the guest
> >>makes?
> >
> >How?  With the chardev, it can only keep feeding data, and that data
> >will be consumed when chr_can_read() returns>  0.  And even then the
> >mgmt app has no idea how much data was asked for, and how much was
> >consumed.
> 
> Okay, then the right approach is to use a message protocol where
> QEMU asks for a certain amount of data and then the daemon sends it
> back.

Why is a message protocol necessary?  And a daemon isn't necessary,
too.

> I think this is pretty much why the egd protocol exists, no?  Why
> not just implement egd protocol support?

Perhaps I'm not getting what you're saying.

What we have here, via the event, is to tell the mgmt app that the
guest has need for entropy.  The guest also tells us exactly how many
bytes it needs.  We're just passing on that info to the mgmt app, if
it needs it.

In any case, any data pushed down the chardev will be consumed by the
device, and sent to the guest when the guest puts a buf in the vq.

> Once we introduce a protocol of any form (even raw), we have to
> support it forever so let's not do it carelessly.

There's no need for a protocol at all here.  Can you skim through the
code and point out where you think we're disagreeing?

		Amit
Anthony Liguori June 5, 2012, 9:54 a.m. UTC | #7
On 06/05/2012 05:41 PM, Amit Shah wrote:
> On (Mon) 04 Jun 2012 [19:04:41], Anthony Liguori wrote:
>> On 05/26/2012 04:20 AM, Amit Shah wrote:
>>> On (Fri) 25 May 2012 [15:00:53], Anthony Liguori wrote:
>>>> On 05/25/2012 02:32 PM, Amit Shah wrote:
>>>>> The Linux kernel already has a virtio-rng driver, this is the device
>>>>> implementation.
>>>>>
>>>>> When the guest asks for entropy from the virtio hwrng, it puts a buffer
>>>>> in the vq.  We then put entropy into that buffer, and push it back to
>>>>> the guest.
>>>>>
>>>>> The chardev connected to this device is fed the data to be sent to the
>>>>> guest.
>>>>>
>>>>> Invocation is simple:
>>>>>
>>>>>    $ qemu ... -device virtio-rng-pci,chardev=foo
>>>>>
>>>>> In the guest, we see
>>>>>
>>>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_available
>>>>>    virtio
>>>>>
>>>>>    $ cat /sys/devices/virtual/misc/hw_random/rng_current
>>>>>    virtio
>>>>>
>>>>>    # cat /dev/hwrng
>>>>>
>>>>> Simply feeding /dev/urandom from the host to the chardev is sufficient:
>>>>>
>>>>>    $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
>>>>>               -device virtio-rng,chardev=foo
>>>>>
>>>>>    $ nc -U /tmp/foo<    /dev/urandom
>>>>>
>>>>> A QMP event is sent for interested apps to monitor activity and send the
>>>>> appropriate number of bytes that get asked by the guest:
>>>>>
>>>>>    {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
>>>>>     "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}
>>>>
>>>> I don't understand the point of this event.  Can't a management app
>>>> just create a socket and then it can see all the requests the guest
>>>> makes?
>>>
>>> How?  With the chardev, it can only keep feeding data, and that data
>>> will be consumed when chr_can_read() returns>   0.  And even then the
>>> mgmt app has no idea how much data was asked for, and how much was
>>> consumed.
>>
>> Okay, then the right approach is to use a message protocol where
>> QEMU asks for a certain amount of data and then the daemon sends it
>> back.
>
> Why is a message protocol necessary?  And a daemon isn't necessary,
> too.
>
>> I think this is pretty much why the egd protocol exists, no?  Why
>> not just implement egd protocol support?
>
> Perhaps I'm not getting what you're saying.
>
> What we have here, via the event, is to tell the mgmt app that the
> guest has need for entropy.

*out-of-band*

Why not just make that all in-band?

I understand you're trying to accomodate a use-case of feeding /dev/random to 
QEMU.  I don't think this is a necessary use-case though because I don't think 
it's correct.  You need to use an entropy daemon of some sort to make sure 
entropy is being distributed in a fair fashion.

> The guest also tells us exactly how many
> bytes it needs.  We're just passing on that info to the mgmt app, if
> it needs it.
>
> In any case, any data pushed down the chardev will be consumed by the
> device, and sent to the guest when the guest puts a buf in the vq.
>
>> Once we introduce a protocol of any form (even raw), we have to
>> support it forever so let's not do it carelessly.
>
> There's no need for a protocol at all here.

The event *is* a protocol.  That's the point.

Here's what you're making a management app do:

1) Open a socket
2) Feed socket fd to QEMU
3) Listen for event on QMP for entropy request
4) Read entropy based on (3) from /dev/random
5) Send entropy to socket to QEMU

This is pretty silly when you can just do:

1) Open a socket
2) Listen for entropy request
3) Read entry based on (2) from /dev/random
4) Respond to (2) with (3)

And there's already a tool and protocol for exactly the above--egd.

There's simply no good reason to involve QMP here.

Regards,

Anthony Liguori

   Can you skim through the
> code and point out where you think we're disagreeing?
>
> 		Amit
Amit Shah June 5, 2012, 10:16 a.m. UTC | #8
On (Tue) 05 Jun 2012 [17:54:30], Anthony Liguori wrote:
> On 06/05/2012 05:41 PM, Amit Shah wrote:
> >On (Mon) 04 Jun 2012 [19:04:41], Anthony Liguori wrote:
> >>On 05/26/2012 04:20 AM, Amit Shah wrote:
> >>>On (Fri) 25 May 2012 [15:00:53], Anthony Liguori wrote:
> >>>>On 05/25/2012 02:32 PM, Amit Shah wrote:
> >>>>>The Linux kernel already has a virtio-rng driver, this is the device
> >>>>>implementation.
> >>>>>
> >>>>>When the guest asks for entropy from the virtio hwrng, it puts a buffer
> >>>>>in the vq.  We then put entropy into that buffer, and push it back to
> >>>>>the guest.
> >>>>>
> >>>>>The chardev connected to this device is fed the data to be sent to the
> >>>>>guest.
> >>>>>
> >>>>>Invocation is simple:
> >>>>>
> >>>>>   $ qemu ... -device virtio-rng-pci,chardev=foo
> >>>>>
> >>>>>In the guest, we see
> >>>>>
> >>>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> >>>>>   virtio
> >>>>>
> >>>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> >>>>>   virtio
> >>>>>
> >>>>>   # cat /dev/hwrng
> >>>>>
> >>>>>Simply feeding /dev/urandom from the host to the chardev is sufficient:
> >>>>>
> >>>>>   $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
> >>>>>              -device virtio-rng,chardev=foo
> >>>>>
> >>>>>   $ nc -U /tmp/foo<    /dev/urandom
> >>>>>
> >>>>>A QMP event is sent for interested apps to monitor activity and send the
> >>>>>appropriate number of bytes that get asked by the guest:
> >>>>>
> >>>>>   {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
> >>>>>    "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}
> >>>>
> >>>>I don't understand the point of this event.  Can't a management app
> >>>>just create a socket and then it can see all the requests the guest
> >>>>makes?
> >>>
> >>>How?  With the chardev, it can only keep feeding data, and that data
> >>>will be consumed when chr_can_read() returns>   0.  And even then the
> >>>mgmt app has no idea how much data was asked for, and how much was
> >>>consumed.
> >>
> >>Okay, then the right approach is to use a message protocol where
> >>QEMU asks for a certain amount of data and then the daemon sends it
> >>back.
> >
> >Why is a message protocol necessary?  And a daemon isn't necessary,
> >too.
> >
> >>I think this is pretty much why the egd protocol exists, no?  Why
> >>not just implement egd protocol support?
> >
> >Perhaps I'm not getting what you're saying.
> >
> >What we have here, via the event, is to tell the mgmt app that the
> >guest has need for entropy.
> 
> *out-of-band*
> 
> Why not just make that all in-band?
> 
> I understand you're trying to accomodate a use-case of feeding
> /dev/random to QEMU.  I don't think this is a necessary use-case
> though because I don't think it's correct.  You need to use an
> entropy daemon of some sort to make sure entropy is being
> distributed in a fair fashion.

In my view, qemu doesn't operate at a system-level where it can see
how system entropy is being used.

libvirt does operate at that level.

For casual development, I don't want to have egd on my system to feed
in entropy to the guest, and I don't need to know how to configure
egd and then provide its output to qemu.

libvirt can do that for me.  But for development, I'd just want to be
able to do something like

qemu -chardev socket,path=/tmp/foo,id=foo,server,nowait \
     -device virtio-rng,chardev=foo

and then just feed in some data to /tmp/foo.

> >The guest also tells us exactly how many
> >bytes it needs.  We're just passing on that info to the mgmt app, if
> >it needs it.
> >
> >In any case, any data pushed down the chardev will be consumed by the
> >device, and sent to the guest when the guest puts a buf in the vq.
> >
> >>Once we introduce a protocol of any form (even raw), we have to
> >>support it forever so let's not do it carelessly.
> >
> >There's no need for a protocol at all here.
> 
> The event *is* a protocol.  That's the point.

But there is no requirement to use the event.  From the cmdline above,
I can just do something like

nc -U /tmp/foo < /dev/urandom

and forget about the event.  The event is just a way to tell
interested apps what the guest did.  Nothing more.

> Here's what you're making a management app do:
> 
> 1) Open a socket
> 2) Feed socket fd to QEMU
> 3) Listen for event on QMP for entropy request

This step isn't necessary.

> 4) Read entropy based on (3) from /dev/random
> 5) Send entropy to socket to QEMU
> 
> This is pretty silly when you can just do:
> 
> 1) Open a socket
> 2) Listen for entropy request
> 3) Read entry based on (2) from /dev/random
> 4) Respond to (2) with (3)

Quoting danpb's mail from this thread:

> IIUC, there are three ways mgmt apps can use the RNG with the
> chardev
> 
>  - Wire it up to a source that just blindly provides all the
>    entropy QEMU desires (as you /dev/urandom example above)
> 
>  - Feed in a fixed amount of entropy every minute, regardless
>    of how much QEMU desires
> 
>  - Feed in entropy on demand, in response to the ENTROPY_NEEDED
>    event notification (possibly throttling)
> 
> These options sounds like they should cover all reasonable needs,
> so gets my vote. Probably want to include the ENTROPY_NEEDED
> event in my patch which adds rate limiting to guest initiated
> events.

		Amit
Daniel P. Berrangé June 11, 2012, 1:34 p.m. UTC | #9
On Tue, Jun 05, 2012 at 03:46:31PM +0530, Amit Shah wrote:
> On (Tue) 05 Jun 2012 [17:54:30], Anthony Liguori wrote:
> > On 06/05/2012 05:41 PM, Amit Shah wrote:
> > >On (Mon) 04 Jun 2012 [19:04:41], Anthony Liguori wrote:
> > >>On 05/26/2012 04:20 AM, Amit Shah wrote:
> > >>>On (Fri) 25 May 2012 [15:00:53], Anthony Liguori wrote:
> > >>>>On 05/25/2012 02:32 PM, Amit Shah wrote:
> > >>>>>The Linux kernel already has a virtio-rng driver, this is the device
> > >>>>>implementation.
> > >>>>>
> > >>>>>When the guest asks for entropy from the virtio hwrng, it puts a buffer
> > >>>>>in the vq.  We then put entropy into that buffer, and push it back to
> > >>>>>the guest.
> > >>>>>
> > >>>>>The chardev connected to this device is fed the data to be sent to the
> > >>>>>guest.
> > >>>>>
> > >>>>>Invocation is simple:
> > >>>>>
> > >>>>>   $ qemu ... -device virtio-rng-pci,chardev=foo
> > >>>>>
> > >>>>>In the guest, we see
> > >>>>>
> > >>>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_available
> > >>>>>   virtio
> > >>>>>
> > >>>>>   $ cat /sys/devices/virtual/misc/hw_random/rng_current
> > >>>>>   virtio
> > >>>>>
> > >>>>>   # cat /dev/hwrng
> > >>>>>
> > >>>>>Simply feeding /dev/urandom from the host to the chardev is sufficient:
> > >>>>>
> > >>>>>   $ qemu ... -chardev socket,path=/tmp/foo,server,nowait,id=foo \
> > >>>>>              -device virtio-rng,chardev=foo
> > >>>>>
> > >>>>>   $ nc -U /tmp/foo<    /dev/urandom
> > >>>>>
> > >>>>>A QMP event is sent for interested apps to monitor activity and send the
> > >>>>>appropriate number of bytes that get asked by the guest:
> > >>>>>
> > >>>>>   {"timestamp": {"seconds": 1337966878, "microseconds": 517009}, \
> > >>>>>    "event": "ENTROPY_NEEDED", "data": {"bytes": 64}}
> > >>>>
> > >>>>I don't understand the point of this event.  Can't a management app
> > >>>>just create a socket and then it can see all the requests the guest
> > >>>>makes?
> > >>>
> > >>>How?  With the chardev, it can only keep feeding data, and that data
> > >>>will be consumed when chr_can_read() returns>   0.  And even then the
> > >>>mgmt app has no idea how much data was asked for, and how much was
> > >>>consumed.
> > >>
> > >>Okay, then the right approach is to use a message protocol where
> > >>QEMU asks for a certain amount of data and then the daemon sends it
> > >>back.
> > >
> > >Why is a message protocol necessary?  And a daemon isn't necessary,
> > >too.
> > >
> > >>I think this is pretty much why the egd protocol exists, no?  Why
> > >>not just implement egd protocol support?
> > >
> > >Perhaps I'm not getting what you're saying.
> > >
> > >What we have here, via the event, is to tell the mgmt app that the
> > >guest has need for entropy.
> > 
> > *out-of-band*
> > 
> > Why not just make that all in-band?
> > 
> > I understand you're trying to accomodate a use-case of feeding
> > /dev/random to QEMU.  I don't think this is a necessary use-case
> > though because I don't think it's correct.  You need to use an
> > entropy daemon of some sort to make sure entropy is being
> > distributed in a fair fashion.
> 
> In my view, qemu doesn't operate at a system-level where it can see
> how system entropy is being used.
> 
> libvirt does operate at that level.
> 
> For casual development, I don't want to have egd on my system to feed
> in entropy to the guest, and I don't need to know how to configure
> egd and then provide its output to qemu.
> 
> libvirt can do that for me.  But for development, I'd just want to be
> able to do something like
> 
> qemu -chardev socket,path=/tmp/foo,id=foo,server,nowait \
>      -device virtio-rng,chardev=foo
> 
> and then just feed in some data to /tmp/foo.

I tend to agree, while the EGD wire protocol may be useful if you
are wanting to connect QEMU to EGD, for apps that are just directly
mnanaging QEMU it imposes a unnecessary burden on apps, to learn
a new protocol in addition to their existing knowledge of QMP.

In addition, I don't think implementing the EGD protocol is mutually
exclusive with raw QEMU chardevs. eg enablement of an EGD protocol on
the chardev could reasonably be done as an optional feature ontop of
the raw chardev, in the same way that chardevs can be made to optionally
run the telnet line discipline.

I'd like to see us have the raw chardev impl + QMP event as a first
step, and then add EGD protocol on top of that (if desired) as a
second step.

Regards,
Daniel
diff mbox

Patch

diff --git a/Makefile.objs b/Makefile.objs
index 70c5c79..5850762 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -210,6 +210,7 @@  user-obj-y += $(qom-obj-twice-y)
 hw-obj-y =
 hw-obj-y += vl.o loader.o
 hw-obj-$(CONFIG_VIRTIO) += virtio-console.o
+hw-obj-$(CONFIG_VIRTIO) += virtio-rng.o
 hw-obj-y += usb/libhw.o
 hw-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o
 hw-obj-y += fw_cfg.o
diff --git a/hw/pci.h b/hw/pci.h
index 8d0aa49..0a22f91 100644
--- a/hw/pci.h
+++ b/hw/pci.h
@@ -76,6 +76,7 @@ 
 #define PCI_DEVICE_ID_VIRTIO_BALLOON     0x1002
 #define PCI_DEVICE_ID_VIRTIO_CONSOLE     0x1003
 #define PCI_DEVICE_ID_VIRTIO_SCSI        0x1004
+#define PCI_DEVICE_ID_VIRTIO_RNG         0x1005
 
 #define FMT_PCIBUS                      PRIx64
 
diff --git a/hw/s390-virtio-bus.c b/hw/s390-virtio-bus.c
index 1d38a8f..e75711e 100644
--- a/hw/s390-virtio-bus.c
+++ b/hw/s390-virtio-bus.c
@@ -26,6 +26,7 @@ 
 #include "loader.h"
 #include "elf.h"
 #include "hw/virtio.h"
+#include "hw/virtio-rng.h"
 #include "hw/virtio-serial.h"
 #include "hw/virtio-net.h"
 #include "hw/sysbus.h"
@@ -204,6 +205,18 @@  static int s390_virtio_scsi_init(VirtIOS390Device *dev)
     return s390_virtio_device_init(dev, vdev);
 }
 
+static int s390_virtio_rng_init(VirtIOS390Device *dev)
+{
+    VirtIODevice *vdev;
+
+    vdev = virtio_rng_init((DeviceState *)dev, &dev->rng);
+    if (!vdev) {
+        return -1;
+    }
+
+    return s390_virtio_device_init(dev, vdev);
+}
+
 static uint64_t s390_virtio_device_vq_token(VirtIOS390Device *dev, int vq)
 {
     ram_addr_t token_off;
@@ -445,6 +458,27 @@  static TypeInfo s390_virtio_serial = {
     .class_init    = s390_virtio_serial_class_init,
 };
 
+static Property s390_virtio_rng_properties[] = {
+    DEFINE_PROP_CHR("chardev", VirtIOS390Device, rng.chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void s390_virtio_rng_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    VirtIOS390DeviceClass *k = VIRTIO_S390_DEVICE_CLASS(klass);
+
+    k->init = s390_virtio_rng_init;
+    dc->props = s390_virtio_rng_properties;
+}
+
+static TypeInfo s390_virtio_rng = {
+    .name          = "virtio-rng-s390",
+    .parent        = TYPE_VIRTIO_S390_DEVICE,
+    .instance_size = sizeof(VirtIOS390Device),
+    .class_init    = s390_virtio_rng_class_init,
+};
+
 static int s390_virtio_busdev_init(DeviceState *dev)
 {
     VirtIOS390Device *_dev = (VirtIOS390Device *)dev;
@@ -524,6 +558,7 @@  static void s390_virtio_register_types(void)
     type_register_static(&s390_virtio_blk);
     type_register_static(&s390_virtio_net);
     type_register_static(&s390_virtio_scsi);
+    type_register_static(&s390_virtio_rng);
     type_register_static(&s390_virtio_bridge_info);
 }
 
diff --git a/hw/s390-virtio-bus.h b/hw/s390-virtio-bus.h
index 4b99d02..5fc53b3 100644
--- a/hw/s390-virtio-bus.h
+++ b/hw/s390-virtio-bus.h
@@ -19,6 +19,7 @@ 
 
 #include "virtio-blk.h"
 #include "virtio-net.h"
+#include "virtio-rng.h"
 #include "virtio-serial.h"
 #include "virtio-scsi.h"
 
@@ -71,6 +72,7 @@  struct VirtIOS390Device {
     virtio_serial_conf serial;
     virtio_net_conf net;
     VirtIOSCSIConf scsi;
+    VirtIORNGConf rng;
 };
 
 typedef struct VirtIOS390Bus {
diff --git a/hw/virtio-pci.c b/hw/virtio-pci.c
index 79b86f1..bd5851e 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -810,6 +810,28 @@  static int virtio_balloon_exit_pci(PCIDevice *pci_dev)
     return virtio_exit_pci(pci_dev);
 }
 
+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);
+    if (!vdev) {
+        return -1;
+    }
+    virtio_init_pci(proxy, vdev);
+    return 0;
+}
+
+static int virtio_rng_exit_pci(PCIDevice *pci_dev)
+{
+    VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
+
+    virtio_pci_stop_ioeventfd(proxy);
+    virtio_rng_exit(proxy->vdev);
+    return virtio_exit_pci(pci_dev);
+}
+
 static Property virtio_blk_properties[] = {
     DEFINE_PROP_HEX32("class", VirtIOPCIProxy, class_code, 0),
     DEFINE_BLOCK_PROPERTIES(VirtIOPCIProxy, blk.conf),
@@ -938,6 +960,34 @@  static TypeInfo virtio_balloon_info = {
     .class_init    = virtio_balloon_class_init,
 };
 
+static Property virtio_rng_properties[] = {
+    DEFINE_VIRTIO_COMMON_FEATURES(VirtIOPCIProxy, host_features),
+    DEFINE_PROP_CHR("chardev", VirtIOPCIProxy, rng.chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void virtio_rng_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
+
+    k->init = virtio_rng_init_pci;
+    k->exit = virtio_rng_exit_pci;
+    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
+    k->device_id = PCI_DEVICE_ID_VIRTIO_RNG;
+    k->revision = VIRTIO_PCI_ABI_VERSION;
+    k->class_id = PCI_CLASS_OTHERS;
+    dc->reset = virtio_pci_reset;
+    dc->props = virtio_rng_properties;
+}
+
+static TypeInfo virtio_rng_info = {
+    .name          = "virtio-rng-pci",
+    .parent        = TYPE_PCI_DEVICE,
+    .instance_size = sizeof(VirtIOPCIProxy),
+    .class_init    = virtio_rng_class_init,
+};
+
 static int virtio_scsi_init_pci(PCIDevice *pci_dev)
 {
     VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev);
@@ -999,6 +1049,7 @@  static void virtio_pci_register_types(void)
     type_register_static(&virtio_serial_info);
     type_register_static(&virtio_balloon_info);
     type_register_static(&virtio_scsi_info);
+    type_register_static(&virtio_rng_info);
 }
 
 type_init(virtio_pci_register_types)
diff --git a/hw/virtio-pci.h b/hw/virtio-pci.h
index 889e59e..cc3f828 100644
--- a/hw/virtio-pci.h
+++ b/hw/virtio-pci.h
@@ -17,6 +17,7 @@ 
 
 #include "virtio-blk.h"
 #include "virtio-net.h"
+#include "virtio-rng.h"
 #include "virtio-serial.h"
 #include "virtio-scsi.h"
 
@@ -42,6 +43,7 @@  typedef struct {
     virtio_serial_conf serial;
     virtio_net_conf net;
     VirtIOSCSIConf scsi;
+    VirtIORNGConf rng;
     bool ioeventfd_disabled;
     bool ioeventfd_started;
 } VirtIOPCIProxy;
diff --git a/hw/virtio-rng.c b/hw/virtio-rng.c
new file mode 100644
index 0000000..f4f9078
--- /dev/null
+++ b/hw/virtio-rng.c
@@ -0,0 +1,199 @@ 
+/*
+ * A virtio device implementing a hardware random number generator.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ * Copyright 2012 Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#include "iov.h"
+#include "qdev.h"
+#include "qjson.h"
+#include "virtio.h"
+#include "virtio-rng.h"
+
+typedef struct VirtIORNG {
+    VirtIODevice vdev;
+
+    DeviceState *qdev;
+
+    /* Only one vq - guest puts buffer(s) on it when it needs entropy */
+    VirtQueue *vq;
+    VirtQueueElement elem;
+
+    /* Config data for the device -- currently only chardev */
+    VirtIORNGConf *conf;
+
+    /* Whether we've popped a vq element into 'elem' above */
+    bool popped;
+} VirtIORNG;
+
+static bool is_guest_ready(VirtIORNG *vrng)
+{
+    if (virtio_queue_ready(vrng->vq)
+        && (vrng->vdev.status & VIRTIO_CONFIG_S_DRIVER_OK)) {
+        return true;
+    }
+    return false;
+}
+
+static void send_qevent_for_entropy(size_t size)
+{
+    QObject *data;
+
+    data = qobject_from_jsonf("{ 'bytes': %" PRId64 " }",
+                              size);
+    monitor_protocol_event(QEVENT_ENTROPY_NEEDED, data);
+    qobject_decref(data);
+}
+
+static size_t pop_an_elem(VirtIORNG *vrng)
+{
+    size_t size;
+
+    if (!vrng->popped && !virtqueue_pop(vrng->vq, &vrng->elem)) {
+        return 0;
+    }
+    vrng->popped = true;
+
+    size = iov_size(vrng->elem.in_sg, vrng->elem.in_num);
+    return size;
+}
+
+static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+    size_t size;
+
+    size = pop_an_elem(vrng);
+    if (size) {
+        send_qevent_for_entropy(size);
+    }
+}
+
+static int chr_can_read(void *opaque)
+{
+    VirtIORNG *vrng = opaque;
+
+    if (!is_guest_ready(vrng)) {
+        return 0;
+    }
+    return pop_an_elem(vrng);
+}
+
+/* Send data from a char device over to the guest */
+static void chr_read(void *opaque, const uint8_t *buf, int size)
+{
+    VirtIORNG *vrng = opaque;
+    size_t len;
+    int offset;
+
+    if (!is_guest_ready(vrng)) {
+        return;
+    }
+
+    offset = 0;
+    while (offset < size) {
+        if (!pop_an_elem(vrng)) {
+            break;
+        }
+        len = iov_from_buf(vrng->elem.in_sg, vrng->elem.in_num,
+                           buf + offset, 0, size - offset);
+        offset += len;
+
+        virtqueue_push(vrng->vq, &vrng->elem, len);
+        vrng->popped = false;
+    }
+    virtio_notify(&vrng->vdev, vrng->vq);
+
+    /*
+     * Lastly, if we had multiple elems queued by the guest, and we
+     * didn't have enough data to fill them all, indicate we want more
+     * data.  We can't stick this into chr_can_read(), as it'll just
+     * end up spamming the management app.
+     */
+    len = pop_an_elem(vrng);
+    if (len) {
+        send_qevent_for_entropy(len);
+    }
+}
+
+static uint32_t get_features(VirtIODevice *vdev, uint32_t f)
+{
+    return f;
+}
+
+static void virtio_rng_save(QEMUFile *f, void *opaque)
+{
+    VirtIORNG *vrng = opaque;
+
+    virtio_save(&vrng->vdev, f);
+
+    qemu_put_byte(f, vrng->popped);
+    if (vrng->popped) {
+        qemu_put_buffer(f, (unsigned char *)&vrng->elem,
+                        sizeof(vrng->elem));
+    }
+}
+
+static int virtio_rng_load(QEMUFile *f, void *opaque, int version_id)
+{
+    VirtIORNG *vrng = opaque;
+
+    if (version_id != 1) {
+        return -EINVAL;
+    }
+    virtio_load(&vrng->vdev, f);
+
+    vrng->popped = qemu_get_byte(f);
+    if (vrng->popped) {
+        qemu_get_buffer(f, (unsigned char *)&vrng->elem,
+                        sizeof(vrng->elem));
+        virtqueue_map_sg(vrng->elem.in_sg, vrng->elem.in_addr,
+                         vrng->elem.in_num, 1);
+        virtqueue_map_sg(vrng->elem.out_sg, vrng->elem.out_addr,
+                         vrng->elem.out_num, 0);
+    }
+    return 0;
+}
+
+VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf)
+{
+    VirtIORNG *vrng;
+    VirtIODevice *vdev;
+
+    if (!conf->chr) {
+        error_report("chardev property expected");
+        return NULL;
+    }
+
+    vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
+                              sizeof(VirtIORNG));
+
+    vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+
+    vrng->vq = virtio_add_queue(vdev, 8, handle_input);
+    vrng->vdev.get_features = get_features;
+
+    vrng->qdev = dev;
+    vrng->conf = conf;
+    vrng->popped = false;
+    register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
+                    virtio_rng_load, vrng);
+
+    qemu_chr_add_handlers(conf->chr, chr_can_read, chr_read, NULL, vrng);
+
+    return vdev;
+}
+
+void virtio_rng_exit(VirtIODevice *vdev)
+{
+    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+
+    qemu_chr_add_handlers(vrng->conf->chr, NULL, NULL, NULL, NULL);
+    unregister_savevm(vrng->qdev, "virtio-rng", vrng);
+    virtio_cleanup(vdev);
+}
diff --git a/hw/virtio-rng.h b/hw/virtio-rng.h
new file mode 100644
index 0000000..b132acd
--- /dev/null
+++ b/hw/virtio-rng.h
@@ -0,0 +1,24 @@ 
+/*
+ * Virtio RNG Support
+ *
+ * Copyright Red Hat, Inc. 2012
+ * Copyright Amit Shah <amit.shah@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or
+ * (at your option) any later version.  See the COPYING file in the
+ * top-level directory.
+ */
+
+#ifndef _QEMU_VIRTIO_RNG_H
+#define _QEMU_VIRTIO_RNG_H
+
+#include "qemu-char.h"
+
+/* The Virtio ID for the virtio rng device */
+#define VIRTIO_ID_RNG    4
+
+struct VirtIORNGConf {
+    CharDriverState *chr;
+};
+
+#endif
diff --git a/hw/virtio.h b/hw/virtio.h
index 85aabe5..b4b5bf6 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -201,6 +201,8 @@  VirtIODevice *virtio_serial_init(DeviceState *dev, virtio_serial_conf *serial);
 VirtIODevice *virtio_balloon_init(DeviceState *dev);
 typedef struct VirtIOSCSIConf VirtIOSCSIConf;
 VirtIODevice *virtio_scsi_init(DeviceState *dev, VirtIOSCSIConf *conf);
+typedef struct VirtIORNGConf VirtIORNGConf;
+VirtIODevice *virtio_rng_init(DeviceState *dev, VirtIORNGConf *conf);
 #ifdef CONFIG_LINUX
 VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
 #endif
@@ -211,6 +213,7 @@  void virtio_blk_exit(VirtIODevice *vdev);
 void virtio_serial_exit(VirtIODevice *vdev);
 void virtio_balloon_exit(VirtIODevice *vdev);
 void virtio_scsi_exit(VirtIODevice *vdev);
+void virtio_rng_exit(VirtIODevice *vdev);
 
 #define DEFINE_VIRTIO_COMMON_FEATURES(_state, _field) \
 	DEFINE_PROP_BIT("indirect_desc", _state, _field, \
diff --git a/monitor.c b/monitor.c
index 12a6fe2..177b73e 100644
--- a/monitor.c
+++ b/monitor.c
@@ -493,6 +493,9 @@  void monitor_protocol_event(MonitorEvent event, QObject *data)
         case QEVENT_WAKEUP:
             event_name = "WAKEUP";
             break;
+        case QEVENT_ENTROPY_NEEDED:
+            event_name = "ENTROPY_NEEDED";
+            break;
         default:
             abort();
             break;
diff --git a/monitor.h b/monitor.h
index 0d49800..731d599 100644
--- a/monitor.h
+++ b/monitor.h
@@ -41,6 +41,7 @@  typedef enum MonitorEvent {
     QEVENT_DEVICE_TRAY_MOVED,
     QEVENT_SUSPEND,
     QEVENT_WAKEUP,
+    QEVENT_ENTROPY_NEEDED,
     QEVENT_MAX,
 } MonitorEvent;