| |
| #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 << 20) | (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]); |
| } |