Skip to content

Commit f3c5244

Browse files
committed
rndr_with_fallback: Add a RNDR backend with a Linux fallback
Currently, detecting whether FEAT_RNG is available without std relies on the Linux Kernel's MRS emulation. This commit adds a safe rndr_with_fallback backend for Linux systems. With this backend, getrandom will use the RNDR register on Linux systems where it is available and automatically fallback onto using Linux's getrandom syscall on systems where it is not. This implementation allows the crate to be build for Linux with this feature in advance and then run without having to know whether FEAT_RNG is implemented or not. For the time being, this backend is not used by default on any platform configuration. The intention is for it to be usable as an opt-in when an opt-in mechanism is available in the crate.
1 parent 6783b87 commit f3c5244

3 files changed

Lines changed: 82 additions & 1 deletion

File tree

src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ impl Error {
8282
///
8383
/// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.from_raw_os_error
8484
#[allow(dead_code)]
85-
pub(super) fn from_os_error(code: u32) -> Self {
85+
pub fn from_os_error(code: u32) -> Self {
8686
match NonZeroU32::new(code) {
8787
Some(code) if code.get() < Self::INTERNAL_START => Self(code),
8888
_ => Self::UNEXPECTED,

src/rndr_with_fallback.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//! Linux-only safe RNDR register backend for aarch64 targets with fallback
2+
3+
#[cfg(any(
4+
not(any(target_os = "linux", target_os = "android")),
5+
not(target_feature = "rand"),
6+
not(target_arch = "aarch64")
7+
))]
8+
compile_error!(
9+
"The rndr_with_fallback backend requires the `rand` target feature to be enabled
10+
at compile time and can only be built for Linux or Android."
11+
);
12+
13+
use crate::{lazy::LazyBool, linux_android_with_fallback, rndr, Error};
14+
use core::arch::asm;
15+
use core::mem::MaybeUninit;
16+
17+
// Check whether FEAT_RNG is available on the system
18+
//
19+
// Requires the caller either be running in EL1 or be on a system supporting MRS emulation.
20+
// Due to the above, the implementation is currently restricted to Linux.
21+
fn is_rndr_available() -> bool {
22+
let mut id_aa64isar0: u64;
23+
24+
// If FEAT_RNG is implemented, ID_AA64ISAR0_EL1.RNDR (bits 60-63) are 0b0001
25+
// This is okay to do from EL0 in Linux because Linux will emulate MRS as per
26+
// https://docs.kernel.org/arch/arm64/cpu-feature-registers.html
27+
unsafe {
28+
asm!(
29+
"mrs {id}, ID_AA64ISAR0_EL1",
30+
id = out(reg) id_aa64isar0,
31+
);
32+
}
33+
34+
(id_aa64isar0 >> 60) & 0xf >= 1
35+
}
36+
37+
pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
38+
static RNDR_AVAILABLE: LazyBool = LazyBool::new();
39+
if !RNDR_AVAILABLE.unsync_init(is_rndr_available) {
40+
return Err(Error::NO_RNDR);
41+
}
42+
43+
// We've already checked that RNDR is available
44+
if rndr::getrandom_inner(dest).is_ok() {
45+
Ok(())
46+
} else {
47+
linux_android_with_fallback::getrandom_inner(dest)
48+
}
49+
}

tests/rndr_with_fallback.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#![cfg(all(
2+
target_os = "linux",
3+
target_arch = "aarch64",
4+
feature = "rndr",
5+
target_feature = "rand"
6+
))]
7+
8+
use getrandom::Error;
9+
#[macro_use]
10+
extern crate cfg_if;
11+
#[path = "../src/lazy.rs"]
12+
mod lazy;
13+
#[path = "../src/linux_android.rs"]
14+
mod linux_android;
15+
#[path = "../src/linux_android_with_fallback.rs"]
16+
mod linux_android_with_fallback;
17+
#[path = "../src/rndr.rs"]
18+
mod rndr;
19+
#[path = "../src/rndr_with_fallback.rs"]
20+
mod rndr_with_fallback;
21+
#[path = "../src/use_file.rs"]
22+
mod use_file;
23+
#[path = "../src/util.rs"]
24+
mod util;
25+
#[path = "../src/util_libc.rs"]
26+
mod util_libc;
27+
28+
fn getrandom_impl(dest: &mut [u8]) -> Result<(), Error> {
29+
rndr_with_fallback::getrandom_inner(unsafe { util::slice_as_uninit_mut(dest) })?;
30+
Ok(())
31+
}
32+
mod common;

0 commit comments

Comments
 (0)