]> err.no Git - linux-2.6/commitdiff
[PATCH] Add new generic HW RNG core
authorMichael Buesch <mb@bu3sch.de>
Mon, 26 Jun 2006 07:24:59 +0000 (00:24 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Mon, 26 Jun 2006 16:58:19 +0000 (09:58 -0700)
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Cc: Jeff Garzik <jeff@garzik.org>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
MAINTAINERS
drivers/char/Kconfig
drivers/char/Makefile
drivers/char/hw_random/Kconfig [new file with mode: 0644]
drivers/char/hw_random/Makefile [new file with mode: 0644]
drivers/char/hw_random/core.c [new file with mode: 0644]
include/linux/hw_random.h [new file with mode: 0644]

index 4dcd2f1f14d67845ffec12639160325debe35e5e..28c0a96769270659f1948e14f9d2456a016ef718 100644 (file)
@@ -1118,6 +1118,11 @@ L:       lm-sensors@lm-sensors.org
 W:     http://www.lm-sensors.nu/
 S:     Maintained
 
+HARDWARE RANDOM NUMBER GENERATOR CORE
+P:     Michael Buesch
+M:     mb@bu3sch.de
+S:     Maintained
+
 HARD DRIVE ACTIVE PROTECTION SYSTEM (HDAPS) DRIVER
 P:     Robert Love
 M:     rlove@rlove.org
@@ -1436,6 +1441,11 @@ P:       Tigran Aivazian
 M:     tigran@veritas.com
 S:     Maintained
 
+INTEL IXP4XX RANDOM NUMBER GENERATOR SUPPORT
+P:     Deepak Saxena
+M:     dsaxena@plexity.net
+S:     Maintained
+
 INTEL PRO/100 ETHERNET SUPPORT
 P:     John Ronciak
 M:     john.ronciak@intel.com
@@ -2725,6 +2735,11 @@ P:       Christoph Hellwig
 M:     hch@infradead.org
 S:     Maintained
 
+TI OMAP RANDOM NUMBER GENERATOR SUPPORT
+P:     Deepak Saxena
+M:     dsaxena@plexity.net
+S:     Maintained
+
 TI PARALLEL LINK CABLE DRIVER
 P:     Romain Lievin
 M:     roms@lpg.ticalc.org
index e4b44c68ad0f0101bdbb8dac1682d3544ecaaec0..ed31638bd75cf967d50b9e835105522c809033ac 100644 (file)
@@ -670,6 +670,8 @@ config NWFLASH
 
          If you're not sure, say N.
 
+source "drivers/char/hw_random/Kconfig"
+
 config NVRAM
        tristate "/dev/nvram support"
        depends on ATARI || X86 || ARM || GENERIC_NVRAM
index 79aecef93b605b828abf8aa1e324a556529c85fe..524105597ea7d7ff82339902afc0c15cda691447 100644 (file)
@@ -75,6 +75,7 @@ endif
 obj-$(CONFIG_TOSHIBA)          += toshiba.o
 obj-$(CONFIG_I8K)              += i8k.o
 obj-$(CONFIG_DS1620)           += ds1620.o
+obj-$(CONFIG_HW_RANDOM)                += hw_random/
 obj-$(CONFIG_FTAPE)            += ftape/
 obj-$(CONFIG_COBALT_LCD)       += lcd.o
 obj-$(CONFIG_PPDEV)            += ppdev.o
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
new file mode 100644 (file)
index 0000000..fa4a245
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Hardware Random Number Generator (RNG) configuration
+#
+
+config HW_RANDOM
+       bool "Hardware Random Number Generator Core support"
+       default y
+       ---help---
+         Hardware Random Number Generator Core infrastructure.
+
+         If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
new file mode 100644 (file)
index 0000000..aa752af
--- /dev/null
@@ -0,0 +1,5 @@
+#
+# Makefile for HW Random Number Generator (RNG) device drivers.
+#
+
+obj-$(CONFIG_HW_RANDOM) += core.o
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
new file mode 100644 (file)
index 0000000..88b0266
--- /dev/null
@@ -0,0 +1,354 @@
+/*
+        Added support for the AMD Geode LX RNG
+       (c) Copyright 2004-2005 Advanced Micro Devices, Inc.
+
+       derived from
+
+       Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+       (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+
+       derived from
+
+        Hardware driver for the AMD 768 Random Number Generator (RNG)
+        (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+
+       derived from
+
+       Hardware driver for Intel i810 Random Number Generator (RNG)
+       Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+       Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
+
+       Added generic RNG API
+       Copyright 2006 Michael Buesch <mbuesch@freenet.de>
+       Copyright 2005 (c) MontaVista Software, Inc.
+
+       Please read Documentation/hw_random.txt for details on use.
+
+       ----------------------------------------------------------
+       This software may be used and distributed according to the terms
+        of the GNU General Public License, incorporated herein by reference.
+
+ */
+
+
+#include <linux/device.h>
+#include <linux/hw_random.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+
+#define RNG_MODULE_NAME                "hw_random"
+#define PFX                    RNG_MODULE_NAME ": "
+#define RNG_MISCDEV_MINOR      183 /* official */
+
+
+static struct hwrng *current_rng;
+static LIST_HEAD(rng_list);
+static DEFINE_MUTEX(rng_mutex);
+
+
+static inline int hwrng_init(struct hwrng *rng)
+{
+       if (!rng->init)
+               return 0;
+       return rng->init(rng);
+}
+
+static inline void hwrng_cleanup(struct hwrng *rng)
+{
+       if (rng && rng->cleanup)
+               rng->cleanup(rng);
+}
+
+static inline int hwrng_data_present(struct hwrng *rng)
+{
+       if (!rng->data_present)
+               return 1;
+       return rng->data_present(rng);
+}
+
+static inline int hwrng_data_read(struct hwrng *rng, u32 *data)
+{
+       return rng->data_read(rng, data);
+}
+
+
+static int rng_dev_open(struct inode *inode, struct file *filp)
+{
+       /* enforce read-only access to this chrdev */
+       if ((filp->f_mode & FMODE_READ) == 0)
+               return -EINVAL;
+       if (filp->f_mode & FMODE_WRITE)
+               return -EINVAL;
+       return 0;
+}
+
+static ssize_t rng_dev_read(struct file *filp, char __user *buf,
+                           size_t size, loff_t *offp)
+{
+       u32 data;
+       ssize_t ret = 0;
+       int i, err = 0;
+       int data_present;
+       int bytes_read;
+
+       while (size) {
+               err = -ERESTARTSYS;
+               if (mutex_lock_interruptible(&rng_mutex))
+                       goto out;
+               if (!current_rng) {
+                       mutex_unlock(&rng_mutex);
+                       err = -ENODEV;
+                       goto out;
+               }
+               if (filp->f_flags & O_NONBLOCK) {
+                       data_present = hwrng_data_present(current_rng);
+               } else {
+                       /* Some RNG require some time between data_reads to gather
+                        * new entropy. Poll it.
+                        */
+                       for (i = 0; i < 20; i++) {
+                               data_present = hwrng_data_present(current_rng);
+                               if (data_present)
+                                       break;
+                               udelay(10);
+                       }
+               }
+               bytes_read = 0;
+               if (data_present)
+                       bytes_read = hwrng_data_read(current_rng, &data);
+               mutex_unlock(&rng_mutex);
+
+               err = -EAGAIN;
+               if (!bytes_read && (filp->f_flags & O_NONBLOCK))
+                       goto out;
+
+               err = -EFAULT;
+               while (bytes_read && size) {
+                       if (put_user((u8)data, buf++))
+                               goto out;
+                       size--;
+                       ret++;
+                       bytes_read--;
+                       data >>= 8;
+               }
+
+               if (need_resched())
+                       schedule_timeout_interruptible(1);
+               err = -ERESTARTSYS;
+               if (signal_pending(current))
+                       goto out;
+       }
+out:
+       return ret ? : err;
+}
+
+
+static struct file_operations rng_chrdev_ops = {
+       .owner          = THIS_MODULE,
+       .open           = rng_dev_open,
+       .read           = rng_dev_read,
+};
+
+static struct miscdevice rng_miscdev = {
+       .minor          = RNG_MISCDEV_MINOR,
+       .name           = RNG_MODULE_NAME,
+       .fops           = &rng_chrdev_ops,
+};
+
+
+static ssize_t hwrng_attr_current_store(struct class_device *class,
+                                       const char *buf, size_t len)
+{
+       int err;
+       struct hwrng *rng;
+
+       err = mutex_lock_interruptible(&rng_mutex);
+       if (err)
+               return -ERESTARTSYS;
+       err = -ENODEV;
+       list_for_each_entry(rng, &rng_list, list) {
+               if (strcmp(rng->name, buf) == 0) {
+                       if (rng == current_rng) {
+                               err = 0;
+                               break;
+                       }
+                       err = hwrng_init(rng);
+                       if (err)
+                               break;
+                       hwrng_cleanup(current_rng);
+                       current_rng = rng;
+                       err = 0;
+                       break;
+               }
+       }
+       mutex_unlock(&rng_mutex);
+
+       return err ? : len;
+}
+
+static ssize_t hwrng_attr_current_show(struct class_device *class,
+                                      char *buf)
+{
+       int err;
+       ssize_t ret;
+       const char *name = "none";
+
+       err = mutex_lock_interruptible(&rng_mutex);
+       if (err)
+               return -ERESTARTSYS;
+       if (current_rng)
+               name = current_rng->name;
+       ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
+       mutex_unlock(&rng_mutex);
+
+       return ret;
+}
+
+static ssize_t hwrng_attr_available_show(struct class_device *class,
+                                        char *buf)
+{
+       int err;
+       ssize_t ret = 0;
+       struct hwrng *rng;
+
+       err = mutex_lock_interruptible(&rng_mutex);
+       if (err)
+               return -ERESTARTSYS;
+       buf[0] = '\0';
+       list_for_each_entry(rng, &rng_list, list) {
+               strncat(buf, rng->name, PAGE_SIZE - ret - 1);
+               ret += strlen(rng->name);
+               strncat(buf, " ", PAGE_SIZE - ret - 1);
+               ret++;
+       }
+       strncat(buf, "\n", PAGE_SIZE - ret - 1);
+       ret++;
+       mutex_unlock(&rng_mutex);
+
+       return ret;
+}
+
+static CLASS_DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
+                        hwrng_attr_current_show,
+                        hwrng_attr_current_store);
+static CLASS_DEVICE_ATTR(rng_available, S_IRUGO,
+                        hwrng_attr_available_show,
+                        NULL);
+
+
+static void unregister_miscdev(void)
+{
+       class_device_remove_file(rng_miscdev.class,
+                                &class_device_attr_rng_available);
+       class_device_remove_file(rng_miscdev.class,
+                                &class_device_attr_rng_current);
+       misc_deregister(&rng_miscdev);
+}
+
+static int register_miscdev(void)
+{
+       int err;
+
+       err = misc_register(&rng_miscdev);
+       if (err)
+               goto out;
+       err = class_device_create_file(rng_miscdev.class,
+                                      &class_device_attr_rng_current);
+       if (err)
+               goto err_misc_dereg;
+       err = class_device_create_file(rng_miscdev.class,
+                                      &class_device_attr_rng_available);
+       if (err)
+               goto err_remove_current;
+out:
+       return err;
+
+err_remove_current:
+       class_device_remove_file(rng_miscdev.class,
+                                &class_device_attr_rng_current);
+err_misc_dereg:
+       misc_deregister(&rng_miscdev);
+       goto out;
+}
+
+int hwrng_register(struct hwrng *rng)
+{
+       int must_register_misc;
+       int err = -EINVAL;
+       struct hwrng *old_rng, *tmp;
+
+       if (rng->name == NULL ||
+           rng->data_read == NULL)
+               goto out;
+
+       mutex_lock(&rng_mutex);
+
+       /* Must not register two RNGs with the same name. */
+       err = -EEXIST;
+       list_for_each_entry(tmp, &rng_list, list) {
+               if (strcmp(tmp->name, rng->name) == 0)
+                       goto out_unlock;
+       }
+
+       must_register_misc = (current_rng == NULL);
+       old_rng = current_rng;
+       if (!old_rng) {
+               err = hwrng_init(rng);
+               if (err)
+                       goto out_unlock;
+               current_rng = rng;
+       }
+       err = 0;
+       if (must_register_misc) {
+               err = register_miscdev();
+               if (err) {
+                       if (!old_rng) {
+                               hwrng_cleanup(rng);
+                               current_rng = NULL;
+                       }
+                       goto out_unlock;
+               }
+       }
+       INIT_LIST_HEAD(&rng->list);
+       list_add_tail(&rng->list, &rng_list);
+out_unlock:
+       mutex_unlock(&rng_mutex);
+out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(hwrng_register);
+
+void hwrng_unregister(struct hwrng *rng)
+{
+       int err;
+
+       mutex_lock(&rng_mutex);
+
+       list_del(&rng->list);
+       if (current_rng == rng) {
+               hwrng_cleanup(rng);
+               if (list_empty(&rng_list)) {
+                       current_rng = NULL;
+               } else {
+                       current_rng = list_entry(rng_list.prev, struct hwrng, list);
+                       err = hwrng_init(current_rng);
+                       if (err)
+                               current_rng = NULL;
+               }
+       }
+       if (list_empty(&rng_list))
+               unregister_miscdev();
+
+       mutex_unlock(&rng_mutex);
+}
+EXPORT_SYMBOL_GPL(hwrng_unregister);
+
+
+MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/hw_random.h b/include/linux/hw_random.h
new file mode 100644 (file)
index 0000000..21ea761
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+       Hardware Random Number Generator
+
+       Please read Documentation/hw_random.txt for details on use.
+
+       ----------------------------------------------------------
+       This software may be used and distributed according to the terms
+        of the GNU General Public License, incorporated herein by reference.
+
+ */
+
+#ifndef LINUX_HWRANDOM_H_
+#define LINUX_HWRANDOM_H_
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/list.h>
+
+/**
+ * struct hwrng - Hardware Random Number Generator driver
+ * @name:              Unique RNG name.
+ * @init:              Initialization callback (can be NULL).
+ * @cleanup:           Cleanup callback (can be NULL).
+ * @data_present:      Callback to determine if data is available
+ *                     on the RNG. If NULL, it is assumed that
+ *                     there is always data available.
+ * @data_read:         Read data from the RNG device.
+ *                     Returns the number of lower random bytes in "data".
+ *                     Must not be NULL.
+ * @priv:              Private data, for use by the RNG driver.
+ */
+struct hwrng {
+       const char *name;
+       int (*init)(struct hwrng *rng);
+       void (*cleanup)(struct hwrng *rng);
+       int (*data_present)(struct hwrng *rng);
+       int (*data_read)(struct hwrng *rng, u32 *data);
+       unsigned long priv;
+
+       /* internal. */
+       struct list_head list;
+};
+
+/** Register a new Hardware Random Number Generator driver. */
+extern int hwrng_register(struct hwrng *rng);
+/** Unregister a Hardware Random Number Generator driver. */
+extern void hwrng_unregister(struct hwrng *rng);
+
+#endif /* __KERNEL__ */
+#endif /* LINUX_HWRANDOM_H_ */