1 // Copyright 2016 Brian Smith.
2 //
3 // Permission to use, copy, modify, and/or distribute this software for any
4 // purpose with or without fee is hereby granted, provided that the above
5 // copyright notice and this permission notice appear in all copies.
6 //
7 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10 // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 // OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15 /// A witness indicating that CPU features have been detected and cached.
16 ///
17 /// TODO: Eventually all feature detection logic should be done through
18 /// functions that accept a `Features` parameter, to guarantee that nothing
19 /// tries to read the cached values before they are written.
20 ///
21 /// This is a zero-sized type so that it can be "stored" wherever convenient.
22 #[derive(Copy, Clone)]
23 pub(crate) struct Features(());
24
25 #[inline(always)]
features() -> Features26 pub(crate) fn features() -> Features {
27 // We don't do runtime feature detection on iOS. instead some features are
28 // assumed to be present; see `arm::Feature`.
29 #[cfg(all(
30 any(
31 target_arch = "aarch64",
32 target_arch = "arm",
33 target_arch = "x86",
34 target_arch = "x86_64"
35 ),
36 not(target_os = "ios")
37 ))]
38 {
39 static INIT: spin::Once<()> = spin::Once::new();
40 let () = INIT.call_once(|| {
41 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
42 {
43 extern "C" {
44 fn GFp_cpuid_setup();
45 }
46 unsafe {
47 GFp_cpuid_setup();
48 }
49 }
50
51 #[cfg(all(
52 any(target_os = "android", target_os = "linux"),
53 any(target_arch = "aarch64", target_arch = "arm")
54 ))]
55 {
56 arm::linux_setup();
57 }
58
59 #[cfg(all(target_os = "fuchsia", any(target_arch = "aarch64")))]
60 {
61 arm::fuchsia_setup();
62 }
63
64 #[cfg(all(target_os = "freebsd", any(target_arch = "aarch64", target_arch = "arm")))]
65 {
66 arm::freebsd_setup();
67 }
68 });
69 }
70
71 Features(())
72 }
73
74 pub(crate) mod arm {
75 #[cfg(all(
76 any(target_os = "android", target_os = "linux"),
77 any(target_arch = "aarch64", target_arch = "arm")
78 ))]
linux_setup()79 pub fn linux_setup() {
80 use libc::c_ulong;
81
82 // XXX: The `libc` crate doesn't provide `libc::getauxval` consistently
83 // across all Android/Linux targets, e.g. musl.
84 extern "C" {
85 fn getauxval(type_: c_ulong) -> c_ulong;
86 }
87
88 const AT_HWCAP: c_ulong = 16;
89
90 #[cfg(target_arch = "aarch64")]
91 const HWCAP_NEON: c_ulong = 1 << 1;
92
93 #[cfg(target_arch = "arm")]
94 const HWCAP_NEON: c_ulong = 1 << 12;
95
96 let caps = unsafe { getauxval(AT_HWCAP) };
97
98 // We assume NEON is available on AARCH64 because it is a required
99 // feature.
100 #[cfg(target_arch = "aarch64")]
101 debug_assert!(caps & HWCAP_NEON == HWCAP_NEON);
102
103 // OpenSSL and BoringSSL don't enable any other features if NEON isn't
104 // available.
105 if caps & HWCAP_NEON == HWCAP_NEON {
106 let mut features = NEON.mask;
107
108 #[cfg(target_arch = "aarch64")]
109 const OFFSET: c_ulong = 3;
110
111 #[cfg(target_arch = "arm")]
112 const OFFSET: c_ulong = 0;
113
114 #[cfg(target_arch = "arm")]
115 let caps = {
116 const AT_HWCAP2: c_ulong = 26;
117 unsafe { getauxval(AT_HWCAP2) }
118 };
119
120 const HWCAP_AES: c_ulong = 1 << 0 + OFFSET;
121 const HWCAP_PMULL: c_ulong = 1 << 1 + OFFSET;
122 const HWCAP_SHA2: c_ulong = 1 << 3 + OFFSET;
123
124 if caps & HWCAP_AES == HWCAP_AES {
125 features |= AES.mask;
126 }
127 if caps & HWCAP_PMULL == HWCAP_PMULL {
128 features |= PMULL.mask;
129 }
130 if caps & HWCAP_SHA2 == HWCAP_SHA2 {
131 features |= 1 << 4;
132 }
133
134 unsafe { GFp_armcap_P = features };
135 }
136 }
137
138 #[cfg(all(target_os = "fuchsia", any(target_arch = "aarch64")))]
fuchsia_setup()139 pub fn fuchsia_setup() {
140 type zx_status_t = i32;
141
142 #[link(name = "zircon")]
143 extern "C" {
144 fn zx_system_get_features(kind: u32, features: *mut u32) -> zx_status_t;
145 }
146
147 const ZX_OK: i32 = 0;
148 const ZX_FEATURE_KIND_CPU: u32 = 0;
149 const ZX_ARM64_FEATURE_ISA_ASIMD: u32 = 1 << 2;
150 const ZX_ARM64_FEATURE_ISA_AES: u32 = 1 << 3;
151 const ZX_ARM64_FEATURE_ISA_PMULL: u32 = 1 << 4;
152 const ZX_ARM64_FEATURE_ISA_SHA2: u32 = 1 << 6;
153
154 let mut caps = 0;
155 let rc = unsafe { zx_system_get_features(ZX_FEATURE_KIND_CPU, &mut caps) };
156
157 // OpenSSL and BoringSSL don't enable any other features if NEON isn't
158 // available.
159 if rc == ZX_OK && (caps & ZX_ARM64_FEATURE_ISA_ASIMD == ZX_ARM64_FEATURE_ISA_ASIMD) {
160 let mut features = NEON.mask;
161
162 if caps & ZX_ARM64_FEATURE_ISA_AES == ZX_ARM64_FEATURE_ISA_AES {
163 features |= AES.mask;
164 }
165 if caps & ZX_ARM64_FEATURE_ISA_PMULL == ZX_ARM64_FEATURE_ISA_PMULL {
166 features |= PMULL.mask;
167 }
168 if caps & ZX_ARM64_FEATURE_ISA_SHA2 == ZX_ARM64_FEATURE_ISA_SHA2 {
169 features |= 1 << 4;
170 }
171
172 unsafe { GFp_armcap_P = features };
173 }
174 }
175
176 #[cfg(all(target_os = "freebsd", any(target_arch = "aarch64", target_arch = "arm")))]
freebsd_setup()177 pub fn freebsd_setup() {
178 extern crate std;
179 use libc::{c_int, c_ulong, c_void};
180
181 extern "C" {
182 fn elf_aux_info(aux: c_int, buf: *mut c_void, buflen: c_int) -> c_int;
183 }
184
185 const AT_HWCAP: c_int = 25;
186
187 #[cfg(target_arch = "aarch64")]
188 const HWCAP_NEON: c_ulong = 1 << 1;
189
190 #[cfg(target_arch = "arm")]
191 const HWCAP_NEON: c_ulong = 1 << 12;
192
193 let caps: c_ulong = 0;
194 let buffer : *mut c_void = { let t: *const c_ulong = ∩︀ t} as *mut c_void;
195
196 unsafe {
197 let _ret = elf_aux_info(
198 AT_HWCAP,
199 buffer,
200 std::mem::size_of_val(&caps) as i32
201 );
202 }
203
204 // We assume NEON is available on AARCH64 because it is a required
205 // feature.
206 #[cfg(target_arch = "aarch64")]
207 debug_assert!(caps & HWCAP_NEON == HWCAP_NEON);
208
209 // OpenSSL and BoringSSL don't enable any other features if NEON isn't
210 // available.
211 if caps & HWCAP_NEON == HWCAP_NEON {
212 let mut features = NEON.mask;
213
214 #[cfg(target_arch = "aarch64")]
215 const OFFSET: c_ulong = 3;
216
217 #[cfg(target_arch = "arm")]
218 const OFFSET: c_ulong = 0;
219 #[cfg(target_arch = "arm")]
220 const AT_HWCAP2: c_int = 26;
221 #[cfg(target_arch = "arm")]
222 let caps: c_ulong = 0;
223 #[cfg(target_arch = "arm")]
224 let buffer : *mut c_void = { let t: *const c_ulong = ∩︀ t} as *mut c_void;
225
226 #[cfg(target_arch = "arm")]
227 unsafe {
228 let _ret = elf_aux_info(
229 AT_HWCAP2,
230 buffer,
231 std::mem::size_of_val(&caps) as i32
232 );
233 };
234
235 const HWCAP_AES: c_ulong = 1 << 0 + OFFSET;
236 const HWCAP_PMULL: c_ulong = 1 << 1 + OFFSET;
237 const HWCAP_SHA2: c_ulong = 1 << 3 + OFFSET;
238
239 if caps & HWCAP_AES == HWCAP_AES {
240 features |= AES.mask;
241 }
242 if caps & HWCAP_PMULL == HWCAP_PMULL {
243 features |= PMULL.mask;
244 }
245 if caps & HWCAP_SHA2 == HWCAP_SHA2 {
246 features |= 1 << 4;
247 }
248
249 unsafe { GFp_armcap_P = features };
250 }
251 }
252
253 #[cfg(not(target_arch = "wasm32"))]
254 pub(crate) struct Feature {
255 #[cfg_attr(
256 any(
257 target_os = "ios",
258 not(any(target_arch = "arm", target_arch = "aarch64"))
259 ),
260 allow(dead_code)
261 )]
262 mask: u32,
263
264 #[cfg_attr(not(target_os = "ios"), allow(dead_code))]
265 ios: bool,
266 }
267
268 #[cfg(not(target_arch = "wasm32"))]
269 impl Feature {
270 #[inline(always)]
available(&self, _: super::Features) -> bool271 pub fn available(&self, _: super::Features) -> bool {
272 #[cfg(all(target_os = "ios", any(target_arch = "arm", target_arch = "aarch64")))]
273 {
274 return self.ios;
275 }
276
277 #[cfg(all(
278 any(target_os = "android", target_os = "linux", target_os = "fuchsia", target_os = "freebsd"),
279 any(target_arch = "arm", target_arch = "aarch64")
280 ))]
281 {
282 return self.mask == self.mask & unsafe { GFp_armcap_P };
283 }
284
285 #[cfg(not(any(target_arch = "arm", target_arch = "aarch64")))]
286 {
287 return false;
288 }
289 }
290 }
291
292 // Keep in sync with `ARMV7_NEON`.
293 #[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
294 pub(crate) const NEON: Feature = Feature {
295 mask: 1 << 0,
296 ios: true,
297 };
298
299 // Keep in sync with `ARMV8_AES`.
300 #[cfg(any(
301 target_arch = "aarch64",
302 target_arch = "arm",
303 target_arch = "x86",
304 target_arch = "x86_64"
305 ))]
306 pub(crate) const AES: Feature = Feature {
307 mask: 1 << 2,
308 ios: true,
309 };
310
311 // Keep in sync with `ARMV8_PMULL`.
312 #[cfg(any(
313 target_arch = "aarch64",
314 target_arch = "arm",
315 target_arch = "x86",
316 target_arch = "x86_64"
317 ))]
318 pub(crate) const PMULL: Feature = Feature {
319 mask: 1 << 5,
320 ios: true,
321 };
322
323 #[cfg(all(
324 any(target_os = "android", target_os = "linux", target_os = "fuchsia", target_os = "freebsd"),
325 any(target_arch = "arm", target_arch = "aarch64")
326 ))]
327 extern "C" {
328 static mut GFp_armcap_P: u32;
329 }
330 }
331
332 #[cfg_attr(
333 not(any(target_arch = "x86", target_arch = "x86_64")),
334 allow(dead_code)
335 )]
336 pub(crate) mod intel {
337 pub(crate) struct Feature {
338 word: usize,
339 mask: u32,
340 }
341
342 impl Feature {
343 #[inline(always)]
available(&self, _: super::Features) -> bool344 pub fn available(&self, _: super::Features) -> bool {
345 #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
346 {
347 extern "C" {
348 static mut GFp_ia32cap_P: [u32; 4];
349 }
350 return self.mask == self.mask & unsafe { GFp_ia32cap_P[self.word] };
351 }
352
353 #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
354 {
355 return false;
356 }
357 }
358 }
359
360 pub(crate) const FXSR: Feature = Feature {
361 word: 0,
362 mask: 1 << 24,
363 };
364
365 pub(crate) const PCLMULQDQ: Feature = Feature {
366 word: 1,
367 mask: 1 << 1,
368 };
369
370 pub(crate) const SSSE3: Feature = Feature {
371 word: 1,
372 mask: 1 << 9,
373 };
374
375 #[cfg(target_arch = "x86_64")]
376 pub(crate) const MOVBE: Feature = Feature {
377 word: 1,
378 mask: 1 << 22,
379 };
380
381 pub(crate) const AES: Feature = Feature {
382 word: 1,
383 mask: 1 << 25,
384 };
385
386 #[cfg(target_arch = "x86_64")]
387 pub(crate) const AVX: Feature = Feature {
388 word: 1,
389 mask: 1 << 28,
390 };
391
392 #[cfg(all(target_arch = "x86_64", test))]
393 mod x86_64_tests {
394 use super::*;
395
396 #[test]
test_avx_movbe_mask()397 fn test_avx_movbe_mask() {
398 // This is the OpenSSL style of testing these bits.
399 assert_eq!((AVX.mask | MOVBE.mask) >> 22, 0x41);
400 }
401 }
402 }
403