1
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <assert.h>
6
7// This file determines x86/AMD64 features a processor supports.
8//
9// We return:
10// - 0 if the machine matches the asked-for feature.
11// - 1 if the machine does not.
12// - 2 if the asked-for feature isn't recognised (this will be the case for
13//     any feature if run on a non-x86/AMD64 machine).
14// - 3 if there was a usage error (it also prints an error message).
15// viz:
16#define FEATURE_PRESENT       0
17#define FEATURE_NOT_PRESENT   1
18#define UNRECOGNISED_FEATURE  2
19#define USAGE_ERROR           3
20
21
22#define False  0
23#define True   1
24typedef int    Bool;
25
26
27#if defined(VGA_x86) || defined(VGA_amd64)
28static void cpuid ( unsigned int n,
29                    unsigned int* a, unsigned int* b,
30                    unsigned int* c, unsigned int* d )
31{
32   __asm__ __volatile__ (
33      "cpuid"
34      : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)      /* output */
35      : "0" (n)         /* input */
36   );
37}
38
39static Bool vendorStringEquals ( char* str )
40{
41   char vstr[13];
42   unsigned int a, b, c, d;
43   cpuid(0, &a, &b, &c, &d);
44   memcpy(&vstr[0], &b, 4);
45   memcpy(&vstr[4], &d, 4);
46   memcpy(&vstr[8], &c, 4);
47   vstr[12] = 0;
48   return 0 == strcmp(vstr, str);
49}
50
51static Bool have_xgetbv ( void )
52{
53#if defined(VGA_amd64)
54   unsigned long long int w;
55   __asm__ __volatile__("movq $0,%%rcx ; "
56                        ".byte 0x0F,0x01,0xD0 ; " /* xgetbv */
57                        "movq %%rax,%0"
58                        :/*OUT*/"=r"(w) :/*IN*/
59                        :/*TRASH*/"rdx","rcx");
60   if ((w & 6) == 6) {
61      /* OS has enabled both XMM and YMM state support */
62      return True;
63   } else {
64      return False;
65   }
66#else
67   return False;
68#endif
69}
70
71static Bool go(char* cpu)
72{
73   unsigned int level = 0, cmask = 0, dmask = 0, a, b, c, d;
74   Bool require_amd = False;
75   Bool require_xgetbv = False;
76   if        ( strcmp( cpu, "x86-fpu" ) == 0 ) {
77     level = 1;
78     dmask = 1 << 0;
79   } else if ( strcmp( cpu, "x86-cmov" ) == 0 ) {
80     level = 1;
81     dmask = 1 << 15;
82   } else if ( strcmp( cpu, "x86-mmx" ) == 0 ) {
83     level = 1;
84     dmask = 1 << 23;
85   } else if ( strcmp( cpu, "x86-mmxext" ) == 0 ) {
86     level = 0x80000001;
87     dmask = 1 << 22;
88   } else if ( strcmp( cpu, "x86-sse" ) == 0 ) {
89     level = 1;
90     dmask = 1 << 25;
91   } else if ( strcmp( cpu, "x86-sse2" ) == 0 ) {
92     level = 1;
93     dmask = 1 << 26;
94   } else if ( strcmp( cpu, "x86-sse3" ) == 0 ) {
95     level = 1;
96     cmask = 1 << 0;
97   } else if ( strcmp( cpu, "x86-ssse3" ) == 0 ) {
98     level = 1;
99     cmask = 1 << 9;
100   } else if ( strcmp( cpu, "x86-lzcnt" ) == 0 ) {
101     level = 0x80000001;
102     cmask = 1 << 5;
103     require_amd = True;
104#if defined(VGA_amd64)
105   } else if ( strcmp( cpu, "amd64-sse3" ) == 0 ) {
106     level = 1;
107     cmask = 1 << 0;
108   } else if ( strcmp( cpu, "amd64-pclmulqdq" ) == 0 ) {
109     level = 1;
110     cmask = 1 << 1;
111   } else if ( strcmp( cpu, "amd64-ssse3" ) == 0 ) {
112     level = 1;
113     cmask = 1 << 9;
114   } else if ( strcmp( cpu, "amd64-cx16" ) == 0 ) {
115     level = 1;
116     cmask = 1 << 13;
117   } else if ( strcmp( cpu, "amd64-lzcnt" ) == 0 ) {
118     level = 0x80000001;
119     cmask = 1 << 5;
120     require_amd = True;
121   } else if ( strcmp( cpu, "amd64-sse42" ) == 0 ) {
122     level = 1;
123     cmask = 1 << 20;
124   } else if ( strcmp( cpu, "amd64-avx" ) == 0 ) {
125     level = 1;
126     cmask = (1 << 27) | (1 << 28);
127     require_xgetbv = True;
128#endif
129   } else {
130     return UNRECOGNISED_FEATURE;
131   }
132
133   assert( !(cmask != 0 && dmask != 0) );
134   assert( !(cmask == 0 && dmask == 0) );
135
136   if (require_amd && !vendorStringEquals("AuthenticAMD"))
137      return FEATURE_NOT_PRESENT;
138      // regardless of what that feature actually is
139
140   cpuid( level & 0x80000000, &a, &b, &c, &d );
141
142   if ( a >= level ) {
143      cpuid( level, &a, &b, &c, &d );
144
145      if (dmask > 0 && (d & dmask) == dmask) {
146         if (require_xgetbv && !have_xgetbv())
147            return FEATURE_NOT_PRESENT;
148         else
149            return FEATURE_PRESENT;
150      }
151      if (cmask > 0 && (c & cmask) == cmask) {
152         if (require_xgetbv && !have_xgetbv())
153            return FEATURE_NOT_PRESENT;
154         else
155            return FEATURE_PRESENT;
156      }
157   }
158   return FEATURE_NOT_PRESENT;
159}
160
161#else
162
163static Bool go(char* cpu)
164{
165   // Feature not recognised (non-x86/AMD64 machine!)
166   return UNRECOGNISED_FEATURE;
167}
168
169#endif   // defined(VGA_x86)  || defined(VGA_amd64)
170
171
172//---------------------------------------------------------------------------
173// main
174//---------------------------------------------------------------------------
175int main(int argc, char **argv)
176{
177   if ( argc != 2 ) {
178      fprintf( stderr, "usage: x86_amd64_features <feature>\n" );
179      exit(USAGE_ERROR);
180   }
181   return go(argv[1]);
182}
183