1 // Copyright 2022 The Abseil Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "absl/crc/internal/cpu_detect.h"
16
17 #include <cstdint>
18 #include <string>
19
20 #include "absl/base/config.h"
21
22 #if defined(__aarch64__) && defined(__linux__)
23 #include <asm/hwcap.h>
24 #include <sys/auxv.h>
25 #endif
26
27 #if defined(_WIN32) || defined(_WIN64)
28 #include <intrin.h>
29 #endif
30
31 #if defined(__x86_64__) || defined(_M_X64)
32 #if ABSL_HAVE_BUILTIN(__cpuid)
33 // MSVC-equivalent __cpuid intrinsic declaration for clang-like compilers
34 // for non-Windows build environments.
35 extern void __cpuid(int[4], int);
36 #elif !defined(_WIN32) && !defined(_WIN64)
37 // MSVC defines this function for us.
38 // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
__cpuid(int cpu_info[4],int info_type)39 static void __cpuid(int cpu_info[4], int info_type) {
40 __asm__ volatile("cpuid \n\t"
41 : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
42 "=d"(cpu_info[3])
43 : "a"(info_type), "c"(0));
44 }
45 #endif // !defined(_WIN32) && !defined(_WIN64)
46 #endif // defined(__x86_64__) || defined(_M_X64)
47
48 namespace absl {
49 ABSL_NAMESPACE_BEGIN
50 namespace crc_internal {
51
52 #if defined(__x86_64__) || defined(_M_X64)
53
54 namespace {
55
56 enum class Vendor {
57 kUnknown,
58 kIntel,
59 kAmd,
60 };
61
GetVendor()62 Vendor GetVendor() {
63 // Get the vendor string (issue CPUID with eax = 0).
64 int cpu_info[4];
65 __cpuid(cpu_info, 0);
66
67 std::string vendor;
68 vendor.append(reinterpret_cast<char*>(&cpu_info[1]), 4);
69 vendor.append(reinterpret_cast<char*>(&cpu_info[3]), 4);
70 vendor.append(reinterpret_cast<char*>(&cpu_info[2]), 4);
71 if (vendor == "GenuineIntel") {
72 return Vendor::kIntel;
73 } else if (vendor == "AuthenticAMD") {
74 return Vendor::kAmd;
75 } else {
76 return Vendor::kUnknown;
77 }
78 }
79
GetIntelCpuType()80 CpuType GetIntelCpuType() {
81 // To get general information and extended features we send eax = 1 and
82 // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx.
83 // (See Intel 64 and IA-32 Architectures Software Developer's Manual
84 // Volume 2A: Instruction Set Reference, A-M CPUID).
85 // https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-2a-manual.html
86 // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
87 int cpu_info[4];
88 __cpuid(cpu_info, 1);
89
90 // Response in eax bits as follows:
91 // 0-3 (stepping id)
92 // 4-7 (model number),
93 // 8-11 (family code),
94 // 12-13 (processor type),
95 // 16-19 (extended model)
96 // 20-27 (extended family)
97
98 int family = (cpu_info[0] >> 8) & 0x0f;
99 int model_num = (cpu_info[0] >> 4) & 0x0f;
100 int ext_family = (cpu_info[0] >> 20) & 0xff;
101 int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
102
103 int brand_id = cpu_info[1] & 0xff;
104
105 // Process the extended family and model info if necessary
106 if (family == 0x0f) {
107 family += ext_family;
108 }
109
110 if (family == 0x0f || family == 0x6) {
111 model_num += (ext_model_num << 4);
112 }
113
114 switch (brand_id) {
115 case 0: // no brand ID, so parse CPU family/model
116 switch (family) {
117 case 6: // Most PentiumIII processors are in this category
118 switch (model_num) {
119 case 0x2c: // Westmere: Gulftown
120 return CpuType::kIntelWestmere;
121 case 0x2d: // Sandybridge
122 return CpuType::kIntelSandybridge;
123 case 0x3e: // Ivybridge
124 return CpuType::kIntelIvybridge;
125 case 0x3c: // Haswell (client)
126 case 0x3f: // Haswell
127 return CpuType::kIntelHaswell;
128 case 0x4f: // Broadwell
129 case 0x56: // BroadwellDE
130 return CpuType::kIntelBroadwell;
131 case 0x55: // Skylake Xeon
132 if ((cpu_info[0] & 0x0f) < 5) { // stepping < 5 is skylake
133 return CpuType::kIntelSkylakeXeon;
134 } else { // stepping >= 5 is cascadelake
135 return CpuType::kIntelCascadelakeXeon;
136 }
137 case 0x5e: // Skylake (client)
138 return CpuType::kIntelSkylake;
139 default:
140 return CpuType::kUnknown;
141 }
142 default:
143 return CpuType::kUnknown;
144 }
145 default:
146 return CpuType::kUnknown;
147 }
148 }
149
GetAmdCpuType()150 CpuType GetAmdCpuType() {
151 // To get general information and extended features we send eax = 1 and
152 // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx.
153 // (See Intel 64 and IA-32 Architectures Software Developer's Manual
154 // Volume 2A: Instruction Set Reference, A-M CPUID).
155 // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
156 int cpu_info[4];
157 __cpuid(cpu_info, 1);
158
159 // Response in eax bits as follows:
160 // 0-3 (stepping id)
161 // 4-7 (model number),
162 // 8-11 (family code),
163 // 12-13 (processor type),
164 // 16-19 (extended model)
165 // 20-27 (extended family)
166
167 int family = (cpu_info[0] >> 8) & 0x0f;
168 int model_num = (cpu_info[0] >> 4) & 0x0f;
169 int ext_family = (cpu_info[0] >> 20) & 0xff;
170 int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
171
172 if (family == 0x0f) {
173 family += ext_family;
174 model_num += (ext_model_num << 4);
175 }
176
177 switch (family) {
178 case 0x17:
179 switch (model_num) {
180 case 0x0: // Stepping Ax
181 case 0x1: // Stepping Bx
182 return CpuType::kAmdNaples;
183 case 0x30: // Stepping Ax
184 case 0x31: // Stepping Bx
185 return CpuType::kAmdRome;
186 default:
187 return CpuType::kUnknown;
188 }
189 break;
190 case 0x19:
191 switch (model_num) {
192 case 0x0: // Stepping Ax
193 case 0x1: // Stepping B0
194 return CpuType::kAmdMilan;
195 case 0x10: // Stepping A0
196 case 0x11: // Stepping B0
197 return CpuType::kAmdGenoa;
198 case 0x44: // Stepping A0
199 return CpuType::kAmdRyzenV3000;
200 default:
201 return CpuType::kUnknown;
202 }
203 break;
204 default:
205 return CpuType::kUnknown;
206 }
207 }
208
209 } // namespace
210
GetCpuType()211 CpuType GetCpuType() {
212 switch (GetVendor()) {
213 case Vendor::kIntel:
214 return GetIntelCpuType();
215 case Vendor::kAmd:
216 return GetAmdCpuType();
217 default:
218 return CpuType::kUnknown;
219 }
220 }
221
SupportsArmCRC32PMULL()222 bool SupportsArmCRC32PMULL() { return false; }
223
224 #elif defined(__aarch64__) && defined(__linux__)
225
226 #ifndef HWCAP_CPUID
227 #define HWCAP_CPUID (1 << 11)
228 #endif
229
230 #define ABSL_INTERNAL_AARCH64_ID_REG_READ(id, val) \
231 asm("mrs %0, " #id : "=r"(val))
232
233 CpuType GetCpuType() {
234 // MIDR_EL1 is not visible to EL0, however the access will be emulated by
235 // linux if AT_HWCAP has HWCAP_CPUID set.
236 //
237 // This method will be unreliable on heterogeneous computing systems (ex:
238 // big.LITTLE) since the value of MIDR_EL1 will change based on the calling
239 // thread.
240 uint64_t hwcaps = getauxval(AT_HWCAP);
241 if (hwcaps & HWCAP_CPUID) {
242 uint64_t midr = 0;
243 ABSL_INTERNAL_AARCH64_ID_REG_READ(MIDR_EL1, midr);
244 uint32_t implementer = (midr >> 24) & 0xff;
245 uint32_t part_number = (midr >> 4) & 0xfff;
246 switch (implementer) {
247 case 0x41:
248 switch (part_number) {
249 case 0xd0c: return CpuType::kArmNeoverseN1;
250 case 0xd40: return CpuType::kArmNeoverseV1;
251 case 0xd49: return CpuType::kArmNeoverseN2;
252 case 0xd4f: return CpuType::kArmNeoverseV2;
253 default:
254 return CpuType::kUnknown;
255 }
256 break;
257 case 0xc0:
258 switch (part_number) {
259 case 0xac3: return CpuType::kAmpereSiryn;
260 default:
261 return CpuType::kUnknown;
262 }
263 break;
264 default:
265 return CpuType::kUnknown;
266 }
267 }
268 return CpuType::kUnknown;
269 }
270
271 bool SupportsArmCRC32PMULL() {
272 uint64_t hwcaps = getauxval(AT_HWCAP);
273 return (hwcaps & HWCAP_CRC32) && (hwcaps & HWCAP_PMULL);
274 }
275
276 #else
277
278 CpuType GetCpuType() { return CpuType::kUnknown; }
279
280 bool SupportsArmCRC32PMULL() { return false; }
281
282 #endif
283
284 } // namespace crc_internal
285 ABSL_NAMESPACE_END
286 } // namespace absl
287