diff --git a/.github/.cspell/project-dictionary.txt b/.github/.cspell/project-dictionary.txt index 05cbfe8a..b2e26093 100644 --- a/.github/.cspell/project-dictionary.txt +++ b/.github/.cspell/project-dictionary.txt @@ -5,6 +5,7 @@ algr allnoconfig aosp armasm +armreg Auxinfo auxv auxvec @@ -18,6 +19,7 @@ cdsg cinc cirruslabs clgr +clidr cmovae cmovb cmovge @@ -78,10 +80,13 @@ lwsync machdep mfence mgba +midr mipsn miscompiles mmfr +mpidr mstatus +mvfr negs neoverse newrepo @@ -104,6 +109,7 @@ qword RAII rcpc reentrancy +revidr rsbegin rsend rsil diff --git a/src/imp/atomic128/README.md b/src/imp/atomic128/README.md index 5a9758fa..3657add5 100644 --- a/src/imp/atomic128/README.md +++ b/src/imp/atomic128/README.md @@ -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 | diff --git a/src/imp/atomic128/aarch64.rs b/src/imp/atomic128/aarch64.rs index 3055ec1f..b43b43b4 100644 --- a/src/imp/atomic128/aarch64.rs +++ b/src/imp/atomic128/aarch64.rs @@ -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))] @@ -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", @@ -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", @@ -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", diff --git a/src/imp/atomic128/detect/aarch64_aa64reg.rs b/src/imp/atomic128/detect/aarch64_aa64reg.rs index 79fa61a1..8d7393f3 100644 --- a/src/imp/atomic128/detect/aarch64_aa64reg.rs +++ b/src/imp/atomic128/detect/aarch64_aa64reg.rs @@ -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 // @@ -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. @@ -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. @@ -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 { + const OUT_LEN: ffi::c_size_t = + core::mem::size_of::() 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::(), + (&mut buf as *mut ffi::aarch64_sysctl_cpu_id).cast::(), + &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. @@ -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::() == size_of::() + // ); + static_assert!( + size_of::() == size_of::() + ); + 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, diff --git a/tests/helper/src/gen/sys/aarch64_netbsd/aarch64_armreg.rs b/tests/helper/src/gen/sys/aarch64_netbsd/aarch64_armreg.rs new file mode 100644 index 00000000..70c8a0be --- /dev/null +++ b/tests/helper/src/gen/sys/aarch64_netbsd/aarch64_armreg.rs @@ -0,0 +1,27 @@ +// This file is @generated by portable-atomic-internal-codegen +// (gen function at tools/codegen/src/ffi.rs). +// It is not intended for manual editing. + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct aarch64_sysctl_cpu_id { + pub ac_midr: u64, + pub ac_revidr: u64, + pub ac_mpidr: u64, + pub ac_aa64dfr0: u64, + pub ac_aa64dfr1: u64, + pub ac_aa64isar0: u64, + pub ac_aa64isar1: u64, + pub ac_aa64mmfr0: u64, + pub ac_aa64mmfr1: u64, + pub ac_aa64mmfr2: u64, + pub ac_aa64pfr0: u64, + pub ac_aa64pfr1: u64, + pub ac_aa64zfr0: u64, + pub ac_mvfr0: u32, + pub ac_mvfr1: u32, + pub ac_mvfr2: u32, + pub ac_pad: u32, + pub ac_clidr: u64, + pub ac_ctr: u64, +} diff --git a/tests/helper/src/gen/sys/aarch64_netbsd/mod.rs b/tests/helper/src/gen/sys/aarch64_netbsd/mod.rs new file mode 100644 index 00000000..e6dd2d35 --- /dev/null +++ b/tests/helper/src/gen/sys/aarch64_netbsd/mod.rs @@ -0,0 +1,10 @@ +// This file is @generated by portable-atomic-internal-codegen +// (gen function at tools/codegen/src/ffi.rs). +// It is not intended for manual editing. + +#![cfg_attr(rustfmt, rustfmt::skip)] +mod sys_sysctl; +pub use sys_sysctl::sysctlbyname; +mod aarch64_armreg; +pub use aarch64_armreg::aarch64_sysctl_cpu_id; +pub type c_char = u8; diff --git a/tests/helper/src/gen/sys/aarch64_netbsd/sys_sysctl.rs b/tests/helper/src/gen/sys/aarch64_netbsd/sys_sysctl.rs new file mode 100644 index 00000000..5f891625 --- /dev/null +++ b/tests/helper/src/gen/sys/aarch64_netbsd/sys_sysctl.rs @@ -0,0 +1,13 @@ +// This file is @generated by portable-atomic-internal-codegen +// (gen function at tools/codegen/src/ffi.rs). +// It is not intended for manual editing. + +extern "C" { + pub fn sysctlbyname( + arg1: *const ::std::os::raw::c_char, + arg2: *mut ::core::ffi::c_void, + arg3: *mut usize, + arg4: *const ::core::ffi::c_void, + arg5: usize, + ) -> ::std::os::raw::c_int; +} diff --git a/tests/helper/src/gen/sys/mod.rs b/tests/helper/src/gen/sys/mod.rs index 40e7c6ea..8e7c13e5 100644 --- a/tests/helper/src/gen/sys/mod.rs +++ b/tests/helper/src/gen/sys/mod.rs @@ -300,6 +300,24 @@ mod powerpc64le_freebsd; ) )] pub use powerpc64le_freebsd::*; +#[cfg( + all( + target_arch = "aarch64", + target_os = "netbsd", + target_endian = "little", + target_pointer_width = "64" + ) +)] +mod aarch64_netbsd; +#[cfg( + all( + target_arch = "aarch64", + target_os = "netbsd", + target_endian = "little", + target_pointer_width = "64" + ) +)] +pub use aarch64_netbsd::*; #[cfg( all( target_arch = "aarch64", diff --git a/tools/build.sh b/tools/build.sh index 2a558e48..d57ee7ce 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -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 diff --git a/tools/codegen/src/ffi.rs b/tools/codegen/src/ffi.rs index ee143432..1b5d37cd 100644 --- a/tools/codegen/src/ffi.rs +++ b/tools/codegen/src/ffi.rs @@ -234,6 +234,31 @@ static TARGETS: &[Target] = &[ }, ], }, + Target { + triples: &["aarch64-unknown-netbsd"], + headers: &[ + Header { + // https://github.com/NetBSD/src/blob/HEAD/sys/sys/sysctl.h + path: "sys/sysctl.h", + types: &[], + vars: &[], + functions: &["sysctlbyname"], + arch: &[], + os: &[], + env: &[], + }, + Header { + // https://github.com/NetBSD/src/blob/HEAD/sys/arch/aarch64/include/armreg.h + path: "aarch64/armreg.h", + types: &["aarch64_sysctl_cpu_id"], + vars: &[], + functions: &[], + arch: &[aarch64], + os: &[], + env: &[], + }, + ], + }, Target { triples: &["aarch64-unknown-openbsd"], headers: &[ @@ -448,6 +473,14 @@ pub(crate) fn gen() -> Result<()> { include = vec![src_dir.join("include")]; include_header!("sys/types.h"); } + netbsd => { + header_path = src_dir.join("include").join(header.path); + include = vec![ + src_dir.join("include"), + src_dir.join("include/sys"), + src_dir.join("lib/libpthread"), + ] + } fuchsia => { header_path = src_dir.join(header.path); include = vec![ @@ -766,6 +799,26 @@ fn download_headers(target: &TargetSpec, download_dir: &Utf8Path) -> Result { + src_dir = clone(download_dir, "NetBSD/src", &[])?; + let arch = netbsd_arch(target); + for path in ["sys", "uvm"] { + symlink(src_dir.join("sys").join(path), src_dir.join("include").join(path))?; + } + for path in [arch, "machine"] { + symlink( + src_dir.join("sys/arch").join(arch).join("include"), + src_dir.join("include").join(path), + )?; + } + let link = match target.arch { + aarch64 => Some(("arm", src_dir.join("include").join("arm"))), + _ => None, + }; + if let Some((arch, link)) = link { + symlink(src_dir.join("sys/arch").join(arch).join("include"), link)?; + } + } openbsd => { src_dir = clone(download_dir, "openbsd/src", &["/include/", "/sys/"])?; // TODO: use https://github.com/openbsd/src/blob/HEAD/Makefile? @@ -877,6 +930,14 @@ fn freebsd_arch(target: &TargetSpec) -> &'static str { _ => todo!("{target:?}"), } } +fn netbsd_arch(target: &TargetSpec) -> &'static str { + // https://github.com/NetBSD/src/tree/HEAD/sys/arch + match target.arch { + aarch64 => "aarch64", + riscv32 | riscv64 => "riscv", + _ => todo!("{target:?}"), + } +} fn openbsd_arch(target: &TargetSpec) -> &'static str { // https://github.com/openbsd/src/tree/HEAD/sys/arch match target.arch {