summaryrefslogtreecommitdiff
path: root/random/rndhw.c
diff options
context:
space:
mode:
Diffstat (limited to 'random/rndhw.c')
-rw-r--r--random/rndhw.c68
1 files changed, 68 insertions, 0 deletions
diff --git a/random/rndhw.c b/random/rndhw.c
index 775d90f0..cbb28d1a 100644
--- a/random/rndhw.c
+++ b/random/rndhw.c
@@ -1,5 +1,6 @@
/* rndhw.c - Access to the external random daemon
* Copyright (C) 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2012 Dmitry Kasatkin
*
* This file is part of Libgcrypt.
*
@@ -34,6 +35,16 @@
# endif
#endif /*ENABLE_PADLOCK_SUPPORT*/
+#undef USE_DRNG
+#ifdef ENABLE_DRNG_SUPPORT
+# ifdef HAVE_GCC_ATTRIBUTE_ALIGNED
+# if (defined (__i386__) && SIZEOF_UNSIGNED_LONG == 4) || defined(__x86_64__)
+# define USE_DRNG 1
+# endif
+# endif
+#endif /*ENABLE_RDRAND_SUPPORT*/
+
+typedef void (*add_fn_t)(const void*, size_t, enum random_origins);
/* Keep track on whether the RNG has problems. */
static volatile int rng_failed;
@@ -109,6 +120,55 @@ poll_padlock (void (*add)(const void*, size_t, enum random_origins),
#endif /*USE_PADLOCK*/
+#ifdef USE_DRNG
+# define RDRAND_RETRY_LOOPS 10
+# define RDRAND_INT ".byte 0x0f,0xc7,0xf0"
+# ifdef __x86_64__
+# define RDRAND_LONG ".byte 0x48,0x0f,0xc7,0xf0"
+# else
+# define RDRAND_LONG RDRAND_INT
+# endif
+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;
+}
+
+
+static inline int
+rdrand_nlong (unsigned long *v, int count)
+{
+ while (count--)
+ if (!rdrand_long(v++))
+ return 0;
+ return 1;
+}
+
+
+static size_t
+poll_drng (add_fn_t add, enum random_origins origin, int fast)
+{
+ volatile char buffer[64] __attribute__ ((aligned (8)));
+ unsigned int nbytes = sizeof (buffer);
+
+ (void)fast;
+
+ if (!rdrand_nlong ((unsigned long *)buffer, sizeof(buffer)/sizeof(long)))
+ return 0;
+ (*add)((void *)buffer, nbytes, origin);
+ return nbytes;
+}
+#endif /*USE_DRNG*/
+
+
int
_gcry_rndhw_failed_p (void)
{
@@ -125,6 +185,10 @@ _gcry_rndhw_poll_fast (void (*add)(const void*, size_t, enum random_origins),
(void)add;
(void)origin;
+#ifdef USE_DRNG
+ if ((_gcry_get_hw_features () & HWF_INTEL_RDRAND))
+ poll_drng (add, origin, 1);
+#endif
#ifdef USE_PADLOCK
if ((_gcry_get_hw_features () & HWF_PADLOCK_RNG))
poll_padlock (add, origin, 1);
@@ -143,6 +207,10 @@ _gcry_rndhw_poll_slow (void (*add)(const void*, size_t, enum random_origins),
(void)add;
(void)origin;
+#ifdef USE_DRNG
+ if ((_gcry_get_hw_features () & HWF_INTEL_RDRAND))
+ nbytes += poll_drng (add, origin, 0);
+#endif
#ifdef USE_PADLOCK
if ((_gcry_get_hw_features () & HWF_PADLOCK_RNG))
nbytes += poll_padlock (add, origin, 0);