mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-25 08:12:30 +00:00
Consolidated hash drivers.
This commit is contained in:
parent
0c9661a661
commit
eb64d71247
@ -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;
|
||||
//! 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;
|
||||
|
||||
pub use _version::*;
|
||||
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<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
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<P = T> + 'd,
|
||||
dma: impl Peripheral<P = D> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + '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<T>,
|
||||
{
|
||||
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<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]).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<T>,
|
||||
{
|
||||
// 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<P = Self> + 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);
|
||||
|
@ -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<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
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<P = T> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + '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<P = Self> + 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);
|
@ -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<T: Instance> {
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
|
||||
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<T>> {
|
||||
_peripheral: PeripheralRef<'d, T>,
|
||||
dma: PeripheralRef<'d, D>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance, D: Dma<T>> Hash<'d, T, D> {
|
||||
/// Instantiates, resets, and enables the HASH peripheral.
|
||||
pub fn new(
|
||||
peripheral: impl Peripheral<P = T> + 'd,
|
||||
dma: impl Peripheral<P = D> + 'd,
|
||||
_irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + '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<P = Self> + 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);
|
@ -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 {}
|
||||
}
|
||||
|
@ -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"]
|
||||
|
@ -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();
|
||||
|
@ -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<embassy_stm32::peripherals::USART3>;},
|
||||
);
|
||||
#[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<embassy_stm32::peripherals::LPUART1>;},
|
||||
);
|
||||
#[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<embassy_stm32::peripherals::LPUART1>;},
|
||||
@ -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<embassy_stm32::peripherals::USART3>;},
|
||||
@ -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<embassy_stm32::peripherals::USART3>;},
|
||||
@ -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<embassy_stm32::peripherals::LPUART1>;},
|
||||
|
Loading…
Reference in New Issue
Block a user