162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Randomness driver for virtio
462306a36Sopenharmony_ci *  Copyright (C) 2007, 2008 Rusty Russell IBM Corporation
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <asm/barrier.h>
862306a36Sopenharmony_ci#include <linux/err.h>
962306a36Sopenharmony_ci#include <linux/hw_random.h>
1062306a36Sopenharmony_ci#include <linux/scatterlist.h>
1162306a36Sopenharmony_ci#include <linux/spinlock.h>
1262306a36Sopenharmony_ci#include <linux/virtio.h>
1362306a36Sopenharmony_ci#include <linux/virtio_rng.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic DEFINE_IDA(rng_index_ida);
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct virtrng_info {
2062306a36Sopenharmony_ci	struct hwrng hwrng;
2162306a36Sopenharmony_ci	struct virtqueue *vq;
2262306a36Sopenharmony_ci	char name[25];
2362306a36Sopenharmony_ci	int index;
2462306a36Sopenharmony_ci	bool hwrng_register_done;
2562306a36Sopenharmony_ci	bool hwrng_removed;
2662306a36Sopenharmony_ci	/* data transfer */
2762306a36Sopenharmony_ci	struct completion have_data;
2862306a36Sopenharmony_ci	unsigned int data_avail;
2962306a36Sopenharmony_ci	unsigned int data_idx;
3062306a36Sopenharmony_ci	/* minimal size returned by rng_buffer_size() */
3162306a36Sopenharmony_ci#if SMP_CACHE_BYTES < 32
3262306a36Sopenharmony_ci	u8 data[32];
3362306a36Sopenharmony_ci#else
3462306a36Sopenharmony_ci	u8 data[SMP_CACHE_BYTES];
3562306a36Sopenharmony_ci#endif
3662306a36Sopenharmony_ci};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void random_recv_done(struct virtqueue *vq)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	struct virtrng_info *vi = vq->vdev->priv;
4162306a36Sopenharmony_ci	unsigned int len;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
4462306a36Sopenharmony_ci	if (!virtqueue_get_buf(vi->vq, &len))
4562306a36Sopenharmony_ci		return;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	smp_store_release(&vi->data_avail, len);
4862306a36Sopenharmony_ci	complete(&vi->have_data);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic void request_entropy(struct virtrng_info *vi)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	struct scatterlist sg;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	reinit_completion(&vi->have_data);
5662306a36Sopenharmony_ci	vi->data_idx = 0;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	sg_init_one(&sg, vi->data, sizeof(vi->data));
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* There should always be room for one buffer. */
6162306a36Sopenharmony_ci	virtqueue_add_inbuf(vi->vq, &sg, 1, vi->data, GFP_KERNEL);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	virtqueue_kick(vi->vq);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic unsigned int copy_data(struct virtrng_info *vi, void *buf,
6762306a36Sopenharmony_ci			      unsigned int size)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	size = min_t(unsigned int, size, vi->data_avail);
7062306a36Sopenharmony_ci	memcpy(buf, vi->data + vi->data_idx, size);
7162306a36Sopenharmony_ci	vi->data_idx += size;
7262306a36Sopenharmony_ci	vi->data_avail -= size;
7362306a36Sopenharmony_ci	if (vi->data_avail == 0)
7462306a36Sopenharmony_ci		request_entropy(vi);
7562306a36Sopenharmony_ci	return size;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	int ret;
8162306a36Sopenharmony_ci	struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
8262306a36Sopenharmony_ci	unsigned int chunk;
8362306a36Sopenharmony_ci	size_t read;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (vi->hwrng_removed)
8662306a36Sopenharmony_ci		return -ENODEV;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	read = 0;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* copy available data */
9162306a36Sopenharmony_ci	if (smp_load_acquire(&vi->data_avail)) {
9262306a36Sopenharmony_ci		chunk = copy_data(vi, buf, size);
9362306a36Sopenharmony_ci		size -= chunk;
9462306a36Sopenharmony_ci		read += chunk;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (!wait)
9862306a36Sopenharmony_ci		return read;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	/* We have already copied available entropy,
10162306a36Sopenharmony_ci	 * so either size is 0 or data_avail is 0
10262306a36Sopenharmony_ci	 */
10362306a36Sopenharmony_ci	while (size != 0) {
10462306a36Sopenharmony_ci		/* data_avail is 0 but a request is pending */
10562306a36Sopenharmony_ci		ret = wait_for_completion_killable(&vi->have_data);
10662306a36Sopenharmony_ci		if (ret < 0)
10762306a36Sopenharmony_ci			return ret;
10862306a36Sopenharmony_ci		/* if vi->data_avail is 0, we have been interrupted
10962306a36Sopenharmony_ci		 * by a cleanup, but buffer stays in the queue
11062306a36Sopenharmony_ci		 */
11162306a36Sopenharmony_ci		if (vi->data_avail == 0)
11262306a36Sopenharmony_ci			return read;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		chunk = copy_data(vi, buf + read, size);
11562306a36Sopenharmony_ci		size -= chunk;
11662306a36Sopenharmony_ci		read += chunk;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return read;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic void virtio_cleanup(struct hwrng *rng)
12362306a36Sopenharmony_ci{
12462306a36Sopenharmony_ci	struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	complete(&vi->have_data);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int probe_common(struct virtio_device *vdev)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	int err, index;
13262306a36Sopenharmony_ci	struct virtrng_info *vi = NULL;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	vi = kzalloc(sizeof(struct virtrng_info), GFP_KERNEL);
13562306a36Sopenharmony_ci	if (!vi)
13662306a36Sopenharmony_ci		return -ENOMEM;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	vi->index = index = ida_simple_get(&rng_index_ida, 0, 0, GFP_KERNEL);
13962306a36Sopenharmony_ci	if (index < 0) {
14062306a36Sopenharmony_ci		err = index;
14162306a36Sopenharmony_ci		goto err_ida;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	sprintf(vi->name, "virtio_rng.%d", index);
14462306a36Sopenharmony_ci	init_completion(&vi->have_data);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	vi->hwrng = (struct hwrng) {
14762306a36Sopenharmony_ci		.read = virtio_read,
14862306a36Sopenharmony_ci		.cleanup = virtio_cleanup,
14962306a36Sopenharmony_ci		.priv = (unsigned long)vi,
15062306a36Sopenharmony_ci		.name = vi->name,
15162306a36Sopenharmony_ci	};
15262306a36Sopenharmony_ci	vdev->priv = vi;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	/* We expect a single virtqueue. */
15562306a36Sopenharmony_ci	vi->vq = virtio_find_single_vq(vdev, random_recv_done, "input");
15662306a36Sopenharmony_ci	if (IS_ERR(vi->vq)) {
15762306a36Sopenharmony_ci		err = PTR_ERR(vi->vq);
15862306a36Sopenharmony_ci		goto err_find;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	virtio_device_ready(vdev);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* we always have a pending entropy request */
16462306a36Sopenharmony_ci	request_entropy(vi);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cierr_find:
16962306a36Sopenharmony_ci	ida_simple_remove(&rng_index_ida, index);
17062306a36Sopenharmony_cierr_ida:
17162306a36Sopenharmony_ci	kfree(vi);
17262306a36Sopenharmony_ci	return err;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistatic void remove_common(struct virtio_device *vdev)
17662306a36Sopenharmony_ci{
17762306a36Sopenharmony_ci	struct virtrng_info *vi = vdev->priv;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	vi->hwrng_removed = true;
18062306a36Sopenharmony_ci	vi->data_avail = 0;
18162306a36Sopenharmony_ci	vi->data_idx = 0;
18262306a36Sopenharmony_ci	complete(&vi->have_data);
18362306a36Sopenharmony_ci	if (vi->hwrng_register_done)
18462306a36Sopenharmony_ci		hwrng_unregister(&vi->hwrng);
18562306a36Sopenharmony_ci	virtio_reset_device(vdev);
18662306a36Sopenharmony_ci	vdev->config->del_vqs(vdev);
18762306a36Sopenharmony_ci	ida_simple_remove(&rng_index_ida, vi->index);
18862306a36Sopenharmony_ci	kfree(vi);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int virtrng_probe(struct virtio_device *vdev)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	return probe_common(vdev);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic void virtrng_remove(struct virtio_device *vdev)
19762306a36Sopenharmony_ci{
19862306a36Sopenharmony_ci	remove_common(vdev);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void virtrng_scan(struct virtio_device *vdev)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct virtrng_info *vi = vdev->priv;
20462306a36Sopenharmony_ci	int err;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	err = hwrng_register(&vi->hwrng);
20762306a36Sopenharmony_ci	if (!err)
20862306a36Sopenharmony_ci		vi->hwrng_register_done = true;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
21262306a36Sopenharmony_cistatic int virtrng_freeze(struct virtio_device *vdev)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	remove_common(vdev);
21562306a36Sopenharmony_ci	return 0;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic int virtrng_restore(struct virtio_device *vdev)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	int err;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	err = probe_common(vdev);
22362306a36Sopenharmony_ci	if (!err) {
22462306a36Sopenharmony_ci		struct virtrng_info *vi = vdev->priv;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		/*
22762306a36Sopenharmony_ci		 * Set hwrng_removed to ensure that virtio_read()
22862306a36Sopenharmony_ci		 * does not block waiting for data before the
22962306a36Sopenharmony_ci		 * registration is complete.
23062306a36Sopenharmony_ci		 */
23162306a36Sopenharmony_ci		vi->hwrng_removed = true;
23262306a36Sopenharmony_ci		err = hwrng_register(&vi->hwrng);
23362306a36Sopenharmony_ci		if (!err) {
23462306a36Sopenharmony_ci			vi->hwrng_register_done = true;
23562306a36Sopenharmony_ci			vi->hwrng_removed = false;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	return err;
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci#endif
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_cistatic const struct virtio_device_id id_table[] = {
24462306a36Sopenharmony_ci	{ VIRTIO_ID_RNG, VIRTIO_DEV_ANY_ID },
24562306a36Sopenharmony_ci	{ 0 },
24662306a36Sopenharmony_ci};
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic struct virtio_driver virtio_rng_driver = {
24962306a36Sopenharmony_ci	.driver.name =	KBUILD_MODNAME,
25062306a36Sopenharmony_ci	.driver.owner =	THIS_MODULE,
25162306a36Sopenharmony_ci	.id_table =	id_table,
25262306a36Sopenharmony_ci	.probe =	virtrng_probe,
25362306a36Sopenharmony_ci	.remove =	virtrng_remove,
25462306a36Sopenharmony_ci	.scan =		virtrng_scan,
25562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
25662306a36Sopenharmony_ci	.freeze =	virtrng_freeze,
25762306a36Sopenharmony_ci	.restore =	virtrng_restore,
25862306a36Sopenharmony_ci#endif
25962306a36Sopenharmony_ci};
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_cimodule_virtio_driver(virtio_rng_driver);
26262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table);
26362306a36Sopenharmony_ciMODULE_DESCRIPTION("Virtio random number driver");
26462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
265