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 = &caps; 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 = &caps; 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