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}