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

source code of solid/src/solid/devices/backends/shared/cpufeatures.cpp