Merge branch '2.5.x'
[catacomb] / base / dispatch.c
index c8bbc0b..db9c319 100644 (file)
@@ -54,6 +54,7 @@
 struct cpuid { unsigned a, b, c, d; };
 extern int dispatch_x86ish_cpuid(struct cpuid *, unsigned a, unsigned c);
 extern int dispatch_x86ish_xmmregisters_p(void);
+extern int dispatch_x86ish_rdrand(unsigned *);
 
 static void cpuid(struct cpuid *cc, unsigned a, unsigned c)
 {
@@ -101,6 +102,40 @@ static int xmm_registers_available_p(void)
   return (f);
 }
 
+/* --- @rdrand_works_p@ --- *
+ *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Nonzero if the `rdrand' instruction actually works.  Assumes
+ *             that it's already been verified to be safe to issue.
+ */
+
+static int rdrand_works_p(void)
+{
+  unsigned ref, x, i;
+
+  /* Check that it doesn't always give the same answer.  Try four times: this
+   * will fail with probability %$2^{-128}$% with a truly random generator,
+   * which seems fair enough.
+   */
+  if (dispatch_x86ish_rdrand(&ref)) goto fail;
+  for (i = 0; i < 4; i++) {
+    if (dispatch_x86ish_rdrand(&x)) goto fail;
+    if (x != ref) goto not_stuck;
+  }
+  dispatch_debug("RDRAND always returns 0x%08x!", ref);
+  return (0);
+
+not_stuck:
+  dispatch_debug("RDRAND instruction looks plausible");
+  return (1);
+
+fail:
+  dispatch_debug("RDRAND instruction fails too often");
+  return (0);
+}
+
 #endif
 
 /*----- General feature probing using auxiliary vectors -------------------*/
@@ -191,6 +226,7 @@ static unsigned hwcaps = 0;
 #  define WANTAUX(_)                                                   \
        WANT_AT_HWCAP(_)
 #  define CAPMAP(_)                                                    \
+       _(ARM_NEON, "arm:neon")                                         \
        _(ARM_AES, "arm:aes")                                           \
        _(ARM_PMULL, "arm:pmull")
 #endif
@@ -311,6 +347,7 @@ static void probe_hwcaps(void)
 #  endif
 #endif
 #if CPUFAM_ARM64
+  if (probed.hwcap & HWCAP_ASIMD) hw |= HF_ARM_NEON;
   if (probed.hwcap & HWCAP_AES) hw |= HF_ARM_AES;
   if (probed.hwcap & HWCAP_PMULL) hw |= HF_ARM_PMULL;
 #endif
@@ -453,7 +490,7 @@ int cpu_feature_p(int feat)
                 cpuid_features_p(CPUID1D_SSE2, CPUID1C_AESNI) &&
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_RDRAND, "x86:rdrand",
-                cpuid_features_p(0, CPUID1C_RDRAND));
+                cpuid_features_p(0, CPUID1C_RDRAND) && rdrand_works_p());
     CASE_CPUFEAT(X86_AVX, "x86:avx",
                 cpuid_features_p(0, CPUID1C_AVX) &&
                 xmm_registers_available_p());