1/*
2 Copyright 2010 Kevin Ottens <ervin@kde.org>
3 Copyright 2013 Patrick Spendrin <ps_ml@gmx.de>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), which shall
11 act as a proxy defined in Section 6 of version 3 of the license.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library. If not, see <http://www.gnu.org/licenses/>.
20*/
21
22#include "cpufeatures.h"
23
24#ifndef _MSC_VER
25//for cpuFeatures
26#include <csignal>
27#include <csetjmp>
28#else
29#include <intrin.h>
30#endif
31#include <config-processor.h>
32
33#if defined(__GNUC__) || defined(__INTEL_COMPILER)
34# define HAVE_GNU_INLINE_ASM
35#endif
36
37namespace Solid
38{
39namespace Backends
40{
41namespace Shared
42{
43
44#ifndef _MSC_VER
45typedef void (*kde_sighandler_t)(int);
46
47#if defined( __i386__ ) || defined( __x86_64__ )
48static jmp_buf env;
49
50#ifdef HAVE_X86_SSE
51// Sighandler for the SSE OS support check
52static void sighandler(int)
53{
54 std::longjmp(env, 1);
55}
56#endif
57#endif
58
59#ifdef __i386__
60#define ASM_REG(reg) "%e" reg
61#define ASM_POP(reg) "popl %%e" reg " \n\t"
62#define ASM_PUSH(reg) "pushl %%e" reg " \n\t"
63#define ASM_XOR_REG(reg1, reg2) "xorl %%e" reg1 ", %%e" reg2 " \n\t"
64#define ASM_XOR_VAR(var, reg) "xorl " var ", %%e" reg " \n\t"
65#define ASM_CMP_REG(reg1, reg2) "cmpl %%e" reg1 ", %%e" reg2 " \n\t"
66#define ASM_MOV_REG(reg1, reg2) "movl %%e" reg1 ", %%e" reg2 " \n\t"
67#define ASM_MOV_VAR(var, reg) "movl " var ", %%e" reg " \n\t"
68#elif defined(__x86_64__)
69#define ASM_REG(reg) "%r" reg
70#define ASM_POP(reg) "popq %%r" reg " \n\t"
71#define ASM_PUSH(reg) "pushq %%r" reg " \n\t"
72#define ASM_XOR_REG(reg1, reg2) "xorq %%r" reg1 ", %%r" reg2 " \n\t"
73#define ASM_XOR_VAR(var, reg) "xorq " var ", %%r" reg " \n\t"
74#define ASM_CMP_REG(reg1, reg2) "cmpq %%r" reg1 ", %%r" reg2 " \n\t"
75#define ASM_MOV_REG(reg1, reg2) "movq %%r" reg1 ", %%r" reg2 " \n\t"
76#define ASM_MOV_VAR(var, reg) "movq " var ", %%r" reg " \n\t"
77#endif
78#endif
79
80#ifdef __PPC__
81static sigjmp_buf jmpbuf;
82static sig_atomic_t canjump = 0;
83
84static void sigill_handler(int sig)
85{
86 if (!canjump) {
87 signal(sig, SIG_DFL);
88 raise(sig);
89 }
90 canjump = 0;
91 siglongjmp(jmpbuf, 1);
92}
93#endif
94
95Solid::Processor::InstructionSets cpuFeatures()
96{
97 volatile unsigned int features = 0;
98
99#if defined( HAVE_GNU_INLINE_ASM )
100#if defined( __i386__ ) || defined( __x86_64__ )
101 bool haveCPUID = false;
102 unsigned int result = 0, result2 = 0;
103
104 // First check if the CPU supports the CPUID instruction
105 __asm__ __volatile__(
106 // Try to toggle the CPUID bit in the EFLAGS register
107 "pushf \n\t" // Push the EFLAGS register onto the stack
108 ASM_POP("cx") // Pop the value into ECX
109 ASM_MOV_REG("cx", "dx") // Copy ECX to EDX
110 ASM_XOR_VAR("$0x00200000", "cx") // Toggle bit 21 (CPUID) in ECX
111 ASM_PUSH("cx") // Push the modified value onto the stack
112 "popf \n\t" // Pop it back into EFLAGS
113
114 // Check if the CPUID bit was successfully toggled
115 "pushf \n\t" // Push EFLAGS back onto the stack
116 ASM_POP("cx") // Pop the value into ECX
117 ASM_XOR_REG("ax", "ax") // Zero out the EAX register
118 ASM_CMP_REG("cx", "dx") // Compare ECX with EDX
119 "je .Lno_cpuid_support%= \n\t" // Jump if they're identical
120 ASM_MOV_VAR("$1", "ax") // Set EAX to true
121 ".Lno_cpuid_support%=: \n\t"
122 : "=a"(haveCPUID) : : ASM_REG("cx"), ASM_REG("dx"));
123
124 // If we don't have CPUID we won't have the other extensions either
125 if (haveCPUID) {
126 // Execute CPUID with the feature request bit set
127 __asm__ __volatile__(
128 ASM_PUSH("bx") // Save EBX
129 ASM_MOV_VAR("$1", "ax") // Set EAX to 1 (features request)
130 "cpuid \n\t" // Call CPUID
131 ASM_POP("bx") // Restore EBX
132 : "=d"(result), "=c"(result2) : : ASM_REG("ax"));
133
134 features = result & 0x06800000; //copy the mmx and sse & sse2 bits to features
135 features |= result2 & 0x00180101; //copy the ssse3, sse3 and sse4.1, sse4.2 bits to features
136
137 __asm__ __volatile__(
138 ASM_PUSH("bx")
139 ASM_PUSH("dx")
140 ASM_MOV_VAR("$0x80000000", "ax")
141 ASM_MOV_VAR("$0x80000000", "dx")
142 "cpuid \n\t"
143 ASM_CMP_REG("dx", "ax")
144 "jbe .Lno_extended%= \n\t"
145 ASM_MOV_VAR("$0x80000001", "ax")
146 "cpuid \n\t"
147 ".Lno_extended%=: \n\t"
148 ASM_POP("dx")
149 ASM_POP("bx") // Restore EBX
150 : "=d"(result) : : ASM_REG("ax"), ASM_REG("cx"));
151
152 if (result & 0x80000000) {
153 features |= 0x80000000;
154 }
155
156#ifdef HAVE_X86_SSE
157 // Test bit 25 (SSE support)
158 if (features & 0x02000000) {
159 // OS support test for SSE.
160 // Install our own sighandler for SIGILL.
161 kde_sighandler_t oldhandler = std::signal(SIGILL, sighandler);
162
163 // Try executing an SSE insn to see if we get a SIGILL
164 if (setjmp(env)) {
165 features &= ~0x06080001; // The OS support test failed
166 } else {
167 __asm__ __volatile__("xorps %xmm0, %xmm0");
168 }
169
170 // Restore the default sighandler
171 std::signal(SIGILL, oldhandler);
172
173 // Note: The OS requirements for SSE2 are the same as for SSE
174 // so we don't have to do any additional tests for that.
175 }
176#endif // HAVE_X86_SSE
177 }
178#elif defined __PPC__ && defined HAVE_PPC_ALTIVEC
179 signal(SIGILL, sigill_handler);
180 if (sigsetjmp(jmpbuf, 1)) {
181 signal(SIGILL, SIG_DFL);
182 } else {
183 canjump = 1;
184 __asm__ __volatile__("mtspr 256, %0\n\t"
185 "vand %%v0, %%v0, %%v0"
186 : /* none */
187 : "r"(-1));
188 signal(SIGILL, SIG_DFL);
189 features = 0x2;
190 }
191#endif // __i386__ || __x86_64__
192#elif defined(_MSC_VER)
193 int array[4], ft = 1;
194 __cpuid(array, ft);
195
196 features = array[3] & 0x06800000; //copy the mmx and sse & sse2 bits to features
197 features |= array[2] & 0x00180101; //copy the ssse3, sse3 and sse4.1, sse4.2 bits to features
198
199 if (array[3] & 0x80000000) {
200 features |= 0x80000000;
201 }
202#endif //HAVE_GNU_INLINE_ASM
203 Solid::Processor::InstructionSets featureflags;
204
205 if (features & 0x80000000) {
206 featureflags |= Solid::Processor::Amd3DNow;
207 }
208 if (features & 0x00800000) {
209 featureflags |= Solid::Processor::IntelMmx;
210 }
211 if (features & 0x02000000) {
212 featureflags |= Solid::Processor::IntelSse;
213 }
214 if (features & 0x04000000) {
215 featureflags |= Solid::Processor::IntelSse2;
216 }
217 if (features & 0x00000001) {
218 featureflags |= Solid::Processor::IntelSse3;
219 }
220 if (features & 0x00000100) {
221 featureflags |= Solid::Processor::IntelSsse3;
222 }
223 if (features & 0x00080000) {
224 featureflags |= Solid::Processor::IntelSse41;
225 }
226 if (features & 0x00100000) {
227 featureflags |= Solid::Processor::IntelSse42;
228 }
229
230 if (features & 0x2) {
231 featureflags |= Solid::Processor::AltiVec;
232 }
233
234 return featureflags;
235}
236
237}
238}
239}
240