headers: remove sched.h from interrupt.h
[safe/jmp/linux-2.6] / drivers / char / random.c
index 7c13581..04b505e 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/percpu.h>
 #include <linux/cryptohash.h>
+#include <linux/fips.h>
+
+#ifdef CONFIG_GENERIC_HARDIRQS
+# include <linux/irq.h>
+#endif
 
 #include <asm/processor.h>
 #include <asm/uaccess.h>
@@ -409,6 +414,7 @@ struct entropy_store {
        unsigned add_ptr;
        int entropy_count;
        int input_rotate;
+       __u8 *last_data;
 };
 
 static __u32 input_pool_data[INPUT_POOL_WORDS];
@@ -558,7 +564,7 @@ struct timer_rand_state {
        unsigned dont_count_entropy:1;
 };
 
-#ifndef CONFIG_SPARSE_IRQ
+#ifndef CONFIG_GENERIC_HARDIRQS
 
 static struct timer_rand_state *irq_timer_state[NR_IRQS];
 
@@ -848,12 +854,21 @@ static ssize_t extract_entropy(struct entropy_store *r, void *buf,
 {
        ssize_t ret = 0, i;
        __u8 tmp[EXTRACT_SIZE];
+       unsigned long flags;
 
        xfer_secondary_pool(r, nbytes);
        nbytes = account(r, nbytes, min, reserved);
 
        while (nbytes) {
                extract_buf(r, tmp);
+
+               if (r->last_data) {
+                       spin_lock_irqsave(&r->lock, flags);
+                       if (!memcmp(tmp, r->last_data, EXTRACT_SIZE))
+                               panic("Hardware RNG duplicated output!\n");
+                       memcpy(r->last_data, tmp, EXTRACT_SIZE);
+                       spin_unlock_irqrestore(&r->lock, flags);
+               }
                i = min_t(int, nbytes, EXTRACT_SIZE);
                memcpy(buf, tmp, i);
                nbytes -= i;
@@ -936,6 +951,9 @@ static void init_std_data(struct entropy_store *r)
        now = ktime_get_real();
        mix_pool_bytes(r, &now, sizeof(now));
        mix_pool_bytes(r, utsname(), sizeof(*(utsname())));
+       /* Enable continuous test in fips mode */
+       if (fips_enabled)
+               r->last_data = kmalloc(EXTRACT_SIZE, GFP_KERNEL);
 }
 
 static int rand_initialize(void)
@@ -1213,7 +1231,7 @@ static char sysctl_bootid[16];
  * as an ASCII string in the standard UUID format.  If accesses via the
  * sysctl system call, it is returned as 16 bytes of binary data.
  */
-static int proc_do_uuid(ctl_table *table, int write, struct file *filp,
+static int proc_do_uuid(ctl_table *table, int write,
                        void __user *buffer, size_t *lenp, loff_t *ppos)
 {
        ctl_table fake_table;
@@ -1236,7 +1254,7 @@ static int proc_do_uuid(ctl_table *table, int write, struct file *filp,
        fake_table.data = buf;
        fake_table.maxlen = sizeof(buf);
 
-       return proc_dostring(&fake_table, write, filp, buffer, lenp, ppos);
+       return proc_dostring(&fake_table, write, buffer, lenp, ppos);
 }
 
 static int uuid_strategy(ctl_table *table,
@@ -1484,7 +1502,8 @@ static void rekey_seq_generator(struct work_struct *work)
        keyptr->count = (ip_cnt & COUNT_MASK) << HASH_BITS;
        smp_wmb();
        ip_cnt++;
-       schedule_delayed_work(&rekey_work, REKEY_INTERVAL);
+       schedule_delayed_work(&rekey_work,
+                             round_jiffies_relative(REKEY_INTERVAL));
 }
 
 static inline struct keydata *get_keyptr(void)
@@ -1660,15 +1679,20 @@ EXPORT_SYMBOL(secure_dccp_sequence_number);
  * value is not cryptographically secure but for several uses the cost of
  * depleting entropy is too high
  */
+DEFINE_PER_CPU(__u32 [4], get_random_int_hash);
 unsigned int get_random_int(void)
 {
-       /*
-        * Use IP's RNG. It suits our purpose perfectly: it re-keys itself
-        * every second, from the entropy pool (and thus creates a limited
-        * drain on it), and uses halfMD4Transform within the second. We
-        * also mix it with jiffies and the PID:
-        */
-       return secure_ip_id((__force __be32)(current->pid + jiffies));
+       struct keydata *keyptr;
+       __u32 *hash = get_cpu_var(get_random_int_hash);
+       int ret;
+
+       keyptr = get_keyptr();
+       hash[0] += current->pid + jiffies + get_cycles();
+
+       ret = half_md4_transform(hash, keyptr->secret);
+       put_cpu_var(get_random_int_hash);
+
+       return ret;
 }
 
 /*