154cd2af33da0ec9925f0c2a184470c683adc2de9njn 254cd2af33da0ec9925f0c2a184470c683adc2de9njn#include <stdio.h> 354cd2af33da0ec9925f0c2a184470c683adc2de9njn#include <stdlib.h> 454cd2af33da0ec9925f0c2a184470c683adc2de9njn#include <string.h> 554cd2af33da0ec9925f0c2a184470c683adc2de9njn#include <assert.h> 654cd2af33da0ec9925f0c2a184470c683adc2de9njn 754cd2af33da0ec9925f0c2a184470c683adc2de9njn// This file determines x86/AMD64 features a processor supports. 854cd2af33da0ec9925f0c2a184470c683adc2de9njn// 954cd2af33da0ec9925f0c2a184470c683adc2de9njn// We return: 1054cd2af33da0ec9925f0c2a184470c683adc2de9njn// - 0 if the machine matches the asked-for feature. 1154cd2af33da0ec9925f0c2a184470c683adc2de9njn// - 1 if the machine does not. 1254cd2af33da0ec9925f0c2a184470c683adc2de9njn// - 2 if the asked-for feature isn't recognised (this will be the case for 1354cd2af33da0ec9925f0c2a184470c683adc2de9njn// any feature if run on a non-x86/AMD64 machine). 1454cd2af33da0ec9925f0c2a184470c683adc2de9njn// - 3 if there was a usage error (it also prints an error message). 15bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj// viz: 16bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj#define FEATURE_PRESENT 0 17bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj#define FEATURE_NOT_PRESENT 1 18bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj#define UNRECOGNISED_FEATURE 2 199670e8a4bc3f9b9454a8a7cb616bfeded225e2a6sewardj#define USAGE_ERROR 3 20bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj 2154cd2af33da0ec9925f0c2a184470c683adc2de9njn 2254cd2af33da0ec9925f0c2a184470c683adc2de9njn#define False 0 2354cd2af33da0ec9925f0c2a184470c683adc2de9njn#define True 1 2454cd2af33da0ec9925f0c2a184470c683adc2de9njntypedef int Bool; 2554cd2af33da0ec9925f0c2a184470c683adc2de9njn 263ea5b0b33e91e2461168c17b70f0ea26963722ccnjn 273ea5b0b33e91e2461168c17b70f0ea26963722ccnjn#if defined(VGA_x86) || defined(VGA_amd64) 2854cd2af33da0ec9925f0c2a184470c683adc2de9njnstatic void cpuid ( unsigned int n, 2954cd2af33da0ec9925f0c2a184470c683adc2de9njn unsigned int* a, unsigned int* b, 3054cd2af33da0ec9925f0c2a184470c683adc2de9njn unsigned int* c, unsigned int* d ) 3154cd2af33da0ec9925f0c2a184470c683adc2de9njn{ 3254cd2af33da0ec9925f0c2a184470c683adc2de9njn __asm__ __volatile__ ( 3354cd2af33da0ec9925f0c2a184470c683adc2de9njn "cpuid" 3454cd2af33da0ec9925f0c2a184470c683adc2de9njn : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) /* output */ 3554cd2af33da0ec9925f0c2a184470c683adc2de9njn : "0" (n) /* input */ 3654cd2af33da0ec9925f0c2a184470c683adc2de9njn ); 3754cd2af33da0ec9925f0c2a184470c683adc2de9njn} 383cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj 393cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardjstatic Bool vendorStringEquals ( char* str ) 403cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj{ 413cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj char vstr[13]; 423cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj unsigned int a, b, c, d; 433cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj cpuid(0, &a, &b, &c, &d); 443cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj memcpy(&vstr[0], &b, 4); 453cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj memcpy(&vstr[4], &d, 4); 463cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj memcpy(&vstr[8], &c, 4); 473cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj vstr[12] = 0; 483cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj return 0 == strcmp(vstr, str); 493cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj} 503cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj 51bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardjstatic Bool have_xgetbv ( void ) 52bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj{ 53bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj#if defined(VGA_amd64) 54bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj unsigned long long int w; 55bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj __asm__ __volatile__("movq $0,%%rcx ; " 56bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj ".byte 0x0F,0x01,0xD0 ; " /* xgetbv */ 57bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj "movq %%rax,%0" 58bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj :/*OUT*/"=r"(w) :/*IN*/ 59bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj :/*TRASH*/"rdx","rcx"); 60bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj if ((w & 6) == 6) { 61bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj /* OS has enabled both XMM and YMM state support */ 62bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return True; 63bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj } else { 64bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return False; 65bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj } 66bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj#else 67bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return False; 68bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj#endif 69bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj} 70bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj 7154cd2af33da0ec9925f0c2a184470c683adc2de9njnstatic Bool go(char* cpu) 7254cd2af33da0ec9925f0c2a184470c683adc2de9njn{ 7354cd2af33da0ec9925f0c2a184470c683adc2de9njn unsigned int level = 0, cmask = 0, dmask = 0, a, b, c, d; 743cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj Bool require_amd = False; 75bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj Bool require_xgetbv = False; 7654cd2af33da0ec9925f0c2a184470c683adc2de9njn if ( strcmp( cpu, "x86-fpu" ) == 0 ) { 7754cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 1; 7854cd2af33da0ec9925f0c2a184470c683adc2de9njn dmask = 1 << 0; 7954cd2af33da0ec9925f0c2a184470c683adc2de9njn } else if ( strcmp( cpu, "x86-cmov" ) == 0 ) { 8054cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 1; 8154cd2af33da0ec9925f0c2a184470c683adc2de9njn dmask = 1 << 15; 8254cd2af33da0ec9925f0c2a184470c683adc2de9njn } else if ( strcmp( cpu, "x86-mmx" ) == 0 ) { 8354cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 1; 8454cd2af33da0ec9925f0c2a184470c683adc2de9njn dmask = 1 << 23; 8554cd2af33da0ec9925f0c2a184470c683adc2de9njn } else if ( strcmp( cpu, "x86-mmxext" ) == 0 ) { 8654cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 0x80000001; 8754cd2af33da0ec9925f0c2a184470c683adc2de9njn dmask = 1 << 22; 8854cd2af33da0ec9925f0c2a184470c683adc2de9njn } else if ( strcmp( cpu, "x86-sse" ) == 0 ) { 8954cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 1; 9054cd2af33da0ec9925f0c2a184470c683adc2de9njn dmask = 1 << 25; 9154cd2af33da0ec9925f0c2a184470c683adc2de9njn } else if ( strcmp( cpu, "x86-sse2" ) == 0 ) { 9254cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 1; 9354cd2af33da0ec9925f0c2a184470c683adc2de9njn dmask = 1 << 26; 9454cd2af33da0ec9925f0c2a184470c683adc2de9njn } else if ( strcmp( cpu, "x86-sse3" ) == 0 ) { 9554cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 1; 9654cd2af33da0ec9925f0c2a184470c683adc2de9njn cmask = 1 << 0; 9754cd2af33da0ec9925f0c2a184470c683adc2de9njn } else if ( strcmp( cpu, "x86-ssse3" ) == 0 ) { 9854cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 1; 9954cd2af33da0ec9925f0c2a184470c683adc2de9njn cmask = 1 << 9; 1003cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj } else if ( strcmp( cpu, "x86-lzcnt" ) == 0 ) { 1013cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj level = 0x80000001; 1023cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj cmask = 1 << 5; 1033cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj require_amd = True; 10454cd2af33da0ec9925f0c2a184470c683adc2de9njn#if defined(VGA_amd64) 10554cd2af33da0ec9925f0c2a184470c683adc2de9njn } else if ( strcmp( cpu, "amd64-sse3" ) == 0 ) { 10654cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 1; 10754cd2af33da0ec9925f0c2a184470c683adc2de9njn cmask = 1 << 0; 108e3ae8a3c0768f7fe0d2627e6f889bff3c4c053ecsewardj } else if ( strcmp( cpu, "amd64-pclmulqdq" ) == 0 ) { 109e3ae8a3c0768f7fe0d2627e6f889bff3c4c053ecsewardj level = 1; 110e3ae8a3c0768f7fe0d2627e6f889bff3c4c053ecsewardj cmask = 1 << 1; 11154cd2af33da0ec9925f0c2a184470c683adc2de9njn } else if ( strcmp( cpu, "amd64-ssse3" ) == 0 ) { 11254cd2af33da0ec9925f0c2a184470c683adc2de9njn level = 1; 11354cd2af33da0ec9925f0c2a184470c683adc2de9njn cmask = 1 << 9; 114a01ed5e21c0a4fcbc0025ff03ccf47289491f25esewardj } else if ( strcmp( cpu, "amd64-cx16" ) == 0 ) { 115a01ed5e21c0a4fcbc0025ff03ccf47289491f25esewardj level = 1; 116a01ed5e21c0a4fcbc0025ff03ccf47289491f25esewardj cmask = 1 << 13; 1173cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj } else if ( strcmp( cpu, "amd64-lzcnt" ) == 0 ) { 1183cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj level = 0x80000001; 1193cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj cmask = 1 << 5; 1203cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj require_amd = True; 12127d176ae5148b92ffe384702c534e1573f5a7719sewardj } else if ( strcmp( cpu, "amd64-sse42" ) == 0 ) { 12227d176ae5148b92ffe384702c534e1573f5a7719sewardj level = 1; 12327d176ae5148b92ffe384702c534e1573f5a7719sewardj cmask = 1 << 20; 124bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj } else if ( strcmp( cpu, "amd64-avx" ) == 0 ) { 125bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj level = 1; 1265e82b89684e6c2440ef635f529289727e006b351sewardj cmask = (1 << 27) | (1 << 28); 127bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj require_xgetbv = True; 12854cd2af33da0ec9925f0c2a184470c683adc2de9njn#endif 12954cd2af33da0ec9925f0c2a184470c683adc2de9njn } else { 130bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return UNRECOGNISED_FEATURE; 13154cd2af33da0ec9925f0c2a184470c683adc2de9njn } 13254cd2af33da0ec9925f0c2a184470c683adc2de9njn 13354cd2af33da0ec9925f0c2a184470c683adc2de9njn assert( !(cmask != 0 && dmask != 0) ); 13454cd2af33da0ec9925f0c2a184470c683adc2de9njn assert( !(cmask == 0 && dmask == 0) ); 13554cd2af33da0ec9925f0c2a184470c683adc2de9njn 1363cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj if (require_amd && !vendorStringEquals("AuthenticAMD")) 137bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return FEATURE_NOT_PRESENT; 1383cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj // regardless of what that feature actually is 1393cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj 14054cd2af33da0ec9925f0c2a184470c683adc2de9njn cpuid( level & 0x80000000, &a, &b, &c, &d ); 14154cd2af33da0ec9925f0c2a184470c683adc2de9njn 14254cd2af33da0ec9925f0c2a184470c683adc2de9njn if ( a >= level ) { 14354cd2af33da0ec9925f0c2a184470c683adc2de9njn cpuid( level, &a, &b, &c, &d ); 14454cd2af33da0ec9925f0c2a184470c683adc2de9njn 145bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj if (dmask > 0 && (d & dmask) == dmask) { 146bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj if (require_xgetbv && !have_xgetbv()) 147bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return FEATURE_NOT_PRESENT; 148bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj else 149bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return FEATURE_PRESENT; 150bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj } 1519670e8a4bc3f9b9454a8a7cb616bfeded225e2a6sewardj if (cmask > 0 && (c & cmask) == cmask) { 152bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj if (require_xgetbv && !have_xgetbv()) 153bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return FEATURE_NOT_PRESENT; 154bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj else 155bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return FEATURE_PRESENT; 156bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj } 15754cd2af33da0ec9925f0c2a184470c683adc2de9njn } 158bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return FEATURE_NOT_PRESENT; 15954cd2af33da0ec9925f0c2a184470c683adc2de9njn} 16054cd2af33da0ec9925f0c2a184470c683adc2de9njn 161f76d27a697a7b0bf3b84490baf60623fc96a23afnjn#else 16254cd2af33da0ec9925f0c2a184470c683adc2de9njn 16354cd2af33da0ec9925f0c2a184470c683adc2de9njnstatic Bool go(char* cpu) 16454cd2af33da0ec9925f0c2a184470c683adc2de9njn{ 165bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj // Feature not recognised (non-x86/AMD64 machine!) 166bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj return UNRECOGNISED_FEATURE; 16754cd2af33da0ec9925f0c2a184470c683adc2de9njn} 16854cd2af33da0ec9925f0c2a184470c683adc2de9njn 16954cd2af33da0ec9925f0c2a184470c683adc2de9njn#endif // defined(VGA_x86) || defined(VGA_amd64) 17054cd2af33da0ec9925f0c2a184470c683adc2de9njn 17154cd2af33da0ec9925f0c2a184470c683adc2de9njn 17254cd2af33da0ec9925f0c2a184470c683adc2de9njn//--------------------------------------------------------------------------- 17354cd2af33da0ec9925f0c2a184470c683adc2de9njn// main 17454cd2af33da0ec9925f0c2a184470c683adc2de9njn//--------------------------------------------------------------------------- 17554cd2af33da0ec9925f0c2a184470c683adc2de9njnint main(int argc, char **argv) 17654cd2af33da0ec9925f0c2a184470c683adc2de9njn{ 17754cd2af33da0ec9925f0c2a184470c683adc2de9njn if ( argc != 2 ) { 17854cd2af33da0ec9925f0c2a184470c683adc2de9njn fprintf( stderr, "usage: x86_amd64_features <feature>\n" ); 1799670e8a4bc3f9b9454a8a7cb616bfeded225e2a6sewardj exit(USAGE_ERROR); 18054cd2af33da0ec9925f0c2a184470c683adc2de9njn } 18154cd2af33da0ec9925f0c2a184470c683adc2de9njn return go(argv[1]); 18254cd2af33da0ec9925f0c2a184470c683adc2de9njn} 183