Skip to content

Commit

Permalink
Fix configuration location in flash
Browse files Browse the repository at this point in the history
  • Loading branch information
surban committed Sep 6, 2023
1 parent 9dd29a8 commit 27c74a5
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 64 deletions.
59 changes: 25 additions & 34 deletions openemc-firmware/src/flash_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ enum FlashCopy {

/// Data stored in flash.
pub struct FlashBackened<T> {
flash_offset1: u32,
flash_offset2: u32,
flash_addr1: usize,
flash_addr2: usize,
reserved: usize,
data: T,
version: u8,
Expand All @@ -40,9 +40,9 @@ where
{
const HEADER_SIZE: usize = 8;

/// Create copies at specified offsets in flash.
pub fn new_at(
flash: &mut FlashWriter, crc: &mut Crc, flash_offset1: u32, flash_offset2: u32, reserved: usize,
/// Create copies at specified addresses in flash.
pub fn new(
flash: &mut FlashWriter, crc: &mut Crc, flash_addr1: usize, flash_addr2: usize, reserved: usize,
erase: bool,
) -> Self {
assert!(size_of::<T>() % 2 == 0, "data must have even size");
Expand All @@ -51,12 +51,12 @@ where
reserved >= size_of::<T>() + Self::HEADER_SIZE,
"reserved size must be at least size of data plus header size"
);
assert!(flash_offset1 % flash_page_size() as u32 == 0, "flash copy 1 is not aligned to page boundary");
assert!(flash_offset2 % flash_page_size() as u32 == 0, "flash copy 2 is not aligned to page boundary");
assert!(flash_addr1 % flash_page_size() == 0, "flash copy 1 is not aligned to page boundary");
assert!(flash_addr2 % flash_page_size() == 0, "flash copy 2 is not aligned to page boundary");

let mut this = Self {
flash_offset1,
flash_offset2,
flash_addr1,
flash_addr2,
reserved,
data: T::validate(unsafe { zeroed() }),
version: 0,
Expand All @@ -72,24 +72,15 @@ where
this
}

/// Create copies with end offset in flash specified.
pub fn new_at_end(
flash: &mut FlashWriter, crc: &mut Crc, reserved: usize, end_offset: u32, erase: bool,
) -> Self {
let flash_offset2 = end_offset - reserved as u32;
let flash_offset1 = flash_offset2 - reserved as u32;
Self::new_at(flash, crc, flash_offset1, flash_offset2, reserved, erase)
}

/// Read copy from flash and verify CRC32.
fn load_from<'a>(
flash: &'a FlashWriter, crc: &mut Crc, offset: u32, reserved: usize,
flash: &'a FlashWriter, crc: &mut Crc, addr: usize, reserved: usize,
) -> Option<(u8, &'a MaybeUninit<T>)> {
let mut crc_buf = [0; 4];
flash.read_into(offset, &mut crc_buf);
flash.read_into(addr, &mut crc_buf);
let crc_read = u32::from_le_bytes(crc_buf);

let protected = flash.read_unwrap(offset + 4, reserved - 4);
let protected = flash.read_unwrap(addr + 4, reserved - 4);
if crc_read != crc32(crc, protected) {
return None;
}
Expand All @@ -101,28 +92,28 @@ where
}

/// Save copy to flash protected with CRC32 checksum.
fn save_to(flash: &mut FlashWriter, crc: &mut Crc, offset: u32, version: u8, reserved: usize, value: &T) {
flash.erase_unwrap(offset, reserved);
fn save_to(flash: &mut FlashWriter, crc: &mut Crc, addr: usize, version: u8, reserved: usize, value: &T) {
flash.erase_unwrap(addr, reserved);

flash.write_unwrap(offset + 4, &[version, 0, 0, 0]);
flash.write_unwrap(addr + 4, &[version, 0, 0, 0]);

let data_pos = offset + Self::HEADER_SIZE as u32;
let data_pos = addr + Self::HEADER_SIZE;
let data = unsafe { slice::from_raw_parts(value as *const T as *const u8, size_of::<T>()) };
flash.write_unwrap(data_pos, data);

for pos in (data_pos + data.len() as u32..offset + reserved as u32).step_by(2) {
for pos in (data_pos + data.len()..addr + reserved).step_by(2) {
flash.write_unwrap(pos, &[0, 0]);
}

let protected = flash.read_unwrap(offset + 4, reserved - 4);
let protected = flash.read_unwrap(addr + 4, reserved - 4);
let crc_buf = crc32(crc, protected).to_le_bytes();
flash.write_unwrap(offset, &crc_buf);
flash.write_unwrap(addr, &crc_buf);
}

/// Load data from flash.
pub fn load(&mut self, flash: &mut FlashWriter, crc: &mut Crc) {
let data1 = Self::load_from(flash, crc, self.flash_offset1, self.reserved);
let data2 = Self::load_from(flash, crc, self.flash_offset2, self.reserved);
let data1 = Self::load_from(flash, crc, self.flash_addr1, self.reserved);
let data2 = Self::load_from(flash, crc, self.flash_addr2, self.reserved);

let (version, data, next_save) = match (data1, data2) {
(Some((version1, data1)), None) => (version1, *data1, FlashCopy::Copy2),
Expand All @@ -148,20 +139,20 @@ where
self.version = self.version.wrapping_add(1);
self.next_save = match self.next_save {
FlashCopy::Copy1 => {
Self::save_to(flash, crc, self.flash_offset1, self.version, self.reserved, &self.data);
Self::save_to(flash, crc, self.flash_addr1, self.version, self.reserved, &self.data);
FlashCopy::Copy2
}
FlashCopy::Copy2 => {
Self::save_to(flash, crc, self.flash_offset2, self.version, self.reserved, &self.data);
Self::save_to(flash, crc, self.flash_addr2, self.version, self.reserved, &self.data);
FlashCopy::Copy1
}
};
}

/// Erase data in flash and init with defaults.
pub fn erase(&mut self, flash: &mut FlashWriter) {
flash.erase_unwrap(self.flash_offset1, self.reserved);
flash.erase_unwrap(self.flash_offset2, self.reserved);
flash.erase_unwrap(self.flash_addr1, self.reserved);
flash.erase_unwrap(self.flash_addr2, self.reserved);

self.version = 0;
self.data = T::validate(unsafe { zeroed() });
Expand Down
12 changes: 6 additions & 6 deletions openemc-firmware/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ use stm32f1xx_hal::{
backup_domain::BackupDomain,
crc::Crc,
flash,
flash::{FlashWriter, FLASH_START},
flash::FlashWriter,
gpio::{Alternate, OpenDrain, Pin},
i2c,
i2c::I2c,
Expand Down Expand Up @@ -366,16 +366,16 @@ mod app {

// Load configuration from flash.
let cfg_size = CFG_FLASH_PAGES * flash_page_size();
let cfg_addr = flash_end() - 2 * cfg_size;
defmt::debug!("program ends at 0x{:x} and configuration is at 0x{:x}", flash_program_end(), cfg_addr);
defmt::assert!(flash_program_end() <= cfg_addr, "no space for configuration in flash");
let cfg_addr2 = flash_end() - cfg_size;
let cfg_addr1 = cfg_addr2 - cfg_size;
defmt::debug!("program ends at 0x{:x} and configuration is at 0x{:x}", flash_program_end(), cfg_addr1);
defmt::assert!(flash_program_end() <= cfg_addr1, "no space for configuration in flash");
let erase_cfg = bi.boot_reason == BootReason::FactoryReset as _ || option_env!("FACTORY_RESET").is_some();
if erase_cfg {
defmt::info!("Erasing configuration due to factory reset");
}
let mut fw = FlashWriter::new(&mut flash);
let cfg =
FlashBackened::new_at_end(&mut fw, &mut crc, cfg_size, cfg_addr as u32 - FLASH_START, erase_cfg);
let cfg = FlashBackened::new(&mut fw, &mut crc, cfg_addr1, cfg_addr2, cfg_size, erase_cfg);

// Create board handler.
defmt::info!("board new");
Expand Down
40 changes: 16 additions & 24 deletions openemc-firmware/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
//! Debug functions.
//! Utility functions.
use core::{mem::size_of, ptr};
use defmt::{panic, unwrap};
use stm32f1xx_hal::flash::{self, FlashSize, FlashWriter, SectorSize, FLASH_START};

use crate::{flash_end, flash_page_size};

// use crate::CPU_CLOCK;
//
// /// Approximately delay execution by specified amount of milliseconds.
// pub fn delay_ms(ms: u32) {
// let cycles = ms * (crate::CPU_CLOCK / 1000);
// cortex_m::asm::delay(cycles)
// }

/// Byte representation of an array of u64s.
pub fn array_from_u64<const D: usize, const F: usize>(data: &[u64; D]) -> [u8; F] {
defmt::assert!(F == D * size_of::<u64>());
Expand Down Expand Up @@ -89,16 +81,16 @@ pub trait FlashUtil {
fn new(parts: &mut flash::Parts) -> FlashWriter;

/// Read and panic if failed.
fn read_unwrap(&self, offset: u32, length: usize) -> &[u8];
fn read_unwrap(&self, addr: usize, length: usize) -> &[u8];

/// Read from flash into provided buffer.
fn read_into(&self, offset: u32, buf: &mut [u8]);
fn read_into(&self, addr: usize, buf: &mut [u8]);

/// Erase and panic if failed.
fn erase_unwrap(&mut self, offset: u32, length: usize);
fn erase_unwrap(&mut self, addr: usize, length: usize);

/// Write and panic if failed.
fn write_unwrap(&mut self, offset: u32, data: &[u8]);
fn write_unwrap(&mut self, addr: usize, data: &[u8]);
}

impl<'a> FlashUtil for FlashWriter<'a> {
Expand Down Expand Up @@ -126,28 +118,28 @@ impl<'a> FlashUtil for FlashWriter<'a> {
parts.writer(sector_size, flash_size)
}

fn read_unwrap(&self, offset: u32, length: usize) -> &[u8] {
let Ok(data) = self.read(offset, length) else {
panic!("flash read of length {} at offset 0x{:x} failed", length, offset);
fn read_unwrap(&self, addr: usize, length: usize) -> &[u8] {
let Ok(data) = self.read(addr as u32 - FLASH_START, length) else {
panic!("flash read of length {} at 0x{:x} failed", length, addr);
};
data
}

fn read_into(&self, offset: u32, buf: &mut [u8]) {
fn read_into(&self, addr: usize, buf: &mut [u8]) {
let len = buf.len();
let data = self.read_unwrap(offset, len);
let data = self.read_unwrap(addr, len);
buf.copy_from_slice(data);
}

fn erase_unwrap(&mut self, offset: u32, length: usize) {
let Ok(()) = self.erase(offset, length) else {
panic!("flash erase of length {} at offset 0x{:x} failed", length, offset);
fn erase_unwrap(&mut self, addr: usize, length: usize) {
let Ok(()) = self.erase(addr as u32 - FLASH_START, length) else {
panic!("flash erase of length {} at 0x{:x} failed", length, addr);
};
}

fn write_unwrap(&mut self, offset: u32, data: &[u8]) {
let Ok(()) = self.write(offset, data) else {
panic!("flash write of length {} at offset 0x{:x} failed", data.len(), offset);
fn write_unwrap(&mut self, addr: usize, data: &[u8]) {
let Ok(()) = self.write(addr as u32 - FLASH_START, data) else {
panic!("flash write of length {} at 0x{:x} failed", data.len(), addr);
};
}
}

0 comments on commit 27c74a5

Please sign in to comment.