aboutsummaryrefslogtreecommitdiff
path: root/vendor/portable-atomic/src/imp/atomic128/detect/aarch64_aa64reg.rs
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
committerValentin Popov <valentin@popov.link>2024-01-08 00:21:28 +0300
commit1b6a04ca5504955c571d1c97504fb45ea0befee4 (patch)
tree7579f518b23313e8a9748a88ab6173d5e030b227 /vendor/portable-atomic/src/imp/atomic128/detect/aarch64_aa64reg.rs
parent5ecd8cf2cba827454317368b68571df0d13d7842 (diff)
downloadfparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.tar.xz
fparkan-1b6a04ca5504955c571d1c97504fb45ea0befee4.zip
Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
Diffstat (limited to 'vendor/portable-atomic/src/imp/atomic128/detect/aarch64_aa64reg.rs')
-rw-r--r--vendor/portable-atomic/src/imp/atomic128/detect/aarch64_aa64reg.rs628
1 files changed, 628 insertions, 0 deletions
diff --git a/vendor/portable-atomic/src/imp/atomic128/detect/aarch64_aa64reg.rs b/vendor/portable-atomic/src/imp/atomic128/detect/aarch64_aa64reg.rs
new file mode 100644
index 0000000..4cbdb51
--- /dev/null
+++ b/vendor/portable-atomic/src/imp/atomic128/detect/aarch64_aa64reg.rs
@@ -0,0 +1,628 @@
+// SPDX-License-Identifier: Apache-2.0 OR MIT
+
+// 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 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
+//
+// Refs:
+// - https://developer.arm.com/documentation/ddi0601/latest/AArch64-Registers
+// - https://www.kernel.org/doc/Documentation/arm64/cpu-feature-registers.txt
+// - https://github.com/rust-lang/stdarch/blob/a0c30f3e3c75adcd6ee7efc94014ebcead61c507/crates/std_detect/src/detect/os/aarch64.rs
+//
+// Supported platforms:
+// - Linux 4.11+ (emulate mrs instruction)
+// 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 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.
+// (Looking into HWCAP_CPUID in auxvec, it appears that Valgrind is setting it
+// to false correctly, but qemu-user is setting it to true.)
+// - On FreeBSD, this approach does not work on FreeBSD 12 on QEMU (confirmed on
+// FreeBSD 12.{2,3,4}), and we got SIGILL (worked on FreeBSD 13 and 14).
+
+include!("common.rs");
+
+#[cfg_attr(test, derive(Debug, PartialEq))]
+struct AA64Reg {
+ aa64isar0: u64,
+ #[cfg(test)]
+ aa64isar1: u64,
+ // OpenBSD has an API to get this, but currently always returns 0.
+ // https://github.com/openbsd/src/blob/6a233889798dc3ecb18acc52dce1e57862af2957/sys/arch/arm64/arm64/machdep.c#L371-L377
+ #[cfg_attr(target_os = "openbsd", cfg(test))]
+ aa64mmfr2: u64,
+}
+
+#[cold]
+fn _detect(info: &mut CpuInfo) {
+ let AA64Reg {
+ aa64isar0,
+ #[cfg(test)]
+ aa64isar1,
+ #[cfg_attr(target_os = "openbsd", cfg(test))]
+ aa64mmfr2,
+ } = imp::aa64reg();
+
+ // ID_AA64ISAR0_EL1, Instruction Set Attribute Register 0
+ // https://developer.arm.com/documentation/ddi0601/2023-06/AArch64-Registers/ID-AA64ISAR0-EL1--AArch64-Instruction-Set-Attribute-Register-0?lang=en
+ let atomic = extract(aa64isar0, 23, 20);
+ if atomic >= 2 {
+ info.set(CpuInfo::HAS_LSE);
+ // we currently only use FEAT_LSE and FEAT_LSE2 in outline-atomics.
+ #[cfg(test)]
+ {
+ if atomic >= 3 {
+ info.set(CpuInfo::HAS_LSE128);
+ }
+ }
+ }
+ // we currently only use FEAT_LSE and FEAT_LSE2 in outline-atomics.
+ #[cfg(test)]
+ {
+ // ID_AA64ISAR1_EL1, Instruction Set Attribute Register 1
+ // https://developer.arm.com/documentation/ddi0601/2023-06/AArch64-Registers/ID-AA64ISAR1-EL1--AArch64-Instruction-Set-Attribute-Register-1?lang=en
+ if extract(aa64isar1, 23, 20) >= 3 {
+ info.set(CpuInfo::HAS_RCPC3);
+ }
+ }
+ // OpenBSD has an API to get this, but currently always returns 0.
+ // https://github.com/openbsd/src/blob/6a233889798dc3ecb18acc52dce1e57862af2957/sys/arch/arm64/arm64/machdep.c#L371-L377
+ #[cfg_attr(target_os = "openbsd", cfg(test))]
+ {
+ // ID_AA64MMFR2_EL1, AArch64 Memory Model Feature Register 2
+ // https://developer.arm.com/documentation/ddi0601/2023-06/AArch64-Registers/ID-AA64MMFR2-EL1--AArch64-Memory-Model-Feature-Register-2?lang=en
+ if extract(aa64mmfr2, 35, 32) >= 1 {
+ info.set(CpuInfo::HAS_LSE2);
+ }
+ }
+}
+
+fn extract(x: u64, high: usize, low: usize) -> u64 {
+ (x >> low) & ((1 << (high - low + 1)) - 1)
+}
+
+#[cfg(not(any(target_os = "netbsd", target_os = "openbsd")))]
+mod imp {
+ // This module is test-only. See parent module docs for details.
+
+ #[cfg(not(portable_atomic_no_asm))]
+ use core::arch::asm;
+
+ use super::AA64Reg;
+
+ pub(super) fn aa64reg() -> AA64Reg {
+ // SAFETY: This is safe on FreeBSD 12.0+. FreeBSD 11 was EoL on 2021-09-30.
+ // Note that stdarch has been doing the same thing since before FreeBSD 11 was EoL.
+ // https://github.com/rust-lang/stdarch/pull/611
+ unsafe {
+ let aa64isar0: u64;
+ asm!(
+ "mrs {0}, ID_AA64ISAR0_EL1",
+ out(reg) aa64isar0,
+ options(pure, nomem, nostack, preserves_flags)
+ );
+ #[cfg(test)]
+ let aa64isar1: u64;
+ #[cfg(test)]
+ {
+ asm!(
+ "mrs {0}, ID_AA64ISAR1_EL1",
+ out(reg) aa64isar1,
+ options(pure, nomem, nostack, preserves_flags)
+ );
+ }
+ let aa64mmfr2: u64;
+ asm!(
+ "mrs {0}, ID_AA64MMFR2_EL1",
+ out(reg) aa64mmfr2,
+ options(pure, nomem, nostack, preserves_flags)
+ );
+ AA64Reg {
+ aa64isar0,
+ #[cfg(test)]
+ aa64isar1,
+ aa64mmfr2,
+ }
+ }
+ }
+}
+#[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,
+ }
+ }
+
+ pub(super) 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,
+ 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,
+ aa64mmfr2: 0,
+ },
+ }
+ }
+}
+#[cfg(target_os = "openbsd")]
+mod imp {
+ // 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 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_int, c_size_t, c_uint, c_void};
+
+ // Defined in sys/sysctl.h.
+ // https://github.com/openbsd/src/blob/72ccc03bd11da614f31f7ff76e3f6fce99bc1c79/sys/sys/sysctl.h#L82
+ pub(crate) const CTL_MACHDEP: c_int = 7;
+ // Defined in machine/cpu.h.
+ // https://github.com/openbsd/src/blob/72ccc03bd11da614f31f7ff76e3f6fce99bc1c79/sys/arch/arm64/include/cpu.h#L25-L40
+ pub(crate) const CPU_ID_AA64ISAR0: c_int = 2;
+ #[cfg(test)]
+ pub(crate) const CPU_ID_AA64ISAR1: c_int = 3;
+ // OpenBSD has an API to get this, but currently always returns 0.
+ // https://github.com/openbsd/src/blob/6a233889798dc3ecb18acc52dce1e57862af2957/sys/arch/arm64/arm64/machdep.c#L371-L377
+ #[cfg(test)]
+ pub(crate) const CPU_ID_AA64MMFR2: c_int = 7;
+
+ extern "C" {
+ // Defined in sys/sysctl.h.
+ // https://man.openbsd.org/sysctl.2
+ // https://github.com/openbsd/src/blob/72ccc03bd11da614f31f7ff76e3f6fce99bc1c79/sys/sys/sysctl.h
+ // https://github.com/rust-lang/libc/blob/0.2.139/src/unix/bsd/netbsdlike/openbsd/mod.rs#L1817-L1824
+ 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: *mut c_void,
+ new_len: c_size_t,
+ ) -> c_int;
+ }
+ }
+
+ // 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.
+ pub(super) fn aa64reg() -> AA64Reg {
+ let aa64isar0 = sysctl64(&[ffi::CTL_MACHDEP, ffi::CPU_ID_AA64ISAR0]).unwrap_or(0);
+ #[cfg(test)]
+ let aa64isar1 = sysctl64(&[ffi::CTL_MACHDEP, ffi::CPU_ID_AA64ISAR1]).unwrap_or(0);
+ #[cfg(test)]
+ let aa64mmfr2 = sysctl64(&[ffi::CTL_MACHDEP, ffi::CPU_ID_AA64MMFR2]).unwrap_or(0);
+ AA64Reg {
+ aa64isar0,
+ #[cfg(test)]
+ aa64isar1,
+ #[cfg(test)]
+ aa64mmfr2,
+ }
+ }
+
+ fn sysctl64(mib: &[ffi::c_int]) -> Option<u64> {
+ const OUT_LEN: ffi::c_size_t = core::mem::size_of::<u64>() as ffi::c_size_t;
+ let mut out = 0_u64;
+ 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,
+ (&mut out as *mut u64).cast::<ffi::c_void>(),
+ &mut out_len,
+ ptr::null_mut(),
+ 0,
+ )
+ };
+ if res == -1 {
+ return None;
+ }
+ debug_assert_eq!(out_len, OUT_LEN);
+ Some(out)
+ }
+}
+
+#[allow(
+ clippy::alloc_instead_of_core,
+ clippy::std_instead_of_alloc,
+ clippy::std_instead_of_core,
+ clippy::undocumented_unsafe_blocks,
+ clippy::wildcard_imports
+)]
+#[cfg(test)]
+mod tests {
+ use std::{
+ process::Command,
+ string::{String, ToString},
+ };
+
+ use super::*;
+
+ #[test]
+ fn test_aa64reg() {
+ let AA64Reg { aa64isar0, aa64isar1, aa64mmfr2 } = imp::aa64reg();
+ std::eprintln!("aa64isar0={}", aa64isar0);
+ std::eprintln!("aa64isar1={}", aa64isar1);
+ std::eprintln!("aa64mmfr2={}", aa64mmfr2);
+ if cfg!(target_os = "openbsd") {
+ let output = Command::new("sysctl").arg("machdep").output().unwrap();
+ assert!(output.status.success());
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ // OpenBSD 7.1+
+ assert_eq!(
+ stdout.lines().find_map(|s| s.strip_prefix("machdep.id_aa64isar0=")).unwrap_or("0"),
+ aa64isar0.to_string(),
+ );
+ assert_eq!(
+ stdout.lines().find_map(|s| s.strip_prefix("machdep.id_aa64isar1=")).unwrap_or("0"),
+ aa64isar1.to_string(),
+ );
+ // OpenBSD 7.3+
+ assert_eq!(
+ stdout.lines().find_map(|s| s.strip_prefix("machdep.id_aa64mmfr2=")).unwrap_or("0"),
+ aa64mmfr2.to_string(),
+ );
+ }
+ if detect().test(CpuInfo::HAS_LSE) {
+ let atomic = extract(aa64isar0, 23, 20);
+ if detect().test(CpuInfo::HAS_LSE128) {
+ assert_eq!(atomic, 3);
+ } else {
+ assert_eq!(atomic, 2);
+ }
+ }
+ if detect().test(CpuInfo::HAS_LSE2) {
+ assert_eq!(extract(aa64mmfr2, 35, 32), 1);
+ }
+ if detect().test(CpuInfo::HAS_RCPC3) {
+ assert_eq!(extract(aa64isar1, 23, 20), 3);
+ }
+ }
+
+ #[allow(clippy::cast_possible_wrap)]
+ #[cfg(target_os = "netbsd")]
+ #[test]
+ fn test_netbsd() {
+ use c_types::*;
+ use core::{arch::asm, mem, ptr};
+ use imp::ffi;
+ use test_helper::sys;
+
+ // Call syscall using asm instead of libc.
+ // Note that NetBSD does not guarantee the stability of raw syscall as
+ // much as Linux does (It may actually be stable enough, though: https://lists.llvm.org/pipermail/llvm-dev/2019-June/133393.html).
+ //
+ // This is currently used only for testing.
+ unsafe fn sysctl_cpu_id_asm_syscall(name: &[&[u8]]) -> Result<AA64Reg, c_int> {
+ // https://github.com/golang/go/blob/4badad8d477ffd7a6b762c35bc69aed82faface7/src/syscall/asm_netbsd_arm64.s
+ #[inline]
+ unsafe 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,
+ ) -> Result<c_int, c_int> {
+ #[allow(clippy::cast_possible_truncation)]
+ // SAFETY: the caller must uphold the safety contract.
+ unsafe {
+ let mut n = sys::SYS___sysctl as u64;
+ let r: i64;
+ asm!(
+ "svc 0",
+ "b.cc 2f",
+ "mov x17, x0",
+ "mov x0, #-1",
+ "2:",
+ inout("x17") n,
+ inout("x0") ptr_reg!(name) => r,
+ inout("x1") name_len as u64 => _,
+ in("x2") ptr_reg!(old_p),
+ in("x3") ptr_reg!(old_len_p),
+ in("x4") ptr_reg!(new_p),
+ in("x5") new_len as u64,
+ options(nostack),
+ );
+ if r as c_int == -1 {
+ Err(n as c_int)
+ } else {
+ Ok(r as c_int)
+ }
+ }
+ }
+
+ // https://github.com/golang/sys/blob/4badad8d477ffd7a6b762c35bc69aed82faface7/cpu/cpu_netbsd_arm64.go.
+ use std::{vec, vec::Vec};
+ fn sysctl_nodes(mib: &mut Vec<i32>) -> Result<Vec<sys::sysctlnode>, i32> {
+ mib.push(sys::CTL_QUERY);
+ let mut q_node = sys::sysctlnode {
+ sysctl_flags: sys::SYSCTL_VERS_1,
+ ..unsafe { mem::zeroed() }
+ };
+ let qp = (&mut q_node as *mut sys::sysctlnode).cast::<ffi::c_void>();
+ let sz = mem::size_of::<sys::sysctlnode>();
+ let mut olen = 0;
+ #[allow(clippy::cast_possible_truncation)]
+ unsafe {
+ sysctl(mib.as_ptr(), mib.len() as c_uint, ptr::null_mut(), &mut olen, qp, sz)?;
+ }
+
+ let mut nodes = Vec::<sys::sysctlnode>::with_capacity(olen / sz);
+ let np = nodes.as_mut_ptr().cast::<ffi::c_void>();
+ #[allow(clippy::cast_possible_truncation)]
+ unsafe {
+ sysctl(mib.as_ptr(), mib.len() as c_uint, np, &mut olen, qp, sz)?;
+ nodes.set_len(olen / sz);
+ }
+
+ mib.pop(); // pop CTL_QUERY
+ Ok(nodes)
+ }
+ fn name_to_mib(parts: &[&[u8]]) -> Result<Vec<i32>, i32> {
+ let mut mib = vec![];
+ for (part_no, &part) in parts.iter().enumerate() {
+ let nodes = sysctl_nodes(&mut mib)?;
+ for node in nodes {
+ let mut n = vec![];
+ for b in node.sysctl_name {
+ if b != 0 {
+ n.push(b);
+ }
+ }
+ if n == part {
+ mib.push(node.sysctl_num);
+ break;
+ }
+ }
+ if mib.len() != part_no + 1 {
+ return Err(0);
+ }
+ }
+
+ Ok(mib)
+ }
+
+ const OUT_LEN: ffi::c_size_t =
+ core::mem::size_of::<ffi::aarch64_sysctl_cpu_id>() as ffi::c_size_t;
+
+ let mib = name_to_mib(name)?;
+
+ let mut buf: ffi::aarch64_sysctl_cpu_id = unsafe { core::mem::zeroed() };
+ let mut out_len = OUT_LEN;
+ #[allow(clippy::cast_possible_truncation)]
+ unsafe {
+ sysctl(
+ mib.as_ptr(),
+ mib.len() as c_uint,
+ (&mut buf as *mut ffi::aarch64_sysctl_cpu_id).cast::<ffi::c_void>(),
+ &mut out_len,
+ ptr::null_mut(),
+ 0,
+ )?;
+ }
+ Ok(AA64Reg {
+ aa64isar0: buf.aa64isar0,
+ #[cfg(test)]
+ aa64isar1: buf.aa64isar1,
+ #[cfg(test)]
+ aa64mmfr2: buf.aa64mmfr2,
+ })
+ }
+
+ unsafe {
+ assert_eq!(
+ imp::sysctl_cpu_id(b"machdep.cpu0.cpu_id\0").unwrap(),
+ sysctl_cpu_id_asm_syscall(&[b"machdep", b"cpu0", b"cpu_id"]).unwrap()
+ );
+ }
+ }
+
+ // Static assertions for FFI bindings.
+ // This checks that FFI bindings defined in this crate, FFI bindings defined
+ // in libc, and FFI bindings generated for the platform's latest header file
+ // using bindgen have compatible signatures (or the same values if constants).
+ // Since this is static assertion, we can detect problems with
+ // `cargo check --tests --target <target>` run in CI (via TESTS=1 build.sh)
+ // 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,
+ clippy::cast_sign_loss,
+ clippy::no_effect_underscore_binding
+ )]
+ const _: fn() = || {
+ use imp::ffi;
+ use test_helper::{libc, sys};
+ let mut _sysctl: unsafe extern "C" fn(
+ *const ffi::c_int,
+ ffi::c_uint,
+ *mut ffi::c_void,
+ *mut ffi::c_size_t,
+ *mut ffi::c_void,
+ ffi::c_size_t,
+ ) -> ffi::c_int = ffi::sysctl;
+ _sysctl = libc::sysctl;
+ _sysctl = sys::sysctl;
+ static_assert!(ffi::CTL_MACHDEP == libc::CTL_MACHDEP);
+ static_assert!(ffi::CTL_MACHDEP == sys::CTL_MACHDEP as ffi::c_int);
+ // static_assert!(ffi::CPU_ID_AA64ISAR0 == libc::CPU_ID_AA64ISAR0); // libc doesn't have this
+ static_assert!(ffi::CPU_ID_AA64ISAR0 == sys::CPU_ID_AA64ISAR0 as ffi::c_int);
+ // static_assert!(ffi::CPU_ID_AA64ISAR1 == libc::CPU_ID_AA64ISAR1); // libc doesn't have this
+ static_assert!(ffi::CPU_ID_AA64ISAR1 == sys::CPU_ID_AA64ISAR1 as ffi::c_int);
+ // static_assert!(ffi::CPU_ID_AA64MMFR2 == libc::CPU_ID_AA64MMFR2); // libc doesn't have this
+ static_assert!(ffi::CPU_ID_AA64MMFR2 == sys::CPU_ID_AA64MMFR2 as ffi::c_int);
+ };
+}