nrf5x/trng.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! TRNG driver, nRF5X-family
6//!
7//! The TRNG generates 1 byte randomness at the time value in the interval
8//! 0 <= r <= 255.
9//!
10//! Because that the he capsule requires 4 bytes of randomness at the time.
11//! 4 bytes of randomness must be generated before returning back to capsule.
12//!
13//! Therefore this module will have to use the TRNG four times.
14//! A counter `index` has been introduced to keep track of this.
15//! The four bytes of randomness is stored in a `Cell<u32>` which shifted
16//! according to append one byte at the time.
17//!
18//! In the current implementation if done > 4 for some strange reason the
19//! random generation will be restarted
20//!
21//! Authors
22//! -------------------
23//! * Niklas Adolfsson <niklasadolfsson1@gmail.com>
24//! * Fredrik Nilsson <frednils@student.chalmers.se>
25//! * Date: March 01, 2017
26
27use core::cell::Cell;
28use kernel::hil::entropy::{self, Continue};
29use kernel::utilities::cells::OptionalCell;
30use kernel::utilities::registers::interfaces::{Readable, Writeable};
31use kernel::utilities::registers::{register_bitfields, ReadOnly, ReadWrite, WriteOnly};
32use kernel::utilities::StaticRef;
33use kernel::ErrorCode;
34
35const RNG_BASE: StaticRef<RngRegisters> =
36 unsafe { StaticRef::new(0x4000D000 as *const RngRegisters) };
37
38#[repr(C)]
39pub struct RngRegisters {
40 /// Task starting the random number generator
41 /// Address: 0x000 - 0x004
42 pub task_start: WriteOnly<u32, Task::Register>,
43 /// Task stopping the random number generator
44 /// Address: 0x004 - 0x008
45 pub task_stop: WriteOnly<u32, Task::Register>,
46 /// Reserved
47 _reserved1: [u32; 62],
48 /// Event being generated for every new random number written to the VALUE register
49 /// Address: 0x100 - 0x104
50 pub event_valrdy: ReadWrite<u32, Event::Register>,
51 /// Reserved
52 _reserved2: [u32; 63],
53 /// Shortcut register
54 /// Address: 0x200 - 0x204
55 pub shorts: ReadWrite<u32, Shorts::Register>,
56 _reserved3: [u32; 64],
57 /// Enable interrupt
58 /// Address: 0x304 - 0x308
59 pub intenset: ReadWrite<u32, Intenset::Register>,
60 /// Disable interrupt
61 /// Address: 0x308 - 0x30c
62 pub intenclr: ReadWrite<u32, Intenclr::Register>,
63 _reserved4: [u32; 126],
64 /// Configuration register
65 /// Address: 0x504 - 0x508
66 pub config: ReadWrite<u32, Config::Register>,
67 /// Output random number
68 /// Address: 0x508 - 0x50c
69 pub value: ReadOnly<u32, Value::Register>,
70}
71
72register_bitfields! [u32,
73 /// Start task
74 Task [
75 ENABLE OFFSET(0) NUMBITS(1)
76 ],
77
78 /// Ready event
79 Event [
80 READY OFFSET(0) NUMBITS(1)
81 ],
82
83 /// Shortcut register
84 Shorts [
85 /// Shortcut between VALRDY event and STOP task
86 VALRDY_STOP OFFSET(0) NUMBITS(1)
87 ],
88
89 /// Enable interrupt
90 Intenset [
91 VALRDY OFFSET(0) NUMBITS(1)
92 ],
93
94 /// Disable interrupt
95 Intenclr [
96 VALRDY OFFSET(0) NUMBITS(1)
97 ],
98
99 /// Configuration register
100 Config [
101 /// Bias correction
102 DERCEN OFFSET(0) NUMBITS(32)
103 ],
104
105 /// Output random number
106 Value [
107 /// Generated random number
108 VALUE OFFSET(0) NUMBITS(8)
109 ]
110];
111
112pub struct Trng<'a> {
113 registers: StaticRef<RngRegisters>,
114 client: OptionalCell<&'a dyn entropy::Client32>,
115 index: Cell<usize>,
116 randomness: Cell<u32>,
117}
118
119impl<'a> Trng<'a> {
120 pub const fn new() -> Trng<'a> {
121 Trng {
122 registers: RNG_BASE,
123 client: OptionalCell::empty(),
124 index: Cell::new(0),
125 randomness: Cell::new(0),
126 }
127 }
128
129 /// RNG Interrupt handler
130 pub fn handle_interrupt(&self) {
131 self.disable_interrupts();
132
133 match self.index.get() {
134 // fetch more data need 4 bytes because the capsule requires that
135 e @ 0..=3 => {
136 // 3 lines below to change data in Cell, perhaps it can be done more nicely
137 let mut rn = self.randomness.get();
138 // 1 byte randomness
139 let r = self.registers.value.get();
140 // e = 0 -> byte 1 LSB
141 // e = 1 -> byte 2
142 // e = 2 -> byte 3
143 // e = 3 -> byte 4 MSB
144 rn |= r << (8 * e);
145 self.randomness.set(rn);
146
147 self.index.set(e + 1);
148 self.start_rng()
149 }
150 // fetched 4 bytes of data generated, then notify the capsule
151 4 => {
152 self.client.map(|client| {
153 let result = client.entropy_available(&mut TrngIter(self), Ok(()));
154 if Continue::Done != result {
155 // need more randomness i.e generate more randomness
156 self.start_rng();
157 }
158 });
159 }
160 // This should never happen if the logic is correct
161 // Restart randomness generation if this condition occurs
162 _ => {
163 self.index.set(0);
164 self.randomness.set(0);
165 }
166 }
167 }
168
169 fn enable_interrupts(&self) {
170 self.registers.intenset.write(Intenset::VALRDY::SET);
171 }
172
173 fn disable_interrupts(&self) {
174 self.registers.intenclr.write(Intenclr::VALRDY::SET);
175 }
176
177 fn start_rng(&self) {
178 // Reset `valrdy`
179 self.registers.event_valrdy.write(Event::READY::CLEAR);
180
181 // Enable interrupts
182 self.enable_interrupts();
183
184 // Start rng
185 self.registers.task_start.write(Task::ENABLE::SET);
186 }
187}
188
189struct TrngIter<'a, 'b: 'a>(&'a Trng<'b>);
190
191impl Iterator for TrngIter<'_, '_> {
192 type Item = u32;
193
194 fn next(&mut self) -> Option<u32> {
195 if self.0.index.get() == 4 {
196 let rn = self.0.randomness.get();
197 // indicate 4 bytes of randomness taken by the capsule
198 self.0.index.set(0);
199 self.0.randomness.set(0);
200 Some(rn)
201 } else {
202 None
203 }
204 }
205}
206
207impl<'a> entropy::Entropy32<'a> for Trng<'a> {
208 fn get(&self) -> Result<(), ErrorCode> {
209 self.start_rng();
210 Ok(())
211 }
212
213 fn cancel(&self) -> Result<(), ErrorCode> {
214 Err(ErrorCode::FAIL)
215 }
216
217 fn set_client(&'a self, client: &'a dyn entropy::Client32) {
218 self.client.set(client);
219 }
220}