aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaleb Garrett <[email protected]>2024-02-04 17:16:33 -0500
committerCaleb Garrett <[email protected]>2024-02-04 17:16:33 -0500
commit66f44b95d70547be8e32daac1ab611eec5fbe28a (patch)
tree5c917f26dd348b753025ecaf6bcb02131199846c
parent72bbfec39d3f826c1a8dd485af2da4bcbdd32e35 (diff)
Addressed hash CI build issues.
-rw-r--r--embassy-stm32/Cargo.toml4
-rw-r--r--embassy-stm32/src/hash/mod.rs357
-rw-r--r--embassy-stm32/src/hash/v1.rs334
-rw-r--r--embassy-stm32/src/hash/v2v3.rs385
-rw-r--r--examples/stm32f7/src/bin/eth.rs2
-rw-r--r--examples/stm32f7/src/bin/hash.rs6
6 files changed, 731 insertions, 357 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index d8a4c65fa..00d8a5f63 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -68,7 +68,7 @@ rand_core = "0.6.3"
68sdio-host = "0.5.0" 68sdio-host = "0.5.0"
69critical-section = "1.1" 69critical-section = "1.1"
70#stm32-metapac = { version = "15" } 70#stm32-metapac = { version = "15" }
71stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15" } 71stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f" }
72vcell = "0.1.3" 72vcell = "0.1.3"
73bxcan = "0.7.0" 73bxcan = "0.7.0"
74nb = "1.0.0" 74nb = "1.0.0"
@@ -87,7 +87,7 @@ critical-section = { version = "1.1", features = ["std"] }
87proc-macro2 = "1.0.36" 87proc-macro2 = "1.0.36"
88quote = "1.0.15" 88quote = "1.0.15"
89#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} 89#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
90stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15", default-features = false, features = ["metadata"]} 90stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f", default-features = false, features = ["metadata"]}
91 91
92 92
93[features] 93[features]
diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs
index ac4854f80..6b23f3b55 100644
--- a/embassy-stm32/src/hash/mod.rs
+++ b/embassy-stm32/src/hash/mod.rs
@@ -1,352 +1,7 @@
1//! Hash generator (HASH) 1//! Hash Accelerator (HASH)
2use core::cmp::min; 2#[cfg_attr(hash_v1, path = "v1.rs")]
3use core::future::poll_fn; 3#[cfg_attr(hash_v2, path = "v2v3.rs")]
4use core::marker::PhantomData; 4#[cfg_attr(hash_v3, path = "v2v3.rs")]
5use core::ptr; 5mod _version;
6use core::task::Poll;
7 6
8use embassy_hal_internal::{into_ref, PeripheralRef}; 7pub use _version::*;
9use embassy_sync::waitqueue::AtomicWaker;
10
11use crate::dma::Transfer;
12use crate::peripherals::HASH;
13use stm32_metapac::hash::regs::*;
14
15use crate::interrupt::typelevel::Interrupt;
16use crate::rcc::sealed::RccPeripheral;
17use crate::{interrupt, pac, peripherals, Peripheral};
18
19#[cfg(hash_v1)]
20const NUM_CONTEXT_REGS: usize = 51;
21#[cfg(hash_v2)]
22const NUM_CONTEXT_REGS: usize = 54;
23const DIGEST_BLOCK_SIZE: usize = 64;
24
25static HASH_WAKER: AtomicWaker = AtomicWaker::new();
26
27/// HASH interrupt handler.
28pub struct InterruptHandler<T: Instance> {
29 _phantom: PhantomData<T>,
30}
31
32impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
33 unsafe fn on_interrupt() {
34 let bits = T::regs().sr().read();
35 if bits.dinis() {
36 T::regs().imr().modify(|reg| reg.set_dinie(false));
37 HASH_WAKER.wake();
38 }
39 if bits.dcis() {
40 T::regs().imr().modify(|reg| reg.set_dcie(false));
41 HASH_WAKER.wake();
42 }
43 }
44}
45
46///Hash algorithm selection
47#[derive(PartialEq)]
48pub enum Algorithm {
49 /// SHA-1 Algorithm
50 SHA1 = 0,
51 /// MD5 Algorithm
52 MD5 = 1,
53 #[cfg(hash_v2)]
54 /// SHA-224 Algorithm
55 SHA224 = 2,
56 #[cfg(hash_v2)]
57 /// SHA-256 Algorithm
58 SHA256 = 3,
59}
60
61/// Input data width selection
62#[repr(u8)]
63#[derive(Clone, Copy)]
64pub enum DataType {
65 ///32-bit data, no data is swapped.
66 Width32 = 0,
67 ///16-bit data, each half-word is swapped.
68 Width16 = 1,
69 ///8-bit data, all bytes are swapped.
70 Width8 = 2,
71 ///1-bit data, all bits are swapped.
72 Width1 = 3,
73}
74
75/// Stores the state of the HASH peripheral for suspending/resuming
76/// digest calculation.
77pub struct Context {
78 buffer: [u8; DIGEST_BLOCK_SIZE],
79 buflen: usize,
80 algo: Algorithm,
81 format: DataType,
82 imr: u32,
83 str: u32,
84 cr: u32,
85 csr: [u32; NUM_CONTEXT_REGS],
86}
87
88/// HASH driver.
89pub struct Hash<'d, T: Instance, D: Dma<T>> {
90 _peripheral: PeripheralRef<'d, T>,
91 dma: PeripheralRef<'d, D>,
92}
93
94impl<'d, T: Instance, D: Dma<T>> Hash<'d, T, D> {
95 /// Instantiates, resets, and enables the HASH peripheral.
96 pub fn new(peripheral: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = D> + 'd) -> Self {
97 HASH::enable_and_reset();
98 into_ref!(peripheral, dma);
99 let instance = Self {
100 _peripheral: peripheral,
101 dma: dma,
102 };
103
104 T::Interrupt::unpend();
105 unsafe { T::Interrupt::enable() };
106
107 instance
108 }
109
110 /// Starts computation of a new hash and returns the saved peripheral state.
111 pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context {
112 // Define a context for this new computation.
113 let mut ctx = Context {
114 buffer: [0; DIGEST_BLOCK_SIZE],
115 buflen: 0,
116 algo: algorithm,
117 format: format,
118 imr: 0,
119 str: 0,
120 cr: 0,
121 csr: [0; NUM_CONTEXT_REGS],
122 };
123
124 // Set the data type in the peripheral.
125 T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8));
126
127 // Select the algorithm.
128 let mut algo0 = false;
129 let mut algo1 = false;
130 if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 {
131 algo0 = true;
132 }
133 if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 {
134 algo1 = true;
135 }
136 T::regs().cr().modify(|w| w.set_algo0(algo0));
137 T::regs().cr().modify(|w| w.set_algo1(algo1));
138
139 // Enable multiple DMA transfers.
140 T::regs().cr().modify(|w| w.set_mdmat(true));
141
142 // Set init to load the context registers. Necessary before storing context.
143 T::regs().cr().modify(|w| w.set_init(true));
144
145 // Store and return the state of the peripheral.
146 self.store_context(&mut ctx).await;
147 ctx
148 }
149
150 /// Restores the peripheral state using the given context,
151 /// then updates the state with the provided data.
152 /// Peripheral state is saved upon return.
153 pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) {
154 let data_waiting = input.len() + ctx.buflen;
155 if data_waiting < DIGEST_BLOCK_SIZE {
156 // There isn't enough data to digest a block, so append it to the buffer.
157 ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
158 ctx.buflen += input.len();
159 return;
160 }
161
162 // Restore the peripheral state.
163 self.load_context(&ctx);
164
165 let mut ilen_remaining = input.len();
166 let mut input_start = 0;
167
168 // First ingest the data in the buffer.
169 let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
170 if empty_len > 0 {
171 let copy_len = min(empty_len, ilen_remaining);
172 ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]);
173 ctx.buflen += copy_len;
174 ilen_remaining -= copy_len;
175 input_start += copy_len;
176 }
177 self.accumulate(&ctx.buffer).await;
178 ctx.buflen = 0;
179
180 // Move any extra data to the now-empty buffer.
181 let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE;
182 if leftovers > 0 {
183 assert!(ilen_remaining >= leftovers);
184 ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
185 ctx.buflen += leftovers;
186 ilen_remaining -= leftovers;
187 } else {
188 ctx.buffer
189 .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]);
190 ctx.buflen += DIGEST_BLOCK_SIZE;
191 ilen_remaining -= DIGEST_BLOCK_SIZE;
192 }
193
194 // Hash the remaining data.
195 self.accumulate(&input[input_start..input_start + ilen_remaining]).await;
196
197 // Save the peripheral context.
198 self.store_context(ctx).await;
199 }
200
201 /// Computes a digest for the given context. A slice of the provided digest buffer is returned.
202 /// The length of the returned slice is dependent on the digest length of the selected algorithm.
203 pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'a [u8] {
204 // Restore the peripheral state.
205 self.load_context(&ctx);
206
207 // Must be cleared prior to the last DMA transfer.
208 T::regs().cr().modify(|w| w.set_mdmat(false));
209
210 // Hash the leftover bytes, if any.
211 self.accumulate(&ctx.buffer[0..ctx.buflen]).await;
212 ctx.buflen = 0;
213
214 // Wait for completion.
215 poll_fn(|cx| {
216 // Check if already done.
217 let bits = T::regs().sr().read();
218 if bits.dcis() {
219 return Poll::Ready(());
220 }
221 // Register waker, then enable interrupts.
222 HASH_WAKER.register(cx.waker());
223 T::regs().imr().modify(|reg| reg.set_dinie(true));
224 // Check for completion.
225 let bits = T::regs().sr().read();
226 if bits.dcis() {
227 Poll::Ready(())
228 } else {
229 Poll::Pending
230 }
231 })
232 .await;
233
234 // Return the digest.
235 let digest_words = match ctx.algo {
236 Algorithm::SHA1 => 5,
237 Algorithm::MD5 => 4,
238 Algorithm::SHA224 => 7,
239 Algorithm::SHA256 => 8,
240 };
241 let mut i = 0;
242 while i < digest_words {
243 let word = T::regs().hr(i).read();
244 digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
245 i += 1;
246 }
247 &digest[0..digest_words * 4]
248 }
249
250 /// Push data into the hash core.
251 async fn accumulate(&mut self, input: &[u8]) {
252 // Ignore an input length of 0.
253 if input.len() == 0 {
254 return;
255 }
256
257 // Set the number of valid bits.
258 let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
259 T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
260
261 // Configure DMA to transfer input to hash core.
262 let dma_request = self.dma.request();
263 let dst_ptr = T::regs().din().as_ptr();
264 let mut num_words = input.len() / 4;
265 if input.len() % 4 > 0 {
266 num_words += 1;
267 }
268 let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words);
269 let dma_transfer =
270 unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) };
271 T::regs().cr().modify(|w| w.set_dmae(true));
272
273 // Wait for the transfer to complete.
274 dma_transfer.await;
275 }
276
277 /// Save the peripheral state to a context.
278 async fn store_context(&mut self, ctx: &mut Context) {
279 // Wait for interrupt.
280 poll_fn(|cx| {
281 // Check if already done.
282 let bits = T::regs().sr().read();
283 if bits.dinis() {
284 return Poll::Ready(());
285 }
286 // Register waker, then enable interrupts.
287 HASH_WAKER.register(cx.waker());
288 T::regs().imr().modify(|reg| reg.set_dinie(true));
289 // Check for completion.
290 let bits = T::regs().sr().read();
291 if bits.dinis() {
292 Poll::Ready(())
293 } else {
294 Poll::Pending
295 }
296 })
297 .await;
298
299 ctx.imr = T::regs().imr().read().0;
300 ctx.str = T::regs().str().read().0;
301 ctx.cr = T::regs().cr().read().0;
302 let mut i = 0;
303 while i < NUM_CONTEXT_REGS {
304 ctx.csr[i] = T::regs().csr(i).read();
305 i += 1;
306 }
307 }
308
309 /// Restore the peripheral state from a context.
310 fn load_context(&mut self, ctx: &Context) {
311 // Restore the peripheral state from the context.
312 T::regs().imr().write_value(Imr { 0: ctx.imr });
313 T::regs().str().write_value(Str { 0: ctx.str });
314 T::regs().cr().write_value(Cr { 0: ctx.cr });
315 T::regs().cr().modify(|w| w.set_init(true));
316 let mut i = 0;
317 while i < NUM_CONTEXT_REGS {
318 T::regs().csr(i).write_value(ctx.csr[i]);
319 i += 1;
320 }
321 }
322}
323
324pub(crate) mod sealed {
325 use super::*;
326
327 pub trait Instance {
328 fn regs() -> pac::hash::Hash;
329 }
330}
331
332/// HASH instance trait.
333pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
334 /// Interrupt for this HASH instance.
335 type Interrupt: interrupt::typelevel::Interrupt;
336}
337
338foreach_interrupt!(
339 ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => {
340 impl Instance for peripherals::$inst {
341 type Interrupt = crate::interrupt::typelevel::$irq;
342 }
343
344 impl sealed::Instance for peripherals::$inst {
345 fn regs() -> crate::pac::hash::Hash {
346 crate::pac::$inst
347 }
348 }
349 };
350);
351
352dma_trait!(Dma, Instance);
diff --git a/embassy-stm32/src/hash/v1.rs b/embassy-stm32/src/hash/v1.rs
new file mode 100644
index 000000000..50f9adc83
--- /dev/null
+++ b/embassy-stm32/src/hash/v1.rs
@@ -0,0 +1,334 @@
1//! Hash generator (HASH)
2use core::cmp::min;
3use core::future::poll_fn;
4use core::marker::PhantomData;
5use core::task::Poll;
6
7use embassy_hal_internal::{into_ref, PeripheralRef};
8use embassy_sync::waitqueue::AtomicWaker;
9use stm32_metapac::hash::regs::*;
10
11use crate::interrupt::typelevel::Interrupt;
12use crate::peripherals::HASH;
13use crate::rcc::sealed::RccPeripheral;
14use crate::{interrupt, pac, peripherals, Peripheral};
15
16const NUM_CONTEXT_REGS: usize = 51;
17const HASH_BUFFER_LEN: usize = 68;
18const DIGEST_BLOCK_SIZE: usize = 64;
19const MAX_DIGEST_SIZE: usize = 20;
20
21static HASH_WAKER: AtomicWaker = AtomicWaker::new();
22
23/// HASH interrupt handler.
24pub struct InterruptHandler<T: Instance> {
25 _phantom: PhantomData<T>,
26}
27
28impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
29 unsafe fn on_interrupt() {
30 let bits = T::regs().sr().read();
31 if bits.dinis() {
32 T::regs().imr().modify(|reg| reg.set_dinie(false));
33 HASH_WAKER.wake();
34 }
35 if bits.dcis() {
36 T::regs().imr().modify(|reg| reg.set_dcie(false));
37 HASH_WAKER.wake();
38 }
39 }
40}
41
42///Hash algorithm selection
43#[derive(PartialEq)]
44pub enum Algorithm {
45 /// SHA-1 Algorithm
46 SHA1 = 0,
47 /// MD5 Algorithm
48 MD5 = 1,
49}
50
51/// Input data width selection
52#[repr(u8)]
53#[derive(Clone, Copy)]
54pub enum DataType {
55 ///32-bit data, no data is swapped.
56 Width32 = 0,
57 ///16-bit data, each half-word is swapped.
58 Width16 = 1,
59 ///8-bit data, all bytes are swapped.
60 Width8 = 2,
61 ///1-bit data, all bits are swapped.
62 Width1 = 3,
63}
64
65/// Stores the state of the HASH peripheral for suspending/resuming
66/// digest calculation.
67pub struct Context {
68 first_word_sent: bool,
69 buffer: [u8; HASH_BUFFER_LEN],
70 buflen: usize,
71 algo: Algorithm,
72 format: DataType,
73 imr: u32,
74 str: u32,
75 cr: u32,
76 csr: [u32; NUM_CONTEXT_REGS],
77}
78
79/// HASH driver.
80pub struct Hash<'d, T: Instance> {
81 _peripheral: PeripheralRef<'d, T>,
82}
83
84impl<'d, T: Instance> Hash<'d, T> {
85 /// Instantiates, resets, and enables the HASH peripheral.
86 pub fn new(peripheral: impl Peripheral<P = T> + 'd) -> Self {
87 HASH::enable_and_reset();
88 into_ref!(peripheral);
89 let instance = Self {
90 _peripheral: peripheral,
91 };
92
93 T::Interrupt::unpend();
94 unsafe { T::Interrupt::enable() };
95
96 instance
97 }
98
99 /// Starts computation of a new hash and returns the saved peripheral state.
100 pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context {
101 // Define a context for this new computation.
102 let mut ctx = Context {
103 first_word_sent: false,
104 buffer: [0; HASH_BUFFER_LEN],
105 buflen: 0,
106 algo: algorithm,
107 format: format,
108 imr: 0,
109 str: 0,
110 cr: 0,
111 csr: [0; NUM_CONTEXT_REGS],
112 };
113
114 // Set the data type in the peripheral.
115 T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8));
116
117 // Select the algorithm.
118 if ctx.algo == Algorithm::MD5 {
119 T::regs().cr().modify(|w| w.set_algo(true));
120 }
121
122 // Store and return the state of the peripheral.
123 self.store_context(&mut ctx).await;
124 ctx
125 }
126
127 /// Restores the peripheral state using the given context,
128 /// then updates the state with the provided data.
129 /// Peripheral state is saved upon return.
130 pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) {
131 let mut data_waiting = input.len() + ctx.buflen;
132 if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) {
133 // There isn't enough data to digest a block, so append it to the buffer.
134 ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
135 ctx.buflen += input.len();
136 return;
137 }
138
139 // Restore the peripheral state.
140 self.load_context(&ctx);
141
142 let mut ilen_remaining = input.len();
143 let mut input_start = 0;
144
145 // Handle first block.
146 if !ctx.first_word_sent {
147 let empty_len = ctx.buffer.len() - ctx.buflen;
148 let copy_len = min(empty_len, ilen_remaining);
149 // Fill the buffer.
150 if copy_len > 0 {
151 ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]);
152 ctx.buflen += copy_len;
153 ilen_remaining -= copy_len;
154 input_start += copy_len;
155 }
156 self.accumulate(ctx.buffer.as_slice());
157 data_waiting -= ctx.buflen;
158 ctx.buflen = 0;
159 ctx.first_word_sent = true;
160 }
161
162 if data_waiting < DIGEST_BLOCK_SIZE {
163 // There isn't enough data remaining to process another block, so store it.
164 ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]);
165 ctx.buflen += ilen_remaining;
166 } else {
167 // First ingest the data in the buffer.
168 let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
169 if empty_len > 0 {
170 let copy_len = min(empty_len, ilen_remaining);
171 ctx.buffer[ctx.buflen..ctx.buflen + copy_len]
172 .copy_from_slice(&input[input_start..input_start + copy_len]);
173 ctx.buflen += copy_len;
174 ilen_remaining -= copy_len;
175 input_start += copy_len;
176 }
177 self.accumulate(&ctx.buffer[0..64]);
178 ctx.buflen = 0;
179
180 // Move any extra data to the now-empty buffer.
181 let leftovers = ilen_remaining % 64;
182 if leftovers > 0 {
183 ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
184 ctx.buflen += leftovers;
185 ilen_remaining -= leftovers;
186 }
187
188 // Hash the remaining data.
189 self.accumulate(&input[input_start..input_start + ilen_remaining]);
190 }
191
192 // Save the peripheral context.
193 self.store_context(ctx).await;
194 }
195
196 /// Computes a digest for the given context. A slice of the provided digest buffer is returned.
197 /// The length of the returned slice is dependent on the digest length of the selected algorithm.
198 pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; MAX_DIGEST_SIZE]) -> &'a [u8] {
199 // Restore the peripheral state.
200 self.load_context(&ctx);
201
202 // Hash the leftover bytes, if any.
203 self.accumulate(&ctx.buffer[0..ctx.buflen]);
204 ctx.buflen = 0;
205
206 //Start the digest calculation.
207 T::regs().str().write(|w| w.set_dcal(true));
208
209 // Wait for completion.
210 poll_fn(|cx| {
211 // Check if already done.
212 let bits = T::regs().sr().read();
213 if bits.dcis() {
214 return Poll::Ready(());
215 }
216 // Register waker, then enable interrupts.
217 HASH_WAKER.register(cx.waker());
218 T::regs().imr().modify(|reg| reg.set_dinie(true));
219 // Check for completion.
220 let bits = T::regs().sr().read();
221 if bits.dcis() {
222 Poll::Ready(())
223 } else {
224 Poll::Pending
225 }
226 })
227 .await;
228
229 // Return the digest.
230 let digest_words = match ctx.algo {
231 Algorithm::SHA1 => 5,
232 Algorithm::MD5 => 4,
233 };
234 let mut i = 0;
235 while i < digest_words {
236 let word = T::regs().hr(i).read();
237 digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
238 i += 1;
239 }
240 &digest[0..digest_words * 4]
241 }
242
243 /// Push data into the hash core.
244 fn accumulate(&mut self, input: &[u8]) {
245 // Set the number of valid bits.
246 let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
247 T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
248
249 let mut i = 0;
250 while i < input.len() {
251 let mut word: [u8; 4] = [0; 4];
252 let copy_idx = min(i + 4, input.len());
253 word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]);
254 T::regs().din().write_value(u32::from_ne_bytes(word));
255 i += 4;
256 }
257 }
258
259 /// Save the peripheral state to a context.
260 async fn store_context(&mut self, ctx: &mut Context) {
261 // Wait for interrupt.
262 poll_fn(|cx| {
263 // Check if already done.
264 let bits = T::regs().sr().read();
265 if bits.dinis() {
266 return Poll::Ready(());
267 }
268 // Register waker, then enable interrupts.
269 HASH_WAKER.register(cx.waker());
270 T::regs().imr().modify(|reg| reg.set_dinie(true));
271 // Check for completion.
272 let bits = T::regs().sr().read();
273 if bits.dinis() {
274 Poll::Ready(())
275 } else {
276 Poll::Pending
277 }
278 })
279 .await;
280
281 ctx.imr = T::regs().imr().read().0;
282 ctx.str = T::regs().str().read().0;
283 ctx.cr = T::regs().cr().read().0;
284 let mut i = 0;
285 while i < NUM_CONTEXT_REGS {
286 ctx.csr[i] = T::regs().csr(i).read();
287 i += 1;
288 }
289 }
290
291 /// Restore the peripheral state from a context.
292 fn load_context(&mut self, ctx: &Context) {
293 // Restore the peripheral state from the context.
294 T::regs().imr().write_value(Imr { 0: ctx.imr });
295 T::regs().str().write_value(Str { 0: ctx.str });
296 T::regs().cr().write_value(Cr { 0: ctx.cr });
297 T::regs().cr().modify(|w| w.set_init(true));
298 let mut i = 0;
299 while i < NUM_CONTEXT_REGS {
300 T::regs().csr(i).write_value(ctx.csr[i]);
301 i += 1;
302 }
303 }
304}
305
306pub(crate) mod sealed {
307 use super::*;
308
309 pub trait Instance {
310 fn regs() -> pac::hash::Hash;
311 }
312}
313
314/// HASH instance trait.
315pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
316 /// Interrupt for this HASH instance.
317 type Interrupt: interrupt::typelevel::Interrupt;
318}
319
320foreach_interrupt!(
321 ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => {
322 impl Instance for peripherals::$inst {
323 type Interrupt = crate::interrupt::typelevel::$irq;
324 }
325
326 impl sealed::Instance for peripherals::$inst {
327 fn regs() -> crate::pac::hash::Hash {
328 crate::pac::$inst
329 }
330 }
331 };
332);
333
334dma_trait!(Dma, Instance);
diff --git a/embassy-stm32/src/hash/v2v3.rs b/embassy-stm32/src/hash/v2v3.rs
new file mode 100644
index 000000000..058864568
--- /dev/null
+++ b/embassy-stm32/src/hash/v2v3.rs
@@ -0,0 +1,385 @@
1//! Hash generator (HASH)
2use core::cmp::min;
3use core::future::poll_fn;
4use core::marker::PhantomData;
5use core::ptr;
6use core::task::Poll;
7
8use embassy_hal_internal::{into_ref, PeripheralRef};
9use embassy_sync::waitqueue::AtomicWaker;
10use stm32_metapac::hash::regs::*;
11
12use crate::dma::Transfer;
13use crate::interrupt::typelevel::Interrupt;
14use crate::peripherals::HASH;
15use crate::rcc::sealed::RccPeripheral;
16use crate::{interrupt, pac, peripherals, Peripheral};
17
18#[cfg(hash_v2)]
19const NUM_CONTEXT_REGS: usize = 54;
20#[cfg(hash_v3)]
21const NUM_CONTEXT_REGS: usize = 103;
22const DIGEST_BLOCK_SIZE: usize = 64;
23const MAX_DIGEST_SIZE: usize = 64;
24
25static HASH_WAKER: AtomicWaker = AtomicWaker::new();
26
27/// HASH interrupt handler.
28pub struct InterruptHandler<T: Instance> {
29 _phantom: PhantomData<T>,
30}
31
32impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
33 unsafe fn on_interrupt() {
34 let bits = T::regs().sr().read();
35 if bits.dinis() {
36 T::regs().imr().modify(|reg| reg.set_dinie(false));
37 HASH_WAKER.wake();
38 }
39 if bits.dcis() {
40 T::regs().imr().modify(|reg| reg.set_dcie(false));
41 HASH_WAKER.wake();
42 }
43 }
44}
45
46///Hash algorithm selection
47#[derive(Clone, Copy, PartialEq)]
48pub enum Algorithm {
49 /// SHA-1 Algorithm
50 SHA1 = 0,
51
52 #[cfg(hash_v2)]
53 /// MD5 Algorithm
54 MD5 = 1,
55
56 /// SHA-224 Algorithm
57 SHA224 = 2,
58
59 /// SHA-256 Algorithm
60 SHA256 = 3,
61
62 #[cfg(hash_v3)]
63 /// SHA-384 Algorithm
64 SHA384 = 12,
65
66 #[cfg(hash_v3)]
67 /// SHA-512/224 Algorithm
68 SHA512_224 = 13,
69
70 #[cfg(hash_v3)]
71 /// SHA-512/256 Algorithm
72 SHA512_256 = 14,
73
74 #[cfg(hash_v3)]
75 /// SHA-256 Algorithm
76 SHA512 = 15,
77}
78
79/// Input data width selection
80#[repr(u8)]
81#[derive(Clone, Copy)]
82pub enum DataType {
83 ///32-bit data, no data is swapped.
84 Width32 = 0,
85 ///16-bit data, each half-word is swapped.
86 Width16 = 1,
87 ///8-bit data, all bytes are swapped.
88 Width8 = 2,
89 ///1-bit data, all bits are swapped.
90 Width1 = 3,
91}
92
93/// Stores the state of the HASH peripheral for suspending/resuming
94/// digest calculation.
95pub struct Context {
96 buffer: [u8; DIGEST_BLOCK_SIZE],
97 buflen: usize,
98 algo: Algorithm,
99 format: DataType,
100 imr: u32,
101 str: u32,
102 cr: u32,
103 csr: [u32; NUM_CONTEXT_REGS],
104}
105
106/// HASH driver.
107pub struct Hash<'d, T: Instance, D: Dma<T>> {
108 _peripheral: PeripheralRef<'d, T>,
109 dma: PeripheralRef<'d, D>,
110}
111
112impl<'d, T: Instance, D: Dma<T>> Hash<'d, T, D> {
113 /// Instantiates, resets, and enables the HASH peripheral.
114 pub fn new(peripheral: impl Peripheral<P = T> + 'd, dma: impl Peripheral<P = D> + 'd) -> Self {
115 HASH::enable_and_reset();
116 into_ref!(peripheral, dma);
117 let instance = Self {
118 _peripheral: peripheral,
119 dma: dma,
120 };
121
122 T::Interrupt::unpend();
123 unsafe { T::Interrupt::enable() };
124
125 instance
126 }
127
128 /// Starts computation of a new hash and returns the saved peripheral state.
129 pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context {
130 // Define a context for this new computation.
131 let mut ctx = Context {
132 buffer: [0; DIGEST_BLOCK_SIZE],
133 buflen: 0,
134 algo: algorithm,
135 format: format,
136 imr: 0,
137 str: 0,
138 cr: 0,
139 csr: [0; NUM_CONTEXT_REGS],
140 };
141
142 // Set the data type in the peripheral.
143 T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8));
144
145 #[cfg(hash_v2)]
146 {
147 // Select the algorithm.
148 let mut algo0 = false;
149 let mut algo1 = false;
150 if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 {
151 algo0 = true;
152 }
153 if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 {
154 algo1 = true;
155 }
156 T::regs().cr().modify(|w| w.set_algo0(algo0));
157 T::regs().cr().modify(|w| w.set_algo1(algo1));
158 }
159
160 #[cfg(hash_v3)]
161 T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8));
162
163 // Enable multiple DMA transfers.
164 T::regs().cr().modify(|w| w.set_mdmat(true));
165
166 // Set init to load the context registers. Necessary before storing context.
167 T::regs().cr().modify(|w| w.set_init(true));
168
169 // Store and return the state of the peripheral.
170 self.store_context(&mut ctx).await;
171 ctx
172 }
173
174 /// Restores the peripheral state using the given context,
175 /// then updates the state with the provided data.
176 /// Peripheral state is saved upon return.
177 pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) {
178 let data_waiting = input.len() + ctx.buflen;
179 if data_waiting < DIGEST_BLOCK_SIZE {
180 // There isn't enough data to digest a block, so append it to the buffer.
181 ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input);
182 ctx.buflen += input.len();
183 return;
184 }
185
186 // Restore the peripheral state.
187 self.load_context(&ctx);
188
189 let mut ilen_remaining = input.len();
190 let mut input_start = 0;
191
192 // First ingest the data in the buffer.
193 let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen;
194 if empty_len > 0 {
195 let copy_len = min(empty_len, ilen_remaining);
196 ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]);
197 ctx.buflen += copy_len;
198 ilen_remaining -= copy_len;
199 input_start += copy_len;
200 }
201 self.accumulate(&ctx.buffer).await;
202 ctx.buflen = 0;
203
204 // Move any extra data to the now-empty buffer.
205 let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE;
206 if leftovers > 0 {
207 assert!(ilen_remaining >= leftovers);
208 ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]);
209 ctx.buflen += leftovers;
210 ilen_remaining -= leftovers;
211 } else {
212 ctx.buffer
213 .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]);
214 ctx.buflen += DIGEST_BLOCK_SIZE;
215 ilen_remaining -= DIGEST_BLOCK_SIZE;
216 }
217
218 // Hash the remaining data.
219 self.accumulate(&input[input_start..input_start + ilen_remaining]).await;
220
221 // Save the peripheral context.
222 self.store_context(ctx).await;
223 }
224
225 /// Computes a digest for the given context. A slice of the provided digest buffer is returned.
226 /// The length of the returned slice is dependent on the digest length of the selected algorithm.
227 pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; MAX_DIGEST_SIZE]) -> &'a [u8] {
228 // Restore the peripheral state.
229 self.load_context(&ctx);
230
231 // Must be cleared prior to the last DMA transfer.
232 T::regs().cr().modify(|w| w.set_mdmat(false));
233
234 // Hash the leftover bytes, if any.
235 self.accumulate(&ctx.buffer[0..ctx.buflen]).await;
236 ctx.buflen = 0;
237
238 // Wait for completion.
239 poll_fn(|cx| {
240 // Check if already done.
241 let bits = T::regs().sr().read();
242 if bits.dcis() {
243 return Poll::Ready(());
244 }
245 // Register waker, then enable interrupts.
246 HASH_WAKER.register(cx.waker());
247 T::regs().imr().modify(|reg| reg.set_dinie(true));
248 // Check for completion.
249 let bits = T::regs().sr().read();
250 if bits.dcis() {
251 Poll::Ready(())
252 } else {
253 Poll::Pending
254 }
255 })
256 .await;
257
258 // Return the digest.
259 let digest_words = match ctx.algo {
260 Algorithm::SHA1 => 5,
261 #[cfg(hash_v2)]
262 Algorithm::MD5 => 4,
263 Algorithm::SHA224 => 7,
264 Algorithm::SHA256 => 8,
265 #[cfg(hash_v3)]
266 Algorithm::SHA384 => 12,
267 #[cfg(hash_v3)]
268 Algorithm::SHA512_224 => 7,
269 #[cfg(hash_v3)]
270 Algorithm::SHA512_256 => 8,
271 #[cfg(hash_v3)]
272 Algorithm::SHA512 => 16,
273 };
274 let mut i = 0;
275 while i < digest_words {
276 let word = T::regs().hr(i).read();
277 digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice());
278 i += 1;
279 }
280 &digest[0..digest_words * 4]
281 }
282
283 /// Push data into the hash core.
284 async fn accumulate(&mut self, input: &[u8]) {
285 // Ignore an input length of 0.
286 if input.len() == 0 {
287 return;
288 }
289
290 // Set the number of valid bits.
291 let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8;
292 T::regs().str().modify(|w| w.set_nblw(num_valid_bits));
293
294 // Configure DMA to transfer input to hash core.
295 let dma_request = self.dma.request();
296 let dst_ptr = T::regs().din().as_ptr();
297 let mut num_words = input.len() / 4;
298 if input.len() % 4 > 0 {
299 num_words += 1;
300 }
301 let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words);
302 let dma_transfer =
303 unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) };
304 T::regs().cr().modify(|w| w.set_dmae(true));
305
306 // Wait for the transfer to complete.
307 dma_transfer.await;
308 }
309
310 /// Save the peripheral state to a context.
311 async fn store_context(&mut self, ctx: &mut Context) {
312 // Wait for interrupt.
313 poll_fn(|cx| {
314 // Check if already done.
315 let bits = T::regs().sr().read();
316 if bits.dinis() {
317 return Poll::Ready(());
318 }
319 // Register waker, then enable interrupts.
320 HASH_WAKER.register(cx.waker());
321 T::regs().imr().modify(|reg| reg.set_dinie(true));
322 // Check for completion.
323 let bits = T::regs().sr().read();
324 if bits.dinis() {
325 Poll::Ready(())
326 } else {
327 Poll::Pending
328 }
329 })
330 .await;
331
332 ctx.imr = T::regs().imr().read().0;
333 ctx.str = T::regs().str().read().0;
334 ctx.cr = T::regs().cr().read().0;
335 let mut i = 0;
336 while i < NUM_CONTEXT_REGS {
337 ctx.csr[i] = T::regs().csr(i).read();
338 i += 1;
339 }
340 }
341
342 /// Restore the peripheral state from a context.
343 fn load_context(&mut self, ctx: &Context) {
344 // Restore the peripheral state from the context.
345 T::regs().imr().write_value(Imr { 0: ctx.imr });
346 T::regs().str().write_value(Str { 0: ctx.str });
347 T::regs().cr().write_value(Cr { 0: ctx.cr });
348 T::regs().cr().modify(|w| w.set_init(true));
349 let mut i = 0;
350 while i < NUM_CONTEXT_REGS {
351 T::regs().csr(i).write_value(ctx.csr[i]);
352 i += 1;
353 }
354 }
355}
356
357pub(crate) mod sealed {
358 use super::*;
359
360 pub trait Instance {
361 fn regs() -> pac::hash::Hash;
362 }
363}
364
365/// HASH instance trait.
366pub trait Instance: sealed::Instance + Peripheral<P = Self> + crate::rcc::RccPeripheral + 'static + Send {
367 /// Interrupt for this HASH instance.
368 type Interrupt: interrupt::typelevel::Interrupt;
369}
370
371foreach_interrupt!(
372 ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => {
373 impl Instance for peripherals::$inst {
374 type Interrupt = crate::interrupt::typelevel::$irq;
375 }
376
377 impl sealed::Instance for peripherals::$inst {
378 fn regs() -> crate::pac::hash::Hash {
379 crate::pac::$inst
380 }
381 }
382 };
383);
384
385dma_trait!(Dma, Instance);
diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs
index 5bff48197..9a608e909 100644
--- a/examples/stm32f7/src/bin/eth.rs
+++ b/examples/stm32f7/src/bin/eth.rs
@@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _};
19 19
20bind_interrupts!(struct Irqs { 20bind_interrupts!(struct Irqs {
21 ETH => eth::InterruptHandler; 21 ETH => eth::InterruptHandler;
22 RNG => rng::InterruptHandler<peripherals::RNG>; 22 HASH_RNG => rng::InterruptHandler<peripherals::RNG>;
23}); 23});
24 24
25type Device = Ethernet<'static, ETH, GenericSMI>; 25type Device = Ethernet<'static, ETH, GenericSMI>;
diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs
index a9f5aa197..cf52cea5c 100644
--- a/examples/stm32f7/src/bin/hash.rs
+++ b/examples/stm32f7/src/bin/hash.rs
@@ -3,12 +3,12 @@
3 3
4use defmt::info; 4use defmt::info;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_stm32::hash::*;
6use embassy_stm32::Config; 7use embassy_stm32::Config;
7use embassy_time::Instant; 8use embassy_time::Instant;
8use {defmt_rtt as _, panic_probe as _};
9 9
10use embassy_stm32::hash::*;
11use sha2::{Digest, Sha256}; 10use sha2::{Digest, Sha256};
11use {defmt_rtt as _, panic_probe as _};
12 12
13#[embassy_executor::main] 13#[embassy_executor::main]
14async fn main(_spawner: Spawner) -> ! { 14async fn main(_spawner: Spawner) -> ! {
@@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) -> ! {
26 let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; 26 let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await;
27 hw_hasher.update(&mut context, test_1).await; 27 hw_hasher.update(&mut context, test_1).await;
28 hw_hasher.update(&mut context, test_2).await; 28 hw_hasher.update(&mut context, test_2).await;
29 let mut buffer: [u8; 32] = [0; 32]; 29 let mut buffer: [u8; 64] = [0; 64];
30 let hw_digest = hw_hasher.finish(context, &mut buffer).await; 30 let hw_digest = hw_hasher.finish(context, &mut buffer).await;
31 31
32 let hw_end_time = Instant::now(); 32 let hw_end_time = Instant::now();