Patchwork [4/5] virtio: Add virtio-rng driver

login
register
mail settings
Submitter Ian Molton
Date Feb. 1, 2010, 1:34 p.m.
Message ID <1265031265-14717-5-git-send-email-ian.molton@collabora.co.uk>
Download mbox | patch
Permalink /patch/44179/
State New
Headers show

Comments

Ian Molton - Feb. 1, 2010, 1:34 p.m.
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
Anthony Liguori - Feb. 1, 2010, 3:31 p.m.
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
Gerd Hoffmann - Feb. 1, 2010, 4:03 p.m.
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
Anthony Liguori - Feb. 1, 2010, 4:50 p.m.
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
>
Ian Molton - Feb. 1, 2010, 10:41 p.m.
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
Ian Molton - Feb. 1, 2010, 10:48 p.m.
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 - Feb. 9, 2010, 10:50 a.m.
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?

Patch

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