Merge remote-tracking branch 'upstream/master' into avoid-write-before-erase

This commit is contained in:
Rasmus Melchior Jacobsen 2023-04-04 12:29:24 +02:00
commit c38eb9660b
5 changed files with 38 additions and 77 deletions

View File

@ -1,7 +1,7 @@
use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind};
use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash; use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
use crate::{FirmwareWriter, Partition, State, BOOT_MAGIC, SWAP_MAGIC}; use crate::{Partition, State, BOOT_MAGIC, SWAP_MAGIC};
/// Errors returned by FirmwareUpdater /// Errors returned by FirmwareUpdater
#[derive(Debug)] #[derive(Debug)]
@ -256,7 +256,6 @@ impl FirmwareUpdater {
offset: usize, offset: usize,
data: &[u8], data: &[u8],
dfu_flash: &mut F, dfu_flash: &mut F,
block_size: usize,
) -> Result<(), FirmwareUpdaterError> { ) -> Result<(), FirmwareUpdaterError> {
assert!(data.len() >= F::ERASE_SIZE); assert!(data.len() >= F::ERASE_SIZE);
@ -264,25 +263,23 @@ impl FirmwareUpdater {
.erase(dfu_flash, offset as u32, (offset + data.len()) as u32) .erase(dfu_flash, offset as u32, (offset + data.len()) as u32)
.await?; .await?;
FirmwareWriter(self.dfu) self.dfu.write(dfu_flash, offset as u32, data).await?;
.write_block(offset, data, dfu_flash, block_size)
.await?;
Ok(()) Ok(())
} }
/// Prepare for an incoming DFU update by erasing the entire DFU area and /// Prepare for an incoming DFU update by erasing the entire DFU area and
/// returning a `FirmwareWriter`. /// returning its `Partition`.
/// ///
/// Using this instead of `write_firmware` allows for an optimized API in /// Using this instead of `write_firmware` allows for an optimized API in
/// exchange for added complexity. /// exchange for added complexity.
pub async fn prepare_update<F: AsyncNorFlash>( pub async fn prepare_update<F: AsyncNorFlash>(
&mut self, &mut self,
dfu_flash: &mut F, dfu_flash: &mut F,
) -> Result<FirmwareWriter, FirmwareUpdaterError> { ) -> Result<Partition, FirmwareUpdaterError> {
self.dfu.wipe(dfu_flash).await?; self.dfu.wipe(dfu_flash).await?;
Ok(FirmwareWriter(self.dfu)) Ok(self.dfu)
} }
// //
@ -469,29 +466,25 @@ impl FirmwareUpdater {
offset: usize, offset: usize,
data: &[u8], data: &[u8],
dfu_flash: &mut F, dfu_flash: &mut F,
block_size: usize,
) -> Result<(), FirmwareUpdaterError> { ) -> Result<(), FirmwareUpdaterError> {
assert!(data.len() >= F::ERASE_SIZE); assert!(data.len() >= F::ERASE_SIZE);
self.dfu self.dfu
.erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?; .erase_blocking(dfu_flash, offset as u32, (offset + data.len()) as u32)?;
FirmwareWriter(self.dfu).write_block_blocking(offset, data, dfu_flash, block_size)?; self.dfu.write_blocking(dfu_flash, offset as u32, data)?;
Ok(()) Ok(())
} }
/// Prepare for an incoming DFU update by erasing the entire DFU area and /// Prepare for an incoming DFU update by erasing the entire DFU area and
/// returning a `FirmwareWriter`. /// returning its `Partition`.
/// ///
/// Using this instead of `write_firmware_blocking` allows for an optimized /// Using this instead of `write_firmware_blocking` allows for an optimized
/// API in exchange for added complexity. /// API in exchange for added complexity.
pub fn prepare_update_blocking<F: NorFlash>( pub fn prepare_update_blocking<F: NorFlash>(&mut self, flash: &mut F) -> Result<Partition, FirmwareUpdaterError> {
&mut self,
flash: &mut F,
) -> Result<FirmwareWriter, FirmwareUpdaterError> {
self.dfu.wipe_blocking(flash)?; self.dfu.wipe_blocking(flash)?;
Ok(FirmwareWriter(self.dfu)) Ok(self.dfu)
} }
} }

View File

