From 6e9ddd46267fd0fce2333af4f15bfd86f6f17f4d Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:21:36 -0500 Subject: [PATCH 01/14] Added hash module with blocking implementation. Included SHA256 example. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/hash/mod.rs | 260 +++++++++++++++++++++++++++++++ embassy-stm32/src/lib.rs | 2 + examples/stm32f7/Cargo.toml | 5 +- examples/stm32f7/src/bin/hash.rs | 49 ++++++ 5 files changed, 316 insertions(+), 4 deletions(-) create mode 100644 embassy-stm32/src/hash/mod.rs create mode 100644 examples/stm32f7/src/bin/hash.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 70d4daf097..d8a4c65fa1 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab2bc2a739324793656ca1640e1caee2d88df72d" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -87,7 +87,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab2bc2a739324793656ca1640e1caee2d88df72d", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs new file mode 100644 index 0000000000..e3d2d7b160 --- /dev/null +++ b/embassy-stm32/src/hash/mod.rs @@ -0,0 +1,260 @@ +//! Hash generator (HASH) +use core::cmp::min; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +use stm32_metapac::hash::regs::*; + +use crate::pac::HASH as PAC_HASH; +use crate::peripherals::HASH; +use crate::rcc::sealed::RccPeripheral; +use crate::Peripheral; + +const NUM_CONTEXT_REGS: usize = 54; +const HASH_BUFFER_LEN: usize = 68; +const DIGEST_BLOCK_SIZE: usize = 64; + +///Hash algorithm selection +#[derive(PartialEq)] +pub enum Algorithm { + /// SHA-1 Algorithm + SHA1 = 0, + /// MD5 Algorithm + MD5 = 1, + /// SHA-224 Algorithm + SHA224 = 2, + /// SHA-256 Algorithm + SHA256 = 3, +} + +/// Input data width selection +#[repr(u8)] +#[derive(Clone, Copy)] +pub enum DataType { + ///32-bit data, no data is swapped. + Width32 = 0, + ///16-bit data, each half-word is swapped. + Width16 = 1, + ///8-bit data, all bytes are swapped. + Width8 = 2, + ///1-bit data, all bits are swapped. + Width1 = 3, +} + +/// Stores the state of the HASH peripheral for suspending/resuming +/// digest calculation. +pub struct Context { + first_word_sent: bool, + buffer: [u8; HASH_BUFFER_LEN], + buflen: usize, + algo: Algorithm, + format: DataType, + imr: u32, + str: u32, + cr: u32, + csr: [u32; NUM_CONTEXT_REGS], +} + +/// HASH driver. +pub struct Hash<'d> { + _peripheral: PeripheralRef<'d, HASH>, +} + +impl<'d> Hash<'d> { + /// Instantiates, resets, and enables the HASH peripheral. + pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + HASH::enable_and_reset(); + into_ref!(peripheral); + let instance = Self { + _peripheral: peripheral, + }; + instance + } + + /// Starts computation of a new hash and returns the saved peripheral state. + pub fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { + // Define a context for this new computation. + let mut ctx = Context { + first_word_sent: false, + buffer: [0; 68], + buflen: 0, + algo: algorithm, + format: format, + imr: 0, + str: 0, + cr: 0, + csr: [0; NUM_CONTEXT_REGS], + }; + + // Set the data type in the peripheral. + PAC_HASH.cr().modify(|w| w.set_datatype(ctx.format as u8)); + + // Select the algorithm. + let mut algo0 = false; + let mut algo1 = false; + if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { + algo0 = true; + } + if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { + algo1 = true; + } + PAC_HASH.cr().modify(|w| w.set_algo0(algo0)); + PAC_HASH.cr().modify(|w| w.set_algo1(algo1)); + PAC_HASH.cr().modify(|w| w.set_init(true)); + + // Store and return the state of the peripheral. + self.store_context(&mut ctx); + ctx + } + + /// Restores the peripheral state using the given context, + /// then updates the state with the provided data. + pub fn update(&mut self, ctx: &mut Context, input: &[u8]) { + let mut data_waiting = input.len() + ctx.buflen; + if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { + // There isn't enough data to digest a block, so append it to the buffer. + ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); + ctx.buflen += input.len(); + return; + } + + //Restore the peripheral state. + self.load_context(&ctx); + + let mut ilen_remaining = input.len(); + let mut input_start = 0; + + // Handle first block. + if !ctx.first_word_sent { + let empty_len = ctx.buffer.len() - ctx.buflen; + let copy_len = min(empty_len, ilen_remaining); + // Fill the buffer. + if copy_len > 0 { + ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); + ctx.buflen += copy_len; + ilen_remaining -= copy_len; + input_start += copy_len; + } + assert_eq!(ctx.buflen, HASH_BUFFER_LEN); + self.accumulate(ctx.buffer.as_slice()); + data_waiting -= ctx.buflen; + ctx.buflen = 0; + ctx.first_word_sent = true; + } + + if data_waiting < 64 { + // There isn't enough data remaining to process another block, so store it. + assert_eq!(ctx.buflen, 0); + ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); + ctx.buflen += ilen_remaining; + } else { + let mut total_data_sent = 0; + // First ingest the data in the buffer. + let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; + if empty_len > 0 { + let copy_len = min(empty_len, ilen_remaining); + ctx.buffer[ctx.buflen..ctx.buflen + copy_len] + .copy_from_slice(&input[input_start..input_start + copy_len]); + ctx.buflen += copy_len; + ilen_remaining -= copy_len; + input_start += copy_len; + } + assert_eq!(ctx.buflen % 64, 0); + self.accumulate(&ctx.buffer[0..64]); + total_data_sent += ctx.buflen; + ctx.buflen = 0; + + // Move any extra data to the now-empty buffer. + let leftovers = ilen_remaining % 64; + if leftovers > 0 { + assert!(ilen_remaining >= leftovers); + ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); + ctx.buflen += leftovers; + ilen_remaining -= leftovers; + } + assert_eq!(ilen_remaining % 64, 0); + + // Hash the remaining data. + self.accumulate(&input[input_start..input_start + ilen_remaining]); + + total_data_sent += ilen_remaining; + assert_eq!(total_data_sent % 64, 0); + assert!(total_data_sent >= 64); + } + + // Save the peripheral context. + self.store_context(ctx); + } + + /// Computes a digest for the given context. A slice of the provided digest buffer is returned. + /// The length of the returned slice is dependent on the digest length of the selected algorithm. + pub fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'a [u8] { + // Restore the peripheral state. + self.load_context(&ctx); + // Hash the leftover bytes, if any. + self.accumulate(&ctx.buffer[0..ctx.buflen]); + ctx.buflen = 0; + + //Start the digest calculation. + PAC_HASH.str().write(|w| w.set_dcal(true)); + + //Wait for completion. + while !PAC_HASH.sr().read().dcis() {} + + //Return the digest. + let digest_words = match ctx.algo { + Algorithm::SHA1 => 5, + Algorithm::MD5 => 4, + Algorithm::SHA224 => 7, + Algorithm::SHA256 => 8, + }; + let mut i = 0; + while i < digest_words { + let word = PAC_HASH.hr(i).read(); + digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); + i += 1; + } + &digest[0..digest_words * 4] + } + + fn accumulate(&mut self, input: &[u8]) { + //Set the number of valid bits. + let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; + PAC_HASH.str().modify(|w| w.set_nblw(num_valid_bits)); + + let mut i = 0; + while i < input.len() { + let mut word: [u8; 4] = [0; 4]; + let copy_idx = min(i + 4, input.len()); + word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); + PAC_HASH.din().write_value(u32::from_ne_bytes(word)); + i += 4; + } + } + + /// Save the peripheral state to a context. + fn store_context(&mut self, ctx: &mut Context) { + while !PAC_HASH.sr().read().dinis() {} + ctx.imr = PAC_HASH.imr().read().0; + ctx.str = PAC_HASH.str().read().0; + ctx.cr = PAC_HASH.cr().read().0; + let mut i = 0; + while i < NUM_CONTEXT_REGS { + ctx.csr[i] = PAC_HASH.csr(i).read(); + i += 1; + } + } + + /// Restore the peripheral state from a context. + fn load_context(&mut self, ctx: &Context) { + // Restore the peripheral state from the context. + PAC_HASH.imr().write_value(Imr { 0: ctx.imr }); + PAC_HASH.str().write_value(Str { 0: ctx.str }); + PAC_HASH.cr().write_value(Cr { 0: ctx.cr }); + PAC_HASH.cr().modify(|w| w.set_init(true)); + let mut i = 0; + while i < NUM_CONTEXT_REGS { + PAC_HASH.csr(i).write_value(ctx.csr[i]); + i += 1; + } + } +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index a465fccd84..cd1ede0fae 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -45,6 +45,8 @@ pub mod exti; pub mod flash; #[cfg(fmc)] pub mod fmc; +#[cfg(hash)] +pub mod hash; #[cfg(hrtim)] pub mod hrtim; #[cfg(i2c)] diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 941ba38cdf..a612c25548 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -5,8 +5,8 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -# Change stm32f767zi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } +# Change stm32f777zi to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } @@ -28,6 +28,7 @@ rand_core = "0.6.3" critical-section = "1.1" embedded-storage = "0.3.1" static_cell = "2" +sha2 = { version = "0.10.8", default-features = false } [profile.release] debug = 2 diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs new file mode 100644 index 0000000000..1fd0e87ebe --- /dev/null +++ b/examples/stm32f7/src/bin/hash.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::Config; +use embassy_time::{Duration, Instant}; +use {defmt_rtt as _, panic_probe as _}; + +use embassy_stm32::hash::*; +use sha2::{Digest, Sha256}; + +const TEST_STRING_1: &[u8] = b"hello world"; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let config = Config::default(); + let p = embassy_stm32::init(config); + + let hw_start_time = Instant::now(); + + // Compute a digest in hardware. + let mut hw_hasher = Hash::new(p.HASH); + let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8); + hw_hasher.update(&mut context, TEST_STRING_1); + let mut buffer: [u8; 32] = [0; 32]; + let hw_digest = hw_hasher.finish(context, &mut buffer); + + let hw_end_time = Instant::now(); + let hw_execution_time = hw_end_time - hw_start_time; + + let sw_start_time = Instant::now(); + + // Compute a digest in software. + let mut sw_hasher = Sha256::new(); + sw_hasher.update(TEST_STRING_1); + let sw_digest = sw_hasher.finalize(); + + let sw_end_time = Instant::now(); + let sw_execution_time = sw_end_time - sw_start_time; + + info!("Hardware Digest: {:?}", hw_digest); + info!("Software Digest: {:?}", sw_digest[..]); + info!("Hardware Execution Time: {:?}", hw_execution_time); + info!("Software Execution Time: {:?}", sw_execution_time); + assert_eq!(*hw_digest, sw_digest[..]); + + loop {} +} From 1dbfa5ab72e3596932ccb6bd258fac70d2efa563 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:28:12 -0500 Subject: [PATCH 02/14] Added hash v1/v2 configs. --- embassy-stm32/src/hash/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index e3d2d7b160..622777b02a 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -9,7 +9,11 @@ use crate::peripherals::HASH; use crate::rcc::sealed::RccPeripheral; use crate::Peripheral; +#[cfg(hash_v1)] +const NUM_CONTEXT_REGS: usize = 51; +#[cfg(hash_v2)] const NUM_CONTEXT_REGS: usize = 54; + const HASH_BUFFER_LEN: usize = 68; const DIGEST_BLOCK_SIZE: usize = 64; @@ -20,8 +24,10 @@ pub enum Algorithm { SHA1 = 0, /// MD5 Algorithm MD5 = 1, + #[cfg(hash_v2)] /// SHA-224 Algorithm SHA224 = 2, + #[cfg(hash_v2)] /// SHA-256 Algorithm SHA256 = 3, } From 10275309021d933d5dfe4c0a96928432e11cd8b4 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:27:25 -0500 Subject: [PATCH 03/14] Added hash interrupts for async. --- embassy-stm32/src/hash/mod.rs | 171 ++++++++++++++++++++++++++-------- 1 file changed, 134 insertions(+), 37 deletions(-) diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 622777b02a..4e37e60e10 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -1,22 +1,47 @@ //! Hash generator (HASH) use core::cmp::min; +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; use embassy_hal_internal::{into_ref, PeripheralRef}; -use stm32_metapac::hash::regs::*; +use embassy_sync::waitqueue::AtomicWaker; -use crate::pac::HASH as PAC_HASH; use crate::peripherals::HASH; +use stm32_metapac::hash::regs::*; + +use crate::interrupt::typelevel::Interrupt; use crate::rcc::sealed::RccPeripheral; -use crate::Peripheral; +use crate::{interrupt, pac, peripherals, Peripheral}; #[cfg(hash_v1)] const NUM_CONTEXT_REGS: usize = 51; #[cfg(hash_v2)] const NUM_CONTEXT_REGS: usize = 54; - const HASH_BUFFER_LEN: usize = 68; const DIGEST_BLOCK_SIZE: usize = 64; +static HASH_WAKER: AtomicWaker = AtomicWaker::new(); + +/// HASH interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let bits = T::regs().sr().read(); + if bits.dinis() { + T::regs().imr().modify(|reg| reg.set_dinie(false)); + HASH_WAKER.wake(); + } + if bits.dcis() { + T::regs().imr().modify(|reg| reg.set_dcie(false)); + HASH_WAKER.wake(); + } + } +} + ///Hash algorithm selection #[derive(PartialEq)] pub enum Algorithm { @@ -61,23 +86,27 @@ pub struct Context { } /// HASH driver. -pub struct Hash<'d> { - _peripheral: PeripheralRef<'d, HASH>, +pub struct Hash<'d, T: Instance> { + _peripheral: PeripheralRef<'d, T>, } -impl<'d> Hash<'d> { +impl<'d, T: Instance> Hash<'d, T> { /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { HASH::enable_and_reset(); into_ref!(peripheral); let instance = Self { _peripheral: peripheral, }; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + instance } /// Starts computation of a new hash and returns the saved peripheral state. - pub fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { + pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { // Define a context for this new computation. let mut ctx = Context { first_word_sent: false, @@ -92,7 +121,7 @@ impl<'d> Hash<'d> { }; // Set the data type in the peripheral. - PAC_HASH.cr().modify(|w| w.set_datatype(ctx.format as u8)); + T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); // Select the algorithm. let mut algo0 = false; @@ -103,18 +132,19 @@ impl<'d> Hash<'d> { if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { algo1 = true; } - PAC_HASH.cr().modify(|w| w.set_algo0(algo0)); - PAC_HASH.cr().modify(|w| w.set_algo1(algo1)); - PAC_HASH.cr().modify(|w| w.set_init(true)); + T::regs().cr().modify(|w| w.set_algo0(algo0)); + T::regs().cr().modify(|w| w.set_algo1(algo1)); + T::regs().cr().modify(|w| w.set_init(true)); // Store and return the state of the peripheral. - self.store_context(&mut ctx); + self.store_context(&mut ctx).await; ctx } /// Restores the peripheral state using the given context, /// then updates the state with the provided data. - pub fn update(&mut self, ctx: &mut Context, input: &[u8]) { + /// Peripheral state is saved upon return. + pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { let mut data_waiting = input.len() + ctx.buflen; if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { // There isn't enough data to digest a block, so append it to the buffer. @@ -123,7 +153,7 @@ impl<'d> Hash<'d> { return; } - //Restore the peripheral state. + // Restore the peripheral state. self.load_context(&ctx); let mut ilen_remaining = input.len(); @@ -154,6 +184,7 @@ impl<'d> Hash<'d> { ctx.buflen += ilen_remaining; } else { let mut total_data_sent = 0; + // First ingest the data in the buffer. let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; if empty_len > 0 { @@ -188,25 +219,43 @@ impl<'d> Hash<'d> { } // Save the peripheral context. - self.store_context(ctx); + self.store_context(ctx).await; } /// Computes a digest for the given context. A slice of the provided digest buffer is returned. /// The length of the returned slice is dependent on the digest length of the selected algorithm. - pub fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'a [u8] { + pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'a [u8] { // Restore the peripheral state. self.load_context(&ctx); + // Hash the leftover bytes, if any. self.accumulate(&ctx.buffer[0..ctx.buflen]); ctx.buflen = 0; //Start the digest calculation. - PAC_HASH.str().write(|w| w.set_dcal(true)); + T::regs().str().write(|w| w.set_dcal(true)); - //Wait for completion. - while !PAC_HASH.sr().read().dcis() {} + // Wait for completion. + poll_fn(|cx| { + // Check if already done. + let bits = T::regs().sr().read(); + if bits.dcis() { + return Poll::Ready(()); + } + // Register waker, then enable interrupts. + HASH_WAKER.register(cx.waker()); + T::regs().imr().modify(|reg| reg.set_dinie(true)); + // Check for completion. + let bits = T::regs().sr().read(); + if bits.dcis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; - //Return the digest. + // Return the digest. let digest_words = match ctx.algo { Algorithm::SHA1 => 5, Algorithm::MD5 => 4, @@ -215,37 +264,57 @@ impl<'d> Hash<'d> { }; let mut i = 0; while i < digest_words { - let word = PAC_HASH.hr(i).read(); + let word = T::regs().hr(i).read(); digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); i += 1; } &digest[0..digest_words * 4] } + /// Push data into the hash core. fn accumulate(&mut self, input: &[u8]) { - //Set the number of valid bits. + // Set the number of valid bits. let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; - PAC_HASH.str().modify(|w| w.set_nblw(num_valid_bits)); + T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); let mut i = 0; while i < input.len() { let mut word: [u8; 4] = [0; 4]; let copy_idx = min(i + 4, input.len()); word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); - PAC_HASH.din().write_value(u32::from_ne_bytes(word)); + T::regs().din().write_value(u32::from_ne_bytes(word)); i += 4; } } /// Save the peripheral state to a context. - fn store_context(&mut self, ctx: &mut Context) { - while !PAC_HASH.sr().read().dinis() {} - ctx.imr = PAC_HASH.imr().read().0; - ctx.str = PAC_HASH.str().read().0; - ctx.cr = PAC_HASH.cr().read().0; + async fn store_context(&mut self, ctx: &mut Context) { + // Wait for interrupt. + poll_fn(|cx| { + // Check if already done. + let bits = T::regs().sr().read(); + if bits.dinis() { + return Poll::Ready(()); + } + // Register waker, then enable interrupts. + HASH_WAKER.register(cx.waker()); + T::regs().imr().modify(|reg| reg.set_dinie(true)); + // Check for completion. + let bits = T::regs().sr().read(); + if bits.dinis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + ctx.imr = T::regs().imr().read().0; + ctx.str = T::regs().str().read().0; + ctx.cr = T::regs().cr().read().0; let mut i = 0; while i < NUM_CONTEXT_REGS { - ctx.csr[i] = PAC_HASH.csr(i).read(); + ctx.csr[i] = T::regs().csr(i).read(); i += 1; } } @@ -253,14 +322,42 @@ impl<'d> Hash<'d> { /// Restore the peripheral state from a context. fn load_context(&mut self, ctx: &Context) { // Restore the peripheral state from the context. - PAC_HASH.imr().write_value(Imr { 0: ctx.imr }); - PAC_HASH.str().write_value(Str { 0: ctx.str }); - PAC_HASH.cr().write_value(Cr { 0: ctx.cr }); - PAC_HASH.cr().modify(|w| w.set_init(true)); + T::regs().imr().write_value(Imr { 0: ctx.imr }); + T::regs().str().write_value(Str { 0: ctx.str }); + T::regs().cr().write_value(Cr { 0: ctx.cr }); + T::regs().cr().modify(|w| w.set_init(true)); let mut i = 0; while i < NUM_CONTEXT_REGS { - PAC_HASH.csr(i).write_value(ctx.csr[i]); + T::regs().csr(i).write_value(ctx.csr[i]); i += 1; } } } + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + fn regs() -> pac::hash::Hash; + } +} + +/// HASH instance trait. +pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { + /// Interrupt for this HASH instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +foreach_interrupt!( + ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { + impl Instance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } + + impl sealed::Instance for peripherals::$inst { + fn regs() -> crate::pac::hash::Hash { + crate::pac::$inst + } + } + }; +); From 72bbfec39d3f826c1a8dd485af2da4bcbdd32e35 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sat, 3 Feb 2024 16:10:00 -0500 Subject: [PATCH 04/14] Added hash DMA implementation. --- embassy-stm32/build.rs | 1 + embassy-stm32/src/hash/mod.rs | 141 ++++++++++++++----------------- examples/stm32f7/src/bin/hash.rs | 20 +++-- 3 files changed, 78 insertions(+), 84 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 948ce3afff..1a68dfc9d3 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1015,6 +1015,7 @@ fn main() { (("dac", "CH1"), quote!(crate::dac::DacDma1)), (("dac", "CH2"), quote!(crate::dac::DacDma2)), (("timer", "UP"), quote!(crate::timer::UpDma)), + (("hash", "IN"), quote!(crate::hash::Dma)), ] .into(); diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 4e37e60e10..ac4854f804 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -2,11 +2,13 @@ use core::cmp::min; use core::future::poll_fn; use core::marker::PhantomData; +use core::ptr; use core::task::Poll; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use crate::dma::Transfer; use crate::peripherals::HASH; use stm32_metapac::hash::regs::*; @@ -18,7 +20,6 @@ use crate::{interrupt, pac, peripherals, Peripheral}; const NUM_CONTEXT_REGS: usize = 51; #[cfg(hash_v2)] const NUM_CONTEXT_REGS: usize = 54; -const HASH_BUFFER_LEN: usize = 68; const DIGEST_BLOCK_SIZE: usize = 64; static HASH_WAKER: AtomicWaker = AtomicWaker::new(); @@ -74,8 +75,7 @@ pub enum DataType { /// Stores the state of the HASH peripheral for suspending/resuming /// digest calculation. pub struct Context { - first_word_sent: bool, - buffer: [u8; HASH_BUFFER_LEN], + buffer: [u8; DIGEST_BLOCK_SIZE], buflen: usize, algo: Algorithm, format: DataType, @@ -86,17 +86,19 @@ pub struct Context { } /// HASH driver. -pub struct Hash<'d, T: Instance> { +pub struct Hash<'d, T: Instance, D: Dma> { _peripheral: PeripheralRef<'d, T>, + dma: PeripheralRef<'d, D>, } -impl<'d, T: Instance> Hash<'d, T> { +impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + pub fn new(peripheral: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { HASH::enable_and_reset(); - into_ref!(peripheral); + into_ref!(peripheral, dma); let instance = Self { _peripheral: peripheral, + dma: dma, }; T::Interrupt::unpend(); @@ -109,8 +111,7 @@ impl<'d, T: Instance> Hash<'d, T> { pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { // Define a context for this new computation. let mut ctx = Context { - first_word_sent: false, - buffer: [0; 68], + buffer: [0; DIGEST_BLOCK_SIZE], buflen: 0, algo: algorithm, format: format, @@ -134,6 +135,11 @@ impl<'d, T: Instance> Hash<'d, T> { } T::regs().cr().modify(|w| w.set_algo0(algo0)); T::regs().cr().modify(|w| w.set_algo1(algo1)); + + // Enable multiple DMA transfers. + T::regs().cr().modify(|w| w.set_mdmat(true)); + + // Set init to load the context registers. Necessary before storing context. T::regs().cr().modify(|w| w.set_init(true)); // Store and return the state of the peripheral. @@ -145,8 +151,8 @@ impl<'d, T: Instance> Hash<'d, T> { /// then updates the state with the provided data. /// Peripheral state is saved upon return. pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { - let mut data_waiting = input.len() + ctx.buflen; - if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { + let data_waiting = input.len() + ctx.buflen; + if data_waiting < DIGEST_BLOCK_SIZE { // There isn't enough data to digest a block, so append it to the buffer. ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); ctx.buflen += input.len(); @@ -159,65 +165,35 @@ impl<'d, T: Instance> Hash<'d, T> { let mut ilen_remaining = input.len(); let mut input_start = 0; - // Handle first block. - if !ctx.first_word_sent { - let empty_len = ctx.buffer.len() - ctx.buflen; + // First ingest the data in the buffer. + let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; + if empty_len > 0 { let copy_len = min(empty_len, ilen_remaining); - // Fill the buffer. - if copy_len > 0 { - ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); - ctx.buflen += copy_len; - ilen_remaining -= copy_len; - input_start += copy_len; - } - assert_eq!(ctx.buflen, HASH_BUFFER_LEN); - self.accumulate(ctx.buffer.as_slice()); - data_waiting -= ctx.buflen; - ctx.buflen = 0; - ctx.first_word_sent = true; + ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]); + ctx.buflen += copy_len; + ilen_remaining -= copy_len; + input_start += copy_len; } + self.accumulate(&ctx.buffer).await; + ctx.buflen = 0; - if data_waiting < 64 { - // There isn't enough data remaining to process another block, so store it. - assert_eq!(ctx.buflen, 0); - ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); - ctx.buflen += ilen_remaining; + // Move any extra data to the now-empty buffer. + let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE; + if leftovers > 0 { + assert!(ilen_remaining >= leftovers); + ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); + ctx.buflen += leftovers; + ilen_remaining -= leftovers; } else { - let mut total_data_sent = 0; - - // First ingest the data in the buffer. - let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; - if empty_len > 0 { - let copy_len = min(empty_len, ilen_remaining); - ctx.buffer[ctx.buflen..ctx.buflen + copy_len] - .copy_from_slice(&input[input_start..input_start + copy_len]); - ctx.buflen += copy_len; - ilen_remaining -= copy_len; - input_start += copy_len; - } - assert_eq!(ctx.buflen % 64, 0); - self.accumulate(&ctx.buffer[0..64]); - total_data_sent += ctx.buflen; - ctx.buflen = 0; - - // Move any extra data to the now-empty buffer. - let leftovers = ilen_remaining % 64; - if leftovers > 0 { - assert!(ilen_remaining >= leftovers); - ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); - ctx.buflen += leftovers; - ilen_remaining -= leftovers; - } - assert_eq!(ilen_remaining % 64, 0); - - // Hash the remaining data. - self.accumulate(&input[input_start..input_start + ilen_remaining]); - - total_data_sent += ilen_remaining; - assert_eq!(total_data_sent % 64, 0); - assert!(total_data_sent >= 64); + ctx.buffer + .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]); + ctx.buflen += DIGEST_BLOCK_SIZE; + ilen_remaining -= DIGEST_BLOCK_SIZE; } + // Hash the remaining data. + self.accumulate(&input[input_start..input_start + ilen_remaining]).await; + // Save the peripheral context. self.store_context(ctx).await; } @@ -228,13 +204,13 @@ impl<'d, T: Instance> Hash<'d, T> { // Restore the peripheral state. self.load_context(&ctx); + // Must be cleared prior to the last DMA transfer. + T::regs().cr().modify(|w| w.set_mdmat(false)); + // Hash the leftover bytes, if any. - self.accumulate(&ctx.buffer[0..ctx.buflen]); + self.accumulate(&ctx.buffer[0..ctx.buflen]).await; ctx.buflen = 0; - //Start the digest calculation. - T::regs().str().write(|w| w.set_dcal(true)); - // Wait for completion. poll_fn(|cx| { // Check if already done. @@ -272,19 +248,30 @@ impl<'d, T: Instance> Hash<'d, T> { } /// Push data into the hash core. - fn accumulate(&mut self, input: &[u8]) { + async fn accumulate(&mut self, input: &[u8]) { + // Ignore an input length of 0. + if input.len() == 0 { + return; + } + // Set the number of valid bits. let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); - let mut i = 0; - while i < input.len() { - let mut word: [u8; 4] = [0; 4]; - let copy_idx = min(i + 4, input.len()); - word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); - T::regs().din().write_value(u32::from_ne_bytes(word)); - i += 4; + // Configure DMA to transfer input to hash core. + let dma_request = self.dma.request(); + let dst_ptr = T::regs().din().as_ptr(); + let mut num_words = input.len() / 4; + if input.len() % 4 > 0 { + num_words += 1; } + let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); + let dma_transfer = + unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) }; + T::regs().cr().modify(|w| w.set_dmae(true)); + + // Wait for the transfer to complete. + dma_transfer.await; } /// Save the peripheral state to a context. @@ -361,3 +348,5 @@ foreach_interrupt!( } }; ); + +dma_trait!(Dma, Instance); diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 1fd0e87ebe..a9f5aa197f 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -4,27 +4,30 @@ use defmt::info; use embassy_executor::Spawner; use embassy_stm32::Config; -use embassy_time::{Duration, Instant}; +use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; use embassy_stm32::hash::*; use sha2::{Digest, Sha256}; -const TEST_STRING_1: &[u8] = b"hello world"; - #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { let config = Config::default(); let p = embassy_stm32::init(config); + let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh"; + let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr"; + + let mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7); + let hw_start_time = Instant::now(); // Compute a digest in hardware. - let mut hw_hasher = Hash::new(p.HASH); - let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8); - hw_hasher.update(&mut context, TEST_STRING_1); + let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; + hw_hasher.update(&mut context, test_1).await; + hw_hasher.update(&mut context, test_2).await; let mut buffer: [u8; 32] = [0; 32]; - let hw_digest = hw_hasher.finish(context, &mut buffer); + let hw_digest = hw_hasher.finish(context, &mut buffer).await; let hw_end_time = Instant::now(); let hw_execution_time = hw_end_time - hw_start_time; @@ -33,7 +36,8 @@ async fn main(_spawner: Spawner) -> ! { // Compute a digest in software. let mut sw_hasher = Sha256::new(); - sw_hasher.update(TEST_STRING_1); + sw_hasher.update(test_1); + sw_hasher.update(test_2); let sw_digest = sw_hasher.finalize(); let sw_end_time = Instant::now(); From 66f44b95d70547be8e32daac1ab611eec5fbe28a Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:16:33 -0500 Subject: [PATCH 05/14] Addressed hash CI build issues. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/hash/mod.rs | 357 +--------------------------- embassy-stm32/src/hash/v1.rs | 334 +++++++++++++++++++++++++++ embassy-stm32/src/hash/v2v3.rs | 385 +++++++++++++++++++++++++++++++ examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32f7/src/bin/hash.rs | 6 +- 6 files changed, 731 insertions(+), 357 deletions(-) create mode 100644 embassy-stm32/src/hash/v1.rs create mode 100644 embassy-stm32/src/hash/v2v3.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d8a4c65fa1..00d8a5f63f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -87,7 +87,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-0cb3a4fcaec702c93b3700715de796636d562b15", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index ac4854f804..6b23f3b55a 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -1,352 +1,7 @@ -//! Hash generator (HASH) -use core::cmp::min; -use core::future::poll_fn; -use core::marker::PhantomData; -use core::ptr; -use core::task::Poll; +//! Hash Accelerator (HASH) +#[cfg_attr(hash_v1, path = "v1.rs")] +#[cfg_attr(hash_v2, path = "v2v3.rs")] +#[cfg_attr(hash_v3, path = "v2v3.rs")] +mod _version; -use embassy_hal_internal::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; - -use crate::dma::Transfer; -use crate::peripherals::HASH; -use stm32_metapac::hash::regs::*; - -use crate::interrupt::typelevel::Interrupt; -use crate::rcc::sealed::RccPeripheral; -use crate::{interrupt, pac, peripherals, Peripheral}; - -#[cfg(hash_v1)] -const NUM_CONTEXT_REGS: usize = 51; -#[cfg(hash_v2)] -const NUM_CONTEXT_REGS: usize = 54; -const DIGEST_BLOCK_SIZE: usize = 64; - -static HASH_WAKER: AtomicWaker = AtomicWaker::new(); - -/// HASH interrupt handler. -pub struct InterruptHandler { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - unsafe fn on_interrupt() { - let bits = T::regs().sr().read(); - if bits.dinis() { - T::regs().imr().modify(|reg| reg.set_dinie(false)); - HASH_WAKER.wake(); - } - if bits.dcis() { - T::regs().imr().modify(|reg| reg.set_dcie(false)); - HASH_WAKER.wake(); - } - } -} - -///Hash algorithm selection -#[derive(PartialEq)] -pub enum Algorithm { - /// SHA-1 Algorithm - SHA1 = 0, - /// MD5 Algorithm - MD5 = 1, - #[cfg(hash_v2)] - /// SHA-224 Algorithm - SHA224 = 2, - #[cfg(hash_v2)] - /// SHA-256 Algorithm - SHA256 = 3, -} - -/// Input data width selection -#[repr(u8)] -#[derive(Clone, Copy)] -pub enum DataType { - ///32-bit data, no data is swapped. - Width32 = 0, - ///16-bit data, each half-word is swapped. - Width16 = 1, - ///8-bit data, all bytes are swapped. - Width8 = 2, - ///1-bit data, all bits are swapped. - Width1 = 3, -} - -/// Stores the state of the HASH peripheral for suspending/resuming -/// digest calculation. -pub struct Context { - buffer: [u8; DIGEST_BLOCK_SIZE], - buflen: usize, - algo: Algorithm, - format: DataType, - imr: u32, - str: u32, - cr: u32, - csr: [u32; NUM_CONTEXT_REGS], -} - -/// HASH driver. -pub struct Hash<'d, T: Instance, D: Dma> { - _peripheral: PeripheralRef<'d, T>, - dma: PeripheralRef<'d, D>, -} - -impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { - /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { - HASH::enable_and_reset(); - into_ref!(peripheral, dma); - let instance = Self { - _peripheral: peripheral, - dma: dma, - }; - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - instance - } - - /// Starts computation of a new hash and returns the saved peripheral state. - pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { - // Define a context for this new computation. - let mut ctx = Context { - buffer: [0; DIGEST_BLOCK_SIZE], - buflen: 0, - algo: algorithm, - format: format, - imr: 0, - str: 0, - cr: 0, - csr: [0; NUM_CONTEXT_REGS], - }; - - // Set the data type in the peripheral. - T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); - - // Select the algorithm. - let mut algo0 = false; - let mut algo1 = false; - if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { - algo0 = true; - } - if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { - algo1 = true; - } - T::regs().cr().modify(|w| w.set_algo0(algo0)); - T::regs().cr().modify(|w| w.set_algo1(algo1)); - - // Enable multiple DMA transfers. - T::regs().cr().modify(|w| w.set_mdmat(true)); - - // Set init to load the context registers. Necessary before storing context. - T::regs().cr().modify(|w| w.set_init(true)); - - // Store and return the state of the peripheral. - self.store_context(&mut ctx).await; - ctx - } - - /// Restores the peripheral state using the given context, - /// then updates the state with the provided data. - /// Peripheral state is saved upon return. - pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { - let data_waiting = input.len() + ctx.buflen; - if data_waiting < DIGEST_BLOCK_SIZE { - // There isn't enough data to digest a block, so append it to the buffer. - ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); - ctx.buflen += input.len(); - return; - } - - // Restore the peripheral state. - self.load_context(&ctx); - - let mut ilen_remaining = input.len(); - let mut input_start = 0; - - // First ingest the data in the buffer. - let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; - if empty_len > 0 { - let copy_len = min(empty_len, ilen_remaining); - ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]); - ctx.buflen += copy_len; - ilen_remaining -= copy_len; - input_start += copy_len; - } - self.accumulate(&ctx.buffer).await; - ctx.buflen = 0; - - // Move any extra data to the now-empty buffer. - let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE; - if leftovers > 0 { - assert!(ilen_remaining >= leftovers); - ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); - ctx.buflen += leftovers; - ilen_remaining -= leftovers; - } else { - ctx.buffer - .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]); - ctx.buflen += DIGEST_BLOCK_SIZE; - ilen_remaining -= DIGEST_BLOCK_SIZE; - } - - // Hash the remaining data. - self.accumulate(&input[input_start..input_start + ilen_remaining]).await; - - // Save the peripheral context. - self.store_context(ctx).await; - } - - /// Computes a digest for the given context. A slice of the provided digest buffer is returned. - /// The length of the returned slice is dependent on the digest length of the selected algorithm. - pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; 32]) -> &'a [u8] { - // Restore the peripheral state. - self.load_context(&ctx); - - // Must be cleared prior to the last DMA transfer. - T::regs().cr().modify(|w| w.set_mdmat(false)); - - // Hash the leftover bytes, if any. - self.accumulate(&ctx.buffer[0..ctx.buflen]).await; - ctx.buflen = 0; - - // Wait for completion. - poll_fn(|cx| { - // Check if already done. - let bits = T::regs().sr().read(); - if bits.dcis() { - return Poll::Ready(()); - } - // Register waker, then enable interrupts. - HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dinie(true)); - // Check for completion. - let bits = T::regs().sr().read(); - if bits.dcis() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - // Return the digest. - let digest_words = match ctx.algo { - Algorithm::SHA1 => 5, - Algorithm::MD5 => 4, - Algorithm::SHA224 => 7, - Algorithm::SHA256 => 8, - }; - let mut i = 0; - while i < digest_words { - let word = T::regs().hr(i).read(); - digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); - i += 1; - } - &digest[0..digest_words * 4] - } - - /// Push data into the hash core. - async fn accumulate(&mut self, input: &[u8]) { - // Ignore an input length of 0. - if input.len() == 0 { - return; - } - - // Set the number of valid bits. - let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; - T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); - - // Configure DMA to transfer input to hash core. - let dma_request = self.dma.request(); - let dst_ptr = T::regs().din().as_ptr(); - let mut num_words = input.len() / 4; - if input.len() % 4 > 0 { - num_words += 1; - } - let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); - let dma_transfer = - unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) }; - T::regs().cr().modify(|w| w.set_dmae(true)); - - // Wait for the transfer to complete. - dma_transfer.await; - } - - /// Save the peripheral state to a context. - async fn store_context(&mut self, ctx: &mut Context) { - // Wait for interrupt. - poll_fn(|cx| { - // Check if already done. - let bits = T::regs().sr().read(); - if bits.dinis() { - return Poll::Ready(()); - } - // Register waker, then enable interrupts. - HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dinie(true)); - // Check for completion. - let bits = T::regs().sr().read(); - if bits.dinis() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - ctx.imr = T::regs().imr().read().0; - ctx.str = T::regs().str().read().0; - ctx.cr = T::regs().cr().read().0; - let mut i = 0; - while i < NUM_CONTEXT_REGS { - ctx.csr[i] = T::regs().csr(i).read(); - i += 1; - } - } - - /// Restore the peripheral state from a context. - fn load_context(&mut self, ctx: &Context) { - // Restore the peripheral state from the context. - T::regs().imr().write_value(Imr { 0: ctx.imr }); - T::regs().str().write_value(Str { 0: ctx.str }); - T::regs().cr().write_value(Cr { 0: ctx.cr }); - T::regs().cr().modify(|w| w.set_init(true)); - let mut i = 0; - while i < NUM_CONTEXT_REGS { - T::regs().csr(i).write_value(ctx.csr[i]); - i += 1; - } - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - fn regs() -> pac::hash::Hash; - } -} - -/// HASH instance trait. -pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { - /// Interrupt for this HASH instance. - type Interrupt: interrupt::typelevel::Interrupt; -} - -foreach_interrupt!( - ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { - impl Instance for peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$irq; - } - - impl sealed::Instance for peripherals::$inst { - fn regs() -> crate::pac::hash::Hash { - crate::pac::$inst - } - } - }; -); - -dma_trait!(Dma, Instance); +pub use _version::*; diff --git a/embassy-stm32/src/hash/v1.rs b/embassy-stm32/src/hash/v1.rs new file mode 100644 index 0000000000..50f9adc834 --- /dev/null +++ b/embassy-stm32/src/hash/v1.rs @@ -0,0 +1,334 @@ +//! Hash generator (HASH) +use core::cmp::min; +use core::future::poll_fn; +use core::marker::PhantomData; +use core::task::Poll; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +use stm32_metapac::hash::regs::*; + +use crate::interrupt::typelevel::Interrupt; +use crate::peripherals::HASH; +use crate::rcc::sealed::RccPeripheral; +use crate::{interrupt, pac, peripherals, Peripheral}; + +const NUM_CONTEXT_REGS: usize = 51; +const HASH_BUFFER_LEN: usize = 68; +const DIGEST_BLOCK_SIZE: usize = 64; +const MAX_DIGEST_SIZE: usize = 20; + +static HASH_WAKER: AtomicWaker = AtomicWaker::new(); + +/// HASH interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let bits = T::regs().sr().read(); + if bits.dinis() { + T::regs().imr().modify(|reg| reg.set_dinie(false)); + HASH_WAKER.wake(); + } + if bits.dcis() { + T::regs().imr().modify(|reg| reg.set_dcie(false)); + HASH_WAKER.wake(); + } + } +} + +///Hash algorithm selection +#[derive(PartialEq)] +pub enum Algorithm { + /// SHA-1 Algorithm + SHA1 = 0, + /// MD5 Algorithm + MD5 = 1, +} + +/// Input data width selection +#[repr(u8)] +#[derive(Clone, Copy)] +pub enum DataType { + ///32-bit data, no data is swapped. + Width32 = 0, + ///16-bit data, each half-word is swapped. + Width16 = 1, + ///8-bit data, all bytes are swapped. + Width8 = 2, + ///1-bit data, all bits are swapped. + Width1 = 3, +} + +/// Stores the state of the HASH peripheral for suspending/resuming +/// digest calculation. +pub struct Context { + first_word_sent: bool, + buffer: [u8; HASH_BUFFER_LEN], + buflen: usize, + algo: Algorithm, + format: DataType, + imr: u32, + str: u32, + cr: u32, + csr: [u32; NUM_CONTEXT_REGS], +} + +/// HASH driver. +pub struct Hash<'d, T: Instance> { + _peripheral: PeripheralRef<'d, T>, +} + +impl<'d, T: Instance> Hash<'d, T> { + /// Instantiates, resets, and enables the HASH peripheral. + pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + HASH::enable_and_reset(); + into_ref!(peripheral); + let instance = Self { + _peripheral: peripheral, + }; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + instance + } + + /// Starts computation of a new hash and returns the saved peripheral state. + pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { + // Define a context for this new computation. + let mut ctx = Context { + first_word_sent: false, + buffer: [0; HASH_BUFFER_LEN], + buflen: 0, + algo: algorithm, + format: format, + imr: 0, + str: 0, + cr: 0, + csr: [0; NUM_CONTEXT_REGS], + }; + + // Set the data type in the peripheral. + T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); + + // Select the algorithm. + if ctx.algo == Algorithm::MD5 { + T::regs().cr().modify(|w| w.set_algo(true)); + } + + // Store and return the state of the peripheral. + self.store_context(&mut ctx).await; + ctx + } + + /// Restores the peripheral state using the given context, + /// then updates the state with the provided data. + /// Peripheral state is saved upon return. + pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { + let mut data_waiting = input.len() + ctx.buflen; + if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { + // There isn't enough data to digest a block, so append it to the buffer. + ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); + ctx.buflen += input.len(); + return; + } + + // Restore the peripheral state. + self.load_context(&ctx); + + let mut ilen_remaining = input.len(); + let mut input_start = 0; + + // Handle first block. + if !ctx.first_word_sent { + let empty_len = ctx.buffer.len() - ctx.buflen; + let copy_len = min(empty_len, ilen_remaining); + // Fill the buffer. + if copy_len > 0 { + ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); + ctx.buflen += copy_len; + ilen_remaining -= copy_len; + input_start += copy_len; + } + self.accumulate(ctx.buffer.as_slice()); + data_waiting -= ctx.buflen; + ctx.buflen = 0; + ctx.first_word_sent = true; + } + + if data_waiting < DIGEST_BLOCK_SIZE { + // There isn't enough data remaining to process another block, so store it. + ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); + ctx.buflen += ilen_remaining; + } else { + // First ingest the data in the buffer. + let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; + if empty_len > 0 { + let copy_len = min(empty_len, ilen_remaining); + ctx.buffer[ctx.buflen..ctx.buflen + copy_len] + .copy_from_slice(&input[input_start..input_start + copy_len]); + ctx.buflen += copy_len; + ilen_remaining -= copy_len; + input_start += copy_len; + } + self.accumulate(&ctx.buffer[0..64]); + ctx.buflen = 0; + + // Move any extra data to the now-empty buffer. + let leftovers = ilen_remaining % 64; + if leftovers > 0 { + ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); + ctx.buflen += leftovers; + ilen_remaining -= leftovers; + } + + // Hash the remaining data. + self.accumulate(&input[input_start..input_start + ilen_remaining]); + } + + // Save the peripheral context. + self.store_context(ctx).await; + } + + /// Computes a digest for the given context. A slice of the provided digest buffer is returned. + /// The length of the returned slice is dependent on the digest length of the selected algorithm. + pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; MAX_DIGEST_SIZE]) -> &'a [u8] { + // Restore the peripheral state. + self.load_context(&ctx); + + // Hash the leftover bytes, if any. + self.accumulate(&ctx.buffer[0..ctx.buflen]); + ctx.buflen = 0; + + //Start the digest calculation. + T::regs().str().write(|w| w.set_dcal(true)); + + // Wait for completion. + poll_fn(|cx| { + // Check if already done. + let bits = T::regs().sr().read(); + if bits.dcis() { + return Poll::Ready(()); + } + // Register waker, then enable interrupts. + HASH_WAKER.register(cx.waker()); + T::regs().imr().modify(|reg| reg.set_dinie(true)); + // Check for completion. + let bits = T::regs().sr().read(); + if bits.dcis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + // Return the digest. + let digest_words = match ctx.algo { + Algorithm::SHA1 => 5, + Algorithm::MD5 => 4, + }; + let mut i = 0; + while i < digest_words { + let word = T::regs().hr(i).read(); + digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); + i += 1; + } + &digest[0..digest_words * 4] + } + + /// Push data into the hash core. + fn accumulate(&mut self, input: &[u8]) { + // Set the number of valid bits. + let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; + T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); + + let mut i = 0; + while i < input.len() { + let mut word: [u8; 4] = [0; 4]; + let copy_idx = min(i + 4, input.len()); + word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); + T::regs().din().write_value(u32::from_ne_bytes(word)); + i += 4; + } + } + + /// Save the peripheral state to a context. + async fn store_context(&mut self, ctx: &mut Context) { + // Wait for interrupt. + poll_fn(|cx| { + // Check if already done. + let bits = T::regs().sr().read(); + if bits.dinis() { + return Poll::Ready(()); + } + // Register waker, then enable interrupts. + HASH_WAKER.register(cx.waker()); + T::regs().imr().modify(|reg| reg.set_dinie(true)); + // Check for completion. + let bits = T::regs().sr().read(); + if bits.dinis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + ctx.imr = T::regs().imr().read().0; + ctx.str = T::regs().str().read().0; + ctx.cr = T::regs().cr().read().0; + let mut i = 0; + while i < NUM_CONTEXT_REGS { + ctx.csr[i] = T::regs().csr(i).read(); + i += 1; + } + } + + /// Restore the peripheral state from a context. + fn load_context(&mut self, ctx: &Context) { + // Restore the peripheral state from the context. + T::regs().imr().write_value(Imr { 0: ctx.imr }); + T::regs().str().write_value(Str { 0: ctx.str }); + T::regs().cr().write_value(Cr { 0: ctx.cr }); + T::regs().cr().modify(|w| w.set_init(true)); + let mut i = 0; + while i < NUM_CONTEXT_REGS { + T::regs().csr(i).write_value(ctx.csr[i]); + i += 1; + } + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + fn regs() -> pac::hash::Hash; + } +} + +/// HASH instance trait. +pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { + /// Interrupt for this HASH instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +foreach_interrupt!( + ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { + impl Instance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } + + impl sealed::Instance for peripherals::$inst { + fn regs() -> crate::pac::hash::Hash { + crate::pac::$inst + } + } + }; +); + +dma_trait!(Dma, Instance); diff --git a/embassy-stm32/src/hash/v2v3.rs b/embassy-stm32/src/hash/v2v3.rs new file mode 100644 index 0000000000..0588645681 --- /dev/null +++ b/embassy-stm32/src/hash/v2v3.rs @@ -0,0 +1,385 @@ +//! Hash generator (HASH) +use core::cmp::min; +use core::future::poll_fn; +use core::marker::PhantomData; +use core::ptr; +use core::task::Poll; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +use stm32_metapac::hash::regs::*; + +use crate::dma::Transfer; +use crate::interrupt::typelevel::Interrupt; +use crate::peripherals::HASH; +use crate::rcc::sealed::RccPeripheral; +use crate::{interrupt, pac, peripherals, Peripheral}; + +#[cfg(hash_v2)] +const NUM_CONTEXT_REGS: usize = 54; +#[cfg(hash_v3)] +const NUM_CONTEXT_REGS: usize = 103; +const DIGEST_BLOCK_SIZE: usize = 64; +const MAX_DIGEST_SIZE: usize = 64; + +static HASH_WAKER: AtomicWaker = AtomicWaker::new(); + +/// HASH interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let bits = T::regs().sr().read(); + if bits.dinis() { + T::regs().imr().modify(|reg| reg.set_dinie(false)); + HASH_WAKER.wake(); + } + if bits.dcis() { + T::regs().imr().modify(|reg| reg.set_dcie(false)); + HASH_WAKER.wake(); + } + } +} + +///Hash algorithm selection +#[derive(Clone, Copy, PartialEq)] +pub enum Algorithm { + /// SHA-1 Algorithm + SHA1 = 0, + + #[cfg(hash_v2)] + /// MD5 Algorithm + MD5 = 1, + + /// SHA-224 Algorithm + SHA224 = 2, + + /// SHA-256 Algorithm + SHA256 = 3, + + #[cfg(hash_v3)] + /// SHA-384 Algorithm + SHA384 = 12, + + #[cfg(hash_v3)] + /// SHA-512/224 Algorithm + SHA512_224 = 13, + + #[cfg(hash_v3)] + /// SHA-512/256 Algorithm + SHA512_256 = 14, + + #[cfg(hash_v3)] + /// SHA-256 Algorithm + SHA512 = 15, +} + +/// Input data width selection +#[repr(u8)] +#[derive(Clone, Copy)] +pub enum DataType { + ///32-bit data, no data is swapped. + Width32 = 0, + ///16-bit data, each half-word is swapped. + Width16 = 1, + ///8-bit data, all bytes are swapped. + Width8 = 2, + ///1-bit data, all bits are swapped. + Width1 = 3, +} + +/// Stores the state of the HASH peripheral for suspending/resuming +/// digest calculation. +pub struct Context { + buffer: [u8; DIGEST_BLOCK_SIZE], + buflen: usize, + algo: Algorithm, + format: DataType, + imr: u32, + str: u32, + cr: u32, + csr: [u32; NUM_CONTEXT_REGS], +} + +/// HASH driver. +pub struct Hash<'d, T: Instance, D: Dma> { + _peripheral: PeripheralRef<'d, T>, + dma: PeripheralRef<'d, D>, +} + +impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { + /// Instantiates, resets, and enables the HASH peripheral. + pub fn new(peripheral: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { + HASH::enable_and_reset(); + into_ref!(peripheral, dma); + let instance = Self { + _peripheral: peripheral, + dma: dma, + }; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + instance + } + + /// Starts computation of a new hash and returns the saved peripheral state. + pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { + // Define a context for this new computation. + let mut ctx = Context { + buffer: [0; DIGEST_BLOCK_SIZE], + buflen: 0, + algo: algorithm, + format: format, + imr: 0, + str: 0, + cr: 0, + csr: [0; NUM_CONTEXT_REGS], + }; + + // Set the data type in the peripheral. + T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); + + #[cfg(hash_v2)] + { + // Select the algorithm. + let mut algo0 = false; + let mut algo1 = false; + if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { + algo0 = true; + } + if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { + algo1 = true; + } + T::regs().cr().modify(|w| w.set_algo0(algo0)); + T::regs().cr().modify(|w| w.set_algo1(algo1)); + } + + #[cfg(hash_v3)] + T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8)); + + // Enable multiple DMA transfers. + T::regs().cr().modify(|w| w.set_mdmat(true)); + + // Set init to load the context registers. Necessary before storing context. + T::regs().cr().modify(|w| w.set_init(true)); + + // Store and return the state of the peripheral. + self.store_context(&mut ctx).await; + ctx + } + + /// Restores the peripheral state using the given context, + /// then updates the state with the provided data. + /// Peripheral state is saved upon return. + pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { + let data_waiting = input.len() + ctx.buflen; + if data_waiting < DIGEST_BLOCK_SIZE { + // There isn't enough data to digest a block, so append it to the buffer. + ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); + ctx.buflen += input.len(); + return; + } + + // Restore the peripheral state. + self.load_context(&ctx); + + let mut ilen_remaining = input.len(); + let mut input_start = 0; + + // First ingest the data in the buffer. + let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; + if empty_len > 0 { + let copy_len = min(empty_len, ilen_remaining); + ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]); + ctx.buflen += copy_len; + ilen_remaining -= copy_len; + input_start += copy_len; + } + self.accumulate(&ctx.buffer).await; + ctx.buflen = 0; + + // Move any extra data to the now-empty buffer. + let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE; + if leftovers > 0 { + assert!(ilen_remaining >= leftovers); + ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); + ctx.buflen += leftovers; + ilen_remaining -= leftovers; + } else { + ctx.buffer + .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]); + ctx.buflen += DIGEST_BLOCK_SIZE; + ilen_remaining -= DIGEST_BLOCK_SIZE; + } + + // Hash the remaining data. + self.accumulate(&input[input_start..input_start + ilen_remaining]).await; + + // Save the peripheral context. + self.store_context(ctx).await; + } + + /// Computes a digest for the given context. A slice of the provided digest buffer is returned. + /// The length of the returned slice is dependent on the digest length of the selected algorithm. + pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; MAX_DIGEST_SIZE]) -> &'a [u8] { + // Restore the peripheral state. + self.load_context(&ctx); + + // Must be cleared prior to the last DMA transfer. + T::regs().cr().modify(|w| w.set_mdmat(false)); + + // Hash the leftover bytes, if any. + self.accumulate(&ctx.buffer[0..ctx.buflen]).await; + ctx.buflen = 0; + + // Wait for completion. + poll_fn(|cx| { + // Check if already done. + let bits = T::regs().sr().read(); + if bits.dcis() { + return Poll::Ready(()); + } + // Register waker, then enable interrupts. + HASH_WAKER.register(cx.waker()); + T::regs().imr().modify(|reg| reg.set_dinie(true)); + // Check for completion. + let bits = T::regs().sr().read(); + if bits.dcis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + // Return the digest. + let digest_words = match ctx.algo { + Algorithm::SHA1 => 5, + #[cfg(hash_v2)] + Algorithm::MD5 => 4, + Algorithm::SHA224 => 7, + Algorithm::SHA256 => 8, + #[cfg(hash_v3)] + Algorithm::SHA384 => 12, + #[cfg(hash_v3)] + Algorithm::SHA512_224 => 7, + #[cfg(hash_v3)] + Algorithm::SHA512_256 => 8, + #[cfg(hash_v3)] + Algorithm::SHA512 => 16, + }; + let mut i = 0; + while i < digest_words { + let word = T::regs().hr(i).read(); + digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); + i += 1; + } + &digest[0..digest_words * 4] + } + + /// Push data into the hash core. + async fn accumulate(&mut self, input: &[u8]) { + // Ignore an input length of 0. + if input.len() == 0 { + return; + } + + // Set the number of valid bits. + let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; + T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); + + // Configure DMA to transfer input to hash core. + let dma_request = self.dma.request(); + let dst_ptr = T::regs().din().as_ptr(); + let mut num_words = input.len() / 4; + if input.len() % 4 > 0 { + num_words += 1; + } + let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); + let dma_transfer = + unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) }; + T::regs().cr().modify(|w| w.set_dmae(true)); + + // Wait for the transfer to complete. + dma_transfer.await; + } + + /// Save the peripheral state to a context. + async fn store_context(&mut self, ctx: &mut Context) { + // Wait for interrupt. + poll_fn(|cx| { + // Check if already done. + let bits = T::regs().sr().read(); + if bits.dinis() { + return Poll::Ready(()); + } + // Register waker, then enable interrupts. + HASH_WAKER.register(cx.waker()); + T::regs().imr().modify(|reg| reg.set_dinie(true)); + // Check for completion. + let bits = T::regs().sr().read(); + if bits.dinis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + ctx.imr = T::regs().imr().read().0; + ctx.str = T::regs().str().read().0; + ctx.cr = T::regs().cr().read().0; + let mut i = 0; + while i < NUM_CONTEXT_REGS { + ctx.csr[i] = T::regs().csr(i).read(); + i += 1; + } + } + + /// Restore the peripheral state from a context. + fn load_context(&mut self, ctx: &Context) { + // Restore the peripheral state from the context. + T::regs().imr().write_value(Imr { 0: ctx.imr }); + T::regs().str().write_value(Str { 0: ctx.str }); + T::regs().cr().write_value(Cr { 0: ctx.cr }); + T::regs().cr().modify(|w| w.set_init(true)); + let mut i = 0; + while i < NUM_CONTEXT_REGS { + T::regs().csr(i).write_value(ctx.csr[i]); + i += 1; + } + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + fn regs() -> pac::hash::Hash; + } +} + +/// HASH instance trait. +pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { + /// Interrupt for this HASH instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +foreach_interrupt!( + ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { + impl Instance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } + + impl sealed::Instance for peripherals::$inst { + fn regs() -> crate::pac::hash::Hash { + crate::pac::$inst + } + } + }; +); + +dma_trait!(Dma, Instance); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index 5bff48197f..9a608e909d 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 _}; bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; - RNG => rng::InterruptHandler; + HASH_RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index a9f5aa197f..cf52cea5c6 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -3,12 +3,12 @@ use defmt::info; use embassy_executor::Spawner; +use embassy_stm32::hash::*; use embassy_stm32::Config; use embassy_time::Instant; -use {defmt_rtt as _, panic_probe as _}; -use embassy_stm32::hash::*; use sha2::{Digest, Sha256}; +use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { @@ -26,7 +26,7 @@ async fn main(_spawner: Spawner) -> ! { let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; hw_hasher.update(&mut context, test_1).await; hw_hasher.update(&mut context, test_2).await; - let mut buffer: [u8; 32] = [0; 32]; + let mut buffer: [u8; 64] = [0; 64]; let hw_digest = hw_hasher.finish(context, &mut buffer).await; let hw_end_time = Instant::now(); From a260c0a701b0385691b57a22a19d86d7ce4788b8 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sun, 4 Feb 2024 17:21:16 -0500 Subject: [PATCH 06/14] Format hash example. --- examples/stm32f7/src/bin/hash.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index cf52cea5c6..4bd9b4e2e0 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -6,7 +6,6 @@ use embassy_executor::Spawner; use embassy_stm32::hash::*; use embassy_stm32::Config; use embassy_time::Instant; - use sha2::{Digest, Sha256}; use {defmt_rtt as _, panic_probe as _}; From 079bb7b4901b3fa6787c2ca5d8c022de3533ad8c Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:36:02 -0500 Subject: [PATCH 07/14] Added STM32 hash test. --- tests/stm32/Cargo.toml | 23 ++++++++----- tests/stm32/src/bin/hash.rs | 66 +++++++++++++++++++++++++++++++++++++ tests/stm32/src/common.rs | 7 ++++ 3 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 tests/stm32/src/bin/hash.rs diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index cb1bd9a506..d02f1a253e 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -15,22 +15,23 @@ stm32f446re = ["embassy-stm32/stm32f446re", "chrono", "stop", "can", "not-gpdma" stm32f767zi = ["embassy-stm32/stm32f767zi", "chrono", "not-gpdma", "eth", "rng"] stm32g071rb = ["embassy-stm32/stm32g071rb", "cm0", "not-gpdma", "dac"] stm32g491re = ["embassy-stm32/stm32g491re", "chrono", "stop", "not-gpdma", "rng", "fdcan"] -stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng"] -stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan"] -stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan"] +stm32h563zi = ["embassy-stm32/stm32h563zi", "chrono", "eth", "rng", "hash"] +stm32h753zi = ["embassy-stm32/stm32h753zi", "chrono", "not-gpdma", "eth", "rng", "fdcan", "hash"] +stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "chrono", "not-gpdma", "eth", "dac", "rng", "fdcan", "hash"] stm32h7a3zi = ["embassy-stm32/stm32h7a3zi", "not-gpdma", "rng", "fdcan"] stm32l073rz = ["embassy-stm32/stm32l073rz", "cm0", "not-gpdma", "rng"] stm32l152re = ["embassy-stm32/stm32l152re", "chrono", "not-gpdma"] stm32l496zg = ["embassy-stm32/stm32l496zg", "not-gpdma", "rng"] -stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng"] +stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash"] stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"] -stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng"] -stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng"] -stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"] +stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"] +stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"] +stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"] stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] -stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng"] +stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"] stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] +hash = [] eth = ["embassy-executor/task-arena-size-16384"] rng = [] sdmmc = [] @@ -74,6 +75,7 @@ static_cell = "2" portable-atomic = { version = "1.5", features = [] } chrono = { version = "^0.4", default-features = false, optional = true} +sha2 = { version = "0.10.8", default-features = false } # BEGIN TESTS # Generated by gen_test.py. DO NOT EDIT. @@ -107,6 +109,11 @@ name = "gpio" path = "src/bin/gpio.rs" required-features = [] +[[bin]] +name = "hash" +path = "src/bin/hash.rs" +required-features = [ "hash",] + [[bin]] name = "rng" path = "src/bin/rng.rs" diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs new file mode 100644 index 0000000000..60a60b0f1b --- /dev/null +++ b/tests/stm32/src/bin/hash.rs @@ -0,0 +1,66 @@ +// required-features: hash +#![no_std] +#![no_main] + +#[path = "../common.rs"] +mod common; +use common::*; +use embassy_executor::Spawner; +use embassy_stm32::hash::*; +use embassy_stm32::{bind_interrupts, peripherals, hash}; +use sha2::{Digest, Sha224, Sha256}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + HASH_RNG => hash::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); + let dma = peri!(p, HASH_DMA); + let mut hw_hasher = Hash::new(p.HASH, dma); + + let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh"; + let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr"; + let test_3: &[u8] = b"a.ewtkluGWEBR.KAJRBTA,RMNRBG,FDMGB.kger.tkasjrbt.akrjtba.krjtba.ktmyna,nmbvtyliasd;gdrtba,sfvs.kgjzshd.gkbsr.tksejb.SDkfBSE.gkfgb>ESkfbSE>gkJSBESE>kbSE>fk"; + + // Start an SHA-256 digest. + let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; + hw_hasher.update(&mut sha256context, test_1).await; + + // Interrupt the SHA-256 digest to compute an SHA-224 digest. + let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8).await; + hw_hasher.update(&mut sha224context, test_3).await; + let mut sha224_digest_buffer: [u8; 64] = [0; 64]; + let sha224_digest = hw_hasher.finish(sha224context, &mut sha224_digest_buffer).await; + + // Finish the SHA-256 digest. + hw_hasher.update(&mut sha256context, test_2).await; + let mut sha_256_digest_buffer: [u8; 64] = [0; 64]; + let sha256_digest = hw_hasher.finish(sha256context, &mut sha_256_digest_buffer).await; + + // Compute the SHA-256 digest in software. + let mut sw_sha256_hasher = Sha256::new(); + sw_sha256_hasher.update(test_1); + sw_sha256_hasher.update(test_2); + let sw_sha256_digest = sw_sha256_hasher.finalize(); + + //Compute the SHA-224 digest in software. + let mut sw_sha224_hasher = Sha224::new(); + sw_sha224_hasher.update(test_3); + let sw_sha224_digest = sw_sha224_hasher.finalize(); + + // Compare the SHA-256 digests. + info!("Hardware SHA-256 Digest: {:?}", sha256_digest); + info!("Software SHA-256 Digest: {:?}", sw_sha256_digest[..]); + defmt::assert!(*sha256_digest == sw_sha256_digest[..]); + + // Compare the SHA-224 digests. + info!("Hardware SHA-256 Digest: {:?}", sha224_digest); + info!("Software SHA-256 Digest: {:?}", sw_sha224_digest[..]); + defmt::assert!(*sha224_digest == sw_sha224_digest[..]); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index fefe72c860..7e7915b2ec 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -128,6 +128,7 @@ define_peris!( ); #[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi"))] define_peris!( + HASH_DMA = DMA1_CH0, UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1, ADC = ADC1, DAC = DAC1, DAC_PIN = PA4, @@ -141,18 +142,21 @@ define_peris!( ); #[cfg(feature = "stm32u585ai")] define_peris!( + HASH_DMA = GPDMA1_CH0, UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, SPI = SPI1, SPI_SCK = PE13, SPI_MOSI = PE15, SPI_MISO = PE14, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, ); #[cfg(feature = "stm32u5a5zj")] define_peris!( + HASH_DMA = GPDMA1_CH0, UART = LPUART1, UART_TX = PG7, UART_RX = PG8, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler;}, ); #[cfg(feature = "stm32h563zi")] define_peris!( + HASH_DMA = GPDMA1_CH0, UART = LPUART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, SPI = SPI4, SPI_SCK = PE12, SPI_MOSI = PE14, SPI_MISO = PE13, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler;}, @@ -171,6 +175,7 @@ define_peris!( ); #[cfg(feature = "stm32l4a6zg")] define_peris!( + HASH_DMA = DMA2_CH7, UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, @@ -196,6 +201,7 @@ define_peris!( ); #[cfg(feature = "stm32l552ze")] define_peris!( + HASH_DMA = DMA1_CH0, UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, @@ -226,6 +232,7 @@ define_peris!( ); #[cfg(feature = "stm32wba52cg")] define_peris!( + HASH_DMA = GPDMA1_CH0, UART = LPUART1, UART_TX = PB5, UART_RX = PA10, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, SPI = SPI1, SPI_SCK = PB4, SPI_MOSI = PA15, SPI_MISO = PB3, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler;}, From 09973ad4827222251a46cb7b1f48841245b54709 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:44:50 -0500 Subject: [PATCH 08/14] Corrected hash CI build issues. --- tests/stm32/src/bin/hash.rs | 4 ++-- tests/stm32/src/common.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index 60a60b0f1b..05b61a10c9 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -7,7 +7,7 @@ mod common; use common::*; use embassy_executor::Spawner; use embassy_stm32::hash::*; -use embassy_stm32::{bind_interrupts, peripherals, hash}; +use embassy_stm32::{bind_interrupts, hash, peripherals}; use sha2::{Digest, Sha224, Sha256}; use {defmt_rtt as _, panic_probe as _}; @@ -39,7 +39,7 @@ async fn main(_spawner: Spawner) { hw_hasher.update(&mut sha256context, test_2).await; let mut sha_256_digest_buffer: [u8; 64] = [0; 64]; let sha256_digest = hw_hasher.finish(sha256context, &mut sha_256_digest_buffer).await; - + // Compute the SHA-256 digest in software. let mut sw_sha256_hasher = Sha256::new(); sw_sha256_hasher.update(test_1); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 7e7915b2ec..14d5b6d7b2 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -201,7 +201,7 @@ define_peris!( ); #[cfg(feature = "stm32l552ze")] define_peris!( - HASH_DMA = DMA1_CH0, + HASH_DMA = DMA1_CH1, UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, From 0b70d67bf645708c6681220c4f1c2e3ffb69dc35 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:54:17 -0500 Subject: [PATCH 09/14] Separated hash interrupt bindings. --- tests/stm32/src/bin/hash.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index 05b61a10c9..53dd0551f9 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -11,10 +11,26 @@ use embassy_stm32::{bind_interrupts, hash, peripherals}; use sha2::{Digest, Sha224, Sha256}; use {defmt_rtt as _, panic_probe as _}; +#[cfg(any( + feature = "stm32l4a6zg", + feature = "stm32h755zi", + feature = "stm32h753zi" +))] bind_interrupts!(struct Irqs { HASH_RNG => hash::InterruptHandler; }); +#[cfg(any( + feature = "stm32wba52cg", + feature = "stm32l552ze", + feature = "stm32h563zi", + feature = "stm32u5a5zj", + feature = "stm32u585ai" +))] +bind_interrupts!(struct Irqs { + HASH => hash::InterruptHandler; + }); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); From b7db75adff16eb0a4670e926dc664549433654fd Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Tue, 6 Feb 2024 10:44:52 -0500 Subject: [PATCH 10/14] Updated stm32-metapac. --- embassy-stm32/Cargo.toml | 4 ++-- tests/stm32/src/bin/hash.rs | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 63bc321973..ef6063656d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5674011dd7db845c9d70d6a20a16129221026d25" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-aa5dbf859fae743306f5d816905f166de824241f", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5674011dd7db845c9d70d6a20a16129221026d25", default-features = false, features = ["metadata"]} [features] diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index 53dd0551f9..2867115dc6 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -11,11 +11,7 @@ use embassy_stm32::{bind_interrupts, hash, peripherals}; use sha2::{Digest, Sha224, Sha256}; use {defmt_rtt as _, panic_probe as _}; -#[cfg(any( - feature = "stm32l4a6zg", - feature = "stm32h755zi", - feature = "stm32h753zi" -))] +#[cfg(any(feature = "stm32l4a6zg", feature = "stm32h755zi", feature = "stm32h753zi"))] bind_interrupts!(struct Irqs { HASH_RNG => hash::InterruptHandler; }); @@ -29,7 +25,7 @@ bind_interrupts!(struct Irqs { ))] bind_interrupts!(struct Irqs { HASH => hash::InterruptHandler; - }); +}); #[embassy_executor::main] async fn main(_spawner: Spawner) { From bfa67c29932ba9b326da0c661b1b03dcee2ef3fe Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Tue, 6 Feb 2024 18:37:48 -0500 Subject: [PATCH 11/14] Fix digest interrupt enable. --- embassy-stm32/src/hash/v1.rs | 2 +- embassy-stm32/src/hash/v2v3.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/hash/v1.rs b/embassy-stm32/src/hash/v1.rs index 50f9adc834..36beb7c3e2 100644 --- a/embassy-stm32/src/hash/v1.rs +++ b/embassy-stm32/src/hash/v1.rs @@ -215,7 +215,7 @@ impl<'d, T: Instance> Hash<'d, T> { } // Register waker, then enable interrupts. HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dinie(true)); + T::regs().imr().modify(|reg| reg.set_dcie(true)); // Check for completion. let bits = T::regs().sr().read(); if bits.dcis() { diff --git a/embassy-stm32/src/hash/v2v3.rs b/embassy-stm32/src/hash/v2v3.rs index 0588645681..ba1e05f0c0 100644 --- a/embassy-stm32/src/hash/v2v3.rs +++ b/embassy-stm32/src/hash/v2v3.rs @@ -244,7 +244,7 @@ impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { } // Register waker, then enable interrupts. HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dinie(true)); + T::regs().imr().modify(|reg| reg.set_dcie(true)); // Check for completion. let bits = T::regs().sr().read(); if bits.dcis() { From f6645750c95ac008f74b980b553117e7a390a833 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Thu, 8 Feb 2024 17:24:27 -0500 Subject: [PATCH 12/14] Removed hash DMA from unsupported configs. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/hash/mod.rs | 7 +- embassy-stm32/src/hash/{v1.rs => v1v3v4.rs} | 77 +++++++++++++++++++-- embassy-stm32/src/hash/{v2v3.rs => v2.rs} | 6 +- examples/stm32f7/.cargo/config.toml | 2 +- examples/stm32f7/src/bin/hash.rs | 9 ++- 6 files changed, 89 insertions(+), 16 deletions(-) rename embassy-stm32/src/hash/{v1.rs => v1v3v4.rs} (84%) rename embassy-stm32/src/hash/{v2v3.rs => v2.rs} (98%) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ef6063656d..87815c63a2 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -68,7 +68,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5674011dd7db845c9d70d6a20a16129221026d25" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-36a3262735a169e31b702bcb0ac6c0067c3f078e" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -89,7 +89,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-5674011dd7db845c9d70d6a20a16129221026d25", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-36a3262735a169e31b702bcb0ac6c0067c3f078e", default-features = false, features = ["metadata"]} [features] diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 6b23f3b55a..64c1a0a8cb 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -1,7 +1,8 @@ //! Hash Accelerator (HASH) -#[cfg_attr(hash_v1, path = "v1.rs")] -#[cfg_attr(hash_v2, path = "v2v3.rs")] -#[cfg_attr(hash_v3, path = "v2v3.rs")] +#[cfg_attr(hash_v1, path = "v1v3v4.rs")] +#[cfg_attr(hash_v2, path = "v2.rs")] +#[cfg_attr(hash_v3, path = "v1v3v4.rs")] +#[cfg_attr(hash_v4, path = "v1v3v4.rs")] mod _version; pub use _version::*; diff --git a/embassy-stm32/src/hash/v1.rs b/embassy-stm32/src/hash/v1v3v4.rs similarity index 84% rename from embassy-stm32/src/hash/v1.rs rename to embassy-stm32/src/hash/v1v3v4.rs index 36beb7c3e2..771144b115 100644 --- a/embassy-stm32/src/hash/v1.rs +++ b/embassy-stm32/src/hash/v1v3v4.rs @@ -13,10 +13,16 @@ use crate::peripherals::HASH; use crate::rcc::sealed::RccPeripheral; use crate::{interrupt, pac, peripherals, Peripheral}; +#[cfg(hash_v1)] const NUM_CONTEXT_REGS: usize = 51; -const HASH_BUFFER_LEN: usize = 68; -const DIGEST_BLOCK_SIZE: usize = 64; -const MAX_DIGEST_SIZE: usize = 20; +#[cfg(hash_v3)] +const NUM_CONTEXT_REGS: usize = 103; +#[cfg(hash_v4)] +const NUM_CONTEXT_REGS: usize = 54; + +const HASH_BUFFER_LEN: usize = 132; +const DIGEST_BLOCK_SIZE: usize = 128; +const MAX_DIGEST_SIZE: usize = 128; static HASH_WAKER: AtomicWaker = AtomicWaker::new(); @@ -40,12 +46,36 @@ impl interrupt::typelevel::Handler for InterruptHandl } ///Hash algorithm selection -#[derive(PartialEq)] +#[derive(Clone, Copy, PartialEq)] pub enum Algorithm { /// SHA-1 Algorithm SHA1 = 0, + + #[cfg(any(hash_v1, hash_v4))] /// MD5 Algorithm MD5 = 1, + + /// SHA-224 Algorithm + SHA224 = 2, + + /// SHA-256 Algorithm + SHA256 = 3, + + #[cfg(hash_v3)] + /// SHA-384 Algorithm + SHA384 = 12, + + #[cfg(hash_v3)] + /// SHA-512/224 Algorithm + SHA512_224 = 13, + + #[cfg(hash_v3)] + /// SHA-512/256 Algorithm + SHA512_256 = 14, + + #[cfg(hash_v3)] + /// SHA-256 Algorithm + SHA512 = 15, } /// Input data width selection @@ -83,7 +113,10 @@ pub struct Hash<'d, T: Instance> { impl<'d, T: Instance> Hash<'d, T> { /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd) -> Self { + pub fn new( + peripheral: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { HASH::enable_and_reset(); into_ref!(peripheral); let instance = Self { @@ -115,10 +148,31 @@ impl<'d, T: Instance> Hash<'d, T> { T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); // Select the algorithm. + #[cfg(hash_v1)] if ctx.algo == Algorithm::MD5 { T::regs().cr().modify(|w| w.set_algo(true)); } + #[cfg(hash_v2)] + { + // Select the algorithm. + let mut algo0 = false; + let mut algo1 = false; + if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { + algo0 = true; + } + if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { + algo1 = true; + } + T::regs().cr().modify(|w| w.set_algo0(algo0)); + T::regs().cr().modify(|w| w.set_algo1(algo1)); + } + + #[cfg(any(hash_v3, hash_v4))] + T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8)); + + T::regs().cr().modify(|w| w.set_init(true)); + // Store and return the state of the peripheral. self.store_context(&mut ctx).await; ctx @@ -174,7 +228,7 @@ impl<'d, T: Instance> Hash<'d, T> { ilen_remaining -= copy_len; input_start += copy_len; } - self.accumulate(&ctx.buffer[0..64]); + self.accumulate(&ctx.buffer[0..DIGEST_BLOCK_SIZE]); ctx.buflen = 0; // Move any extra data to the now-empty buffer. @@ -229,7 +283,18 @@ impl<'d, T: Instance> Hash<'d, T> { // Return the digest. let digest_words = match ctx.algo { Algorithm::SHA1 => 5, + #[cfg(any(hash_v1, hash_v4))] Algorithm::MD5 => 4, + Algorithm::SHA224 => 7, + Algorithm::SHA256 => 8, + #[cfg(hash_v3)] + Algorithm::SHA384 => 12, + #[cfg(hash_v3)] + Algorithm::SHA512_224 => 7, + #[cfg(hash_v3)] + Algorithm::SHA512_256 => 8, + #[cfg(hash_v3)] + Algorithm::SHA512 => 16, }; let mut i = 0; while i < digest_words { diff --git a/embassy-stm32/src/hash/v2v3.rs b/embassy-stm32/src/hash/v2.rs similarity index 98% rename from embassy-stm32/src/hash/v2v3.rs rename to embassy-stm32/src/hash/v2.rs index ba1e05f0c0..b8104c8255 100644 --- a/embassy-stm32/src/hash/v2v3.rs +++ b/embassy-stm32/src/hash/v2.rs @@ -111,7 +111,11 @@ pub struct Hash<'d, T: Instance, D: Dma> { impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { /// Instantiates, resets, and enables the HASH peripheral. - pub fn new(peripheral: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { + pub fn new( + peripheral: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { HASH::enable_and_reset(); into_ref!(peripheral, dma); let instance = Self { diff --git a/examples/stm32f7/.cargo/config.toml b/examples/stm32f7/.cargo/config.toml index 9088eea6ea..086da2d78d 100644 --- a/examples/stm32f7/.cargo/config.toml +++ b/examples/stm32f7/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` -runner = "probe-rs run --chip STM32F767ZITx" +runner = "probe-rs run --chip STM32F777ZITx" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 4bd9b4e2e0..7d96bd49cd 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -3,12 +3,15 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::hash::*; -use embassy_stm32::Config; +use embassy_stm32::{bind_interrupts, Config, hash, hash::*, peripherals}; use embassy_time::Instant; use sha2::{Digest, Sha256}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + HASH_RNG => hash::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) -> ! { let config = Config::default(); @@ -17,7 +20,7 @@ async fn main(_spawner: Spawner) -> ! { let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh"; let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr"; - let mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7); + let mut hw_hasher = Hash::new(p.HASH, p.DMA2_CH7, Irqs); let hw_start_time = Instant::now(); From eb64d71247dd7c217c7ead98635610fdd8a104e3 Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sun, 11 Feb 2024 11:32:29 -0500 Subject: [PATCH 13/14] Consolidated hash drivers. --- embassy-stm32/src/hash/mod.rs | 553 ++++++++++++++++++++++++++++++- embassy-stm32/src/hash/v1v3v4.rs | 399 ---------------------- embassy-stm32/src/hash/v2.rs | 389 ---------------------- examples/stm32f7/src/bin/hash.rs | 10 +- tests/stm32/Cargo.toml | 2 +- tests/stm32/src/bin/hash.rs | 30 +- tests/stm32/src/common.rs | 7 - 7 files changed, 566 insertions(+), 824 deletions(-) delete mode 100644 embassy-stm32/src/hash/v1v3v4.rs delete mode 100644 embassy-stm32/src/hash/v2.rs diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 64c1a0a8cb..f0c2c839a9 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -1,8 +1,545 @@ -//! Hash Accelerator (HASH) -#[cfg_attr(hash_v1, path = "v1v3v4.rs")] -#[cfg_attr(hash_v2, path = "v2.rs")] -#[cfg_attr(hash_v3, path = "v1v3v4.rs")] -#[cfg_attr(hash_v4, path = "v1v3v4.rs")] -mod _version; - -pub use _version::*; +//! Hash generator (HASH) +use core::cmp::min; +#[cfg(hash_v2)] +use core::future::poll_fn; +use core::marker::PhantomData; +#[cfg(hash_v2)] +use core::ptr; +#[cfg(hash_v2)] +use core::task::Poll; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +use stm32_metapac::hash::regs::*; + +use crate::dma::NoDma; +#[cfg(hash_v2)] +use crate::dma::Transfer; +use crate::interrupt::typelevel::Interrupt; +use crate::peripherals::HASH; +use crate::rcc::sealed::RccPeripheral; +use crate::{interrupt, pac, peripherals, Peripheral}; + +#[cfg(hash_v1)] +const NUM_CONTEXT_REGS: usize = 51; +#[cfg(hash_v3)] +const NUM_CONTEXT_REGS: usize = 103; +#[cfg(any(hash_v2, hash_v4))] +const NUM_CONTEXT_REGS: usize = 54; + +const HASH_BUFFER_LEN: usize = 132; +const DIGEST_BLOCK_SIZE: usize = 128; + +static HASH_WAKER: AtomicWaker = AtomicWaker::new(); + +/// HASH interrupt handler. +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let bits = T::regs().sr().read(); + if bits.dinis() { + T::regs().imr().modify(|reg| reg.set_dinie(false)); + HASH_WAKER.wake(); + } + if bits.dcis() { + T::regs().imr().modify(|reg| reg.set_dcie(false)); + HASH_WAKER.wake(); + } + } +} + +///Hash algorithm selection +#[derive(Clone, Copy, PartialEq)] +pub enum Algorithm { + /// SHA-1 Algorithm + SHA1 = 0, + + #[cfg(any(hash_v1, hash_v2, hash_v4))] + /// MD5 Algorithm + MD5 = 1, + + /// SHA-224 Algorithm + SHA224 = 2, + + /// SHA-256 Algorithm + SHA256 = 3, + + #[cfg(hash_v3)] + /// SHA-384 Algorithm + SHA384 = 12, + + #[cfg(hash_v3)] + /// SHA-512/224 Algorithm + SHA512_224 = 13, + + #[cfg(hash_v3)] + /// SHA-512/256 Algorithm + SHA512_256 = 14, + + #[cfg(hash_v3)] + /// SHA-256 Algorithm + SHA512 = 15, +} + +/// Input data width selection +#[repr(u8)] +#[derive(Clone, Copy)] +pub enum DataType { + ///32-bit data, no data is swapped. + Width32 = 0, + ///16-bit data, each half-word is swapped. + Width16 = 1, + ///8-bit data, all bytes are swapped. + Width8 = 2, + ///1-bit data, all bits are swapped. + Width1 = 3, +} + +/// Stores the state of the HASH peripheral for suspending/resuming +/// digest calculation. +pub struct Context { + first_word_sent: bool, + buffer: [u8; HASH_BUFFER_LEN], + buflen: usize, + algo: Algorithm, + format: DataType, + imr: u32, + str: u32, + cr: u32, + csr: [u32; NUM_CONTEXT_REGS], +} + +/// HASH driver. +pub struct Hash<'d, T: Instance, D = NoDma> { + _peripheral: PeripheralRef<'d, T>, + #[allow(dead_code)] + dma: PeripheralRef<'d, D>, +} + +impl<'d, T: Instance, D> Hash<'d, T, D> { + /// Instantiates, resets, and enables the HASH peripheral. + pub fn new( + peripheral: impl Peripheral

