cpufeatures/
aarch64.rs

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)]
11macro_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(any(target_os = "linux", target_os = "android"))]
25#[macro_export]
26#[doc(hidden)]
27macro_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(any(target_os = "linux", target_os = "android"))]
36pub fn getauxval_hwcap() -> u64 {
37    unsafe { libc::getauxval(libc::AT_HWCAP) }
38}
39
40// Apple platform's runtime detection of target CPU features using `sysctlbyname`.
41#[cfg(target_vendor = "apple")]
42#[macro_export]
43#[doc(hidden)]
44macro_rules! __detect_target_features {
45    ($($tf:tt),+) => {{
46        $($crate::check!($tf) & )+ true
47    }};
48}
49
50// Linux `expand_check_macro`
51#[cfg(any(target_os = "linux", target_os = "android"))]
52macro_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(any(target_os = "linux", target_os = "android"))]
68__expand_check_macro! {
69    ("aes",    AES),    // Enable AES support.
70    ("dit",    DIT),    // Enable DIT support.
71    ("sha2",   SHA2),   // Enable SHA1 and SHA256 support.
72    ("sha3",   SHA3),   // Enable SHA512 and SHA3 support.
73    ("sm4",    SM4),    // Enable SM3 and SM4 support.
74}
75
76/// Linux hardware capabilities mapped to target features.
77///
78/// Note that LLVM target features are coarser grained than what Linux supports
79/// and imply more capabilities under each feature. This module attempts to
80/// provide that mapping accordingly.
81///
82/// See this issue for more info: <https://github.com/RustCrypto/utils/issues/395>
83#[cfg(any(target_os = "linux", target_os = "android"))]
84pub mod hwcaps {
85    use libc::c_ulong;
86
87    pub const AES: c_ulong = libc::HWCAP_AES | libc::HWCAP_PMULL;
88    pub const DIT: c_ulong = libc::HWCAP_DIT;
89    pub const SHA2: c_ulong = libc::HWCAP_SHA2;
90    pub const SHA3: c_ulong = libc::HWCAP_SHA3 | libc::HWCAP_SHA512;
91    pub const SM4: c_ulong = libc::HWCAP_SM3 | libc::HWCAP_SM4;
92}
93
94// Apple OS (macOS, iOS, watchOS, and tvOS) `check!` macro.
95//
96// NOTE: several of these instructions (e.g. `aes`, `sha2`) can be assumed to
97// be present on all Apple ARM64 hardware.
98//
99// Newer CPU instructions now have nodes within sysctl's `hw.optional`
100// namespace, however the ones that do not can safely be assumed to be
101// present on all Apple ARM64 devices, now and for the foreseeable future.
102//
103// See discussion on this issue for more information:
104// <https://github.com/RustCrypto/utils/issues/378>
105#[cfg(target_vendor = "apple")]
106#[macro_export]
107#[doc(hidden)]
108macro_rules! check {
109    ("aes") => {
110        true
111    };
112    ("dit") => {
113        // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Enable-DIT-for-constant-time-cryptographic-operations
114        unsafe {
115            $crate::aarch64::sysctlbyname(b"hw.optional.arm.FEAT_DIT\0")
116        }
117    };
118    ("sha2") => {
119        true
120    };
121    ("sha3") => {
122        unsafe {
123            // `sha3` target feature implies SHA-512 as well
124            $crate::aarch64::sysctlbyname(b"hw.optional.armv8_2_sha512\0")
125                && $crate::aarch64::sysctlbyname(b"hw.optional.armv8_2_sha3\0")
126        }
127    };
128    ("sm4") => {
129        false
130    };
131}
132
133/// Apple helper function for calling `sysctlbyname`.
134#[cfg(target_vendor = "apple")]
135pub unsafe fn sysctlbyname(name: &[u8]) -> bool {
136    assert_eq!(
137        name.last().cloned(),
138        Some(0),
139        "name is not NUL terminated: {:?}",
140        name
141    );
142
143    let mut value: u32 = 0;
144    let mut size = core::mem::size_of::<u32>();
145
146    let rc = libc::sysctlbyname(
147        name.as_ptr() as *const i8,
148        &mut value as *mut _ as *mut libc::c_void,
149        &mut size,
150        core::ptr::null_mut(),
151        0,
152    );
153
154    assert_eq!(size, 4, "unexpected sysctlbyname(3) result size");
155    assert_eq!(rc, 0, "sysctlbyname returned error code: {}", rc);
156    value != 0
157}
158
159// On other targets, runtime CPU feature detection is unavailable
160#[cfg(not(any(target_vendor = "apple", target_os = "linux", target_os = "android",)))]
161#[macro_export]
162#[doc(hidden)]
163macro_rules! __detect_target_features {
164    ($($tf:tt),+) => {
165        false
166    };
167}