base/dispatch.c, base/dispatch-x86ish.S: Add opcode to `rdrand_works_p'.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 6 Apr 2020 00:02:41 +0000 (00:02 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Sat, 9 May 2020 19:57:33 +0000 (20:57 +0100)
I want to add support foer the `rdseed' instruction, but this might be
broken on AMD64 like `rdrand'.  Rather than duplicate this logic, add an
opcode argument to the checking functions.

base/dispatch-x86ish.S
base/dispatch.c

index 94f44ee..b3b6d1c 100644 (file)
@@ -158,22 +158,27 @@ ENDFUNC
 /// Checking `rdrand'.
 
 FUNC(dispatch_x86ish_rdrand)
-       // Enter with one argument: a pointer X_OUT to a 32-bit word.  Try to
-       // generate a random word using `rdrand'.  If successful, set *X_OUT
-       // to the generated word, and return zero; otherwise, return -1.
+       // Enter with two arguments: a code OP requesting `rdrand' (0), and a
+       // pointer X_OUT to a 32-bit word.  Try to generate a random word
+       // using the requested instruction.  If successful, set *X_OUT to the
+       // generated word, and return zero; otherwise, return -1.
 
 #if CPUFAM_X86
+#  define OP eax
 #  define X_OUT edx
 #  define COUNT ecx
-       mov     X_OUT, [SP + 4]
+       mov     OP, [SP + 4]
+       mov     X_OUT, [SP + 8]
 #endif
 #if CPUFAM_AMD64 && ABI_SYSV
-#  define X_OUT rdi
+#  define OP edi
+#  define X_OUT rsi
 #  define COUNT ecx
 #endif
 #if CPUFAM_AMD64 && ABI_WIN
-#  define X_OUT rcx
-#  define COUNT edx
+#  define OP rcx
+#  define X_OUT rdx
+#  define COUNT r8d
 #endif
   endprologue
 
@@ -182,9 +187,10 @@ FUNC(dispatch_x86ish_rdrand)
        jc      9f
        dec     COUNT
        jnz     0b
+       jmp     8f
 
        // Failed to come up with a random value.
-       mov     eax, -1
+8:     mov     eax, -1
        ret
 
        // Success.
index 7652f32..bda7f88 100644 (file)
@@ -61,7 +61,7 @@ enum {
 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 *);
+extern int dispatch_x86ish_rdrand(unsigned op, unsigned *);
 
 static void cpuid(struct cpuid *cc, unsigned a, unsigned c)
 {
@@ -130,28 +130,36 @@ static int xmm_registers_available_p(void)
  *             that it's already been verified to be safe to issue.
  */
 
-static int rdrand_works_p(void)
+enum { OP_RDRAND, OP_RDSEED };
+
+static int rdrand_works_p(unsigned op)
 {
   unsigned ref, x, i;
+  const char *what;
+
+  switch (op) {
+    case OP_RDRAND: what = "RDRAND"; break;
+    default: assert(!"unexpected op");
+  }
 
   /* 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;
+  if (dispatch_x86ish_rdrand(op, &ref)) goto fail;
   for (i = 0; i < 4; i++) {
-    if (dispatch_x86ish_rdrand(&x)) goto fail;
+    if (dispatch_x86ish_rdrand(op, &x)) goto fail;
     if (x != ref) goto not_stuck;
   }
-  dispatch_debug("RDRAND always returns 0x%08x!", ref);
+  dispatch_debug("%s always returns 0x%08x!", what, ref);
   return (0);
 
 not_stuck:
-  dispatch_debug("RDRAND instruction looks plausible");
+  dispatch_debug("%s instruction looks plausible", what);
   return (1);
 
 fail:
-  dispatch_debug("RDRAND instruction fails too often");
+  dispatch_debug("%s instruction fails too often", what);
   return (0);
 }
 
@@ -509,7 +517,7 @@ int cpu_feature_p(int feat)
                 xmm_registers_available_p());
     CASE_CPUFEAT(X86_RDRAND, "x86:rdrand",
                 cpuid_feature_p(CPUID_1_C, CPUID1C_RDRAND) &&
-                rdrand_works_p());
+                rdrand_works_p(OP_RDRAND));
     CASE_CPUFEAT(X86_AVX, "x86:avx",
                 cpuid_feature_p(CPUID_1_C, CPUID1C_AVX) &&
                 xmm_registers_available_p());