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 namespace absl {
32 ABSL_NAMESPACE_BEGIN
33 namespace crc_internal {
34
35 #if defined(__x86_64__) || defined(_M_X64)
36
37 namespace {
38
39 #if !defined(_WIN32) && !defined(_WIN64)
40 // MSVC defines this function for us.
41 // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
__cpuid(int cpu_info[4],int info_type)42 static void __cpuid(int cpu_info[4], int info_type) {
43 __asm__ volatile("cpuid \n\t"
44 : "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]),
45 "=d"(cpu_info[3])
46 : "a"(info_type), "c"(0));
47 }
48 #endif // !defined(_WIN32) && !defined(_WIN64)
49
50 enum class Vendor {
51 kUnknown,
52 kIntel,
53 kAmd,
54 };
55
GetVendor()56 Vendor GetVendor() {
57 // Get the vendor string (issue CPUID with eax = 0).
58 int cpu_info[4];
59 __cpuid(cpu_info, 0);
60
61 std::string vendor;
62 vendor.append(reinterpret_cast<char*>(&cpu_info[1]), 4);
63 vendor.append(reinterpret_cast<char*>(&cpu_info[3]), 4);
64 vendor.append(reinterpret_cast<char*>(&cpu_info[2]), 4);
65 if (vendor == "GenuineIntel") {
66 return Vendor::kIntel;
67 } else if (vendor == "AuthenticAMD") {
68 return Vendor::kAmd;
69 } else {
70 return Vendor::kUnknown;
71 }
72 }
73
GetIntelCpuType()74 CpuType GetIntelCpuType() {
75 // To get general information and extended features we send eax = 1 and
76 // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx.
77 // (See Intel 64 and IA-32 Architectures Software Developer's Manual
78 // Volume 2A: Instruction Set Reference, A-M CPUID).
79 // https://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-vol-2a-manual.html
80 // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
81 int cpu_info[4];
82 __cpuid(cpu_info, 1);
83
84 // Response in eax bits as follows:
85 // 0-3 (stepping id)
86 // 4-7 (model number),
87 // 8-11 (family code),
88 // 12-13 (processor type),
89 // 16-19 (extended model)
90 // 20-27 (extended family)
91
92 int family = (cpu_info[0] >> 8) & 0x0f;
93 int model_num = (cpu_info[0] >> 4) & 0x0f;
94 int ext_family = (cpu_info[0] >> 20) & 0xff;
95 int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
96
97 int brand_id = cpu_info[1] & 0xff;
98
99 // Process the extended family and model info if necessary
100 if (family == 0x0f) {
101 family += ext_family;
102 }
103
104 if (family == 0x0f || family == 0x6) {
105 model_num += (ext_model_num << 4);
106 }
107
108 switch (brand_id) {
109 case 0: // no brand ID, so parse CPU family/model
110 switch (family) {
111 case 6: // Most PentiumIII processors are in this category
112 switch (model_num) {
113 case 0x2c: // Westmere: Gulftown
114 return CpuType::kIntelWestmere;
115 case 0x2d: // Sandybridge
116 return CpuType::kIntelSandybridge;
117 case 0x3e: // Ivybridge
118 return CpuType::kIntelIvybridge;
119 case 0x3c: // Haswell (client)
120 case 0x3f: // Haswell
121 return CpuType::kIntelHaswell;
122 case 0x4f: // Broadwell
123 case 0x56: // BroadwellDE
124 return CpuType::kIntelBroadwell;
125 case 0x55: // Skylake Xeon
126 if ((cpu_info[0] & 0x0f) < 5) { // stepping < 5 is skylake
127 return CpuType::kIntelSkylakeXeon;
128 } else { // stepping >= 5 is cascadelake
129 return CpuType::kIntelCascadelakeXeon;
130 }
131 case 0x5e: // Skylake (client)
132 return CpuType::kIntelSkylake;
133 default:
134 return CpuType::kUnknown;
135 }
136 default:
137 return CpuType::kUnknown;
138 }
139 default:
140 return CpuType::kUnknown;
141 }
142 }
143
GetAmdCpuType()144 CpuType GetAmdCpuType() {
145 // To get general information and extended features we send eax = 1 and
146 // ecx = 0 to cpuid. The response is returned in eax, ebx, ecx and edx.
147 // (See Intel 64 and IA-32 Architectures Software Developer's Manual
148 // Volume 2A: Instruction Set Reference, A-M CPUID).
149 // https://learn.microsoft.com/en-us/cpp/intrinsics/cpuid-cpuidex
150 int cpu_info[4];
151 __cpuid(cpu_info, 1);
152
153 // Response in eax bits as follows:
154 // 0-3 (stepping id)
155 // 4-7 (model number),
156 // 8-11 (family code),
157 // 12-13 (processor type),
158 // 16-19 (extended model)
159 // 20-27 (extended family)
160
161 int family = (cpu_info[0] >> 8) & 0x0f;
162 int model_num = (cpu_info[0] >> 4) & 0x0f;
163 int ext_family = (cpu_info[0] >> 20) & 0xff;
164 int ext_model_num = (cpu_info[0] >> 16) & 0x0f;
165
166 if (family == 0x0f) {
167 family += ext_family;
168 model_num += (ext_model_num << 4);
169 }
170
171 switch (family) {
172 case 0x17:
173 switch (model_num) {
174 case 0x0: // Stepping Ax
175 case 0x1: // Stepping Bx
176 return CpuType::kAmdNaples;
177 case 0x30: // Stepping Ax
178 case 0x31: // Stepping Bx
179 return CpuType::kAmdRome;
180 default:
181 return CpuType::kUnknown;
182 }
183 break;
184 case 0x19:
185 switch (model_num) {
186 case 0x1: // Stepping B0
187 return CpuType::kAmdMilan;
188 default:
189 return CpuType::kUnknown;
190 }
191 break;
192 default:
193 return CpuType::kUnknown;
194 }
195 }
196
197 } // namespace
198
GetCpuType()199 CpuType GetCpuType() {
200 switch (GetVendor()) {
201 case Vendor::kIntel:
202 return GetIntelCpuType();
203 case Vendor::kAmd:
204 return GetAmdCpuType();
205 default:
206 return CpuType::kUnknown;
207 }
208 }
209
SupportsArmCRC32PMULL()210 bool SupportsArmCRC32PMULL() { return false; }
211
212 #elif defined(__aarch64__) && defined(__linux__)
213
214 #ifndef HWCAP_CPUID
215 #define HWCAP_CPUID (1 << 11)
216 #endif
217
218 #define ABSL_INTERNAL_AARCH64_ID_REG_READ(id, val) \
219 asm("mrs %0, " #id : "=r"(val))
220
221 CpuType GetCpuType() {
222 // MIDR_EL1 is not visible to EL0, however the access will be emulated by
223 // linux if AT_HWCAP has HWCAP_CPUID set.
224 //
225 // This method will be unreliable on heterogeneous computing systems (ex:
226 // big.LITTLE) since the value of MIDR_EL1 will change based on the calling
227 // thread.
228 uint64_t hwcaps = getauxval(AT_HWCAP);
229 if (hwcaps & HWCAP_CPUID) {
230 uint64_t midr = 0;
231 ABSL_INTERNAL_AARCH64_ID_REG_READ(MIDR_EL1, midr);
232 uint32_t implementer = (midr >> 24) & 0xff;
233 uint32_t part_number = (midr >> 4) & 0xfff;
234 if (implementer == 0x41 && part_number == 0xd0c) {
235 return CpuType::kArmNeoverseN1;
236 }
237 }
238 return CpuType::kUnknown;
239 }
240
241 bool SupportsArmCRC32PMULL() {
242 uint64_t hwcaps = getauxval(AT_HWCAP);
243 return (hwcaps & HWCAP_CRC32) && (hwcaps & HWCAP_PMULL);
244 }
245
246 #else
247
248 CpuType GetCpuType() { return CpuType::kUnknown; }
249
250 bool SupportsArmCRC32PMULL() { return false; }
251
252 #endif
253
254 } // namespace crc_internal
255 ABSL_NAMESPACE_END
256 } // namespace absl
257