cortex_a/asm/
random.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2//
3// Copyright (c) 2022 Amazon.com, Inc. or its affiliates.
4//
5// Author(s):
6//   - Ali Saidi <alisaidi@amazon.com>
7
8#[cfg(target_arch = "aarch64")]
9use core::arch::asm;
10
11/// Implement an interface for accessing Arm v8.5 RNG instructions.
12///
13/// An empty struct is used to confirm that the system has the instructions available.
14///
15/// # Example
16///
17/// ```no_run
18/// use cortex_a::asm::random::ArmRng;
19/// if let Some(rng) = ArmRng::new() {
20///     let rand_num = rng.rndr();
21/// }
22/// ```
23#[derive(Copy, Clone, Debug)]
24pub struct ArmRng;
25
26impl ArmRng {
27    /// Return an empty object that is used to gate calling rndr and rndrss on discovery of the
28    /// feature so each call doesn't need to confirm it.
29    #[cfg(target_arch = "aarch64")]
30    #[inline]
31    pub fn new() -> Option<Self> {
32        use crate::registers::ID_AA64ISAR0_EL1;
33        use tock_registers::interfaces::Readable;
34
35        if ID_AA64ISAR0_EL1.is_set(ID_AA64ISAR0_EL1::RNDR) {
36            Some(ArmRng)
37        } else {
38            None
39        }
40    }
41
42    #[cfg(not(target_arch = "aarch64"))]
43    pub fn new() -> Option<Self> {
44        None
45    }
46
47    /// Return an random number from the Arm v8.5 RNG.
48    ///
49    /// This returns an option because the instruction can fail (e.g. the entropy is exhausted or
50    /// the RNG has failed.)
51    #[cfg(target_arch = "aarch64")]
52    #[inline]
53    pub fn rndr(&self) -> Option<u64> {
54        let mut flags: u64;
55        let mut data: u64;
56
57        unsafe {
58            asm!(
59                "mrs {o}, s3_3_c2_c4_0",
60                "mrs {f}, nzcv",
61                o = out(reg) data,
62                f = out(reg) flags,
63                options(nomem, nostack));
64        }
65
66        if flags != 0 {
67            None
68        } else {
69            Some(data)
70        }
71    }
72
73    #[cfg(not(target_arch = "aarch64"))]
74    pub fn rndr(&self) -> Option<u64> {
75        None
76    }
77
78    /// Return a random number from the Arm v8.5 RNG after reseeding it.
79    ///
80    /// This returns an option because the instruction can fail (e.g. the entropy is exhausted or
81    /// the RNG has failed.)
82    #[cfg(target_arch = "aarch64")]
83    #[inline]
84    pub fn rndrss(&self) -> Option<u64> {
85        let mut flags: u64;
86        let mut data: u64;
87
88        unsafe {
89            asm!(
90                "mrs {o}, s3_3_c2_c4_1",
91                "mrs {f}, nzcv",
92                o = out(reg) data,
93                f = out(reg) flags,
94                options(nomem, nostack));
95        }
96
97        if flags != 0 {
98            None
99        } else {
100            Some(data)
101        }
102    }
103
104    #[cfg(not(target_arch = "aarch64"))]
105    pub fn rndrss(&self) -> Option<u64> {
106        None
107    }
108}
109
110#[cfg(all(test, target_os = "linux"))]
111mod tests {
112    use super::*;
113
114    #[test]
115    pub fn test_rndr() {
116        // This works on Linux from userspace since Linux emulatates the Arm ID registers on the
117        // userspace undef.
118        if let Some(rand) = ArmRng::new() {
119            assert!(rand.rndr().unwrap() != 0);
120            assert!(rand.rndrss().unwrap() != 0);
121        }
122    }
123}