qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 1/1] virtio-rng: device to send host entropy to gues


From: Amit Shah
Subject: [Qemu-devel] [PATCH 1/1] virtio-rng: device to send host entropy to guest
Date: Wed, 16 May 2012 17:00:28 +0530

The Linux kernel already has a virtio-rng driver, this is the device
implementation.

When Linux needs more entropy, it puts a buffer in the vq.  We then put
entropy into that buffer, and push it back to the guest.

Feeding randomness from host's /dev/urandom into the guest is
sufficient, so this is a simple implementation that opens /dev/urandom
and reads from it whenever required.

Invocation is simple:

  qemu ... -device virtio-rng-pci

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

There are ways to extend the device to be more generic and collect
entropy from other sources, but this is simple enough and works for now.

Signed-off-by: Amit Shah <address@hidden>
---
 Makefile.objs   |    1 +
 hw/pci.h        |    1 +
 hw/virtio-pci.c |   50 +++++++++++++++++++++
 hw/virtio-rng.c |  130 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 hw/virtio-rng.h |   18 ++++++++
 hw/virtio.h     |    2 +
 6 files changed, 202 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/virtio-pci.c b/hw/virtio-pci.c
index 4a4413d..7f2d630 100644
--- a/hw/virtio-pci.c
+++ b/hw/virtio-pci.c
@@ -812,6 +812,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);
+    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, block),
@@ -937,6 +959,33 @@ 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_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);
@@ -998,6 +1047,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-rng.c b/hw/virtio-rng.c
new file mode 100644
index 0000000..e1f3d1c
--- /dev/null
+++ b/hw/virtio-rng.c
@@ -0,0 +1,130 @@
+/* A virtio device for feeding entropy into a guest.
+ *
+ * Copyright 2012 Red Hat, Inc.
+ * Copyright 2012 Amit Shah <address@hidden>
+ *
+ * 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 "virtio.h"
+#include "virtio-rng.h"
+
+typedef struct VirtIORNG {
+    VirtIODevice vdev;
+
+    DeviceState *qdev;
+
+    /* Only one vq - guest puts a buffer on it when it needs entropy */
+    VirtQueue *vq;
+
+    int input_fd;
+} VirtIORNG;
+
+static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
+{
+    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+    VirtQueueElement elem;
+    char *buf;
+    ssize_t size, offset, ret;
+
+    if (!virtqueue_pop(vq, &elem)) {
+        return;
+    }
+    size = iov_size(elem.in_sg, elem.in_num);
+
+    buf = g_malloc(size);
+    do {
+        ret = read(vrng->input_fd, buf, size);
+    } while (ret == -1 && errno == EINTR);
+    if (ret < 0) {
+        /* We can't get randomness -- give up for now. */
+        virtqueue_push(vq, &elem, 0);
+        goto skip;
+    }
+
+    offset = 0;
+    size = ret;
+    while (offset < size) {
+        size_t len;
+
+        /* We've already popped the first elem */
+        if (offset && !virtqueue_pop(vq, &elem)) {
+            break;
+        }
+
+        len = iov_from_buf(elem.in_sg, elem.in_num,
+                           buf + offset, 0, size - offset);
+        offset += len;
+
+        virtqueue_push(vq, &elem, len);
+    }
+skip:
+    g_free(buf);
+    virtio_notify(vdev, vq);
+}
+
+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);
+}
+
+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);
+
+    return 0;
+}
+
+VirtIODevice *virtio_rng_init(DeviceState *dev)
+{
+    VirtIORNG *vrng;
+    VirtIODevice *vdev;
+    int input_fd;
+
+    input_fd = open("/dev/urandom", O_RDONLY);
+    if (input_fd < 0) {
+        error_report("error %d opening /dev/urandom", errno);
+        return NULL;
+    }
+
+    vdev = virtio_common_init("virtio-rng", VIRTIO_ID_RNG, 0,
+                              sizeof(VirtIORNG));
+
+    vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+
+    vrng->input_fd = input_fd;
+
+    vrng->vq = virtio_add_queue(vdev, 8, handle_input);
+    vrng->vdev.get_features = get_features;
+
+    vrng->qdev = dev;
+    register_savevm(dev, "virtio-rng", -1, 1, virtio_rng_save,
+                    virtio_rng_load, vrng);
+
+    return vdev;
+}
+
+void virtio_rng_exit(VirtIODevice *vdev)
+{
+    VirtIORNG *vrng = DO_UPCAST(VirtIORNG, vdev, vdev);
+
+    close(vrng->input_fd);
+    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..2e1eba3
--- /dev/null
+++ b/hw/virtio-rng.h
@@ -0,0 +1,18 @@
+/*
+ * Virtio RNG Support
+ *
+ * Copyright Red Hat, Inc. 2012
+ * Copyright Amit Shah <address@hidden>
+ *
+ * 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
+
+/* The Virtio ID for the virtio rng device */
+#define VIRTIO_ID_RNG    4
+
+#endif
diff --git a/hw/virtio.h b/hw/virtio.h
index 0aef7d1..0315e0c 100644
--- a/hw/virtio.h
+++ b/hw/virtio.h
@@ -201,6 +201,7 @@ 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);
+VirtIODevice *virtio_rng_init(DeviceState *dev);
 #ifdef CONFIG_LINUX
 VirtIODevice *virtio_9p_init(DeviceState *dev, V9fsConf *conf);
 #endif
@@ -211,6 +212,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, \
-- 
1.7.7.6




reply via email to

[Prev in Thread] Current Thread [Next in Thread]