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                        reactivate_fd(random_fd, RANDOM_IRQ);
  77                        add_sigio_fd(random_fd);
  78
  79                        add_wait_queue(&host_read_wait, &wait);
  80                        set_current_state(TASK_INTERRUPTIBLE);
  81
  82                        schedule();
  83                        remove_wait_queue(&host_read_wait, &wait);
  84
  85                        if (atomic_dec_and_test(&host_sleep_count)) {
  86                                ignore_sigio_fd(random_fd);
  87                                deactivate_fd(random_fd, RANDOM_IRQ);
  88                        }
  89                }
  90                else
  91                        return n;
  92
  93                if (signal_pending (current))
  94                        return ret ? : -ERESTARTSYS;
  95        }
  96        return ret;
  97}
  98
  99static const struct file_operations rng_chrdev_ops = {
 100        .owner          = THIS_MODULE,
 101        .open           = rng_dev_open,
 102        .read           = rng_dev_read,
 103        .llseek         = noop_llseek,
 104};
 105
 106/* rng_init shouldn't be called more than once at boot time */
 107static struct miscdevice rng_miscdev = {
 108        RNG_MISCDEV_MINOR,
 109        RNG_MODULE_NAME,
 110        &rng_chrdev_ops,
 111};
 112
 113static irqreturn_t random_interrupt(int irq, void *data)
 114{
 115        wake_up(&host_read_wait);
 116
 117        return IRQ_HANDLED;
 118}
 119
 120/*
 121 * rng_init - initialize RNG module
 122 */
 123static int __init rng_init (void)
 124{
 125        int err;
 126
 127        err = os_open_file("/dev/random", of_read(OPENFLAGS()), 0);
 128        if (err < 0)
 129                goto out;
 130
 131        random_fd = err;
 132
 133        err = um_request_irq(RANDOM_IRQ, random_fd, IRQ_READ, random_interrupt,
 134                             0, "random", NULL);
 135        if (err)
 136                goto err_out_cleanup_hw;
 137
 138        sigio_broken(random_fd, 1);
 139
 140        err = misc_register (&rng_miscdev);
 141        if (err) {
 142                printk (KERN_ERR RNG_MODULE_NAME ": misc device register "
 143                        "failed\n");
 144                goto err_out_cleanup_hw;
 145        }
 146out:
 147        return err;
 148
 149err_out_cleanup_hw:
 150        os_close_file(random_fd);
 151        random_fd = -1;
 152        goto out;
 153}
 154
 155/*
 156 * rng_cleanup - shutdown RNG module
 157 */
 158
 159static void cleanup(void)
 160{
 161        free_irq_by_fd(random_fd);
 162        os_close_file(random_fd);
 163}
 164
 165static void __exit rng_cleanup(void)
 166{
 167        os_close_file(random_fd);
 168        misc_deregister (&rng_miscdev);
 169}
 170
 171module_init (rng_init);
 172module_exit (rng_cleanup);
 173__uml_exitcall(cleanup);
 174
 175MODULE_DESCRIPTION("UML Host Random Number Generator (RNG) driver");
 176MODULE_LICENSE("GPL");
 177