include!("common.rs");
use core::ptr;
#[allow(non_camel_case_types, non_upper_case_globals)]
mod ffi {
pub(crate) use crate::utils::ffi::{c_long, c_size_t, c_uint, c_ulong};
sys_struct!({
pub(crate) struct riscv_hwprobe {
pub(crate) key: i64,
pub(crate) value: u64,
}
});
sys_const!({
pub(crate) const __NR_riscv_hwprobe: c_long = 258;
pub(crate) const RISCV_HWPROBE_KEY_IMA_EXT_0: i64 = 4;
pub(crate) const RISCV_HWPROBE_EXT_ZACAS: u64 = 1 << 34;
});
#[cfg(not(all(
target_os = "linux",
any(target_arch = "riscv32", all(target_arch = "riscv64", target_pointer_width = "64")),
)))]
extern "C" {
pub(crate) fn syscall(number: c_long, ...) -> c_long;
}
#[cfg(all(
target_os = "linux", // https://github.com/bytecodealliance/rustix/issues/1095
any(target_arch = "riscv32", all(target_arch = "riscv64", target_pointer_width = "64")),
))]
#[inline]
pub(crate) unsafe fn syscall(
number: c_long,
a0: *mut riscv_hwprobe,
a1: c_size_t,
a2: c_size_t,
a3: *mut c_ulong,
a4: c_uint,
) -> c_long {
#[cfg(not(portable_atomic_no_asm))]
use core::arch::asm;
let a4 = a4 as usize;
let r;
unsafe {
asm!(
"ecall",
in("a7") number,
inout("a0") a0 => r,
in("a1") a1,
in("a2") a2,
in("a3") a3,
in("a4") a4,
options(nostack, preserves_flags)
);
}
r
}
pub(crate) unsafe fn __riscv_hwprobe(
pairs: *mut riscv_hwprobe,
pair_count: c_size_t,
cpu_set_size: c_size_t,
cpus: *mut c_ulong,
flags: c_uint,
) -> c_long {
unsafe { syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_set_size, cpus, flags) }
}
}
fn riscv_hwprobe(out: &mut ffi::riscv_hwprobe) -> bool {
unsafe { ffi::__riscv_hwprobe(out, 1, 0, ptr::null_mut(), 0) == 0 }
}
#[cold]
fn _detect(info: &mut CpuInfo) {
let mut out = ffi::riscv_hwprobe { key: ffi::RISCV_HWPROBE_KEY_IMA_EXT_0, value: 0 };
if riscv_hwprobe(&mut out) && out.key != -1 {
let value = out.value;
if value & ffi::RISCV_HWPROBE_EXT_ZACAS != 0 {
info.set(CpuInfo::HAS_ZACAS);
}
}
}
#[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 super::*;
#[test]
fn test_alternative() {
unsafe fn __riscv_hwprobe_libc(
pairs: *mut ffi::riscv_hwprobe,
pair_count: ffi::c_size_t,
cpu_set_size: ffi::c_size_t,
cpus: *mut ffi::c_ulong,
flags: ffi::c_uint,
) -> ffi::c_long {
unsafe {
libc::syscall(ffi::__NR_riscv_hwprobe, pairs, pair_count, cpu_set_size, cpus, flags)
}
}
fn riscv_hwprobe_libc(out: &mut ffi::riscv_hwprobe) -> bool {
unsafe { __riscv_hwprobe_libc(out, 1, 0, ptr::null_mut(), 0) == 0 }
}
let mut out = ffi::riscv_hwprobe { key: ffi::RISCV_HWPROBE_KEY_IMA_EXT_0, value: 0 };
let mut libc_out = ffi::riscv_hwprobe { key: ffi::RISCV_HWPROBE_KEY_IMA_EXT_0, value: 0 };
assert_eq!(riscv_hwprobe(&mut out), riscv_hwprobe_libc(&mut libc_out));
assert_eq!(out, libc_out);
}
const _: fn() = || {
#[cfg(not(all(
target_os = "linux",
any(
target_arch = "riscv32",
all(target_arch = "riscv64", target_pointer_width = "64"),
),
)))]
{
use test_helper::sys;
let mut _syscall: unsafe extern "C" fn(num: ffi::c_long, ...) -> ffi::c_long =
ffi::syscall;
_syscall = libc::syscall;
_syscall = sys::syscall;
}
};
}