Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove raw-cpuid dependency and use rdrand intrinsics #85

Merged
merged 1 commit into from
Jul 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
83 changes: 48 additions & 35 deletions src/instructions/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,77 @@ pub struct RdRand(());
impl RdRand {
/// Creates Some(RdRand) if RDRAND is supported, None otherwise
pub fn new() -> Option<Self> {
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
}
}

/// Uniformly sampled u64.
/// May fail in rare circumstances or heavy load.
#[inline]
pub fn get_u64(&self) -> Option<u64> {
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<u32> {
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<u16> {
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());
}
}
}