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;
128a0664b9ca67b594bd6f570a61d3301167a24750cElliott Hughes   } else if (strcmp (cpu,  "amd64-fma4" ) == 0) {
129a0664b9ca67b594bd6f570a61d3301167a24750cElliott Hughes     level = 0x80000001;
130a0664b9ca67b594bd6f570a61d3301167a24750cElliott Hughes     cmask = 1 << 16;
131a0664b9ca67b594bd6f570a61d3301167a24750cElliott Hughes     require_amd = True;
13254cd2af33da0ec9925f0c2a184470c683adc2de9njn#endif
13354cd2af33da0ec9925f0c2a184470c683adc2de9njn   } else {
134bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj     return UNRECOGNISED_FEATURE;
13554cd2af33da0ec9925f0c2a184470c683adc2de9njn   }
13654cd2af33da0ec9925f0c2a184470c683adc2de9njn
13754cd2af33da0ec9925f0c2a184470c683adc2de9njn   assert( !(cmask != 0 && dmask != 0) );
13854cd2af33da0ec9925f0c2a184470c683adc2de9njn   assert( !(cmask == 0 && dmask == 0) );
13954cd2af33da0ec9925f0c2a184470c683adc2de9njn
1403cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj   if (require_amd && !vendorStringEquals("AuthenticAMD"))
141bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj      return FEATURE_NOT_PRESENT;
1423cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj      // regardless of what that feature actually is
1433cf36a9ccb55ef06cd7a0937fc89440ae420dc3dsewardj
14454cd2af33da0ec9925f0c2a184470c683adc2de9njn   cpuid( level & 0x80000000, &a, &b, &c, &d );
14554cd2af33da0ec9925f0c2a184470c683adc2de9njn
14654cd2af33da0ec9925f0c2a184470c683adc2de9njn   if ( a >= level ) {
14754cd2af33da0ec9925f0c2a184470c683adc2de9njn      cpuid( level, &a, &b, &c, &d );
14854cd2af33da0ec9925f0c2a184470c683adc2de9njn
149bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj      if (dmask > 0 && (d & dmask) == dmask) {
150bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj         if (require_xgetbv && !have_xgetbv())
151bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj            return FEATURE_NOT_PRESENT;
152bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj         else
153bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj            return FEATURE_PRESENT;
154bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj      }
1559670e8a4bc3f9b9454a8a7cb616bfeded225e2a6sewardj      if (cmask > 0 && (c & cmask) == cmask) {
156bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj         if (require_xgetbv && !have_xgetbv())
157bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj            return FEATURE_NOT_PRESENT;
158bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj         else
159bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj            return FEATURE_PRESENT;
160bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj      }
16154cd2af33da0ec9925f0c2a184470c683adc2de9njn   }
162bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj   return FEATURE_NOT_PRESENT;
16354cd2af33da0ec9925f0c2a184470c683adc2de9njn}
16454cd2af33da0ec9925f0c2a184470c683adc2de9njn
165f76d27a697a7b0bf3b84490baf60623fc96a23afnjn#else
16654cd2af33da0ec9925f0c2a184470c683adc2de9njn
16754cd2af33da0ec9925f0c2a184470c683adc2de9njnstatic Bool go(char* cpu)
16854cd2af33da0ec9925f0c2a184470c683adc2de9njn{
169bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj   // Feature not recognised (non-x86/AMD64 machine!)
170bedf990c986d7bff91ad9be9bf0aaeee34cb0de3sewardj   return UNRECOGNISED_FEATURE;
17154cd2af33da0ec9925f0c2a184470c683adc2de9njn}
17254cd2af33da0ec9925f0c2a184470c683adc2de9njn
17354cd2af33da0ec9925f0c2a184470c683adc2de9njn#endif   // defined(VGA_x86)  || defined(VGA_amd64)
17454cd2af33da0ec9925f0c2a184470c683adc2de9njn
17554cd2af33da0ec9925f0c2a184470c683adc2de9njn
17654cd2af33da0ec9925f0c2a184470c683adc2de9njn//---------------------------------------------------------------------------
17754cd2af33da0ec9925f0c2a184470c683adc2de9njn// main
17854cd2af33da0ec9925f0c2a184470c683adc2de9njn//---------------------------------------------------------------------------
17954cd2af33da0ec9925f0c2a184470c683adc2de9njnint main(int argc, char **argv)
18054cd2af33da0ec9925f0c2a184470c683adc2de9njn{
18154cd2af33da0ec9925f0c2a184470c683adc2de9njn   if ( argc != 2 ) {
18254cd2af33da0ec9925f0c2a184470c683adc2de9njn      fprintf( stderr, "usage: x86_amd64_features <feature>\n" );
1839670e8a4bc3f9b9454a8a7cb616bfeded225e2a6sewardj      exit(USAGE_ERROR);
18454cd2af33da0ec9925f0c2a184470c683adc2de9njn   }
18554cd2af33da0ec9925f0c2a184470c683adc2de9njn   return go(argv[1]);
18654cd2af33da0ec9925f0c2a184470c683adc2de9njn}
187