diff --git a/.github/.cspell/project-dictionary.txt b/.github/.cspell/project-dictionary.txt index a85d53731..47ca44dd9 100644 --- a/.github/.cspell/project-dictionary.txt +++ b/.github/.cspell/project-dictionary.txt @@ -3,6 +3,7 @@ adcs addc adde armasm +armreg autogen auxv auxvec @@ -17,6 +18,7 @@ cdsg cfgs cinc cirruslabs +clidr cmpd cmpld cmpxchg @@ -25,6 +27,7 @@ cpsid cpsie CPSR cpuid +cpus crand crandc cror @@ -54,6 +57,7 @@ isync ldaxp ldxp libcalls +libpthread libtcl libtest lqarx @@ -65,13 +69,20 @@ memcpy metavar mfence mgba +midr miscompilation mmfr moreutils +mpidr mstatus +mvfr +NCPU +NCPUONLINE newtype nographic nostdinc +NPROCESSORS +ONLN opensbi osxsave prefetcher @@ -81,6 +92,7 @@ qword RAII rclass reentrancy +revidr sbcs seqlock sete @@ -124,3 +136,4 @@ xgetbv xmmword xsave xsub +zeroable diff --git a/src/imp/atomic128/aarch64.rs b/src/imp/atomic128/aarch64.rs index 7de84666c..5736bb86a 100644 --- a/src/imp/atomic128/aarch64.rs +++ b/src/imp/atomic128/aarch64.rs @@ -53,13 +53,17 @@ include!("macros.rs"); path = "detect/aarch64_linux.rs" )] #[cfg_attr(target_os = "windows", path = "detect/aarch64_windows.rs")] -#[cfg_attr(any(target_os = "freebsd", target_os = "openbsd"), path = "detect/aarch64_aa64reg.rs")] +#[cfg_attr( + any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"), + path = "detect/aarch64_aa64reg.rs" +)] #[cfg_attr( not(any( all(target_os = "linux", target_env = "gnu"), target_os = "android", target_os = "windows", target_os = "freebsd", + target_os = "netbsd", target_os = "openbsd" )), path = "detect/aarch64_std.rs" @@ -308,6 +312,7 @@ unsafe fn atomic_compare_exchange( target_os = "android", target_os = "windows", target_os = "freebsd", + target_os = "netbsd", target_os = "openbsd", ) )))] @@ -323,6 +328,7 @@ unsafe fn atomic_compare_exchange( target_os = "android", target_os = "windows", target_os = "freebsd", + target_os = "netbsd", target_os = "openbsd", ) ))] diff --git a/src/imp/atomic128/detect/aarch64_aa64reg.rs b/src/imp/atomic128/detect/aarch64_aa64reg.rs index 019a9167f..639b7d7a8 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 of FEAT_LSE on OpenBSD. +// As of nightly-2023-01-23, is_aarch64_feature_detected doesn't support run-time detection of FEAT_LSE 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+ // https://github.com/freebsd/freebsd-src/commit/398810619cb32abf349f8de23f29510b2ee0839b +// - NetBSD 9.0+ (through sysctl) +// https://github.com/NetBSD/src/commit/70057642485887b9601804adb983c70d11e48d9a // - OpenBSD 7.1+ (through sysctl) // https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8 // -// For now, this module is only used on FreeBSD and OpenBSD. +// For now, this module is only used on FreeBSD/NetBSD/OpenBSD. // On Linux, this module is test only because 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. (Looking into HWCAP_CPUID in auxvec, it appears @@ -33,6 +35,7 @@ include!("common.rs"); +#[cfg_attr(target_os = "netbsd", derive(Default, PartialEq))] struct AA64Reg { aa64isar0: u64, #[cfg(test)] @@ -75,7 +78,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 { #[cfg(not(portable_atomic_no_asm))] use core::arch::asm; @@ -112,6 +115,247 @@ mod imp { } } } +// NetBSD doesn't trap the mrs instruction, but exposes the system registers through sysctl. +// https://github.com/NetBSD/src/commit/70057642485887b9601804adb983c70d11e48d9a +// https://github.com/golang/sys/commit/ef9fd89ba245e184bdd308f7f2b4f3c551fa5b0f +#[cfg(target_os = "netbsd")] +mod imp { + use core::{mem::MaybeUninit, ptr}; + + use super::AA64Reg; + + // core::ffi::c_* (except c_void) requires Rust 1.64 + #[allow(non_camel_case_types)] + pub(super) mod ffi { + pub(crate) use core::ffi::c_void; + // c_char is u8 on aarch64 netbsd + // https://github.com/rust-lang/rust/blob/1.67.0/library/core/src/ffi/mod.rs#L104-L157 + // https://github.com/rust-lang/libc/blob/0.2.139/src/unix/bsd/netbsdlike/netbsd/aarch64.rs#L5 + pub(crate) type c_char = u8; + // c_{,u}int is {i,u}32 on non-16-bit architectures + // https://github.com/rust-lang/rust/blob/1.67.0/library/core/src/ffi/mod.rs#L159-L173 + pub(crate) type c_int = i32; + pub(crate) type c_uint = u32; + // c_size_t is usize + // https://github.com/rust-lang/rust/blob/1.67.0/library/core/src/ffi/mod.rs#L83-L88 + pub(crate) type c_size_t = usize; + + extern "C" { + // Defined in sys/sysctl.h. + // https://man.netbsd.org/sysctl.3 + // https://github.com/rust-lang/libc/blob/0.2.139/src/unix/bsd/netbsdlike/netbsd/mod.rs#L2574 + pub(crate) fn sysctl( + name: *const c_int, + name_len: c_uint, + old_p: *mut c_void, + old_len_p: *mut c_size_t, + new_p: *const c_void, + new_len: c_size_t, + ) -> c_int; + 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/49ff686c908df01d34af98d7a46d51aabe7008fa/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/70057642485887b9601804adb983c70d11e48d9a + pub(crate) _midr: u64, /* Main ID Register */ + pub(crate) _revidr: u64, /* Revision ID Register */ + pub(crate) _mpidr: u64, /* Multiprocessor Affinity Register */ + pub(crate) _aa64dfr0: u64, /* A64 Debug Feature Register 0 */ + pub(crate) _aa64dfr1: u64, /* A64 Debug Feature Register 1 */ + pub(crate) _aa64isar0: u64, /* A64 Instruction Set Attribute Register 0 */ + pub(crate) _aa64isar1: u64, /* A64 Instruction Set Attribute Register 1 */ + pub(crate) _aa64mmfr0: u64, /* A64 Memory Model Feature Register 0 */ + pub(crate) _aa64mmfr1: u64, /* A64 Memory Model Feature Register 1 */ + pub(crate) _aa64mmfr2: u64, /* A64 Memory Model Feature Register 2 */ + pub(crate) _aa64pfr0: u64, /* A64 Processor Feature Register 0 */ + pub(crate) _aa64pfr1: u64, /* A64 Processor Feature Register 1 */ + pub(crate) _aa64zfr0: u64, /* A64 SVE Feature ID Register 0 */ + pub(crate) _mvfr0: u32, /* Media and VFP Feature Register 0 */ + pub(crate) _mvfr1: u32, /* Media and VFP Feature Register 1 */ + pub(crate) _mvfr2: u32, /* Media and VFP Feature Register 2 */ + // NetBSD 10.0+ + // https://github.com/NetBSD/src/commit/908d3fadf77e1b392db35d1076f7f9961c1d351d + pub(crate) _pad: u32, + pub(crate) _clidr: u64, /* Cache Level ID Register */ + pub(crate) _ctr: u64, /* Cache Type Register */ + } + + // Defined in sys/sysctl.h. + // https://github.com/NetBSD/src/blob/49ff686c908df01d34af98d7a46d51aabe7008fa/sys/sys/sysctl.h + pub(crate) const CTL_HW: c_int = 6; + pub(crate) const HW_NCPU: c_int = 3; + } + + #[inline] + fn sysctl32(mib: &[ffi::c_int]) -> Option { + const OUT_LEN: ffi::c_size_t = core::mem::size_of::() as ffi::c_size_t; + let mut out = MaybeUninit::::uninit(); + let mut out_len = OUT_LEN; + #[allow(clippy::cast_possible_truncation)] + // SAFETY: + // - `mib.len()` does not exceed the size of `mib`. + // - `out_len` does not exceed the size of `out`. + // - `sysctl` is thread-safe. + let res = unsafe { + ffi::sysctl( + mib.as_ptr(), + mib.len() as ffi::c_uint, + out.as_mut_ptr() as *mut ffi::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() }) + } + + #[allow(clippy::used_underscore_binding)] + #[inline] + unsafe fn sysctl_cpu_id(buf: &mut ffi::aarch64_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); + + 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() as *const ffi::c_char, + buf as *mut _ as *mut ffi::c_void, + &mut out_len, + ptr::null_mut(), + 0, + ) + }; + if res != 0 { + return None; + } + Some(AA64Reg { + aa64isar0: buf._aa64isar0, + #[cfg(test)] + aa64mmfr2: buf._aa64mmfr2, + }) + } + + #[inline] + pub(super) fn aa64reg() -> AA64Reg { + // SAFETY: all fields of aarch64_sysctl_cpu_id are zeroable and we use + // the result when machdep.cpuN.cpu_id sysctl was successful. + let mut cpu_id_buf: ffi::aarch64_sysctl_cpu_id = unsafe { core::mem::zeroed() }; + // First, 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. + let cpu0 = match unsafe { sysctl_cpu_id(&mut cpu_id_buf, b"machdep.cpu0.cpu_id\0") } { + Some(cpu0) => cpu0, + None => return AA64Reg::default(), + }; + // Second, get the number of cpus. + // If failed, returns default because nothing can be assumed about the other cores. + // Do not use available_parallelism/_SC_NPROCESSORS_ONLN/HW_NCPUONLINE because + // offline cores may become online during execution. + let cpus = match sysctl32(&[ffi::CTL_HW, ffi::HW_NCPU]) { + Some(0) | None => return AA64Reg::default(), // failed + Some(1) => return cpu0, // single-core + Some(cpus) => cpus, + }; + // Unfortunately, there is a bug in Samsung's SoC that supports + // different CPU features in big and little cores. + // https://web.archive.org/web/20210908112244/https://medium.com/@niaow/a-big-little-problem-a-tale-of-big-little-gone-wrong-e7778ce744bb + // https://github.com/golang/go/issues/28431#issuecomment-433573689 + // https://en.wikichip.org/wiki/samsung/exynos/9810 + // So, make sure that all cores provide the same CPU features. + // Note that we are only checking the consistency of the registers to + // which we actually refer. (If we check all registers, fields such as + // product variant are also checked, which breaks runtime detection on + // most big.LITTLE SoCs.) + let mut name_buf = MachdepNameBuffer::new(); + for n in 1..cpus { + // SAFETY: MachdepNameBuffer::name returns a valid name in a C string. + let cpu = match unsafe { sysctl_cpu_id(&mut cpu_id_buf, name_buf.name(n)) } { + Some(cpu) => cpu, + None => return AA64Reg::default(), + }; + if cpu != cpu0 { + return AA64Reg::default(); + } + } + cpu0 + } + + pub(super) struct MachdepNameBuffer { + buf: [u8; NAME_MAX_LEN], + } + + const NAME_PREFIX: &[u8] = b"machdep.cpu"; + const NAME_SUFFIX: &[u8] = b".cpu_id\0"; + pub(super) const U32_MAX_LEN: usize = 10; + const NAME_MAX_LEN: usize = NAME_PREFIX.len() + NAME_SUFFIX.len() + U32_MAX_LEN; + + impl MachdepNameBuffer { + #[inline] + pub(super) fn new() -> Self { + let mut buf: [u8; NAME_MAX_LEN] = [0; NAME_MAX_LEN]; + buf[..NAME_PREFIX.len()].copy_from_slice(NAME_PREFIX); + Self { buf } + } + + #[allow(clippy::cast_possible_truncation, clippy::unreadable_literal)] + #[inline] + pub(super) fn name(&mut self, mut cpu: u32) -> &[u8] { + let mut len = NAME_PREFIX.len(); + // integer -> string conversion which is optimized for small numbers. + macro_rules! put { + ($cur:tt $($tt:tt)*) => { + if cpu >= $cur { + put!($($tt)*); + let n = cpu / $cur; + self.buf[len] = (n as u8) + b'0'; + len += 1; + cpu %= $cur; + } + }; + () => {}; + } + put!( + 10 + 100 + 1000 + 10000 + 100000 + 1000000 + 10000000 + 100000000 + 1000000000 + ); + self.buf[len] = (cpu as u8) + b'0'; + len += 1; + self.buf[len..len + NAME_SUFFIX.len()].copy_from_slice(NAME_SUFFIX); + len += NAME_SUFFIX.len(); + &self.buf[..len] + } + } +} // 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 @@ -259,6 +503,75 @@ mod tests { // without actually running tests on these platforms. // See also tools/codegen/src/ffi.rs. // TODO: 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 crate::tests::sys::*; + use core::mem::size_of; + use imp::ffi; + let _: ffi::c_char = 0 as std::os::raw::c_char; + let _: ffi::c_int = 0 as std::os::raw::c_int; + let _: ffi::c_uint = 0 as std::os::raw::c_uint; + let _: ffi::c_char = 0 as libc::c_char; + let _: ffi::c_int = 0 as libc::c_int; + let _: ffi::c_uint = 0 as libc::c_uint; + let _: ffi::c_size_t = 0 as libc::size_t; + let mut _sysctl: unsafe extern "C" fn( + *const ffi::c_int, + ffi::c_uint, + *mut ffi::c_void, + *mut ffi::c_size_t, + *const ffi::c_void, + ffi::c_size_t, + ) -> ffi::c_int = ffi::sysctl; + _sysctl = libc::sysctl; + _sysctl = sys_sysctl::sysctl; + 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_sysctl::sysctlbyname; + let [] = [(); (ffi::CTL_HW - libc::CTL_HW) as usize]; + let [] = [(); (ffi::CTL_HW - sys_sysctl::CTL_HW as ffi::c_int) as usize]; + let [] = [(); (ffi::HW_NCPU - libc::HW_NCPU) as usize]; + let [] = [(); (ffi::HW_NCPU - sys_sysctl::HW_NCPU as ffi::c_int) as usize]; + // libc doesn't have this + // let [] = [(); size_of::() + // - size_of::()]; + let [] = [(); size_of::() + - size_of::()]; + let ffi: ffi::aarch64_sysctl_cpu_id = unsafe { core::mem::zeroed() }; + let _ = aarch64_armreg::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, @@ -294,4 +607,25 @@ mod tests { let [] = [(); (ffi::CPU_ID_AA64MMFR2 - machine_cpu::CPU_ID_AA64MMFR2 as ffi::c_int) as usize]; }; + + #[cfg(target_os = "netbsd")] + #[test] + fn machdep_name_buffer() { + use std::string::ToString; + assert_eq!(u32::MAX.to_string().len(), imp::U32_MAX_LEN); + assert_eq!(imp::MachdepNameBuffer::new().name(0), b"machdep.cpu0.cpu_id\0"); + assert_eq!(imp::MachdepNameBuffer::new().name(1), b"machdep.cpu1.cpu_id\0"); + assert_eq!(imp::MachdepNameBuffer::new().name(10), b"machdep.cpu10.cpu_id\0"); + assert_eq!(imp::MachdepNameBuffer::new().name(100), b"machdep.cpu100.cpu_id\0"); + assert_eq!(imp::MachdepNameBuffer::new().name(1023), b"machdep.cpu1023.cpu_id\0"); + assert_eq!(imp::MachdepNameBuffer::new().name(u32::MAX), b"machdep.cpu4294967295.cpu_id\0"); + } + #[cfg(target_os = "netbsd")] + ::quickcheck::quickcheck! { + fn quickcheck_machdep_name_buffer(x: u32) -> bool { + let expected = std::ffi::CString::new(std::format!("machdep.cpu{}.cpu_id", x)).unwrap(); + assert_eq!(imp::MachdepNameBuffer::new().name(x), expected.into_bytes_with_nul()); + true + } + } } diff --git a/src/imp/atomic128/detect/aarch64_std.rs b/src/imp/atomic128/detect/aarch64_std.rs index 79c765f29..4975cce5c 100644 --- a/src/imp/atomic128/detect/aarch64_std.rs +++ b/src/imp/atomic128/detect/aarch64_std.rs @@ -7,6 +7,7 @@ target_os = "android", target_os = "windows", // target_os = "freebsd", + // target_os = "netbsd", // target_os = "openbsd", )), any(target_feature = "lse", portable_atomic_target_feature = "lse"), @@ -31,13 +32,14 @@ pub(crate) fn has_lse() -> bool { )), // https://github.com/rust-lang/stdarch/blob/a0c30f3e3c75adcd6ee7efc94014ebcead61c507/crates/std_detect/src/detect/mod.rs // It is fine to use std for targets that we know can be linked to std. - // Note: std may not be available on tier 3 such as aarch64 FreeBSD/OpenBSD. + // Note: std may not be available on tier 3 such as aarch64 FreeBSD/NetBSD/OpenBSD. any( feature = "std", all(target_os = "linux", any(target_env = "gnu", target_env = "musl")), target_os = "android", target_os = "windows", // target_os = "freebsd", + // target_os = "netbsd", // target_os = "openbsd", ) ))] diff --git a/src/tests/gen/sys/aarch64_netbsd/aarch64_armreg.rs b/src/tests/gen/sys/aarch64_netbsd/aarch64_armreg.rs new file mode 100644 index 000000000..70c8a0be8 --- /dev/null +++ b/src/tests/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/src/tests/gen/sys/aarch64_netbsd/mod.rs b/src/tests/gen/sys/aarch64_netbsd/mod.rs new file mode 100644 index 000000000..925c5f4d8 --- /dev/null +++ b/src/tests/gen/sys/aarch64_netbsd/mod.rs @@ -0,0 +1,7 @@ +// 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)] +pub(crate) mod sys_sysctl; +pub(crate) mod aarch64_armreg; diff --git a/src/tests/gen/sys/aarch64_netbsd/sys_sysctl.rs b/src/tests/gen/sys/aarch64_netbsd/sys_sysctl.rs new file mode 100644 index 000000000..eec654c59 --- /dev/null +++ b/src/tests/gen/sys/aarch64_netbsd/sys_sysctl.rs @@ -0,0 +1,26 @@ +// This file is @generated by portable-atomic-internal-codegen +// (gen function at tools/codegen/src/ffi.rs). +// It is not intended for manual editing. + +pub const CTL_HW: u32 = 6; +pub const HW_NCPU: u32 = 3; +pub type u_int = ::std::os::raw::c_uint; +extern "C" { + pub fn sysctl( + arg1: *const ::std::os::raw::c_int, + arg2: u_int, + arg3: *mut ::core::ffi::c_void, + arg4: *mut usize, + arg5: *const ::core::ffi::c_void, + arg6: usize, + ) -> ::std::os::raw::c_int; +} +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/src/tests/gen/sys/mod.rs b/src/tests/gen/sys/mod.rs index 299e485e1..5547235b1 100644 --- a/src/tests/gen/sys/mod.rs +++ b/src/tests/gen/sys/mod.rs @@ -50,6 +50,10 @@ pub(crate) use aarch64_linux_gnu_ilp32::*; mod aarch64_linux_android; #[cfg(all(target_arch = "aarch64", target_os = "android"))] pub(crate) use aarch64_linux_android::*; +#[cfg(all(target_arch = "aarch64", target_os = "netbsd"))] +mod aarch64_netbsd; +#[cfg(all(target_arch = "aarch64", target_os = "netbsd"))] +pub(crate) use aarch64_netbsd::*; #[cfg(all(target_arch = "aarch64", target_os = "openbsd"))] mod aarch64_openbsd; #[cfg(all(target_arch = "aarch64", target_os = "openbsd"))] diff --git a/tools/build.sh b/tools/build.sh index 8c0afd082..582aff87c 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -59,6 +59,7 @@ default_targets=( aarch64-unknown-linux-gnu aarch64-unknown-linux-musl aarch64-unknown-linux-uclibc # custom target + aarch64-unknown-netbsd aarch64-unknown-openbsd # aarch64 always support lse & lse2 aarch64-apple-darwin diff --git a/tools/codegen/src/ffi.rs b/tools/codegen/src/ffi.rs index b381f9224..d7959e5d8 100644 --- a/tools/codegen/src/ffi.rs +++ b/tools/codegen/src/ffi.rs @@ -1,8 +1,8 @@ // Run-time feature detection on aarch64 usually requires the use of // platform APIs, and we define our own FFI bindings to those APIs. // -// We use only one function and a few constants per platform, but -// technically OS can change these APIs so it is preferable to be able +// We use only one or two function(s) and a few types/constants per platform, +// but technically OS can change these APIs so it is preferable to be able // to detect them. // // See also https://github.com/rust-lang/libc/issues/570. @@ -54,6 +54,25 @@ 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: &["CTL_HW", "HW_NCPU"], + functions: &["sysctl", "sysctlbyname"], + }, + 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: &[], + }, + ], + }, Target { triples: &["aarch64-unknown-openbsd"], headers: &[ @@ -138,6 +157,12 @@ pub(crate) fn gen() -> Result<()> { }; vec![src_dir.join("arch").join(arch).join("include/uapi")] } + netbsd => vec![ + src_dir.join("sys"), + src_dir.join("sys/sys"), + src_dir.join("include"), + src_dir.join("lib/libpthread"), + ], openbsd => vec![src_dir.join("sys")], _ => todo!("{target:?}"), }; @@ -157,7 +182,7 @@ pub(crate) fn gen() -> Result<()> { let header_path = match target.os { linux | android => src_dir.join(header.path), - openbsd => src_dir.join(format!("sys/{}", header.path)), + netbsd | openbsd => src_dir.join(format!("sys/{}", header.path)), _ => todo!("{target:?}"), }; @@ -226,6 +251,7 @@ fn git_clone(target: &TargetSpec, download_cache_dir: &Utf8Path) -> Result clone(download_cache_dir, "torvalds/linux")?, + netbsd => clone(download_cache_dir, "NetBSD/src")?, openbsd => clone(download_cache_dir, "openbsd/src")?, _ => todo!("{target:?}"), }; @@ -246,6 +272,26 @@ fn git_clone(target: &TargetSpec, download_cache_dir: &Utf8Path) -> Result Result<()> { match target.os { linux | android => {} + netbsd => { + let arch = match target.arch { + aarch64 => "aarch64", + _ => todo!("{target:?}"), + }; + let link1 = &src_dir.join("sys").join(arch); + let link2 = &src_dir.join("sys/machine"); + let link3 = match target.arch { + aarch64 => Some(("arm", src_dir.join("sys/arm"))), + _ => None, + }; + fs::os::unix::fs::symlink(src_dir.join("sys/arch").join(arch).join("include"), link1)?; + fs::os::unix::fs::symlink(src_dir.join("sys/arch").join(arch).join("include"), link2)?; + if let Some((arch, link)) = &link3 { + fs::os::unix::fs::symlink( + src_dir.join("sys/arch").join(arch).join("include"), + link, + )?; + } + } openbsd => { let arch = match target.arch { aarch64 => "arm64",