1ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 2ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <stdio.h> 3ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <stdlib.h> 4ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <string.h> 5ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#include <assert.h> 6ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 7ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// This file determines x86/AMD64 features a processor supports. 8ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// 9ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// We return: 10ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - 0 if the machine matches the asked-for feature. 11ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - 1 if the machine does not. 12ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - 2 if the asked-for feature isn't recognised (this will be the case for 13ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// any feature if run on a non-x86/AMD64 machine). 14ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// - 3 if there was a usage error (it also prints an error message). 15436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov// viz: 16436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov#define FEATURE_PRESENT 0 17436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov#define FEATURE_NOT_PRESENT 1 18436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov#define UNRECOGNISED_FEATURE 2 19436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov#define USAGE_ERROR 3 20436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov 21ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 22ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define False 0 23ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#define True 1 24ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Browntypedef int Bool; 25ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 26ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 27ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if defined(VGA_x86) || defined(VGA_amd64) 28ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic void cpuid ( unsigned int n, 29ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown unsigned int* a, unsigned int* b, 30ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown unsigned int* c, unsigned int* d ) 31ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{ 32ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown __asm__ __volatile__ ( 33ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown "cpuid" 34ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) /* output */ 35ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown : "0" (n) /* input */ 36ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown ); 37ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown} 38ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 39ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Bool vendorStringEquals ( char* str ) 40ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{ 41ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown char vstr[13]; 42ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown unsigned int a, b, c, d; 43ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cpuid(0, &a, &b, &c, &d); 44ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown memcpy(&vstr[0], &b, 4); 45ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown memcpy(&vstr[4], &d, 4); 46ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown memcpy(&vstr[8], &c, 4); 47ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown vstr[12] = 0; 48ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown return 0 == strcmp(vstr, str); 49ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown} 50ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 51436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanovstatic Bool have_xgetbv ( void ) 52436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov{ 53436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov#if defined(VGA_amd64) 54436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov unsigned long long int w; 55436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov __asm__ __volatile__("movq $0,%%rcx ; " 56436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov ".byte 0x0F,0x01,0xD0 ; " /* xgetbv */ 57436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov "movq %%rax,%0" 58436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov :/*OUT*/"=r"(w) :/*IN*/ 59436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov :/*TRASH*/"rdx","rcx"); 60436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov if ((w & 6) == 6) { 61436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov /* OS has enabled both XMM and YMM state support */ 62436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return True; 63436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov } else { 64436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return False; 65436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov } 66436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov#else 67436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return False; 68436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov#endif 69436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov} 70436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov 71ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Bool go(char* cpu) 72ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{ 73ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown unsigned int level = 0, cmask = 0, dmask = 0, a, b, c, d; 74ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown Bool require_amd = False; 75436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov Bool require_xgetbv = False; 76ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown if ( strcmp( cpu, "x86-fpu" ) == 0 ) { 77ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 78ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown dmask = 1 << 0; 79ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "x86-cmov" ) == 0 ) { 80ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 81ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown dmask = 1 << 15; 82ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "x86-mmx" ) == 0 ) { 83ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 84ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown dmask = 1 << 23; 85ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "x86-mmxext" ) == 0 ) { 86ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 0x80000001; 87ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown dmask = 1 << 22; 88ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "x86-sse" ) == 0 ) { 89ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 90ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown dmask = 1 << 25; 91ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "x86-sse2" ) == 0 ) { 92ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 93ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown dmask = 1 << 26; 94ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "x86-sse3" ) == 0 ) { 95ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 96ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cmask = 1 << 0; 97ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "x86-ssse3" ) == 0 ) { 98ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 99ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cmask = 1 << 9; 100ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "x86-lzcnt" ) == 0 ) { 101ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 0x80000001; 102ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cmask = 1 << 5; 103ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown require_amd = True; 104ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#if defined(VGA_amd64) 105ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "amd64-sse3" ) == 0 ) { 106ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 107ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cmask = 1 << 0; 108ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "amd64-pclmulqdq" ) == 0 ) { 109ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 110ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cmask = 1 << 1; 111ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "amd64-ssse3" ) == 0 ) { 112ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 113ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cmask = 1 << 9; 114ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "amd64-cx16" ) == 0 ) { 115ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 1; 116ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cmask = 1 << 13; 117ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else if ( strcmp( cpu, "amd64-lzcnt" ) == 0 ) { 118ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown level = 0x80000001; 119ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cmask = 1 << 5; 120ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown require_amd = True; 121b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov } else if ( strcmp( cpu, "amd64-sse42" ) == 0 ) { 122b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov level = 1; 123b32f58018498ea2225959b0ba11c18f0c433deefEvgeniy Stepanov cmask = 1 << 20; 124436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov } else if ( strcmp( cpu, "amd64-avx" ) == 0 ) { 125436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov level = 1; 126436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov cmask = (1 << 27) | (1 << 28); 127436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov require_xgetbv = True; 128ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif 129ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } else { 130436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return UNRECOGNISED_FEATURE; 131ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } 132ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 133ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown assert( !(cmask != 0 && dmask != 0) ); 134ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown assert( !(cmask == 0 && dmask == 0) ); 135ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 136ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown if (require_amd && !vendorStringEquals("AuthenticAMD")) 137436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return FEATURE_NOT_PRESENT; 138ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown // regardless of what that feature actually is 139ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 140ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cpuid( level & 0x80000000, &a, &b, &c, &d ); 141ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 142ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown if ( a >= level ) { 143ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown cpuid( level, &a, &b, &c, &d ); 144ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 145436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov if (dmask > 0 && (d & dmask) == dmask) { 146436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov if (require_xgetbv && !have_xgetbv()) 147436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return FEATURE_NOT_PRESENT; 148436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov else 149436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return FEATURE_PRESENT; 150436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov } 151436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov if (cmask > 0 && (c & cmask) == cmask) { 152436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov if (require_xgetbv && !have_xgetbv()) 153436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return FEATURE_NOT_PRESENT; 154436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov else 155436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return FEATURE_PRESENT; 156436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov } 157ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } 158436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return FEATURE_NOT_PRESENT; 159ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown} 160ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 161ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#else 162ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 163ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownstatic Bool go(char* cpu) 164ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{ 165436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov // Feature not recognised (non-x86/AMD64 machine!) 166436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov return UNRECOGNISED_FEATURE; 167ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown} 168ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 169ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown#endif // defined(VGA_x86) || defined(VGA_amd64) 170ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 171ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown 172ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//--------------------------------------------------------------------------- 173ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown// main 174ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown//--------------------------------------------------------------------------- 175ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brownint main(int argc, char **argv) 176ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown{ 177ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown if ( argc != 2 ) { 178ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown fprintf( stderr, "usage: x86_amd64_features <feature>\n" ); 179436e89c602e787e7a27dd6624b09beed41a0da8aDmitriy Ivanov exit(USAGE_ERROR); 180ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown } 181ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown return go(argv[1]); 182ed07e00d438c74b7a23c01bfffde77e3968305e4Jeff Brown} 183