+ 'd, + dma: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { + HASH::enable_and_reset(); + into_ref!(peripheral, dma); + let instance = Self { + _peripheral: peripheral, + dma: dma, + }; + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + + instance + } + + /// Starts computation of a new hash and returns the saved peripheral state. + pub fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { + // Define a context for this new computation. + let mut ctx = Context { + first_word_sent: false, + buffer: [0; HASH_BUFFER_LEN], + buflen: 0, + algo: algorithm, + format: format, + imr: 0, + str: 0, + cr: 0, + csr: [0; NUM_CONTEXT_REGS], + }; + + // Set the data type in the peripheral. + T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); + + // Select the algorithm. + #[cfg(hash_v1)] + if ctx.algo == Algorithm::MD5 { + T::regs().cr().modify(|w| w.set_algo(true)); + } + + #[cfg(hash_v2)] + { + // Select the algorithm. + let mut algo0 = false; + let mut algo1 = false; + if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { + algo0 = true; + } + if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { + algo1 = true; + } + T::regs().cr().modify(|w| w.set_algo0(algo0)); + T::regs().cr().modify(|w| w.set_algo1(algo1)); + } + + #[cfg(any(hash_v3, hash_v4))] + T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8)); + + T::regs().cr().modify(|w| w.set_init(true)); + + // Store and return the state of the peripheral. + self.store_context(&mut ctx); + ctx + } + + /// Restores the peripheral state using the given context, + /// then updates the state with the provided data. + /// Peripheral state is saved upon return. + pub fn update_blocking(&mut self, ctx: &mut Context, input: &[u8]) { + let mut data_waiting = input.len() + ctx.buflen; + if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { + // There isn't enough data to digest a block, so append it to the buffer. + ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); + ctx.buflen += input.len(); + return; + } + + // Restore the peripheral state. + self.load_context(&ctx); + + let mut ilen_remaining = input.len(); + let mut input_start = 0; + + // Handle first block. + if !ctx.first_word_sent { + let empty_len = ctx.buffer.len() - ctx.buflen; + let copy_len = min(empty_len, ilen_remaining); + // Fill the buffer. + if copy_len > 0 { + ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); + ctx.buflen += copy_len; + ilen_remaining -= copy_len; + input_start += copy_len; + } + self.accumulate_blocking(ctx.buffer.as_slice()); + data_waiting -= ctx.buflen; + ctx.buflen = 0; + ctx.first_word_sent = true; + } + + if data_waiting < DIGEST_BLOCK_SIZE { + // There isn't enough data remaining to process another block, so store it. + ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); + ctx.buflen += ilen_remaining; + } else { + // First ingest the data in the buffer. + let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; + if empty_len > 0 { + let copy_len = min(empty_len, ilen_remaining); + ctx.buffer[ctx.buflen..ctx.buflen + copy_len] + .copy_from_slice(&input[input_start..input_start + copy_len]); + ctx.buflen += copy_len; + ilen_remaining -= copy_len; + input_start += copy_len; + } + self.accumulate_blocking(&ctx.buffer[0..DIGEST_BLOCK_SIZE]); + ctx.buflen = 0; + + // Move any extra data to the now-empty buffer. + let leftovers = ilen_remaining % 64; + if leftovers > 0 { + ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); + ctx.buflen += leftovers; + ilen_remaining -= leftovers; + } + + // Hash the remaining data. + self.accumulate_blocking(&input[input_start..input_start + ilen_remaining]); + } + + // Save the peripheral context. + self.store_context(ctx); + } + + /// Restores the peripheral state using the given context, + /// then updates the state with the provided data. + /// Peripheral state is saved upon return. + #[cfg(hash_v2)] + pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) + where + D: crate::hash::Dma, + { + let data_waiting = input.len() + ctx.buflen; + if data_waiting < DIGEST_BLOCK_SIZE { + // There isn't enough data to digest a block, so append it to the buffer. + ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); + ctx.buflen += input.len(); + return; + } + + // Restore the peripheral state. + self.load_context(&ctx); + + // Enable multiple DMA transfers. + T::regs().cr().modify(|w| w.set_mdmat(true)); + + let mut ilen_remaining = input.len(); + let mut input_start = 0; + + // First ingest the data in the buffer. + let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; + if empty_len > 0 { + let copy_len = min(empty_len, ilen_remaining); + ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]); + ctx.buflen += copy_len; + ilen_remaining -= copy_len; + input_start += copy_len; + } + self.accumulate(&ctx.buffer[..DIGEST_BLOCK_SIZE]).await; + ctx.buflen = 0; + + // Move any extra data to the now-empty buffer. + let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE; + if leftovers > 0 { + assert!(ilen_remaining >= leftovers); + ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); + ctx.buflen += leftovers; + ilen_remaining -= leftovers; + } else { + ctx.buffer + .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]); + ctx.buflen += DIGEST_BLOCK_SIZE; + ilen_remaining -= DIGEST_BLOCK_SIZE; + } + + // Hash the remaining data. + self.accumulate(&input[input_start..input_start + ilen_remaining]).await; + + // Save the peripheral context. + self.store_context(ctx); + } + + /// Computes a digest for the given context. + /// The digest buffer must be large enough to accomodate a digest for the selected algorithm. + /// The largest returned digest size is 128 bytes for SHA-512. + /// Panics if the supplied digest buffer is too short. + pub fn finish_blocking(&mut self, mut ctx: Context, digest: &mut [u8]) -> usize { + // Restore the peripheral state. + self.load_context(&ctx); + + // Hash the leftover bytes, if any. + self.accumulate_blocking(&ctx.buffer[0..ctx.buflen]); + ctx.buflen = 0; + + //Start the digest calculation. + T::regs().str().write(|w| w.set_dcal(true)); + + // Block waiting for digest. + while !T::regs().sr().read().dcis() {} + + // Return the digest. + let digest_words = match ctx.algo { + Algorithm::SHA1 => 5, + #[cfg(any(hash_v1, hash_v2, hash_v4))] + Algorithm::MD5 => 4, + Algorithm::SHA224 => 7, + Algorithm::SHA256 => 8, + #[cfg(hash_v3)] + Algorithm::SHA384 => 12, + #[cfg(hash_v3)] + Algorithm::SHA512_224 => 7, + #[cfg(hash_v3)] + Algorithm::SHA512_256 => 8, + #[cfg(hash_v3)] + Algorithm::SHA512 => 16, + }; + + let digest_len_bytes = digest_words * 4; + // Panics if the supplied digest buffer is too short. + if digest.len() < digest_len_bytes { + panic!("Digest buffer must be at least {} bytes long.", digest_words * 4); + } + + let mut i = 0; + while i < digest_words { + let word = T::regs().hr(i).read(); + digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); + i += 1; + } + digest_len_bytes + } + + /// Computes a digest for the given context. + /// The digest buffer must be large enough to accomodate a digest for the selected algorithm. + /// The largest returned digest size is 128 bytes for SHA-512. + /// Panics if the supplied digest buffer is too short. + #[cfg(hash_v2)] + pub async fn finish(&mut self, mut ctx: Context, digest: &mut [u8]) -> usize + where + D: crate::hash::Dma, + { + // Restore the peripheral state. + self.load_context(&ctx); + + // Must be cleared prior to the last DMA transfer. + T::regs().cr().modify(|w| w.set_mdmat(false)); + + // Hash the leftover bytes, if any. + self.accumulate(&ctx.buffer[0..ctx.buflen]).await; + ctx.buflen = 0; + + // Wait for completion. + poll_fn(|cx| { + // Check if already done. + let bits = T::regs().sr().read(); + if bits.dcis() { + return Poll::Ready(()); + } + // Register waker, then enable interrupts. + HASH_WAKER.register(cx.waker()); + T::regs().imr().modify(|reg| reg.set_dcie(true)); + // Check for completion. + let bits = T::regs().sr().read(); + if bits.dcis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; + + // Return the digest. + let digest_words = match ctx.algo { + Algorithm::SHA1 => 5, + #[cfg(any(hash_v1, hash_v2, hash_v4))] + Algorithm::MD5 => 4, + Algorithm::SHA224 => 7, + Algorithm::SHA256 => 8, + #[cfg(hash_v3)] + Algorithm::SHA384 => 12, + #[cfg(hash_v3)] + Algorithm::SHA512_224 => 7, + #[cfg(hash_v3)] + Algorithm::SHA512_256 => 8, + #[cfg(hash_v3)] + Algorithm::SHA512 => 16, + }; + + let digest_len_bytes = digest_words * 4; + // Panics if the supplied digest buffer is too short. + if digest.len() < digest_len_bytes { + panic!("Digest buffer must be at least {} bytes long.", digest_words * 4); + } + + let mut i = 0; + while i < digest_words { + let word = T::regs().hr(i).read(); + digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); + i += 1; + } + digest_len_bytes + } + + /// Push data into the hash core. + fn accumulate_blocking(&mut self, input: &[u8]) { + // Set the number of valid bits. + let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; + T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); + + let mut i = 0; + while i < input.len() { + let mut word: [u8; 4] = [0; 4]; + let copy_idx = min(i + 4, input.len()); + word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); + T::regs().din().write_value(u32::from_ne_bytes(word)); + i += 4; + } + } + + /// Push data into the hash core. + #[cfg(hash_v2)] + async fn accumulate(&mut self, input: &[u8]) + where + D: crate::hash::Dma, + { + // Ignore an input length of 0. + if input.len() == 0 { + return; + } + + // Set the number of valid bits. + let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; + T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); + + // Configure DMA to transfer input to hash core. + let dma_request = self.dma.request(); + let dst_ptr = T::regs().din().as_ptr(); + let mut num_words = input.len() / 4; + if input.len() % 4 > 0 { + num_words += 1; + } + let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); + let dma_transfer = + unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) }; + T::regs().cr().modify(|w| w.set_dmae(true)); + + // Wait for the transfer to complete. + dma_transfer.await; + } + + /// Save the peripheral state to a context. + fn store_context(&mut self, ctx: &mut Context) { + // Block waiting for data in ready. + while !T::regs().sr().read().dinis() {} + + // Store peripheral context. + ctx.imr = T::regs().imr().read().0; + ctx.str = T::regs().str().read().0; + ctx.cr = T::regs().cr().read().0; + let mut i = 0; + while i < NUM_CONTEXT_REGS { + ctx.csr[i] = T::regs().csr(i).read(); + i += 1; + } + } + + /// Restore the peripheral state from a context. + fn load_context(&mut self, ctx: &Context) { + // Restore the peripheral state from the context. + T::regs().imr().write_value(Imr { 0: ctx.imr }); + T::regs().str().write_value(Str { 0: ctx.str }); + T::regs().cr().write_value(Cr { 0: ctx.cr }); + T::regs().cr().modify(|w| w.set_init(true)); + let mut i = 0; + while i < NUM_CONTEXT_REGS { + T::regs().csr(i).write_value(ctx.csr[i]); + i += 1; + } + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + fn regs() -> pac::hash::Hash; + } +} + +/// HASH instance trait. +pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { + /// Interrupt for this HASH instance. + type Interrupt: interrupt::typelevel::Interrupt; +} + +foreach_interrupt!( + ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { + impl Instance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } + + impl sealed::Instance for peripherals::$inst { + fn regs() -> crate::pac::hash::Hash { + crate::pac::$inst + } + } + }; +); + +dma_trait!(Dma, Instance); diff --git a/embassy-stm32/src/hash/v1v3v4.rs b/embassy-stm32/src/hash/v1v3v4.rs deleted file mode 100644 index 771144b115..0000000000 --- a/embassy-stm32/src/hash/v1v3v4.rs +++ /dev/null @@ -1,399 +0,0 @@ -//! Hash generator (HASH) -use core::cmp::min; -use core::future::poll_fn; -use core::marker::PhantomData; -use core::task::Poll; - -use embassy_hal_internal::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; -use stm32_metapac::hash::regs::*; - -use crate::interrupt::typelevel::Interrupt; -use crate::peripherals::HASH; -use crate::rcc::sealed::RccPeripheral; -use crate::{interrupt, pac, peripherals, Peripheral}; - -#[cfg(hash_v1)] -const NUM_CONTEXT_REGS: usize = 51; -#[cfg(hash_v3)] -const NUM_CONTEXT_REGS: usize = 103; -#[cfg(hash_v4)] -const NUM_CONTEXT_REGS: usize = 54; - -const HASH_BUFFER_LEN: usize = 132; -const DIGEST_BLOCK_SIZE: usize = 128; -const MAX_DIGEST_SIZE: usize = 128; - -static HASH_WAKER: AtomicWaker = AtomicWaker::new(); - -/// HASH interrupt handler. -pub struct InterruptHandler { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - unsafe fn on_interrupt() { - let bits = T::regs().sr().read(); - if bits.dinis() { - T::regs().imr().modify(|reg| reg.set_dinie(false)); - HASH_WAKER.wake(); - } - if bits.dcis() { - T::regs().imr().modify(|reg| reg.set_dcie(false)); - HASH_WAKER.wake(); - } - } -} - -///Hash algorithm selection -#[derive(Clone, Copy, PartialEq)] -pub enum Algorithm { - /// SHA-1 Algorithm - SHA1 = 0, - - #[cfg(any(hash_v1, hash_v4))] - /// MD5 Algorithm - MD5 = 1, - - /// SHA-224 Algorithm - SHA224 = 2, - - /// SHA-256 Algorithm - SHA256 = 3, - - #[cfg(hash_v3)] - /// SHA-384 Algorithm - SHA384 = 12, - - #[cfg(hash_v3)] - /// SHA-512/224 Algorithm - SHA512_224 = 13, - - #[cfg(hash_v3)] - /// SHA-512/256 Algorithm - SHA512_256 = 14, - - #[cfg(hash_v3)] - /// SHA-256 Algorithm - SHA512 = 15, -} - -/// Input data width selection -#[repr(u8)] -#[derive(Clone, Copy)] -pub enum DataType { - ///32-bit data, no data is swapped. - Width32 = 0, - ///16-bit data, each half-word is swapped. - Width16 = 1, - ///8-bit data, all bytes are swapped. - Width8 = 2, - ///1-bit data, all bits are swapped. - Width1 = 3, -} - -/// Stores the state of the HASH peripheral for suspending/resuming -/// digest calculation. -pub struct Context { - first_word_sent: bool, - buffer: [u8; HASH_BUFFER_LEN], - buflen: usize, - algo: Algorithm, - format: DataType, - imr: u32, - str: u32, - cr: u32, - csr: [u32; NUM_CONTEXT_REGS], -} - -/// HASH driver. -pub struct Hash<'d, T: Instance> { - _peripheral: PeripheralRef<'d, T>, -} - -impl<'d, T: Instance> Hash<'d, T> { - /// Instantiates, resets, and enables the HASH peripheral. - pub fn new( - peripheral: impl Peripheral

