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#include <linux/idr.h>
  28
  29static DEFINE_IDA(rng_index_ida);
  30
  31struct virtrng_info {
  32        struct hwrng hwrng;
  33        struct virtqueue *vq;
  34        struct completion have_data;
  35        char name[25];
  36        unsigned int data_avail;
  37        int index;
  38        bool busy;
  39        bool hwrng_register_done;
  40        bool hwrng_removed;
  41};
  42
  43static void random_recv_done(struct virtqueue *vq)
  44{
  45        struct virtrng_info *vi = vq->vdev->priv;
  46
  47        /* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
  48        if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
  49                return;
  50
  51        complete(&vi->have_data);
  52}
  53
  54/* The host will fill any buffer we give it with sweet, sweet randomness. */
  55static void register_buffer(struct virtrng_info *vi, u8 *buf, size_t size)
  56{
  57        struct scatterlist sg;
  58
  59        sg_init_one(&sg, buf, size);
  60
  61        /* There should always be room for one buffer. */
  62        virtqueue_add_inbuf(vi->vq, &sg, 1, buf, GFP_KERNEL);
  63
  64        virtqueue_kick(vi->vq);
  65}
  66
  67static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
  68{
  69        int ret;
  70        struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
  71
  72        if (vi->hwrng_removed)
  73                return -ENODEV;
  74
  75        if (!vi->busy) {
  76                vi->busy = true;
  77                init_completion(&vi->have_data);
  78                register_buffer(vi, buf, size);
  79        }
  80
  81        if (!wait)
  82                return 0;
  83
  84        ret = wait_for_completion_killable(&vi->have_data);
  85        if (ret < 0)
  86                return ret;
  87
  88        vi->busy = false;
  89
  90        return vi->data_avail;
  91}
  92
  93static void virtio_cleanup(struct hwrng *rng)
  94{
  95        struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
  96
  97        if (vi->busy)
  98                wait_for_completion(&vi->have_data);
  99}
 100
 101static int probe_common(struct virtio_device *vdev)
 102{
 103        int err, index;
 104        struct virtrng_info *vi = NULL;
 105
 106        vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
 107        if (!vi)
 108                return -ENOMEM;
 109
 110        vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
 111        if (index < 0) {
 112                kfree(vi);
 113                return index;
 114        }
 115        sprintf(vi->name, "virtio_rng.%d", index);
 116        init_completion(&vi->have_data);
 117
 118        vi->hwrng = (struct hwrng) {
 119                .read = virtio_read,
 120                .cleanup = virtio_cleanup,
 121                .priv = (unsigned long)vi,
 122                .name = vi->name,
 123                .quality = 1000,
 124        };
 125        vdev->priv = vi;
 126
 127        /* We expect a single virtqueue. */
 128        vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
 129        if (IS_ERR(vi->vq)) {
 130                err = PTR_ERR(vi->vq);
 131                vi->vq = NULL;
 132                kfree(vi);
 133                ida_simple_remove(&rng_index_ida, index);
 134                return err;
 135        }
 136
 137        return 0;
 138}
 139
 140static void remove_common(struct virtio_device *vdev)
 141{
 142        struct virtrng_info *vi = vdev->priv;
 143
 144        vi->hwrng_removed = true;
 145        vi->data_avail = 0;
 146        complete(&vi->have_data);
 147        vdev->config->reset(vdev);
 148        vi->busy = false;
 149        if (vi->hwrng_register_done)
 150                hwrng_unregister(&vi->hwrng);
 151        vdev->config->del_vqs(vdev);
 152        ida_simple_remove(&rng_index_ida, vi->index);
 153        kfree(vi);
 154}
 155
 156static int virtrng_probe(struct virtio_device *vdev)
 157{
 158        return probe_common(vdev);
 159}
 160
 161static void virtrng_remove(struct virtio_device *vdev)
 162{
 163        remove_common(vdev);
 164}
 165
 166static void virtrng_scan(struct virtio_device *vdev)
 167{
 168        struct virtrng_info *vi = vdev->priv;
 169        int err;
 170
 171        err = hwrng_register(&vi->hwrng);
 172        if (!err)
 173                vi->hwrng_register_done = true;
 174}
 175
 176#ifdef CONFIG_PM_SLEEP
 177static int virtrng_freeze(struct virtio_device *vdev)
 178{
 179        remove_common(vdev);
 180        return 0;
 181}
 182
 183static int virtrng_restore(struct virtio_device *vdev)
 184{
 185        return probe_common(vdev);
 186}
 187#endif
 188
 189static struct virtio_device_id id_table[] = {
 190        { VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },
 191        { 0 },
 192};
 193
 194static struct virtio_driver virtio_rng_driver = {
 195        .driver.name =  KBUILD_MODNAME,
 196        .driver.owner = THIS_MODULE,
 197        .id_table =     id_table,
 198        .probe =        virtrng_probe,
 199        .remove =       virtrng_remove,
 200        .scan =         virtrng_scan,
 201#ifdef CONFIG_PM_SLEEP
 202        .freeze =       virtrng_freeze,
 203        .restore =      virtrng_restore,
 204#endif
 205};
 206
 207module_virtio_driver(virtio_rng_driver);
 208MODULE_DEVICE_TABLE(virtio, id_table);
 209MODULE_DESCRIPTION("Virtio random number driver");
 210MODULE_LICENSE("GPL");
 211