Skip to content

Commit

Permalink
rndr_with_fallback: Add a RNDR backend with a Linux fallback
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mrkajetanp committed Jul 30, 2024
1 parent 6783b87 commit f3c5244
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 1 deletion.
2 changes: 1 addition & 1 deletion src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl Error {
///
/// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.from_raw_os_error
#[allow(dead_code)]
pub(super) fn from_os_error(code: u32) -> Self {
pub fn from_os_error(code: u32) -> Self {
match NonZeroU32::new(code) {
Some(code) if code.get() < Self::INTERNAL_START => Self(code),
_ => Self::UNEXPECTED,
Expand Down
49 changes: 49 additions & 0 deletions src/rndr_with_fallback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//! Linux-only safe RNDR register backend for aarch64 targets with fallback
#[cfg(any(
not(any(target_os = "linux", target_os = "android")),
not(target_feature = "rand"),
not(target_arch = "aarch64")
))]
compile_error!(
"The rndr_with_fallback backend requires the `rand` target feature to be enabled
at compile time and can only be built for Linux or Android."
);

use crate::{lazy::LazyBool, linux_android_with_fallback, rndr, Error};
use core::arch::asm;
use core::mem::MaybeUninit;

// Check whether FEAT_RNG is available on the system
//
// Requires the caller either be running in EL1 or be on a system supporting MRS emulation.
// Due to the above, the implementation is currently restricted to Linux.
fn is_rndr_available() -> bool {
let mut id_aa64isar0: u64;

// If FEAT_RNG is implemented, ID_AA64ISAR0_EL1.RNDR (bits 60-63) are 0b0001
// This is okay to do from EL0 in Linux because Linux will emulate MRS as per
// https://docs.kernel.org/arch/arm64/cpu-feature-registers.html
unsafe {
asm!(
"mrs {id}, ID_AA64ISAR0_EL1",
id = out(reg) id_aa64isar0,
);
}

(id_aa64isar0 >> 60) & 0xf >= 1
}

pub fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> {
static RNDR_AVAILABLE: LazyBool = LazyBool::new();
if !RNDR_AVAILABLE.unsync_init(is_rndr_available) {
return Err(Error::NO_RNDR);
}

// We've already checked that RNDR is available
if rndr::getrandom_inner(dest).is_ok() {
Ok(())
} else {
linux_android_with_fallback::getrandom_inner(dest)
}
}
32 changes: 32 additions & 0 deletions tests/rndr_with_fallback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#![cfg(all(
target_os = "linux",
target_arch = "aarch64",
feature = "rndr",
target_feature = "rand"
))]

use getrandom::Error;
#[macro_use]
extern crate cfg_if;
#[path = "../src/lazy.rs"]
mod lazy;
#[path = "../src/linux_android.rs"]
mod linux_android;
#[path = "../src/linux_android_with_fallback.rs"]
mod linux_android_with_fallback;
#[path = "../src/rndr.rs"]
mod rndr;
#[path = "../src/rndr_with_fallback.rs"]
mod rndr_with_fallback;
#[path = "../src/use_file.rs"]
mod use_file;
#[path = "../src/util.rs"]
mod util;
#[path = "../src/util_libc.rs"]
mod util_libc;

fn getrandom_impl(dest: &mut [u8]) -> Result<(), Error> {
rndr_with_fallback::getrandom_inner(unsafe { util::slice_as_uninit_mut(dest) })?;
Ok(())
}
mod common;

0 comments on commit f3c5244

Please sign in to comment.