diff --git a/.travis.yml b/.travis.yml index 0ea94751..3b634285 100644 --- a/.travis.yml +++ b/.travis.yml @@ -106,6 +106,9 @@ matrix: - rustup target add x86_64-unknown-netbsd - rustup target add x86_64-unknown-redox - rustup target add x86_64-fortanix-unknown-sgx + # For no_std targets + - rustup component add rust-src + - cargo install cargo-xbuild || true script: - cargo build --target=x86_64-sun-solaris --all-features - cargo build --target=x86_64-unknown-cloudabi --all-features @@ -114,6 +117,7 @@ matrix: - cargo build --target=x86_64-unknown-netbsd --all-features - cargo build --target=x86_64-unknown-redox --all-features - cargo build --target=x86_64-fortanix-unknown-sgx --all-features + - cargo xbuild --target=x86_64-unknown-uefi # also test minimum dependency versions are usable - cargo generate-lockfile -Z minimal-versions - cargo build --target=x86_64-sun-solaris --all-features @@ -123,6 +127,7 @@ matrix: - cargo build --target=x86_64-unknown-netbsd --all-features - cargo build --target=x86_64-unknown-redox --all-features - cargo build --target=x86_64-fortanix-unknown-sgx --all-features + - cargo xbuild --target=x86_64-unknown-uefi # Trust cross-built/emulated targets. We must repeat all non-default values. - rust: stable diff --git a/Cargo.toml b/Cargo.toml index abb2d7c2..7cb539da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,5 +39,8 @@ stdweb = { version = "0.4.9", optional = true } [target.wasm32-wasi.dependencies] libc = "0.2.54" +[target.'cfg(any(target_env = "sgx", target_os = "uefi"))'.dependencies] +lazy_static = { version = "1.3.0", features = ["spin_no_std"] } + [features] std = [] diff --git a/src/lib.rs b/src/lib.rs index f2189651..32ccfdf2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -190,6 +190,7 @@ mod_use!(cfg(target_os = "redox"), use_file); mod_use!(cfg(target_os = "solaris"), solaris_illumos); mod_use!(cfg(windows), windows); mod_use!(cfg(target_env = "sgx"), rdrand); +mod_use!(cfg(all(target_arch = "x86_64", target_os = "uefi")), rdrand); mod_use!(cfg(target_os = "wasi"), wasi); mod_use!( @@ -231,6 +232,7 @@ mod_use!( target_os = "openbsd", target_os = "redox", target_os = "solaris", + all(target_arch = "x86_64", target_os = "uefi"), target_env = "sgx", windows, all( diff --git a/src/rdrand.rs b/src/rdrand.rs index 0c45f48b..8bd7a1de 100644 --- a/src/rdrand.rs +++ b/src/rdrand.rs @@ -9,11 +9,9 @@ //! Implementation for SGX using RDRAND instruction use crate::Error; use core::mem; -use core::arch::x86_64::_rdrand64_step; +use core::arch::x86_64::{__cpuid, _rdrand64_step}; use core::num::NonZeroU32; - -#[cfg(not(target_feature = "rdrand"))] -compile_error!("enable rdrand target feature!"); +use lazy_static::lazy_static; // Recommendation from "IntelĀ® Digital Random Number Generator (DRNG) Software // Implementation Guide" - Section 5.2.1 and "IntelĀ® 64 and IA-32 Architectures @@ -21,21 +19,51 @@ compile_error!("enable rdrand target feature!"); const RETRY_LIMIT: usize = 10; const WORD_SIZE: usize = mem::size_of::(); -fn rdrand() -> Result<[u8; WORD_SIZE], Error> { +#[target_feature(enable = "rdrand")] +unsafe fn rdrand() -> Result<[u8; WORD_SIZE], Error> { for _ in 0..RETRY_LIMIT { - unsafe { - // SAFETY: we've checked RDRAND support, and u64 can have any value. - let mut el = mem::uninitialized(); - if _rdrand64_step(&mut el) == 1 { - return Ok(el.to_ne_bytes()); - } - }; + let mut el = mem::uninitialized(); + if _rdrand64_step(&mut el) == 1 { + return Ok(el.to_ne_bytes()); + } } error!("RDRAND failed, CPU issue likely"); Err(Error::UNKNOWN) } +// "rdrand" target feature requires "+rdrnd" flag, see https://github.com/rust-lang/rust/issues/49653. +#[cfg(all(target_env = "sgx", not(target_feature = "rdrand")))] +compile_error!( + "SGX targets require 'rdrand' target feature. Enable by using -C target-feature=+rdrnd." +); + +// TODO use is_x86_feature_detected!("rdrand") when that works in core. See: +// https://github.com/rust-lang-nursery/stdsimd/issues/464 +fn is_rdrand_supported() -> bool { + if cfg!(target_feature = "rdrand") { + true + } else { + // SAFETY: All x86_64 CPUs support CPUID leaf 1 + const FLAG: u32 = 1 << 30; + lazy_static! { + static ref HAS_RDRAND: bool = unsafe { __cpuid(1).ecx & FLAG != 0 }; + } + *HAS_RDRAND + } +} + pub fn getrandom_inner(dest: &mut [u8]) -> Result<(), Error> { + if !is_rdrand_supported() { + return Err(Error::UNAVAILABLE); + } + + // SAFETY: After this point, rdrand is supported, so calling the rdrand + // functions is not undefined behavior. + unsafe { rdrand_exact(dest) } +} + +#[target_feature(enable = "rdrand")] +unsafe fn rdrand_exact(dest: &mut [u8]) -> Result<(), Error> { // We use chunks_exact_mut instead of chunks_mut as it allows almost all // calls to memcpy to be elided by the compiler. let mut chunks = dest.chunks_exact_mut(WORD_SIZE);