use core::sync::atomic::{AtomicU8, Ordering};
pub(crate) fn missing_cpu_feature() -> Option<&'static str> {
None
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct HaveCryptoExt(());
impl HaveCryptoExt {
pub(crate) fn check() -> Option<Self> {
(features() & CRYPTO_EXT != 0).then_some(Self(()))
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct HaveNeon(());
impl HaveNeon {
pub(crate) fn check() -> Option<Self> {
(features() & NEON != 0).then_some(Self(()))
}
}
const NEON: u8 = 1 << 0;
const CRYPTO_EXT: u8 = 1 << 1;
const DETECTED: u8 = 1 << 7;
static FEATURES: AtomicU8 = AtomicU8::new(0);
#[inline]
fn features() -> u8 {
match FEATURES.load(Ordering::Relaxed) {
0 => detect_once(),
features => features,
}
}
#[cold]
#[inline(never)]
fn detect_once() -> u8 {
let features = detect() | DETECTED;
FEATURES.store(features, Ordering::Relaxed);
features
}
#[cfg(any(target_os = "linux", target_os = "android"))]
fn detect() -> u8 {
const HWCAP_NEON: libc::c_ulong = 1 << 12;
const HWCAP2_AES: libc::c_ulong = 1 << 0;
const HWCAP2_PMULL: libc::c_ulong = 1 << 1;
const HWCAP2_SHA1: libc::c_ulong = 1 << 2;
const HWCAP2_SHA2: libc::c_ulong = 1 << 3;
let (hwcap, hwcap2) = unsafe {
(
libc::getauxval(libc::AT_HWCAP),
libc::getauxval(libc::AT_HWCAP2),
)
};
let neon = test_toggle("neon", hwcap & HWCAP_NEON != 0);
let crypto_ext = neon
&& test_toggle("aes", hwcap2 & HWCAP2_AES != 0)
&& test_toggle("pmull", hwcap2 & HWCAP2_PMULL != 0)
&& test_toggle("sha1", hwcap2 & HWCAP2_SHA1 != 0)
&& test_toggle("sha2", hwcap2 & HWCAP2_SHA2 != 0);
(if neon { NEON } else { 0 }) | (if crypto_ext { CRYPTO_EXT } else { 0 })
}
#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn detect() -> u8 {
0
}
#[cfg(all(
not(any(debug_assertions, rind_cpu_disable)),
any(target_os = "linux", target_os = "android")
))]
fn test_toggle(_id: &str, detected: bool) -> bool {
detected
}
#[cfg(all(
any(debug_assertions, rind_cpu_disable),
any(target_os = "linux", target_os = "android")
))]
fn test_toggle(id: &str, detected: bool) -> bool {
if std::env::var(format!("RIND_CPU_DISABLE_{id}")).is_ok() {
println!("DEBUG: denying cpuid {id:?}");
false
} else {
detected
}
}
pub(crate) fn enter_cpu_state() -> u32 {
0
}
pub(crate) fn leave_cpu_state(_old: u32) {}
pub(in crate::low) fn zero_bytes(ptr: *mut u8, len: usize) {
unsafe {
core::arch::asm!(
" 2: subs {len}, {len}, #16",
" blo 3f",
" str {zero}, [{ptr}], #4",
" str {zero}, [{ptr}], #4",
" str {zero}, [{ptr}], #4",
" str {zero}, [{ptr}], #4",
" b 2b",
" 3: add {len}, {len}, #16",
" 4: subs {len}, {len}, #4",
" blo 5f",
" str {zero}, [{ptr}], #4",
" b 4b",
" 5: add {len}, {len}, #4",
" 6: subs {len}, {len}, #1",
" blo 7f",
" strb {zero}, [{ptr}], #1",
" b 6b",
" 7:",
ptr = inout(reg) ptr => _,
len = inout(reg) len => _,
zero = in(reg) 0u32,
options(nostack),
)
}
}
pub(in crate::low) unsafe fn ct_compare_bytes(a: *const u8, b: *const u8, len: usize) -> u8 {
let mut acc = 0u32;
unsafe {
core::arch::asm!(
" 2: subs {len}, {len}, #1",
" blo 3f",
" ldrb {tmpa}, [{a}], #1",
" ldrb {tmpb}, [{b}], #1",
" eor {tmpa}, {tmpa}, {tmpb}",
" orr {acc}, {acc}, {tmpa}",
" b 2b",
" 3:",
a = inout(reg) a => _,
b = inout(reg) b => _,
len = inout(reg) len => _,
acc = inout(reg) acc,
tmpa = out(reg) _,
tmpb = out(reg) _,
options(readonly, nostack),
)
}
acc as u8
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn detects_the_expected_cpu() {
if let Some(expected) = std::env::var_os("RIND_TEST_ARM_NEON") {
assert_eq!(HaveNeon::check().is_some(), expected == "1");
}
if let Some(expected) = std::env::var_os("RIND_TEST_ARM_CRYPTO_EXT") {
assert_eq!(HaveCryptoExt::check().is_some(), expected == "1");
}
}
}