#![cfg_attr(
not(any(target_arch = "aarch64", target_arch = "arm")),
allow(dead_code)
)]
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
mod abi_assumptions {
#[cfg(target_arch = "aarch64")]
const _ASSUMED_POINTER_SIZE: usize = 8;
#[cfg(target_arch = "arm")]
const _ASSUMED_POINTER_SIZE: usize = 4;
const _ASSUMED_USIZE_SIZE: () = assert!(core::mem::size_of::<usize>() == _ASSUMED_POINTER_SIZE);
const _ASSUMED_REF_SIZE: () =
assert!(core::mem::size_of::<&'static u8>() == _ASSUMED_POINTER_SIZE);
const _ASSUMED_ENDIANNESS: () = assert!(cfg!(target_endian = "little"));
}
#[cfg(all(
any(target_os = "android", target_os = "linux"),
any(target_arch = "aarch64", target_arch = "arm"),
not(target_env = "uclibc")
))]
fn detect_features() -> u32 {
use libc::c_ulong;
extern "C" {
fn getauxval(type_: c_ulong) -> c_ulong;
}
const AT_HWCAP: c_ulong = 16;
#[cfg(target_arch = "aarch64")]
const HWCAP_NEON: c_ulong = 1 << 1;
#[cfg(target_arch = "arm")]
const HWCAP_NEON: c_ulong = 1 << 12;
let caps = unsafe { getauxval(AT_HWCAP) };
#[cfg(target_arch = "aarch64")]
debug_assert!(caps & HWCAP_NEON == HWCAP_NEON);
let mut features = 0;
if caps & HWCAP_NEON == HWCAP_NEON {
features = NEON.mask;
#[cfg(target_arch = "aarch64")]
const OFFSET: c_ulong = 3;
#[cfg(target_arch = "arm")]
const OFFSET: c_ulong = 0;
#[cfg(target_arch = "arm")]
let caps = {
const AT_HWCAP2: c_ulong = 26;
unsafe { getauxval(AT_HWCAP2) }
};
const HWCAP_AES: c_ulong = 1 << 0 + OFFSET;
const HWCAP_PMULL: c_ulong = 1 << 1 + OFFSET;
const HWCAP_SHA2: c_ulong = 1 << 3 + OFFSET;
if caps & HWCAP_AES == HWCAP_AES {
features |= AES.mask;
}
if caps & HWCAP_PMULL == HWCAP_PMULL {
features |= PMULL.mask;
}
if caps & HWCAP_SHA2 == HWCAP_SHA2 {
features |= SHA256.mask;
}
}
features
}
#[cfg(all(target_os = "fuchsia", target_arch = "aarch64"))]
fn detect_features() -> u32 {
type zx_status_t = i32;
#[link(name = "zircon")]
extern "C" {
fn zx_system_get_features(kind: u32, features: *mut u32) -> zx_status_t;
}
const ZX_OK: i32 = 0;
const ZX_FEATURE_KIND_CPU: u32 = 0;
const ZX_ARM64_FEATURE_ISA_ASIMD: u32 = 1 << 2;
const ZX_ARM64_FEATURE_ISA_AES: u32 = 1 << 3;
const ZX_ARM64_FEATURE_ISA_PMULL: u32 = 1 << 4;
const ZX_ARM64_FEATURE_ISA_SHA2: u32 = 1 << 6;
let mut caps = 0;
let rc = unsafe { zx_system_get_features(ZX_FEATURE_KIND_CPU, &mut caps) };
let mut features = 0;
if rc == ZX_OK && (caps & ZX_ARM64_FEATURE_ISA_ASIMD == ZX_ARM64_FEATURE_ISA_ASIMD) {
features = NEON.mask;
if caps & ZX_ARM64_FEATURE_ISA_AES == ZX_ARM64_FEATURE_ISA_AES {
features |= AES.mask;
}
if caps & ZX_ARM64_FEATURE_ISA_PMULL == ZX_ARM64_FEATURE_ISA_PMULL {
features |= PMULL.mask;
}
if caps & ZX_ARM64_FEATURE_ISA_SHA2 == ZX_ARM64_FEATURE_ISA_SHA2 {
features |= 1 << 4;
}
}
features
}
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
fn detect_features() -> u32 {
const _ASSERT_NEON_DETECTED: () = assert!((ARMCAP_STATIC & NEON.mask) == NEON.mask);
let mut features = ARMCAP_STATIC;
let result = unsafe {
windows_sys::Win32::System::Threading::IsProcessorFeaturePresent(
windows_sys::Win32::System::Threading::PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE,
)
};
if result != 0 {
features |= AES.mask;
features |= PMULL.mask;
features |= SHA256.mask;
}
features
}
#[cfg(all(
any(target_arch = "aarch64", target_arch = "arm"),
not(any(
target_os = "android",
target_os = "fuchsia",
all(target_os = "linux", not(target_env = "uclibc")),
target_os = "windows"
))
))]
fn detect_features() -> u32 {
0
}
macro_rules! features {
{
$(
$target_feature_name:expr => $name:ident {
mask: $mask:expr,
}
),+
, } => {
$(
#[allow(dead_code)]
pub(crate) const $name: Feature = Feature {
mask: $mask,
};
)+
const ARMCAP_STATIC: u32 = 0
$(
| (
if cfg!(all(any(target_arch = "aarch64", target_arch = "arm"),
target_feature = $target_feature_name)) {
$name.mask
} else {
0
}
)
)+;
#[cfg(all(test, any(target_arch = "arm", target_arch = "aarch64")))]
const ALL_FEATURES: [Feature; 4] = [
$(
$name
),+
];
}
}
pub(crate) struct Feature {
mask: u32,
}
impl Feature {
#[inline(always)]
pub fn available(&self, _: super::Features) -> bool {
if self.mask == self.mask & ARMCAP_STATIC {
return true;
}
#[cfg(all(
any(
target_os = "android",
target_os = "fuchsia",
all(target_os = "linux", not(target_env = "uclibc")),
target_os = "windows"
),
any(target_arch = "arm", target_arch = "aarch64")
))]
{
if self.mask == self.mask & unsafe { OPENSSL_armcap_P } {
return true;
}
}
false
}
}
features! {
"neon" => NEON {
mask: 1 << 0,
},
"aes" => AES {
mask: 1 << 2,
},
"sha2" => SHA256 {
mask: 1 << 4,
},
"aes" => PMULL {
mask: 1 << 5,
},
}
#[cfg(any(target_arch = "aarch64", target_arch = "arm"))]
pub unsafe fn init_global_shared_with_assembly() {
let detected = detect_features();
let filtered = (if cfg!(feature = "unstable-testing-arm-no-hw") {
AES.mask | SHA256.mask | PMULL.mask
} else {
0
}) | (if cfg!(feature = "unstable-testing-arm-no-neon") {
NEON.mask
} else {
0
});
let detected = detected & !filtered;
OPENSSL_armcap_P = ARMCAP_STATIC | detected;
}
#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
prefixed_extern! {
static mut OPENSSL_armcap_P: u32;
}
#[allow(clippy::assertions_on_constants)]
const _AARCH64_HAS_NEON: () =
assert!(((ARMCAP_STATIC & NEON.mask) == NEON.mask) || !cfg!(target_arch = "aarch64"));
#[allow(clippy::assertions_on_constants)]
const _AARCH64_APPLE_FEATURES: u32 = NEON.mask | AES.mask | SHA256.mask | PMULL.mask;
#[allow(clippy::assertions_on_constants)]
const _AARCH64_APPLE_TARGETS_EXPECTED_FEATURES: () = assert!(
((ARMCAP_STATIC & _AARCH64_APPLE_FEATURES) == _AARCH64_APPLE_FEATURES)
|| !cfg!(all(target_arch = "aarch64", target_vendor = "apple"))
);
#[cfg(all(test, any(target_arch = "arm", target_arch = "aarch64")))]
mod tests {
use super::*;
#[test]
fn test_mask_abi() {
assert_eq!(NEON.mask, 1);
assert_eq!(AES.mask, 4);
assert_eq!(SHA256.mask, 16);
assert_eq!(PMULL.mask, 32);
}
#[test]
fn test_armcap_static_is_subset_of_armcap_dynamic() {
let cpu = crate::cpu::features();
let armcap_dynamic = unsafe { OPENSSL_armcap_P };
assert_eq!(armcap_dynamic & ARMCAP_STATIC, ARMCAP_STATIC);
ALL_FEATURES.iter().for_each(|feature| {
if (ARMCAP_STATIC & feature.mask) != 0 {
assert!(feature.available(cpu));
}
})
}
}