Skip to content

Commit

Permalink
detect: Support run-time detection on aarch64 NetBSD
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e committed Sep 12, 2023
1 parent 7b25950 commit f8e04b5
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 5 deletions.
6 changes: 6 additions & 0 deletions .github/.cspell/project-dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ algr
allnoconfig
aosp
armasm
armreg
Auxinfo
auxv
auxvec
Expand All @@ -18,6 +19,7 @@ cdsg
cinc
cirruslabs
clgr
clidr
cmovae
cmovb
cmovge
Expand Down Expand Up @@ -78,10 +80,13 @@ lwsync
machdep
mfence
mgba
midr
mipsn
miscompiles
mmfr
mpidr
mstatus
mvfr
negs
neoverse
newrepo
Expand All @@ -104,6 +109,7 @@ qword
RAII
rcpc
reentrancy
revidr
rsbegin
rsend
rsil
Expand Down
1 change: 1 addition & 0 deletions src/imp/atomic128/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Here is the table of targets that support run-time feature detection and the ins
| aarch64 | linux | getauxval | Only enabled by default on `*-linux-gnu*`, and `*-linux-musl*"` (default is static linking)/`*-linux-ohos*` (default is dynamic linking) with dynamic linking enabled. |
| aarch64 | android | getauxval | Enabled by default |
| aarch64 | freebsd | elf_aux_info | Enabled by default |
| aarch64 | netbsd | sysctl | Enabled by default |
| aarch64 | openbsd | sysctl | Enabled by default |
| aarch64 | macos | sysctl | Currently only used in tests because FEAT_LSE and FEAT_LSE2 are always available at compile-time. |
| aarch64 | windows | IsProcessorFeaturePresent | Enabled by default |
Expand Down
5 changes: 4 additions & 1 deletion src/imp/atomic128/aarch64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ include!("macros.rs");
mod detect;
#[cfg(not(portable_atomic_no_outline_atomics))]
#[cfg(any(test, not(any(target_feature = "lse", portable_atomic_target_feature = "lse"))))]
#[cfg(target_os = "openbsd")]
#[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
#[path = "detect/aarch64_aa64reg.rs"]
mod detect;
#[cfg(not(portable_atomic_no_outline_atomics))]
Expand Down Expand Up @@ -140,6 +140,7 @@ macro_rules! debug_assert_lse {
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia",
target_os = "windows",
Expand Down Expand Up @@ -514,6 +515,7 @@ unsafe fn atomic_compare_exchange(
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia",
target_os = "windows",
Expand All @@ -538,6 +540,7 @@ unsafe fn atomic_compare_exchange(
),
target_os = "android",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "fuchsia",
target_os = "windows",
Expand Down
176 changes: 172 additions & 4 deletions src/imp/atomic128/detect/aarch64_aa64reg.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Run-time feature detection on aarch64 Linux/FreeBSD/OpenBSD by parsing system registers.
// Run-time feature detection on aarch64 Linux/FreeBSD/NetBSD/OpenBSD by parsing system registers.
//
// As of nightly-2023-01-23, is_aarch64_feature_detected doesn't support run-time detection on OpenBSD.
// As of nightly-2023-01-23, is_aarch64_feature_detected doesn't support run-time detection on NetBSD/OpenBSD.
// https://github.com/rust-lang/stdarch/blob/a0c30f3e3c75adcd6ee7efc94014ebcead61c507/crates/std_detect/src/detect/mod.rs
// https://github.com/rust-lang/stdarch/pull/1374
//
Expand All @@ -14,10 +14,12 @@
// https://github.com/torvalds/linux/commit/77c97b4ee21290f5f083173d957843b615abbff2
// - FreeBSD 12.0+ (emulate mrs instruction)
// https://github.com/freebsd/freebsd-src/commit/398810619cb32abf349f8de23f29510b2ee0839b
// - NetBSD 9.0+ (through sysctl)
// https://github.com/NetBSD/src/commit/0e9d25528729f7fea53e78275d1bc5039dfe8ffb
// - OpenBSD 7.1+ (through sysctl)
// https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8
//
// For now, this module is only used on OpenBSD.
// For now, this module is only used on NetBSD/OpenBSD.
// On Linux/FreeBSD, this module is test-only:
// - On Linux, this approach requires a higher kernel version than Rust supports,
// and also does not work with qemu-user (as of QEMU 7.2) and Valgrind.
Expand Down Expand Up @@ -79,7 +81,7 @@ fn extract(x: u64, high: usize, low: usize) -> u64 {
(x >> low) & ((1 << (high - low + 1)) - 1)
}

#[cfg(not(target_os = "openbsd"))]
#[cfg(not(any(target_os = "netbsd", target_os = "openbsd")))]
mod imp {
// This module is test-only. See parent module docs for details.

Expand Down Expand Up @@ -129,6 +131,122 @@ mod imp {
}
}
}
#[cfg(target_os = "netbsd")]
mod imp {
// NetBSD doesn't trap the mrs instruction, but exposes the system registers through sysctl.
// https://github.com/NetBSD/src/commit/0e9d25528729f7fea53e78275d1bc5039dfe8ffb
// https://github.com/golang/sys/commit/ef9fd89ba245e184bdd308f7f2b4f3c551fa5b0f

use core::ptr;

use super::AA64Reg;

// core::ffi::c_* (except c_void) requires Rust 1.64, libc will soon require Rust 1.47
#[allow(non_camel_case_types)]
pub(super) mod ffi {
pub(crate) use super::super::c_types::{c_char, c_int, c_size_t, c_void};

extern "C" {
// Defined in sys/sysctl.h.
// https://man.netbsd.org/sysctl.3
// https://github.com/NetBSD/src/blob/167403557cf60bed09a63fc84d941a1a4bd7d52e/sys/sys/sysctl.h
// https://github.com/rust-lang/libc/blob/0.2.139/src/unix/bsd/netbsdlike/netbsd/mod.rs#L2582
pub(crate) fn sysctlbyname(
name: *const c_char,
old_p: *mut c_void,
old_len_p: *mut c_size_t,
new_p: *const c_void,
new_len: c_size_t,
) -> c_int;
}

// Defined in aarch64/armreg.h.
// https://github.com/NetBSD/src/blob/167403557cf60bed09a63fc84d941a1a4bd7d52e/sys/arch/aarch64/include/armreg.h#L1626
#[derive(Clone, Copy)]
#[repr(C)]
pub(crate) struct aarch64_sysctl_cpu_id {
// NetBSD 9.0+
// https://github.com/NetBSD/src/commit/0e9d25528729f7fea53e78275d1bc5039dfe8ffb
pub(crate) midr: u64,
pub(crate) revidr: u64,
pub(crate) mpidr: u64,
pub(crate) aa64dfr0: u64,
pub(crate) aa64dfr1: u64,
pub(crate) aa64isar0: u64,
pub(crate) aa64isar1: u64,
pub(crate) aa64mmfr0: u64,
pub(crate) aa64mmfr1: u64,
pub(crate) aa64mmfr2: u64,
pub(crate) aa64pfr0: u64,
pub(crate) aa64pfr1: u64,
pub(crate) aa64zfr0: u64,
pub(crate) mvfr0: u32,
pub(crate) mvfr1: u32,
pub(crate) mvfr2: u32,
// NetBSD 10.0+
// https://github.com/NetBSD/src/commit/0c7bdc13f0e332cccec56e307f023b4888638973
pub(crate) pad: u32,
pub(crate) clidr: u64,
pub(crate) ctr: u64,
}
}

unsafe fn sysctl_cpu_id(name: &[u8]) -> Option<AA64Reg> {
const OUT_LEN: ffi::c_size_t =
core::mem::size_of::<ffi::aarch64_sysctl_cpu_id>() as ffi::c_size_t;

debug_assert_eq!(name.last(), Some(&0), "{:?}", name);
debug_assert_eq!(name.iter().filter(|&&v| v == 0).count(), 1, "{:?}", name);

// SAFETY: all fields of aarch64_sysctl_cpu_id are zero-able and we use
// the result when machdep.cpuN.cpu_id sysctl was successful.
let mut buf: ffi::aarch64_sysctl_cpu_id = unsafe { core::mem::zeroed() };
let mut out_len = OUT_LEN;
// SAFETY:
// - the caller must guarantee that `name` is ` machdep.cpuN.cpu_id` in a C string.
// - `out_len` does not exceed the size of the value at `buf`.
// - `sysctlbyname` is thread-safe.
let res = unsafe {
ffi::sysctlbyname(
name.as_ptr().cast::<ffi::c_char>(),
(&mut buf as *mut ffi::aarch64_sysctl_cpu_id).cast::<ffi::c_void>(),
&mut out_len,
ptr::null_mut(),
0,
)
};
if res != 0 {
return None;
}
Some(AA64Reg {
aa64isar0: buf.aa64isar0,
#[cfg(test)]
aa64isar1: buf.aa64isar1,
#[cfg(test)]
aa64mmfr2: buf.aa64mmfr2,
})
}

pub(super) fn aa64reg() -> AA64Reg {
// Get system registers for cpu0.
// If failed, returns default because machdep.cpuN.cpu_id sysctl is not available.
// machdep.cpuN.cpu_id sysctl was added on NetBSD 9.0 so it is not available on older versions.
// SAFETY: we passed a valid name in a C string.
// It is ok to check only cpu0, even if there are more CPUs.
// https://github.com/NetBSD/src/commit/bd9707e06ea7d21b5c24df6dfc14cb37c2819416
// https://github.com/golang/sys/commit/ef9fd89ba245e184bdd308f7f2b4f3c551fa5b0f
match unsafe { sysctl_cpu_id(b"machdep.cpu0.cpu_id\0") } {
Some(cpu_id) => cpu_id,
None => AA64Reg {
aa64isar0: 0,
#[cfg(test)]
aa64isar1: 0,
#[cfg(test)]
aa64mmfr2: 0,
},
}
}
}
#[cfg(target_os = "openbsd")]
mod imp {
// OpenBSD doesn't trap the mrs instruction, but exposes the system registers through sysctl.
Expand Down Expand Up @@ -285,6 +403,56 @@ mod tests {
// without actually running tests on these platforms.
// See also tools/codegen/src/ffi.rs.
// TODO(codegen): auto-generate this test
#[cfg(target_os = "netbsd")]
#[allow(
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::no_effect_underscore_binding,
clippy::used_underscore_binding
)]
const _: fn() = || {
use core::mem::size_of;
use imp::ffi;
use test_helper::{libc, sys};
let mut _sysctlbyname: unsafe extern "C" fn(
*const ffi::c_char,
*mut ffi::c_void,
*mut ffi::c_size_t,
*const ffi::c_void,
ffi::c_size_t,
) -> ffi::c_int = ffi::sysctlbyname;
_sysctlbyname = libc::sysctlbyname;
_sysctlbyname = sys::sysctlbyname;
// libc doesn't have this
// static_assert!(
// size_of::<ffi::aarch64_sysctl_cpu_id>() == size_of::<libc::aarch64_sysctl_cpu_id>()
// );
static_assert!(
size_of::<ffi::aarch64_sysctl_cpu_id>() == size_of::<sys::aarch64_sysctl_cpu_id>()
);
let ffi: ffi::aarch64_sysctl_cpu_id = unsafe { core::mem::zeroed() };
let _ = sys::aarch64_sysctl_cpu_id {
ac_midr: ffi.midr,
ac_revidr: ffi.revidr,
ac_mpidr: ffi.mpidr,
ac_aa64dfr0: ffi.aa64dfr0,
ac_aa64dfr1: ffi.aa64dfr1,
ac_aa64isar0: ffi.aa64isar0,
ac_aa64isar1: ffi.aa64isar1,
ac_aa64mmfr0: ffi.aa64mmfr0,
ac_aa64mmfr1: ffi.aa64mmfr1,
ac_aa64mmfr2: ffi.aa64mmfr2,
ac_aa64pfr0: ffi.aa64pfr0,
ac_aa64pfr1: ffi.aa64pfr1,
ac_aa64zfr0: ffi.aa64zfr0,
ac_mvfr0: ffi.mvfr0,
ac_mvfr1: ffi.mvfr1,
ac_mvfr2: ffi.mvfr2,
ac_pad: ffi.pad,
ac_clidr: ffi.clidr,
ac_ctr: ffi.ctr,
};
};
#[cfg(target_os = "openbsd")]
#[allow(
clippy::cast_possible_wrap,
Expand Down
27 changes: 27 additions & 0 deletions tests/helper/src/gen/sys/aarch64_netbsd/aarch64_armreg.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions tests/helper/src/gen/sys/aarch64_netbsd/mod.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions tests/helper/src/gen/sys/aarch64_netbsd/sys_sysctl.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions tests/helper/src/gen/sys/mod.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tools/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ default_targets=(
aarch64-unknown-linux-gnu
aarch64-unknown-linux-musl
aarch64-unknown-linux-uclibc # custom target
aarch64-unknown-netbsd
aarch64-unknown-openbsd
# aarch64 with FEAT_LSE & FEAT_LSE2
aarch64-apple-darwin
Expand Down
Loading

0 comments on commit f8e04b5

Please sign in to comment.