From d2e243b81c5aa300dd0b742b26a37d10ac5ea182 Mon Sep 17 00:00:00 2001 From: Ali Saidi Date: Mon, 9 May 2022 20:50:33 -0500 Subject: [PATCH] Add support for using the Arm v8.5-RNG --- src/asm.rs | 1 + src/asm/random.rs | 111 ++++++++++++++++++++++++++++++ src/registers.rs | 2 + src/registers/id_aa64isar0_el1.rs | 38 ++++++++++ 4 files changed, 152 insertions(+) create mode 100644 src/asm/random.rs create mode 100644 src/registers/id_aa64isar0_el1.rs diff --git a/src/asm.rs b/src/asm.rs index 0e4a942..24ba839 100644 --- a/src/asm.rs +++ b/src/asm.rs @@ -9,6 +9,7 @@ //! Wrappers around ARMv8-A instructions. pub mod barrier; +pub mod random; /// The classic no-op #[inline(always)] diff --git a/src/asm/random.rs b/src/asm/random.rs new file mode 100644 index 0000000..4d91117 --- /dev/null +++ b/src/asm/random.rs @@ -0,0 +1,111 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Copyright (c) 2022 Amazon.com, Inc. or its affiliates. +// +// Author(s): +// - Ali Saidi + + +/// Implement an interface for accessing Arm v8.5 RNG instructions. +/// An empty struct is used to confirm that the system has the +/// instructions available. +/// # Example: +/// ```no_run +/// use cortex_a::asm::random::ArmRng; +/// if let Some(rng) = ArmRng::new() { +/// let rand_num = rng.rndr(); +/// } +/// ``` +#[derive(Copy, Clone, Debug)] +pub struct ArmRng; + +use crate::registers::ID_AA64ISAR0_EL1; +use core::arch::asm; +use tock_registers::interfaces::Readable; + +impl ArmRng { + /// Return an empty object that is used to gate calling + /// rndr and rndrss on discovery of the feature so each + /// call doesn't need to confirm it. + #[inline] + pub fn new() -> Option { + #[cfg(not(target_arch = "aarch64"))] + return None; + + #[cfg(target_arch = "aarch64")] + if ID_AA64ISAR0_EL1.is_set(ID_AA64ISAR0_EL1::RNDR) { + Some(ArmRng) + } else { + None + } + } + + /// Return an random number from the Arm v8.5 RNG. + /// This returns an option because the instruction can fail + /// (e.g. the entropy is exhausted or the RNG has failed.) + #[inline] + pub fn rndr(&self) -> Option { + let mut flags: u64; + let mut data: u64; + + #[cfg(target_arch = "aarch64")] + unsafe { + asm!( + "mrs {o}, s3_3_c2_c4_0", + "mrs {f}, nzcv", + o = out(reg) data, + f = out(reg) flags, + options(nomem, nostack)); + } + if cfg!(not(target_arch = "aarch64")) || flags != 0 { + None + } else { + Some(data) + } + } + + /// Return an random number from the Arm v8.5 RNG after reseeding it + /// This returns an option because the instruction can fail + /// (e.g. the entropy is exhausted or the RNG has failed.) + #[inline] + pub fn rndrss(&self) -> Option { + let mut flags: u64; + let mut data: u64; + + #[cfg(target_arch = "aarch64")] + unsafe { + asm!( + "mrs {o}, s3_3_c2_c4_1", + "mrs {f}, nzcv", + o = out(reg) data, + f = out(reg) flags, + options(nomem, nostack)); + } + + if cfg!(not(target_arch = "aarch64")) || flags != 0 { + None + } else { + Some(data) + } + } + + +} + + +#[cfg(all(test, target_os = "linux"))] +mod tests { + use super::*; + + #[test] + pub fn test_rndr() { + // This works on Linux from userspace since Linux emulatates + // the Arm ID registers on the userspace undef. + if let Some(rand) = ArmRng::new() { + assert!(rand.rndr().unwrap() != 0); + assert!(rand.rndrss().unwrap() != 0); + } + } + +} + diff --git a/src/registers.rs b/src/registers.rs index b805942..8ad25a1 100644 --- a/src/registers.rs +++ b/src/registers.rs @@ -28,6 +28,7 @@ mod far_el2; mod fp; mod hcr_el2; mod id_aa64mmfr0_el1; +mod id_aa64isar0_el1; mod lr; mod mair_el1; mod mair_el2; @@ -78,6 +79,7 @@ pub use far_el2::FAR_EL2; pub use fp::FP; pub use hcr_el2::HCR_EL2; pub use id_aa64mmfr0_el1::ID_AA64MMFR0_EL1; +pub use id_aa64isar0_el1::ID_AA64ISAR0_EL1; pub use lr::LR; pub use mair_el1::MAIR_EL1; pub use mair_el2::MAIR_EL2; diff --git a/src/registers/id_aa64isar0_el1.rs b/src/registers/id_aa64isar0_el1.rs new file mode 100644 index 0000000..9c61fe1 --- /dev/null +++ b/src/registers/id_aa64isar0_el1.rs @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Copyright (c) 2022 Amazon.com, Inc. or its affiliates. +// +// Author(s): +// - Ali Saidi + +//! AArch64 Instruction Set Architecture Feature Register 0 - EL1 +//! +//! Provides information about the implemented instruction set. + +use tock_registers::{interfaces::Readable, register_bitfields}; + +register_bitfields! {u64, + pub ID_AA64ISAR0_EL1 [ + /// Support for Random Number instructions in AArch64. + /// + /// 0000 No random number instructions are implemented + /// 0001 RNDR and RNDRSS are implemented + /// + /// All other values are reserved. + RNDR OFFSET(60) NUMBITS(4) [ + Supported = 0b0001, + NotSupported = 0b0000 + ], + ] +} + +pub struct Reg; + +impl Readable for Reg { + type T = u64; + type R = ID_AA64ISAR0_EL1::Register; + + sys_coproc_read_raw!(u64, "ID_AA64ISAR0_EL1", "x"); +} + +pub const ID_AA64ISAR0_EL1: Reg = Reg {};