From 0fe2e42a2e99b06652a1517c46afd92b8816adff Mon Sep 17 00:00:00 2001 From: Ayodeji Ige Date: Tue, 17 Mar 2026 12:51:48 -0700 Subject: [PATCH 1/2] Add PartitionGuard implementations and enhance Partition functionality --- .../partition-manager/src/ext/bdd.rs | 89 ++++++++++++++++++- .../partition-manager/src/ext/esa.rs | 64 ++++++++++++- .../partition-manager/src/lib.rs | 49 +++++++++- .../partition-manager/src/test/mod.rs | 3 - 4 files changed, 199 insertions(+), 6 deletions(-) diff --git a/partition-manager/partition-manager/src/ext/bdd.rs b/partition-manager/partition-manager/src/ext/bdd.rs index 2ee3ce89..9b30e33c 100644 --- a/partition-manager/partition-manager/src/ext/bdd.rs +++ b/partition-manager/partition-manager/src/ext/bdd.rs @@ -4,7 +4,7 @@ use aligned::Aligned; use block_device_driver::BlockDevice; use embassy_sync::blocking_mutex::raw::RawMutex; -use crate::{Error, Partition, RO, RW}; +use crate::{Error, Partition, PartitionGuard, RO, RW}; impl Partition<'_, F, MARKER, M> { /// Returns the block number on the parent storage medium, given a block size. @@ -37,6 +37,33 @@ impl Partition<'_, F, MARKER, M> { } } +impl PartitionGuard<'_, F, MARKER, M> { + const fn start_block(&self, block_size: u32) -> Option { + if self.offset % block_size != 0 { + None + } else { + Some(self.offset / block_size) + } + } + + const fn check_access( + &self, + block_address: u32, + data: &[Aligned], + ) -> Result<(), Error> + where + F: BlockDevice, + { + let offset = block_address * SIZE as u32; + let size = (data.len() * SIZE) as u32; + if !self.within_bounds(offset, size) { + Err(Error::OutOfBounds) + } else { + Ok(()) + } + } +} + impl, M: RawMutex> BlockDevice for Partition<'_, F, RO, M> { type Error = Error; type Align = F::Align; @@ -94,3 +121,63 @@ impl, M: RawMutex> BlockDevice for Ok(self.size as u64) } } + +// PartitionGuard trait implementations +impl, M: RawMutex> BlockDevice for PartitionGuard<'_, F, RO, M> { + type Error = Error; + type Align = F::Align; + + async fn read( + &mut self, + block_address: u32, + data: &mut [Aligned], + ) -> Result<(), Self::Error> { + self.check_access(block_address, data)?; + let start_block = self.start_block(SIZE as u32).ok_or(Error::NotAligned)?; + + Ok(self.guard.read(start_block + block_address, data).await?) + } + + async fn write( + &mut self, + _block_address: u32, + _data: &[Aligned], + ) -> Result<(), Self::Error> { + Err(Error::ReadOnly) + } + + async fn size(&mut self) -> Result { + Ok(self.size as u64) + } +} + +impl, M: RawMutex> BlockDevice for PartitionGuard<'_, F, RW, M> { + type Error = Error; + type Align = F::Align; + + async fn read( + &mut self, + block_address: u32, + data: &mut [Aligned], + ) -> Result<(), Self::Error> { + self.check_access(block_address, data)?; + let start_block = self.start_block(SIZE as u32).ok_or(Error::NotAligned)?; + + Ok(self.guard.read(start_block + block_address, data).await?) + } + + async fn write( + &mut self, + block_address: u32, + data: &[Aligned], + ) -> Result<(), Self::Error> { + self.check_access(block_address, data)?; + let start_block = self.start_block(SIZE as u32).ok_or(Error::NotAligned)?; + + Ok(self.guard.write(start_block + block_address, data).await?) + } + + async fn size(&mut self) -> Result { + Ok(self.size as u64) + } +} diff --git a/partition-manager/partition-manager/src/ext/esa.rs b/partition-manager/partition-manager/src/ext/esa.rs index 2c219e6f..a85dae7d 100644 --- a/partition-manager/partition-manager/src/ext/esa.rs +++ b/partition-manager/partition-manager/src/ext/esa.rs @@ -1,6 +1,6 @@ //! Embedded Storage Async -use crate::{Error, Partition, RO, RW}; +use crate::{Error, Partition, PartitionGuard, RO, RW}; use core::fmt::Debug; use embassy_sync::blocking_mutex::raw::RawMutex; use embedded_storage_async::nor_flash::{ @@ -13,6 +13,7 @@ impl NorFlashError for Error { Error::OutOfBounds => NorFlashErrorKind::OutOfBounds, Error::NotAligned => NorFlashErrorKind::NotAligned, Error::ReadOnly => NorFlashErrorKind::Other, // Note: actually unreachable, only thrown by other impls. + Error::Locked => NorFlashErrorKind::Other, Error::Inner(e) => e.kind(), } } @@ -22,6 +23,10 @@ impl ErrorType for Partition<'_, F, MARKER type Error = Error; } +impl ErrorType for PartitionGuard<'_, F, MARKER, M> { + type Error = Error; +} + impl ReadNorFlash for Partition<'_, F, RO, M> { const READ_SIZE: usize = F::READ_SIZE; @@ -75,3 +80,60 @@ impl NorFlash for Partition<'_, F, RW, M> { } impl MultiwriteNorFlash for Partition<'_, F, RW, M> {} + +// PartitionGuard trait implementations + +impl ReadNorFlash for PartitionGuard<'_, F, RO, M> { + const READ_SIZE: usize = F::READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if !self.within_bounds(offset, bytes.len() as u32) { + return Err(Error::OutOfBounds); + } + + Ok(self.guard.read(offset + self.offset, bytes).await?) + } + + fn capacity(&self) -> usize { + self.size as usize + } +} + +impl ReadNorFlash for PartitionGuard<'_, F, RW, M> { + const READ_SIZE: usize = F::READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + if !self.within_bounds(offset, bytes.len() as u32) { + return Err(Error::OutOfBounds); + } + + Ok(self.guard.read(offset + self.offset, bytes).await?) + } + + fn capacity(&self) -> usize { + self.size as usize + } +} + +impl NorFlash for PartitionGuard<'_, F, RW, M> { + const WRITE_SIZE: usize = F::WRITE_SIZE; + const ERASE_SIZE: usize = F::ERASE_SIZE; + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + if !self.within_bounds(from, to.saturating_sub(from)) { + return Err(Error::OutOfBounds); + } + + Ok(self.guard.erase(from + self.offset, to + self.offset).await?) + } + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + if !self.within_bounds(offset, bytes.len() as u32) { + return Err(Error::OutOfBounds); + } + + Ok(self.guard.write(offset + self.offset, bytes).await?) + } +} + +impl MultiwriteNorFlash for PartitionGuard<'_, F, RW, M> {} diff --git a/partition-manager/partition-manager/src/lib.rs b/partition-manager/partition-manager/src/lib.rs index e54be9ac..f112f853 100644 --- a/partition-manager/partition-manager/src/lib.rs +++ b/partition-manager/partition-manager/src/lib.rs @@ -7,9 +7,11 @@ pub use partition_manager_macros as macros; use core::{fmt::Debug, marker::PhantomData}; use embassy_sync::{ blocking_mutex::raw::{NoopRawMutex, RawMutex}, - mutex::Mutex, + mutex::{Mutex, MutexGuard}, }; +pub use embassy_sync::mutex::TryLockError; + mod ext; #[cfg(test)] @@ -43,6 +45,37 @@ impl<'a, F, MARKER, M: RawMutex> Partition<'a, F, MARKER, M> { _marker: PhantomData, } } + + /// Lock the underlying storage and return a guard that allows direct operations. + pub async fn lock(&self) -> PartitionGuard<'_, F, MARKER, M> { + PartitionGuard { + guard: self.storage.lock().await, + offset: self.offset, + size: self.size, + _marker: PhantomData, + } + } + + /// Attempt to lock the underlying storage without blocking. + pub fn try_lock(&self) -> Result, TryLockError> { + Ok(PartitionGuard { + guard: self.storage.try_lock()?, + offset: self.offset, + size: self.size, + _marker: PhantomData, + }) + } +} + +/// A guard that provides access to a partition's underlying storage. +/// +/// Obtained via [`Partition::lock`] or [`Partition::try_lock`]. +/// The underlying mutex is held for the lifetime of this guard. +pub struct PartitionGuard<'a, F, MARKER, M: RawMutex = NoopRawMutex> { + guard: MutexGuard<'a, M, F>, + offset: u32, + size: u32, + _marker: PhantomData, } impl Partition<'_, F, RW, M> { @@ -101,6 +134,18 @@ impl Partition<'_, F, MARKER, M> { } } +impl PartitionGuard<'_, F, MARKER, M> { + /// Checks whether an address range lies within the partition. + #[allow(unused)] + const fn within_bounds(&self, offset: u32, size: u32) -> bool { + if let Some(end) = offset.checked_add(size) { + end <= self.size + } else { + false + } + } +} + /// Marker type for read-only partitions. pub struct RO; @@ -117,6 +162,8 @@ pub enum Error { NotAligned, /// Tried to perform an Write or Erase operation on a read-only partition. ReadOnly, + /// Could not acquire the storage lock. + Locked, /// Underlying device returned an error. Inner(E), } diff --git a/partition-manager/partition-manager/src/test/mod.rs b/partition-manager/partition-manager/src/test/mod.rs index af0efe82..a22a089c 100644 --- a/partition-manager/partition-manager/src/test/mod.rs +++ b/partition-manager/partition-manager/src/test/mod.rs @@ -1,6 +1,3 @@ -#![allow(clippy::panic)] -#![allow(clippy::unwrap_used)] - mod mock; #[cfg(feature = "bdd")] From c50b0ab793a250cc6ad7efe1ea61e33309767c48 Mon Sep 17 00:00:00 2001 From: Ayodeji Ige Date: Tue, 17 Mar 2026 14:20:28 -0700 Subject: [PATCH 2/2] Remove unuseed lock errod --- partition-manager/partition-manager/src/ext/esa.rs | 1 - partition-manager/partition-manager/src/lib.rs | 2 -- 2 files changed, 3 deletions(-) diff --git a/partition-manager/partition-manager/src/ext/esa.rs b/partition-manager/partition-manager/src/ext/esa.rs index a85dae7d..e48770b1 100644 --- a/partition-manager/partition-manager/src/ext/esa.rs +++ b/partition-manager/partition-manager/src/ext/esa.rs @@ -13,7 +13,6 @@ impl NorFlashError for Error { Error::OutOfBounds => NorFlashErrorKind::OutOfBounds, Error::NotAligned => NorFlashErrorKind::NotAligned, Error::ReadOnly => NorFlashErrorKind::Other, // Note: actually unreachable, only thrown by other impls. - Error::Locked => NorFlashErrorKind::Other, Error::Inner(e) => e.kind(), } } diff --git a/partition-manager/partition-manager/src/lib.rs b/partition-manager/partition-manager/src/lib.rs index f112f853..d6b0843e 100644 --- a/partition-manager/partition-manager/src/lib.rs +++ b/partition-manager/partition-manager/src/lib.rs @@ -162,8 +162,6 @@ pub enum Error { NotAligned, /// Tried to perform an Write or Erase operation on a read-only partition. ReadOnly, - /// Could not acquire the storage lock. - Locked, /// Underlying device returned an error. Inner(E), }