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 <linux/slab.h>
  44#include <asm/uaccess.h>
  45
  46
  47#define RNG_MODULE_NAME         "hw_random"
  48#define PFX                     RNG_MODULE_NAME ": "
  49#define RNG_MISCDEV_MINOR       183 /* official */
  50
  51
  52static struct hwrng *current_rng;
  53static LIST_HEAD(rng_list);
  54static DEFINE_MUTEX(rng_mutex);
  55static int data_avail;
  56static u8 *rng_buffer;
  57
  58static size_t rng_buffer_size(void)
  59{
  60        return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES;
  61}
  62
  63static inline int hwrng_init(struct hwrng *rng)
  64{
  65        if (!rng->init)
  66                return 0;
  67        return rng->init(rng);
  68}
  69
  70static inline void hwrng_cleanup(struct hwrng *rng)
  71{
  72        if (rng && rng->cleanup)
  73                rng->cleanup(rng);
  74}
  75
  76static int rng_dev_open(struct inode *inode, struct file *filp)
  77{
  78        /* enforce read-only access to this chrdev */
  79        if ((filp->f_mode & FMODE_READ) == 0)
  80                return -EINVAL;
  81        if (filp->f_mode & FMODE_WRITE)
  82                return -EINVAL;
  83        return 0;
  84}
  85
  86static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
  87                        int wait) {
  88        int present;
  89
  90        if (rng->read)
  91                return rng->read(rng, (void *)buffer, size, wait);
  92
  93        if (rng->data_present)
  94                present = rng->data_present(rng, wait);
  95        else
  96                present = 1;
  97
  98        if (present)
  99                return rng->data_read(rng, (u32 *)buffer);
 100
 101        return 0;
 102}
 103
 104static ssize_t rng_dev_read(struct file *filp, char __user *buf,
 105                            size_t size, loff_t *offp)
 106{
 107        ssize_t ret = 0;
 108        int err = 0;
 109        int bytes_read, len;
 110
 111        while (size) {
 112                if (mutex_lock_interruptible(&rng_mutex)) {
 113                        err = -ERESTARTSYS;
 114                        goto out;
 115                }
 116
 117                if (!current_rng) {
 118                        err = -ENODEV;
 119                        goto out_unlock;
 120                }
 121
 122                if (!data_avail) {
 123                        bytes_read = rng_get_data(current_rng, rng_buffer,
 124                                rng_buffer_size(),
 125                                !(filp->f_flags & O_NONBLOCK));
 126                        if (bytes_read < 0) {
 127                                err = bytes_read;
 128                                goto out_unlock;
 129                        }
 130                        data_avail = bytes_read;
 131                }
 132
 133                if (!data_avail) {
 134                        if (filp->f_flags & O_NONBLOCK) {
 135                                err = -EAGAIN;
 136                                goto out_unlock;
 137                        }
 138                } else {
 139                        len = data_avail;
 140                        if (len > size)
 141                                len = size;
 142
 143                        data_avail -= len;
 144
 145                        if (copy_to_user(buf + ret, rng_buffer + data_avail,
 146                                                                len)) {
 147                                err = -EFAULT;
 148                                goto out_unlock;
 149                        }
 150
 151                        size -= len;
 152                        ret += len;
 153                }
 154
 155                mutex_unlock(&rng_mutex);
 156
 157                if (need_resched())
 158                        schedule_timeout_interruptible(1);
 159
 160                if (signal_pending(current)) {
 161                        err = -ERESTARTSYS;
 162                        goto out;
 163                }
 164        }
 165out:
 166        return ret ? : err;
 167out_unlock:
 168        mutex_unlock(&rng_mutex);
 169        goto out;
 170}
 171
 172
 173static const struct file_operations rng_chrdev_ops = {
 174        .owner          = THIS_MODULE,
 175        .open           = rng_dev_open,
 176        .read           = rng_dev_read,
 177        .llseek         = noop_llseek,
 178};
 179
 180static struct miscdevice rng_miscdev = {
 181        .minor          = RNG_MISCDEV_MINOR,
 182        .name           = RNG_MODULE_NAME,
 183        .nodename       = "hwrng",
 184        .fops           = &rng_chrdev_ops,
 185};
 186
 187
 188static ssize_t hwrng_attr_current_store(struct device *dev,
 189                                        struct device_attribute *attr,
 190                                        const char *buf, size_t len)
 191{
 192        int err;
 193        struct hwrng *rng;
 194
 195        err = mutex_lock_interruptible(&rng_mutex);
 196        if (err)
 197                return -ERESTARTSYS;
 198        err = -ENODEV;
 199        list_for_each_entry(rng, &rng_list, list) {
 200                if (strcmp(rng->name, buf) == 0) {
 201                        if (rng == current_rng) {
 202                                err = 0;
 203                                break;
 204                        }
 205                        err = hwrng_init(rng);
 206                        if (err)
 207                                break;
 208                        hwrng_cleanup(current_rng);
 209                        current_rng = rng;
 210                        err = 0;
 211                        break;
 212                }
 213        }
 214        mutex_unlock(&rng_mutex);
 215
 216        return err ? : len;
 217}
 218
 219static ssize_t hwrng_attr_current_show(struct device *dev,
 220                                       struct device_attribute *attr,
 221                                       char *buf)
 222{
 223        int err;
 224        ssize_t ret;
 225        const char *name = "none";
 226
 227        err = mutex_lock_interruptible(&rng_mutex);
 228        if (err)
 229                return -ERESTARTSYS;
 230        if (current_rng)
 231                name = current_rng->name;
 232        ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
 233        mutex_unlock(&rng_mutex);
 234
 235        return ret;
 236}
 237
 238static ssize_t hwrng_attr_available_show(struct device *dev,
 239                                         struct device_attribute *attr,
 240                                         char *buf)
 241{
 242        int err;
 243        ssize_t ret = 0;
 244        struct hwrng *rng;
 245
 246        err = mutex_lock_interruptible(&rng_mutex);
 247        if (err)
 248                return -ERESTARTSYS;
 249        buf[0] = '\0';
 250        list_for_each_entry(rng, &rng_list, list) {
 251                strncat(buf, rng->name, PAGE_SIZE - ret - 1);
 252                ret += strlen(rng->name);
 253                strncat(buf, " ", PAGE_SIZE - ret - 1);
 254                ret++;
 255        }
 256        strncat(buf, "\n", PAGE_SIZE - ret - 1);
 257        ret++;
 258        mutex_unlock(&rng_mutex);
 259
 260        return ret;
 261}
 262
 263static DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
 264                   hwrng_attr_current_show,
 265                   hwrng_attr_current_store);
 266static DEVICE_ATTR(rng_available, S_IRUGO,
 267                   hwrng_attr_available_show,
 268                   NULL);
 269
 270
 271static void unregister_miscdev(void)
 272{
 273        device_remove_file(rng_miscdev.this_device, &dev_attr_rng_available);
 274        device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
 275        misc_deregister(&rng_miscdev);
 276}
 277
 278static int register_miscdev(void)
 279{
 280        int err;
 281
 282        err = misc_register(&rng_miscdev);
 283        if (err)
 284                goto out;
 285        err = device_create_file(rng_miscdev.this_device,
 286                                 &dev_attr_rng_current);
 287        if (err)
 288                goto err_misc_dereg;
 289        err = device_create_file(rng_miscdev.this_device,
 290                                 &dev_attr_rng_available);
 291        if (err)
 292                goto err_remove_current;
 293out:
 294        return err;
 295
 296err_remove_current:
 297        device_remove_file(rng_miscdev.this_device, &dev_attr_rng_current);
 298err_misc_dereg:
 299        misc_deregister(&rng_miscdev);
 300        goto out;
 301}
 302
 303int hwrng_register(struct hwrng *rng)
 304{
 305        int must_register_misc;
 306        int err = -EINVAL;
 307        struct hwrng *old_rng, *tmp;
 308
 309        if (rng->name == NULL ||
 310            (rng->data_read == NULL && rng->read == NULL))
 311                goto out;
 312
 313        mutex_lock(&rng_mutex);
 314
 315        /* kmalloc makes this safe for virt_to_page() in virtio_rng.c */
 316        err = -ENOMEM;
 317        if (!rng_buffer) {
 318                rng_buffer = kmalloc(rng_buffer_size(), GFP_KERNEL);
 319                if (!rng_buffer)
 320                        goto out_unlock;
 321        }
 322
 323        /* Must not register two RNGs with the same name. */
 324        err = -EEXIST;
 325        list_for_each_entry(tmp, &rng_list, list) {
 326                if (strcmp(tmp->name, rng->name) == 0)
 327                        goto out_unlock;
 328        }
 329
 330        must_register_misc = (current_rng == NULL);
 331        old_rng = current_rng;
 332        if (!old_rng) {
 333                err = hwrng_init(rng);
 334                if (err)
 335                        goto out_unlock;
 336                current_rng = rng;
 337        }
 338        err = 0;
 339        if (must_register_misc) {
 340                err = register_miscdev();
 341                if (err) {
 342                        if (!old_rng) {
 343                                hwrng_cleanup(rng);
 344                                current_rng = NULL;
 345                        }
 346                        goto out_unlock;
 347                }
 348        }
 349        INIT_LIST_HEAD(&rng->list);
 350        list_add_tail(&rng->list, &rng_list);
 351out_unlock:
 352        mutex_unlock(&rng_mutex);
 353out:
 354        return err;
 355}
 356EXPORT_SYMBOL_GPL(hwrng_register);
 357
 358void hwrng_unregister(struct hwrng *rng)
 359{
 360        int err;
 361
 362        mutex_lock(&rng_mutex);
 363
 364        list_del(&rng->list);
 365        if (current_rng == rng) {
 366                hwrng_cleanup(rng);
 367                if (list_empty(&rng_list)) {
 368                        current_rng = NULL;
 369                } else {
 370                        current_rng = list_entry(rng_list.prev, struct hwrng, list);
 371                        err = hwrng_init(current_rng);
 372                        if (err)
 373                                current_rng = NULL;
 374                }
 375        }
 376        if (list_empty(&rng_list))
 377                unregister_miscdev();
 378
 379        mutex_unlock(&rng_mutex);
 380}
 381EXPORT_SYMBOL_GPL(hwrng_unregister);
 382
 383static void __exit hwrng_exit(void)
 384{
 385        mutex_lock(&rng_mutex);
 386        BUG_ON(current_rng);
 387        kfree(rng_buffer);
 388        mutex_unlock(&rng_mutex);
 389}
 390
 391module_exit(hwrng_exit);
 392
 393MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
 394MODULE_LICENSE("GPL");
 395