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