humpback-dds/src/flash_store.rs

166 lines
5.2 KiB
Rust

use crate::flash::Error as FlashError;
use crate::flash::Flash;
use sfkv::{Store, StoreBackend};
use stm32h7xx_hal::pac::FLASH;
use log::error;
// Use the first 8KiB of last sector of bank 2 for config
// i.e. 0x081E0000 to 0x081E2000
// The sector size might be too large for `compact()` to work with intended benefit.
pub const FLASH_SECTOR_SIZE: usize = 0x2000;
pub const FLASH_BANK: u8 = 2;
pub const FLASH_SECTOR: u8 = 7;
pub const FLASH_SECTOR_OFFSET: usize = 0x1E0000;
pub static mut BACKUP_SPACE: [u8; FLASH_SECTOR_SIZE] = [0x00; FLASH_SECTOR_SIZE];
// Middle space will pretend to be the flash (i.e. the fake flash, or a very large cache)
// STM32H7 flash is an abomination.
// static mut MIDDLE_SPACE: [u8; FLASH_SECTOR_SIZE] = [0xFF; FLASH_SECTOR_SIZE];
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SFKVProcessType {
Length,
Type,
Space,
Data,
}
// Manage access to flash memory
// Motivation: STM32H7 flash only accepts 32-bytes transaction, SFKV mandates byte-by-byte access
// A middle layer is needed to inplement SFKV, that is acceptable to STM32H7 flash
// Idea: Forces BACKUP_SPACE to act as a cache.
pub struct FakeFlashManager {
// FSM: Track the type of data being programmed
process_type: SFKVProcessType,
}
impl StoreBackend for FakeFlashManager {
type Data = [u8];
// Return
fn data(&self) -> &Self::Data {
unsafe { &BACKUP_SPACE }
}
type Error = FlashError;
fn erase(&mut self) -> Result<(), Self::Error> {
self.reset_state();
Ok(())
}
fn program(&mut self, offset: usize, payload: &[u8]) -> Result<(), Self::Error> {
// Skip programming if payload is 0 length
if payload.len() == 0 {
self.advance_state();
return Ok(());
}
// Examine payload address
// If it is not identical to the programming address of the bankup space,
// Program trailing bytes of 0xFF to prevent misinterpretation of data.
unsafe {
let cache_ptr: *const u8 = &BACKUP_SPACE[offset] as *const u8;
let payload_ptr: *const u8 = &(*payload)[0] as *const u8;
BACKUP_SPACE[offset..(offset + payload.len())].copy_from_slice(payload);
// This part program extra trailing termination bytes (4 0xFF s)
// However, if the remaining space is too small, there is also no need to program these bytes
// Bytes that are too short will not be picked up by the iterator in sfkv.
// If the data is programmed back to the exact same spot,
// then we can assert that no k-v pair were ditched
// Then there is no concern of accidentally interpreting unused flash memory as malformatted.
if (cache_ptr != payload_ptr)
&& (offset + payload.len() + 4 <= FLASH_SECTOR_SIZE)
&& self.process_type == SFKVProcessType::Data
{
BACKUP_SPACE[(offset + payload.len())..(offset + payload.len() + 4)]
.copy_from_slice(&[0xFF; 4]);
}
}
self.advance_state();
Ok(())
}
fn backup_space(&self) -> &'static mut [u8] {
unsafe { &mut BACKUP_SPACE }
}
}
impl FakeFlashManager {
pub fn new() -> Self {
Self {
process_type: SFKVProcessType::Length,
}
}
pub fn advance_state(&mut self) {
self.process_type = match self.process_type {
SFKVProcessType::Length => SFKVProcessType::Type,
SFKVProcessType::Type => SFKVProcessType::Space,
SFKVProcessType::Space => SFKVProcessType::Data,
SFKVProcessType::Data => SFKVProcessType::Length,
};
}
pub fn reset_state(&mut self) {
self.process_type = SFKVProcessType::Length;
}
}
pub type FlashStore = Store<FakeFlashManager>;
fn init_flash_cache(flash: FLASH) -> Flash {
let flash = Flash::new(flash);
// Initialize backup space
// Now backup space acts like a cache,
// sfkv will perform in-place operation in cache
// flash will only be updated after invoking `save()`
unsafe {
BACKUP_SPACE.copy_from_slice(flash.read(FLASH_SECTOR_OFFSET, FLASH_SECTOR_SIZE));
}
flash
}
fn init_flash_store() -> FlashStore {
let ffm = FakeFlashManager::new();
let mut store = FlashStore::new(ffm);
// just try to read the store
match store.get_bytes_used() {
Ok(_) => {}
Err(e) => {
error!("corrupt store, erasing. error: {:?}", e);
let _ = store
.erase()
.map_err(|e| error!("flash erase failed: {:?}", e));
}
}
store
}
pub fn init_flash(flash: FLASH) -> (Flash, FlashStore) {
let flash = init_flash_cache(flash);
let store = init_flash_store();
(flash, store)
}
pub fn update_flash(flash: &mut Flash, store: &FlashStore) -> Result<(), FlashError> {
flash.erase_sector(FLASH_BANK, FLASH_SECTOR)?;
let flash_size = store.get_bytes_used().unwrap();
let save_size = if (flash_size % 0x20) == 0 {
flash_size
} else {
flash_size + 0x20 - (flash_size % 0x20)
};
unsafe {
flash
.program(FLASH_SECTOR_OFFSET, &BACKUP_SPACE[..save_size])
.map(|_| ())
}
}