+ 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - ) -> Self { - HASH::enable_and_reset(); - into_ref!(peripheral); - let instance = Self { - _peripheral: peripheral, - }; - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - instance - } - - /// Starts computation of a new hash and returns the saved peripheral state. - pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { - // Define a context for this new computation. - let mut ctx = Context { - first_word_sent: false, - buffer: [0; HASH_BUFFER_LEN], - buflen: 0, - algo: algorithm, - format: format, - imr: 0, - str: 0, - cr: 0, - csr: [0; NUM_CONTEXT_REGS], - }; - - // Set the data type in the peripheral. - T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); - - // Select the algorithm. - #[cfg(hash_v1)] - if ctx.algo == Algorithm::MD5 { - T::regs().cr().modify(|w| w.set_algo(true)); - } - - #[cfg(hash_v2)] - { - // Select the algorithm. - let mut algo0 = false; - let mut algo1 = false; - if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { - algo0 = true; - } - if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { - algo1 = true; - } - T::regs().cr().modify(|w| w.set_algo0(algo0)); - T::regs().cr().modify(|w| w.set_algo1(algo1)); - } - - #[cfg(any(hash_v3, hash_v4))] - T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8)); - - T::regs().cr().modify(|w| w.set_init(true)); - - // Store and return the state of the peripheral. - self.store_context(&mut ctx).await; - ctx - } - - /// Restores the peripheral state using the given context, - /// then updates the state with the provided data. - /// Peripheral state is saved upon return. - pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { - let mut data_waiting = input.len() + ctx.buflen; - if data_waiting < DIGEST_BLOCK_SIZE || (data_waiting < ctx.buffer.len() && !ctx.first_word_sent) { - // There isn't enough data to digest a block, so append it to the buffer. - ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); - ctx.buflen += input.len(); - return; - } - - // Restore the peripheral state. - self.load_context(&ctx); - - let mut ilen_remaining = input.len(); - let mut input_start = 0; - - // Handle first block. - if !ctx.first_word_sent { - let empty_len = ctx.buffer.len() - ctx.buflen; - let copy_len = min(empty_len, ilen_remaining); - // Fill the buffer. - if copy_len > 0 { - ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[0..copy_len]); - ctx.buflen += copy_len; - ilen_remaining -= copy_len; - input_start += copy_len; - } - self.accumulate(ctx.buffer.as_slice()); - data_waiting -= ctx.buflen; - ctx.buflen = 0; - ctx.first_word_sent = true; - } - - if data_waiting < DIGEST_BLOCK_SIZE { - // There isn't enough data remaining to process another block, so store it. - ctx.buffer[0..ilen_remaining].copy_from_slice(&input[input_start..input_start + ilen_remaining]); - ctx.buflen += ilen_remaining; - } else { - // First ingest the data in the buffer. - let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; - if empty_len > 0 { - let copy_len = min(empty_len, ilen_remaining); - ctx.buffer[ctx.buflen..ctx.buflen + copy_len] - .copy_from_slice(&input[input_start..input_start + copy_len]); - ctx.buflen += copy_len; - ilen_remaining -= copy_len; - input_start += copy_len; - } - self.accumulate(&ctx.buffer[0..DIGEST_BLOCK_SIZE]); - ctx.buflen = 0; - - // Move any extra data to the now-empty buffer. - let leftovers = ilen_remaining % 64; - if leftovers > 0 { - ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); - ctx.buflen += leftovers; - ilen_remaining -= leftovers; - } - - // Hash the remaining data. - self.accumulate(&input[input_start..input_start + ilen_remaining]); - } - - // Save the peripheral context. - self.store_context(ctx).await; - } - - /// Computes a digest for the given context. A slice of the provided digest buffer is returned. - /// The length of the returned slice is dependent on the digest length of the selected algorithm. - pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; MAX_DIGEST_SIZE]) -> &'a [u8] { - // Restore the peripheral state. - self.load_context(&ctx); - - // Hash the leftover bytes, if any. - self.accumulate(&ctx.buffer[0..ctx.buflen]); - ctx.buflen = 0; - - //Start the digest calculation. - T::regs().str().write(|w| w.set_dcal(true)); - - // Wait for completion. - poll_fn(|cx| { - // Check if already done. - let bits = T::regs().sr().read(); - if bits.dcis() { - return Poll::Ready(()); - } - // Register waker, then enable interrupts. - HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dcie(true)); - // Check for completion. - let bits = T::regs().sr().read(); - if bits.dcis() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - // Return the digest. - let digest_words = match ctx.algo { - Algorithm::SHA1 => 5, - #[cfg(any(hash_v1, hash_v4))] - Algorithm::MD5 => 4, - Algorithm::SHA224 => 7, - Algorithm::SHA256 => 8, - #[cfg(hash_v3)] - Algorithm::SHA384 => 12, - #[cfg(hash_v3)] - Algorithm::SHA512_224 => 7, - #[cfg(hash_v3)] - Algorithm::SHA512_256 => 8, - #[cfg(hash_v3)] - Algorithm::SHA512 => 16, - }; - let mut i = 0; - while i < digest_words { - let word = T::regs().hr(i).read(); - digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); - i += 1; - } - &digest[0..digest_words * 4] - } - - /// Push data into the hash core. - fn accumulate(&mut self, input: &[u8]) { - // Set the number of valid bits. - let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; - T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); - - let mut i = 0; - while i < input.len() { - let mut word: [u8; 4] = [0; 4]; - let copy_idx = min(i + 4, input.len()); - word[0..copy_idx - i].copy_from_slice(&input[i..copy_idx]); - T::regs().din().write_value(u32::from_ne_bytes(word)); - i += 4; - } - } - - /// Save the peripheral state to a context. - async fn store_context(&mut self, ctx: &mut Context) { - // Wait for interrupt. - poll_fn(|cx| { - // Check if already done. - let bits = T::regs().sr().read(); - if bits.dinis() { - return Poll::Ready(()); - } - // Register waker, then enable interrupts. - HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dinie(true)); - // Check for completion. - let bits = T::regs().sr().read(); - if bits.dinis() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - ctx.imr = T::regs().imr().read().0; - ctx.str = T::regs().str().read().0; - ctx.cr = T::regs().cr().read().0; - let mut i = 0; - while i < NUM_CONTEXT_REGS { - ctx.csr[i] = T::regs().csr(i).read(); - i += 1; - } - } - - /// Restore the peripheral state from a context. - fn load_context(&mut self, ctx: &Context) { - // Restore the peripheral state from the context. - T::regs().imr().write_value(Imr { 0: ctx.imr }); - T::regs().str().write_value(Str { 0: ctx.str }); - T::regs().cr().write_value(Cr { 0: ctx.cr }); - T::regs().cr().modify(|w| w.set_init(true)); - let mut i = 0; - while i < NUM_CONTEXT_REGS { - T::regs().csr(i).write_value(ctx.csr[i]); - i += 1; - } - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - fn regs() -> pac::hash::Hash; - } -} - -/// HASH instance trait. -pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { - /// Interrupt for this HASH instance. - type Interrupt: interrupt::typelevel::Interrupt; -} - -foreach_interrupt!( - ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { - impl Instance for peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$irq; - } - - impl sealed::Instance for peripherals::$inst { - fn regs() -> crate::pac::hash::Hash { - crate::pac::$inst - } - } - }; -); - -dma_trait!(Dma, Instance); diff --git a/embassy-stm32/src/hash/v2.rs b/embassy-stm32/src/hash/v2.rs deleted file mode 100644 index b8104c8255..0000000000 --- a/embassy-stm32/src/hash/v2.rs +++ /dev/null @@ -1,389 +0,0 @@ -//! Hash generator (HASH) -use core::cmp::min; -use core::future::poll_fn; -use core::marker::PhantomData; -use core::ptr; -use core::task::Poll; - -use embassy_hal_internal::{into_ref, PeripheralRef}; -use embassy_sync::waitqueue::AtomicWaker; -use stm32_metapac::hash::regs::*; - -use crate::dma::Transfer; -use crate::interrupt::typelevel::Interrupt; -use crate::peripherals::HASH; -use crate::rcc::sealed::RccPeripheral; -use crate::{interrupt, pac, peripherals, Peripheral}; - -#[cfg(hash_v2)] -const NUM_CONTEXT_REGS: usize = 54; -#[cfg(hash_v3)] -const NUM_CONTEXT_REGS: usize = 103; -const DIGEST_BLOCK_SIZE: usize = 64; -const MAX_DIGEST_SIZE: usize = 64; - -static HASH_WAKER: AtomicWaker = AtomicWaker::new(); - -/// HASH interrupt handler. -pub struct InterruptHandler { - _phantom: PhantomData, -} - -impl interrupt::typelevel::Handler for InterruptHandler { - unsafe fn on_interrupt() { - let bits = T::regs().sr().read(); - if bits.dinis() { - T::regs().imr().modify(|reg| reg.set_dinie(false)); - HASH_WAKER.wake(); - } - if bits.dcis() { - T::regs().imr().modify(|reg| reg.set_dcie(false)); - HASH_WAKER.wake(); - } - } -} - -///Hash algorithm selection -#[derive(Clone, Copy, PartialEq)] -pub enum Algorithm { - /// SHA-1 Algorithm - SHA1 = 0, - - #[cfg(hash_v2)] - /// MD5 Algorithm - MD5 = 1, - - /// SHA-224 Algorithm - SHA224 = 2, - - /// SHA-256 Algorithm - SHA256 = 3, - - #[cfg(hash_v3)] - /// SHA-384 Algorithm - SHA384 = 12, - - #[cfg(hash_v3)] - /// SHA-512/224 Algorithm - SHA512_224 = 13, - - #[cfg(hash_v3)] - /// SHA-512/256 Algorithm - SHA512_256 = 14, - - #[cfg(hash_v3)] - /// SHA-256 Algorithm - SHA512 = 15, -} - -/// Input data width selection -#[repr(u8)] -#[derive(Clone, Copy)] -pub enum DataType { - ///32-bit data, no data is swapped. - Width32 = 0, - ///16-bit data, each half-word is swapped. - Width16 = 1, - ///8-bit data, all bytes are swapped. - Width8 = 2, - ///1-bit data, all bits are swapped. - Width1 = 3, -} - -/// Stores the state of the HASH peripheral for suspending/resuming -/// digest calculation. -pub struct Context { - buffer: [u8; DIGEST_BLOCK_SIZE], - buflen: usize, - algo: Algorithm, - format: DataType, - imr: u32, - str: u32, - cr: u32, - csr: [u32; NUM_CONTEXT_REGS], -} - -/// HASH driver. -pub struct Hash<'d, T: Instance, D: Dma> { - _peripheral: PeripheralRef<'d, T>, - dma: PeripheralRef<'d, D>, -} - -impl<'d, T: Instance, D: Dma> Hash<'d, T, D> { - /// Instantiates, resets, and enables the HASH peripheral. - pub fn new( - peripheral: impl Peripheral

