brintos

brintos / linux-shallow public Read only

0
0
Text · 5.4 KiB · dd998f4 Raw
260 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Randomness driver for virtio4 *  Copyright (C) 2007, 2008 Rusty Russell IBM Corporation5 */6 7#include <asm/barrier.h>8#include <linux/err.h>9#include <linux/hw_random.h>10#include <linux/scatterlist.h>11#include <linux/spinlock.h>12#include <linux/virtio.h>13#include <linux/virtio_rng.h>14#include <linux/module.h>15#include <linux/slab.h>16 17static DEFINE_IDA(rng_index_ida);18 19struct virtrng_info {20	struct hwrng hwrng;21	struct virtqueue *vq;22	char name[25];23	int index;24	bool hwrng_register_done;25	bool hwrng_removed;26	/* data transfer */27	struct completion have_data;28	unsigned int data_avail;29	unsigned int data_idx;30	/* minimal size returned by rng_buffer_size() */31#if SMP_CACHE_BYTES < 3232	u8 data[32];33#else34	u8 data[SMP_CACHE_BYTES];35#endif36};37 38static void random_recv_done(struct virtqueue *vq)39{40	struct virtrng_info *vi = vq->vdev->priv;41	unsigned int len;42 43	/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */44	if (!virtqueue_get_buf(vi->vq, &len))45		return;46 47	smp_store_release(&vi->data_avail, len);48	complete(&vi->have_data);49}50 51static void request_entropy(struct virtrng_info *vi)52{53	struct scatterlist sg;54 55	reinit_completion(&vi->have_data);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 (smp_load_acquire(&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 0102	 */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 interrupted109		 * by a cleanup, but buffer stays in the queue110		 */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_alloc(&rng_index_ida, 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	};152	vdev->priv = vi;153 154	/* We expect a single virtqueue. */155	vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");156	if (IS_ERR(vi->vq)) {157		err = PTR_ERR(vi->vq);158		goto err_find;159	}160 161	virtio_device_ready(vdev);162 163	/* we always have a pending entropy request */164	request_entropy(vi);165 166	return 0;167 168err_find:169	ida_free(&rng_index_ida, index);170err_ida:171	kfree(vi);172	return err;173}174 175static void remove_common(struct virtio_device *vdev)176{177	struct virtrng_info *vi = vdev->priv;178 179	vi->hwrng_removed = true;180	vi->data_avail = 0;181	vi->data_idx = 0;182	complete(&vi->have_data);183	if (vi->hwrng_register_done)184		hwrng_unregister(&vi->hwrng);185	virtio_reset_device(vdev);186	vdev->config->del_vqs(vdev);187	ida_free(&rng_index_ida, vi->index);188	kfree(vi);189}190 191static int virtrng_probe(struct virtio_device *vdev)192{193	return probe_common(vdev);194}195 196static void virtrng_remove(struct virtio_device *vdev)197{198	remove_common(vdev);199}200 201static void virtrng_scan(struct virtio_device *vdev)202{203	struct virtrng_info *vi = vdev->priv;204	int err;205 206	err = hwrng_register(&vi->hwrng);207	if (!err)208		vi->hwrng_register_done = true;209}210 211static int virtrng_freeze(struct virtio_device *vdev)212{213	remove_common(vdev);214	return 0;215}216 217static int virtrng_restore(struct virtio_device *vdev)218{219	int err;220 221	err = probe_common(vdev);222	if (!err) {223		struct virtrng_info *vi = vdev->priv;224 225		/*226		 * Set hwrng_removed to ensure that virtio_read()227		 * does not block waiting for data before the228		 * registration is complete.229		 */230		vi->hwrng_removed = true;231		err = hwrng_register(&vi->hwrng);232		if (!err) {233			vi->hwrng_register_done = true;234			vi->hwrng_removed = false;235		}236	}237 238	return err;239}240 241static const struct virtio_device_id id_table[] = {242	{ VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },243	{ 0 },244};245 246static struct virtio_driver virtio_rng_driver = {247	.driver.name =	KBUILD_MODNAME,248	.id_table =	id_table,249	.probe =	virtrng_probe,250	.remove =	virtrng_remove,251	.scan =		virtrng_scan,252	.freeze =	pm_sleep_ptr(virtrng_freeze),253	.restore =	pm_sleep_ptr(virtrng_restore),254};255 256module_virtio_driver(virtio_rng_driver);257MODULE_DEVICE_TABLE(virtio, id_table);258MODULE_DESCRIPTION("Virtio random number driver");259MODULE_LICENSE("GPL");260