| pub fn fill_bytes(bytes: &mut [u8]) { | |
| // Handle zero-byte request | |
| if bytes.is_empty() { | |
| return; | |
| } | |
| // Try EFI_RNG_PROTOCOL | |
| if rng_protocol::fill_bytes(bytes) { | |
| return; | |
| } | |
| // Fallback to rdrand if rng protocol missing. | |
| // | |
| // For real-world example, see [issue-13825](https://github.com/rust-lang/rust/issues/138252#issuecomment-2891270323) | |
| #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] | |
| if rdrand::fill_bytes(bytes) { | |
| return; | |
| } | |
| panic!("failed to generate random data"); | |
| } | |
| mod rng_protocol { | |
| use r_efi::protocols::rng; | |
| use crate::sys::pal::helpers; | |
| pub(crate) fn fill_bytes(bytes: &mut [u8]) -> bool { | |
| if let Ok(handles) = helpers::locate_handles(rng::PROTOCOL_GUID) { | |
| for handle in handles { | |
| if let Ok(protocol) = | |
| helpers::open_protocol::<rng::Protocol>(handle, rng::PROTOCOL_GUID) | |
| { | |
| let r = unsafe { | |
| ((*protocol.as_ptr()).get_rng)( | |
| protocol.as_ptr(), | |
| crate::ptr::null_mut(), | |
| bytes.len(), | |
| bytes.as_mut_ptr(), | |
| ) | |
| }; | |
| if r.is_error() { | |
| continue; | |
| } else { | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| false | |
| } | |
| } | |
| /// Port from [getrandom](https://github.com/rust-random/getrandom/blob/master/src/backends/rdrand.rs) | |
| #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] | |
| mod rdrand { | |
| cfg_select! { | |
| target_arch = "x86_64" => { | |
| use crate::arch::x86_64 as arch; | |
| use arch::_rdrand64_step as rdrand_step; | |
| type Word = u64; | |
| } | |
| target_arch = "x86" => { | |
| use crate::arch::x86 as arch; | |
| use arch::_rdrand32_step as rdrand_step; | |
| type Word = u32; | |
| } | |
| } | |
| static RDRAND_GOOD: crate::sync::LazyLock<bool> = crate::sync::LazyLock::new(is_rdrand_good); | |
| // Recommendation from "Intel® Digital Random Number Generator (DRNG) Software | |
| // Implementation Guide" - Section 5.2.1 and "Intel® 64 and IA-32 Architectures | |
| // Software Developer’s Manual" - Volume 1 - Section 7.3.17.1. | |
| const RETRY_LIMIT: usize = 10; | |
| unsafe fn rdrand() -> Option<Word> { | |
| for _ in 0..RETRY_LIMIT { | |
| let mut val = 0; | |
| if unsafe { rdrand_step(&mut val) } == 1 { | |
| return Some(val); | |
| } | |
| } | |
| None | |
| } | |
| // Run a small self-test to make sure we aren't repeating values | |
| // Adapted from Linux's test in arch/x86/kernel/cpu/rdrand.c | |
| // Fails with probability < 2^(-90) on 32-bit systems | |
| unsafe fn self_test() -> bool { | |
| // On AMD, RDRAND returns 0xFF...FF on failure, count it as a collision. | |
| let mut prev = Word::MAX; | |
| let mut fails = 0; | |
| for _ in 0..8 { | |
| match unsafe { rdrand() } { | |
| Some(val) if val == prev => fails += 1, | |
| Some(val) => prev = val, | |
| None => return false, | |
| }; | |
| } | |
| fails <= 2 | |
| } | |
| fn is_rdrand_good() -> bool { | |
| #[cfg(not(target_feature = "rdrand"))] | |
| { | |
| // SAFETY: All Rust x86 targets are new enough to have CPUID, and we | |
| // check that leaf 1 is supported before using it. | |
| let cpuid0 = arch::__cpuid(0); | |
| if cpuid0.eax < 1 { | |
| return false; | |
| } | |
| let cpuid1 = arch::__cpuid(1); | |
| let vendor_id = | |
| [cpuid0.ebx.to_le_bytes(), cpuid0.edx.to_le_bytes(), cpuid0.ecx.to_le_bytes()]; | |
| if vendor_id == [*b"Auth", *b"enti", *b"cAMD"] { | |
| let mut family = (cpuid1.eax >> 8) & 0xF; | |
| if family == 0xF { | |
| family += (cpuid1.eax >> 20) & 0xFF; | |
| } | |
| // AMD CPUs families before 17h (Zen) sometimes fail to set CF when | |
| // RDRAND fails after suspend. Don't use RDRAND on those families. | |
| // See https://bugzilla.redhat.com/show_bug.cgi?id=1150286 | |
| if family < 0x17 { | |
| return false; | |
| } | |
| } | |
| const RDRAND_FLAG: u32 = 1 << 30; | |
| if cpuid1.ecx & RDRAND_FLAG == 0 { | |
| return false; | |
| } | |
| } | |
| // SAFETY: We have already checked that rdrand is available. | |
| unsafe { self_test() } | |
| } | |
| unsafe fn rdrand_exact(dest: &mut [u8]) -> Option<()> { | |
| let (chunks, tail) = dest.as_chunks_mut(); | |
| for chunk in chunks { | |
| *chunk = unsafe { rdrand() }?.to_ne_bytes(); | |
| } | |
| let n = tail.len(); | |
| if n > 0 { | |
| let src = unsafe { rdrand() }?.to_ne_bytes(); | |
| tail.copy_from_slice(&src[..n]); | |
| } | |
| Some(()) | |
| } | |
| pub(crate) fn fill_bytes(bytes: &mut [u8]) -> bool { | |
| if *RDRAND_GOOD { unsafe { rdrand_exact(bytes).is_some() } } else { false } | |
| } | |
| } |