+ 'd, - dma: impl Peripheral

+ 'd, - _irq: impl interrupt::typelevel::Binding> + 'd, - ) -> Self { - HASH::enable_and_reset(); - into_ref!(peripheral, dma); - let instance = Self { - _peripheral: peripheral, - dma: dma, - }; - - T::Interrupt::unpend(); - unsafe { T::Interrupt::enable() }; - - instance - } - - /// Starts computation of a new hash and returns the saved peripheral state. - pub async fn start(&mut self, algorithm: Algorithm, format: DataType) -> Context { - // Define a context for this new computation. - let mut ctx = Context { - buffer: [0; DIGEST_BLOCK_SIZE], - buflen: 0, - algo: algorithm, - format: format, - imr: 0, - str: 0, - cr: 0, - csr: [0; NUM_CONTEXT_REGS], - }; - - // Set the data type in the peripheral. - T::regs().cr().modify(|w| w.set_datatype(ctx.format as u8)); - - #[cfg(hash_v2)] - { - // Select the algorithm. - let mut algo0 = false; - let mut algo1 = false; - if ctx.algo == Algorithm::MD5 || ctx.algo == Algorithm::SHA256 { - algo0 = true; - } - if ctx.algo == Algorithm::SHA224 || ctx.algo == Algorithm::SHA256 { - algo1 = true; - } - T::regs().cr().modify(|w| w.set_algo0(algo0)); - T::regs().cr().modify(|w| w.set_algo1(algo1)); - } - - #[cfg(hash_v3)] - T::regs().cr().modify(|w| w.set_algo(ctx.algo as u8)); - - // Enable multiple DMA transfers. - T::regs().cr().modify(|w| w.set_mdmat(true)); - - // Set init to load the context registers. Necessary before storing context. - T::regs().cr().modify(|w| w.set_init(true)); - - // Store and return the state of the peripheral. - self.store_context(&mut ctx).await; - ctx - } - - /// Restores the peripheral state using the given context, - /// then updates the state with the provided data. - /// Peripheral state is saved upon return. - pub async fn update(&mut self, ctx: &mut Context, input: &[u8]) { - let data_waiting = input.len() + ctx.buflen; - if data_waiting < DIGEST_BLOCK_SIZE { - // There isn't enough data to digest a block, so append it to the buffer. - ctx.buffer[ctx.buflen..ctx.buflen + input.len()].copy_from_slice(input); - ctx.buflen += input.len(); - return; - } - - // Restore the peripheral state. - self.load_context(&ctx); - - let mut ilen_remaining = input.len(); - let mut input_start = 0; - - // First ingest the data in the buffer. - let empty_len = DIGEST_BLOCK_SIZE - ctx.buflen; - if empty_len > 0 { - let copy_len = min(empty_len, ilen_remaining); - ctx.buffer[ctx.buflen..ctx.buflen + copy_len].copy_from_slice(&input[input_start..input_start + copy_len]); - ctx.buflen += copy_len; - ilen_remaining -= copy_len; - input_start += copy_len; - } - self.accumulate(&ctx.buffer).await; - ctx.buflen = 0; - - // Move any extra data to the now-empty buffer. - let leftovers = ilen_remaining % DIGEST_BLOCK_SIZE; - if leftovers > 0 { - assert!(ilen_remaining >= leftovers); - ctx.buffer[0..leftovers].copy_from_slice(&input[input.len() - leftovers..input.len()]); - ctx.buflen += leftovers; - ilen_remaining -= leftovers; - } else { - ctx.buffer - .copy_from_slice(&input[input.len() - DIGEST_BLOCK_SIZE..input.len()]); - ctx.buflen += DIGEST_BLOCK_SIZE; - ilen_remaining -= DIGEST_BLOCK_SIZE; - } - - // Hash the remaining data. - self.accumulate(&input[input_start..input_start + ilen_remaining]).await; - - // Save the peripheral context. - self.store_context(ctx).await; - } - - /// Computes a digest for the given context. A slice of the provided digest buffer is returned. - /// The length of the returned slice is dependent on the digest length of the selected algorithm. - pub async fn finish<'a>(&mut self, mut ctx: Context, digest: &'a mut [u8; MAX_DIGEST_SIZE]) -> &'a [u8] { - // Restore the peripheral state. - self.load_context(&ctx); - - // Must be cleared prior to the last DMA transfer. - T::regs().cr().modify(|w| w.set_mdmat(false)); - - // Hash the leftover bytes, if any. - self.accumulate(&ctx.buffer[0..ctx.buflen]).await; - ctx.buflen = 0; - - // Wait for completion. - poll_fn(|cx| { - // Check if already done. - let bits = T::regs().sr().read(); - if bits.dcis() { - return Poll::Ready(()); - } - // Register waker, then enable interrupts. - HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dcie(true)); - // Check for completion. - let bits = T::regs().sr().read(); - if bits.dcis() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - // Return the digest. - let digest_words = match ctx.algo { - Algorithm::SHA1 => 5, - #[cfg(hash_v2)] - Algorithm::MD5 => 4, - Algorithm::SHA224 => 7, - Algorithm::SHA256 => 8, - #[cfg(hash_v3)] - Algorithm::SHA384 => 12, - #[cfg(hash_v3)] - Algorithm::SHA512_224 => 7, - #[cfg(hash_v3)] - Algorithm::SHA512_256 => 8, - #[cfg(hash_v3)] - Algorithm::SHA512 => 16, - }; - let mut i = 0; - while i < digest_words { - let word = T::regs().hr(i).read(); - digest[(i * 4)..((i * 4) + 4)].copy_from_slice(word.to_be_bytes().as_slice()); - i += 1; - } - &digest[0..digest_words * 4] - } - - /// Push data into the hash core. - async fn accumulate(&mut self, input: &[u8]) { - // Ignore an input length of 0. - if input.len() == 0 { - return; - } - - // Set the number of valid bits. - let num_valid_bits: u8 = (8 * (input.len() % 4)) as u8; - T::regs().str().modify(|w| w.set_nblw(num_valid_bits)); - - // Configure DMA to transfer input to hash core. - let dma_request = self.dma.request(); - let dst_ptr = T::regs().din().as_ptr(); - let mut num_words = input.len() / 4; - if input.len() % 4 > 0 { - num_words += 1; - } - let src_ptr = ptr::slice_from_raw_parts(input.as_ptr().cast(), num_words); - let dma_transfer = - unsafe { Transfer::new_write_raw(&mut self.dma, dma_request, src_ptr, dst_ptr, Default::default()) }; - T::regs().cr().modify(|w| w.set_dmae(true)); - - // Wait for the transfer to complete. - dma_transfer.await; - } - - /// Save the peripheral state to a context. - async fn store_context(&mut self, ctx: &mut Context) { - // Wait for interrupt. - poll_fn(|cx| { - // Check if already done. - let bits = T::regs().sr().read(); - if bits.dinis() { - return Poll::Ready(()); - } - // Register waker, then enable interrupts. - HASH_WAKER.register(cx.waker()); - T::regs().imr().modify(|reg| reg.set_dinie(true)); - // Check for completion. - let bits = T::regs().sr().read(); - if bits.dinis() { - Poll::Ready(()) - } else { - Poll::Pending - } - }) - .await; - - ctx.imr = T::regs().imr().read().0; - ctx.str = T::regs().str().read().0; - ctx.cr = T::regs().cr().read().0; - let mut i = 0; - while i < NUM_CONTEXT_REGS { - ctx.csr[i] = T::regs().csr(i).read(); - i += 1; - } - } - - /// Restore the peripheral state from a context. - fn load_context(&mut self, ctx: &Context) { - // Restore the peripheral state from the context. - T::regs().imr().write_value(Imr { 0: ctx.imr }); - T::regs().str().write_value(Str { 0: ctx.str }); - T::regs().cr().write_value(Cr { 0: ctx.cr }); - T::regs().cr().modify(|w| w.set_init(true)); - let mut i = 0; - while i < NUM_CONTEXT_REGS { - T::regs().csr(i).write_value(ctx.csr[i]); - i += 1; - } - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - fn regs() -> pac::hash::Hash; - } -} - -/// HASH instance trait. -pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { - /// Interrupt for this HASH instance. - type Interrupt: interrupt::typelevel::Interrupt; -} - -foreach_interrupt!( - ($inst:ident, hash, HASH, GLOBAL, $irq:ident) => { - impl Instance for peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$irq; - } - - impl sealed::Instance for peripherals::$inst { - fn regs() -> crate::pac::hash::Hash { - crate::pac::$inst - } - } - }; -); - -dma_trait!(Dma, Instance); diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 7d96bd49cd..31f8d32a7d 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -3,7 +3,7 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::{bind_interrupts, Config, hash, hash::*, peripherals}; +use embassy_stm32::{bind_interrupts, hash, hash::*, peripherals, Config}; use embassy_time::Instant; use sha2::{Digest, Sha256}; use {defmt_rtt as _, panic_probe as _}; @@ -25,11 +25,11 @@ async fn main(_spawner: Spawner) -> ! { let hw_start_time = Instant::now(); // Compute a digest in hardware. - let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; + let mut context = hw_hasher.start(Algorithm::SHA256, DataType::Width8); hw_hasher.update(&mut context, test_1).await; hw_hasher.update(&mut context, test_2).await; - let mut buffer: [u8; 64] = [0; 64]; - let hw_digest = hw_hasher.finish(context, &mut buffer).await; + let mut hw_digest: [u8; 32] = [0; 32]; + hw_hasher.finish(context, &mut hw_digest).await; let hw_end_time = Instant::now(); let hw_execution_time = hw_end_time - hw_start_time; @@ -49,7 +49,7 @@ async fn main(_spawner: Spawner) -> ! { info!("Software Digest: {:?}", sw_digest[..]); info!("Hardware Execution Time: {:?}", hw_execution_time); info!("Software Execution Time: {:?}", sw_execution_time); - assert_eq!(*hw_digest, sw_digest[..]); + assert_eq!(hw_digest, sw_digest[..]); loop {} } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index d02f1a253e..fc4420687e 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -26,7 +26,7 @@ stm32l4a6zg = ["embassy-stm32/stm32l4a6zg", "chrono", "not-gpdma", "rng", "hash" stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng"] stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash"] stm32u585ai = ["embassy-stm32/stm32u585ai", "chrono", "rng", "hash"] -stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng", "hash"] +stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "chrono", "rng"] stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] stm32wba52cg = ["embassy-stm32/stm32wba52cg", "chrono", "rng", "hash"] stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] diff --git a/tests/stm32/src/bin/hash.rs b/tests/stm32/src/bin/hash.rs index 2867115dc6..cfcf3d9768 100644 --- a/tests/stm32/src/bin/hash.rs +++ b/tests/stm32/src/bin/hash.rs @@ -6,6 +6,7 @@ mod common; use common::*; use embassy_executor::Spawner; +use embassy_stm32::dma::NoDma; use embassy_stm32::hash::*; use embassy_stm32::{bind_interrupts, hash, peripherals}; use sha2::{Digest, Sha224, Sha256}; @@ -30,27 +31,26 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); - let dma = peri!(p, HASH_DMA); - let mut hw_hasher = Hash::new(p.HASH, dma); + let mut hw_hasher = Hash::new(p.HASH, NoDma, Irqs); let test_1: &[u8] = b"as;dfhaslfhas;oifvnasd;nifvnhasd;nifvhndlkfghsd;nvfnahssdfgsdafgsasdfasdfasdfasdfasdfghjklmnbvcalskdjghalskdjgfbaslkdjfgbalskdjgbalskdjbdfhsdfhsfghsfghfgh"; let test_2: &[u8] = b"fdhalksdjfhlasdjkfhalskdjfhgal;skdjfgalskdhfjgalskdjfglafgadfgdfgdafgaadsfgfgdfgadrgsyfthxfgjfhklhjkfgukhulkvhlvhukgfhfsrghzdhxyfufynufyuszeradrtydyytserr"; let test_3: &[u8] = b"a.ewtkluGWEBR.KAJRBTA,RMNRBG,FDMGB.kger.tkasjrbt.akrjtba.krjtba.ktmyna,nmbvtyliasd;gdrtba,sfvs.kgjzshd.gkbsr.tksejb.SDkfBSE.gkfgb>ESkfbSE>gkJSBESE>kbSE>fk"; // Start an SHA-256 digest. - let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8).await; - hw_hasher.update(&mut sha256context, test_1).await; + let mut sha256context = hw_hasher.start(Algorithm::SHA256, DataType::Width8); + hw_hasher.update_blocking(&mut sha256context, test_1); // Interrupt the SHA-256 digest to compute an SHA-224 digest. - let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8).await; - hw_hasher.update(&mut sha224context, test_3).await; - let mut sha224_digest_buffer: [u8; 64] = [0; 64]; - let sha224_digest = hw_hasher.finish(sha224context, &mut sha224_digest_buffer).await; + let mut sha224context = hw_hasher.start(Algorithm::SHA224, DataType::Width8); + hw_hasher.update_blocking(&mut sha224context, test_3); + let mut sha224_digest_buffer: [u8; 28] = [0; 28]; + let _ = hw_hasher.finish_blocking(sha224context, &mut sha224_digest_buffer); // Finish the SHA-256 digest. - hw_hasher.update(&mut sha256context, test_2).await; - let mut sha_256_digest_buffer: [u8; 64] = [0; 64]; - let sha256_digest = hw_hasher.finish(sha256context, &mut sha_256_digest_buffer).await; + hw_hasher.update_blocking(&mut sha256context, test_2); + let mut sha256_digest_buffer: [u8; 32] = [0; 32]; + let _ = hw_hasher.finish_blocking(sha256context, &mut sha256_digest_buffer); // Compute the SHA-256 digest in software. let mut sw_sha256_hasher = Sha256::new(); @@ -64,14 +64,14 @@ async fn main(_spawner: Spawner) { let sw_sha224_digest = sw_sha224_hasher.finalize(); // Compare the SHA-256 digests. - info!("Hardware SHA-256 Digest: {:?}", sha256_digest); + info!("Hardware SHA-256 Digest: {:?}", sha256_digest_buffer); info!("Software SHA-256 Digest: {:?}", sw_sha256_digest[..]); - defmt::assert!(*sha256_digest == sw_sha256_digest[..]); + defmt::assert!(sha256_digest_buffer == sw_sha256_digest[..]); // Compare the SHA-224 digests. - info!("Hardware SHA-256 Digest: {:?}", sha224_digest); + info!("Hardware SHA-256 Digest: {:?}", sha224_digest_buffer); info!("Software SHA-256 Digest: {:?}", sw_sha224_digest[..]); - defmt::assert!(*sha224_digest == sw_sha224_digest[..]); + defmt::assert!(sha224_digest_buffer == sw_sha224_digest[..]); info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 14d5b6d7b2..fefe72c860 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -128,7 +128,6 @@ define_peris!( ); #[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi"))] define_peris!( - HASH_DMA = DMA1_CH0, UART = USART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = DMA1_CH0, UART_RX_DMA = DMA1_CH1, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PB5, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH0, SPI_RX_DMA = DMA1_CH1, ADC = ADC1, DAC = DAC1, DAC_PIN = PA4, @@ -142,21 +141,18 @@ define_peris!( ); #[cfg(feature = "stm32u585ai")] define_peris!( - HASH_DMA = GPDMA1_CH0, UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, SPI = SPI1, SPI_SCK = PE13, SPI_MOSI = PE15, SPI_MISO = PE14, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, ); #[cfg(feature = "stm32u5a5zj")] define_peris!( - HASH_DMA = GPDMA1_CH0, UART = LPUART1, UART_TX = PG7, UART_RX = PG8, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler;}, ); #[cfg(feature = "stm32h563zi")] define_peris!( - HASH_DMA = GPDMA1_CH0, UART = LPUART1, UART_TX = PB6, UART_RX = PB7, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, SPI = SPI4, SPI_SCK = PE12, SPI_MOSI = PE14, SPI_MISO = PE13, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler;}, @@ -175,7 +171,6 @@ define_peris!( ); #[cfg(feature = "stm32l4a6zg")] define_peris!( - HASH_DMA = DMA2_CH7, UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH2, UART_RX_DMA = DMA1_CH3, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, @@ -201,7 +196,6 @@ define_peris!( ); #[cfg(feature = "stm32l552ze")] define_peris!( - HASH_DMA = DMA1_CH1, UART = USART3, UART_TX = PD8, UART_RX = PD9, UART_TX_DMA = DMA1_CH1, UART_RX_DMA = DMA1_CH2, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH1, SPI_RX_DMA = DMA1_CH2, @irq UART = {USART3 => embassy_stm32::usart::InterruptHandler;}, @@ -232,7 +226,6 @@ define_peris!( ); #[cfg(feature = "stm32wba52cg")] define_peris!( - HASH_DMA = GPDMA1_CH0, UART = LPUART1, UART_TX = PB5, UART_RX = PA10, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, SPI = SPI1, SPI_SCK = PB4, SPI_MOSI = PA15, SPI_MISO = PB3, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler;}, From 7bf044278e8c85b5ea6dbe3de6b3cdea884c995a Mon Sep 17 00:00:00 2001 From: Caleb Garrett <47389035+caleb-garrett@users.noreply.github.com> Date: Sun, 11 Feb 2024 11:47:38 -0500 Subject: [PATCH 14/14] fmt --- examples/stm32f7/src/bin/hash.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index 31f8d32a7d..96e50f84bf 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -3,7 +3,8 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::{bind_interrupts, hash, hash::*, peripherals, Config}; +use embassy_stm32::hash::*; +use embassy_stm32::{bind_interrupts, hash, peripherals, Config}; use embassy_time::Instant; use sha2::{Digest, Sha256}; use {defmt_rtt as _, panic_probe as _};