#include "config.h"
#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dispatch.h"
-/*----- Main code ---------------------------------------------------------*/
+/*----- Intel x86/AMD64 feature probing -----------------------------------*/
-#ifdef CPUFAM_X86
+#if CPUFAM_X86 || CPUFAM_AMD64
-#define EFLAGS_ID (1u << 21)
-#define CPUID1D_SSE2 (1u << 26)
-#define CPUID1D_FXSR (1u << 24)
+# define EFLAGS_ID (1u << 21)
+# define CPUID1D_SSE2 (1u << 26)
+# define CPUID1D_FXSR (1u << 24)
+# define CPUID1C_AESNI (1u << 25)
struct cpuid { unsigned a, b, c, d; };
*/
#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)
: "g" (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"
+ : "=g" (ff)
+ : "g" (f));
+ return (ff);
+}
+# endif
#endif
static void cpuid(struct cpuid *cc, unsigned a, unsigned c)
#ifdef __GNUC__
/* Stupid dance to detect whether the CPUID instruction is available. */
f = getflags();
- if (!(setflags(f | EFLAGS_ID) & EFLAGS_ID)) return;
- if ( setflags(f & ~EFLAGS_ID) & EFLAGS_ID ) return;
+ if (!(setflags(f | EFLAGS_ID) & EFLAGS_ID) ||
+ setflags(f & ~EFLAGS_ID) & EFLAGS_ID) {
+ 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
}
static unsigned cpuid_maxleaf(void)
{ struct cpuid c; cpuid(&c, 0, 0); return (c.a); }
+/* --- @cpuid_features_p@ --- *
+ *
+ * Arguments: @unsigned dbits@ = bits to check in EDX
+ * @unsigned cbits@ = bits to check in ECX
+ *
+ * Returns: Nonzero if all the requested bits are set in the CPUID result
+ * on leaf 1.
+ */
+
static int cpuid_features_p(unsigned dbits, unsigned cbits)
{
struct cpuid c;
return ((c.d & dbits) == dbits && (c.c & cbits) == cbits);
}
+/* --- @xmm_registers_available_p@ --- *
+ *
+ * Arguments: ---
+ *
+ * Returns: Nonzero if the operating system has made the XMM registers
+ * available for use.
+ */
+
static int xmm_registers_available_p(void)
{
#ifdef __GNUC__
* 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"
: "=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
+ 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
}
#endif
+/*----- External interface ------------------------------------------------*/
+
+/* --- @dispatch_debug@ --- *
+ *
+ * Arguments: @const char *fmt@ = a format string
+ * @...@ = additional arguments
+ *
+ * Returns: ---
+ *
+ * Use: Writes a formatted message to standard output if dispatch
+ * debugging is enabled.
+ */
+
+void dispatch_debug(const char *fmt, ...)
+{
+ va_list ap;
+ const char *e = getenv("CATACOMB_CPUDISPATCH_DEBUG");
+
+ if (e && *e != 'n' && *e != '0') {
+ va_start(ap, fmt);
+ fputs("Catacomb CPUDISPATCH: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ fputc('\n', stderr);
+ va_end(ap);
+ }
+}
+
/* --- @check_env@ --- *
*
* Arguments: @const char *ftok@ = feature token
#include <stdio.h>
+static int IGNORABLE
+ feat_debug(const char *ftok, const char *check, int verdict)
+{
+ if (verdict >= 0) {
+ dispatch_debug("feature `%s': %s -> %s", ftok, check,
+ verdict ? "available" : "absent");
+ }
+ return (verdict);
+}
+
int cpu_feature_p(int feat)
{
int IGNORABLE f;
IGNORE(f);
-#define CHECK_ENV(ftok) \
- do { if ((f = check_env(ftok)) >= 0) return (f); } while (0)
+#define CASE_CPUFEAT(feat, ftok, cond) case CPUFEAT_##feat: \
+ if ((f = feat_debug(ftok, "environment override", \
+ check_env(ftok))) >= 0) \
+ return (f); \
+ else \
+ return (feat_debug(ftok, "runtime probe", cond));
switch (feat) {
-#ifdef CPUFAM_X86
- case CPUFEAT_X86_SSE2: {
- CHECK_ENV("x86:sse2");
- return (xmm_registers_available_p() &&
- cpuid_features_p(CPUID1D_SSE2, 0));
- }
+#if CPUFAM_X86 || CPUFAM_AMD64
+ CASE_CPUFEAT(X86_SSE2, "x86:sse2",
+ xmm_registers_available_p() &&
+ cpuid_features_p(CPUID1D_SSE2, 0));
+ CASE_CPUFEAT(X86_AESNI, "x86:aesni",
+ xmm_registers_available_p() &&
+ cpuid_features_p(CPUID1D_SSE2, CPUID1C_AESNI));
#endif
default:
+ dispatch_debug("denying unknown feature %d", feat);
return (0);
}
-#undef CHECK_ENV
+#undef CASE_CPUFEAT
}
/*----- That's all, folks -------------------------------------------------*/