static void do_cpuid(int regs[], int h)
{
- __asm__ __volatile__(
-#if defined __x86_64__
- "pushq %%rbx;\n"
-#else
- "pushl %%ebx;\n"
-#endif
- "cpuid;\n"
-#if defined __x86_64__
- "popq %%rbx;\n"
-#else
- "popl %%ebx;\n"
-#endif
- : "=a"(regs[0]), [ebx] "=r"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
+ /* clang-format off */
+ __asm__ __volatile__("cpuid"
+ : "=a"(regs[0]), "=b"(regs[1]), "=c"(regs[2]), "=d"(regs[3])
: "a"(h));
+ /* clang-format on */
}
#elif defined _MSC_VER
#if HAS_X86_CPUID
-static int has_rdrand()
+static int get_rdrand_seed(void);
+
+/* Valid values are -1 (haven't tested), 0 (no), and 1 (yes). */
+static int _has_rdrand = -1;
+
+static int has_rdrand(void)
{
- // CPUID.01H:ECX.RDRAND[bit 30] == 1
- int regs[4];
- do_cpuid(regs, 1);
- return (regs[2] & (1 << 30)) != 0;
+ if (_has_rdrand != -1)
+ {
+ return _has_rdrand;
+ }
+
+ /* CPUID.01H:ECX.RDRAND[bit 30] == 1 */
+ int regs[4];
+ do_cpuid(regs, 1);
+ if (!(regs[2] & (1 << 30)))
+ {
+ _has_rdrand = 0;
+ return 0;
+ }
+
+ /*
+ * Some CPUs advertise RDRAND in CPUID, but return 0xFFFFFFFF
+ * unconditionally. To avoid locking up later, test RDRAND here. If over
+ * 3 trials RDRAND has returned the same value, declare it broken.
+ * Example CPUs are AMD Ryzen 3000 series
+ * and much older AMD APUs, such as the E1-1500
+ * https://github.com/systemd/systemd/issues/11810
+ * https://linuxreviews.org/RDRAND_stops_returning_random_values_on_older_AMD_CPUs_after_suspend
+ */
+ _has_rdrand = 0;
+ int prev = get_rdrand_seed();
+ for (int i = 0; i < 3; i++)
+ {
+ int temp = get_rdrand_seed();
+ if (temp != prev)
+ {
+ _has_rdrand = 1;
+ break;
+ }
+
+ prev = temp;
+ }
+
+ return _has_rdrand;
}
#endif
#define HAVE_RDRAND 1
-static int get_rdrand_seed()
+static int get_rdrand_seed(void)
{
- DEBUG_SEED("get_rdrand_seed");
- int _eax;
- // rdrand eax
- __asm__ __volatile__("1: .byte 0x0F\n"
- " .byte 0xC7\n"
- " .byte 0xF0\n"
- " jnc 1b;\n"
- : "=a" (_eax));
- return _eax;
+ DEBUG_SEED("get_rdrand_seed");
+ int _eax;
+ /* rdrand eax */
+ /* clang-format off */
+ __asm__ __volatile__("1: .byte 0x0F\n"
+ " .byte 0xC7\n"
+ " .byte 0xF0\n"
+ " jnc 1b;\n"
+ : "=a" (_eax));
+ /* clang-format on */
+ return _eax;
}
#endif
DEBUG_SEED("get_rdrand_seed");
int _eax;
retry:
- // rdrand eax
+ /* rdrand eax */
__asm _emit 0x0F __asm _emit 0xC7 __asm _emit 0xF0
__asm jnc retry
__asm mov _eax, eax
#define HAVE_CRYPTGENRANDOM 1
+/* clang-format off */
#include <windows.h>
+
+/* Caution: these blank lines must remain so clang-format doesn't reorder
+ includes to put windows.h after wincrypt.h */
+
#include <wincrypt.h>
+/* clang-format on */
#ifndef __GNUC__
#pragma comment(lib, "advapi32.lib")
#endif