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