cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

virtio-rng.c (5538B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Randomness driver for virtio
      4 *  Copyright (C) 2007, 2008 Rusty Russell IBM Corporation
      5 */
      6
      7#include <linux/err.h>
      8#include <linux/hw_random.h>
      9#include <linux/scatterlist.h>
     10#include <linux/spinlock.h>
     11#include <linux/virtio.h>
     12#include <linux/virtio_rng.h>
     13#include <linux/module.h>
     14#include <linux/slab.h>
     15
     16static DEFINE_IDA(rng_index_ida);
     17
     18struct virtrng_info {
     19	struct hwrng hwrng;
     20	struct virtqueue *vq;
     21	char name[25];
     22	int index;
     23	bool hwrng_register_done;
     24	bool hwrng_removed;
     25	/* data transfer */
     26	struct completion have_data;
     27	unsigned int data_avail;
     28	unsigned int data_idx;
     29	/* minimal size returned by rng_buffer_size() */
     30#if SMP_CACHE_BYTES < 32
     31	u8 data[32];
     32#else
     33	u8 data[SMP_CACHE_BYTES];
     34#endif
     35};
     36
     37static void random_recv_done(struct virtqueue *vq)
     38{
     39	struct virtrng_info *vi = vq->vdev->priv;
     40
     41	/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
     42	if (!virtqueue_get_buf(vi->vq, &vi->data_avail))
     43		return;
     44
     45	vi->data_idx = 0;
     46
     47	complete(&vi->have_data);
     48}
     49
     50static void request_entropy(struct virtrng_info *vi)
     51{
     52	struct scatterlist sg;
     53
     54	reinit_completion(&vi->have_data);
     55	vi->data_avail = 0;
     56	vi->data_idx = 0;
     57
     58	sg_init_one(&sg, vi->data, sizeof(vi->data));
     59
     60	/* There should always be room for one buffer. */
     61	virtqueue_add_inbuf(vi->vq, &sg, 1, vi->data, GFP_KERNEL);
     62
     63	virtqueue_kick(vi->vq);
     64}
     65
     66static unsigned int copy_data(struct virtrng_info *vi, void *buf,
     67			      unsigned int size)
     68{
     69	size = min_t(unsigned int, size, vi->data_avail);
     70	memcpy(buf, vi->data + vi->data_idx, size);
     71	vi->data_idx += size;
     72	vi->data_avail -= size;
     73	if (vi->data_avail == 0)
     74		request_entropy(vi);
     75	return size;
     76}
     77
     78static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
     79{
     80	int ret;
     81	struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
     82	unsigned int chunk;
     83	size_t read;
     84
     85	if (vi->hwrng_removed)
     86		return -ENODEV;
     87
     88	read = 0;
     89
     90	/* copy available data */
     91	if (vi->data_avail) {
     92		chunk = copy_data(vi, buf, size);
     93		size -= chunk;
     94		read += chunk;
     95	}
     96
     97	if (!wait)
     98		return read;
     99
    100	/* We have already copied available entropy,
    101	 * so either size is 0 or data_avail is 0
    102	 */
    103	while (size != 0) {
    104		/* data_avail is 0 but a request is pending */
    105		ret = wait_for_completion_killable(&vi->have_data);
    106		if (ret < 0)
    107			return ret;
    108		/* if vi->data_avail is 0, we have been interrupted
    109		 * by a cleanup, but buffer stays in the queue
    110		 */
    111		if (vi->data_avail == 0)
    112			return read;
    113
    114		chunk = copy_data(vi, buf + read, size);
    115		size -= chunk;
    116		read += chunk;
    117	}
    118
    119	return read;
    120}
    121
    122static void virtio_cleanup(struct hwrng *rng)
    123{
    124	struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
    125
    126	complete(&vi->have_data);
    127}
    128
    129static int probe_common(struct virtio_device *vdev)
    130{
    131	int err, index;
    132	struct virtrng_info *vi = NULL;
    133
    134	vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
    135	if (!vi)
    136		return -ENOMEM;
    137
    138	vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
    139	if (index < 0) {
    140		err = index;
    141		goto err_ida;
    142	}
    143	sprintf(vi->name, "virtio_rng.%d", index);
    144	init_completion(&vi->have_data);
    145
    146	vi->hwrng = (struct hwrng) {
    147		.read = virtio_read,
    148		.cleanup = virtio_cleanup,
    149		.priv = (unsigned long)vi,
    150		.name = vi->name,
    151		.quality = 1000,
    152	};
    153	vdev->priv = vi;
    154
    155	/* We expect a single virtqueue. */
    156	vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
    157	if (IS_ERR(vi->vq)) {
    158		err = PTR_ERR(vi->vq);
    159		goto err_find;
    160	}
    161
    162	virtio_device_ready(vdev);
    163
    164	/* we always have a pending entropy request */
    165	request_entropy(vi);
    166
    167	return 0;
    168
    169err_find:
    170	ida_simple_remove(&rng_index_ida, index);
    171err_ida:
    172	kfree(vi);
    173	return err;
    174}
    175
    176static void remove_common(struct virtio_device *vdev)
    177{
    178	struct virtrng_info *vi = vdev->priv;
    179
    180	vi->hwrng_removed = true;
    181	vi->data_avail = 0;
    182	vi->data_idx = 0;
    183	complete(&vi->have_data);
    184	if (vi->hwrng_register_done)
    185		hwrng_unregister(&vi->hwrng);
    186	virtio_reset_device(vdev);
    187	vdev->config->del_vqs(vdev);
    188	ida_simple_remove(&rng_index_ida, vi->index);
    189	kfree(vi);
    190}
    191
    192static int virtrng_probe(struct virtio_device *vdev)
    193{
    194	return probe_common(vdev);
    195}
    196
    197static void virtrng_remove(struct virtio_device *vdev)
    198{
    199	remove_common(vdev);
    200}
    201
    202static void virtrng_scan(struct virtio_device *vdev)
    203{
    204	struct virtrng_info *vi = vdev->priv;
    205	int err;
    206
    207	err = hwrng_register(&vi->hwrng);
    208	if (!err)
    209		vi->hwrng_register_done = true;
    210}
    211
    212#ifdef CONFIG_PM_SLEEP
    213static int virtrng_freeze(struct virtio_device *vdev)
    214{
    215	remove_common(vdev);
    216	return 0;
    217}
    218
    219static int virtrng_restore(struct virtio_device *vdev)
    220{
    221	int err;
    222
    223	err = probe_common(vdev);
    224	if (!err) {
    225		struct virtrng_info *vi = vdev->priv;
    226
    227		/*
    228		 * Set hwrng_removed to ensure that virtio_read()
    229		 * does not block waiting for data before the
    230		 * registration is complete.
    231		 */
    232		vi->hwrng_removed = true;
    233		err = hwrng_register(&vi->hwrng);
    234		if (!err) {
    235			vi->hwrng_register_done = true;
    236			vi->hwrng_removed = false;
    237		}
    238	}
    239
    240	return err;
    241}
    242#endif
    243
    244static const struct virtio_device_id id_table[] = {
    245	{ VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },
    246	{ 0 },
    247};
    248
    249static struct virtio_driver virtio_rng_driver = {
    250	.driver.name =	KBUILD_MODNAME,
    251	.driver.owner =	THIS_MODULE,
    252	.id_table =	id_table,
    253	.probe =	virtrng_probe,
    254	.remove =	virtrng_remove,
    255	.scan =		virtrng_scan,
    256#ifdef CONFIG_PM_SLEEP
    257	.freeze =	virtrng_freeze,
    258	.restore =	virtrng_restore,
    259#endif
    260};
    261
    262module_virtio_driver(virtio_rng_driver);
    263MODULE_DEVICE_TABLE(virtio, id_table);
    264MODULE_DESCRIPTION("Virtio random number driver");
    265MODULE_LICENSE("GPL");