@ -1,55 +0,0 @@
use embedded_storage::nor_flash::NorFlash;
use embedded_storage_async::nor_flash::NorFlash as AsyncNorFlash;
use crate::Partition;
/// FirmwareWriter allows writing blocks to an already erased flash.
pub struct FirmwareWriter(pub(crate) Partition);
impl FirmwareWriter {
/// Write data to a flash page.
///
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
///
/// # Safety
///
/// Failing to meet alignment and size requirements may result in a panic.
pub async fn write_block<F: AsyncNorFlash>(
&mut self,
offset: usize,
data: &[u8],
flash: &mut F,
block_size: usize,
) -> Result<(), F::Error> {
let mut offset = offset as u32;
for chunk in data.chunks(block_size) {
self.0.write(flash, offset, chunk).await?;
offset += chunk.len() as u32;
}
Ok(())
}
/// Write data to a flash page.
///
/// The buffer must follow alignment requirements of the target flash and a multiple of page size big.
///
/// # Safety
///
/// Failing to meet alignment and size requirements may result in a panic.
pub fn write_block_blocking<F: NorFlash>(
&mut self,
offset: usize,
data: &[u8],
flash: &mut F,
block_size: usize,
) -> Result<(), F::Error> {
let mut offset = offset as u32;
for chunk in data.chunks(block_size) {
self.0.write_blocking(flash, offset, chunk)?;
offset += chunk.len() as u32;
}
Ok(())
}
}

View File

@ -7,13 +7,11 @@ mod fmt;
mod boot_loader; mod boot_loader;
mod firmware_updater; mod firmware_updater;
mod firmware_writer;
mod mem_flash; mod mem_flash;
mod partition; mod partition;
pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig}; pub use boot_loader::{BootError, BootFlash, BootLoader, Flash, FlashConfig, MultiFlashConfig, SingleFlashConfig};
pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError}; pub use firmware_updater::{FirmwareUpdater, FirmwareUpdaterError};
pub use firmware_writer::FirmwareWriter;
pub use partition::Partition; pub use partition::Partition;
pub(crate) const BOOT_MAGIC: u8 = 0xD0; pub(crate) const BOOT_MAGIC: u8 = 0xD0;
@ -107,7 +105,7 @@ mod tests {
let mut updater = FirmwareUpdater::new(DFU, STATE); let mut updater = FirmwareUpdater::new(DFU, STATE);
let mut offset = 0; let mut offset = 0;
for chunk in update.chunks(4096) { for chunk in update.chunks(4096) {
block_on(updater.write_firmware(offset, chunk, &mut flash, 4096)).unwrap(); block_on(updater.write_firmware(offset, chunk, &mut flash)).unwrap();
offset += chunk.len(); offset += chunk.len();
} }
block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap(); block_on(updater.mark_updated(&mut flash, &mut aligned)).unwrap();
@ -180,7 +178,7 @@ mod tests {
let mut offset = 0; let mut offset = 0;
for chunk in update.chunks(2048) { for chunk in update.chunks(2048) {
block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap();
offset += chunk.len(); offset += chunk.len();
} }
block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();
@ -233,7 +231,7 @@ mod tests {
let mut offset = 0; let mut offset = 0;
for chunk in update.chunks(4096) { for chunk in update.chunks(4096) {
block_on(updater.write_firmware(offset, chunk, &mut dfu, chunk.len())).unwrap(); block_on(updater.write_firmware(offset, chunk, &mut dfu)).unwrap();
offset += chunk.len(); offset += chunk.len();
} }
block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap(); block_on(updater.mark_updated(&mut state, &mut aligned)).unwrap();

View File

@ -1,7 +1,7 @@
# Before upgrading check that everything is available on all tier1 targets here: # Before upgrading check that everything is available on all tier1 targets here:
# https://rust-lang.github.io/rustup-components-history # https://rust-lang.github.io/rustup-components-history
[toolchain] [toolchain]
channel = "nightly-2023-02-07" channel = "nightly-2023-04-02"
components = [ "rust-src", "rustfmt", "llvm-tools-preview" ] components = [ "rust-src", "rustfmt", "llvm-tools-preview" ]
targets = [ targets = [
"thumbv7em-none-eabi", "thumbv7em-none-eabi",

View File

@ -0,0 +1,25 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::{assert, info};
use embassy_executor::Spawner;
use embassy_time::{Duration, Instant, Timer};
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let _p = embassy_nrf::init(Default::default());
info!("Hello World!");
let start = Instant::now();
Timer::after(Duration::from_millis(100)).await;
let end = Instant::now();
let ms = (end - start).as_millis();
info!("slept for {} ms", ms);
assert!(ms >= 99);
assert!(ms < 110);
info!("Test OK");
cortex_m::asm::bkpt();
}