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] 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 d8a4c65fa..00d8a5f63 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 ac4854f80..6b23f3b55 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 000000000..50f9adc83 --- /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 000000000..058864568 --- /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 5bff48197..9a608e909 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _}; 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 a9f5aa197..cf52cea5c 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();