diff options
| author | Felipe Balbi <[email protected]> | 2025-04-11 15:03:53 -0700 |
|---|---|---|
| committer | Felipe Balbi <[email protected]> | 2025-05-09 07:25:23 -0700 |
| commit | 8e7e4332b40707e8d36338ad8ec486320bb3538f (patch) | |
| tree | bb4a56fbdde354eee72c997f141b2c2e487d390a /embassy-imxrt | |
| parent | 64a2b9b2a36adbc26117b8e76ae2b178a5e3eb9f (diff) | |
Add embassy-imxrt RNG driver
Diffstat (limited to 'embassy-imxrt')
| -rw-r--r-- | embassy-imxrt/src/lib.rs | 1 | ||||
| -rw-r--r-- | embassy-imxrt/src/rng.rs | 257 |
2 files changed, 258 insertions, 0 deletions
diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index ad9f81e88..f728ba060 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs | |||
| @@ -20,6 +20,7 @@ pub(crate) mod fmt; | |||
| 20 | pub mod clocks; | 20 | pub mod clocks; |
| 21 | pub mod gpio; | 21 | pub mod gpio; |
| 22 | pub mod iopctl; | 22 | pub mod iopctl; |
| 23 | pub mod rng; | ||
| 23 | 24 | ||
| 24 | #[cfg(feature = "_time-driver")] | 25 | #[cfg(feature = "_time-driver")] |
| 25 | pub mod time_driver; | 26 | pub mod time_driver; |
diff --git a/embassy-imxrt/src/rng.rs b/embassy-imxrt/src/rng.rs new file mode 100644 index 000000000..67e7ab65d --- /dev/null +++ b/embassy-imxrt/src/rng.rs | |||
| @@ -0,0 +1,257 @@ | |||
| 1 | //! True Random Number Generator (TRNG) | ||
| 2 | |||
| 3 | use core::future::poll_fn; | ||
| 4 | use core::marker::PhantomData; | ||
| 5 | use core::task::Poll; | ||
| 6 | |||
| 7 | use embassy_futures::block_on; | ||
| 8 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 9 | use rand_core::{CryptoRng, RngCore}; | ||
| 10 | |||
| 11 | use crate::clocks::{enable_and_reset, SysconPeripheral}; | ||
| 12 | use crate::interrupt::typelevel::Interrupt; | ||
| 13 | use crate::{interrupt, peripherals, Peri, PeripheralType}; | ||
| 14 | |||
| 15 | static RNG_WAKER: AtomicWaker = AtomicWaker::new(); | ||
| 16 | |||
| 17 | /// RNG ;error | ||
| 18 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 19 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 20 | pub enum Error { | ||
| 21 | /// Seed error. | ||
| 22 | SeedError, | ||
| 23 | |||
| 24 | /// HW Error. | ||
| 25 | HwError, | ||
| 26 | |||
| 27 | /// Frequency Count Fail | ||
| 28 | FreqCountFail, | ||
| 29 | } | ||
| 30 | |||
| 31 | /// RNG interrupt handler. | ||
| 32 | pub struct InterruptHandler<T: Instance> { | ||
| 33 | _phantom: PhantomData<T>, | ||
| 34 | } | ||
| 35 | |||
| 36 | impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> { | ||
| 37 | unsafe fn on_interrupt() { | ||
| 38 | let regs = T::info().regs; | ||
| 39 | let int_status = regs.int_status().read(); | ||
| 40 | |||
| 41 | if int_status.ent_val().bit_is_set() | ||
| 42 | || int_status.hw_err().bit_is_set() | ||
| 43 | || int_status.frq_ct_fail().bit_is_set() | ||
| 44 | { | ||
| 45 | regs.int_ctrl().modify(|_, w| { | ||
| 46 | w.ent_val() | ||
| 47 | .ent_val_0() | ||
| 48 | .hw_err() | ||
| 49 | .hw_err_0() | ||
| 50 | .frq_ct_fail() | ||
| 51 | .frq_ct_fail_0() | ||
| 52 | }); | ||
| 53 | RNG_WAKER.wake(); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// RNG driver. | ||
| 59 | pub struct Rng<'d> { | ||
| 60 | info: Info, | ||
| 61 | _lifetime: PhantomData<&'d ()>, | ||
| 62 | } | ||
| 63 | |||
| 64 | impl<'d> Rng<'d> { | ||
| 65 | /// Create a new RNG driver. | ||
| 66 | pub fn new<T: Instance>( | ||
| 67 | _inner: Peri<'d, T>, | ||
| 68 | _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd, | ||
| 69 | ) -> Self { | ||
| 70 | enable_and_reset::<T>(); | ||
| 71 | |||
| 72 | let mut random = Self { | ||
| 73 | info: T::info(), | ||
| 74 | _lifetime: PhantomData, | ||
| 75 | }; | ||
| 76 | random.init(); | ||
| 77 | |||
| 78 | T::Interrupt::unpend(); | ||
| 79 | unsafe { T::Interrupt::enable() }; | ||
| 80 | |||
| 81 | random | ||
| 82 | } | ||
| 83 | |||
| 84 | /// Reset the RNG. | ||
| 85 | pub fn reset(&mut self) { | ||
| 86 | self.info.regs.mctl().write(|w| w.rst_def().set_bit().prgm().set_bit()); | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Fill the given slice with random values. | ||
| 90 | pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { | ||
| 91 | // We have a total of 16 words (512 bits) of entropy at our | ||
| 92 | // disposal. The idea here is to read all bits and copy the | ||
| 93 | // necessary bytes to the slice. | ||
| 94 | for chunk in dest.chunks_mut(64) { | ||
| 95 | self.async_fill_chunk(chunk).await?; | ||
| 96 | } | ||
| 97 | |||
| 98 | Ok(()) | ||
| 99 | } | ||
| 100 | |||
| 101 | async fn async_fill_chunk(&mut self, chunk: &mut [u8]) -> Result<(), Error> { | ||
| 102 | // wait for interrupt | ||
| 103 | let res = poll_fn(|cx| { | ||
| 104 | // Check if already ready. | ||
| 105 | // TODO: Is this necessary? Could we just check once after | ||
| 106 | // the waker has been registered? | ||
| 107 | if self.info.regs.int_status().read().ent_val().bit_is_set() { | ||
| 108 | return Poll::Ready(Ok(())); | ||
| 109 | } | ||
| 110 | |||
| 111 | RNG_WAKER.register(cx.waker()); | ||
| 112 | |||
| 113 | self.unmask_interrupts(); | ||
| 114 | |||
| 115 | let mctl = self.info.regs.mctl().read(); | ||
| 116 | |||
| 117 | // Check again if interrupt fired | ||
| 118 | if mctl.ent_val().bit_is_set() { | ||
| 119 | Poll::Ready(Ok(())) | ||
| 120 | } else if mctl.err().bit_is_set() { | ||
| 121 | Poll::Ready(Err(Error::HwError)) | ||
| 122 | } else if mctl.fct_fail().bit_is_set() { | ||
| 123 | Poll::Ready(Err(Error::FreqCountFail)) | ||
| 124 | } else { | ||
| 125 | Poll::Pending | ||
| 126 | } | ||
| 127 | }) | ||
| 128 | .await; | ||
| 129 | |||
| 130 | let bits = self.info.regs.mctl().read(); | ||
| 131 | |||
| 132 | if bits.ent_val().bit_is_set() { | ||
| 133 | let mut entropy = [0; 16]; | ||
| 134 | |||
| 135 | for (i, item) in entropy.iter_mut().enumerate() { | ||
| 136 | *item = self.info.regs.ent(i).read().bits(); | ||
| 137 | } | ||
| 138 | |||
| 139 | // Read MCTL after reading ENT15 | ||
| 140 | let _ = self.info.regs.mctl().read(); | ||
| 141 | |||
| 142 | if entropy.iter().any(|e| *e == 0) { | ||
| 143 | return Err(Error::SeedError); | ||
| 144 | } | ||
| 145 | |||
| 146 | // SAFETY: entropy is the same for input and output types in | ||
| 147 | // native endianness. | ||
| 148 | let entropy: [u8; 64] = unsafe { core::mem::transmute(entropy) }; | ||
| 149 | |||
| 150 | // write bytes to chunk | ||
| 151 | chunk.copy_from_slice(&entropy[..chunk.len()]); | ||
| 152 | } | ||
| 153 | |||
| 154 | res | ||
| 155 | } | ||
| 156 | |||
| 157 | fn mask_interrupts(&mut self) { | ||
| 158 | self.info.regs.int_mask().write(|w| { | ||
| 159 | w.ent_val() | ||
| 160 | .ent_val_0() | ||
| 161 | .hw_err() | ||
| 162 | .hw_err_0() | ||
| 163 | .frq_ct_fail() | ||
| 164 | .frq_ct_fail_0() | ||
| 165 | }); | ||
| 166 | } | ||
| 167 | |||
| 168 | fn unmask_interrupts(&mut self) { | ||
| 169 | self.info.regs.int_mask().modify(|_, w| { | ||
| 170 | w.ent_val() | ||
| 171 | .ent_val_1() | ||
| 172 | .hw_err() | ||
| 173 | .hw_err_1() | ||
| 174 | .frq_ct_fail() | ||
| 175 | .frq_ct_fail_1() | ||
| 176 | }); | ||
| 177 | } | ||
| 178 | |||
| 179 | fn enable_interrupts(&mut self) { | ||
| 180 | self.info.regs.int_ctrl().write(|w| { | ||
| 181 | w.ent_val() | ||
| 182 | .ent_val_1() | ||
| 183 | .hw_err() | ||
| 184 | .hw_err_1() | ||
| 185 | .frq_ct_fail() | ||
| 186 | .frq_ct_fail_1() | ||
| 187 | }); | ||
| 188 | } | ||
| 189 | |||
| 190 | fn init(&mut self) { | ||
| 191 | self.mask_interrupts(); | ||
| 192 | |||
| 193 | // Switch TRNG to programming mode | ||
| 194 | self.info.regs.mctl().modify(|_, w| w.prgm().set_bit()); | ||
| 195 | |||
| 196 | self.enable_interrupts(); | ||
| 197 | |||
| 198 | // Switch TRNG to Run Mode | ||
| 199 | self.info | ||
| 200 | .regs | ||
| 201 | .mctl() | ||
| 202 | .modify(|_, w| w.trng_acc().set_bit().prgm().clear_bit()); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | impl RngCore for Rng<'_> { | ||
| 207 | fn next_u32(&mut self) -> u32 { | ||
| 208 | let mut bytes = [0u8; 4]; | ||
| 209 | block_on(self.async_fill_bytes(&mut bytes)).unwrap(); | ||
| 210 | u32::from_ne_bytes(bytes) | ||
| 211 | } | ||
| 212 | |||
| 213 | fn next_u64(&mut self) -> u64 { | ||
| 214 | let mut bytes = [0u8; 8]; | ||
| 215 | block_on(self.async_fill_bytes(&mut bytes)).unwrap(); | ||
| 216 | u64::from_ne_bytes(bytes) | ||
| 217 | } | ||
| 218 | |||
| 219 | fn fill_bytes(&mut self, dest: &mut [u8]) { | ||
| 220 | block_on(self.async_fill_bytes(dest)).unwrap(); | ||
| 221 | } | ||
| 222 | |||
| 223 | fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { | ||
| 224 | self.fill_bytes(dest); | ||
| 225 | Ok(()) | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | impl CryptoRng for Rng<'_> {} | ||
| 230 | |||
| 231 | struct Info { | ||
| 232 | regs: crate::pac::Trng, | ||
| 233 | } | ||
| 234 | |||
| 235 | trait SealedInstance { | ||
| 236 | fn info() -> Info; | ||
| 237 | } | ||
| 238 | |||
| 239 | /// RNG instance trait. | ||
| 240 | #[allow(private_bounds)] | ||
| 241 | pub trait Instance: SealedInstance + PeripheralType + SysconPeripheral + 'static + Send { | ||
| 242 | /// Interrupt for this RNG instance. | ||
| 243 | type Interrupt: interrupt::typelevel::Interrupt; | ||
| 244 | } | ||
| 245 | |||
| 246 | impl Instance for peripherals::RNG { | ||
| 247 | type Interrupt = crate::interrupt::typelevel::RNG; | ||
| 248 | } | ||
| 249 | |||
| 250 | impl SealedInstance for peripherals::RNG { | ||
| 251 | fn info() -> Info { | ||
| 252 | // SAFETY: safe from single executor | ||
| 253 | Info { | ||
| 254 | regs: unsafe { crate::pac::Trng::steal() }, | ||
| 255 | } | ||
| 256 | } | ||
| 257 | } | ||
