Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions esp-hal/src/aes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,8 @@ pub mod dma {
DmaError,
DmaRxBuffer,
DmaTxBuffer,
InternalMemoryCachelineAligned,
NoBuffer,
aligned::InternalMemory,
prepare_for_rx,
prepare_for_tx,
},
Expand Down Expand Up @@ -761,7 +761,7 @@ pub mod dma {

#[cfg(dma_can_access_psram)]
unaligned_data_buffers: [Option<ManualWritebackBuffer>; 2],
descriptors: InternalMemoryCachelineAligned<[DmaDescriptor; OUT_DESCR_COUNT + 1]>,
descriptors: InternalMemory<[DmaDescriptor; OUT_DESCR_COUNT + 1]>,
}

// The DMA descriptors prevent auto-implementing Sync and Send, but they can be treated as Send
Expand Down Expand Up @@ -801,9 +801,7 @@ pub mod dma {

#[cfg(dma_can_access_psram)]
unaligned_data_buffers: [const { None }; 2],
descriptors: InternalMemoryCachelineAligned::new(
[DmaDescriptor::EMPTY; OUT_DESCR_COUNT + 1],
),
descriptors: InternalMemory::new([DmaDescriptor::EMPTY; OUT_DESCR_COUNT + 1]),
}
}

Expand Down Expand Up @@ -988,7 +986,7 @@ pub mod dma {
work_item: &mut AesOperation,
) -> Result<(), AesDma<'d>> {
let input_len = work_item.buffers.input.len();
let (input_dscr, output_dscr) = self.descriptors.get_mut().split_at_mut(1);
let (input_dscr, output_dscr) = self.descriptors.get_mut().into_mut().split_at_mut(1);
let (input_buffer, data_len) = unsafe {
// This unwrap is infallible as AES-DMA devices don't have TX DMA alignment
// requirements.
Expand Down
134 changes: 134 additions & 0 deletions esp-hal/src/dma/aligned.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
//! Helper types for cacheline-aligned DMA buffers.

use core::ops::{Deref, DerefMut};

use crate::{
dma::{DmaAlignmentError, DmaBufError},
soc::is_valid_ram_address,
};

/// A cacheline-aligned type wrapper.
///
/// This type is guaranteed to be safely useable as a DMA buffer or
/// descriptor array.
///
/// [`InternalMemoryMut`] is a reference type that carries this guarantee.
// ESP32-P4 internal memory is cached, enforce alignment to avoid memory
// corruption. Technically only needed for IN buffers and descriptor lists.
#[cfg_attr(soc_internal_memory_cached, repr(C, align(64)))] // dcache cache line
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[doc(hidden)]
pub struct InternalMemory<T>(T);

impl<T> InternalMemory<T> {
pub const fn new(init: T) -> Self {
Self(init)
}

pub const fn get_mut(&mut self) -> InternalMemoryMut<'_, T> {
InternalMemoryMut(&mut self.0)
}
}

/// A cacheline-aligned byte buffer.
///
/// This type is guaranteed to be safely useable as a DMA buffer.
///
/// [`InternalMemoryMut`] is a reference type that carries this guarantee.
// ESP32 requires word alignment for DMA buffers.
// ESP32-S2 technically supports byte-aligned DMA buffers, but the
// transfer ends up writing out of bounds.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(not(soc_internal_memory_cached), repr(C, align(4)))]
#[doc(hidden)]
pub struct InternalMemoryBuffer<const N: usize>(InternalMemory<[u8; N]>);

impl<const N: usize> Default for InternalMemoryBuffer<N> {
fn default() -> Self {
Self::new()
}
}

impl<const N: usize> InternalMemoryBuffer<N> {
pub const fn new() -> Self {
Self(InternalMemory::new([0u8; N]))
}

pub const fn get_mut(&mut self) -> InternalMemoryMut<'_, [u8]> {
InternalMemoryMut(&mut self.0.0)
}
}

/// A mutable reference to an [`InternalMemory`] object.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(transparent)]
pub struct InternalMemoryMut<'a, T>(&'a mut T)
where
T: ?Sized;

impl<'a, T: ?Sized> InternalMemoryMut<'a, T> {
/// Creates a new [`InternalMemoryMut`] from a mutable reference, if it's
/// provably compatible.
pub fn new(ptr: &'a mut T) -> Result<Self, DmaBufError> {
let alignment = if cfg!(soc_internal_memory_cached) {
64 // FIXME un-magic this
} else {
4
};
let addr = ptr as *mut T as *mut () as usize;

if !is_valid_ram_address(addr) {
return Err(DmaBufError::UnsupportedMemoryRegion);
}
if !core::mem::size_of_val(ptr).is_multiple_of(alignment) {
return Err(DmaBufError::InvalidAlignment(DmaAlignmentError::Size));
}
if !addr.is_multiple_of(alignment) {
return Err(DmaBufError::InvalidAlignment(DmaAlignmentError::Address));
}

Ok(Self(ptr))
}

/// Creates a new [`InternalMemoryMut`] from *any* mutable reference.
///
/// # Safety
///
/// The caller must ensure that the reference is properly aligned for [`InternalMemory`] and
/// the cachelines occupied by the reference do not overlap with any other data that can be
/// corrupted by a cacheline invalidation operation.
pub const unsafe fn new_unchecked(ptr: &'a mut T) -> Self {
Self(ptr)
}
}

impl<'a, T: ?Sized> InternalMemoryMut<'a, T> {
/// Converts this object into a mutable reference.
pub fn into_mut(self) -> &'a mut T {
self.0
}
}

impl<'a, T, const N: usize> InternalMemoryMut<'a, [T; N]> {
/// Converts this object into a slice reference.
pub fn unsize(self) -> InternalMemoryMut<'a, [T]> {
InternalMemoryMut(self.0)
}
}

impl<'a, T: ?Sized> Deref for InternalMemoryMut<'a, T> {
type Target = T;

fn deref(&self) -> &Self::Target {
self.0
}
}

impl<'a, T: ?Sized> DerefMut for InternalMemoryMut<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0
}
}
Loading
Loading