18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Randomness driver for virtio
48c2ecf20Sopenharmony_ci *  Copyright (C) 2007, 2008 Rusty Russell IBM Corporation
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <asm/barrier.h>
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/hw_random.h>
108c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
118c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
128c2ecf20Sopenharmony_ci#include <linux/virtio.h>
138c2ecf20Sopenharmony_ci#include <linux/virtio_rng.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic DEFINE_IDA(rng_index_ida);
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct virtrng_info {
208c2ecf20Sopenharmony_ci	struct hwrng hwrng;
218c2ecf20Sopenharmony_ci	struct virtqueue *vq;
228c2ecf20Sopenharmony_ci	char name[25];
238c2ecf20Sopenharmony_ci	int index;
248c2ecf20Sopenharmony_ci	bool hwrng_register_done;
258c2ecf20Sopenharmony_ci	bool hwrng_removed;
268c2ecf20Sopenharmony_ci	/* data transfer */
278c2ecf20Sopenharmony_ci	struct completion have_data;
288c2ecf20Sopenharmony_ci	unsigned int data_avail;
298c2ecf20Sopenharmony_ci	unsigned int data_idx;
308c2ecf20Sopenharmony_ci	/* minimal size returned by rng_buffer_size() */
318c2ecf20Sopenharmony_ci#if SMP_CACHE_BYTES < 32
328c2ecf20Sopenharmony_ci	u8 data[32];
338c2ecf20Sopenharmony_ci#else
348c2ecf20Sopenharmony_ci	u8 data[SMP_CACHE_BYTES];
358c2ecf20Sopenharmony_ci#endif
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void random_recv_done(struct virtqueue *vq)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct virtrng_info *vi = vq->vdev->priv;
418c2ecf20Sopenharmony_ci	unsigned int len;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
448c2ecf20Sopenharmony_ci	if (!virtqueue_get_buf(vi->vq, &len))
458c2ecf20Sopenharmony_ci		return;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	smp_store_release(&vi->data_avail, len);
488c2ecf20Sopenharmony_ci	complete(&vi->have_data);
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic void request_entropy(struct virtrng_info *vi)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	struct scatterlist sg;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	reinit_completion(&vi->have_data);
568c2ecf20Sopenharmony_ci	vi->data_idx = 0;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	sg_init_one(&sg, vi->data, sizeof(vi->data));
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/* There should always be room for one buffer. */
618c2ecf20Sopenharmony_ci	virtqueue_add_inbuf(vi->vq, &sg, 1, vi->data, GFP_KERNEL);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	virtqueue_kick(vi->vq);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic unsigned int copy_data(struct virtrng_info *vi, void *buf,
678c2ecf20Sopenharmony_ci			      unsigned int size)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	size = min_t(unsigned int, size, vi->data_avail);
708c2ecf20Sopenharmony_ci	memcpy(buf, vi->data + vi->data_idx, size);
718c2ecf20Sopenharmony_ci	vi->data_idx += size;
728c2ecf20Sopenharmony_ci	vi->data_avail -= size;
738c2ecf20Sopenharmony_ci	if (vi->data_avail == 0)
748c2ecf20Sopenharmony_ci		request_entropy(vi);
758c2ecf20Sopenharmony_ci	return size;
768c2ecf20Sopenharmony_ci}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_cistatic int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	int ret;
818c2ecf20Sopenharmony_ci	struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
828c2ecf20Sopenharmony_ci	unsigned int chunk;
838c2ecf20Sopenharmony_ci	size_t read;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (vi->hwrng_removed)
868c2ecf20Sopenharmony_ci		return -ENODEV;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	read = 0;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/* copy available data */
918c2ecf20Sopenharmony_ci	if (smp_load_acquire(&vi->data_avail)) {
928c2ecf20Sopenharmony_ci		chunk = copy_data(vi, buf, size);
938c2ecf20Sopenharmony_ci		size -= chunk;
948c2ecf20Sopenharmony_ci		read += chunk;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (!wait)
988c2ecf20Sopenharmony_ci		return read;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* We have already copied available entropy,
1018c2ecf20Sopenharmony_ci	 * so either size is 0 or data_avail is 0
1028c2ecf20Sopenharmony_ci	 */
1038c2ecf20Sopenharmony_ci	while (size != 0) {
1048c2ecf20Sopenharmony_ci		/* data_avail is 0 but a request is pending */
1058c2ecf20Sopenharmony_ci		ret = wait_for_completion_killable(&vi->have_data);
1068c2ecf20Sopenharmony_ci		if (ret < 0)
1078c2ecf20Sopenharmony_ci			return ret;
1088c2ecf20Sopenharmony_ci		/* if vi->data_avail is 0, we have been interrupted
1098c2ecf20Sopenharmony_ci		 * by a cleanup, but buffer stays in the queue
1108c2ecf20Sopenharmony_ci		 */
1118c2ecf20Sopenharmony_ci		if (vi->data_avail == 0)
1128c2ecf20Sopenharmony_ci			return read;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		chunk = copy_data(vi, buf + read, size);
1158c2ecf20Sopenharmony_ci		size -= chunk;
1168c2ecf20Sopenharmony_ci		read += chunk;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return read;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void virtio_cleanup(struct hwrng *rng)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	complete(&vi->have_data);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic int probe_common(struct virtio_device *vdev)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	int err, index;
1328c2ecf20Sopenharmony_ci	struct virtrng_info *vi = NULL;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
1358c2ecf20Sopenharmony_ci	if (!vi)
1368c2ecf20Sopenharmony_ci		return -ENOMEM;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
1398c2ecf20Sopenharmony_ci	if (index < 0) {
1408c2ecf20Sopenharmony_ci		err = index;
1418c2ecf20Sopenharmony_ci		goto err_ida;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	sprintf(vi->name, "virtio_rng.%d", index);
1448c2ecf20Sopenharmony_ci	init_completion(&vi->have_data);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	vi->hwrng = (struct hwrng) {
1478c2ecf20Sopenharmony_ci		.read = virtio_read,
1488c2ecf20Sopenharmony_ci		.cleanup = virtio_cleanup,
1498c2ecf20Sopenharmony_ci		.priv = (unsigned long)vi,
1508c2ecf20Sopenharmony_ci		.name = vi->name,
1518c2ecf20Sopenharmony_ci		.quality = 1000,
1528c2ecf20Sopenharmony_ci	};
1538c2ecf20Sopenharmony_ci	vdev->priv = vi;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* We expect a single virtqueue. */
1568c2ecf20Sopenharmony_ci	vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
1578c2ecf20Sopenharmony_ci	if (IS_ERR(vi->vq)) {
1588c2ecf20Sopenharmony_ci		err = PTR_ERR(vi->vq);
1598c2ecf20Sopenharmony_ci		goto err_find;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* we always have a pending entropy request */
1638c2ecf20Sopenharmony_ci	request_entropy(vi);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return 0;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cierr_find:
1688c2ecf20Sopenharmony_ci	ida_simple_remove(&rng_index_ida, index);
1698c2ecf20Sopenharmony_cierr_ida:
1708c2ecf20Sopenharmony_ci	kfree(vi);
1718c2ecf20Sopenharmony_ci	return err;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic void remove_common(struct virtio_device *vdev)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	struct virtrng_info *vi = vdev->priv;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	vi->hwrng_removed = true;
1798c2ecf20Sopenharmony_ci	vi->data_avail = 0;
1808c2ecf20Sopenharmony_ci	vi->data_idx = 0;
1818c2ecf20Sopenharmony_ci	complete(&vi->have_data);
1828c2ecf20Sopenharmony_ci	vdev->config->reset(vdev);
1838c2ecf20Sopenharmony_ci	if (vi->hwrng_register_done)
1848c2ecf20Sopenharmony_ci		hwrng_unregister(&vi->hwrng);
1858c2ecf20Sopenharmony_ci	vdev->config->del_vqs(vdev);
1868c2ecf20Sopenharmony_ci	ida_simple_remove(&rng_index_ida, vi->index);
1878c2ecf20Sopenharmony_ci	kfree(vi);
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic int virtrng_probe(struct virtio_device *vdev)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	return probe_common(vdev);
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic void virtrng_remove(struct virtio_device *vdev)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	remove_common(vdev);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic void virtrng_scan(struct virtio_device *vdev)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	struct virtrng_info *vi = vdev->priv;
2038c2ecf20Sopenharmony_ci	int err;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	err = hwrng_register(&vi->hwrng);
2068c2ecf20Sopenharmony_ci	if (!err)
2078c2ecf20Sopenharmony_ci		vi->hwrng_register_done = true;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
2118c2ecf20Sopenharmony_cistatic int virtrng_freeze(struct virtio_device *vdev)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	remove_common(vdev);
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int virtrng_restore(struct virtio_device *vdev)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	int err;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	err = probe_common(vdev);
2228c2ecf20Sopenharmony_ci	if (!err) {
2238c2ecf20Sopenharmony_ci		struct virtrng_info *vi = vdev->priv;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci		/*
2268c2ecf20Sopenharmony_ci		 * Set hwrng_removed to ensure that virtio_read()
2278c2ecf20Sopenharmony_ci		 * does not block waiting for data before the
2288c2ecf20Sopenharmony_ci		 * registration is complete.
2298c2ecf20Sopenharmony_ci		 */
2308c2ecf20Sopenharmony_ci		vi->hwrng_removed = true;
2318c2ecf20Sopenharmony_ci		err = hwrng_register(&vi->hwrng);
2328c2ecf20Sopenharmony_ci		if (!err) {
2338c2ecf20Sopenharmony_ci			vi->hwrng_register_done = true;
2348c2ecf20Sopenharmony_ci			vi->hwrng_removed = false;
2358c2ecf20Sopenharmony_ci		}
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return err;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci#endif
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic const struct virtio_device_id id_table[] = {
2438c2ecf20Sopenharmony_ci	{ VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },
2448c2ecf20Sopenharmony_ci	{ 0 },
2458c2ecf20Sopenharmony_ci};
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic struct virtio_driver virtio_rng_driver = {
2488c2ecf20Sopenharmony_ci	.driver.name =	KBUILD_MODNAME,
2498c2ecf20Sopenharmony_ci	.driver.owner =	THIS_MODULE,
2508c2ecf20Sopenharmony_ci	.id_table =	id_table,
2518c2ecf20Sopenharmony_ci	.probe =	virtrng_probe,
2528c2ecf20Sopenharmony_ci	.remove =	virtrng_remove,
2538c2ecf20Sopenharmony_ci	.scan =		virtrng_scan,
2548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
2558c2ecf20Sopenharmony_ci	.freeze =	virtrng_freeze,
2568c2ecf20Sopenharmony_ci	.restore =	virtrng_restore,
2578c2ecf20Sopenharmony_ci#endif
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cimodule_virtio_driver(virtio_rng_driver);
2618c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table);
2628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Virtio random number driver");
2638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
264