From d5f9480645d14b9c54c128ca081103c8a8988823 Mon Sep 17 00:00:00 2001 From: occheung Date: Fri, 18 Dec 2020 17:39:45 +0800 Subject: [PATCH] sfkv: init --- src/flash_store.rs | 171 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/flash_store.rs diff --git a/src/flash_store.rs b/src/flash_store.rs new file mode 100644 index 0000000..89c097a --- /dev/null +++ b/src/flash_store.rs @@ -0,0 +1,171 @@ +use crate::flash::Flash; +use crate::flash::Error as FlashError; + +use stm32h7xx_hal::pac::FLASH; +use sfkv::{ StoreBackend, Store }; + +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; + +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(|_| ()) + } +} \ No newline at end of file