diff --git a/Cargo.toml b/Cargo.toml index f33c944ea..dcef61ad3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,9 +34,6 @@ array-init = "0.0.4" version = "0.2.2" default-features = false -[target.'cfg(target_arch = "x86_64")'.dependencies] -raw-cpuid = "6.1.0" - [dependencies.ux] default-features = false version = "0.1.3" diff --git a/src/instructions/random.rs b/src/instructions/random.rs index c5973f8b6..17fe95b1c 100644 --- a/src/instructions/random.rs +++ b/src/instructions/random.rs @@ -8,15 +8,13 @@ pub struct RdRand(()); impl RdRand { /// Creates Some(RdRand) if RDRAND is supported, None otherwise pub fn new() -> Option { - let cpuid = raw_cpuid::CpuId::new(); - let has_rdrand = match cpuid.get_feature_info() { - Some(finfo) => finfo.has_rdrand(), - None => false, - }; - - match has_rdrand { - true => Some(RdRand(())), - false => None, + // RDRAND support indicated by CPUID page 01h, ecx bit 30 + // https://en.wikipedia.org/wiki/RdRand#Overview + let cpuid = unsafe { core::arch::x86_64::__cpuid(0x1) }; + if cpuid.ecx & (1 << 30) != 0 { + Some(RdRand(())) + } else { + None } } @@ -24,48 +22,63 @@ impl RdRand { /// May fail in rare circumstances or heavy load. #[inline] pub fn get_u64(&self) -> Option { - let res: u64; - let ok: u8; + let mut res: u64 = 0; unsafe { - asm!("rdrand %rax; setc $1;" - : "={rax}"(res) "=r"(ok) - :: "flags" : "volatile"); - } - match ok { - 1 => Some(res), - _ => None, + match core::arch::x86_64::_rdrand64_step(&mut res) { + 1 => Some(res), + x => { + debug_assert_eq!(x, 0, "rdrand64 returned non-binary value"); + None + } + } } } /// Uniformly sampled u32. /// May fail in rare circumstances or heavy load. #[inline] pub fn get_u32(&self) -> Option { - let res: u32; - let ok: u8; + let mut res: u32 = 0; unsafe { - asm!("rdrand %eax; setc $1;" - : "={eax}"(res) "=r"(ok) - :: "flags" : "volatile"); - } - match ok { - 1 => Some(res), - _ => None, + match core::arch::x86_64::_rdrand32_step(&mut res) { + 1 => Some(res), + x => { + debug_assert_eq!(x, 0, "rdrand32 returned non-binary value"); + None + } + } } } /// Uniformly sampled u16. /// May fail in rare circumstances or heavy load. #[inline] pub fn get_u16(&self) -> Option { - let res: u16; - let ok: u8; + let mut res: u16 = 0; unsafe { - asm!("rdrand %ax; setc $1;" - : "={ax}"(res) "=r"(ok) - :: "flags" : "volatile"); + match core::arch::x86_64::_rdrand16_step(&mut res) { + 1 => Some(res), + x => { + debug_assert_eq!(x, 0, "rdrand16 returned non-binary value"); + None + } + } } - match ok { - 1 => Some(res), - _ => None, + } +} + +#[cfg(all(test, target_arch = "x86_64"))] +mod tests { + use super::*; + + #[test] + pub fn test_rdrand() { + let rand = RdRand::new(); + if is_x86_feature_detected!("rdrand") { + let rand = rand.unwrap(); + assert!(rand.get_u16().is_some()); + assert!(rand.get_u32().is_some()); + assert!(rand.get_u64().is_some()); + } else { + assert!(rand.is_none()); } } }