Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std_detect: Support run-time detection on aarch64 OpenBSD #1374

Merged
merged 3 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions crates/std_detect/src/detect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ cfg_if! {
mod aarch64;
#[path = "os/freebsd/mod.rs"]
mod os;
} else if #[cfg(all(target_os = "openbsd", target_arch = "aarch64", feature = "libc"))] {
#[allow(dead_code)] // we don't use code that calls the mrs instruction.
#[path = "os/aarch64.rs"]
mod aarch64;
#[path = "os/openbsd/aarch64.rs"]
mod os;
} else if #[cfg(all(target_os = "windows", target_arch = "aarch64"))] {
#[path = "os/windows/aarch64.rs"]
mod os;
Expand Down
99 changes: 55 additions & 44 deletions crates/std_detect/src/detect/os/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,40 +23,60 @@ use core::arch::asm;
///
/// This will cause SIGILL if the current OS is not trapping the mrs instruction.
pub(crate) fn detect_features() -> cache::Initializer {
let mut value = cache::Initializer::default();
// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
let aa64isar0: u64;
unsafe {
asm!(
"mrs {}, ID_AA64ISAR0_EL1",
out(reg) aa64isar0,
options(pure, nomem, preserves_flags, nostack)
);
}

{
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};
// ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1
let aa64isar1: u64;
unsafe {
asm!(
"mrs {}, ID_AA64ISAR1_EL1",
out(reg) aa64isar1,
options(pure, nomem, preserves_flags, nostack)
);
}

// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
let aa64isar0: u64;
unsafe {
asm!(
"mrs {}, ID_AA64ISAR0_EL1",
out(reg) aa64isar0,
options(pure, nomem, preserves_flags, nostack)
);
}
// ID_AA64PFR0_EL1 - Processor Feature Register 0
let aa64pfr0: u64;
unsafe {
asm!(
"mrs {}, ID_AA64PFR0_EL1",
out(reg) aa64pfr0,
options(pure, nomem, preserves_flags, nostack)
);
}

enable_feature(Feature::pmull, bits_shift(aa64isar0, 7, 4) >= 2);
enable_feature(Feature::tme, bits_shift(aa64isar0, 27, 24) == 1);
enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 1);
enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1);
parse_system_registers(aa64isar0, aa64isar1, Some(aa64pfr0))
}

pub(crate) fn parse_system_registers(
aa64isar0: u64,
aa64isar1: u64,
aa64pfr0: Option<u64>,
) -> cache::Initializer {
let mut value = cache::Initializer::default();

// ID_AA64PFR0_EL1 - Processor Feature Register 0
let aa64pfr0: u64;
unsafe {
asm!(
"mrs {}, ID_AA64PFR0_EL1",
out(reg) aa64pfr0,
options(pure, nomem, preserves_flags, nostack)
);
let mut enable_feature = |f, enable| {
if enable {
value.set(f as u32);
}
};

// ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
enable_feature(Feature::pmull, bits_shift(aa64isar0, 7, 4) >= 2);
enable_feature(Feature::tme, bits_shift(aa64isar0, 27, 24) == 1);
enable_feature(Feature::lse, bits_shift(aa64isar0, 23, 20) >= 1);
enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1);

// ID_AA64PFR0_EL1 - Processor Feature Register 0
if let Some(aa64pfr0) = aa64pfr0 {
let fp = bits_shift(aa64pfr0, 19, 16) < 0xF;
let fphp = bits_shift(aa64pfr0, 19, 16) >= 1;
let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF;
Expand All @@ -77,24 +97,15 @@ pub(crate) fn detect_features() -> cache::Initializer {
asimd && bits_shift(aa64isar0, 47, 44) >= 1,
);
enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1);

// ID_AA64ISAR1_EL1 - Instruction Set Attribute Register 1
let aa64isar1: u64;
unsafe {
asm!(
"mrs {}, ID_AA64ISAR1_EL1",
out(reg) aa64isar1,
options(pure, nomem, preserves_flags, nostack)
);
}

// Check for either APA or API field
enable_feature(Feature::paca, bits_shift(aa64isar1, 11, 4) >= 1);
enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1);
// Check for either GPA or GPI field
enable_feature(Feature::pacg, bits_shift(aa64isar1, 31, 24) >= 1);
}

// ID_AA64PFR0_EL1 - Processor Feature Register 0
// Check for either APA or API field
enable_feature(Feature::paca, bits_shift(aa64isar1, 11, 4) >= 1);
enable_feature(Feature::rcpc, bits_shift(aa64isar1, 23, 20) >= 1);
// Check for either GPA or GPI field
enable_feature(Feature::pacg, bits_shift(aa64isar1, 31, 24) >= 1);

value
}

Expand Down
18 changes: 0 additions & 18 deletions crates/std_detect/src/detect/os/freebsd/aarch64.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,3 @@
//! Run-time feature detection for Aarch64 on FreeBSD.

pub(crate) use super::super::aarch64::detect_features;

