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