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