sfkv: init
This commit is contained in:
parent
2e86838a22
commit
d5f9480645
|
@ -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<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(|_| ())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue