mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-30 05:40:39 +00:00
Fix incorrect usage of MaybeUninit (#3677)
* Fix incorrect usage of MaybeUninit * CHANGELOG.md * Run tests in CI * Address review comments * Fix wrong path * Less transmutes * Don't use link_section on MacOS * Fix a search+replace gone wrong * Fix unaligned write buffer, make sure we test all relevant branches * Fix
This commit is contained in:
parent
d7622ff8c3
commit
88151e78f9
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@ -159,11 +159,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# Some of the configuration items in 'rustfmt.toml' require the 'nightly'
|
||||
# release channel:
|
||||
# release channel, MIRI is only available in nightly
|
||||
- uses: dtolnay/rust-toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: rustfmt
|
||||
components: rustfmt,miri
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
# Check the formatting of all packages:
|
||||
@ -174,3 +174,11 @@ jobs:
|
||||
|
||||
# Run tests in esp-bootloader-esp-idf
|
||||
- run: cd esp-bootloader-esp-idf && cargo test --features=std
|
||||
|
||||
# Run tests in esp-storage
|
||||
- run: cd esp-storage && cargo test --features=emulation -- --test-threads=1
|
||||
- run: cd esp-storage && cargo test --features=emulation,bytewise-read -- --test-threads=1
|
||||
|
||||
# Miri tests in esp-storage
|
||||
- run: cd esp-storage && cargo +nightly miri test --features=emulation -- --test-threads=1
|
||||
- run: cd esp-storage && cargo +nightly miri test --features=emulation,bytewise-read -- --test-threads=1
|
||||
|
@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix incorrect usage of MaybeUninit (#3677)
|
||||
|
||||
### Removed
|
||||
|
||||
|
@ -16,7 +16,6 @@ features = ["esp32c6"]
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
embedded-storage = "0.3.1"
|
||||
|
38
esp-storage/src/buffer.rs
Normal file
38
esp-storage/src/buffer.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use core::{mem::MaybeUninit, slice};
|
||||
|
||||
pub fn uninit_slice(bytes: &[u8]) -> &[MaybeUninit<u8>] {
|
||||
unsafe { core::slice::from_raw_parts(bytes.as_ptr() as *const _, bytes.len()) }
|
||||
}
|
||||
|
||||
pub fn uninit_slice_mut(bytes: &mut [u8]) -> &mut [MaybeUninit<u8>] {
|
||||
unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut _, bytes.len()) }
|
||||
}
|
||||
|
||||
pub type FlashWordBuffer = FlashBuffer<4>;
|
||||
|
||||
pub type FlashSectorBuffer = FlashBuffer<4096>;
|
||||
|
||||
#[repr(C, align(4))]
|
||||
pub union FlashBuffer<const N: usize> {
|
||||
bytes: [MaybeUninit<u8>; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> FlashBuffer<N> {
|
||||
pub const fn uninit() -> Self {
|
||||
Self {
|
||||
bytes: [MaybeUninit::uninit(); N],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_bytes_mut(&mut self) -> &mut [MaybeUninit<u8>] {
|
||||
unsafe { self.bytes.as_mut() }
|
||||
}
|
||||
|
||||
pub unsafe fn assume_init_bytes(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.bytes.as_ptr() as *const _, self.bytes.len()) }
|
||||
}
|
||||
|
||||
pub unsafe fn assume_init_bytes_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.bytes.as_mut_ptr() as *mut _, self.bytes.len()) }
|
||||
}
|
||||
}
|
@ -1,28 +1,7 @@
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use crate::chip_specific;
|
||||
|
||||
#[repr(C, align(4))]
|
||||
pub struct FlashSectorBuffer {
|
||||
// NOTE: Ensure that no unaligned fields are added above `data` to maintain its required
|
||||
// alignment
|
||||
data: [u8; FlashStorage::SECTOR_SIZE as usize],
|
||||
}
|
||||
|
||||
impl Deref for FlashSectorBuffer {
|
||||
type Target = [u8; FlashStorage::SECTOR_SIZE as usize];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for FlashSectorBuffer {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum FlashStorageError {
|
||||
@ -71,8 +50,9 @@ impl FlashStorage {
|
||||
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
|
||||
const ADDR: u32 = 0x1000;
|
||||
|
||||
let mut buffer = [0u8; 8];
|
||||
storage.internal_read(ADDR, &mut buffer).ok();
|
||||
let mut buffer = crate::buffer::FlashWordBuffer::uninit();
|
||||
storage.internal_read(ADDR, buffer.as_bytes_mut()).unwrap();
|
||||
let buffer = unsafe { buffer.assume_init_bytes() };
|
||||
let mb = match buffer[3] & 0xf0 {
|
||||
0x00 => 1,
|
||||
0x10 => 2,
|
||||
@ -83,7 +63,6 @@ impl FlashStorage {
|
||||
_ => 0,
|
||||
};
|
||||
storage.capacity = mb * 1024 * 1024;
|
||||
|
||||
storage
|
||||
}
|
||||
|
||||
@ -111,15 +90,15 @@ impl FlashStorage {
|
||||
|
||||
#[allow(clippy::all)]
|
||||
#[inline(never)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn internal_read(
|
||||
&mut self,
|
||||
offset: u32,
|
||||
bytes: &mut [u8],
|
||||
bytes: &mut [MaybeUninit<u8>],
|
||||
) -> Result<(), FlashStorageError> {
|
||||
check_rc(chip_specific::spiflash_read(
|
||||
offset,
|
||||
bytes.as_ptr() as *mut u32,
|
||||
bytes.as_mut_ptr() as *mut u32,
|
||||
bytes.len() as u32,
|
||||
))
|
||||
}
|
||||
@ -136,7 +115,7 @@ impl FlashStorage {
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn internal_erase(&mut self, sector: u32) -> Result<(), FlashStorageError> {
|
||||
self.unlock_once()?;
|
||||
|
||||
@ -144,7 +123,7 @@ impl FlashStorage {
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn internal_write(
|
||||
&mut self,
|
||||
offset: u32,
|
||||
|
@ -39,7 +39,7 @@ crate::rom_fn! {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spi_read_status_high(
|
||||
flash_chip: *const EspRomSpiflashChipT,
|
||||
status: &mut u32,
|
||||
@ -48,26 +48,26 @@ pub(crate) fn spi_read_status_high(
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spi_read_status(flash_chip: *const EspRomSpiflashChipT, status: &mut u32) -> i32 {
|
||||
esp_rom_spi_read_status(flash_chip, status as *mut u32)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spi_write_status(flash_chip: *const EspRomSpiflashChipT, status_value: u32) -> i32 {
|
||||
esp_rom_spi_write_status(flash_chip, status_value)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
fn begin() {
|
||||
// on some chips disabling cache access caused issues - we don't really need
|
||||
// it
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
fn end() {
|
||||
esp_rom_cache_flush(0);
|
||||
esp_rom_cache_flush(1);
|
||||
@ -87,7 +87,7 @@ pub struct EspRomSpiflashChipT {
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 {
|
||||
maybe_with_critical_section(|| {
|
||||
spiflash_wait_for_ready();
|
||||
@ -96,7 +96,7 @@ pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 {
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 {
|
||||
maybe_with_critical_section(|| {
|
||||
let res = esp_rom_spiflash_erase_sector(sector_number);
|
||||
@ -106,7 +106,7 @@ pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
fn spi_write_enable() {
|
||||
spiflash_wait_for_ready();
|
||||
|
||||
@ -116,7 +116,7 @@ fn spi_write_enable() {
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 {
|
||||
maybe_with_critical_section(|| {
|
||||
begin();
|
||||
@ -175,13 +175,13 @@ pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub fn read_register(address: u32) -> u32 {
|
||||
unsafe { (address as *const u32).read_volatile() }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub fn write_register(address: u32, value: u32) {
|
||||
unsafe {
|
||||
(address as *mut u32).write_volatile(value);
|
||||
@ -189,7 +189,7 @@ pub fn write_register(address: u32, value: u32) {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
fn wait_for_ready() {
|
||||
while (read_register(SPI_EXT2_REG) & SPI_ST) != 0 {}
|
||||
// ESP32_OR_LATER ... we don't support anything earlier
|
||||
@ -197,7 +197,7 @@ fn wait_for_ready() {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
fn spiflash_wait_for_ready() {
|
||||
let flashchip = FLASH_CHIP_ADDR as *const EspRomSpiflashChipT;
|
||||
|
||||
@ -212,7 +212,7 @@ fn spiflash_wait_for_ready() {
|
||||
}
|
||||
|
||||
#[inline(never)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spiflash_unlock() -> i32 {
|
||||
let flashchip = FLASH_CHIP_ADDR as *const EspRomSpiflashChipT;
|
||||
if (unsafe { (*flashchip).device_id } >> 16) & 0xff == 0x9D {
|
||||
|
@ -8,25 +8,25 @@ crate::rom_fn! {
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spiflash_read(src_addr: u32, data: *const u32, len: u32) -> i32 {
|
||||
maybe_with_critical_section(|| esp_rom_spiflash_read(src_addr, data, len))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spiflash_unlock() -> i32 {
|
||||
maybe_with_critical_section(esp_rom_spiflash_unlock)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 {
|
||||
maybe_with_critical_section(|| esp_rom_spiflash_erase_sector(sector_number))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 {
|
||||
maybe_with_critical_section(|| esp_rom_spiflash_write(dest_addr, data, len))
|
||||
}
|
||||
|
@ -12,9 +12,9 @@
|
||||
#[cfg_attr(feature = "emulation", path = "stub.rs")]
|
||||
mod chip_specific;
|
||||
|
||||
mod buffer;
|
||||
mod common;
|
||||
|
||||
use common::FlashSectorBuffer;
|
||||
pub use common::{FlashStorage, FlashStorageError};
|
||||
|
||||
pub mod ll;
|
||||
@ -23,7 +23,7 @@ mod storage;
|
||||
|
||||
#[cfg(not(feature = "emulation"))]
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
fn maybe_with_critical_section<R>(f: impl FnOnce() -> R) -> R {
|
||||
#[cfg(feature = "critical-section")]
|
||||
return critical_section::with(|_| f());
|
||||
@ -44,7 +44,7 @@ macro_rules! rom_fn {
|
||||
$(#[$attrs])*
|
||||
#[allow(unused)]
|
||||
#[inline(always)]
|
||||
#[unsafe(link_section = ".rwtext")]
|
||||
#[cfg_attr(not(target_os = "macos"), unsafe(link_section = ".rwtext"))]
|
||||
fn $name($($arg:$ty),*) $(-> $retval)? {
|
||||
unsafe {
|
||||
let rom_fn: unsafe extern "C" fn($($arg: $ty),*) $(-> $retval)? =
|
||||
|
@ -1,5 +1,3 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use embedded_storage::nor_flash::{
|
||||
ErrorType,
|
||||
MultiwriteNorFlash,
|
||||
@ -9,37 +7,19 @@ use embedded_storage::nor_flash::{
|
||||
ReadNorFlash,
|
||||
};
|
||||
|
||||
use crate::{FlashSectorBuffer, FlashStorage, FlashStorageError};
|
||||
|
||||
#[cfg(feature = "bytewise-read")]
|
||||
#[repr(C, align(4))]
|
||||
struct FlashWordBuffer {
|
||||
// NOTE: Ensure that no unaligned fields are added above `data` to maintain its required
|
||||
// alignment
|
||||
data: [u8; FlashStorage::WORD_SIZE as usize],
|
||||
}
|
||||
|
||||
#[cfg(feature = "bytewise-read")]
|
||||
impl core::ops::Deref for FlashWordBuffer {
|
||||
type Target = [u8; FlashStorage::WORD_SIZE as usize];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "bytewise-read")]
|
||||
impl core::ops::DerefMut for FlashWordBuffer {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
use crate::buffer::FlashWordBuffer;
|
||||
use crate::{
|
||||
FlashStorage,
|
||||
FlashStorageError,
|
||||
buffer::{FlashSectorBuffer, uninit_slice, uninit_slice_mut},
|
||||
};
|
||||
|
||||
impl FlashStorage {
|
||||
#[inline(always)]
|
||||
fn is_word_aligned(bytes: &[u8]) -> bool {
|
||||
// TODO: Use is_aligned_to when stabilized (see `pointer_is_aligned`)
|
||||
(unsafe { bytes.as_ptr().offset_from(core::ptr::null()) }) % Self::WORD_SIZE as isize == 0
|
||||
(bytes.as_ptr() as usize) % (Self::WORD_SIZE as usize) == 0
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,13 +52,13 @@ impl ReadNorFlash for FlashStorage {
|
||||
let (offset, bytes) = {
|
||||
let byte_offset = (offset % Self::WORD_SIZE) as usize;
|
||||
if byte_offset > 0 {
|
||||
let mut word_buffer = MaybeUninit::<FlashWordBuffer>::uninit();
|
||||
let word_buffer = unsafe { word_buffer.assume_init_mut() };
|
||||
let mut word_buffer = FlashWordBuffer::uninit();
|
||||
|
||||
let offset = offset - byte_offset as u32;
|
||||
let length = bytes.len().min(word_buffer.len() - byte_offset);
|
||||
let length = bytes.len().min(Self::WORD_SIZE as usize - byte_offset);
|
||||
|
||||
self.internal_read(offset, &mut word_buffer[..])?;
|
||||
self.internal_read(offset, word_buffer.as_bytes_mut())?;
|
||||
let word_buffer = unsafe { word_buffer.assume_init_bytes_mut() };
|
||||
bytes[..length].copy_from_slice(&word_buffer[byte_offset..][..length]);
|
||||
|
||||
(offset + Self::WORD_SIZE, &mut bytes[length..])
|
||||
@ -95,7 +75,7 @@ impl ReadNorFlash for FlashStorage {
|
||||
{
|
||||
// Chunk already is word aligned so we can read directly to it
|
||||
#[cfg(not(feature = "bytewise-read"))]
|
||||
self.internal_read(offset, chunk)?;
|
||||
self.internal_read(offset, uninit_slice_mut(chunk))?;
|
||||
|
||||
#[cfg(feature = "bytewise-read")]
|
||||
{
|
||||
@ -103,22 +83,21 @@ impl ReadNorFlash for FlashStorage {
|
||||
let byte_length = length % Self::WORD_SIZE as usize;
|
||||
let length = length - byte_length;
|
||||
|
||||
self.internal_read(offset, &mut chunk[..length])?;
|
||||
self.internal_read(offset, &mut uninit_slice_mut(chunk)[..length])?;
|
||||
|
||||
// Read not aligned rest of data
|
||||
if byte_length > 0 {
|
||||
let mut word_buffer = MaybeUninit::<FlashWordBuffer>::uninit();
|
||||
let word_buffer = unsafe { word_buffer.assume_init_mut() };
|
||||
let mut word_buffer = FlashWordBuffer::uninit();
|
||||
|
||||
self.internal_read(offset + length as u32, &mut word_buffer[..])?;
|
||||
self.internal_read(offset + length as u32, word_buffer.as_bytes_mut())?;
|
||||
let word_buffer = unsafe { word_buffer.assume_init_bytes_mut() };
|
||||
chunk[length..].copy_from_slice(&word_buffer[..byte_length]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Bytes buffer isn't word-aligned so we might read only via aligned buffer
|
||||
let mut buffer = MaybeUninit::<FlashSectorBuffer>::uninit();
|
||||
let buffer = unsafe { buffer.assume_init_mut() };
|
||||
let mut buffer = FlashSectorBuffer::uninit();
|
||||
|
||||
for (offset, chunk) in (offset..)
|
||||
.step_by(Self::SECTOR_SIZE as _)
|
||||
@ -126,7 +105,7 @@ impl ReadNorFlash for FlashStorage {
|
||||
{
|
||||
// Read to temporary buffer first (chunk length is aligned)
|
||||
#[cfg(not(feature = "bytewise-read"))]
|
||||
self.internal_read(offset, &mut buffer[..chunk.len()])?;
|
||||
self.internal_read(offset, &mut buffer.as_bytes_mut()[..chunk.len()])?;
|
||||
|
||||
// Read to temporary buffer first (chunk length is not aligned)
|
||||
#[cfg(feature = "bytewise-read")]
|
||||
@ -139,8 +118,9 @@ impl ReadNorFlash for FlashStorage {
|
||||
length
|
||||
};
|
||||
|
||||
self.internal_read(offset, &mut buffer[..length])?;
|
||||
self.internal_read(offset, &mut buffer.as_bytes_mut()[..length])?;
|
||||
}
|
||||
let buffer = unsafe { buffer.assume_init_bytes() };
|
||||
|
||||
// Copy to bytes buffer
|
||||
chunk.copy_from_slice(&buffer[..chunk.len()]);
|
||||
@ -174,17 +154,18 @@ impl NorFlash for FlashStorage {
|
||||
}
|
||||
} else {
|
||||
// Bytes buffer isn't word-aligned so we might write only via aligned buffer
|
||||
let mut buffer = MaybeUninit::<FlashSectorBuffer>::uninit();
|
||||
let buffer = unsafe { buffer.assume_init_mut() };
|
||||
let mut buffer = FlashSectorBuffer::uninit();
|
||||
|
||||
for (offset, chunk) in (offset..)
|
||||
.step_by(Self::SECTOR_SIZE as _)
|
||||
.zip(bytes.chunks(Self::SECTOR_SIZE as _))
|
||||
{
|
||||
// Copy to temporary buffer first
|
||||
buffer[..chunk.len()].copy_from_slice(chunk);
|
||||
buffer.as_bytes_mut()[..chunk.len()].copy_from_slice(uninit_slice(chunk));
|
||||
// Write from temporary buffer
|
||||
self.internal_write(offset, &buffer[..chunk.len()])?;
|
||||
self.internal_write(offset, unsafe {
|
||||
&buffer.assume_init_bytes()[..chunk.len()]
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,10 +187,9 @@ impl NorFlash for FlashStorage {
|
||||
|
||||
impl MultiwriteNorFlash for FlashStorage {}
|
||||
|
||||
// Run the tests with `--test-threads=1` - the emulation is not multithread safe
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use super::*;
|
||||
|
||||
const WORD_SIZE: u32 = 4;
|
||||
@ -221,9 +201,7 @@ mod test {
|
||||
|
||||
#[repr(C, align(4))]
|
||||
struct TestBuffer {
|
||||
// NOTE: Ensure that no unaligned fields are added above `data` to maintain its required
|
||||
// alignment
|
||||
data: MaybeUninit<[u8; FLASH_SIZE as _]>,
|
||||
data: [u8; FLASH_SIZE as _],
|
||||
}
|
||||
|
||||
impl TestBuffer {
|
||||
@ -234,34 +212,19 @@ mod test {
|
||||
data[index as usize] = (index & 0xff) as u8;
|
||||
index += 1;
|
||||
}
|
||||
Self {
|
||||
data: MaybeUninit::new(data),
|
||||
}
|
||||
Self { data }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TestBuffer {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
data: MaybeUninit::uninit(),
|
||||
data: [0u8; FLASH_SIZE as usize],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for TestBuffer {
|
||||
type Target = [u8; FLASH_SIZE as usize];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { self.data.assume_init_ref() }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for TestBuffer {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { self.data.assume_init_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(miri))]
|
||||
fn range_gen<const ALIGN: u32, const MAX_OFF: u32, const MAX_LEN: u32>(
|
||||
aligned: Option<bool>,
|
||||
) -> impl Iterator<Item = (u32, u32)> {
|
||||
@ -276,19 +239,45 @@ mod test {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(miri)]
|
||||
fn range_gen<const ALIGN: u32, const MAX_OFF: u32, const MAX_LEN: u32>(
|
||||
aligned: Option<bool>,
|
||||
) -> impl Iterator<Item = (u32, u32)> {
|
||||
// MIRI is very slow - just use a couple of combinations
|
||||
match aligned {
|
||||
Some(true) => vec![(0, 4), (0, 8), (0, 16), (0, 32), (0, 1024)],
|
||||
Some(false) => vec![(3, 7), (11, 11)],
|
||||
None => vec![
|
||||
(0, 4),
|
||||
(0, 8),
|
||||
(0, 16),
|
||||
(0, 32),
|
||||
(0, 1024),
|
||||
(3, 7),
|
||||
(11, 11),
|
||||
(0, 4098),
|
||||
],
|
||||
}
|
||||
.into_iter()
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "bytewise-read"))]
|
||||
fn aligned_read() {
|
||||
let mut flash = FlashStorage::new();
|
||||
flash.capacity = 4 * 4096;
|
||||
let src = TestBuffer::seq();
|
||||
let mut data = TestBuffer::default();
|
||||
|
||||
flash.erase(0, FLASH_SIZE).unwrap();
|
||||
flash.write(0, &*src).unwrap();
|
||||
flash.write(0, &src.data).unwrap();
|
||||
|
||||
for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(Some(true)) {
|
||||
flash.read(off, &mut data[..len as usize]).unwrap();
|
||||
assert_eq!(data[..len as usize], src[off as usize..][..len as usize]);
|
||||
flash.read(off, &mut data.data[..len as usize]).unwrap();
|
||||
assert_eq!(
|
||||
data.data[..len as usize],
|
||||
src.data[off as usize..][..len as usize]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -296,10 +285,11 @@ mod test {
|
||||
#[cfg(not(feature = "bytewise-read"))]
|
||||
fn not_aligned_read_aligned_buffer() {
|
||||
let mut flash = FlashStorage::new();
|
||||
flash.capacity = 4 * 4096;
|
||||
let mut data = TestBuffer::default();
|
||||
|
||||
for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(Some(false)) {
|
||||
flash.read(off, &mut data[..len as usize]).unwrap_err();
|
||||
flash.read(off, &mut data.data[..len as usize]).unwrap_err();
|
||||
}
|
||||
}
|
||||
|
||||
@ -307,17 +297,20 @@ mod test {
|
||||
#[cfg(not(feature = "bytewise-read"))]
|
||||
fn aligned_read_not_aligned_buffer() {
|
||||
let mut flash = FlashStorage::new();
|
||||
flash.capacity = 4 * 4096;
|
||||
let src = TestBuffer::seq();
|
||||
let mut data = TestBuffer::default();
|
||||
|
||||
flash.erase(0, FLASH_SIZE).unwrap();
|
||||
flash.write(0, &*src).unwrap();
|
||||
flash.write(0, &src.data).unwrap();
|
||||
|
||||
for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(Some(true)) {
|
||||
flash.read(off, &mut data[1..][..len as usize]).unwrap();
|
||||
flash
|
||||
.read(off, &mut data.data[1..][..len as usize])
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
data[1..][..len as usize],
|
||||
src[off as usize..][..len as usize]
|
||||
data.data[1..][..len as usize],
|
||||
src.data[off as usize..][..len as usize]
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -326,15 +319,19 @@ mod test {
|
||||
#[cfg(feature = "bytewise-read")]
|
||||
fn bytewise_read_aligned_buffer() {
|
||||
let mut flash = FlashStorage::new();
|
||||
flash.capacity = 4 * 4096;
|
||||
let src = TestBuffer::seq();
|
||||
let mut data = TestBuffer::default();
|
||||
|
||||
flash.erase(0, FLASH_SIZE).unwrap();
|
||||
flash.write(0, &*src).unwrap();
|
||||
flash.write(0, &src.data).unwrap();
|
||||
|
||||
for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(None) {
|
||||
flash.read(off, &mut data[..len as usize]).unwrap();
|
||||
assert_eq!(data[..len as usize], src[off as usize..][..len as usize]);
|
||||
flash.read(off, &mut data.data[..len as usize]).unwrap();
|
||||
assert_eq!(
|
||||
data.data[..len as usize],
|
||||
src.data[off as usize..][..len as usize]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,18 +339,36 @@ mod test {
|
||||
#[cfg(feature = "bytewise-read")]
|
||||
fn bytewise_read_not_aligned_buffer() {
|
||||
let mut flash = FlashStorage::new();
|
||||
flash.capacity = 4 * 4096;
|
||||
let src = TestBuffer::seq();
|
||||
let mut data = TestBuffer::default();
|
||||
|
||||
flash.erase(0, FLASH_SIZE).unwrap();
|
||||
flash.write(0, &*src).unwrap();
|
||||
flash.write(0, &src.data).unwrap();
|
||||
|
||||
for (off, len) in range_gen::<WORD_SIZE, MAX_OFFSET, MAX_LENGTH>(None) {
|
||||
flash.read(off, &mut data[1..][..len as usize]).unwrap();
|
||||
flash
|
||||
.read(off, &mut data.data[1..][..len as usize])
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
data[1..][..len as usize],
|
||||
src[off as usize..][..len as usize]
|
||||
data.data[1..][..len as usize],
|
||||
src.data[off as usize..][..len as usize]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_not_aligned_buffer() {
|
||||
let mut flash = FlashStorage::new();
|
||||
flash.capacity = 4 * 4096;
|
||||
let mut read_data = TestBuffer::default();
|
||||
let write_data = TestBuffer::seq();
|
||||
|
||||
flash.erase(0, FLASH_SIZE).unwrap();
|
||||
flash.write(0, &write_data.data[1..129]).unwrap();
|
||||
|
||||
flash.read(0, &mut read_data.data[..128]).unwrap();
|
||||
|
||||
assert_eq!(&read_data.data[..128], &write_data.data[1..129]);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use embedded_storage::{ReadStorage, Storage};
|
||||
|
||||
use crate::{FlashSectorBuffer, FlashStorage, FlashStorageError};
|
||||
use crate::{FlashStorage, FlashStorageError, buffer::FlashSectorBuffer};
|
||||
|
||||
impl ReadStorage for FlashStorage {
|
||||
type Error = FlashStorageError;
|
||||
@ -14,8 +12,7 @@ impl ReadStorage for FlashStorage {
|
||||
let mut aligned_offset = offset - data_offset;
|
||||
|
||||
// Bypass clearing sector buffer for performance reasons
|
||||
let mut sector_data = MaybeUninit::<FlashSectorBuffer>::uninit();
|
||||
let sector_data = unsafe { sector_data.assume_init_mut() };
|
||||
let mut sector_data = FlashSectorBuffer::uninit();
|
||||
|
||||
while !bytes.is_empty() {
|
||||
let len = bytes.len().min((Self::SECTOR_SIZE - data_offset) as _);
|
||||
@ -24,8 +21,11 @@ impl ReadStorage for FlashStorage {
|
||||
& !(Self::WORD_SIZE - 1) as usize;
|
||||
|
||||
// Read only needed data words
|
||||
self.internal_read(aligned_offset, &mut sector_data[..aligned_end])?;
|
||||
|
||||
let sector_data = &mut sector_data.as_bytes_mut()[..aligned_end];
|
||||
self.internal_read(aligned_offset, sector_data)?;
|
||||
let sector_data = unsafe {
|
||||
core::slice::from_raw_parts_mut(sector_data.as_ptr() as *mut u8, sector_data.len())
|
||||
};
|
||||
bytes[..len].copy_from_slice(§or_data[data_offset as usize..][..len]);
|
||||
|
||||
aligned_offset += Self::SECTOR_SIZE;
|
||||
@ -52,17 +52,17 @@ impl Storage for FlashStorage {
|
||||
let mut aligned_offset = offset - data_offset;
|
||||
|
||||
// Bypass clearing sector buffer for performance reasons
|
||||
let mut sector_data = MaybeUninit::<FlashSectorBuffer>::uninit();
|
||||
let sector_data = unsafe { sector_data.assume_init_mut() };
|
||||
let mut sector_data = FlashSectorBuffer::uninit();
|
||||
|
||||
while !bytes.is_empty() {
|
||||
self.internal_read(aligned_offset, &mut sector_data[..])?;
|
||||
self.internal_read(aligned_offset, sector_data.as_bytes_mut())?;
|
||||
let sector_data = unsafe { sector_data.assume_init_bytes_mut() };
|
||||
|
||||
let len = bytes.len().min((Self::SECTOR_SIZE - data_offset) as _);
|
||||
|
||||
sector_data[data_offset as usize..][..len].copy_from_slice(&bytes[..len]);
|
||||
self.internal_erase(aligned_offset / Self::SECTOR_SIZE)?;
|
||||
self.internal_write(aligned_offset, §or_data[..])?;
|
||||
self.internal_write(aligned_offset, sector_data)?;
|
||||
|
||||
aligned_offset += Self::SECTOR_SIZE;
|
||||
data_offset = 0;
|
||||
|
@ -24,6 +24,7 @@ fn check<const ALIGN: u32, const SIZE: u32, const MAX_LEN: u32>(
|
||||
offset: u32,
|
||||
length: u32,
|
||||
data: *const u32,
|
||||
is_write: bool,
|
||||
) -> bool {
|
||||
if offset % ALIGN > 0 {
|
||||
print_error!("Not aligned offset: {offset}");
|
||||
@ -45,12 +46,12 @@ fn check<const ALIGN: u32, const SIZE: u32, const MAX_LEN: u32>(
|
||||
print_error!("Length out of range: {length} > {MAX_LEN}");
|
||||
return false;
|
||||
}
|
||||
let addr = unsafe { (data as *const u8).offset_from(ptr::null()) } as u32;
|
||||
let addr = data as u32;
|
||||
if addr % ALIGN > 0 {
|
||||
print_error!("Not aligned data: {addr:#0x}");
|
||||
return false;
|
||||
}
|
||||
if unsafe { FLASH_LOCK } {
|
||||
if is_write && unsafe { FLASH_LOCK } {
|
||||
print_error!("Flash locked");
|
||||
return false;
|
||||
}
|
||||
@ -58,7 +59,7 @@ fn check<const ALIGN: u32, const SIZE: u32, const MAX_LEN: u32>(
|
||||
}
|
||||
|
||||
pub(crate) fn spiflash_read(src_addr: u32, data: *mut u32, len: u32) -> i32 {
|
||||
if check::<WORD_SIZE, FLASH_SIZE, SECTOR_SIZE>(src_addr, len, data) {
|
||||
if check::<WORD_SIZE, FLASH_SIZE, SECTOR_SIZE>(src_addr, len, data, false) {
|
||||
maybe_with_critical_section(|| {
|
||||
let src = unsafe { slice::from_raw_parts_mut(data as *mut u8, len as _) };
|
||||
unsafe { src.copy_from_slice(&FLASH_DATA[src_addr as usize..][..len as usize]) };
|
||||
@ -77,7 +78,7 @@ pub(crate) fn spiflash_unlock() -> i32 {
|
||||
}
|
||||
|
||||
pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 {
|
||||
if check::<1, NUM_SECTORS, 1>(sector_number, 1, ptr::null()) {
|
||||
if check::<1, NUM_SECTORS, 1>(sector_number, 1, ptr::null(), true) {
|
||||
maybe_with_critical_section(|| {
|
||||
let dst_addr = sector_number * SECTOR_SIZE;
|
||||
let len = SECTOR_SIZE;
|
||||
@ -90,7 +91,7 @@ pub(crate) fn spiflash_erase_sector(sector_number: u32) -> i32 {
|
||||
}
|
||||
|
||||
pub(crate) fn spiflash_write(dest_addr: u32, data: *const u32, len: u32) -> i32 {
|
||||
if check::<WORD_SIZE, FLASH_SIZE, SECTOR_SIZE>(dest_addr, len, data) {
|
||||
if check::<WORD_SIZE, FLASH_SIZE, SECTOR_SIZE>(dest_addr, len, data, true) {
|
||||
maybe_with_critical_section(|| {
|
||||
let dst = unsafe { slice::from_raw_parts(data as *const u8, len as _) };
|
||||
for (d, s) in unsafe { &mut FLASH_DATA[dest_addr as usize..][..len as usize] }
|
||||
|
Loading…
x
Reference in New Issue
Block a user