From 2d09805b42f6d224574f3f580f71ebc32ba3263b Mon Sep 17 00:00:00 2001 From: whitequark Date: Thu, 10 Dec 2020 18:19:35 +0100 Subject: [PATCH] start with config.rs from artiq's libboard_misoc --- src/lib.rs | 313 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 src/lib.rs diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..7ed368e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,313 @@ +use core::{str, fmt}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Error { + AlreadyLocked, + SpaceExhausted, + Truncated { offset: usize }, + InvalidSize { offset: usize, size: usize }, + MissingSeparator { offset: usize }, + Utf8Error(str::Utf8Error), + NoFlash, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &Error::AlreadyLocked => + write!(f, "attempt at reentrant access"), + &Error::SpaceExhausted => + write!(f, "space exhausted"), + &Error::Truncated { offset }=> + write!(f, "truncated record at offset {}", offset), + &Error::InvalidSize { offset, size } => + write!(f, "invalid record size {} at offset {}", size, offset), + &Error::MissingSeparator { offset } => + write!(f, "missing separator at offset {}", offset), + &Error::Utf8Error(err) => + write!(f, "{}", err), + &Error::NoFlash => + write!(f, "flash memory is not present"), + } + } +} + +#[cfg(has_spiflash)] +mod imp { + use core::str; + use byteorder::{ByteOrder, BigEndian}; + use cache; + use spiflash; + use super::Error; + use core::fmt; + use core::fmt::Write; + + struct FmtWrapper<'a> { + buf: &'a mut [u8], + offset: usize, + } + + impl<'a> FmtWrapper<'a> { + fn new(buf: &'a mut [u8]) -> Self { + FmtWrapper { + buf: buf, + offset: 0, + } + } + + fn contents(&self) -> &[u8] { + &self.buf[..self.offset] + } + } + + impl<'a> fmt::Write for FmtWrapper<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { + let bytes = s.as_bytes(); + let remainder = &mut self.buf[self.offset..]; + let remainder = &mut remainder[..bytes.len()]; + remainder.copy_from_slice(bytes); + self.offset += bytes.len(); + Ok(()) + } + } + + // One flash sector immediately before the firmware. + const ADDR: usize = ::mem::FLASH_BOOT_ADDRESS - spiflash::SECTOR_SIZE; + const SIZE: usize = spiflash::SECTOR_SIZE; + + mod lock { + use core::slice; + use core::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; + use super::Error; + + static LOCKED: AtomicUsize = ATOMIC_USIZE_INIT; + + pub struct Lock; + + impl Lock { + pub fn take() -> Result { + if LOCKED.swap(1, Ordering::SeqCst) != 0 { + Err(Error::AlreadyLocked) + } else { + Ok(Lock) + } + } + + pub fn data(&self) -> &'static [u8] { + unsafe { slice::from_raw_parts(super::ADDR as *const u8, super::SIZE) } + } + } + + impl Drop for Lock { + fn drop(&mut self) { + LOCKED.store(0, Ordering::SeqCst) + } + } + } + + use self::lock::Lock; + + #[derive(Clone)] + struct Iter<'a> { + data: &'a [u8], + offset: usize + } + + impl<'a> Iter<'a> { + fn new(data: &'a [u8]) -> Iter<'a> { + Iter { data: data, offset: 0 } + } + } + + impl<'a> Iterator for Iter<'a> { + type Item = Result<(&'a [u8], &'a [u8]), Error>; + + fn next(&mut self) -> Option { + let data = &self.data[self.offset..]; + + if data.len() < 4 { + // error!("offset {}: truncated record", self.offset); + return Some(Err(Error::Truncated { offset: self.offset })) + } + + let record_size = BigEndian::read_u32(data) as usize; + if record_size == !0 /* all ones; erased flash */ { + return None + } else if record_size < 4 || record_size > data.len() { + return Some(Err(Error::InvalidSize { offset: self.offset, size: record_size })) + } + + let record_body = &data[4..record_size]; + match record_body.iter().position(|&x| x == 0) { + None => { + return Some(Err(Error::MissingSeparator { offset: self.offset })) + } + Some(pos) => { + self.offset += record_size; + + let (key, zero_and_value) = record_body.split_at(pos); + Some(Ok((key, &zero_and_value[1..]))) + } + } + } + } + + pub fn read) -> R, R>(key: &str, f: F) -> R { + f(Lock::take().and_then(|lock| { + let mut iter = Iter::new(lock.data()); + let mut value = &[][..]; + while let Some(result) = iter.next() { + let (record_key, record_value) = result?; + if key.as_bytes() == record_key { + // last write wins + value = record_value + } + } + Ok(value) + })) + } + + pub fn read_str) -> R, R>(key: &str, f: F) -> R { + read(key, |result| { + f(result.and_then(|value| str::from_utf8(value).map_err(Error::Utf8Error))) + }) + } + + unsafe fn append_at(data: &[u8], mut offset: usize, + key: &[u8], value: &[u8]) -> Result { + let record_size = 4 + key.len() + 1 + value.len(); + if offset + record_size > data.len() { + return Err(Error::SpaceExhausted) + } + + let mut record_size_bytes = [0u8; 4]; + BigEndian::write_u32(&mut record_size_bytes[..], record_size as u32); + + { + let mut write = |payload| { + spiflash::write(data.as_ptr().offset(offset as isize) as usize, payload); + offset += payload.len(); + }; + + write(&record_size_bytes[..]); + write(key); + write(&[0]); + write(value); + cache::flush_l2_cache(); + } + + Ok(offset) + } + + fn compact() -> Result<(), Error> { + let lock = Lock::take()?; + let data = lock.data(); + + static mut OLD_DATA: [u8; SIZE] = [0; SIZE]; + let old_data = unsafe { + OLD_DATA.copy_from_slice(data); + &OLD_DATA[..] + }; + + unsafe { spiflash::erase_sector(data.as_ptr() as usize) }; + + // This is worst-case quadratic, but we're limited by a small SPI flash sector size, + // so it does not really matter. + let mut offset = 0; + let mut iter = Iter::new(old_data); + 'iter: while let Some(result) = iter.next() { + let (key, mut value) = result?; + if value.is_empty() { + // This is a removed entry, ignore it. + continue + } + + let mut next_iter = iter.clone(); + while let Some(next_result) = next_iter.next() { + let (next_key, _) = next_result?; + if key == next_key { + // There's another entry that overwrites this one, ignore this one. + continue 'iter + } + } + offset = unsafe { append_at(data, offset, key, value)? }; + } + + Ok(()) + } + + fn append(key: &str, value: &[u8]) -> Result<(), Error> { + let lock = Lock::take()?; + let data = lock.data(); + + let free_offset = { + let mut iter = Iter::new(data); + while let Some(result) = iter.next() { + let _ = result?; + } + iter.offset + }; + + unsafe { append_at(data, free_offset, key.as_bytes(), value)? }; + + Ok(()) + } + + pub fn write(key: &str, value: &[u8]) -> Result<(), Error> { + match append(key, value) { + Err(Error::SpaceExhausted) => { + compact()?; + append(key, value) + } + res => res + } + } + + pub fn write_int(key: &str, value: u32) -> Result<(), Error> { + let mut buf = [0; 16]; + let mut wrapper = FmtWrapper::new(&mut buf); + write!(&mut wrapper, "{}", value).unwrap(); + write(key, wrapper.contents()) + } + + pub fn remove(key: &str) -> Result<(), Error> { + write(key, &[]) + } + + pub fn erase() -> Result<(), Error> { + let lock = Lock::take()?; + let data = lock.data(); + + unsafe { spiflash::erase_sector(data.as_ptr() as usize) }; + cache::flush_l2_cache(); + + Ok(()) + } +} + +#[cfg(not(has_spiflash))] +mod imp { + use super::Error; + + pub fn read) -> R, R>(_key: &str, f: F) -> R { + f(Err(Error::NoFlash)) + } + + pub fn read_str) -> R, R>(_key: &str, f: F) -> R { + f(Err(Error::NoFlash)) + } + + pub fn write(_key: &str, _value: &[u8]) -> Result<(), Error> { + Err(Error::NoFlash) + } + + pub fn remove(_key: &str) -> Result<(), Error> { + Err(Error::NoFlash) + } + + pub fn erase() -> Result<(), Error> { + Err(Error::NoFlash) + } +} + +pub use self::imp::*;