linux/drivers/char/hw_random/virtio-rng.c
<<
>>
Prefs
   1/*
   2 * Randomness driver for virtio
   3 *  Copyright (C) 2007, 2008 Rusty Russell IBM Corporation
   4 *
   5 *  This program is free software; you can redistribute it and/or modify
   6 *  it under the terms of the GNU General Public License as published by
   7 *  the Free Software Foundation; either version 2 of the License, or
   8 *  (at your option) any later version.
   9 *
  10 *  This program is distributed in the hope that it will be useful,
  11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 *  GNU General Public License for more details.
  14 *
  15 *  You should have received a copy of the GNU General Public License
  16 *  along with this program; if not, write to the Free Software
  17 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  18 */
  19
  20#include <linux/err.h>
  21#include <linux/hw_random.h>
  22#include <linux/scatterlist.h>
  23#include <linux/spinlock.h>
  24#include <linux/virtio.h>
  25#include <linux/virtio_rng.h>
  26#include <linux/module.h>
  27
  28static struct virtqueue *vq;
  29static unsigned int data_avail;
  30static DECLARE_COMPLETION(have_data);
  31static bool busy;
  32
  33static void random_recv_done(struct virtqueue *vq)
  34{
  35        /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
  36        if (!virtqueue_get_buf(vq, &data_avail))
  37                return;
  38
  39        complete(&have_data);
  40}
  41
  42/* The host will fill any buffer we give it with sweet, sweet randomness. */
  43static void register_buffer(u8 *buf, size_t size)
  44{
  45        struct scatterlist sg;
  46
  47        sg_init_one(&sg, buf, size);
  48
  49        /* There should always be room for one buffer. */
  50        if (virtqueue_add_inbuf(vq, &sg, 1, buf, GFP_KERNEL) < 0)
  51                BUG();
  52
  53        virtqueue_kick(vq);
  54}
  55
  56static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
  57{
  58        int ret;
  59
  60        if (!busy) {
  61                busy = true;
  62                init_completion(&have_data);
  63                register_buffer(buf, size);
  64        }
  65
  66        if (!wait)
  67                return 0;
  68
  69        ret = wait_for_completion_killable(&have_data);
  70        if (ret < 0)
  71                return ret;
  72
  73        busy = false;
  74
  75        return data_avail;
  76}
  77
  78static void virtio_cleanup(struct hwrng *rng)
  79{
  80        if (busy)
  81                wait_for_completion(&have_data);
  82}
  83
  84
  85static struct hwrng virtio_hwrng = {
  86        .name           = "virtio",
  87        .cleanup        = virtio_cleanup,
  88        .read           = virtio_read,
  89};
  90
  91static int probe_common(struct virtio_device *vdev)
  92{
  93        int err;
  94
  95        if (vq) {
  96                /* We only support one device for now */
  97                return -EBUSY;
  98        }
  99        /* We expect a single virtqueue. */
 100        vq = virtio_find_single_vq(vdev, random_recv_done, "input");
 101        if (IS_ERR(vq)) {
 102                err = PTR_ERR(vq);
 103                vq = NULL;
 104                return err;
 105        }
 106
 107        err = hwrng_register(&virtio_hwrng);
 108        if (err) {
 109                vdev->config->del_vqs(vdev);
 110                vq = NULL;
 111                return err;
 112        }
 113
 114        return 0;
 115}
 116
 117static void remove_common(struct virtio_device *vdev)
 118{
 119        vdev->config->reset(vdev);
 120        busy = false;
 121        hwrng_unregister(&virtio_hwrng);
 122        vdev->config->del_vqs(vdev);
 123        vq = NULL;
 124}
 125
 126static int virtrng_probe(struct virtio_device *vdev)
 127{
 128        return probe_common(vdev);
 129}
 130
 131static void virtrng_remove(struct virtio_device *vdev)
 132{
 133        remove_common(vdev);
 134}
 135
 136#ifdef CONFIG_PM_SLEEP
 137static int virtrng_freeze(struct virtio_device *vdev)
 138{
 139        remove_common(vdev);
 140        return 0;
 141}
 142
 143static int virtrng_restore(struct virtio_device *vdev)
 144{
 145        return probe_common(vdev);
 146}
 147#endif
 148
 149static struct virtio_device_id id_table[] = {
 150        { VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },
 151        { 0 },
 152};
 153
 154static struct virtio_driver virtio_rng_driver = {
 155        .driver.name =  KBUILD_MODNAME,
 156        .driver.owner = THIS_MODULE,
 157        .id_table =     id_table,
 158        .probe =        virtrng_probe,
 159        .remove =       virtrng_remove,
 160#ifdef CONFIG_PM_SLEEP
 161        .freeze =       virtrng_freeze,
 162        .restore =      virtrng_restore,
 163#endif
 164};
 165
 166module_virtio_driver(virtio_rng_driver);
 167MODULE_DEVICE_TABLE(virtio, id_table);
 168MODULE_DESCRIPTION("Virtio random number driver");
 169MODULE_LICENSE("GPL");
 170