On Wednesday 01 March 2006 03:57, you wrote: > > Hi, I'll email you the patchset off-list so you can look at the API > > and write the bcm43xx driver against it. They are a few months old > > and > > need updating to 2.6.latest and it is on my 2.6.18 TODO. If you > > search the > > archives there were a few small issues left such as separating out > > all the > > x86 stuff into separate amd, via, and intel code instead of having > > a single > > file. > > Are the patches in any state to include in -mm? What about something like the following patch. This is a merge of Deepak Saxena's and my stuff. It brings the best features from both patches. :) This is only compile-tested, yet. diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/core.c linux-2.6.16-rc5-mm1/drivers/char/hw_random/core.c --- linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/core.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-rc5-mm1/drivers/char/hw_random/core.c 2006-03-01 14:07:56.000000000 +0100 @@ -0,0 +1,352 @@ +/* + 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 <[email protected]> + + derived from + + Hardware driver for the AMD 768 Random Number Generator (RNG) + (c) Copyright 2001 Red Hat Inc <[email protected]> + + derived from + + Hardware driver for Intel i810 Random Number Generator (RNG) + Copyright 2000,2001 Jeff Garzik <[email protected]> + Copyright 2000,2001 Philipp Rumpf <[email protected]> + + Added generic RNG API + Copyright 2006 Michael Buesch <[email protected]> + 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/hw_random.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/random.h> +#include <linux/miscdevice.h> +#include <linux/smp_lock.h> +#include <linux/mm.h> +#include <linux/delay.h> + +#ifdef __i386__ +#include <asm/msr.h> +#include <asm/cpufeature.h> +#endif + +#include <asm/io.h> +#include <asm/uaccess.h> + + +#define RNG_MODULE_NAME "hw_random" +#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver" +#define PFX RNG_MODULE_NAME ": " + + +/* + * debugging macros + */ + +/* pr_debug() collapses to a no-op if DEBUG is not defined */ +#define DPRINTK(fmt, args...) pr_debug(PFX "%s: " fmt, __FUNCTION__ , ## args) + + +#undef RNG_NDEBUG /* define to enable lightweight runtime checks */ +#ifdef RNG_NDEBUG +#define assert(expr) \ + if(!(expr)) { \ + printk(KERN_DEBUG PFX "Assertion failed! %s,%s,%s," \ + "line=%d\n", #expr, __FILE__, __FUNCTION__, __LINE__); \ + } +#else +#define assert(expr) +#endif + +#define RNG_MISCDEV_MINOR 183 /* official */ + + +static int rng_dev_open (struct inode *inode, struct file *filp); +static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, + loff_t * offp); + +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 struct hwrng *current_rng; +static LIST_HEAD(rng_list); +static DEFINE_MUTEX(rng_mutex); + + +/*********************************************************************** + * + * /dev/hwrandom character device handling (major 10, minor 183) + * + */ + +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) +{ + unsigned int have_data; + u32 data = 0; + ssize_t ret = 0; + int err; + + while (size) { + err = mutex_lock_interruptible(&rng_mutex); + if (err) + return err; + if (!current_rng) { + mutex_unlock(&rng_mutex); + return -ENODEV; + } + have_data = 0; + if (current_rng->data_present == NULL || + current_rng->data_present(current_rng)) + have_data = current_rng->data_read(current_rng, &data); + mutex_unlock(&rng_mutex); + + while (have_data && size) { + if (put_user((u8)data, buf++)) { + ret = ret ? : -EFAULT; + break; + } + size--; + ret++; + have_data--; + data>>=8; + } + + if (filp->f_flags & O_NONBLOCK) + return ret ? : -EAGAIN; + + if(need_resched()) + schedule_timeout_interruptible(1); + else + udelay(200); /* FIXME: We could poll for 250uS ?? */ + + if (signal_pending (current)) + return ret ? : -ERESTARTSYS; + } + return ret; +} + + +static ssize_t hwrng_attr_current_store(struct class_device *class, + const char *buf, size_t len) +{ + int err = -ENODEV; + struct hwrng *rng; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + err = mutex_lock_interruptible(&rng_mutex); + if (err) + return err; + err = -ENODEV; + list_for_each_entry(rng, &rng_list, list) { + if (strncmp(rng->name, buf, len) == 0) { + if (rng->init) { + err = rng->init(rng); + if (err) + break; + } + if (current_rng && current_rng->cleanup) + current_rng->cleanup(current_rng); + current_rng = rng; + err = 0; + break; + } + } + mutex_unlock(&rng_mutex); + + return err ? err : len; +} + +static ssize_t hwrng_attr_current_show(struct class_device *class, + char *buf) +{ + int err; + ssize_t ret; + const char *name; + + err = mutex_lock_interruptible(&rng_mutex); + if (err) + return err; + if (current_rng) + name = current_rng->name; + else + name = "none"; + ret = sprintf(buf, "%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 err; + buf[0] = '\0'; + list_for_each_entry(rng, &rng_list, list) { + ret += strlen(rng->name); + strcat(buf, rng->name); + ret += 1; + strcat(buf, " "); + } + strcat(buf, "\n"); + ret += 1; + 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; + struct hwrng *old_current; + + if (rng->name == NULL) + return -EINVAL; + if (rng->data_read == NULL) + return -EINVAL; + + mutex_lock(&rng_mutex); + must_register_misc = (current_rng == NULL); + old_current = current_rng; + if (!current_rng) { + if (rng->init) { + err = rng->init(rng); + if (err) { + mutex_unlock(&rng_mutex); + return err; + } + } + current_rng = rng; + } + INIT_LIST_HEAD(&rng->list); + list_add_tail(&rng->list, &rng_list); + err = 0; + if (must_register_misc) { + err = register_miscdev(); + if (err) { + if (rng->cleanup) + rng->cleanup(rng); + list_del(&rng->list); + current_rng = old_current; + } + } + + mutex_unlock(&rng_mutex); + + return err; +} +EXPORT_SYMBOL_GPL(hwrng_register); + +void hwrng_unregister(struct hwrng *rng) +{ + mutex_lock(&rng_mutex); + list_del(&rng->list); + if (current_rng == rng) { + if (rng->cleanup) + rng->cleanup(rng); + if (list_empty(&rng_list)) + current_rng = NULL; + else + current_rng = list_entry(rng_list.prev, struct hwrng, list); + } + if (list_empty(&rng_list)) + unregister_miscdev(); + mutex_unlock(&rng_mutex); +} +EXPORT_SYMBOL_GPL(hwrng_unregister); + + +MODULE_AUTHOR("The Linux Kernel team"); +MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); +MODULE_LICENSE("GPL"); diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/ixp4xx-rng.c linux-2.6.16-rc5-mm1/drivers/char/hw_random/ixp4xx-rng.c --- linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/ixp4xx-rng.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-rc5-mm1/drivers/char/hw_random/ixp4xx-rng.c 2006-03-01 14:05:37.000000000 +0100 @@ -0,0 +1,62 @@ +/* + * drivers/char/rng/ixp4xx-rng.c + * + * RNG driver for Intel IXP4xx family of NPUs + * + * Author: Deepak Saxena <[email protected]> + * + * Copyright 2005 (c) MontaVista Software, Inc. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/bitops.h> +#include <linux/hw_random.h> + +#include <asm/io.h> +#include <asm/hardware.h> + + +static u32* __iomem rng_base; + + +static int ixp4xx_rng_data_read(struct hwrng *rng, u32 *buffer) +{ + *buffer = __raw_readl(rng_base); + + return 4; +} + +struct hwrng ixp4xx_rng_ops = { + .name = "ixp4xx", + .data_read = ixp4xx_rng_data_read, +}; + +static int __init ixp4xx_rng_init(void) +{ + rng_base = (u32* __iomem) ioremap(0x70002100, 4); + if (!rng_base) return -ENOMEM; + + return hwrng_register(&ixp4xx_rng_ops); +} + +static void __exit ixp4xx_rng_exit(void) +{ + hwrng_unregister(&ixp4xx_rng_ops); + iounmap(rng_base); +} + +subsys_initcall(ixp4xx_rng_init); +module_exit(ixp4xx_rng_exit); + +MODULE_AUTHOR("Deepak Saxena <[email protected]>"); +MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for IXP4xx"); +MODULE_LICENSE("GPL"); diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/Kconfig linux-2.6.16-rc5-mm1/drivers/char/hw_random/Kconfig --- linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-rc5-mm1/drivers/char/hw_random/Kconfig 2006-03-01 14:06:25.000000000 +0100 @@ -0,0 +1,47 @@ +# +# 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. + +config X86_RNG + tristate "Intel/AMD/VIA HW Random Number Generator support" + depends on HW_RANDOM && (X86 || IA64) && PCI + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on Intel i8xx-based motherboards, + AMD 76x-based motherboards, and Via Nehemiah CPUs. + + To compile this driver as a module, choose M here: the + module will be called x86-rng. + + If unsure, say N. + +config IXP4XX_RNG + tristate "Intel IXP4xx NPU HW Random Number Generator support" + depends on HW_RANDOM && ARCH_IXP4XX + ---help--- + This driver provides kernel-side support for the Random + Number Generator hardware found on the Intel IXP4xx NPU. + + To compile this driver as a module, choose M here: the + module will be called ixp4xx-rng. + +config OMAP_RNG + tristate "OMAP Random Number Generator support" + depends on HW_RANDOM && (ARCH_OMAP16XX || ARCH_OMAP24XX) + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on OMAP16xx and OMAP24xx multimedia + processors. + + To compile this driver as a module, choose M here: the + module will be called omap-rng. + + If unsure, say N. + diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/Makefile linux-2.6.16-rc5-mm1/drivers/char/hw_random/Makefile --- linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-rc5-mm1/drivers/char/hw_random/Makefile 2006-03-01 14:07:25.000000000 +0100 @@ -0,0 +1,8 @@ +# +# Makefile for HW Random Number Generator (RNG) device drivers. +# + +obj-$(CONFIG_HW_RANDOM) += core.o +obj-$(CONFIG_IXP4XX_RNG) += ixp4xx-rng.o +obj-$(CONFIG_X86_RNG) += x86-rng.o +obj-$(CONFIG_OMAP_RNG) += omap-rng.o diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/omap-rng.c linux-2.6.16-rc5-mm1/drivers/char/hw_random/omap-rng.c --- linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/omap-rng.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-rc5-mm1/drivers/char/hw_random/omap-rng.c 2006-03-01 14:05:34.000000000 +0100 @@ -0,0 +1,209 @@ +/* + * driver/char/hw_random/omap-rng.c + * + * RNG driver for TI OMAP CPU family + * + * Author: Deepak Saxena <[email protected]> + * + * Copyright 2005 (c) MontaVista Software, Inc. + * + * Mostly based on original driver: + * + * Copyright (C) 2005 Nokia Corporation + * Author: Juha Yrj��<[email protected]> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * TODO: + * + * - Make status updated be interrupt driven so we don't poll + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/random.h> +#include <linux/err.h> +#include <linux/device.h> +#include <linux/hw_random.h> + +#include <asm/io.h> +#include <asm/hardware/clock.h> + +#define RNG_OUT_REG 0x00 /* Output register */ +#define RNG_STAT_REG 0x04 /* Status register + [0] = STAT_BUSY */ +#define RNG_ALARM_REG 0x24 /* Alarm register + [7:0] = ALARM_COUNTER */ +#define RNG_CONFIG_REG 0x28 /* Configuration register + [11:6] = RESET_COUNT + [5:3] = RING2_DELAY + [2:0] = RING1_DELAY */ +#define RNG_REV_REG 0x3c /* Revision register + [7:0] = REV_NB */ +#define RNG_MASK_REG 0x40 /* Mask and reset register + [2] = IT_EN + [1] = SOFTRESET + [0] = AUTOIDLE */ +#define RNG_SYSSTATUS 0x44 /* System status + [0] = RESETDONE */ + +static void __iomem *rng_base; +static struct clk *rng_ick; +static struct device *rng_dev; + +static u32 omap_rng_read_reg(int reg) +{ + return __raw_readl(rng_base + reg); +} + +static void omap_rng_write_reg(int reg, u32 val) +{ + __raw_writel(val, rng_base + reg); +} + +/* REVISIT: Does the status bit really work on 16xx? */ +static int omap_rng_data_present(struct hwrng *rng) +{ + return omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1; +} + +static int omap_rng_data_read(struct hwrng *rng, u32 *data) +{ + *data = omap_rng_read_reg(RNG_OUT_REG); + + return 4; +} + +static struct hwrng omap_rng_ops = { + .name = "omap", + .data_present = omap_rng_data_present, + .data_read = omap_rng_data_read, +}; + +static int __init omap_rng_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct resource *res, *mem; + int ret; + + /* + * A bit ugly, and it will never actually happen but there can + * be only one RNG and this catches any bork + */ + if (rng_dev) + BUG(); + + if (cpu_is_omap24xx()) { + rng_ick = clk_get(NULL, "rng_ick"); + if (IS_ERR(rng_ick)) { + dev_err(dev, "Could not get rng_ick\n"); + ret = PTR_ERR(rng_ick); + return ret; + } + else { + clk_use(rng_ick); + } + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) + return -ENOENT; + + mem = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + if (mem == NULL) + return -EBUSY; + + dev_set_drvdata(dev, mem); + rng_base = (u32 __iomem *)io_p2v(res->start); + + ret = hwrng_register(&omap_rng_ops); + if (ret) { + release_resource(mem); + rng_base = NULL; + return ret; + } + + dev_info(dev, "OMAP Random Number Generator ver. %02x\n", + omap_rng_read_reg(RNG_REV_REG)); + omap_rng_write_reg(RNG_MASK_REG, 0x1); + + rng_dev = dev; + + return 0; +} + +static int __exit omap_rng_remove(struct device *dev) +{ + struct resource *mem = dev_get_drvdata(dev); + + hwrng_unregister(&omap_rng_ops); + + omap_rng_write_reg(RNG_MASK_REG, 0x0); + + if (cpu_is_omap24xx()) { + clk_unuse(rng_ick); + clk_put(rng_ick); + } + + release_resource(mem); + rng_base = NULL; + + return 0; +} + +#ifdef CONFIG_PM + +static int omap_rng_suspend(struct device *dev, pm_message_t message, u32 level) +{ + omap_rng_write_reg(RNG_MASK_REG, 0x0); + + return 0; +} + +static int omap_rng_resume(struct device *dev, pm_message_t message, u32 level) +{ + omap_rng_write_reg(RNG_MASK_REG, 0x1); + + return 1; +} + +#else + +#define omap_rng_suspend NULL +#define omap_rng_resume NULL + +#endif + + +static struct device_driver omap_rng_driver = { + .name = "omap_rng", + .bus = &platform_bus_type, + .probe = omap_rng_probe, + .remove = __exit_p(omap_rng_remove), + .suspend = omap_rng_suspend, + .resume = omap_rng_resume +}; + +static int __init omap_rng_init(void) +{ + if (!cpu_is_omap16xx() && !cpu_is_omap24xx()) + return -ENODEV; + + return driver_register(&omap_rng_driver); +} + +static void __exit omap_rng_exit(void) +{ + driver_unregister(&omap_rng_driver); +} + +module_init(omap_rng_init); +module_exit(omap_rng_exit); + +MODULE_AUTHOR("Deepak Saxena (and others)"); +MODULE_LICENSE("GPL"); diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/x86-rng.c linux-2.6.16-rc5-mm1/drivers/char/hw_random/x86-rng.c --- linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random/x86-rng.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-rc5-mm1/drivers/char/hw_random/x86-rng.c 2006-03-01 14:14:11.000000000 +0100 @@ -0,0 +1,593 @@ +/* + * drivers/char/rng/x86.c + * + * RNG driver for Intel/AMD/VIA RNGs + * + * Copyright 2005 (c) MontaVista Software, Inc. + * + * with the majority of the code coming from: + * + * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG) + * (c) Copyright 2003 Red Hat Inc <[email protected]> + * + * derived from + * + * Hardware driver for the AMD 768 Random Number Generator (RNG) + * (c) Copyright 2001 Red Hat Inc <[email protected]> + * + * derived from + * + * Hardware driver for Intel i810 Random Number Generator (RNG) + * Copyright 2000,2001 Jeff Garzik <[email protected]> + * Copyright 2000,2001 Philipp Rumpf <[email protected]> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/random.h> +#include <linux/miscdevice.h> +#include <linux/smp_lock.h> +#include <linux/mm.h> +#include <linux/delay.h> +#include <linux/hw_random.h> + +#include <asm/msr.h> +#include <asm/cpufeature.h> + +#include <asm/io.h> + + +/* + * debugging macros + */ + +/* pr_debug() collapses to a no-op if DEBUG is not defined */ +#define DPRINTK(fmt, args...) pr_debug(PFX "%s: " fmt, __FUNCTION__ , ## args) + +#define RNG_VERSION "1.1.0" +#define RNG_MODULE_NAME "x86-rng" +#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION +#define PFX RNG_MODULE_NAME ": " + +#undef RNG_NDEBUG /* define to enable lightweight runtime checks */ +#ifdef RNG_NDEBUG +#define assert(expr) \ + if(!(expr)) { \ + printk(KERN_DEBUG PFX "Assertion failed! %s,%s,%s," \ + "line=%d\n", #expr, __FILE__, __FUNCTION__, __LINE__); \ + } +#else +#define assert(expr) +#endif + +static struct hwrng *x86_rng_ops; + +static int __init intel_init (struct hwrng *rng); +static void intel_cleanup(struct hwrng *rng); +static int intel_data_present (struct hwrng *rng); +static int intel_data_read (struct hwrng *rng, u32 *data); + +static int __init amd_init (struct hwrng *rng); +static void amd_cleanup(struct hwrng *rng); +static int amd_data_present (struct hwrng *rng); +static int amd_data_read (struct hwrng *rng, u32 *data); + +#ifdef __i386__ +static int __init via_init(struct hwrng *rng); +static void via_cleanup(struct hwrng *rng); +static int via_data_present (struct hwrng *rng); +static int via_data_read (struct hwrng *rng, u32 *data); +#endif + +static int __init geode_init(struct hwrng *rng); +static void geode_cleanup(struct hwrng *rng); +static int geode_data_present (struct hwrng *rng); +static int geode_data_read (struct hwrng *rng, u32 *data); + +enum { + rng_hw_none, + rng_hw_intel, + rng_hw_amd, +#ifdef __i386__ + rng_hw_via, +#endif + rng_hw_geode, +}; + +static struct hwrng rng_vendor_ops[] = { + { /* rng_hw_none */ + }, { /* rng_hw_intel */ + .name = "intel", + .init = intel_init, + .cleanup = intel_cleanup, + .data_present = intel_data_present, + .data_read = intel_data_read, + }, { /* rng_hw_amd */ + .name = "amd", + .init = amd_init, + .cleanup = amd_cleanup, + .data_present = amd_data_present, + .data_read = amd_data_read, + }, +#ifdef __i386__ + { /* rng_hw_via */ + .name = "via", + .init = via_init, + .cleanup = via_cleanup, + .data_present = via_data_present, + .data_read = via_data_read, + }, +#endif + { /* rng_hw_geode */ + .name = "geode", + .init = geode_init, + .cleanup = geode_cleanup, + .data_present = geode_data_present, + .data_read = geode_data_read, + }, +}; + +/* + * Data for PCI driver interface + * + * This data only exists for exporting the supported + * PCI ids via MODULE_DEVICE_TABLE. We do not actually + * register a pci_driver, because someone else might one day + * want to register another driver on the same PCI id. + */ +static struct pci_device_id rng_pci_tbl[] = { + { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_amd }, + { 0x1022, 0x746b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_amd }, + + { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, + + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_geode }, + + { 0, }, /* terminate list */ +}; +MODULE_DEVICE_TABLE (pci, rng_pci_tbl); + + +/*********************************************************************** + * + * Intel RNG operations + * + */ + +/* + * RNG registers (offsets from rng_mem) + */ +#define INTEL_RNG_HW_STATUS 0 +#define INTEL_RNG_PRESENT 0x40 +#define INTEL_RNG_ENABLED 0x01 +#define INTEL_RNG_STATUS 1 +#define INTEL_RNG_DATA_PRESENT 0x01 +#define INTEL_RNG_DATA 2 + +/* + * Magic address at which Intel PCI bridges locate the RNG + */ +#define INTEL_RNG_ADDR 0xFFBC015F +#define INTEL_RNG_ADDR_LEN 3 + +static inline u8 intel_hwstatus (void __iomem *rng_mem) +{ + assert (rng_mem != NULL); + return readb (rng_mem + INTEL_RNG_HW_STATUS); +} + +static inline u8 intel_hwstatus_set (void __iomem *rng_mem, u8 hw_status) +{ + assert (rng_mem != NULL); + writeb (hw_status, rng_mem + INTEL_RNG_HW_STATUS); + return intel_hwstatus (rng_mem); +} + +static int intel_data_present(struct hwrng *rng) +{ + void __iomem *rng_mem = (void __iomem *)rng->priv; + + assert (rng_mem != NULL); + return (readb (rng_mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT) ? + 1 : 0; +} + +static int intel_data_read(struct hwrng *rng, u32 *data) +{ + void __iomem *rng_mem = (void __iomem *)rng->priv; + + assert (rng_mem != NULL); + *data = readb (rng_mem + INTEL_RNG_DATA); + + return 1; +} + +static int __init intel_init(struct hwrng *rng) +{ + void __iomem *rng_mem; + int rc; + u8 hw_status; + + DPRINTK ("ENTER\n"); + + rng_mem = ioremap (INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); + if (rng_mem == NULL) { + printk (KERN_ERR PFX "cannot ioremap RNG Memory\n"); + rc = -EBUSY; + goto err_out; + } + rng->priv = (unsigned long)rng_mem; + + /* Check for Intel 82802 */ + hw_status = intel_hwstatus (rng_mem); + if ((hw_status & INTEL_RNG_PRESENT) == 0) { + printk (KERN_ERR PFX "RNG not detected\n"); + rc = -ENODEV; + goto err_out_free_map; + } + + /* turn RNG h/w on, if it's off */ + if ((hw_status & INTEL_RNG_ENABLED) == 0) + hw_status = intel_hwstatus_set (rng_mem, hw_status | INTEL_RNG_ENABLED); + if ((hw_status & INTEL_RNG_ENABLED) == 0) { + printk (KERN_ERR PFX "cannot enable RNG, aborting\n"); + rc = -EIO; + goto err_out_free_map; + } + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out_free_map: + iounmap (rng_mem); +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + +static void intel_cleanup(struct hwrng *rng) +{ + void __iomem *rng_mem = (void __iomem *)rng->priv; + u8 hw_status; + + hw_status = intel_hwstatus (rng_mem); + if (hw_status & INTEL_RNG_ENABLED) + intel_hwstatus_set (rng_mem, hw_status & ~INTEL_RNG_ENABLED); + else + printk(KERN_WARNING PFX "unusual: RNG already disabled\n"); + iounmap(rng_mem); +} + +/*********************************************************************** + * + * AMD RNG operations + * + */ + +static struct pci_dev *amd_pdev; + +static int amd_data_present (struct hwrng *rng) +{ + u32 pmbase = (u32)rng->priv; + + return !!(inl(pmbase + 0xF4) & 1); +} + + +static int amd_data_read (struct hwrng *rng, u32 *data) +{ + u32 pmbase = (u32)rng->priv; + + *data = inl(pmbase + 0xF0); + + return 4; +} + +static int __init amd_init(struct hwrng *rng) +{ + u32 pmbase; + int rc; + u8 rnen; + + DPRINTK ("ENTER\n"); + + amd_pdev = (struct pci_dev *)rng->priv; + pci_read_config_dword(amd_pdev, 0x58, &pmbase); + + pmbase &= 0x0000FF00; + + if (pmbase == 0) + { + printk (KERN_ERR PFX "power management base not set\n"); + rc = -EIO; + goto err_out; + } + rng->priv = (unsigned long)pmbase; + + pci_read_config_byte(amd_pdev, 0x40, &rnen); + rnen |= (1 << 7); /* RNG on */ + pci_write_config_byte(amd_pdev, 0x40, rnen); + + pci_read_config_byte(amd_pdev, 0x41, &rnen); + rnen |= (1 << 7); /* PMIO enable */ + pci_write_config_byte(amd_pdev, 0x41, rnen); + + pr_info( PFX "AMD768 system management I/O registers at 0x%X.\n", + pmbase); + + DPRINTK ("EXIT, returning 0\n"); + return 0; + +err_out: + DPRINTK ("EXIT, returning %d\n", rc); + return rc; +} + +static void amd_cleanup(struct hwrng *rng) +{ + u8 rnen; + + pci_read_config_byte(amd_pdev, 0x40, &rnen); + rnen &= ~(1 << 7); /* RNG off */ + pci_write_config_byte(amd_pdev, 0x40, rnen); + amd_pdev = NULL; + + /* FIXME: twiddle pmio, also? */ +} + +#ifdef __i386__ +/*********************************************************************** + * + * VIA RNG operations + * + */ + +enum { + VIA_STRFILT_CNT_SHIFT = 16, + VIA_STRFILT_FAIL = (1 << 15), + VIA_STRFILT_ENABLE = (1 << 14), + VIA_RAWBITS_ENABLE = (1 << 13), + VIA_RNG_ENABLE = (1 << 6), + VIA_XSTORE_CNT_MASK = 0x0F, + + VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */ + VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF, + VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_2_MASK = 0xFFFF, + VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */ + VIA_RNG_CHUNK_1_MASK = 0xFF, +}; + +/* + * Investigate using the 'rep' prefix to obtain 32 bits of random data + * in one insn. The upside is potentially better performance. The + * downside is that the instruction becomes no longer atomic. Due to + * this, just like familiar issues with /dev/random itself, the worst + * case of a 'rep xstore' could potentially pause a cpu for an + * unreasonably long time. In practice, this condition would likely + * only occur when the hardware is failing. (or so we hope :)) + * + * Another possible performance boost may come from simply buffering + * until we have 4 bytes, thus returning a u32 at a time, + * instead of the current u8-at-a-time. + */ + +static inline u32 xstore(u32 *addr, u32 edx_in) +{ + u32 eax_out; + + asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */" + :"=m"(*addr), "=a"(eax_out) + :"D"(addr), "d"(edx_in)); + + return eax_out; +} + +static int via_data_present(struct hwrng *rng) +{ + u32 bytes_out; + u32 *via_rng_datum = (u32 *)(&rng->priv); + + /* We choose the recommended 1-byte-per-instruction RNG rate, + * for greater randomness at the expense of speed. Larger + * values 2, 4, or 8 bytes-per-instruction yield greater + * speed at lesser randomness. + * + * If you change this to another VIA_CHUNK_n, you must also + * change the ->n_bytes values in rng_vendor_ops[] tables. + * VIA_CHUNK_8 requires further code changes. + * + * A copy of MSR_VIA_RNG is placed in eax_out when xstore + * completes. + */ + + *via_rng_datum = 0; /* paranoia, not really necessary */ + bytes_out = xstore(via_rng_datum, VIA_RNG_CHUNK_1) & VIA_XSTORE_CNT_MASK; + if (bytes_out == 0) + return 0; + + return 1; +} + +static int via_data_read(struct hwrng *rng, u32 *data) +{ + u32 via_rng_datum = (u32)rng->priv; + + *data = via_rng_datum; + + return 1; +} + +static int __init via_init(struct hwrng *rng) +{ + u32 lo, hi, old_lo; + + /* Control the RNG via MSR. Tread lightly and pay very close + * close attention to values written, as the reserved fields + * are documented to be "undefined and unpredictable"; but it + * does not say to write them as zero, so I make a guess that + * we restore the values we find in the register. + */ + rdmsr(MSR_VIA_RNG, lo, hi); + + old_lo = lo; + lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT); + lo &= ~VIA_XSTORE_CNT_MASK; + lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE); + lo |= VIA_RNG_ENABLE; + + if (lo != old_lo) + wrmsr(MSR_VIA_RNG, lo, hi); + + /* perhaps-unnecessary sanity check; remove after testing if + unneeded */ + rdmsr(MSR_VIA_RNG, lo, hi); + if ((lo & VIA_RNG_ENABLE) == 0) { + printk(KERN_ERR PFX "cannot enable VIA C3 RNG, aborting\n"); + return -ENODEV; + } + + return 0; +} + +static void via_cleanup(struct hwrng *rng) +{ + /* do nothing */ +} +#endif + +/*********************************************************************** + * + * AMD Geode RNG operations + * + */ + +#define GEODE_RNG_DATA_REG 0x50 +#define GEODE_RNG_STATUS_REG 0x54 + +static int geode_data_read(struct hwrng *rng, u32 *data) +{ + void __iomem *geode_rng_base = (void __iomem *)rng->priv; + + assert(geode_rng_base != NULL); + *data = readl(geode_rng_base + GEODE_RNG_DATA_REG); + + return 4; +} + +static int geode_data_present(struct hwrng *rng) +{ + void __iomem *geode_rng_base = (void __iomem *)rng->priv; + u32 val; + + assert(geode_rng_base != NULL); + val = readl(geode_rng_base + GEODE_RNG_STATUS_REG); + + return !!val; +} + +static void geode_cleanup(struct hwrng *rng) +{ + void __iomem *geode_rng_base = (void __iomem *)rng->priv; + + iounmap(geode_rng_base); + geode_rng_base = NULL; +} + +static int geode_init(struct hwrng *rng) +{ + void __iomem *geode_rng_base; + struct pci_dev *dev = (struct pci_dev *)rng->priv; + unsigned long rng_base = pci_resource_start(dev, 0); + + if (rng_base == 0) + return 1; + + geode_rng_base = ioremap(rng_base, 0x58); + + if (geode_rng_base == NULL) { + printk(KERN_ERR PFX "Cannot ioremap RNG memory\n"); + return -EBUSY; + } + rng->priv = (unsigned long)geode_rng_base; + + return 0; +} + + +/* + * rng_init - initialize RNG module + */ +static int __init x86_rng_init(void) +{ + int rc; + struct pci_dev *pdev = NULL; + const struct pci_device_id *ent; + + DPRINTK ("ENTER\n"); + + /* Probe for Intel, AMD RNGs */ + for_each_pci_dev(pdev) { + ent = pci_match_id(rng_pci_tbl, pdev); + if (ent) { + x86_rng_ops = &rng_vendor_ops[ent->driver_data]; + goto match; + } + } + + /* Probe for VIA RNG */ + if (cpu_has_xstore) { + x86_rng_ops = &rng_vendor_ops[rng_hw_via]; + pdev = NULL; + goto match; + } + + DPRINTK ("EXIT, returning -ENODEV\n"); + return -ENODEV; + +match: + x86_rng_ops->priv = (unsigned long)pdev; + rc = hwrng_register(x86_rng_ops); + if (rc) + return rc; + + pr_info( RNG_DRIVER_NAME " loaded\n"); + + DPRINTK ("EXIT, returning 0\n"); + return 0; +} + +/* + * rng_init - shutdown RNG module + */ +static void __exit x86_rng_exit (void) +{ + DPRINTK ("ENTER\n"); + + hwrng_unregister(x86_rng_ops); + + DPRINTK ("EXIT\n"); +} + +subsys_initcall(x86_rng_init); +module_exit(x86_rng_exit); + +MODULE_AUTHOR("The Linux Kernel team"); +MODULE_DESCRIPTION("H/W RNG driver for Intel/AMD/VIA chipsets"); +MODULE_LICENSE("GPL"); diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random.c linux-2.6.16-rc5-mm1/drivers/char/hw_random.c --- linux-2.6.16-rc5-mm1.orig/drivers/char/hw_random.c 2006-03-01 13:18:58.000000000 +0100 +++ linux-2.6.16-rc5-mm1/drivers/char/hw_random.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,698 +0,0 @@ -/* - 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 <[email protected]> - - derived from - - Hardware driver for the AMD 768 Random Number Generator (RNG) - (c) Copyright 2001 Red Hat Inc <[email protected]> - - derived from - - Hardware driver for Intel i810 Random Number Generator (RNG) - Copyright 2000,2001 Jeff Garzik <[email protected]> - Copyright 2000,2001 Philipp Rumpf <[email protected]> - - 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/module.h> -#include <linux/kernel.h> -#include <linux/fs.h> -#include <linux/init.h> -#include <linux/pci.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <linux/random.h> -#include <linux/miscdevice.h> -#include <linux/smp_lock.h> -#include <linux/mm.h> -#include <linux/delay.h> - -#ifdef __i386__ -#include <asm/msr.h> -#include <asm/cpufeature.h> -#endif - -#include <asm/io.h> -#include <asm/uaccess.h> - - -/* - * core module and version information - */ -#define RNG_VERSION "1.0.0" -#define RNG_MODULE_NAME "hw_random" -#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION -#define PFX RNG_MODULE_NAME ": " - - -/* - * debugging macros - */ - -/* pr_debug() collapses to a no-op if DEBUG is not defined */ -#define DPRINTK(fmt, args...) pr_debug(PFX "%s: " fmt, __FUNCTION__ , ## args) - - -#undef RNG_NDEBUG /* define to enable lightweight runtime checks */ -#ifdef RNG_NDEBUG -#define assert(expr) \ - if(!(expr)) { \ - printk(KERN_DEBUG PFX "Assertion failed! %s,%s,%s," \ - "line=%d\n", #expr, __FILE__, __FUNCTION__, __LINE__); \ - } -#else -#define assert(expr) -#endif - -#define RNG_MISCDEV_MINOR 183 /* official */ - -static int rng_dev_open (struct inode *inode, struct file *filp); -static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size, - loff_t * offp); - -static int __init intel_init (struct pci_dev *dev); -static void intel_cleanup(void); -static unsigned int intel_data_present (void); -static u32 intel_data_read (void); - -static int __init amd_init (struct pci_dev *dev); -static void amd_cleanup(void); -static unsigned int amd_data_present (void); -static u32 amd_data_read (void); - -#ifdef __i386__ -static int __init via_init(struct pci_dev *dev); -static void via_cleanup(void); -static unsigned int via_data_present (void); -static u32 via_data_read (void); -#endif - -static int __init geode_init(struct pci_dev *dev); -static void geode_cleanup(void); -static unsigned int geode_data_present (void); -static u32 geode_data_read (void); - -struct rng_operations { - int (*init) (struct pci_dev *dev); - void (*cleanup) (void); - unsigned int (*data_present) (void); - u32 (*data_read) (void); - unsigned int n_bytes; /* number of bytes per ->data_read */ -}; -static struct rng_operations *rng_ops; - -static struct file_operations rng_chrdev_ops = { - .owner = THIS_MODULE, - .open = rng_dev_open, - .read = rng_dev_read, -}; - - -static struct miscdevice rng_miscdev = { - RNG_MISCDEV_MINOR, - RNG_MODULE_NAME, - &rng_chrdev_ops, -}; - -enum { - rng_hw_none, - rng_hw_intel, - rng_hw_amd, -#ifdef __i386__ - rng_hw_via, -#endif - rng_hw_geode, -}; - -static struct rng_operations rng_vendor_ops[] = { - /* rng_hw_none */ - { }, - - /* rng_hw_intel */ - { intel_init, intel_cleanup, intel_data_present, - intel_data_read, 1 }, - - /* rng_hw_amd */ - { amd_init, amd_cleanup, amd_data_present, amd_data_read, 4 }, - -#ifdef __i386__ - /* rng_hw_via */ - { via_init, via_cleanup, via_data_present, via_data_read, 1 }, -#endif - - /* rng_hw_geode */ - { geode_init, geode_cleanup, geode_data_present, geode_data_read, 4 } -}; - -/* - * Data for PCI driver interface - * - * This data only exists for exporting the supported - * PCI ids via MODULE_DEVICE_TABLE. We do not actually - * register a pci_driver, because someone else might one day - * want to register another driver on the same PCI id. - */ -static struct pci_device_id rng_pci_tbl[] = { - { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_amd }, - { 0x1022, 0x746b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_amd }, - - { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, - { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, - { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, - { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, - { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, - { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel }, - - { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_geode }, - - { 0, }, /* terminate list */ -}; -MODULE_DEVICE_TABLE (pci, rng_pci_tbl); - - -/*********************************************************************** - * - * Intel RNG operations - * - */ - -/* - * RNG registers (offsets from rng_mem) - */ -#define INTEL_RNG_HW_STATUS 0 -#define INTEL_RNG_PRESENT 0x40 -#define INTEL_RNG_ENABLED 0x01 -#define INTEL_RNG_STATUS 1 -#define INTEL_RNG_DATA_PRESENT 0x01 -#define INTEL_RNG_DATA 2 - -/* - * Magic address at which Intel PCI bridges locate the RNG - */ -#define INTEL_RNG_ADDR 0xFFBC015F -#define INTEL_RNG_ADDR_LEN 3 - -/* token to our ioremap'd RNG register area */ -static void __iomem *rng_mem; - -static inline u8 intel_hwstatus (void) -{ - assert (rng_mem != NULL); - return readb (rng_mem + INTEL_RNG_HW_STATUS); -} - -static inline u8 intel_hwstatus_set (u8 hw_status) -{ - assert (rng_mem != NULL); - writeb (hw_status, rng_mem + INTEL_RNG_HW_STATUS); - return intel_hwstatus (); -} - -static unsigned int intel_data_present(void) -{ - assert (rng_mem != NULL); - - return (readb (rng_mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT) ? - 1 : 0; -} - -static u32 intel_data_read(void) -{ - assert (rng_mem != NULL); - - return readb (rng_mem + INTEL_RNG_DATA); -} - -static int __init intel_init (struct pci_dev *dev) -{ - int rc; - u8 hw_status; - - DPRINTK ("ENTER\n"); - - rng_mem = ioremap (INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN); - if (rng_mem == NULL) { - printk (KERN_ERR PFX "cannot ioremap RNG Memory\n"); - rc = -EBUSY; - goto err_out; - } - - /* Check for Intel 82802 */ - hw_status = intel_hwstatus (); - if ((hw_status & INTEL_RNG_PRESENT) == 0) { - printk (KERN_ERR PFX "RNG not detected\n"); - rc = -ENODEV; - goto err_out_free_map; - } - - /* turn RNG h/w on, if it's off */ - if ((hw_status & INTEL_RNG_ENABLED) == 0) - hw_status = intel_hwstatus_set (hw_status | INTEL_RNG_ENABLED); - if ((hw_status & INTEL_RNG_ENABLED) == 0) { - printk (KERN_ERR PFX "cannot enable RNG, aborting\n"); - rc = -EIO; - goto err_out_free_map; - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_free_map: - iounmap (rng_mem); - rng_mem = NULL; -err_out: - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - -static void intel_cleanup(void) -{ - u8 hw_status; - - hw_status = intel_hwstatus (); - if (hw_status & INTEL_RNG_ENABLED) - intel_hwstatus_set (hw_status & ~INTEL_RNG_ENABLED); - else - printk(KERN_WARNING PFX "unusual: RNG already disabled\n"); - iounmap(rng_mem); - rng_mem = NULL; -} - -/*********************************************************************** - * - * AMD RNG operations - * - */ - -static u32 pmbase; /* PMxx I/O base */ -static struct pci_dev *amd_dev; - -static unsigned int amd_data_present (void) -{ - return inl(pmbase + 0xF4) & 1; -} - - -static u32 amd_data_read (void) -{ - return inl(pmbase + 0xF0); -} - -static int __init amd_init (struct pci_dev *dev) -{ - int rc; - u8 rnen; - - DPRINTK ("ENTER\n"); - - pci_read_config_dword(dev, 0x58, &pmbase); - - pmbase &= 0x0000FF00; - - if (pmbase == 0) - { - printk (KERN_ERR PFX "power management base not set\n"); - rc = -EIO; - goto err_out; - } - - pci_read_config_byte(dev, 0x40, &rnen); - rnen |= (1 << 7); /* RNG on */ - pci_write_config_byte(dev, 0x40, rnen); - - pci_read_config_byte(dev, 0x41, &rnen); - rnen |= (1 << 7); /* PMIO enable */ - pci_write_config_byte(dev, 0x41, rnen); - - pr_info( PFX "AMD768 system management I/O registers at 0x%X.\n", - pmbase); - - amd_dev = dev; - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out: - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - -static void amd_cleanup(void) -{ - u8 rnen; - - pci_read_config_byte(amd_dev, 0x40, &rnen); - rnen &= ~(1 << 7); /* RNG off */ - pci_write_config_byte(amd_dev, 0x40, rnen); - - /* FIXME: twiddle pmio, also? */ -} - -#ifdef __i386__ -/*********************************************************************** - * - * VIA RNG operations - * - */ - -enum { - VIA_STRFILT_CNT_SHIFT = 16, - VIA_STRFILT_FAIL = (1 << 15), - VIA_STRFILT_ENABLE = (1 << 14), - VIA_RAWBITS_ENABLE = (1 << 13), - VIA_RNG_ENABLE = (1 << 6), - VIA_XSTORE_CNT_MASK = 0x0F, - - VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */ - VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */ - VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF, - VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */ - VIA_RNG_CHUNK_2_MASK = 0xFFFF, - VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */ - VIA_RNG_CHUNK_1_MASK = 0xFF, -}; - -static u32 via_rng_datum; - -/* - * Investigate using the 'rep' prefix to obtain 32 bits of random data - * in one insn. The upside is potentially better performance. The - * downside is that the instruction becomes no longer atomic. Due to - * this, just like familiar issues with /dev/random itself, the worst - * case of a 'rep xstore' could potentially pause a cpu for an - * unreasonably long time. In practice, this condition would likely - * only occur when the hardware is failing. (or so we hope :)) - * - * Another possible performance boost may come from simply buffering - * until we have 4 bytes, thus returning a u32 at a time, - * instead of the current u8-at-a-time. - */ - -static inline u32 xstore(u32 *addr, u32 edx_in) -{ - u32 eax_out; - - asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */" - :"=m"(*addr), "=a"(eax_out) - :"D"(addr), "d"(edx_in)); - - return eax_out; -} - -static unsigned int via_data_present(void) -{ - u32 bytes_out; - - /* We choose the recommended 1-byte-per-instruction RNG rate, - * for greater randomness at the expense of speed. Larger - * values 2, 4, or 8 bytes-per-instruction yield greater - * speed at lesser randomness. - * - * If you change this to another VIA_CHUNK_n, you must also - * change the ->n_bytes values in rng_vendor_ops[] tables. - * VIA_CHUNK_8 requires further code changes. - * - * A copy of MSR_VIA_RNG is placed in eax_out when xstore - * completes. - */ - via_rng_datum = 0; /* paranoia, not really necessary */ - bytes_out = xstore(&via_rng_datum, VIA_RNG_CHUNK_1) & VIA_XSTORE_CNT_MASK; - if (bytes_out == 0) - return 0; - - return 1; -} - -static u32 via_data_read(void) -{ - return via_rng_datum; -} - -static int __init via_init(struct pci_dev *dev) -{ - u32 lo, hi, old_lo; - - /* Control the RNG via MSR. Tread lightly and pay very close - * close attention to values written, as the reserved fields - * are documented to be "undefined and unpredictable"; but it - * does not say to write them as zero, so I make a guess that - * we restore the values we find in the register. - */ - rdmsr(MSR_VIA_RNG, lo, hi); - - old_lo = lo; - lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT); - lo &= ~VIA_XSTORE_CNT_MASK; - lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE); - lo |= VIA_RNG_ENABLE; - - if (lo != old_lo) - wrmsr(MSR_VIA_RNG, lo, hi); - - /* perhaps-unnecessary sanity check; remove after testing if - unneeded */ - rdmsr(MSR_VIA_RNG, lo, hi); - if ((lo & VIA_RNG_ENABLE) == 0) { - printk(KERN_ERR PFX "cannot enable VIA C3 RNG, aborting\n"); - return -ENODEV; - } - - return 0; -} - -static void via_cleanup(void) -{ - /* do nothing */ -} -#endif - -/*********************************************************************** - * - * AMD Geode RNG operations - * - */ - -static void __iomem *geode_rng_base = NULL; - -#define GEODE_RNG_DATA_REG 0x50 -#define GEODE_RNG_STATUS_REG 0x54 - -static u32 geode_data_read(void) -{ - u32 val; - - assert(geode_rng_base != NULL); - val = readl(geode_rng_base + GEODE_RNG_DATA_REG); - return val; -} - -static unsigned int geode_data_present(void) -{ - u32 val; - - assert(geode_rng_base != NULL); - val = readl(geode_rng_base + GEODE_RNG_STATUS_REG); - return val; -} - -static void geode_cleanup(void) -{ - iounmap(geode_rng_base); - geode_rng_base = NULL; -} - -static int geode_init(struct pci_dev *dev) -{ - unsigned long rng_base = pci_resource_start(dev, 0); - - if (rng_base == 0) - return 1; - - geode_rng_base = ioremap(rng_base, 0x58); - - if (geode_rng_base == NULL) { - printk(KERN_ERR PFX "Cannot ioremap RNG memory\n"); - return -EBUSY; - } - - return 0; -} - -/*********************************************************************** - * - * /dev/hwrandom character device handling (major 10, minor 183) - * - */ - -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) -{ - static DEFINE_SPINLOCK(rng_lock); - unsigned int have_data; - u32 data = 0; - ssize_t ret = 0; - - while (size) { - spin_lock(&rng_lock); - - have_data = 0; - if (rng_ops->data_present()) { - data = rng_ops->data_read(); - have_data = rng_ops->n_bytes; - } - - spin_unlock (&rng_lock); - - while (have_data && size) { - if (put_user((u8)data, buf++)) { - ret = ret ? : -EFAULT; - break; - } - size--; - ret++; - have_data--; - data>>=8; - } - - if (filp->f_flags & O_NONBLOCK) - return ret ? : -EAGAIN; - - if(need_resched()) - schedule_timeout_interruptible(1); - else - udelay(200); /* FIXME: We could poll for 250uS ?? */ - - if (signal_pending (current)) - return ret ? : -ERESTARTSYS; - } - return ret; -} - - - -/* - * rng_init_one - look for and attempt to init a single RNG - */ -static int __init rng_init_one (struct pci_dev *dev) -{ - int rc; - - DPRINTK ("ENTER\n"); - - assert(rng_ops != NULL); - - rc = rng_ops->init(dev); - if (rc) - goto err_out; - - rc = misc_register (&rng_miscdev); - if (rc) { - printk (KERN_ERR PFX "misc device register failed\n"); - goto err_out_cleanup_hw; - } - - DPRINTK ("EXIT, returning 0\n"); - return 0; - -err_out_cleanup_hw: - rng_ops->cleanup(); -err_out: - DPRINTK ("EXIT, returning %d\n", rc); - return rc; -} - - - -MODULE_AUTHOR("The Linux Kernel team"); -MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver"); -MODULE_LICENSE("GPL"); - - -/* - * rng_init - initialize RNG module - */ -static int __init rng_init (void) -{ - int rc; - struct pci_dev *pdev = NULL; - const struct pci_device_id *ent; - - DPRINTK ("ENTER\n"); - - /* Probe for Intel, AMD, Geode RNGs */ - for_each_pci_dev(pdev) { - ent = pci_match_id(rng_pci_tbl, pdev); - if (ent) { - rng_ops = &rng_vendor_ops[ent->driver_data]; - goto match; - } - } - -#ifdef __i386__ - /* Probe for VIA RNG */ - if (cpu_has_xstore) { - rng_ops = &rng_vendor_ops[rng_hw_via]; - pdev = NULL; - goto match; - } -#endif - - DPRINTK ("EXIT, returning -ENODEV\n"); - return -ENODEV; - -match: - rc = rng_init_one (pdev); - if (rc) - return rc; - - pr_info( RNG_DRIVER_NAME " loaded\n"); - - DPRINTK ("EXIT, returning 0\n"); - return 0; -} - - -/* - * rng_init - shutdown RNG module - */ -static void __exit rng_cleanup (void) -{ - DPRINTK ("ENTER\n"); - - misc_deregister (&rng_miscdev); - - if (rng_ops->cleanup) - rng_ops->cleanup(); - - DPRINTK ("EXIT\n"); -} - - -module_init (rng_init); -module_exit (rng_cleanup); diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/drivers/char/Kconfig linux-2.6.16-rc5-mm1/drivers/char/Kconfig --- linux-2.6.16-rc5-mm1.orig/drivers/char/Kconfig 2006-03-01 13:18:58.000000000 +0100 +++ linux-2.6.16-rc5-mm1/drivers/char/Kconfig 2006-03-01 13:48:16.000000000 +0100 @@ -652,20 +652,7 @@ If you're not sure, say N. -config HW_RANDOM - tristate "Intel/AMD/VIA HW Random Number Generator support" - depends on (X86 || IA64) && PCI - ---help--- - This driver provides kernel-side support for the Random Number - Generator hardware found on Intel i8xx-based motherboards, - AMD 76x-based motherboards, and Via Nehemiah CPUs. - - Provides a character driver, used to read() entropy data. - - To compile this driver as a module, choose M here: the - module will be called hw_random. - - If unsure, say N. +source "drivers/char/hw_random/Kconfig" config NVRAM tristate "/dev/nvram support" diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/drivers/char/Makefile linux-2.6.16-rc5-mm1/drivers/char/Makefile --- linux-2.6.16-rc5-mm1.orig/drivers/char/Makefile 2006-03-01 13:18:57.000000000 +0100 +++ linux-2.6.16-rc5-mm1/drivers/char/Makefile 2006-03-01 13:27:47.000000000 +0100 @@ -74,7 +74,7 @@ obj-$(CONFIG_TOSHIBA) += toshiba.o obj-$(CONFIG_I8K) += i8k.o obj-$(CONFIG_DS1620) += ds1620.o -obj-$(CONFIG_HW_RANDOM) += hw_random.o +obj-$(CONFIG_HW_RANDOM) += hw_random/ obj-$(CONFIG_FTAPE) += ftape/ obj-$(CONFIG_COBALT_LCD) += lcd.o obj-$(CONFIG_PPDEV) += ppdev.o diff -urNX linux-2.6.16-rc5-mm1/Documentation/dontdiff linux-2.6.16-rc5-mm1.orig/include/linux/hw_random.h linux-2.6.16-rc5-mm1/include/linux/hw_random.h --- linux-2.6.16-rc5-mm1.orig/include/linux/hw_random.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.16-rc5-mm1/include/linux/hw_random.h 2006-03-01 14:12:54.000000000 +0100 @@ -0,0 +1,44 @@ +/* + 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_ + +#include <linux/types.h> +#include <linux/list.h> + +struct pci_dev; + +struct hwrng { + /** Unique name. */ + const char *name; + + /** Initialization callback. */ + int (*init) (struct hwrng *rng); + /** Cleanup callback. */ + void (*cleanup) (struct hwrng *rng); + /** Is the RNG able to provide data now? */ + int (*data_present) (struct hwrng *rng); + /** Read data from the RNG device. + * Returns the number of random bytes in data. + */ + int (*data_read) (struct hwrng *rng, u32 *data); + /** Private data, for use by the RNG driver. */ + unsigned long priv; + + /* internal. */ + struct list_head list; +}; + +extern int hwrng_register(struct hwrng *rng); +extern void hwrng_unregister(struct hwrng *rng); + +#endif /* LINUX_HWRANDOM_H_ */ -- Greetings Michael.
Attachment:
pgpWanmk0Z3rg.pgp
Description: PGP signature
- References:
- [PATCH] Generic hardware RNG support
- From: Michael Buesch <[email protected]>
- Re: [PATCH] Generic hardware RNG support
- From: Deepak Saxena <[email protected]>
- Re: [PATCH] Generic hardware RNG support
- From: Kumar Gala <[email protected]>
- [PATCH] Generic hardware RNG support
- Prev by Date: Re: depmod kernel compilation
- Next by Date: RE: adding swap workarounds oom - was: Re: Out of Memory: Killed process 16498 (java).
- Previous by thread: Re: [PATCH] Generic hardware RNG support
- Next by thread: 2.6.16-rc5-mm1
- Index(es):