linux/arch/um/drivers/random.c
<<
>>
Prefs
   1/* Copyright (C) 2005 - 2008 Jeff Dike <jdike@{linux.intel,addtoit}.com> */
   2
   3/* Much of this ripped from drivers/char/hw_random.c, see there for other
   4 * copyright.
   5 *
   6 * This software may be used and distributed according to the terms
   7 * of the GNU General Public License, incorporated herein by reference.
   8 */
   9#include <linux/sched/signal.h>
  10#include <linux/module.h>
  11#include <linux/fs.h>
  12#include <linux/interrupt.h>
  13#include <linux/miscdevice.h>
  14#include <linux/delay.h>
  15#include <linux/uaccess.h>
  16#include <init.h>
  17#include <irq_kern.h>
  18#include <os.h>
  19
  20/*
  21 * core module and version information
  22 */
  23#define RNG_VERSION "1.0.0"
  24#define RNG_MODULE_NAME "hw_random"
  25
  26#define RNG_MISCDEV_MINOR               183 /* official */
  27
  28/* Changed at init time, in the non-modular case, and at module load
  29 * time, in the module case.  Presumably, the module subsystem
  30 * protects against a module being loaded twice at the same time.
  31 */
  32static int random_fd = -1;
  33static DECLARE_WAIT_QUEUE_HEAD(host_read_wait);
  34
  35static int rng_dev_open (struct inode *inode, struct file *filp)
  36{
  37        /* enforce read-only access to this chrdev */
  38        if ((filp->f_mode & FMODE_READ) == 0)
  39                return -EINVAL;
  40        if ((filp->f_mode & FMODE_WRITE) != 0)
  41                return -EINVAL;
  42
  43        return 0;
  44}
  45
  46static atomic_t host_sleep_count = ATOMIC_INIT(0);
  47
  48static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
  49                             loff_t *offp)
  50{
  51        u32 data;
  52        int n, ret = 0, have_data;
  53
  54        while (size) {
  55                n = os_read_file(random_fd, &data, sizeof(data));
  56                if (n > 0) {
  57                        have_data = n;
  58                        while (have_data && size) {
  59                                if (put_user((u8) data, buf++)) {
  60                                        ret = ret ? : -EFAULT;
  61                                        break;
  62                                }
  63                                size--;
  64                                ret++;
  65                                have_data--;
  66                                data >>= 8;
  67                        }
  68                }
  69                else if (n == -EAGAIN) {
  70                        DECLARE_WAITQUEUE(wait, current);
  71
  72                        if (filp->f_flags & O_NONBLOCK)
  73                                return ret ? : -EAGAIN;
  74
  75                        atomic_inc(&host_sleep_count);
  76                        add_sigio_fd(random_fd);
  77
  78                        add_wait_queue(&host_read_wait, &wait);
  79                        set_current_state(TASK_INTERRUPTIBLE);
  80
  81                        schedule();
  82                        remove_wait_queue(&host_read_wait, &wait);
  83
  84                        if (atomic_dec_and_test(&host_sleep_count)) {
  85                                ignore_sigio_fd(random_fd);
  86                                deactivate_fd(random_fd, RANDOM_IRQ);
  87                        }
  88                }
  89                else
  90                        return n;
  91
  92                if (signal_pending (current))
  93                        return ret ? : -ERESTARTSYS;
  94        }
  95        return ret;
  96}
  97
  98static const struct file_operations rng_chrdev_ops = {
  99        .owner          = THIS_MODULE,
 100        .open           = rng_dev_open,
 101        .read           = rng_dev_read,
 102        .llseek         = noop_llseek,
 103};
 104
 105/* rng_init shouldn't be called more than once at boot time */
 106static struct miscdevice rng_miscdev = {
 107        RNG_MISCDEV_MINOR,
 108        RNG_MODULE_NAME,
 109        &rng_chrdev_ops,
 110};
 111
 112static irqreturn_t random_interrupt(int irq, void *data)
 113{
 114        wake_up(&host_read_wait);
 115
 116        return IRQ_HANDLED;
 117}
 118
 119/*
 120 * rng_init - initialize RNG module
 121 */
 122static int __init rng_init (void)
 123{
 124        int err;
 125
 126        err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0);
 127        if (err < 0)
 128                goto out;
 129
 130        random_fd = err;
 131
 132        err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
 133                             0, "random", NULL);
 134        if (err)
 135                goto err_out_cleanup_hw;
 136
 137        sigio_broken(random_fd, 1);
 138
 139        err = misc_register (&rng_miscdev);
 140        if (err) {
 141                printk (KERN_ERR RNG_MODULE_NAME ": misc device register "
 142                        "failed\n");
 143                goto err_out_cleanup_hw;
 144        }
 145out:
 146        return err;
 147
 148err_out_cleanup_hw:
 149        os_close_file(random_fd);
 150        random_fd = -1;
 151        goto out;
 152}
 153
 154/*
 155 * rng_cleanup - shutdown RNG module
 156 */
 157
 158static void cleanup(void)
 159{
 160        free_irq_by_fd(random_fd);
 161        os_close_file(random_fd);
 162}
 163
 164static void __exit rng_cleanup(void)
 165{
 166        os_close_file(random_fd);
 167        misc_deregister (&rng_miscdev);
 168}
 169
 170module_init (rng_init);
 171module_exit (rng_cleanup);
 172__uml_exitcall(cleanup);
 173
 174MODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
 175MODULE_LICENSE("GPL");
 176