13d88958eSJeff Dike /* Copyright (C) 2005 - 2008 Jeff Dike <jdike@{linux.intel,addtoit}.com> */
23d88958eSJeff Dike
3567b5650SPaolo 'Blaisorblade' Giarrusso /* Much of this ripped from drivers/char/hw_random.c, see there for other
4567b5650SPaolo 'Blaisorblade' Giarrusso * copyright.
5567b5650SPaolo 'Blaisorblade' Giarrusso *
6567b5650SPaolo 'Blaisorblade' Giarrusso * This software may be used and distributed according to the terms
7567b5650SPaolo 'Blaisorblade' Giarrusso * of the GNU General Public License, incorporated herein by reference.
8567b5650SPaolo 'Blaisorblade' Giarrusso */
9174cd4b1SIngo Molnar #include <linux/sched/signal.h>
101da177e4SLinus Torvalds #include <linux/module.h>
111da177e4SLinus Torvalds #include <linux/fs.h>
125d33e4d7SJeff Dike #include <linux/interrupt.h>
131da177e4SLinus Torvalds #include <linux/miscdevice.h>
1472d3e093SChristopher Obbard #include <linux/hw_random.h>
151da177e4SLinus Torvalds #include <linux/delay.h>
167c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
17ff6a1798SAnton Ivanov #include <init.h>
1837185b33SAl Viro #include <irq_kern.h>
1937185b33SAl Viro #include <os.h>
201da177e4SLinus Torvalds
211da177e4SLinus Torvalds /*
2272d3e093SChristopher Obbard * core module information
231da177e4SLinus Torvalds */
245d33e4d7SJeff Dike #define RNG_MODULE_NAME "hw_random"
251da177e4SLinus Torvalds
26730760e9SJeff Dike /* Changed at init time, in the non-modular case, and at module load
27730760e9SJeff Dike * time, in the module case. Presumably, the module subsystem
28730760e9SJeff Dike * protects against a module being loaded twice at the same time.
29730760e9SJeff Dike */
301da177e4SLinus Torvalds static int random_fd = -1;
31*9e70cbd1SChristopher Obbard static struct hwrng hwrng;
3272d3e093SChristopher Obbard static DECLARE_COMPLETION(have_data);
331da177e4SLinus Torvalds
rng_dev_read(struct hwrng * rng,void * buf,size_t max,bool block)3472d3e093SChristopher Obbard static int rng_dev_read(struct hwrng *rng, void *buf, size_t max, bool block)
351da177e4SLinus Torvalds {
3672d3e093SChristopher Obbard int ret;
371da177e4SLinus Torvalds
3872d3e093SChristopher Obbard for (;;) {
3972d3e093SChristopher Obbard ret = os_read_file(random_fd, buf, max);
4072d3e093SChristopher Obbard if (block && ret == -EAGAIN) {
415d33e4d7SJeff Dike add_sigio_fd(random_fd);
425d33e4d7SJeff Dike
4372d3e093SChristopher Obbard ret = wait_for_completion_killable(&have_data);
445d33e4d7SJeff Dike
455d33e4d7SJeff Dike ignore_sigio_fd(random_fd);
465d33e4d7SJeff Dike deactivate_fd(random_fd, RANDOM_IRQ);
473d88958eSJeff Dike
4872d3e093SChristopher Obbard if (ret < 0)
4972d3e093SChristopher Obbard break;
5072d3e093SChristopher Obbard } else {
5172d3e093SChristopher Obbard break;
521da177e4SLinus Torvalds }
531da177e4SLinus Torvalds }
541da177e4SLinus Torvalds
5572d3e093SChristopher Obbard return ret != -EAGAIN ? ret : 0;
5672d3e093SChristopher Obbard }
571da177e4SLinus Torvalds
random_interrupt(int irq,void * data)585d33e4d7SJeff Dike static irqreturn_t random_interrupt(int irq, void *data)
595d33e4d7SJeff Dike {
6072d3e093SChristopher Obbard complete(&have_data);
615d33e4d7SJeff Dike
625d33e4d7SJeff Dike return IRQ_HANDLED;
635d33e4d7SJeff Dike }
645d33e4d7SJeff Dike
651da177e4SLinus Torvalds /*
661da177e4SLinus Torvalds * rng_init - initialize RNG module
671da177e4SLinus Torvalds */
rng_init(void)681da177e4SLinus Torvalds static int __init rng_init (void)
691da177e4SLinus Torvalds {
701da177e4SLinus Torvalds int err;
711da177e4SLinus Torvalds
721da177e4SLinus Torvalds err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0);
731da177e4SLinus Torvalds if (err < 0)
741da177e4SLinus Torvalds goto out;
751da177e4SLinus Torvalds
761da177e4SLinus Torvalds random_fd = err;
775d33e4d7SJeff Dike err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
78aab94460STheodore Ts'o 0, "random", NULL);
7936d46a59SJohannes Berg if (err < 0)
801da177e4SLinus Torvalds goto err_out_cleanup_hw;
811da177e4SLinus Torvalds
822fccfcc0SJohannes Berg sigio_broken(random_fd);
8372d3e093SChristopher Obbard hwrng.name = RNG_MODULE_NAME;
8472d3e093SChristopher Obbard hwrng.read = rng_dev_read;
855d33e4d7SJeff Dike
8672d3e093SChristopher Obbard err = hwrng_register(&hwrng);
871da177e4SLinus Torvalds if (err) {
8872d3e093SChristopher Obbard pr_err(RNG_MODULE_NAME " registering failed (%d)\n", err);
891da177e4SLinus Torvalds goto err_out_cleanup_hw;
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds out:
921da177e4SLinus Torvalds return err;
931da177e4SLinus Torvalds
941da177e4SLinus Torvalds err_out_cleanup_hw:
955d33e4d7SJeff Dike os_close_file(random_fd);
961da177e4SLinus Torvalds random_fd = -1;
971da177e4SLinus Torvalds goto out;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds
1001da177e4SLinus Torvalds /*
1011da177e4SLinus Torvalds * rng_cleanup - shutdown RNG module
1021da177e4SLinus Torvalds */
103ff6a1798SAnton Ivanov
cleanup(void)104ff6a1798SAnton Ivanov static void cleanup(void)
105ff6a1798SAnton Ivanov {
106ff6a1798SAnton Ivanov free_irq_by_fd(random_fd);
107ff6a1798SAnton Ivanov os_close_file(random_fd);
108ff6a1798SAnton Ivanov }
109ff6a1798SAnton Ivanov
rng_cleanup(void)1101da177e4SLinus Torvalds static void __exit rng_cleanup(void)
1111da177e4SLinus Torvalds {
11272d3e093SChristopher Obbard hwrng_unregister(&hwrng);
1135d33e4d7SJeff Dike os_close_file(random_fd);
1141da177e4SLinus Torvalds }
1151da177e4SLinus Torvalds
1161da177e4SLinus Torvalds module_init (rng_init);
1171da177e4SLinus Torvalds module_exit (rng_cleanup);
118ff6a1798SAnton Ivanov __uml_exitcall(cleanup);
119567b5650SPaolo 'Blaisorblade' Giarrusso
120567b5650SPaolo 'Blaisorblade' Giarrusso MODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
121567b5650SPaolo 'Blaisorblade' Giarrusso MODULE_LICENSE("GPL");
122