Merge branch 'x86-rdrand-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 28 Oct 2011 12:29:07 +0000 (05:29 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 28 Oct 2011 12:29:07 +0000 (05:29 -0700)
* 'x86-rdrand-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86, random: Verify RDRAND functionality and allow it to be disabled
  x86, random: Architectural inlines to get random integers with RDRAND
  random: Add support for architectural random hooks

Fix up trivial conflicts in drivers/char/random.c: the architectural
random hooks touched "get_random_int()" that was simplified to use MD5
and not do the keyptr thing any more (see commit 6e5714eaf77d: "net:
Compute protocol sequence numbers and fragment IDs using MD5").

Documentation/kernel-parameters.txt
arch/x86/Kconfig
arch/x86/include/asm/archrandom.h [new file with mode: 0644]
arch/x86/kernel/cpu/Makefile
arch/x86/kernel/cpu/common.c
arch/x86/kernel/cpu/rdrand.c [new file with mode: 0644]
drivers/char/random.c
include/linux/random.h

index 6afd091..93413ce 100644 (file)
@@ -1797,6 +1797,11 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
 
        noresidual      [PPC] Don't use residual data on PReP machines.
 
+       nordrand        [X86] Disable the direct use of the RDRAND
+                       instruction even if it is supported by the
+                       processor.  RDRAND is still available to user
+                       space applications.
+
        noresume        [SWSUSP] Disables resume and restores original swap
                        space.
 
index 16db864..77f7a38 100644 (file)
@@ -1454,6 +1454,15 @@ config ARCH_USES_PG_UNCACHED
        def_bool y
        depends on X86_PAT
 
+config ARCH_RANDOM
+       def_bool y
+       prompt "x86 architectural random number generator" if EXPERT
+       ---help---
+         Enable the x86 architectural RDRAND instruction
+         (Intel Bull Mountain technology) to generate random numbers.
+         If supported, this is a high bandwidth, cryptographically
+         secure hardware random number generator.
+
 config EFI
        bool "EFI runtime service support"
        depends on ACPI
diff --git a/arch/x86/include/asm/archrandom.h b/arch/x86/include/asm/archrandom.h
new file mode 100644 (file)
index 0000000..0d9ec77
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * This file is part of the Linux kernel.
+ *
+ * Copyright (c) 2011, Intel Corporation
+ * Authors: Fenghua Yu <fenghua.yu@intel.com>,
+ *          H. Peter Anvin <hpa@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef ASM_X86_ARCHRANDOM_H
+#define ASM_X86_ARCHRANDOM_H
+
+#include <asm/processor.h>
+#include <asm/cpufeature.h>
+#include <asm/alternative.h>
+#include <asm/nops.h>
+
+#define RDRAND_RETRY_LOOPS     10
+
+#define RDRAND_INT     ".byte 0x0f,0xc7,0xf0"
+#ifdef CONFIG_X86_64
+# define RDRAND_LONG   ".byte 0x48,0x0f,0xc7,0xf0"
+#else
+# define RDRAND_LONG   RDRAND_INT
+#endif
+
+#ifdef CONFIG_ARCH_RANDOM
+
+#define GET_RANDOM(name, type, rdrand, nop)                    \
+static inline int name(type *v)                                        \
+{                                                              \
+       int ok;                                                 \
+       alternative_io("movl $0, %0\n\t"                        \
+                      nop,                                     \
+                      "\n1: " rdrand "\n\t"                    \
+                      "jc 2f\n\t"                              \
+                      "decl %0\n\t"                            \
+                      "jnz 1b\n\t"                             \
+                      "2:",                                    \
+                      X86_FEATURE_RDRAND,                      \
+                      ASM_OUTPUT2("=r" (ok), "=a" (*v)),       \
+                      "0" (RDRAND_RETRY_LOOPS));               \
+       return ok;                                              \
+}
+
+#ifdef CONFIG_X86_64
+
+GET_RANDOM(arch_get_random_long, unsigned long, RDRAND_LONG, ASM_NOP5);
+GET_RANDOM(arch_get_random_int, unsigned int, RDRAND_INT, ASM_NOP4);
+
+#else
+
+GET_RANDOM(arch_get_random_long, unsigned long, RDRAND_LONG, ASM_NOP3);
+GET_RANDOM(arch_get_random_int, unsigned int, RDRAND_INT, ASM_NOP3);
+
+#endif /* CONFIG_X86_64 */
+
+#endif  /* CONFIG_ARCH_RANDOM */
+
+extern void x86_init_rdrand(struct cpuinfo_x86 *c);
+
+#endif /* ASM_X86_ARCHRANDOM_H */
index fe6eb19..25f24dc 100644 (file)
@@ -15,6 +15,7 @@ CFLAGS_common.o               := $(nostackp)
 obj-y                  := intel_cacheinfo.o scattered.o topology.o
 obj-y                  += proc.o capflags.o powerflags.o common.o
 obj-y                  += vmware.o hypervisor.o sched.o mshyperv.o
+obj-y                  += rdrand.o
 
 obj-$(CONFIG_X86_32)   += bugs.o
 obj-$(CONFIG_X86_64)   += bugs_64.o
index ec63df5..aa003b1 100644 (file)
@@ -15,6 +15,7 @@
 #include <asm/stackprotector.h>
 #include <asm/perf_event.h>
 #include <asm/mmu_context.h>
+#include <asm/archrandom.h>
 #include <asm/hypervisor.h>
 #include <asm/processor.h>
 #include <asm/sections.h>
@@ -860,6 +861,7 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
 #endif
 
        init_hypervisor(c);
+       x86_init_rdrand(c);
 
        /*
         * Clear/Set all flags overriden by options, need do it
diff --git a/arch/x86/kernel/cpu/rdrand.c b/arch/x86/kernel/cpu/rdrand.c
new file mode 100644 (file)
index 0000000..feca286
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * This file is part of the Linux kernel.
+ *
+ * Copyright (c) 2011, Intel Corporation
+ * Authors: Fenghua Yu <fenghua.yu@intel.com>,
+ *          H. Peter Anvin <hpa@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <asm/processor.h>
+#include <asm/archrandom.h>
+#include <asm/sections.h>
+
+static int __init x86_rdrand_setup(char *s)
+{
+       setup_clear_cpu_cap(X86_FEATURE_RDRAND);
+       return 1;
+}
+__setup("nordrand", x86_rdrand_setup);
+
+/* We can't use arch_get_random_long() here since alternatives haven't run */
+static inline int rdrand_long(unsigned long *v)
+{
+       int ok;
+       asm volatile("1: " RDRAND_LONG "\n\t"
+                    "jc 2f\n\t"
+                    "decl %0\n\t"
+                    "jnz 1b\n\t"
+                    "2:"
+                    : "=r" (ok), "=a" (*v)
+                    : "0" (RDRAND_RETRY_LOOPS));
+       return ok;
+}
+
+/*
+ * Force a reseed cycle; we are architecturally guaranteed a reseed
+ * after no more than 512 128-bit chunks of random data.  This also
+ * acts as a test of the CPU capability.
+ */
+#define RESEED_LOOP ((512*128)/sizeof(unsigned long))
+
+void __cpuinit x86_init_rdrand(struct cpuinfo_x86 *c)
+{
+#ifdef CONFIG_ARCH_RANDOM
+       unsigned long tmp;
+       int i, count, ok;
+
+       if (!cpu_has(c, X86_FEATURE_RDRAND))
+               return;         /* Nothing to do */
+
+       for (count = i = 0; i < RESEED_LOOP; i++) {
+               ok = rdrand_long(&tmp);
+               if (ok)
+                       count++;
+       }
+
+       if (count != RESEED_LOOP)
+               clear_cpu_cap(c, X86_FEATURE_RDRAND);
+#endif
+}
index c35a785..63e19ba 100644 (file)
@@ -932,7 +932,21 @@ static ssize_t extract_entropy_user(struct entropy_store *r, void __user *buf,
  */
 void get_random_bytes(void *buf, int nbytes)
 {
-       extract_entropy(&nonblocking_pool, buf, nbytes, 0, 0);
+       char *p = buf;
+
+       while (nbytes) {
+               unsigned long v;
+               int chunk = min(nbytes, (int)sizeof(unsigned long));
+               
+               if (!arch_get_random_long(&v))
+                       break;
+               
+               memcpy(buf, &v, chunk);
+               p += chunk;
+               nbytes -= chunk;
+       }
+
+       extract_entropy(&nonblocking_pool, p, nbytes, 0, 0);
 }
 EXPORT_SYMBOL(get_random_bytes);
 
@@ -1318,9 +1332,14 @@ late_initcall(random_int_secret_init);
 DEFINE_PER_CPU(__u32 [MD5_DIGEST_WORDS], get_random_int_hash);
 unsigned int get_random_int(void)
 {
-       __u32 *hash = get_cpu_var(get_random_int_hash);
+       __u32 *hash;
        unsigned int ret;
 
+       if (arch_get_random_int(&ret))
+               return ret;
+
+       hash = get_cpu_var(get_random_int_hash);
+
        hash[0] += current->pid + jiffies + get_cycles();
        md5_transform(hash, random_int_secret);
        ret = hash[0];
index d13059f..8f74538 100644 (file)
@@ -91,6 +91,19 @@ static inline void prandom32_seed(struct rnd_state *state, u64 seed)
        state->s3 = __seed(i, 15);
 }
 
+#ifdef CONFIG_ARCH_RANDOM
+# include <asm/archrandom.h>
+#else
+static inline int arch_get_random_long(unsigned long *v)
+{
+       return 0;
+}
+static inline int arch_get_random_int(unsigned int *v)
+{
+       return 0;
+}
+#endif
+
 #endif /* __KERNEL___ */
 
 #endif /* _LINUX_RANDOM_H */