1 /*
2  * x86_cpu_features.c - feature detection for x86 processors
3  *
4  * Originally public domain; changes after 2016-09-07 are copyrighted.
5  *
6  * Copyright 2016 Eric Biggers
7  *
8  * Permission is hereby granted, free of charge, to any person
9  * obtaining a copy of this software and associated documentation
10  * files (the "Software"), to deal in the Software without
11  * restriction, including without limitation the rights to use,
12  * copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the
14  * Software is furnished to do so, subject to the following
15  * conditions:
16  *
17  * The above copyright notice and this permission notice shall be
18  * included in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27  * OTHER DEALINGS IN THE SOFTWARE.
28  */
29 
30 #include "x86_cpu_features.h"
31 
32 #if X86_CPU_FEATURES_ENABLED
33 
34 #define DEBUG 0
35 
36 #if DEBUG
37 #  include <stdio.h>
38 #endif
39 
40 u32 _x86_cpu_features = 0;
41 
42 /* With old GCC versions we have to manually save and restore the x86_32 PIC
43  * register (ebx).  See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47602  */
44 #if defined(__i386__) && defined(__PIC__)
45 #  define EBX_CONSTRAINT "=r"
46 #else
47 #  define EBX_CONSTRAINT "=b"
48 #endif
49 
50 /* Execute the CPUID instruction.  */
51 static inline void
cpuid(u32 leaf,u32 subleaf,u32 * a,u32 * b,u32 * c,u32 * d)52 cpuid(u32 leaf, u32 subleaf, u32 *a, u32 *b, u32 *c, u32 *d)
53 {
54 	__asm__(".ifnc %%ebx, %1; mov  %%ebx, %1; .endif\n"
55 		"cpuid                                  \n"
56 		".ifnc %%ebx, %1; xchg %%ebx, %1; .endif\n"
57 		: "=a" (*a), EBX_CONSTRAINT (*b), "=c" (*c), "=d" (*d)
58 		: "a" (leaf), "c" (subleaf));
59 }
60 
61 /* Read an extended control register.  */
62 static inline u64
read_xcr(u32 index)63 read_xcr(u32 index)
64 {
65 	u32 edx, eax;
66 
67 	/* Execute the "xgetbv" instruction.  Old versions of binutils do not
68 	 * recognize this instruction, so list the raw bytes instead.  */
69 	__asm__ (".byte 0x0f, 0x01, 0xd0" : "=d" (edx), "=a" (eax) : "c" (index));
70 
71 	return ((u64)edx << 32) | eax;
72 }
73 
74 #define IS_SET(reg, bit) ((reg) & ((u32)1 << (bit)))
75 
76 /* Initialize _x86_cpu_features with bits for interesting processor features. */
77 void
x86_setup_cpu_features(void)78 x86_setup_cpu_features(void)
79 {
80 	u32 features = 0;
81 	u32 dummy1, dummy2, dummy3, dummy4;
82 	u32 max_function;
83 	u32 features_1, features_2, features_3, features_4;
84 	bool os_saves_ymm_regs = false;
85 
86 	/* Get maximum supported function  */
87 	cpuid(0, 0, &max_function, &dummy2, &dummy3, &dummy4);
88 	if (max_function < 1)
89 		goto out;
90 
91 	/* Standard feature flags  */
92 	cpuid(1, 0, &dummy1, &dummy2, &features_2, &features_1);
93 
94 	if (IS_SET(features_1, 25))
95 		features |= X86_CPU_FEATURE_SSE;
96 
97 	if (IS_SET(features_1, 26))
98 		features |= X86_CPU_FEATURE_SSE2;
99 
100 	if (IS_SET(features_2, 0))
101 		features |= X86_CPU_FEATURE_SSE3;
102 
103 	if (IS_SET(features_2, 1))
104 		features |= X86_CPU_FEATURE_PCLMULQDQ;
105 
106 	if (IS_SET(features_2, 9))
107 		features |= X86_CPU_FEATURE_SSSE3;
108 
109 	if (IS_SET(features_2, 19))
110 		features |= X86_CPU_FEATURE_SSE4_1;
111 
112 	if (IS_SET(features_2, 20))
113 		features |= X86_CPU_FEATURE_SSE4_2;
114 
115 	if (IS_SET(features_2, 27)) /* OSXSAVE set?  */
116 		if ((read_xcr(0) & 0x6) == 0x6)
117 			os_saves_ymm_regs = true;
118 
119 	if (os_saves_ymm_regs && IS_SET(features_2, 28))
120 		features |= X86_CPU_FEATURE_AVX;
121 
122 	if (max_function < 7)
123 		goto out;
124 
125 	/* Extended feature flags  */
126 	cpuid(7, 0, &dummy1, &features_3, &features_4, &dummy4);
127 
128 	if (IS_SET(features_3, 3))
129 		features |= X86_CPU_FEATURE_BMI;
130 
131 	if (os_saves_ymm_regs && IS_SET(features_3, 5))
132 		features |= X86_CPU_FEATURE_AVX2;
133 
134 	if (IS_SET(features_3, 8))
135 		features |= X86_CPU_FEATURE_BMI2;
136 
137 out:
138 
139 #if DEBUG
140 	printf("Detected x86 CPU features: ");
141 	if (features & X86_CPU_FEATURE_SSE)
142 		printf("SSE ");
143 	if (features & X86_CPU_FEATURE_SSE2)
144 		printf("SSE2 ");
145 	if (features & X86_CPU_FEATURE_SSE3)
146 		printf("SSE3 ");
147 	if (features & X86_CPU_FEATURE_PCLMULQDQ)
148 		printf("PCLMULQDQ ");
149 	if (features & X86_CPU_FEATURE_SSSE3)
150 		printf("SSSE3 ");
151 	if (features & X86_CPU_FEATURE_SSE4_1)
152 		printf("SSE4.1 ");
153 	if (features & X86_CPU_FEATURE_SSE4_2)
154 		printf("SSE4.2 ");
155 	if (features & X86_CPU_FEATURE_BMI)
156 		printf("BMI ");
157 	if (features & X86_CPU_FEATURE_AVX)
158 		printf("AVX ");
159 	if (features & X86_CPU_FEATURE_BMI2)
160 		printf("BMI2 ");
161 	if (features & X86_CPU_FEATURE_AVX2)
162 		printf("AVX2 ");
163 	printf("\n");
164 #endif /* DEBUG */
165 
166 	_x86_cpu_features = features | X86_CPU_FEATURES_KNOWN;
167 }
168 
169 #endif /* X86_CPU_FEATURES_ENABLED */
170