1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2
3 /*
4 Run-time CPU feature detection on AArch64 Linux/Android/FreeBSD/NetBSD/OpenBSD by parsing system registers.
5
6 As of nightly-2024-09-07, is_aarch64_feature_detected doesn't support run-time detection on NetBSD.
7 https://github.com/rust-lang/stdarch/blob/d9466edb4c53cece8686ee6e17b028436ddf4151/crates/std_detect/src/detect/mod.rs
8 Run-time detection on OpenBSD by is_aarch64_feature_detected is supported on Rust 1.70+.
9 https://github.com/rust-lang/stdarch/pull/1374
10
11 Refs:
12 - https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers
13 - https://github.com/torvalds/linux/blob/v6.11/Documentation/arch/arm64/cpu-feature-registers.rst
14 - https://github.com/rust-lang/stdarch/blob/a0c30f3e3c75adcd6ee7efc94014ebcead61c507/crates/std_detect/src/detect/os/aarch64.rs
15
16 Supported platforms:
17 - Linux 4.11+ (emulate mrs instruction)
18 https://github.com/torvalds/linux/commit/77c97b4ee21290f5f083173d957843b615abbff2
19 - FreeBSD 12.0+ (emulate mrs instruction)
20 https://github.com/freebsd/freebsd-src/commit/398810619cb32abf349f8de23f29510b2ee0839b
21 - NetBSD 9.0+ (through sysctl/sysctlbyname)
22 https://github.com/NetBSD/src/commit/0e9d25528729f7fea53e78275d1bc5039dfe8ffb
23 - OpenBSD 7.1+ (through sysctl)
24 https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8
25
26 For now, this module is only used on NetBSD/OpenBSD.
27
28 On Linux/Android/FreeBSD, we use auxv.rs and this module is test-only because:
29 - On Linux/Android, this approach requires a higher kernel version than Rust supports,
30 and also does not work with qemu-user (as of 7.2) and Valgrind (as of 3.19).
31 (Looking into HWCAP_CPUID in auxvec, it appears that Valgrind is setting it
32 to false correctly, but qemu-user is setting it to true.)
33 - On FreeBSD, this approach does not work on FreeBSD 12 on QEMU (confirmed on
34 FreeBSD 12.{2,3,4}), and we got SIGILL (worked on FreeBSD 13 and 14).
35 */
36
37 include!("common.rs");
38
39 #[cfg_attr(test, derive(Debug, PartialEq))]
40 struct AA64Reg {
41 aa64isar0: u64,
42 aa64isar1: u64,
43 aa64mmfr2: u64,
44 }
45
46 #[cold]
_detect(info: &mut CpuInfo)47 fn _detect(info: &mut CpuInfo) {
48 let AA64Reg { aa64isar0, aa64isar1, aa64mmfr2 } = imp::aa64reg();
49
50 // ID_AA64ISAR0_EL1, AArch64 Instruction Set Attribute Register 0
51 // https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers/ID-AA64ISAR0-EL1--AArch64-Instruction-Set-Attribute-Register-0
52 let atomic = extract(aa64isar0, 23, 20);
53 if atomic >= 0b0010 {
54 info.set(CpuInfo::HAS_LSE);
55 if atomic >= 0b0011 {
56 info.set(CpuInfo::HAS_LSE128);
57 }
58 }
59 // ID_AA64ISAR1_EL1, AArch64 Instruction Set Attribute Register 1
60 // https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers/ID-AA64ISAR1-EL1--AArch64-Instruction-Set-Attribute-Register-1
61 if extract(aa64isar1, 23, 20) >= 0b0011 {
62 info.set(CpuInfo::HAS_RCPC3);
63 }
64 // ID_AA64MMFR2_EL1, AArch64 Memory Model Feature Register 2
65 // https://developer.arm.com/documentation/ddi0601/2024-06/AArch64-Registers/ID-AA64MMFR2-EL1--AArch64-Memory-Model-Feature-Register-2
66 if extract(aa64mmfr2, 35, 32) >= 0b0001 {
67 info.set(CpuInfo::HAS_LSE2);
68 }
69 }
70
extract(x: u64, high: usize, low: usize) -> u6471 fn extract(x: u64, high: usize, low: usize) -> u64 {
72 (x >> low) & ((1 << (high - low + 1)) - 1)
73 }
74
75 #[cfg(not(any(target_os = "netbsd", target_os = "openbsd")))]
76 mod imp {
77 // This module is test-only. See parent module docs for details.
78
79 #[cfg(not(portable_atomic_no_asm))]
80 use core::arch::asm;
81
82 use super::AA64Reg;
83
aa64reg() -> AA64Reg84 pub(super) fn aa64reg() -> AA64Reg {
85 // SAFETY: This is safe on FreeBSD 12.0+. FreeBSD 11 was EoL on 2021-09-30.
86 // Note that stdarch has been doing the same thing since before FreeBSD 11 was EoL.
87 // https://github.com/rust-lang/stdarch/pull/611
88 unsafe {
89 let aa64isar0: u64;
90 asm!(
91 "mrs {0}, ID_AA64ISAR0_EL1",
92 out(reg) aa64isar0,
93 options(pure, nomem, nostack, preserves_flags),
94 );
95 let aa64isar1: u64;
96 asm!(
97 "mrs {0}, ID_AA64ISAR1_EL1",
98 out(reg) aa64isar1,
99 options(pure, nomem, nostack, preserves_flags),
100 );
101 let aa64mmfr2: u64;
102 asm!(
103 "mrs {0}, ID_AA64MMFR2_EL1",
104 out(reg) aa64mmfr2,
105 options(pure, nomem, nostack, preserves_flags),
106 );
107 AA64Reg { aa64isar0, aa64isar1, aa64mmfr2 }
108 }
109 }
110 }
111 #[cfg(target_os = "netbsd")]
112 mod imp {
113 // NetBSD doesn't trap the mrs instruction, but exposes the system registers through sysctl.
114 // https://github.com/NetBSD/src/commit/0e9d25528729f7fea53e78275d1bc5039dfe8ffb
115 // https://github.com/golang/sys/commit/ef9fd89ba245e184bdd308f7f2b4f3c551fa5b0f
116
117 use core::ptr;
118
119 use super::AA64Reg;
120
121 // core::ffi::c_* (except c_void) requires Rust 1.64, libc will soon require Rust 1.47
122 #[allow(non_camel_case_types)]
123 pub(super) mod ffi {
124 pub(crate) use super::super::c_types::{c_char, c_int, c_size_t, c_void};
125
126 // Defined in aarch64/armreg.h.
127 // https://github.com/NetBSD/src/blob/432a1357026b10c184d8a0ddb683008a23cc7cd9/sys/arch/aarch64/include/armreg.h#L1863
128 #[derive(Clone, Copy)]
129 #[repr(C)]
130 pub(crate) struct aarch64_sysctl_cpu_id {
131 // NetBSD 9.0+
132 // https://github.com/NetBSD/src/commit/0e9d25528729f7fea53e78275d1bc5039dfe8ffb
133 pub(crate) midr: u64,
134 pub(crate) revidr: u64,
135 pub(crate) mpidr: u64,
136 pub(crate) aa64dfr0: u64,
137 pub(crate) aa64dfr1: u64,
138 pub(crate) aa64isar0: u64,
139 pub(crate) aa64isar1: u64,
140 pub(crate) aa64mmfr0: u64,
141 pub(crate) aa64mmfr1: u64,
142 pub(crate) aa64mmfr2: u64,
143 pub(crate) aa64pfr0: u64,
144 pub(crate) aa64pfr1: u64,
145 pub(crate) aa64zfr0: u64,
146 pub(crate) mvfr0: u32,
147 pub(crate) mvfr1: u32,
148 pub(crate) mvfr2: u32,
149 // NetBSD 10.0+
150 // https://github.com/NetBSD/src/commit/0c7bdc13f0e332cccec56e307f023b4888638973
151 pub(crate) pad: u32,
152 pub(crate) clidr: u64,
153 pub(crate) ctr: u64,
154 }
155
156 extern "C" {
157 // Defined in sys/sysctl.h.
158 // https://man.netbsd.org/sysctl.3
159 // https://github.com/NetBSD/src/blob/432a1357026b10c184d8a0ddb683008a23cc7cd9/sys/sys/sysctl.h
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_int160 pub(crate) fn sysctlbyname(
161 name: *const c_char,
162 old_p: *mut c_void,
163 old_len_p: *mut c_size_t,
164 new_p: *const c_void,
165 new_len: c_size_t,
166 ) -> c_int;
167 }
168 }
169
sysctl_cpu_id(name: &[u8]) -> Option<AA64Reg>170 pub(super) unsafe fn sysctl_cpu_id(name: &[u8]) -> Option<AA64Reg> {
171 const OUT_LEN: ffi::c_size_t =
172 core::mem::size_of::<ffi::aarch64_sysctl_cpu_id>() as ffi::c_size_t;
173
174 debug_assert_eq!(name.last(), Some(&0), "{:?}", name);
175 debug_assert_eq!(name.iter().filter(|&&v| v == 0).count(), 1, "{:?}", name);
176
177 // SAFETY: all fields of aarch64_sysctl_cpu_id are zero-able and we use
178 // the result when machdep.cpuN.cpu_id sysctl was successful.
179 let mut buf: ffi::aarch64_sysctl_cpu_id = unsafe { core::mem::zeroed() };
180 let mut out_len = OUT_LEN;
181 // SAFETY:
182 // - the caller must guarantee that `name` is ` machdep.cpuN.cpu_id` in a C string.
183 // - `out_len` does not exceed the size of the value at `buf`.
184 // - `sysctlbyname` is thread-safe.
185 let res = unsafe {
186 ffi::sysctlbyname(
187 name.as_ptr().cast::<ffi::c_char>(),
188 (&mut buf as *mut ffi::aarch64_sysctl_cpu_id).cast::<ffi::c_void>(),
189 &mut out_len,
190 ptr::null_mut(),
191 0,
192 )
193 };
194 if res != 0 {
195 return None;
196 }
197 Some(AA64Reg {
198 aa64isar0: buf.aa64isar0,
199 aa64isar1: buf.aa64isar1,
200 aa64mmfr2: buf.aa64mmfr2,
201 })
202 }
203
aa64reg() -> AA64Reg204 pub(super) fn aa64reg() -> AA64Reg {
205 // Get system registers for cpu0.
206 // If failed, returns default because machdep.cpuN.cpu_id sysctl is not available.
207 // machdep.cpuN.cpu_id sysctl was added in NetBSD 9.0 so it is not available on older versions.
208 // SAFETY: we passed a valid name in a C string.
209 // It is ok to check only cpu0, even if there are more CPUs.
210 // https://github.com/NetBSD/src/commit/bd9707e06ea7d21b5c24df6dfc14cb37c2819416
211 // https://github.com/golang/sys/commit/ef9fd89ba245e184bdd308f7f2b4f3c551fa5b0f
212 match unsafe { sysctl_cpu_id(b"machdep.cpu0.cpu_id\0") } {
213 Some(cpu_id) => cpu_id,
214 None => AA64Reg { aa64isar0: 0, aa64isar1: 0, aa64mmfr2: 0 },
215 }
216 }
217 }
218 #[cfg(target_os = "openbsd")]
219 mod imp {
220 // OpenBSD doesn't trap the mrs instruction, but exposes the system registers through sysctl.
221 // https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8
222 // https://github.com/golang/go/commit/cd54ef1f61945459486e9eea2f016d99ef1da925
223
224 use core::ptr;
225
226 use super::AA64Reg;
227
228 // core::ffi::c_* (except c_void) requires Rust 1.64, libc will soon require Rust 1.47
229 #[allow(non_camel_case_types)]
230 pub(super) mod ffi {
231 pub(crate) use super::super::c_types::{c_int, c_size_t, c_uint, c_void};
232
233 // Defined in sys/sysctl.h.
234 // https://github.com/openbsd/src/blob/ed8f5e8d82ace15e4cefca2c82941b15cb1a7830/sys/sys/sysctl.h#L82
235 pub(crate) const CTL_MACHDEP: c_int = 7;
236
237 // Defined in machine/cpu.h.
238 // https://github.com/openbsd/src/blob/ed8f5e8d82ace15e4cefca2c82941b15cb1a7830/sys/arch/arm64/include/cpu.h#L25-L40
239 // OpenBSD 7.1+
240 // https://github.com/openbsd/src/commit/d335af936b9d7dd9cf655cae1ce19560c45de6c8
241 pub(crate) const CPU_ID_AA64ISAR0: c_int = 2;
242 pub(crate) const CPU_ID_AA64ISAR1: c_int = 3;
243 // OpenBSD 7.3+
244 // https://github.com/openbsd/src/commit/c7654cd65262d532212f65123ee3905ba200365c
245 // However, on OpenBSD 7.3-7.5, querying CPU_ID_AA64MMFR2 always returns 0.
246 // https://github.com/openbsd/src/commit/e8331b74e5c20302d4bd948c9db722af688ccfc1
247 pub(crate) const CPU_ID_AA64MMFR2: c_int = 7;
248
249 extern "C" {
250 // Defined in sys/sysctl.h.
251 // https://man.openbsd.org/sysctl.2
252 // https://github.com/openbsd/src/blob/ed8f5e8d82ace15e4cefca2c82941b15cb1a7830/sys/sys/sysctl.h
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_int253 pub(crate) fn sysctl(
254 name: *const c_int,
255 name_len: c_uint,
256 old_p: *mut c_void,
257 old_len_p: *mut c_size_t,
258 new_p: *mut c_void,
259 new_len: c_size_t,
260 ) -> c_int;
261 }
262 }
263
264 // sysctl returns an unsupported error if operation is not supported,
265 // so we can safely use this function on older versions of OpenBSD.
aa64reg() -> AA64Reg266 pub(super) fn aa64reg() -> AA64Reg {
267 let aa64isar0 = sysctl64(&[ffi::CTL_MACHDEP, ffi::CPU_ID_AA64ISAR0]).unwrap_or(0);
268 let aa64isar1 = sysctl64(&[ffi::CTL_MACHDEP, ffi::CPU_ID_AA64ISAR1]).unwrap_or(0);
269 let aa64mmfr2 = sysctl64(&[ffi::CTL_MACHDEP, ffi::CPU_ID_AA64MMFR2]).unwrap_or(0);
270 AA64Reg { aa64isar0, aa64isar1, aa64mmfr2 }
271 }
272
sysctl64(mib: &[ffi::c_int]) -> Option<u64>273 fn sysctl64(mib: &[ffi::c_int]) -> Option<u64> {
274 const OUT_LEN: ffi::c_size_t = core::mem::size_of::<u64>() as ffi::c_size_t;
275 let mut out = 0_u64;
276 let mut out_len = OUT_LEN;
277 #[allow(clippy::cast_possible_truncation)]
278 let mib_len = mib.len() as ffi::c_uint;
279 // SAFETY:
280 // - `mib.len()` does not exceed the size of `mib`.
281 // - `out_len` does not exceed the size of `out`.
282 // - `sysctl` is thread-safe.
283 let res = unsafe {
284 ffi::sysctl(
285 mib.as_ptr(),
286 mib_len,
287 (&mut out as *mut u64).cast::<ffi::c_void>(),
288 &mut out_len,
289 ptr::null_mut(),
290 0,
291 )
292 };
293 if res == -1 {
294 return None;
295 }
296 debug_assert_eq!(out_len, OUT_LEN);
297 Some(out)
298 }
299 }
300
301 #[allow(
302 clippy::alloc_instead_of_core,
303 clippy::std_instead_of_alloc,
304 clippy::std_instead_of_core,
305 clippy::undocumented_unsafe_blocks,
306 clippy::wildcard_imports
307 )]
308 #[cfg(test)]
309 mod tests {
310 use std::{
311 process::Command,
312 string::{String, ToString},
313 };
314
315 use super::*;
316
317 #[test]
test_aa64reg()318 fn test_aa64reg() {
319 let AA64Reg { aa64isar0, aa64isar1, aa64mmfr2 } = imp::aa64reg();
320 std::eprintln!("aa64isar0={}", aa64isar0);
321 std::eprintln!("aa64isar1={}", aa64isar1);
322 std::eprintln!("aa64mmfr2={}", aa64mmfr2);
323 if cfg!(target_os = "openbsd") {
324 let output = Command::new("sysctl").arg("machdep").output().unwrap();
325 assert!(output.status.success());
326 let stdout = String::from_utf8(output.stdout).unwrap();
327 // OpenBSD 7.1+
328 assert_eq!(
329 stdout.lines().find_map(|s| s.strip_prefix("machdep.id_aa64isar0=")).unwrap_or("0"),
330 aa64isar0.to_string(),
331 );
332 assert_eq!(
333 stdout.lines().find_map(|s| s.strip_prefix("machdep.id_aa64isar1=")).unwrap_or("0"),
334 aa64isar1.to_string(),
335 );
336 // OpenBSD 7.3+
337 assert_eq!(
338 stdout.lines().find_map(|s| s.strip_prefix("machdep.id_aa64mmfr2=")).unwrap_or("0"),
339 aa64mmfr2.to_string(),
340 );
341 }
342 if detect().test(CpuInfo::HAS_LSE) {
343 let atomic = extract(aa64isar0, 23, 20);
344 if detect().test(CpuInfo::HAS_LSE128) {
345 assert_eq!(atomic, 3);
346 } else {
347 assert_eq!(atomic, 2);
348 }
349 }
350 if detect().test(CpuInfo::HAS_LSE2) {
351 assert_eq!(extract(aa64mmfr2, 35, 32), 1);
352 }
353 if detect().test(CpuInfo::HAS_RCPC3) {
354 assert_eq!(extract(aa64isar1, 23, 20), 3);
355 }
356 }
357
358 #[allow(clippy::cast_possible_wrap)]
359 #[cfg(target_os = "netbsd")]
360 #[test]
test_netbsd()361 fn test_netbsd() {
362 use c_types::*;
363 use imp::ffi;
364 #[cfg(not(portable_atomic_no_asm))]
365 use std::arch::asm;
366 use std::{mem, ptr, vec, vec::Vec};
367 use test_helper::sys;
368
369 // Call syscall using asm instead of libc.
370 // Note that NetBSD does not guarantee the stability of raw syscall as
371 // much as Linux does (It may actually be stable enough, though: https://lists.llvm.org/pipermail/llvm-dev/2019-June/133393.html).
372 //
373 // This is currently used only for testing.
374 unsafe fn sysctl_cpu_id_asm_syscall(name: &[&[u8]]) -> Result<AA64Reg, c_int> {
375 // https://github.com/golang/go/blob/4badad8d477ffd7a6b762c35bc69aed82faface7/src/syscall/asm_netbsd_arm64.s
376 #[inline]
377 unsafe fn sysctl(
378 name: *const c_int,
379 name_len: c_uint,
380 old_p: *mut c_void,
381 old_len_p: *mut c_size_t,
382 new_p: *const c_void,
383 new_len: c_size_t,
384 ) -> Result<c_int, c_int> {
385 // SAFETY: the caller must uphold the safety contract.
386 unsafe {
387 let mut n = sys::SYS___sysctl as u64;
388 let r: i64;
389 asm!(
390 "svc 0",
391 "b.cc 2f",
392 "mov x17, x0",
393 "mov x0, #-1",
394 "2:",
395 inout("x17") n,
396 inout("x0") ptr_reg!(name) => r,
397 inout("x1") name_len as u64 => _,
398 in("x2") ptr_reg!(old_p),
399 in("x3") ptr_reg!(old_len_p),
400 in("x4") ptr_reg!(new_p),
401 in("x5") new_len as u64,
402 options(nostack),
403 );
404 #[allow(clippy::cast_possible_truncation)]
405 if r as c_int == -1 {
406 Err(n as c_int)
407 } else {
408 Ok(r as c_int)
409 }
410 }
411 }
412
413 // https://github.com/golang/sys/blob/4badad8d477ffd7a6b762c35bc69aed82faface7/cpu/cpu_netbsd_arm64.go.
414 fn sysctl_nodes(mib: &mut Vec<i32>) -> Result<Vec<sys::sysctlnode>, i32> {
415 mib.push(sys::CTL_QUERY);
416 let mut q_node = sys::sysctlnode {
417 sysctl_flags: sys::SYSCTL_VERS_1,
418 ..unsafe { mem::zeroed() }
419 };
420 let qp = (&mut q_node as *mut sys::sysctlnode).cast::<ffi::c_void>();
421 let sz = mem::size_of::<sys::sysctlnode>();
422 let mut olen = 0;
423 #[allow(clippy::cast_possible_truncation)]
424 let mib_len = mib.len() as c_uint;
425 unsafe {
426 sysctl(mib.as_ptr(), mib_len, ptr::null_mut(), &mut olen, qp, sz)?;
427 }
428
429 let mut nodes = Vec::<sys::sysctlnode>::with_capacity(olen / sz);
430 let np = nodes.as_mut_ptr().cast::<ffi::c_void>();
431 unsafe {
432 sysctl(mib.as_ptr(), mib_len, np, &mut olen, qp, sz)?;
433 nodes.set_len(olen / sz);
434 }
435
436 mib.pop(); // pop CTL_QUERY
437 Ok(nodes)
438 }
439 fn name_to_mib(parts: &[&[u8]]) -> Result<Vec<i32>, i32> {
440 let mut mib = vec![];
441 for (part_no, &part) in parts.iter().enumerate() {
442 let nodes = sysctl_nodes(&mut mib)?;
443 for node in nodes {
444 let mut n = vec![];
445 for b in node.sysctl_name {
446 if b != 0 {
447 n.push(b);
448 }
449 }
450 if n == part {
451 mib.push(node.sysctl_num);
452 break;
453 }
454 }
455 if mib.len() != part_no + 1 {
456 return Err(0);
457 }
458 }
459
460 Ok(mib)
461 }
462
463 const OUT_LEN: ffi::c_size_t =
464 core::mem::size_of::<ffi::aarch64_sysctl_cpu_id>() as ffi::c_size_t;
465
466 let mib = name_to_mib(name)?;
467
468 let mut buf: ffi::aarch64_sysctl_cpu_id = unsafe { core::mem::zeroed() };
469 let mut out_len = OUT_LEN;
470 #[allow(clippy::cast_possible_truncation)]
471 let mib_len = mib.len() as c_uint;
472 unsafe {
473 sysctl(
474 mib.as_ptr(),
475 mib_len,
476 (&mut buf as *mut ffi::aarch64_sysctl_cpu_id).cast::<ffi::c_void>(),
477 &mut out_len,
478 ptr::null_mut(),
479 0,
480 )?;
481 }
482 Ok(AA64Reg {
483 aa64isar0: buf.aa64isar0,
484 aa64isar1: buf.aa64isar1,
485 aa64mmfr2: buf.aa64mmfr2,
486 })
487 }
488
489 unsafe {
490 assert_eq!(
491 imp::sysctl_cpu_id(b"machdep.cpu0.cpu_id\0").unwrap(),
492 sysctl_cpu_id_asm_syscall(&[b"machdep", b"cpu0", b"cpu_id"]).unwrap()
493 );
494 }
495 }
496
497 // Static assertions for FFI bindings.
498 // This checks that FFI bindings defined in this crate, FFI bindings defined
499 // in libc, and FFI bindings generated for the platform's latest header file
500 // using bindgen have compatible signatures (or the same values if constants).
501 // Since this is static assertion, we can detect problems with
502 // `cargo check --tests --target <target>` run in CI (via TESTS=1 build.sh)
503 // without actually running tests on these platforms.
504 // See also tools/codegen/src/ffi.rs.
505 // TODO(codegen): auto-generate this test
506 #[cfg(target_os = "netbsd")]
507 #[allow(
508 clippy::cast_possible_wrap,
509 clippy::cast_sign_loss,
510 clippy::no_effect_underscore_binding,
511 clippy::used_underscore_binding
512 )]
513 const _: fn() = || {
514 use imp::ffi;
515 use std::mem;
516 use test_helper::{libc, sys};
517 let mut _sysctlbyname: unsafe extern "C" fn(
518 *const ffi::c_char,
519 *mut ffi::c_void,
520 *mut ffi::c_size_t,
521 *const ffi::c_void,
522 ffi::c_size_t,
523 ) -> ffi::c_int = ffi::sysctlbyname;
524 _sysctlbyname = libc::sysctlbyname;
525 _sysctlbyname = sys::sysctlbyname;
526 // libc doesn't have this
527 // static_assert!(
528 // mem::size_of::<ffi::aarch64_sysctl_cpu_id>()
529 // == mem::size_of::<libc::aarch64_sysctl_cpu_id>()
530 // );
531 static_assert!(
532 mem::size_of::<ffi::aarch64_sysctl_cpu_id>()
533 == mem::size_of::<sys::aarch64_sysctl_cpu_id>()
534 );
535 let ffi: ffi::aarch64_sysctl_cpu_id = unsafe { mem::zeroed() };
536 let _ = sys::aarch64_sysctl_cpu_id {
537 ac_midr: ffi.midr,
538 ac_revidr: ffi.revidr,
539 ac_mpidr: ffi.mpidr,
540 ac_aa64dfr0: ffi.aa64dfr0,
541 ac_aa64dfr1: ffi.aa64dfr1,
542 ac_aa64isar0: ffi.aa64isar0,
543 ac_aa64isar1: ffi.aa64isar1,
544 ac_aa64mmfr0: ffi.aa64mmfr0,
545 ac_aa64mmfr1: ffi.aa64mmfr1,
546 ac_aa64mmfr2: ffi.aa64mmfr2,
547 ac_aa64pfr0: ffi.aa64pfr0,
548 ac_aa64pfr1: ffi.aa64pfr1,
549 ac_aa64zfr0: ffi.aa64zfr0,
550 ac_mvfr0: ffi.mvfr0,
551 ac_mvfr1: ffi.mvfr1,
552 ac_mvfr2: ffi.mvfr2,
553 ac_pad: ffi.pad,
554 ac_clidr: ffi.clidr,
555 ac_ctr: ffi.ctr,
556 };
557 };
558 #[cfg(target_os = "openbsd")]
559 #[allow(
560 clippy::cast_possible_wrap,
561 clippy::cast_sign_loss,
562 clippy::no_effect_underscore_binding
563 )]
564 const _: fn() = || {
565 use imp::ffi;
566 use test_helper::{libc, sys};
567 let mut _sysctl: unsafe extern "C" fn(
568 *const ffi::c_int,
569 ffi::c_uint,
570 *mut ffi::c_void,
571 *mut ffi::c_size_t,
572 *mut ffi::c_void,
573 ffi::c_size_t,
574 ) -> ffi::c_int = ffi::sysctl;
575 _sysctl = libc::sysctl;
576 _sysctl = sys::sysctl;
577 static_assert!(ffi::CTL_MACHDEP == libc::CTL_MACHDEP);
578 static_assert!(ffi::CTL_MACHDEP == sys::CTL_MACHDEP as ffi::c_int);
579 // static_assert!(ffi::CPU_ID_AA64ISAR0 == libc::CPU_ID_AA64ISAR0); // libc doesn't have this
580 static_assert!(ffi::CPU_ID_AA64ISAR0 == sys::CPU_ID_AA64ISAR0 as ffi::c_int);
581 // static_assert!(ffi::CPU_ID_AA64ISAR1 == libc::CPU_ID_AA64ISAR1); // libc doesn't have this
582 static_assert!(ffi::CPU_ID_AA64ISAR1 == sys::CPU_ID_AA64ISAR1 as ffi::c_int);
583 // static_assert!(ffi::CPU_ID_AA64MMFR2 == libc::CPU_ID_AA64MMFR2); // libc doesn't have this
584 static_assert!(ffi::CPU_ID_AA64MMFR2 == sys::CPU_ID_AA64MMFR2 as ffi::c_int);
585 };
586 }
587