#[cfg(test)]
mod tests {
#[test]
fn dump() {
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
println!("fp16: {:?}", is_aarch64_feature_detected!("fp16"));
println!("sve: {:?}", is_aarch64_feature_detected!("sve"));
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
println!("rdm: {:?}", is_aarch64_feature_detected!("rdm"));
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
println!("tme: {:?}", is_aarch64_feature_detected!("tme"));
}
}
56 changes: 56 additions & 0 deletions crates/std_detect/src/detect/os/openbsd/aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//! Run-time feature detection for Aarch64 on OpenBSD.
//!
//! OpenBSD doesn't trap the mrs instruction, but exposes the system registers through sysctl.
//! https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8
//! https://github.com/golang/go/commit/cd54ef1f61945459486e9eea2f016d99ef1da925

use crate::detect::cache;
use core::{mem::MaybeUninit, ptr};

// Defined in sys/sysctl.h.
// https://github.com/openbsd/src/blob/72ccc03bd11da614f31f7ff76e3f6fce99bc1c79/sys/sys/sysctl.h#L82
const CTL_MACHDEP: libc::c_int = 7;
// Defined in machine/cpu.h.
// https://github.com/openbsd/src/blob/72ccc03bd11da614f31f7ff76e3f6fce99bc1c79/sys/arch/arm64/include/cpu.h#L25-L40
const CPU_ID_AA64ISAR0: libc::c_int = 2;
const CPU_ID_AA64ISAR1: libc::c_int = 3;
const CPU_ID_AA64PFR0: libc::c_int = 8;

/// Try to read the features from the system registers.
pub(crate) fn detect_features() -> cache::Initializer {
// ID_AA64ISAR0_EL1 and ID_AA64ISAR1_EL1 are supported on OpenBSD 7.1+.
// https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8
// Others are supported on OpenBSD 7.3+.
// https://github.com/openbsd/src/commit/c7654cd65262d532212f65123ee3905ba200365c
// sysctl returns an unsupported error if operation is not supported,
// so we can safely use this function on older versions of OpenBSD.
let aa64isar0 = sysctl64(&[CTL_MACHDEP, CPU_ID_AA64ISAR0]).unwrap_or(0);
let aa64isar1 = sysctl64(&[CTL_MACHDEP, CPU_ID_AA64ISAR1]).unwrap_or(0);
// Do not use unwrap_or(0) because in fp and asimd fields, 0 indicates that
// the feature is available.
let aa64pfr0 = sysctl64(&[CTL_MACHDEP, CPU_ID_AA64PFR0]);

super::aarch64::parse_system_registers(aa64isar0, aa64isar1, aa64pfr0)
}

#[inline]
fn sysctl64(mib: &[libc::c_int]) -> Option<u64> {
const OUT_LEN: libc::size_t = core::mem::size_of::<u64>();
let mut out = MaybeUninit::<u64>::uninit();
let mut out_len = OUT_LEN;
let res = unsafe {
libc::sysctl(
mib.as_ptr(),
mib.len() as libc::c_uint,
out.as_mut_ptr() as *mut libc::c_void,
&mut out_len,
ptr::null_mut(),
0,
)
};
if res == -1 || out_len != OUT_LEN {
return None;
}
// SAFETY: we've checked that sysctl was successful and `out` was filled.
Some(unsafe { out.assume_init() })
}
23 changes: 23 additions & 0 deletions crates/std_detect/tests/cpu-detection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,29 @@ fn aarch64_windows() {
println!("sha2: {:?}", is_aarch64_feature_detected!("sha2"));
}

#[test]
#[cfg(all(
target_arch = "aarch64",
any(target_os = "freebsd", target_os = "openbsd")
))]
fn aarch64_bsd() {
println!("asimd: {:?}", is_aarch64_feature_detected!("asimd"));
println!("pmull: {:?}", is_aarch64_feature_detected!("pmull"));
println!("fp: {:?}", is_aarch64_feature_detected!("fp"));
println!("fp16: {:?}", is_aarch64_feature_detected!("fp16"));
println!("sve: {:?}", is_aarch64_feature_detected!("sve"));
println!("crc: {:?}", is_aarch64_feature_detected!("crc"));
println!("lse: {:?}", is_aarch64_feature_detected!("lse"));
println!("rdm: {:?}", is_aarch64_feature_detected!("rdm"));
println!("rcpc: {:?}", is_aarch64_feature_detected!("rcpc"));
println!("dotprod: {:?}", is_aarch64_feature_detected!("dotprod"));
println!("tme: {:?}", is_aarch64_feature_detected!("tme"));
println!("paca: {:?}", is_aarch64_feature_detected!("paca"));
println!("pacg: {:?}", is_aarch64_feature_detected!("pacg"));
println!("aes: {:?}", is_aarch64_feature_detected!("aes"));
println!("sha2: {:?}", is_aarch64_feature_detected!("sha2"));
}

#[test]
#[cfg(all(target_arch = "powerpc", target_os = "linux"))]
fn powerpc_linux() {
Expand Down