13d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike/* Copyright (C) 2005 - 2008 Jeff Dike <jdike@{linux.intel,addtoit}.com> */ 23d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike 3567b56508f7367e161d6d861ef214a900ab45ce9Paolo 'Blaisorblade' Giarrusso/* Much of this ripped from drivers/char/hw_random.c, see there for other 4567b56508f7367e161d6d861ef214a900ab45ce9Paolo 'Blaisorblade' Giarrusso * copyright. 5567b56508f7367e161d6d861ef214a900ab45ce9Paolo 'Blaisorblade' Giarrusso * 6567b56508f7367e161d6d861ef214a900ab45ce9Paolo 'Blaisorblade' Giarrusso * This software may be used and distributed according to the terms 7567b56508f7367e161d6d861ef214a900ab45ce9Paolo 'Blaisorblade' Giarrusso * of the GNU General Public License, incorporated herein by reference. 8567b56508f7367e161d6d861ef214a900ab45ce9Paolo 'Blaisorblade' Giarrusso */ 98192ab42bf60e1e9b7efa046990e9cc5e4a95cf4Jeff Dike#include <linux/sched.h> 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fs.h> 125d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike#include <linux/interrupt.h> 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/miscdevice.h> 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h> 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h> 165d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike#include "irq_kern.h" 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "os.h" 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * core module and version information 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RNG_VERSION "1.0.0" 235d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike#define RNG_MODULE_NAME "hw_random" 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RNG_MISCDEV_MINOR 183 /* official */ 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 27730760e90a173ef81f89beed2f1dad2fab310f68Jeff Dike/* Changed at init time, in the non-modular case, and at module load 28730760e90a173ef81f89beed2f1dad2fab310f68Jeff Dike * time, in the module case. Presumably, the module subsystem 29730760e90a173ef81f89beed2f1dad2fab310f68Jeff Dike * protects against a module being loaded twice at the same time. 30730760e90a173ef81f89beed2f1dad2fab310f68Jeff Dike */ 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int random_fd = -1; 325d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dikestatic DECLARE_WAIT_QUEUE_HEAD(host_read_wait); 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rng_dev_open (struct inode *inode, struct file *filp) 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* enforce read-only access to this chrdev */ 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((filp->f_mode & FMODE_READ) == 0) 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 393d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike if ((filp->f_mode & FMODE_WRITE) != 0) 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -EINVAL; 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 455d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dikestatic atomic_t host_sleep_count = ATOMIC_INIT(0); 465d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike 471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, 483d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike loff_t *offp) 491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 503d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike u32 data; 513d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike int n, ret = 0, have_data; 523d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike 533d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike while (size) { 543d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike n = os_read_file(random_fd, &data, sizeof(data)); 553d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike if (n > 0) { 563d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike have_data = n; 573d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike while (have_data && size) { 583d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike if (put_user((u8) data, buf++)) { 593d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike ret = ret ? : -EFAULT; 603d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike break; 613d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike } 623d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike size--; 633d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike ret++; 643d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike have_data--; 653d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike data >>= 8; 663d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike } 673d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike } 683d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike else if (n == -EAGAIN) { 695d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike DECLARE_WAITQUEUE(wait, current); 705d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike 713d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike if (filp->f_flags & O_NONBLOCK) 723d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike return ret ? : -EAGAIN; 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 745d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike atomic_inc(&host_sleep_count); 755d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike reactivate_fd(random_fd, RANDOM_IRQ); 765d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike add_sigio_fd(random_fd); 775d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike 785d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike add_wait_queue(&host_read_wait, &wait); 795d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike set_task_state(current, TASK_INTERRUPTIBLE); 805d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike 815d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike schedule(); 825d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike set_task_state(current, TASK_RUNNING); 835d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike remove_wait_queue(&host_read_wait, &wait); 845d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike 855d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike if (atomic_dec_and_test(&host_sleep_count)) { 865d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike ignore_sigio_fd(random_fd); 875d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike deactivate_fd(random_fd, RANDOM_IRQ); 885d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike } 893d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike } 903d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike else 913d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike return n; 923d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (signal_pending (current)) 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret ? : -ERESTARTSYS; 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return ret; 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 995e7672ec3f059f764fcc5c78216e24bb16c44dbaJeff Dikestatic const struct file_operations rng_chrdev_ops = { 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .owner = THIS_MODULE, 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .open = rng_dev_open, 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .read = rng_dev_read, 1036038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann .llseek = noop_llseek, 1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 10699b0278f95fc9d55adf65133dc678167a88b632aJeff Dike/* rng_init shouldn't be called more than once at boot time */ 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct miscdevice rng_miscdev = { 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds RNG_MISCDEV_MINOR, 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds RNG_MODULE_NAME, 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds &rng_chrdev_ops, 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1135d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dikestatic irqreturn_t random_interrupt(int irq, void *data) 1145d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike{ 1155d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike wake_up(&host_read_wait); 1165d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike 1175d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike return IRQ_HANDLED; 1185d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike} 1195d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike 1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rng_init - initialize RNG module 1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init rng_init (void) 1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int err; 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1273d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0); 1283d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike if (err < 0) 1293d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike goto out; 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1313d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike random_fd = err; 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1335d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt, 134c0b79a90b1556a7e51d7a49a655eb60306f6258dYong Zhang IRQF_SAMPLE_RANDOM, "random", 1355d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike NULL); 1363d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike if (err) 1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto err_out_cleanup_hw; 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1395d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike sigio_broken(random_fd, 1); 1405d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds err = misc_register (&rng_miscdev); 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (err) { 1433d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike printk (KERN_ERR RNG_MODULE_NAME ": misc device register " 1443d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike "failed\n"); 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto err_out_cleanup_hw; 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1473d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dikeout: 1483d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike return err; 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1503d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dikeerr_out_cleanup_hw: 1515d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike os_close_file(random_fd); 1523d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike random_fd = -1; 1533d88958e01e71bb14a367db75f12f7a59c068f02Jeff Dike goto out; 1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rng_cleanup - shutdown RNG module 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit rng_cleanup (void) 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1615d33e4d7fd9a52d2673e5c730eab81856e100a74Jeff Dike os_close_file(random_fd); 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds misc_deregister (&rng_miscdev); 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init (rng_init); 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit (rng_cleanup); 167567b56508f7367e161d6d861ef214a900ab45ce9Paolo 'Blaisorblade' Giarrusso 168567b56508f7367e161d6d861ef214a900ab45ce9Paolo 'Blaisorblade' GiarrussoMODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver"); 169567b56508f7367e161d6d861ef214a900ab45ce9Paolo 'Blaisorblade' GiarrussoMODULE_LICENSE("GPL"); 170