-
Notifications
You must be signed in to change notification settings - Fork 182
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rndr: Add support for aarch64 RNDR register backend
AArch64 platforms from version Armv8.4 onwards may implement FEAT_RNG. FEAT_RNG introduces the RNDR (and RNDRRS) register, reading from which returns a random number. Add support for using the RNDR register as a backend for getrandom. The implementation is hidden behind a new "rndr" crate feature. Currently, detecting whether FEAT_RNG is available without std relies on the Linux Kernel's MRS emulation. For that reason the `rndr` implementation is marked as unsafe, because we cannot always detect whether the register is available or not. This commit also 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.
- Loading branch information
1 parent
5edb045
commit badb406
Showing
6 changed files
with
132 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
//! RNDR register backend for aarch64 targets | ||
// Arm Architecture Reference Manual for A-profile architecture | ||
// ARM DDI 0487K.a, ID032224, D23.2.147 RNDR, Random Number | ||
|
||
use crate::{util::slice_as_uninit, Error}; | ||
use core::arch::asm; | ||
use core::mem::{size_of, MaybeUninit}; | ||
|
||
const RETRY_LIMIT: usize = 5; | ||
|
||
// Read a random number from the aarch64 rndr register | ||
// | ||
// Callers must ensure that FEAT_RNG is available on the system | ||
// The function assumes that the RNDR register is available | ||
// If it fails to read a random number, it will retry up to 5 times | ||
// After 5 failed reads the function will return None | ||
#[target_feature(enable = "rand")] | ||
unsafe fn rndr() -> Option<u64> { | ||
for _ in 0..RETRY_LIMIT { | ||
let mut x: u64; | ||
let mut nzcv: u64; | ||
|
||
// AArch64 RNDR register is accessible by s3_3_c2_c4_0 | ||
asm!( | ||
"mrs {x}, RNDR", | ||
"mrs {nzcv}, NZCV", | ||
x = out(reg) x, | ||
nzcv = out(reg) nzcv, | ||
); | ||
|
||
// If the hardware returns a genuine random number, PSTATE.NZCV is set to 0b0000 | ||
if nzcv == 0 { | ||
return Some(x); | ||
} | ||
} | ||
|
||
None | ||
} | ||
|
||
pub unsafe fn getrandom_inner(dest: &mut [MaybeUninit<u8>]) -> Result<(), Error> { | ||
rndr_exact(dest).ok_or(Error::FAILED_RNDR) | ||
} | ||
|
||
#[target_feature(enable = "rand")] | ||
unsafe fn rndr_exact(dest: &mut [MaybeUninit<u8>]) -> Option<()> { | ||
let mut chunks = dest.chunks_exact_mut(size_of::<u64>()); | ||
for chunk in chunks.by_ref() { | ||
let src = rndr()?.to_ne_bytes(); | ||
chunk.copy_from_slice(slice_as_uninit(&src)); | ||
} | ||
|
||
let tail = chunks.into_remainder(); | ||
let n = tail.len(); | ||
if n > 0 { | ||
let src = rndr()?.to_ne_bytes(); | ||
tail.copy_from_slice(slice_as_uninit(&src[..n])); | ||
} | ||
Some(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
//! Linux-only safe RNDR register backend for aarch64 targets with fallback | ||
|
||
use crate::{lazy::LazyBool, linux_android_with_fallback, rndr, Error}; | ||
use core::arch::asm; | ||
use core::mem::MaybeUninit; | ||
|
||
#[cfg(any(target_os = "linux", target_os = "android"))] | ||
// 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 unsafe { rndr::getrandom_inner(dest) }.is_ok() { | ||
Ok(()) | ||
} else { | ||
linux_android_with_fallback::getrandom_inner(dest) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
#![cfg(all(target_os = "linux", target_arch = "aarch64", feature = "rndr"))] | ||
|
||
use getrandom::Error; | ||
#[path = "../src/rndr_with_fallback.rs"] | ||
mod rndr_with_fallback; | ||
#[path = "../src/util.rs"] | ||
mod util; | ||
|
||
fn getrandom_impl(dest: &mut [u8]) -> Result<(), Error> { | ||
rndr_with_fallback::getrandom_inner(unsafe { util::slice_as_uninit_mut(dest) })?; | ||
Ok(()) | ||
} | ||
mod common; |