xref: /kernel/linux/linux-6.6/arch/um/drivers/random.c (revision 62306a36)
162306a36Sopenharmony_ci/* Copyright (C) 2005 - 2008 Jeff Dike <jdike@{linux.intel,addtoit}.com> */
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/* Much of this ripped from drivers/char/hw_random.c, see there for other
462306a36Sopenharmony_ci * copyright.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This software may be used and distributed according to the terms
762306a36Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci#include <linux/sched/signal.h>
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/miscdevice.h>
1462306a36Sopenharmony_ci#include <linux/hw_random.h>
1562306a36Sopenharmony_ci#include <linux/delay.h>
1662306a36Sopenharmony_ci#include <linux/uaccess.h>
1762306a36Sopenharmony_ci#include <init.h>
1862306a36Sopenharmony_ci#include <irq_kern.h>
1962306a36Sopenharmony_ci#include <os.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/*
2262306a36Sopenharmony_ci * core module information
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci#define RNG_MODULE_NAME "hw_random"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* Changed at init time, in the non-modular case, and at module load
2762306a36Sopenharmony_ci * time, in the module case.  Presumably, the module subsystem
2862306a36Sopenharmony_ci * protects against a module being loaded twice at the same time.
2962306a36Sopenharmony_ci */
3062306a36Sopenharmony_cistatic int random_fd = -1;
3162306a36Sopenharmony_cistatic struct hwrng hwrng;
3262306a36Sopenharmony_cistatic DECLARE_COMPLETION(have_data);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int rng_dev_read(struct hwrng *rng, void *buf, size_t max, bool block)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	int ret;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	for (;;) {
3962306a36Sopenharmony_ci		ret = os_read_file(random_fd, buf, max);
4062306a36Sopenharmony_ci		if (block && ret == -EAGAIN) {
4162306a36Sopenharmony_ci			add_sigio_fd(random_fd);
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci			ret = wait_for_completion_killable(&have_data);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci			ignore_sigio_fd(random_fd);
4662306a36Sopenharmony_ci			deactivate_fd(random_fd, RANDOM_IRQ);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci			if (ret < 0)
4962306a36Sopenharmony_ci				break;
5062306a36Sopenharmony_ci		} else {
5162306a36Sopenharmony_ci			break;
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return ret != -EAGAIN ? ret : 0;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic irqreturn_t random_interrupt(int irq, void *data)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	complete(&have_data);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return IRQ_HANDLED;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/*
6662306a36Sopenharmony_ci * rng_init - initialize RNG module
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cistatic int __init rng_init (void)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int err;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0);
7362306a36Sopenharmony_ci	if (err < 0)
7462306a36Sopenharmony_ci		goto out;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	random_fd = err;
7762306a36Sopenharmony_ci	err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
7862306a36Sopenharmony_ci			     0, "random", NULL);
7962306a36Sopenharmony_ci	if (err < 0)
8062306a36Sopenharmony_ci		goto err_out_cleanup_hw;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	sigio_broken(random_fd);
8362306a36Sopenharmony_ci	hwrng.name = RNG_MODULE_NAME;
8462306a36Sopenharmony_ci	hwrng.read = rng_dev_read;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	err = hwrng_register(&hwrng);
8762306a36Sopenharmony_ci	if (err) {
8862306a36Sopenharmony_ci		pr_err(RNG_MODULE_NAME " registering failed (%d)\n", err);
8962306a36Sopenharmony_ci		goto err_out_cleanup_hw;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ciout:
9262306a36Sopenharmony_ci	return err;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cierr_out_cleanup_hw:
9562306a36Sopenharmony_ci	os_close_file(random_fd);
9662306a36Sopenharmony_ci	random_fd = -1;
9762306a36Sopenharmony_ci	goto out;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci/*
10162306a36Sopenharmony_ci * rng_cleanup - shutdown RNG module
10262306a36Sopenharmony_ci */
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void cleanup(void)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	free_irq_by_fd(random_fd);
10762306a36Sopenharmony_ci	os_close_file(random_fd);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void __exit rng_cleanup(void)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	hwrng_unregister(&hwrng);
11362306a36Sopenharmony_ci	os_close_file(random_fd);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cimodule_init (rng_init);
11762306a36Sopenharmony_cimodule_exit (rng_cleanup);
11862306a36Sopenharmony_ci__uml_exitcall(cleanup);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciMODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
12162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
122