diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/char/hw_random/core.c | 67 | ||||
-rw-r--r-- | drivers/char/random.c | 315 |
2 files changed, 252 insertions, 130 deletions
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c index c4419ea1ab07..6e02ec103cc7 100644 --- a/drivers/char/hw_random/core.c +++ b/drivers/char/hw_random/core.c @@ -38,6 +38,7 @@ #include <linux/fs.h> #include <linux/sched.h> #include <linux/miscdevice.h> +#include <linux/kthread.h> #include <linux/delay.h> #include <linux/slab.h> #include <linux/random.h> @@ -50,10 +51,22 @@ static struct hwrng *current_rng; +static struct task_struct *hwrng_fill; static LIST_HEAD(rng_list); static DEFINE_MUTEX(rng_mutex); static int data_avail; -static u8 *rng_buffer; +static u8 *rng_buffer, *rng_fillbuf; +static unsigned short current_quality; +static unsigned short default_quality; /* = 0; default to "off" */ + +module_param(current_quality, ushort, 0644); +MODULE_PARM_DESC(current_quality, + "current hwrng entropy estimation per mill"); +module_param(default_quality, ushort, 0644); +MODULE_PARM_DESC(default_quality, + "default entropy content of hwrng per mill"); + +static void start_khwrngd(void); static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size, int wait); @@ -89,6 +102,15 @@ static inline int hwrng_init(struct hwrng *rng) return ret; } add_early_randomness(rng); + + current_quality = rng->quality ? : default_quality; + current_quality &= 1023; + + if (current_quality == 0 && hwrng_fill) + kthread_stop(hwrng_fill); + if (current_quality > 0 && !hwrng_fill) + start_khwrngd(); + return 0; } @@ -325,6 +347,36 @@ err_misc_dereg: goto out; } +static int hwrng_fillfn(void *unused) +{ + long rc; + + while (!kthread_should_stop()) { + if (!current_rng) + break; + rc = rng_get_data(current_rng, rng_fillbuf, + rng_buffer_size(), 1); + if (rc <= 0) { + pr_warn("hwrng: no data available\n"); + msleep_interruptible(10000); + continue; + } + add_hwgenerator_randomness((void *)rng_fillbuf, rc, + rc * current_quality * 8 >> 10); + } + hwrng_fill = NULL; + return 0; +} + +static void start_khwrngd(void) +{ + hwrng_fill = kthread_run(hwrng_fillfn, NULL, "hwrng"); + if (hwrng_fill == ERR_PTR(-ENOMEM)) { + pr_err("hwrng_fill thread creation failed"); + hwrng_fill = NULL; + } +} + int hwrng_register(struct hwrng *rng) { int err = -EINVAL; @@ -343,6 +395,13 @@ int hwrng_register(struct hwrng *rng) if (!rng_buffer) goto out_unlock; } + if (!rng_fillbuf) { + rng_fillbuf = kmalloc(rng_buffer_size(), GFP_KERNEL); + if (!rng_fillbuf) { + kfree(rng_buffer); + goto out_unlock; + } + } /* Must not register two RNGs with the same name. */ err = -EEXIST; @@ -406,8 +465,11 @@ void hwrng_unregister(struct hwrng *rng) current_rng = NULL; } } - if (list_empty(&rng_list)) + if (list_empty(&rng_list)) { unregister_miscdev(); + if (hwrng_fill) + kthread_stop(hwrng_fill); + } mutex_unlock(&rng_mutex); } @@ -418,6 +480,7 @@ static void __exit hwrng_exit(void) mutex_lock(&rng_mutex); BUG_ON(current_rng); kfree(rng_buffer); + kfree(rng_fillbuf); mutex_unlock(&rng_mutex); } diff --git a/drivers/char/random.c b/drivers/char/random.c index 71529e196b84..c18d41db83d8 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -250,6 +250,7 @@ #include <linux/interrupt.h> #include <linux/mm.h> #include <linux/spinlock.h> +#include <linux/kthread.h> #include <linux/percpu.h> #include <linux/cryptohash.h> #include <linux/fips.h> @@ -257,6 +258,8 @@ #include <linux/kmemcheck.h> #include <linux/workqueue.h> #include <linux/irq.h> +#include <linux/syscalls.h> +#include <linux/completion.h> #include <asm/processor.h> #include <asm/uaccess.h> @@ -267,6 +270,8 @@ #define CREATE_TRACE_POINTS #include <trace/events/random.h> +/* #define ADD_INTERRUPT_BENCH */ + /* * Configuration information */ @@ -401,6 +406,7 @@ static struct poolinfo { */ static DECLARE_WAIT_QUEUE_HEAD(random_read_wait); static DECLARE_WAIT_QUEUE_HEAD(random_write_wait); +static DECLARE_WAIT_QUEUE_HEAD(urandom_init_wait); static struct fasync_struct *fasync; /********************************************************************** @@ -481,9 +487,9 @@ static __u32 const twist_table[8] = { * the entropy is concentrated in the low-order bits. */ static void _mix_pool_bytes(struct entropy_store *r, const void *in, - int nbytes, __u8 out[64]) + int nbytes) { - unsigned long i, j, tap1, tap2, tap3, tap4, tap5; + unsigned long i, tap1, tap2, tap3, tap4, tap5; int input_rotate; int wordmask = r->poolinfo->poolwords - 1; const char *bytes = in; @@ -495,9 +501,8 @@ static void _mix_pool_bytes(struct entropy_store *r, const void *in, tap4 = r->poolinfo->tap4; tap5 = r->poolinfo->tap5; - smp_rmb(); - input_rotate = ACCESS_ONCE(r->input_rotate); - i = ACCESS_ONCE(r->add_ptr); + input_rotate = r->input_rotate; + i = r->add_ptr; /* mix one byte at a time to simplify size handling and churn faster */ while (nbytes--) { @@ -524,39 +529,33 @@ static void _mix_pool_bytes(struct entropy_store *r, const void *in, input_rotate = (input_rotate + (i ? 7 : 14)) & 31; } - ACCESS_ONCE(r->input_rotate) = input_rotate; - ACCESS_ONCE(r->add_ptr) = i; - smp_wmb(); - - if (out) - for (j = 0; j < 16; j++) - ((__u32 *)out)[j] = r->pool[(i - j) & wordmask]; + r->input_rotate = input_rotate; + r->add_ptr = i; } static void __mix_pool_bytes(struct entropy_store *r, const void *in, - int nbytes, __u8 out[64]) + int nbytes) { trace_mix_pool_bytes_nolock(r->name, nbytes, _RET_IP_); - _mix_pool_bytes(r, in, nbytes, out); + _mix_pool_bytes(r, in, nbytes); } static void mix_pool_bytes(struct entropy_store *r, const void *in, - int nbytes, __u8 out[64]) + int nbytes) { unsigned long flags; trace_mix_pool_bytes(r->name, nbytes, _RET_IP_); spin_lock_irqsave(&r->lock, flags); - _mix_pool_bytes(r, in, nbytes, out); + _mix_pool_bytes(r, in, nbytes); spin_unlock_irqrestore(&r->lock, flags); } struct fast_pool { __u32 pool[4]; unsigned long last; - unsigned short count; - unsigned char rotate; - unsigned char last_timer_intr; + unsigned short reg_idx; + unsigned char count; }; /* @@ -564,25 +563,29 @@ struct fast_pool { * collector. It's hardcoded for an 128 bit pool and assumes that any * locks that might be needed are taken by the caller. */ -static void fast_mix(struct fast_pool *f, __u32 input[4]) +static void fast_mix(struct fast_pool *f) { - __u32 w; - unsigned input_rotate = f->rotate; - - w = rol32(input[0], input_rotate) ^ f->pool[0] ^ f->pool[3]; - f->pool[0] = (w >> 3) ^ twist_table[w & 7]; - input_rotate = (input_rotate + 14) & 31; - w = rol32(input[1], input_rotate) ^ f->pool[1] ^ f->pool[0]; - f->pool[1] = (w >> 3) ^ twist_table[w & 7]; - input_rotate = (input_rotate + 7) & 31; - w = rol32(input[2], input_rotate) ^ f->pool[2] ^ f->pool[1]; - f->pool[2] = (w >> 3) ^ twist_table[w & 7]; - input_rotate = (input_rotate + 7) & 31; - w = rol32(input[3], input_rotate) ^ f->pool[3] ^ f->pool[2]; - f->pool[3] = (w >> 3) ^ twist_table[w & 7]; - input_rotate = (input_rotate + 7) & 31; - - f->rotate = input_rotate; + __u32 a = f->pool[0], b = f->pool[1]; + __u32 c = f->pool[2], d = f->pool[3]; + + a += b; c += d; + b = rol32(a, 6); d = rol32(c, 27); + d ^= a; b ^= c; + + a += b; c += d; + b = rol32(a, 16); d = rol32(c, 14); + d ^= a; b ^= c; + + a += b; c += d; + b = rol32(a, 6); d = rol32(c, 27); + d ^= a; b ^= c; + + a += b; c += d; + b = rol32(a, 16); d = rol32(c, 14); + d ^= a; b ^= c; + + f->pool[0] = a; f->pool[1] = b; + f->pool[2] = c; f->pool[3] = d; f->count++; } @@ -657,6 +660,7 @@ retry: r->entropy_total = 0; if (r == &nonblocking_pool) { prandom_reseed_late(); + wake_up_interruptible(&urandom_init_wait); pr_notice("random: %s pool is initialized\n", r->name); } } @@ -739,13 +743,13 @@ void add_device_randomness(const void *buf, unsigned int size) trace_add_device_randomness(size, _RET_IP_); spin_lock_irqsave(&input_pool.lock, flags); - _mix_pool_bytes(&input_pool, buf, size, NULL); - _mix_pool_bytes(&input_pool, &time, sizeof(time), NULL); + _mix_pool_bytes(&input_pool, buf, size); + _mix_pool_bytes(&input_pool, &time, sizeof(time)); spin_unlock_irqrestore(&input_pool.lock, flags); spin_lock_irqsave(&nonblocking_pool.lock, flags); - _mix_pool_bytes(&nonblocking_pool, buf, size, NULL); - _mix_pool_bytes(&nonblocking_pool, &time, sizeof(time), NULL); + _mix_pool_bytes(&nonblocking_pool, buf, size); + _mix_pool_bytes(&nonblocking_pool, &time, sizeof(time)); spin_unlock_irqrestore(&nonblocking_pool.lock, flags); } EXPORT_SYMBOL(add_device_randomness); @@ -778,7 +782,7 @@ static void add_timer_randomness(struct timer_rand_state *state, unsigned num) sample.cycles = random_get_entropy(); sample.num = num; r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool; - mix_pool_bytes(r, &sample, sizeof(sample), NULL); + mix_pool_bytes(r, &sample, sizeof(sample)); /* * Calculate number of bits of randomness we probably added. @@ -835,6 +839,38 @@ EXPORT_SYMBOL_GPL(add_input_randomness); static DEFINE_PER_CPU(struct fast_pool, irq_randomness); +#ifdef ADD_INTERRUPT_BENCH +static unsigned long avg_cycles, avg_deviation; + +#define AVG_SHIFT 8 /* Exponential average factor k=1/256 */ +#define FIXED_1_2 (1 << (AVG_SHIFT-1)) + +static void add_interrupt_bench(cycles_t start) +{ + long delta = random_get_entropy() - start; + + /* Use a weighted moving average */ + delta = delta - ((avg_cycles + FIXED_1_2) >> AVG_SHIFT); + avg_cycles += delta; + /* And average deviation */ + delta = abs(delta) - ((avg_deviation + FIXED_1_2) >> AVG_SHIFT); + avg_deviation += delta; +} +#else +#define add_interrupt_bench(x) +#endif + +static __u32 get_reg(struct fast_pool *f, struct pt_regs *regs) +{ + __u32 *ptr = (__u32 *) regs; + + if (regs == NULL) + return 0; + if (f->reg_idx >= sizeof(struct pt_regs) / sizeof(__u32)) + f->reg_idx = 0; + return *(ptr + f->reg_idx++); +} + void add_interrupt_randomness(int irq, int irq_flags) { struct entropy_store *r; @@ -842,55 +878,52 @@ void add_interrupt_randomness(int irq, int irq_flags) struct pt_regs *regs = get_irq_regs(); unsigned long now = jiffies; cycles_t cycles = random_get_entropy(); - __u32 input[4], c_high, j_high; + __u32 c_high, j_high; __u64 ip; unsigned long seed; - int credit; + int credit = 0; + if (cycles == 0) + cycles = get_reg(fast_pool, regs); c_high = (sizeof(cycles) > 4) ? cycles >> 32 : 0; j_high = (sizeof(now) > 4) ? now >> 32 : 0; - input[0] = cycles ^ j_high ^ irq; - input[1] = now ^ c_high; + fast_pool->pool[0] ^= cycles ^ j_high ^ irq; + fast_pool->pool[1] ^= now ^ c_high; ip = regs ? instruction_pointer(regs) : _RET_IP_; - input[2] = ip; - input[3] = ip >> 32; + fast_pool->pool[2] ^= ip; + fast_pool->pool[3] ^= (sizeof(ip) > 4) ? ip >> 32 : + get_reg(fast_pool, regs); - fast_mix(fast_pool, input); + fast_mix(fast_pool); + add_interrupt_bench(cycles); - if ((fast_pool->count & 63) && !time_after(now, fast_pool->last + HZ)) + if ((fast_pool->count < 64) && + !time_after(now, fast_pool->last + HZ)) return; - fast_pool->last = now; - r = nonblocking_pool.initialized ? &input_pool : &nonblocking_pool; - __mix_pool_bytes(r, &fast_pool->pool, sizeof(fast_pool->pool), NULL); + if (!spin_trylock(&r->lock)) + return; - /* - * If we don't have a valid cycle counter, and we see - * back-to-back timer interrupts, then skip giving credit for - * any entropy, otherwise credit 1 bit. - */ - credit = 1; - if (cycles == 0) { - if (irq_flags & __IRQF_TIMER) { - if (fast_pool->last_timer_intr) - credit = 0; - fast_pool->last_timer_intr = 1; - } else - fast_pool->last_timer_intr = 0; - } + fast_pool->last = now; + __mix_pool_bytes(r, &fast_pool->pool, sizeof(fast_pool->pool)); /* * If we have architectural seed generator, produce a seed and - * add it to the pool. For the sake of paranoia count it as - * 50% entropic. + * add it to the pool. For the sake of paranoia don't let the + * architectural seed generator dominate the input from the + * interrupt noise. */ if (arch_get_random_seed_long(&seed)) { - __mix_pool_bytes(r, &seed, sizeof(seed), NULL); - credit += sizeof(seed) * 4; + __mix_pool_bytes(r, &seed, sizeof(seed)); + credit = 1; } + spin_unlock(&r->lock); - credit_entropy_bits(r, credit); + fast_pool->count = 0; + + /* award one bit for the contents of the fast pool */ + credit_entropy_bits(r, credit + 1); } #ifdef CONFIG_BLOCK @@ -922,6 +955,11 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf, static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes); static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) { + if (!r->pull || + r->entropy_count >= (nbytes << (ENTROPY_SHIFT + 3)) || + r->entropy_count > r->poolinfo->poolfracbits) + return; + if (r->limit == 0 && random_min_urandom_seed) { unsigned long now = jiffies; @@ -930,10 +968,8 @@ static void xfer_secondary_pool(struct entropy_store *r, size_t nbytes) return; r->last_pulled = now; } - if (r->pull && - r->entropy_count < (nbytes << (ENTROPY_SHIFT + 3)) && - r->entropy_count < r->poolinfo->poolfracbits) - _xfer_secondary_pool(r, nbytes); + + _xfer_secondary_pool(r, nbytes); } static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes) @@ -953,7 +989,7 @@ static void _xfer_secondary_pool(struct entropy_store *r, size_t nbytes) ENTROPY_BITS(r), ENTROPY_BITS(r->pull)); bytes = extract_entropy(r->pull, tmp, bytes, random_read_wakeup_bits / 8, rsvd_bytes); - mix_pool_bytes(r, tmp, bytes, NULL); + mix_pool_bytes(r, tmp, bytes); credit_entropy_bits(r, bytes*8); } @@ -1039,7 +1075,6 @@ static void extract_buf(struct entropy_store *r, __u8 *out) unsigned long l[LONGS(20)]; } hash; __u32 workspace[SHA_WORKSPACE_WORDS]; - __u8 extract[64]; unsigned long flags; /* @@ -1068,15 +1103,9 @@ static void extract_buf(struct entropy_store *r, __u8 *out) * brute-forcing the feedback as hard as brute-forcing the * hash. */ - __mix_pool_bytes(r, hash.w, sizeof(hash.w), extract); + __mix_pool_bytes(r, hash.w, sizeof(hash.w)); spin_unlock_irqrestore(&r->lock, flags); - /* - * To avoid duplicates, we atomically extract a portion of the - * pool while mixing, and hash one final time. - */ - sha_transform(hash.w, extract, workspace); - memset(extract, 0, sizeof(extract)); memset(workspace, 0, sizeof(workspace)); /* @@ -1160,13 +1189,14 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf, { ssize_t ret = 0, i; __u8 tmp[EXTRACT_SIZE]; + int large_request = (nbytes > 256); trace_extract_entropy_user(r->name, nbytes, ENTROPY_BITS(r), _RET_IP_); xfer_secondary_pool(r, nbytes); nbytes = account(r, nbytes, 0, 0); while (nbytes) { - if (need_resched()) { + if (large_request && need_resched()) { if (signal_pending(current)) { if (ret == 0) ret = -ERESTARTSYS; @@ -1263,14 +1293,14 @@ static void init_std_data(struct entropy_store *r) unsigned long rv; r->last_pulled = jiffies; - mix_pool_bytes(r, &now, sizeof(now), NULL); + mix_pool_bytes(r, &now, sizeof(now)); for (i = r->poolinfo->poolbytes; i > 0; i -= sizeof(rv)) { if (!arch_get_random_seed_long(&rv) && !arch_get_random_long(&rv)) rv = random_get_entropy(); - mix_pool_bytes(r, &rv, sizeof(rv), NULL); + mix_pool_bytes(r, &rv, sizeof(rv)); } - mix_pool_bytes(r, utsname(), sizeof(*(utsname())), NULL); + mix_pool_bytes(r, utsname(), sizeof(*(utsname()))); } /* @@ -1309,39 +1339,8 @@ void rand_initialize_disk(struct gendisk *disk) } #endif -/* - * Attempt an emergency refill using arch_get_random_seed_long(). - * - * As with add_interrupt_randomness() be paranoid and only - * credit the output as 50% entropic. - */ -static int arch_random_refill(void) -{ - const unsigned int nlongs = 64; /* Arbitrary number */ - unsigned int n = 0; - unsigned int i; - unsigned long buf[nlongs]; - - if (!arch_has_random_seed()) - return 0; - - for (i = 0; i < nlongs; i++) { - if (arch_get_random_seed_long(&buf[n])) - n++; - } - - if (n) { - unsigned int rand_bytes = n * sizeof(unsigned long); - - mix_pool_bytes(&input_pool, buf, rand_bytes, NULL); - credit_entropy_bits(&input_pool, rand_bytes*4); - } - - return n; -} - static ssize_t -random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) +_random_read(int nonblock, char __user *buf, size_t nbytes) { ssize_t n; @@ -1360,12 +1359,7 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) return n; /* Pool is (near) empty. Maybe wait and retry. */ - - /* First try an emergency refill */ - if (arch_random_refill()) - continue; - - if (file->f_flags & O_NONBLOCK) + if (nonblock) return -EAGAIN; wait_event_interruptible(random_read_wait, @@ -1377,6 +1371,12 @@ random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) } static ssize_t +random_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) +{ + return _random_read(file->f_flags & O_NONBLOCK, buf, nbytes); +} + +static ssize_t urandom_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { int ret; @@ -1424,7 +1424,7 @@ write_pool(struct entropy_store *r, const char __user *buffer, size_t count) count -= bytes; p += bytes; - mix_pool_bytes(r, buf, bytes, NULL); + mix_pool_bytes(r, buf, bytes); cond_resched(); } @@ -1520,6 +1520,29 @@ const struct file_operations urandom_fops = { .llseek = noop_llseek, }; +SYSCALL_DEFINE3(getrandom, char __user *, buf, size_t, count, + unsigned int, flags) +{ + if (flags & ~(GRND_NONBLOCK|GRND_RANDOM)) + return -EINVAL; + + if (count > INT_MAX) + count = INT_MAX; + + if (flags & GRND_RANDOM) + return _random_read(flags & GRND_NONBLOCK, buf, count); + + if (unlikely(nonblocking_pool.initialized == 0)) { + if (flags & GRND_NONBLOCK) + return -EAGAIN; + wait_event_interruptible(urandom_init_wait, + nonblocking_pool.initialized); + if (signal_pending(current)) + return -ERESTARTSYS; + } + return urandom_read(NULL, buf, count, NULL); +} + /*************************************************************** * Random UUID interface * @@ -1663,6 +1686,22 @@ struct ctl_table random_table[] = { .mode = 0444, .proc_handler = proc_do_uuid, }, +#ifdef ADD_INTERRUPT_BENCH + { + .procname = "add_interrupt_avg_cycles", + .data = &avg_cycles, + .maxlen = sizeof(avg_cycles), + .mode = 0444, + .proc_handler = proc_doulongvec_minmax, + }, + { + .procname = "add_interrupt_avg_deviation", + .data = &avg_deviation, + .maxlen = sizeof(avg_deviation), + .mode = 0444, + .proc_handler = proc_doulongvec_minmax, + }, +#endif { } }; #endif /* CONFIG_SYSCTL */ @@ -1719,3 +1758,23 @@ randomize_range(unsigned long start, unsigned long end, unsigned long len) return 0; return PAGE_ALIGN(get_random_int() % range + start); } + +/* Interface for in-kernel drivers of true hardware RNGs. + * Those devices may produce endless random bits and will be throttled + * when our pool is full. + */ +void add_hwgenerator_randomness(const char *buffer, size_t count, + size_t entropy) +{ + struct entropy_store *poolp = &input_pool; + + /* Suspend writing if we're above the trickle threshold. + * We'll be woken up again once below random_write_wakeup_thresh, + * or when the calling thread is about to terminate. + */ + wait_event_interruptible(random_write_wait, kthread_should_stop() || + ENTROPY_BITS(&input_pool) <= random_write_wakeup_bits); + mix_pool_bytes(poolp, buffer, count); + credit_entropy_bits(poolp, entropy); +} +EXPORT_SYMBOL_GPL(add_hwgenerator_randomness); |