use super::ArchError;
pub fn fill_hardware_entropy(buf: &mut [u8]) -> Result<(), ArchError> {
for chunk in buf.chunks_mut(8) {
let mut value: u64 = 0;
let mut success: bool = false;
for _ in 0..10 {
unsafe {
let nzcv: u64;
core::arch::asm!(
"mrs {0}, s3_3_c2_c4_0", "mrs {1}, nzcv",
out(reg) value,
out(reg) nzcv,
options(nomem, nostack),
);
success = (nzcv & (1 << 30)) == 0;
}
if success {
let bytes = value.to_le_bytes();
let len = chunk.len().min(8);
chunk[..len].copy_from_slice(&bytes[..len]);
break;
}
core::hint::spin_loop();
}
if !success {
return Err(ArchError::HardwareUnavailable);
}
}
Ok(())
}
pub fn fill_seed_entropy(buf: &mut [u8]) -> Result<(), ArchError> {
for chunk in buf.chunks_mut(8) {
let mut value: u64 = 0;
let mut success: bool = false;
for _ in 0..100 {
unsafe {
let nzcv: u64;
core::arch::asm!(
"mrs {0}, s3_3_c2_c4_1", "mrs {1}, nzcv",
out(reg) value,
out(reg) nzcv,
options(nomem, nostack),
);
success = (nzcv & (1 << 30)) == 0;
}
if success {
let bytes = value.to_le_bytes();
let len = chunk.len().min(8);
chunk[..len].copy_from_slice(&bytes[..len]);
break;
}
core::hint::spin_loop();
}
if !success {
return Err(ArchError::HardwareUnavailable);
}
}
Ok(())
}
#[inline]
pub fn get_timestamp() -> u64 {
let value: u64;
unsafe {
core::arch::asm!(
"mrs {0}, cntvct_el0",
out(reg) value,
options(nomem, nostack, preserves_flags),
);
}
value
}
#[cfg(target_os = "linux")]
pub unsafe fn syscall3(num: i64, arg1: usize, arg2: usize, arg3: usize) -> i64 {
let ret: i64;
core::arch::asm!(
"svc #0",
in("x8") num,
inout("x0") arg1 => ret,
in("x1") arg2,
in("x2") arg3,
options(nostack),
);
ret as i64
}
#[inline]
pub fn has_rdrand() -> bool {
let mut test_val: u64 = 0;
let nzcv: u64;
unsafe {
core::arch::asm!(
"mrs {0}, s3_3_c2_c4_0",
"mrs {1}, nzcv",
out(reg) test_val,
out(reg) nzcv,
options(nomem, nostack),
);
}
let _ = test_val; true }
#[inline]
pub fn has_rdseed() -> bool {
has_rdrand() }
#[inline]
pub fn has_aesni() -> bool {
cfg!(target_feature = "aes")
}
#[inline]
pub fn has_pclmulqdq() -> bool {
cfg!(target_feature = "aes") }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_timestamp() {
let t1 = get_timestamp();
let t2 = get_timestamp();
assert!(t2 >= t1); }
#[test]
fn test_entropy_if_available() {
let mut buf = [0u8; 32];
if fill_hardware_entropy(&mut buf).is_ok() {
assert_ne!(buf, [0u8; 32]);
}
}
}