1 //! ARM64 CPU feature detection support.
2 //!
3 //! Unfortunately ARM instructions to detect CPU features cannot be called from
4 //! unprivileged userspace code, so this implementation relies on OS-specific
5 //! APIs for feature detection.
6
7 // Evaluate the given `$body` expression any of the supplied target features
8 // are not enabled. Otherwise returns true.
9 #[macro_export]
10 #[doc(hidden)]
11 macro_rules! __unless_target_features {
12 ($($tf:tt),+ => $body:expr ) => {
13 {
14 #[cfg(not(all($(target_feature=$tf,)*)))]
15 $body
16
17 #[cfg(all($(target_feature=$tf,)*))]
18 true
19 }
20 };
21 }
22
23 // Linux runtime detection of target CPU features using `getauxval`.
24 #[cfg(target_os = "linux")]
25 #[macro_export]
26 #[doc(hidden)]
27 macro_rules! __detect_target_features {
28 ($($tf:tt),+) => {{
29 let hwcaps = $crate::aarch64::getauxval_hwcap();
30 $($crate::check!(hwcaps, $tf) & )+ true
31 }};
32 }
33
34 /// Linux helper function for calling `getauxval` to get `AT_HWCAP`.
35 #[cfg(target_os = "linux")]
getauxval_hwcap() -> u6436 pub fn getauxval_hwcap() -> u64 {
37 unsafe { libc::getauxval(libc::AT_HWCAP) }
38 }
39
40 // MacOS runtime detection of target CPU features using `sysctlbyname`.
41 #[cfg(target_os = "macos")]
42 #[macro_export]
43 #[doc(hidden)]
44 macro_rules! __detect_target_features {
45 ($($tf:tt),+) => {{
46 $($crate::check!($tf) & )+ true
47 }};
48 }
49
50 // Linux `expand_check_macro`
51 #[cfg(target_os = "linux")]
52 macro_rules! __expand_check_macro {
53 ($(($name:tt, $hwcap:ident)),* $(,)?) => {
54 #[macro_export]
55 #[doc(hidden)]
56 macro_rules! check {
57 $(
58 ($hwcaps:expr, $name) => {
59 (($hwcaps & $crate::aarch64::hwcaps::$hwcap) != 0)
60 };
61 )*
62 }
63 };
64 }
65
66 // Linux `expand_check_macro`
67 #[cfg(target_os = "linux")]
68 __expand_check_macro! {
69 ("aes", AES), // Enable AES support.
70 ("crypto", CRYPTO), // Enable cryptographic instructions.
71 ("sha2", SHA2), // Enable SHA1 and SHA256 support.
72 ("sha3", SHA3), // Enable SHA512 and SHA3 support.
73 }
74
75 /// Linux hardware capabilities mapped to target features.
76 ///
77 /// Note that LLVM target features are coarser grained than what Linux supports
78 /// and imply more capabilities under each feature. This module attempts to
79 /// provide that mapping accordingly.
80 ///
81 /// See this issue for more info: <https://github.com/RustCrypto/utils/issues/395>
82 #[cfg(target_os = "linux")]
83 pub mod hwcaps {
84 use libc::c_ulong;
85
86 pub const AES: c_ulong = libc::HWCAP_AES | libc::HWCAP_PMULL;
87 pub const CRYPTO: c_ulong = AES | SHA2;
88 pub const SHA2: c_ulong = libc::HWCAP_SHA2;
89 pub const SHA3: c_ulong = libc::HWCAP_SHA3 | libc::HWCAP_SHA512;
90 }
91
92 // macOS `check!` macro.
93 //
94 // NOTE: several of these instructions (e.g. `aes`, `sha2`) can be assumed to
95 // be present on all Apple ARM64 hardware.
96 //
97 // Newer CPU instructions now have nodes within sysctl's `hw.optional`
98 // namespace, however the ones that do not can safely be assumed to be
99 // present on all Apple ARM64 devices, now and for the foreseeable future.
100 //
101 // See discussion on this issue for more information:
102 // <https://github.com/RustCrypto/utils/issues/378>
103 #[cfg(target_os = "macos")]
104 #[macro_export]
105 #[doc(hidden)]
106 macro_rules! check {
107 ("aes") => {
108 true
109 };
110 ("crypto") => {
111 true
112 };
113 ("sha2") => {
114 true
115 };
116 ("sha3") => {
117 unsafe {
118 // `sha3` target feature implies SHA-512 as well
119 $crate::aarch64::sysctlbyname(b"hw.optional.armv8_2_sha512\0")
120 && $crate::aarch64::sysctlbyname(b"hw.optional.armv8_2_sha3\0")
121 }
122 };
123 }
124
125 /// macOS helper function for calling `sysctlbyname`.
126 #[cfg(target_os = "macos")]
sysctlbyname(name: &[u8]) -> bool127 pub unsafe fn sysctlbyname(name: &[u8]) -> bool {
128 assert_eq!(
129 name.last().cloned(),
130 Some(0),
131 "name is not NUL terminated: {:?}",
132 name
133 );
134
135 let mut value: u32 = 0;
136 let mut size = core::mem::size_of::<u32>();
137
138 let rc = libc::sysctlbyname(
139 name.as_ptr() as *const i8,
140 &mut value as *mut _ as *mut libc::c_void,
141 &mut size,
142 core::ptr::null_mut(),
143 0,
144 );
145
146 assert_eq!(size, 4, "unexpected sysctlbyname(3) result size");
147 assert_eq!(rc, 0, "sysctlbyname returned error code: {}", rc);
148 value != 0
149 }
150
151 // iOS `check!` macro.
152 //
153 // Unfortunately iOS does not provide access to the `sysctl(3)` API which means
154 // we can only return static values for CPU features which can be assumed to
155 // be present on all Apple ARM64 hardware.
156 //
157 // See discussion on this issue for more information:
158 // <https://github.com/RustCrypto/utils/issues/378>
159 #[cfg(target_os = "ios")]
160 #[macro_export]
161 #[doc(hidden)]
162 macro_rules! check {
163 ("aes") => {
164 true
165 };
166 ("crypto") => {
167 true
168 };
169 ("sha2") => {
170 true
171 };
172 ("sha3") => {
173 false
174 };
175 }
176
177 // On other targets, runtime CPU feature detection is unavailable
178 #[cfg(not(any(target_os = "ios", target_os = "linux", target_os = "macos")))]
179 #[macro_export]
180 #[doc(hidden)]
181 macro_rules! __detect_target_features {
182 ($($tf:tt),+) => {
183 false
184 };
185 }
186