diff options
Diffstat (limited to 'drivers/char/hw_random/core.c')
| -rw-r--r-- | drivers/char/hw_random/core.c | 86 | 
1 files changed, 70 insertions, 16 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index 788da05190c..c4419ea1ab0 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -19,7 +19,7 @@  	Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>  	Added generic RNG API -	Copyright 2006 Michael Buesch <mbuesch@freenet.de> +	Copyright 2006 Michael Buesch <m@bues.ch>  	Copyright 2005 (c) MontaVista Software, Inc.  	Please read Documentation/hw_random.txt for details on use. @@ -37,10 +37,10 @@  #include <linux/kernel.h>  #include <linux/fs.h>  #include <linux/sched.h> -#include <linux/smp_lock.h> -#include <linux/init.h>  #include <linux/miscdevice.h>  #include <linux/delay.h> +#include <linux/slab.h> +#include <linux/random.h>  #include <asm/uaccess.h> @@ -53,14 +53,43 @@ static struct hwrng *current_rng;  static LIST_HEAD(rng_list);  static DEFINE_MUTEX(rng_mutex);  static int data_avail; -static u8 rng_buffer[SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES] -	__cacheline_aligned; +static u8 *rng_buffer; + +static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, +			       int wait); + +static size_t rng_buffer_size(void) +{ +	return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES; +} + +static void add_early_randomness(struct hwrng *rng) +{ +	unsigned char bytes[16]; +	int bytes_read; + +	/* +	 * Currently only virtio-rng cannot return data during device +	 * probe, and that's handled in virtio-rng.c itself.  If there +	 * are more such devices, this call to rng_get_data can be +	 * made conditional here instead of doing it per-device. +	 */ +	bytes_read = rng_get_data(rng, bytes, sizeof(bytes), 1); +	if (bytes_read > 0) +		add_device_randomness(bytes, bytes_read); +}  static inline int hwrng_init(struct hwrng *rng)  { -	if (!rng->init) -		return 0; -	return rng->init(rng); +	if (rng->init) { +		int ret; + +		ret =  rng->init(rng); +		if (ret) +			return ret; +	} +	add_early_randomness(rng); +	return 0;  }  static inline void hwrng_cleanup(struct hwrng *rng) @@ -117,7 +146,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,  		if (!data_avail) {  			bytes_read = rng_get_data(current_rng, rng_buffer, -				sizeof(rng_buffer), +				rng_buffer_size(),  				!(filp->f_flags & O_NONBLOCK));  			if (bytes_read < 0) {  				err = bytes_read; @@ -298,7 +327,6 @@ err_misc_dereg:  int hwrng_register(struct hwrng *rng)  { -	int must_register_misc;  	int err = -EINVAL;  	struct hwrng *old_rng, *tmp; @@ -308,6 +336,14 @@ int hwrng_register(struct hwrng *rng)  	mutex_lock(&rng_mutex); +	/* kmalloc makes this safe for virt_to_page() in virtio_rng.c */ +	err = -ENOMEM; +	if (!rng_buffer) { +		rng_buffer = kmalloc(rng_buffer_size(), GFP_KERNEL); +		if (!rng_buffer) +			goto out_unlock; +	} +  	/* Must not register two RNGs with the same name. */  	err = -EEXIST;  	list_for_each_entry(tmp, &rng_list, list) { @@ -315,7 +351,6 @@ int hwrng_register(struct hwrng *rng)  			goto out_unlock;  	} -	must_register_misc = (current_rng == NULL);  	old_rng = current_rng;  	if (!old_rng) {  		err = hwrng_init(rng); @@ -324,18 +359,28 @@ int hwrng_register(struct hwrng *rng)  		current_rng = rng;  	}  	err = 0; -	if (must_register_misc) { +	if (!old_rng) {  		err = register_miscdev();  		if (err) { -			if (!old_rng) { -				hwrng_cleanup(rng); -				current_rng = NULL; -			} +			hwrng_cleanup(rng); +			current_rng = NULL;  			goto out_unlock;  		}  	}  	INIT_LIST_HEAD(&rng->list);  	list_add_tail(&rng->list, &rng_list); + +	if (old_rng && !rng->init) { +		/* +		 * Use a new device's input to add some randomness to +		 * the system.  If this rng device isn't going to be +		 * used right away, its init function hasn't been +		 * called yet; so only use the randomness from devices +		 * that don't need an init callback. +		 */ +		add_early_randomness(rng); +	} +  out_unlock:  	mutex_unlock(&rng_mutex);  out: @@ -368,6 +413,15 @@ void hwrng_unregister(struct hwrng *rng)  }  EXPORT_SYMBOL_GPL(hwrng_unregister); +static void __exit hwrng_exit(void) +{ +	mutex_lock(&rng_mutex); +	BUG_ON(current_rng); +	kfree(rng_buffer); +	mutex_unlock(&rng_mutex); +} + +module_exit(hwrng_exit);  MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");  MODULE_LICENSE("GPL");  | 
