#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> // This file determines x86/AMD64 features a processor supports. // // We return: // - 0 if the machine matches the asked-for feature. // - 1 if the machine does not. // - 2 if the asked-for feature isn't recognised (this will be the case for // any feature if run on a non-x86/AMD64 machine). // - 3 if there was a usage error (it also prints an error message). // viz: #define FEATURE_PRESENT 0 #define FEATURE_NOT_PRESENT 1 #define UNRECOGNISED_FEATURE 2 #define USAGE_ERROR 3 #define False 0 #define True 1 typedef int Bool; #if defined(VGA_x86) || defined(VGA_amd64) static void cpuid ( unsigned int n, unsigned int* a, unsigned int* b, unsigned int* c, unsigned int* d ) { __asm__ __volatile__ ( "cpuid" : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) /* output */ : "0" (n) /* input */ ); } static Bool vendorStringEquals ( char* str ) { char vstr[13]; unsigned int a, b, c, d; cpuid(0, &a, &b, &c, &d); memcpy(&vstr[0], &b, 4); memcpy(&vstr[4], &d, 4); memcpy(&vstr[8], &c, 4); vstr[12] = 0; return 0 == strcmp(vstr, str); } static Bool have_xgetbv ( void ) { #if defined(VGA_amd64) unsigned long long int w; __asm__ __volatile__("movq $0,%%rcx ; " ".byte 0x0F,0x01,0xD0 ; " /* xgetbv */ "movq %%rax,%0" :/*OUT*/"=r"(w) :/*IN*/ :/*TRASH*/"rdx","rcx"); if ((w & 6) == 6) { /* OS has enabled both XMM and YMM state support */ return True; } else { return False; } #else return False; #endif } static Bool go(char* cpu) { unsigned int level = 0, cmask = 0, dmask = 0, a, b, c, d; Bool require_amd = False; Bool require_xgetbv = False; if ( strcmp( cpu, "x86-fpu" ) == 0 ) { level = 1; dmask = 1 << 0; } else if ( strcmp( cpu, "x86-cmov" ) == 0 ) { level = 1; dmask = 1 << 15; } else if ( strcmp( cpu, "x86-mmx" ) == 0 ) { level = 1; dmask = 1 << 23; } else if ( strcmp( cpu, "x86-mmxext" ) == 0 ) { level = 0x80000001; dmask = 1 << 22; } else if ( strcmp( cpu, "x86-sse" ) == 0 ) { level = 1; dmask = 1 << 25; } else if ( strcmp( cpu, "x86-sse2" ) == 0 ) { level = 1; dmask = 1 << 26; } else if ( strcmp( cpu, "x86-sse3" ) == 0 ) { level = 1; cmask = 1 << 0; } else if ( strcmp( cpu, "x86-ssse3" ) == 0 ) { level = 1; cmask = 1 << 9; } else if ( strcmp( cpu, "x86-lzcnt" ) == 0 ) { level = 0x80000001; cmask = 1 << 5; require_amd = True; #if defined(VGA_amd64) } else if ( strcmp( cpu, "amd64-sse3" ) == 0 ) { level = 1; cmask = 1 << 0; } else if ( strcmp( cpu, "amd64-pclmulqdq" ) == 0 ) { level = 1; cmask = 1 << 1; } else if ( strcmp( cpu, "amd64-ssse3" ) == 0 ) { level = 1; cmask = 1 << 9; } else if ( strcmp( cpu, "amd64-cx16" ) == 0 ) { level = 1; cmask = 1 << 13; } else if ( strcmp( cpu, "amd64-lzcnt" ) == 0 ) { level = 0x80000001; cmask = 1 << 5; require_amd = True; } else if ( strcmp( cpu, "amd64-sse42" ) == 0 ) { level = 1; cmask = 1 << 20; } else if ( strcmp( cpu, "amd64-avx" ) == 0 ) { level = 1; cmask = (1 << 27) | (1 << 28); require_xgetbv = True; #endif } else { return UNRECOGNISED_FEATURE; } assert( !(cmask != 0 && dmask != 0) ); assert( !(cmask == 0 && dmask == 0) ); if (require_amd && !vendorStringEquals("AuthenticAMD")) return FEATURE_NOT_PRESENT; // regardless of what that feature actually is cpuid( level & 0x80000000, &a, &b, &c, &d ); if ( a >= level ) { cpuid( level, &a, &b, &c, &d ); if (dmask > 0 && (d & dmask) == dmask) { if (require_xgetbv && !have_xgetbv()) return FEATURE_NOT_PRESENT; else return FEATURE_PRESENT; } if (cmask > 0 && (c & cmask) == cmask) { if (require_xgetbv && !have_xgetbv()) return FEATURE_NOT_PRESENT; else return FEATURE_PRESENT; } } return FEATURE_NOT_PRESENT; } #else static Bool go(char* cpu) { // Feature not recognised (non-x86/AMD64 machine!) return UNRECOGNISED_FEATURE; } #endif // defined(VGA_x86) || defined(VGA_amd64) //--------------------------------------------------------------------------- // main //--------------------------------------------------------------------------- int main(int argc, char **argv) { if ( argc != 2 ) { fprintf( stderr, "usage: x86_amd64_features <feature>\n" ); exit(USAGE_ERROR); } return go(argv[1]); }