From 55df1a880ea655c0c6ab78e69f4bfaaf3eccdb16 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 26 Jan 2023 00:27:21 +0900 Subject: [PATCH 1/3] std_detect: Move aarch64 freebsd test to tests/cpu-detection.rs --- .../src/detect/os/freebsd/aarch64.rs | 18 ----------------- crates/std_detect/tests/cpu-detection.rs | 20 +++++++++++++++++++ 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/crates/std_detect/src/detect/os/freebsd/aarch64.rs b/crates/std_detect/src/detect/os/freebsd/aarch64.rs index 7d972b373f..ccc48f5360 100644 --- a/crates/std_detect/src/detect/os/freebsd/aarch64.rs +++ b/crates/std_detect/src/detect/os/freebsd/aarch64.rs @@ -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")); - } -} diff --git a/crates/std_detect/tests/cpu-detection.rs b/crates/std_detect/tests/cpu-detection.rs index f1232e1145..e090a205c4 100644 --- a/crates/std_detect/tests/cpu-detection.rs +++ b/crates/std_detect/tests/cpu-detection.rs @@ -97,6 +97,26 @@ fn aarch64_windows() { println!("sha2: {:?}", is_aarch64_feature_detected!("sha2")); } +#[test] +#[cfg(all(target_arch = "aarch64", target_os = "freebsd"))] +fn aarch64_freebsd() { + 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() { From c7e045a39a2638a5d2ee612b06dd19cc63bfcd1b Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 26 Jan 2023 00:50:42 +0900 Subject: [PATCH 2/3] std_detect: Split os/aarch64.rs' detect_features into reading and parsing --- crates/std_detect/src/detect/os/aarch64.rs | 135 +++++++++++---------- 1 file changed, 72 insertions(+), 63 deletions(-) diff --git a/crates/std_detect/src/detect/os/aarch64.rs b/crates/std_detect/src/detect/os/aarch64.rs index e0e62ee339..bda5f1bf2d 100644 --- a/crates/std_detect/src/detect/os/aarch64.rs +++ b/crates/std_detect/src/detect/os/aarch64.rs @@ -23,77 +23,86 @@ 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) + ); + } + + parse_system_registers(aa64isar0, aa64isar1, aa64pfr0) +} - 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); +pub(crate) fn parse_system_registers( + aa64isar0: u64, + aa64isar1: u64, + aa64pfr0: 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); } + }; - let fp = bits_shift(aa64pfr0, 19, 16) < 0xF; - let fphp = bits_shift(aa64pfr0, 19, 16) >= 1; - let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF; - let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1; - enable_feature(Feature::fp, fp); - enable_feature(Feature::fp16, fphp); - // SIMD support requires float support - if half-floats are - // supported, it also requires half-float support: - enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp)); - // SIMD extensions require SIMD support: - enable_feature(Feature::aes, asimd && bits_shift(aa64isar0, 7, 4) >= 1); - let sha1 = bits_shift(aa64isar0, 11, 8) >= 1; - let sha2 = bits_shift(aa64isar0, 15, 12) >= 1; - enable_feature(Feature::sha2, asimd && sha1 && sha2); - enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1); - enable_feature( - Feature::dotprod, - asimd && bits_shift(aa64isar0, 47, 44) >= 1, - ); - enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1); + // 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_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_AA64PFR0_EL1 - Processor Feature Register 0 + let fp = bits_shift(aa64pfr0, 19, 16) < 0xF; + let fphp = bits_shift(aa64pfr0, 19, 16) >= 1; + let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF; + let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1; + enable_feature(Feature::fp, fp); + enable_feature(Feature::fp16, fphp); + // SIMD support requires float support - if half-floats are + // supported, it also requires half-float support: + enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp)); + // SIMD extensions require SIMD support: + enable_feature(Feature::aes, asimd && bits_shift(aa64isar0, 7, 4) >= 1); + let sha1 = bits_shift(aa64isar0, 11, 8) >= 1; + let sha2 = bits_shift(aa64isar0, 15, 12) >= 1; + enable_feature(Feature::sha2, asimd && sha1 && sha2); + enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1); + enable_feature( + Feature::dotprod, + asimd && bits_shift(aa64isar0, 47, 44) >= 1, + ); + enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1); - // 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 } From 7e9e266f74e7020f44de56f7ae7ee405e15c4247 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Thu, 26 Jan 2023 22:01:33 +0900 Subject: [PATCH 3/3] std_detect: Support run-time detection on aarch64 OpenBSD --- crates/std_detect/src/detect/mod.rs | 6 ++ crates/std_detect/src/detect/os/aarch64.rs | 46 +++++++-------- .../src/detect/os/openbsd/aarch64.rs | 56 +++++++++++++++++++ crates/std_detect/tests/cpu-detection.rs | 7 ++- 4 files changed, 91 insertions(+), 24 deletions(-) create mode 100644 crates/std_detect/src/detect/os/openbsd/aarch64.rs diff --git a/crates/std_detect/src/detect/mod.rs b/crates/std_detect/src/detect/mod.rs index 9a135c90a3..db7018232d 100644 --- a/crates/std_detect/src/detect/mod.rs +++ b/crates/std_detect/src/detect/mod.rs @@ -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; diff --git a/crates/std_detect/src/detect/os/aarch64.rs b/crates/std_detect/src/detect/os/aarch64.rs index bda5f1bf2d..7f860f44eb 100644 --- a/crates/std_detect/src/detect/os/aarch64.rs +++ b/crates/std_detect/src/detect/os/aarch64.rs @@ -53,13 +53,13 @@ pub(crate) fn detect_features() -> cache::Initializer { ); } - parse_system_registers(aa64isar0, aa64isar1, aa64pfr0) + parse_system_registers(aa64isar0, aa64isar1, Some(aa64pfr0)) } pub(crate) fn parse_system_registers( aa64isar0: u64, aa64isar1: u64, - aa64pfr0: u64, + aa64pfr0: Option, ) -> cache::Initializer { let mut value = cache::Initializer::default(); @@ -76,26 +76,28 @@ pub(crate) fn parse_system_registers( enable_feature(Feature::crc, bits_shift(aa64isar0, 19, 16) >= 1); // ID_AA64PFR0_EL1 - Processor Feature Register 0 - let fp = bits_shift(aa64pfr0, 19, 16) < 0xF; - let fphp = bits_shift(aa64pfr0, 19, 16) >= 1; - let asimd = bits_shift(aa64pfr0, 23, 20) < 0xF; - let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1; - enable_feature(Feature::fp, fp); - enable_feature(Feature::fp16, fphp); - // SIMD support requires float support - if half-floats are - // supported, it also requires half-float support: - enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp)); - // SIMD extensions require SIMD support: - enable_feature(Feature::aes, asimd && bits_shift(aa64isar0, 7, 4) >= 1); - let sha1 = bits_shift(aa64isar0, 11, 8) >= 1; - let sha2 = bits_shift(aa64isar0, 15, 12) >= 1; - enable_feature(Feature::sha2, asimd && sha1 && sha2); - enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1); - enable_feature( - Feature::dotprod, - asimd && bits_shift(aa64isar0, 47, 44) >= 1, - ); - enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1); + 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; + let asimdhp = bits_shift(aa64pfr0, 23, 20) >= 1; + enable_feature(Feature::fp, fp); + enable_feature(Feature::fp16, fphp); + // SIMD support requires float support - if half-floats are + // supported, it also requires half-float support: + enable_feature(Feature::asimd, fp && asimd && (!fphp | asimdhp)); + // SIMD extensions require SIMD support: + enable_feature(Feature::aes, asimd && bits_shift(aa64isar0, 7, 4) >= 1); + let sha1 = bits_shift(aa64isar0, 11, 8) >= 1; + let sha2 = bits_shift(aa64isar0, 15, 12) >= 1; + enable_feature(Feature::sha2, asimd && sha1 && sha2); + enable_feature(Feature::rdm, asimd && bits_shift(aa64isar0, 31, 28) >= 1); + enable_feature( + Feature::dotprod, + asimd && bits_shift(aa64isar0, 47, 44) >= 1, + ); + enable_feature(Feature::sve, asimd && bits_shift(aa64pfr0, 35, 32) >= 1); + } // ID_AA64PFR0_EL1 - Processor Feature Register 0 // Check for either APA or API field diff --git a/crates/std_detect/src/detect/os/openbsd/aarch64.rs b/crates/std_detect/src/detect/os/openbsd/aarch64.rs new file mode 100644 index 0000000000..068dcba7db --- /dev/null +++ b/crates/std_detect/src/detect/os/openbsd/aarch64.rs @@ -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 { + const OUT_LEN: libc::size_t = core::mem::size_of::(); + let mut out = MaybeUninit::::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() }) +} diff --git a/crates/std_detect/tests/cpu-detection.rs b/crates/std_detect/tests/cpu-detection.rs index e090a205c4..ec0875a355 100644 --- a/crates/std_detect/tests/cpu-detection.rs +++ b/crates/std_detect/tests/cpu-detection.rs @@ -98,8 +98,11 @@ fn aarch64_windows() { } #[test] -#[cfg(all(target_arch = "aarch64", target_os = "freebsd"))] -fn aarch64_freebsd() { +#[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"));