xref: /kernel/linux/linux-5.10/arch/um/drivers/random.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/* Copyright (C) 2005 - 2008 Jeff Dike <jdike@{linux.intel,addtoit}.com> */
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci/* Much of this ripped from drivers/char/hw_random.c, see there for other
48c2ecf20Sopenharmony_ci * copyright.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * This software may be used and distributed according to the terms
78c2ecf20Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/fs.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/miscdevice.h>
148c2ecf20Sopenharmony_ci#include <linux/hw_random.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
178c2ecf20Sopenharmony_ci#include <init.h>
188c2ecf20Sopenharmony_ci#include <irq_kern.h>
198c2ecf20Sopenharmony_ci#include <os.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * core module information
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci#define RNG_MODULE_NAME "hw_random"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/* Changed at init time, in the non-modular case, and at module load
278c2ecf20Sopenharmony_ci * time, in the module case.  Presumably, the module subsystem
288c2ecf20Sopenharmony_ci * protects against a module being loaded twice at the same time.
298c2ecf20Sopenharmony_ci */
308c2ecf20Sopenharmony_cistatic int random_fd = -1;
318c2ecf20Sopenharmony_cistatic struct hwrng hwrng;
328c2ecf20Sopenharmony_cistatic DECLARE_COMPLETION(have_data);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic int rng_dev_read(struct hwrng *rng, void *buf, size_t max, bool block)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	int ret;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	for (;;) {
398c2ecf20Sopenharmony_ci		ret = os_read_file(random_fd, buf, max);
408c2ecf20Sopenharmony_ci		if (block && ret == -EAGAIN) {
418c2ecf20Sopenharmony_ci			add_sigio_fd(random_fd);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci			ret = wait_for_completion_killable(&have_data);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci			ignore_sigio_fd(random_fd);
468c2ecf20Sopenharmony_ci			deactivate_fd(random_fd, RANDOM_IRQ);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci			if (ret < 0)
498c2ecf20Sopenharmony_ci				break;
508c2ecf20Sopenharmony_ci		} else {
518c2ecf20Sopenharmony_ci			break;
528c2ecf20Sopenharmony_ci		}
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return ret != -EAGAIN ? ret : 0;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic irqreturn_t random_interrupt(int irq, void *data)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	complete(&have_data);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * rng_init - initialize RNG module
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_cistatic int __init rng_init (void)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	int err;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0);
738c2ecf20Sopenharmony_ci	if (err < 0)
748c2ecf20Sopenharmony_ci		goto out;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	random_fd = err;
778c2ecf20Sopenharmony_ci	err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
788c2ecf20Sopenharmony_ci			     0, "random", NULL);
798c2ecf20Sopenharmony_ci	if (err)
808c2ecf20Sopenharmony_ci		goto err_out_cleanup_hw;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	sigio_broken(random_fd, 1);
838c2ecf20Sopenharmony_ci	hwrng.name = RNG_MODULE_NAME;
848c2ecf20Sopenharmony_ci	hwrng.read = rng_dev_read;
858c2ecf20Sopenharmony_ci	hwrng.quality = 1024;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	err = hwrng_register(&hwrng);
888c2ecf20Sopenharmony_ci	if (err) {
898c2ecf20Sopenharmony_ci		pr_err(RNG_MODULE_NAME " registering failed (%d)\n", err);
908c2ecf20Sopenharmony_ci		goto err_out_cleanup_hw;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ciout:
938c2ecf20Sopenharmony_ci	return err;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cierr_out_cleanup_hw:
968c2ecf20Sopenharmony_ci	os_close_file(random_fd);
978c2ecf20Sopenharmony_ci	random_fd = -1;
988c2ecf20Sopenharmony_ci	goto out;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/*
1028c2ecf20Sopenharmony_ci * rng_cleanup - shutdown RNG module
1038c2ecf20Sopenharmony_ci */
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic void cleanup(void)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	free_irq_by_fd(random_fd);
1088c2ecf20Sopenharmony_ci	os_close_file(random_fd);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic void __exit rng_cleanup(void)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	hwrng_unregister(&hwrng);
1148c2ecf20Sopenharmony_ci	os_close_file(random_fd);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cimodule_init (rng_init);
1188c2ecf20Sopenharmony_cimodule_exit (rng_cleanup);
1198c2ecf20Sopenharmony_ci__uml_exitcall(cleanup);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
1228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
123