#include "config.h"
+#include <assert.h>
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#if CPUFAM_X86 || CPUFAM_AMD64
-# define EFLAGS_ID (1u << 21)
+enum {
+ CPUID_1_D, /* eax = 1 => edx&?? */
# define CPUID1D_SSE2 (1u << 26)
# define CPUID1D_FXSR (1u << 24)
+
+ CPUID_1_C, /* eax = 1 => ecx&?? */
# define CPUID1C_PCLMUL (1u << 1)
# define CPUID1C_SSSE3 (1u << 9)
# define CPUID1C_AESNI (1u << 25)
# define CPUID1C_AVX (1u << 28)
# define CPUID1C_RDRAND (1u << 30)
-struct cpuid { unsigned a, b, c, d; };
-
-/* --- @cpuid@ --- *
- *
- * Arguments: @struct cpuid *cc@ = where to write the result
- * @unsigned a, c@ = EAX and ECX registers to set
- *
- * Returns: ---
- *
- * Use: Minimal C wrapper around the x86 `CPUID' instruction. Checks
- * that the instruction is actually available before invoking
- * it; fills the output structure with zero if it's not going to
- * work.
- */
+ CPUID_7_0_B, /* eax = 7, ecx = 0 => ebx&?? */
+# define CPUID70B_RDSEED (1u << 18)
+};
-#ifdef __GNUC__
-# if CPUFAM_X86
-static __inline__ unsigned getflags(void)
- { unsigned f; __asm__ ("pushf; popl %0" : "=g" (f)); return (f); }
-static __inline__ unsigned setflags(unsigned f)
-{
- unsigned ff;
- __asm__ ("pushf; pushl %1; popf; pushf; popl %0; popf"
- : "=r" (ff)
- : "r" (f));
- return (ff);
-}
-# else
-static __inline__ unsigned long getflags(void)
- { unsigned long f; __asm__ ("pushf; popq %0" : "=g" (f)); return (f); }
-static __inline__ unsigned long long setflags(unsigned long f)
-{
- unsigned long ff;
- __asm__ ("pushf; pushq %1; popf; pushf; popq %0; popf"
- : "=r" (ff)
- : "r" (f));
- return (ff);
-}
-# endif
-#endif
+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 op, unsigned *);
static void cpuid(struct cpuid *cc, unsigned a, unsigned c)
{
-#ifdef __GNUC__
- unsigned f;
-#endif
-
- cc->a = cc->b = cc->c = cc->d = 0;
-
-#ifdef __GNUC__
- /* Stupid dance to detect whether the CPUID instruction is available. */
- f = getflags();
- if (!(setflags(f | EFLAGS_ID) & EFLAGS_ID) ||
- setflags(f & ~EFLAGS_ID) & EFLAGS_ID) {
+ int rc = dispatch_x86ish_cpuid(cc, a, c);
+ if (rc)
dispatch_debug("CPUID instruction not available");
- return;
- }
- setflags(f);
-
- /* Alas, EBX is magical in PIC code, so abuse ESI instead. This isn't
- * pretty, but it works.
- */
-# if CPUFAM_X86
- __asm__ ("pushl %%ebx; cpuid; movl %%ebx, %%esi; popl %%ebx"
- : "=a" (cc->a), "=S" (cc->b), "=c" (cc->c), "=d" (cc->d)
- : "a" (a) , "c" (c));
-# elif CPUFAM_AMD64
- __asm__ ("pushq %%rbx; cpuid; movl %%ebx, %%esi; popq %%rbx"
- : "=a" (cc->a), "=S" (cc->b), "=c" (cc->c), "=d" (cc->d)
- : "a" (a) , "c" (c));
-# else
-# error "I'm confused."
-# endif
- dispatch_debug("CPUID(%08x, %08x) -> %08x, %08x, %08x, %08x",
- a, c, cc->a, cc->b, cc->c, cc->d);
-#else
- dispatch_debug("GNU inline assembler not available; can't CPUID");
-#endif
+ else
+ dispatch_debug("CPUID(%08x, %08x) -> %08x, %08x, %08x, %08x",
+ a, c, cc->a, cc->b, cc->c, cc->d);
}
static unsigned cpuid_maxleaf(void)
{ struct cpuid c; cpuid(&c, 0, 0); return (c.a); }
-/* --- @cpuid_features_p@ --- *
+/* --- @cpuid_feature_p@ --- *
*
- * Arguments: @unsigned dbits@ = bits to check in EDX
- * @unsigned cbits@ = bits to check in ECX
+ * Arguments: @unsigned leaf@ = leaf to look up
+ * @unsigned bits@ = bits to check
*
- * Returns: Nonzero if all the requested bits are set in the CPUID result
- * on leaf 1.
+ * Returns: Nonzero if all the requested bits are set in the requested
+ * CPUID result.
*/
-static int cpuid_features_p(unsigned dbits, unsigned cbits)
+static int cpuid_feature_p(unsigned leaf, unsigned bits)
{
struct cpuid c;
- if (cpuid_maxleaf() < 1) return (0);
- cpuid(&c, 1, 0);
- return ((c.d & dbits) == dbits && (c.c & cbits) == cbits);
+ unsigned r;
+
+ switch (leaf) {
+ case CPUID_1_D:
+ if (cpuid_maxleaf() < 1) return (0);
+ cpuid(&c, 1, 0); r = c.d;
+ break;
+ case CPUID_1_C:
+ if (cpuid_maxleaf() < 1) return (0);
+ cpuid(&c, 1, 0); r = c.c;
+ break;
+ case CPUID_7_0_B:
+ if (cpuid_maxleaf() < 7) return (0);
+ cpuid(&c, 7, 0); r = c.b;
+ break;
+ default:
+ assert(!"unknown cpuid leaf");
+ }
+ return ((r&bits) == bits);
}
/* --- @xmm_registers_available_p@ --- *
static int xmm_registers_available_p(void)
{
-#ifdef __GNUC__
- unsigned f;
- /* This hack is by Agner Fog. Use FXSAVE/FXRSTOR to figure out whether the
- * XMM registers are actually alive.
- */
- if (!cpuid_features_p(CPUID1D_FXSR, 0)) return (0);
-# if CPUFAM_X86
- __asm__ ("movl %%esp, %%edx; subl $512, %%esp; andl $~15, %%esp\n"
- "fxsave (%%esp)\n"
- "movl 160(%%esp), %%eax; xorl $0xaaaa5555, 160(%%esp)\n"
- "fxrstor (%%esp); fxsave (%%esp)\n"
- "movl 160(%%esp), %%ecx; movl %%eax, 160(%%esp)\n"
- "fxrstor (%%esp); movl %%edx, %%esp\n"
- "xorl %%ecx, %%eax"
- : "=a" (f)
- : /* no inputs */
- : "%ecx", "%edx");
-# elif CPUFAM_AMD64
- __asm__ ("movq %%rsp, %%rdx; subq $512, %%rsp; andq $~15, %%rsp\n"
- "fxsave (%%rsp)\n"
- "movl 160(%%rsp), %%eax; xorl $0xaaaa5555, 160(%%rsp)\n"
- "fxrstor (%%rsp); fxsave (%%rsp)\n"
- "movl 160(%%rsp), %%ecx; movl %%eax, 160(%%rsp)\n"
- "fxrstor (%%rsp); movq %%rdx, %%rsp\n"
- "xorl %%ecx, %%eax"
- : "=a" (f)
- : /* no inputs */
- : "%ecx", "%rdx");
-# else
-# error "I'm confused."
-# endif
+ int f = dispatch_x86ish_xmmregisters_p();
+
dispatch_debug("XMM registers %savailable", f ? "" : "not ");
return (f);
-#else
- dispatch_debug("GNU inline assembler not available; can't check for XMM");
- return (0);
-#endif
}
/* --- @rdrand_works_p@ --- *
* that it's already been verified to be safe to issue.
*/
-#ifdef __GNUC__
-static int rdrand(unsigned *x)
-{
- int i, rc;
- unsigned _t;
-
- i = 16;
- __asm__ ("" : "=g" (_t));
- __asm__ ("0: rdrand %2; jc 1f; decl %1; jnz 0b\n"
- "mov $-1, %0; jmp 9f\n"
- "1: movl %2, (%3); xorl %0, %0\n"
- "9:"
- : "=r" (rc), "+r" (i), "+r" (_t)
- : "r" (x)
- : "cc");
- return (rc);
-}
-#endif
+enum { OP_RDRAND, OP_RDSEED };
-static int rdrand_works_p(void)
+static int rdrand_works_p(unsigned op)
{
unsigned ref, x, i;
+ const char *what;
+
+ switch (op) {
+ case OP_RDRAND: what = "RDRAND"; break;
+ case OP_RDSEED: what = "RDSEED"; 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 (rdrand(&ref)) goto fail;
+ if (dispatch_x86ish_rdrand(op, &ref)) goto fail;
for (i = 0; i < 4; i++) {
- if (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);
}
# define WANTAUX(_) \
WANT_AT_HWCAP(_)
# define CAPMAP(_) \
+ _(ARM_NEON, "arm:neon") \
_(ARM_AES, "arm:aes") \
_(ARM_PMULL, "arm:pmull")
#endif
# 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
unsigned hw;
DISPATCH_LOAD(hwcaps, hw);
- if (!(hwcaps & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); }
+ if (!(hw & HF_PROBED)) { probe_hwcaps(); DISPATCH_LOAD(hwcaps, hw); }
return (hw);
}
if (!p) return (-1);
for (;;) {
- while (isspace((unsigned char)*p)) p++;
+ while (ISSPACE(*p)) p++;
if (!*p) return (-1);
switch (*p) {
case '+': d = +1; p++; break;
case '-': d = 0; p++; break;
default: d = -1; break;
}
- for (q = p; *q && !isspace((unsigned char)*q); q++);
+ for (q = p; *q && !ISSPACE(*q); q++);
if (d >= 0) {
for (pp = ftok; p < q && *pp && *p == *pp; p++, pp++);
if ((p == q && !*pp) || (*p == '*' && p + 1 == q)) return (d);
int IGNORABLE f;
IGNORE(f);
#define CASE_CPUFEAT(feat, ftok, cond) case CPUFEAT_##feat: \
- if ((f = feat_debug(ftok, "environment override", \
- check_env(ftok))) >= 0) \
+ if ((f = feat_debug(ftok, "environment override", check_env(ftok))) >= 0) \
return (f); \
else \
return (feat_debug(ftok, "runtime probe", cond));
switch (feat) {
#if CPUFAM_X86 || CPUFAM_AMD64
CASE_CPUFEAT(X86_SSE2, "x86:sse2",
- cpuid_features_p(CPUID1D_SSE2, 0) &&
+ cpuid_feature_p(CPUID_1_D, CPUID1D_SSE2) &&
xmm_registers_available_p());
CASE_CPUFEAT(X86_AESNI, "x86:aesni",
- cpuid_features_p(CPUID1D_SSE2, CPUID1C_AESNI) &&
+ cpuid_feature_p(CPUID_1_C, CPUID1C_AESNI) &&
xmm_registers_available_p());
CASE_CPUFEAT(X86_RDRAND, "x86:rdrand",
- cpuid_features_p(0, CPUID1C_RDRAND) && rdrand_works_p());
+ cpuid_feature_p(CPUID_1_C, CPUID1C_RDRAND) &&
+ rdrand_works_p(OP_RDRAND));
CASE_CPUFEAT(X86_AVX, "x86:avx",
- cpuid_features_p(0, CPUID1C_AVX) &&
+ cpuid_feature_p(CPUID_1_C, CPUID1C_AVX) &&
xmm_registers_available_p());
CASE_CPUFEAT(X86_SSSE3, "x86:ssse3",
- cpuid_features_p(0, CPUID1C_SSSE3) &&
+ cpuid_feature_p(CPUID_1_C, CPUID1C_SSSE3) &&
xmm_registers_available_p());
CASE_CPUFEAT(X86_PCLMUL, "x86:pclmul",
- cpuid_features_p(0, CPUID1C_PCLMUL) &&
+ cpuid_feature_p(CPUID_1_C, CPUID1C_PCLMUL) &&
xmm_registers_available_p());
+ CASE_CPUFEAT(X86_RDSEED, "x86:rdseed",
+ cpuid_feature_p(CPUID_7_0_B, CPUID70B_RDSEED) &&
+ rdrand_works_p(OP_RDSEED));
#endif
#ifdef CAPMAP
# define FEATP__CASE(feat, tok) \