linux/drivers/char/hw_random/core.c
<<
>>
Prefs
   1/*
   2        Added support for the AMD Geode LX RNG
   3        (c) Copyright 2004-2005 Advanced Micro Devices, Inc.
   4
   5        derived from
   6
   7        Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
   8        (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
   9
  10        derived from
  11
  12        Hardware driver for the AMD 768 Random Number Generator (RNG)
  13        (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
  14
  15        derived from
  16
  17        Hardware driver for Intel i810 Random Number Generator (RNG)
  18        Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
  19        Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
  20
  21        Added generic RNG API
  22        Copyright 2006 Michael Buesch <m@bues.ch>
  23        Copyright 2005 (c) MontaVista Software, Inc.
  24
  25        Please read Documentation/hw_random.txt for details on use.
  26
  27        ----------------------------------------------------------
  28        This software may be used and distributed according to the terms
  29        of the GNU General Public License, incorporated herein by reference.
  30
  31 */
  32
  33
  34#include <linux/device.h>
  35#include <linux/hw_random.h>
  36#include <linux/module.h>
  37#include <linux/kernel.h>
  38#include <linux/fs.h>
  39#include <linux/sched.h>
  40#include <linux/init.h>
  41#include <linux/miscdevice.h>
  42#include <linux/delay.h>
  43#include <asm/uaccess.h>
  44
  45
  46#define RNG_MODULE_NAME         "hw_random"
  47#define PFX                     RNG_MODULE_NAME ": "
  48#define RNG_MISCDEV_MINOR       183 /* official */
  49
  50
  51static struct hwrng *current_rng;
  52static LIST_HEAD(rng_list);
  53static DEFINE_MUTEX(rng_mutex);
  54static int data_avail;
  55static u8 rng_buffer[SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES]
  56        __cacheline_aligned;
  57
  58static inline int hwrng_init(struct hwrng *rng)
  59{
  60        if (!rng->init)
  61                return 0;
  62        return rng->init(rng);
  63}
  64
  65static inline void hwrng_cleanup(struct hwrng *rng)
  66{
  67        if (rng && rng->cleanup)
  68                rng->cleanup(rng);
  69}
  70
  71static int rng_dev_open(struct inode *inode, struct file *filp)
  72{
  73        /* enforce read-only access to this chrdev */
  74        if ((filp->f_mode & FMODE_READ) == 0)
  75                return -EINVAL;
  76        if (filp->f_mode & FMODE_WRITE)
  77                return -EINVAL;
  78        return 0;
  79}
  80
  81static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
  82                        int wait) {
  83        int present;
  84
  85        if (rng->read)
  86                return rng->read(rng, (void *)buffer, size, wait);
  87
  88        if (rng->data_present)
  89                present = rng->data_present(rng, wait);
  90        else
  91                present = 1;
  92
  93        if (present)
  94                return rng->data_read(rng, (u32 *)buffer);
  95
  96        return 0;
  97}
  98
  99static ssize_t rng_dev_read(struct file *filp, char __user *buf,
 100                            size_t size, loff_t *offp)
 101{
 102        ssize_t ret = 0;
 103        int err = 0;
 104        int bytes_read, len;
 105
 106        while (size) {
 107                if (mutex_lock_interruptible(&rng_mutex)) {
 108                        err = -ERESTARTSYS;
 109                        goto out;
 110                }
 111
 112                if (!current_rng) {
 113                        err = -ENODEV;
 114                        goto out_unlock;
 115                }
 116
 117                if (!data_avail) {
 118                        bytes_read = rng_get_data(current_rng, rng_buffer,
 119                                sizeof(rng_buffer),
 120                                !(filp->f_flags & O_NONBLOCK));
 121                        if (bytes_read < 0) {
 122                                err = bytes_read;
 123                                goto out_unlock;
 124                        }
 125                        data_avail = bytes_read;
 126                }
 127
 128                if (!data_avail) {
 129                        if (filp->f_flags & O_NONBLOCK) {
 130                                err = -EAGAIN;
 131                                goto out_unlock;
 132                        }
 133                } else {
 134                        len = data_avail;
 135                        if (len > size)
 136                                len = size;
 137
 138                        data_avail -= len;
 139
 140                        if (copy_to_user(buf + ret, rng_buffer + data_avail,
 141                                                                len)) {
 142                                err = -EFAULT;
 143                                goto out_unlock;
 144                        }
 145
 146                        size -= len;
 147                        ret += len;
 148                }
 149
 150                mutex_unlock(&rng_mutex);
 151
 152                if (need_resched())
 153                        schedule_timeout_interruptible(1);
 154
 155                if (signal_pending(current)) {
 156                        err = -ERESTARTSYS;
 157                        goto out;
 158                }
 159        }
 160out:
 161        return ret ? : err;
 162out_unlock:
 163        mutex_unlock(&rng_mutex);
 164        goto out;
 165}
 166
 167
 168static const struct file_operations rng_chrdev_ops = {
 169        .owner          = THIS_MODULE,
 170        .open           = rng_dev_open,
 171        .read           = rng_dev_read,
 172        .llseek         = noop_llseek,
 173};
 174
 175static struct miscdevice rng_miscdev = {
 176        .minor          = RNG_MISCDEV_MINOR,
 177        .name           = RNG_MODULE_NAME,
 178        .nodename       = "hwrng",
 179        .fops           = &rng_chrdev_ops,
 180};
 181
 182
 183static ssize_t hwrng_attr_current_store(struct device *dev,
 184                                        struct device_attribute *attr,
 185                                        const char *buf, size_t len)
 186{
 187        int err;
 188        struct hwrng *rng;
 189
 190        err = mutex_lock_interruptible(&rng_mutex);
 191        if (err)
 192                return -ERESTARTSYS;
 193        err = -ENODEV;
 194        list_for_each_entry(rng, &rng_list, list) {
 195                if (strcmp(rng->name, buf) == 0) {
 196                        if (rng == current_rng) {
 197                                err = 0;
 198                                break;
 199                        }
 200                        err = hwrng_init(rng);
 201                        if (err)
 202                                break;
 203                        hwrng_cleanup(current_rng);
 204                        current_rng = rng;
 205                        err = 0;
 206                        break;
 207                }
 208        }
 209        mutex_unlock(&rng_mutex);
 210
 211        return err ? : len;
 212}
 213
 214static ssize_t hwrng_attr_current_show(struct device *dev,
 215                                       struct device_attribute *attr,
 216                                       char *buf)
 217{
 218        int err;
 219        ssize_t ret;
 220        const char *name = "none";
 221
 222        err = mutex_lock_interruptible(&rng_mutex);
 223        if (err)
 224                return -ERESTARTSYS;
 225        if (current_rng)
 226                name = current_rng->name;
 227        ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
 228        mutex_unlock(&rng_mutex);
 229
 230        return ret;
 231}
 232
 233static ssize_t hwrng_attr_available_show(struct device *dev,
 234                                         struct device_attribute *attr,
 235                                         char *buf)
 236{
 237        int err;
 238        ssize_t ret = 0;
 239        struct hwrng *rng;
 240
 241        err = mutex_lock_interruptible(&rng_mutex);
 242        if (err)
 243                return -ERESTARTSYS;
 244        buf[0] = '\0';
 245        list_for_each_entry(rng, &rng_list, list) {
 246                strncat(buf, rng->name, PAGE_SIZE - ret - 1);
 247                ret += strlen(rng->name);
 248                strncat(buf, " ", PAGE_SIZE - ret - 1);
 249                ret++;
 250        }
 251        strncat(buf, "\n", PAGE_SIZE - ret - 1);
 252        ret++;
 253        mutex_unlock(&rng_mutex);
 254
 255        return ret;
 256}
 257
 258static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
 259                   hwrng_attr_current_show,
 260                   hwrng_attr_current_store);
 261static DEVICE_ATTR(rng_available, S_IRUGO,
 262                   hwrng_attr_available_show,
 263                   NULL);
 264
 265
 266static void unregister_miscdev(void)
 267{
 268        device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available);
 269        device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
 270        misc_deregister(&rng_miscdev);
 271}
 272
 273static int register_miscdev(void)
 274{
 275        int err;
 276
 277        err = misc_register(&rng_miscdev);
 278        if (err)
 279                goto out;
 280        err = device_create_file(rng_miscdev.this_device,
 281                                 &dev_attr_rng_current);
 282        if (err)
 283                goto err_misc_dereg;
 284        err = device_create_file(rng_miscdev.this_device,
 285                                 &dev_attr_rng_available);
 286        if (err)
 287                goto err_remove_current;
 288out:
 289        return err;
 290
 291err_remove_current:
 292        device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
 293err_misc_dereg:
 294        misc_deregister(&rng_miscdev);
 295        goto out;
 296}
 297
 298int hwrng_register(struct hwrng *rng)
 299{
 300        int must_register_misc;
 301        int err = -EINVAL;
 302        struct hwrng *old_rng, *tmp;
 303
 304        if (rng->name == NULL ||
 305            (rng->data_read == NULL && rng->read == NULL))
 306                goto out;
 307
 308        mutex_lock(&rng_mutex);
 309
 310        /* Must not register two RNGs with the same name. */
 311        err = -EEXIST;
 312        list_for_each_entry(tmp, &rng_list, list) {
 313                if (strcmp(tmp->name, rng->name) == 0)
 314                        goto out_unlock;
 315        }
 316
 317        must_register_misc = (current_rng == NULL);
 318        old_rng = current_rng;
 319        if (!old_rng) {
 320                err = hwrng_init(rng);
 321                if (err)
 322                        goto out_unlock;
 323                current_rng = rng;
 324        }
 325        err = 0;
 326        if (must_register_misc) {
 327                err = register_miscdev();
 328                if (err) {
 329                        if (!old_rng) {
 330                                hwrng_cleanup(rng);
 331                                current_rng = NULL;
 332                        }
 333                        goto out_unlock;
 334                }
 335        }
 336        INIT_LIST_HEAD(&rng->list);
 337        list_add_tail(&rng->list, &rng_list);
 338out_unlock:
 339        mutex_unlock(&rng_mutex);
 340out:
 341        return err;
 342}
 343EXPORT_SYMBOL_GPL(hwrng_register);
 344
 345void hwrng_unregister(struct hwrng *rng)
 346{
 347        int err;
 348
 349        mutex_lock(&rng_mutex);
 350
 351        list_del(&rng->list);
 352        if (current_rng == rng) {
 353                hwrng_cleanup(rng);
 354                if (list_empty(&rng_list)) {
 355                        current_rng = NULL;
 356                } else {
 357                        current_rng = list_entry(rng_list.prev, struct hwrng, list);
 358                        err = hwrng_init(current_rng);
 359                        if (err)
 360                                current_rng = NULL;
 361                }
 362        }
 363        if (list_empty(&rng_list))
 364                unregister_miscdev();
 365
 366        mutex_unlock(&rng_mutex);
 367}
 368EXPORT_SYMBOL_GPL(hwrng_unregister);
 369
 370
 371MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
 372MODULE_LICENSE("GPL");
 373