aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/rng.rs105
1 files changed, 66 insertions, 39 deletions
diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs
index 5d61d0361..c4b77d019 100644
--- a/embassy-stm32/src/rng.rs
+++ b/embassy-stm32/src/rng.rs
@@ -3,11 +3,12 @@
3use core::future::poll_fn; 3use core::future::poll_fn;
4use core::task::Poll; 4use core::task::Poll;
5 5
6use embassy_hal_internal::interrupt::InterruptExt;
6use embassy_hal_internal::{into_ref, PeripheralRef}; 7use embassy_hal_internal::{into_ref, PeripheralRef};
7use embassy_sync::waitqueue::AtomicWaker; 8use embassy_sync::waitqueue::AtomicWaker;
8use rand_core::{CryptoRng, RngCore}; 9use rand_core::{CryptoRng, RngCore};
9 10
10use crate::{pac, peripherals, Peripheral}; 11use crate::{interrupt, pac, peripherals, Peripheral};
11 12
12pub(crate) static RNG_WAKER: AtomicWaker = AtomicWaker::new(); 13pub(crate) static RNG_WAKER: AtomicWaker = AtomicWaker::new();
13 14
@@ -28,31 +29,31 @@ impl<'d, T: Instance> Rng<'d, T> {
28 into_ref!(inner); 29 into_ref!(inner);
29 let mut random = Self { _inner: inner }; 30 let mut random = Self { _inner: inner };
30 random.reset(); 31 random.reset();
32 unsafe {
33 interrupt::RNG.enable();
34 }
31 random 35 random
32 } 36 }
33 37
34 #[cfg(rng_v1)] 38 #[cfg(rng_v1)]
35 pub fn reset(&mut self) { 39 pub fn reset(&mut self) {
36 // rng_v2 locks up on seed error, needs reset 40 T::regs().cr().write(|reg| {
37 #[cfg(rng_v2)] 41 reg.set_rngen(false);
38 if T::regs().sr().read().seis() {
39 T::reset();
40 }
41 T::regs().cr().modify(|reg| {
42 reg.set_rngen(true);
43 reg.set_ie(true);
44 }); 42 });
45 T::regs().sr().modify(|reg| { 43 T::regs().sr().modify(|reg| {
46 reg.set_seis(false); 44 reg.set_seis(false);
47 reg.set_ceis(false); 45 reg.set_ceis(false);
48 }); 46 });
47 T::regs().cr().modify(|reg| {
48 reg.set_rngen(true);
49 });
49 // Reference manual says to discard the first. 50 // Reference manual says to discard the first.
50 let _ = self.next_u32(); 51 let _ = self.next_u32();
51 } 52 }
52 53
53 #[cfg(not(rng_v1))] 54 #[cfg(not(rng_v1))]
54 pub fn reset(&mut self) { 55 pub fn reset(&mut self) {
55 T::regs().cr().modify(|reg| { 56 T::regs().cr().write(|reg| {
56 reg.set_rngen(false); 57 reg.set_rngen(false);
57 reg.set_condrst(true); 58 reg.set_condrst(true);
58 // set RNG config "A" according to reference manual 59 // set RNG config "A" according to reference manual
@@ -76,42 +77,68 @@ impl<'d, T: Instance> Rng<'d, T> {
76 T::regs().cr().modify(|reg| { 77 T::regs().cr().modify(|reg| {
77 reg.set_rngen(true); 78 reg.set_rngen(true);
78 reg.set_condrst(false); 79 reg.set_condrst(false);
79 reg.set_ie(true);
80 }); 80 });
81 // wait for CONDRST to be reset 81 // wait for CONDRST to be reset
82 while T::regs().cr().read().condrst() {} 82 while T::regs().cr().read().condrst() {}
83 } 83 }
84 84
85 pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { 85 pub fn recover_seed_error(&mut self) -> () {
86 T::regs().cr().modify(|reg| { 86 self.reset();
87 reg.set_rngen(true); 87 // reset should also clear the SEIS flag
88 }); 88 if T::regs().sr().read().seis() {
89 warn!("recovering from seed error failed");
90 return;
91 }
92 // wait for SECS to be cleared by RNG
93 while T::regs().sr().read().secs() {}
94 }
89 95
96 pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
90 for chunk in dest.chunks_mut(4) { 97 for chunk in dest.chunks_mut(4) {
91 poll_fn(|cx| { 98 let bits = T::regs().sr().read();
92 RNG_WAKER.register(cx.waker()); 99 if bits.seis() {
93 T::regs().cr().modify(|reg| { 100 // in case of noise-source or seed error we try to recover here
94 reg.set_ie(true); 101 // but we must not use the data in DR and we return an error
95 }); 102 // to leave retry-logic to the application
96 103 self.recover_seed_error();
97 let bits = T::regs().sr().read(); 104 return Err(Error::SeedError);
98 105 } else if bits.ceis() {
99 if bits.drdy() { 106 // clock error detected, DR could still be used but keep it safe,
100 Poll::Ready(Ok(())) 107 // clear the error and abort
101 } else if bits.seis() { 108 T::regs().sr().modify(|sr| sr.set_ceis(false));
102 self.reset(); 109 return Err(Error::ClockError);
103 Poll::Ready(Err(Error::SeedError)) 110 } else if bits.drdy() {
104 } else if bits.ceis() { 111 // DR can be read up to four times until the output buffer is empty
105 self.reset(); 112 // DRDY is cleared automatically when that happens
106 Poll::Ready(Err(Error::ClockError)) 113 let random_word = T::regs().dr().read();
107 } else { 114 // reference manual: always check if DR is zero
108 Poll::Pending 115 if random_word == 0 {
116 return Err(Error::SeedError);
117 }
118 // write bytes to chunk
119 for (dest, src) in chunk.iter_mut().zip(random_word.to_be_bytes().iter()) {
120 *dest = *src
109 } 121 }
110 }) 122 } else {
111 .await?; 123 // wait for interrupt
112 let random_bytes = T::regs().dr().read().to_be_bytes(); 124 poll_fn(|cx| {
113 for (dest, src) in chunk.iter_mut().zip(random_bytes.iter()) { 125 // quick check to avoid registration if already done.
114 *dest = *src 126 let bits = T::regs().sr().read();
127 if bits.drdy() || bits.seis() || bits.ceis() {
128 return Poll::Ready(());
129 }
130 RNG_WAKER.register(cx.waker());
131 T::regs().cr().modify(|reg| reg.set_ie(true));
132 // Need to check condition **after** `register` to avoid a race
133 // condition that would result in lost notifications.
134 let bits = T::regs().sr().read();
135 if bits.drdy() || bits.seis() || bits.ceis() {
136 Poll::Ready(())
137 } else {
138 Poll::Pending
139 }
140 })
141 .await;
115 } 142 }
116 } 143 }
117 144
@@ -186,7 +213,7 @@ macro_rules! irq {
186 unsafe fn $irq() { 213 unsafe fn $irq() {
187 let bits = $crate::pac::RNG.sr().read(); 214 let bits = $crate::pac::RNG.sr().read();
188 if bits.drdy() || bits.seis() || bits.ceis() { 215 if bits.drdy() || bits.seis() || bits.ceis() {
189 $crate::pac::RNG.cr().write(|reg| reg.set_ie(false)); 216 $crate::pac::RNG.cr().modify(|reg| reg.set_ie(false));
190 $crate::rng::RNG_WAKER.wake(); 217 $crate::rng::RNG_WAKER.wake();
191 } 218 }
192 } 219 }