Initial commit
This commit is contained in:
455
src/embedded_flash/buffer.rs
Normal file
455
src/embedded_flash/buffer.rs
Normal file
@@ -0,0 +1,455 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{Index, Storage, StorageError, StorageResult};
|
||||
|
||||
pub struct BufferStorage {
|
||||
storage: Box<[u8]>,
|
||||
options: BufferOptions,
|
||||
word_writes: Box<[usize]>,
|
||||
page_erases: Box<[usize]>,
|
||||
snapshot: Snapshot,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct BufferOptions {
|
||||
/// Size of a word in bytes.
|
||||
pub word_size: usize,
|
||||
|
||||
/// Size of a page in bytes.
|
||||
pub page_size: usize,
|
||||
|
||||
/// How many times a word can be written between page erasures
|
||||
pub max_word_writes: usize,
|
||||
|
||||
/// How many times a page can be erased.
|
||||
pub max_page_erases: usize,
|
||||
|
||||
/// Bits cannot be written from 0 to 1.
|
||||
pub strict_write: bool,
|
||||
}
|
||||
|
||||
impl BufferStorage {
|
||||
/// Creates a fake embedded flash using a buffer.
|
||||
///
|
||||
/// This implementation checks that no words are written more than `max_word_writes` between
|
||||
/// page erasures and than no pages are erased more than `max_page_erases`. If `strict_write` is
|
||||
/// true, it also checks that no bits are written from 0 to 1. It also permits to take snapshots
|
||||
/// of the storage during write and erase operations (although words would still be written or
|
||||
/// erased completely).
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// The following preconditions must hold:
|
||||
/// - `options.word_size` must be a power of two.
|
||||
/// - `options.page_size` must be a power of two.
|
||||
/// - `options.page_size` must be word-aligned.
|
||||
/// - `storage.len()` must be page-aligned.
|
||||
pub fn new(storage: Box<[u8]>, options: BufferOptions) -> BufferStorage {
|
||||
assert!(options.word_size.is_power_of_two());
|
||||
assert!(options.page_size.is_power_of_two());
|
||||
let num_words = storage.len() / options.word_size;
|
||||
let num_pages = storage.len() / options.page_size;
|
||||
let buffer = BufferStorage {
|
||||
storage,
|
||||
options,
|
||||
word_writes: vec![0; num_words].into_boxed_slice(),
|
||||
page_erases: vec![0; num_pages].into_boxed_slice(),
|
||||
snapshot: Snapshot::Ready,
|
||||
};
|
||||
assert!(buffer.is_word_aligned(buffer.options.page_size));
|
||||
assert!(buffer.is_page_aligned(buffer.storage.len()));
|
||||
buffer
|
||||
}
|
||||
|
||||
/// Takes a snapshot of the storage after a given amount of word operations.
|
||||
///
|
||||
/// Each time a word is written or erased, the delay is decremented if positive. Otherwise, a
|
||||
/// snapshot is taken before the operation is executed.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a snapshot has been armed and not examined.
|
||||
pub fn arm_snapshot(&mut self, delay: usize) {
|
||||
self.snapshot.arm(delay);
|
||||
}
|
||||
|
||||
/// Unarms and returns the snapshot or the delay remaining.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a snapshot was not armed.
|
||||
pub fn get_snapshot(&mut self) -> Result<Box<[u8]>, usize> {
|
||||
self.snapshot.get()
|
||||
}
|
||||
|
||||
/// Takes a snapshot of the storage.
|
||||
pub fn take_snapshot(&self) -> Box<[u8]> {
|
||||
self.storage.clone()
|
||||
}
|
||||
|
||||
/// Returns the storage.
|
||||
pub fn get_storage(self) -> Box<[u8]> {
|
||||
self.storage
|
||||
}
|
||||
|
||||
fn is_word_aligned(&self, x: usize) -> bool {
|
||||
x & (self.options.word_size - 1) == 0
|
||||
}
|
||||
|
||||
fn is_page_aligned(&self, x: usize) -> bool {
|
||||
x & (self.options.page_size - 1) == 0
|
||||
}
|
||||
|
||||
/// Writes a slice to the storage.
|
||||
///
|
||||
/// The slice `value` is written to `index`. The `erase` boolean specifies whether this is an
|
||||
/// erase operation or a write operation which matters for the checks and updating the shadow
|
||||
/// storage. This also takes a snapshot of the storage if a snapshot was armed and the delay has
|
||||
/// elapsed.
|
||||
///
|
||||
/// The following preconditions should hold:
|
||||
/// - `index` is word-aligned.
|
||||
/// - `value.len()` is word-aligned.
|
||||
///
|
||||
/// The following checks are performed:
|
||||
/// - The region of length `value.len()` starting at `index` fits in a storage page.
|
||||
/// - A word is not written more than `max_word_writes`.
|
||||
/// - A page is not erased more than `max_page_erases`.
|
||||
/// - The new word only switches 1s to 0s (only if `strict_write` is set).
|
||||
fn update_storage(&mut self, index: Index, value: &[u8], erase: bool) -> StorageResult<()> {
|
||||
debug_assert!(self.is_word_aligned(index.byte) && self.is_word_aligned(value.len()));
|
||||
let dst = index.range(value.len(), self)?.step_by(self.word_size());
|
||||
let src = value.chunks(self.word_size());
|
||||
// Check and update page shadow.
|
||||
if erase {
|
||||
let page = index.page;
|
||||
assert!(self.page_erases[page] < self.max_page_erases());
|
||||
self.page_erases[page] += 1;
|
||||
}
|
||||
for (byte, val) in dst.zip(src) {
|
||||
let range = byte..byte + self.word_size();
|
||||
// The driver doesn't write identical words.
|
||||
if &self.storage[range.clone()] == val {
|
||||
continue;
|
||||
}
|
||||
// Check and update word shadow.
|
||||
let word = byte / self.word_size();
|
||||
if erase {
|
||||
self.word_writes[word] = 0;
|
||||
} else {
|
||||
assert!(self.word_writes[word] < self.max_word_writes());
|
||||
self.word_writes[word] += 1;
|
||||
}
|
||||
// Check strict write.
|
||||
if !erase && self.options.strict_write {
|
||||
for (byte, &val) in range.clone().zip(val) {
|
||||
assert_eq!(self.storage[byte] & val, val);
|
||||
}
|
||||
}
|
||||
// Take snapshot if armed and delay expired.
|
||||
self.snapshot.take(&self.storage);
|
||||
// Write storage
|
||||
self.storage[range].copy_from_slice(val);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Storage for BufferStorage {
|
||||
fn word_size(&self) -> usize {
|
||||
self.options.word_size
|
||||
}
|
||||
|
||||
fn page_size(&self) -> usize {
|
||||
self.options.page_size
|
||||
}
|
||||
|
||||
fn num_pages(&self) -> usize {
|
||||
self.storage.len() / self.options.page_size
|
||||
}
|
||||
|
||||
fn max_word_writes(&self) -> usize {
|
||||
self.options.max_word_writes
|
||||
}
|
||||
|
||||
fn max_page_erases(&self) -> usize {
|
||||
self.options.max_page_erases
|
||||
}
|
||||
|
||||
fn read_slice(&self, index: Index, length: usize) -> StorageResult<&[u8]> {
|
||||
Ok(&self.storage[index.range(length, self)?])
|
||||
}
|
||||
|
||||
fn write_slice(&mut self, index: Index, value: &[u8]) -> StorageResult<()> {
|
||||
if !self.is_word_aligned(index.byte) || !self.is_word_aligned(value.len()) {
|
||||
return Err(StorageError::NotAligned);
|
||||
}
|
||||
self.update_storage(index, value, false)
|
||||
}
|
||||
|
||||
fn erase_page(&mut self, page: usize) -> StorageResult<()> {
|
||||
let index = Index { page, byte: 0 };
|
||||
let value = vec![0xff; self.page_size()];
|
||||
self.update_storage(index, &value, true)
|
||||
}
|
||||
}
|
||||
|
||||
// Controls when a snapshot of the storage is taken.
|
||||
//
|
||||
// This can be used to simulate power-offs while the device is writing to the storage or erasing a
|
||||
// page in the storage.
|
||||
enum Snapshot {
|
||||
// Mutable word operations have normal behavior.
|
||||
Ready,
|
||||
// If the delay is positive, mutable word operations decrement it. If the count is zero, mutable
|
||||
// word operations take a snapshot of the storage.
|
||||
Armed { delay: usize },
|
||||
// Mutable word operations have normal behavior.
|
||||
Taken { storage: Box<[u8]> },
|
||||
}
|
||||
|
||||
impl Snapshot {
|
||||
fn arm(&mut self, delay: usize) {
|
||||
match self {
|
||||
Snapshot::Ready => *self = Snapshot::Armed { delay },
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&mut self) -> Result<Box<[u8]>, usize> {
|
||||
let mut snapshot = Snapshot::Ready;
|
||||
std::mem::swap(self, &mut snapshot);
|
||||
match snapshot {
|
||||
Snapshot::Armed { delay } => Err(delay),
|
||||
Snapshot::Taken { storage } => Ok(storage),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn take(&mut self, storage: &[u8]) {
|
||||
if let Snapshot::Armed { delay } = self {
|
||||
if *delay == 0 {
|
||||
let storage = storage.to_vec().into_boxed_slice();
|
||||
*self = Snapshot::Taken { storage };
|
||||
} else {
|
||||
*delay -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
const NUM_PAGES: usize = 2;
|
||||
const OPTIONS: BufferOptions = BufferOptions {
|
||||
word_size: 4,
|
||||
page_size: 16,
|
||||
max_word_writes: 2,
|
||||
max_page_erases: 3,
|
||||
strict_write: true,
|
||||
};
|
||||
// Those words are decreasing bit patterns. Bits are only changed from 1 to 0 and at last one
|
||||
// bit is changed.
|
||||
const BLANK_WORD: &[u8] = &[0xff, 0xff, 0xff, 0xff];
|
||||
const FIRST_WORD: &[u8] = &[0xee, 0xdd, 0xbb, 0x77];
|
||||
const SECOND_WORD: &[u8] = &[0xca, 0xc9, 0xa9, 0x65];
|
||||
const THIRD_WORD: &[u8] = &[0x88, 0x88, 0x88, 0x44];
|
||||
|
||||
fn new_storage() -> Box<[u8]> {
|
||||
vec![0xff; NUM_PAGES * OPTIONS.page_size].into_boxed_slice()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn words_are_decreasing() {
|
||||
fn assert_is_decreasing(prev: &[u8], next: &[u8]) {
|
||||
for (&prev, &next) in prev.iter().zip(next.iter()) {
|
||||
assert_eq!(prev & next, next);
|
||||
assert!(prev != next);
|
||||
}
|
||||
}
|
||||
assert_is_decreasing(BLANK_WORD, FIRST_WORD);
|
||||
assert_is_decreasing(FIRST_WORD, SECOND_WORD);
|
||||
assert_is_decreasing(SECOND_WORD, THIRD_WORD);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn options_ok() {
|
||||
let buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
assert_eq!(buffer.word_size(), OPTIONS.word_size);
|
||||
assert_eq!(buffer.page_size(), OPTIONS.page_size);
|
||||
assert_eq!(buffer.num_pages(), NUM_PAGES);
|
||||
assert_eq!(buffer.max_word_writes(), OPTIONS.max_word_writes);
|
||||
assert_eq!(buffer.max_page_erases(), OPTIONS.max_page_erases);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_write_ok() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 0 };
|
||||
let next_index = Index { page: 0, byte: 4 };
|
||||
assert_eq!(buffer.read_slice(index, 4).unwrap(), BLANK_WORD);
|
||||
buffer.write_slice(index, FIRST_WORD).unwrap();
|
||||
assert_eq!(buffer.read_slice(index, 4).unwrap(), FIRST_WORD);
|
||||
assert_eq!(buffer.read_slice(next_index, 4).unwrap(), BLANK_WORD);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn erase_ok() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 0 };
|
||||
let other_index = Index { page: 1, byte: 0 };
|
||||
buffer.write_slice(index, FIRST_WORD).unwrap();
|
||||
buffer.write_slice(other_index, FIRST_WORD).unwrap();
|
||||
assert_eq!(buffer.read_slice(index, 4).unwrap(), FIRST_WORD);
|
||||
assert_eq!(buffer.read_slice(other_index, 4).unwrap(), FIRST_WORD);
|
||||
buffer.erase_page(0).unwrap();
|
||||
assert_eq!(buffer.read_slice(index, 4).unwrap(), BLANK_WORD);
|
||||
assert_eq!(buffer.read_slice(other_index, 4).unwrap(), FIRST_WORD);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_range() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 12 };
|
||||
let half_index = Index { page: 0, byte: 14 };
|
||||
let over_index = Index { page: 0, byte: 16 };
|
||||
let bad_page = Index { page: 2, byte: 0 };
|
||||
|
||||
// Reading a word in the storage is ok.
|
||||
assert!(buffer.read_slice(index, 4).is_ok());
|
||||
// Reading a half-word in the storage is ok.
|
||||
assert!(buffer.read_slice(half_index, 2).is_ok());
|
||||
// Reading even a single byte outside a page is not ok.
|
||||
assert!(buffer.read_slice(over_index, 1).is_err());
|
||||
// But reading an empty slice just after a page is ok.
|
||||
assert!(buffer.read_slice(over_index, 0).is_ok());
|
||||
// Reading even an empty slice outside the storage is not ok.
|
||||
assert!(buffer.read_slice(bad_page, 0).is_err());
|
||||
|
||||
// Writing a word in the storage is ok.
|
||||
assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
|
||||
// Writing an unaligned word is not ok.
|
||||
assert!(buffer.write_slice(half_index, FIRST_WORD).is_err());
|
||||
// Writing a word outside a page is not ok.
|
||||
assert!(buffer.write_slice(over_index, FIRST_WORD).is_err());
|
||||
// But writing an empty slice just after a page is ok.
|
||||
assert!(buffer.write_slice(over_index, &[]).is_ok());
|
||||
// Writing even an empty slice outside the storage is not ok.
|
||||
assert!(buffer.write_slice(bad_page, &[]).is_err());
|
||||
|
||||
// Only pages in the storage can be erased.
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
assert!(buffer.erase_page(2).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_twice_ok() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 4 };
|
||||
assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
|
||||
assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_twice_and_once_ok() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 0 };
|
||||
let next_index = Index { page: 0, byte: 4 };
|
||||
assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
|
||||
assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
|
||||
assert!(buffer.write_slice(next_index, THIRD_WORD).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn write_three_times_panics() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 4 };
|
||||
assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
|
||||
assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
|
||||
let _ = buffer.write_slice(index, THIRD_WORD);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_twice_then_once_ok() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 0 };
|
||||
assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
|
||||
assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
assert!(buffer.write_slice(index, FIRST_WORD).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn erase_three_times_ok() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn erase_three_times_and_once_ok() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
assert!(buffer.erase_page(1).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn erase_four_times_panics() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
assert!(buffer.erase_page(0).is_ok());
|
||||
let _ = buffer.erase_page(0).is_ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn switch_zero_to_one_panics() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 0 };
|
||||
assert!(buffer.write_slice(index, SECOND_WORD).is_ok());
|
||||
let _ = buffer.write_slice(index, FIRST_WORD);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_storage_ok() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 4 };
|
||||
buffer.write_slice(index, FIRST_WORD).unwrap();
|
||||
let storage = buffer.get_storage();
|
||||
assert_eq!(&storage[..4], BLANK_WORD);
|
||||
assert_eq!(&storage[4..8], FIRST_WORD);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn snapshot_ok() {
|
||||
let mut buffer = BufferStorage::new(new_storage(), OPTIONS);
|
||||
let index = Index { page: 0, byte: 0 };
|
||||
let value = [FIRST_WORD, SECOND_WORD].concat();
|
||||
buffer.arm_snapshot(1);
|
||||
buffer.write_slice(index, &value).unwrap();
|
||||
let storage = buffer.get_snapshot().unwrap();
|
||||
assert_eq!(&storage[..8], &[FIRST_WORD, BLANK_WORD].concat()[..]);
|
||||
let storage = buffer.take_snapshot();
|
||||
assert_eq!(&storage[..8], &value[..]);
|
||||
}
|
||||
}
|
||||
25
src/embedded_flash/mod.rs
Normal file
25
src/embedded_flash/mod.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod buffer;
|
||||
mod storage;
|
||||
mod store;
|
||||
mod syscall;
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub use self::buffer::{BufferOptions, BufferStorage};
|
||||
pub use self::storage::{Index, Storage, StorageError, StorageResult};
|
||||
pub use self::store::{Store, StoreConfig, StoreEntry, StoreError, StoreIndex};
|
||||
pub use self::syscall::SyscallStorage;
|
||||
107
src/embedded_flash/storage.rs
Normal file
107
src/embedded_flash/storage.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct Index {
|
||||
pub page: usize,
|
||||
pub byte: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum StorageError {
|
||||
BadFlash,
|
||||
NotAligned,
|
||||
OutOfBounds,
|
||||
KernelError { code: isize },
|
||||
}
|
||||
|
||||
pub type StorageResult<T> = Result<T, StorageError>;
|
||||
|
||||
/// Abstraction for embedded flash storage.
|
||||
pub trait Storage {
|
||||
/// Returns the size of a word in bytes.
|
||||
fn word_size(&self) -> usize;
|
||||
|
||||
/// Returns the size of a page in bytes.
|
||||
fn page_size(&self) -> usize;
|
||||
|
||||
/// Returns the number of pages in the storage.
|
||||
fn num_pages(&self) -> usize;
|
||||
|
||||
/// Returns how many times a word can be written between page erasures.
|
||||
fn max_word_writes(&self) -> usize;
|
||||
|
||||
/// Returns how many times a page can be erased in the lifetime of the flash.
|
||||
fn max_page_erases(&self) -> usize;
|
||||
|
||||
/// Reads a slice from the storage.
|
||||
///
|
||||
/// The slice does not need to be word-aligned.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// The `index` must designate `length` bytes in the storage.
|
||||
fn read_slice(&self, index: Index, length: usize) -> StorageResult<&[u8]>;
|
||||
|
||||
/// Writes a word-aligned slice to the storage.
|
||||
///
|
||||
/// The written words should not have been written too many times since last page erasure.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// The following preconditions must hold:
|
||||
/// - `index` must be word-aligned.
|
||||
/// - `value.len()` must be a multiple of the word size.
|
||||
/// - `index` must designate `value.len()` bytes in the storage.
|
||||
/// - `value` must be in memory until [read-only allow][tock_1274] is resolved.
|
||||
///
|
||||
/// [tock_1274]: https://github.com/tock/tock/issues/1274.
|
||||
fn write_slice(&mut self, index: Index, value: &[u8]) -> StorageResult<()>;
|
||||
|
||||
/// Erases a page of the storage.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// The `page` must be in the storage.
|
||||
fn erase_page(&mut self, page: usize) -> StorageResult<()>;
|
||||
}
|
||||
|
||||
impl Index {
|
||||
/// Returns whether a slice fits in a storage page.
|
||||
fn is_valid(self, length: usize, storage: &impl Storage) -> bool {
|
||||
self.page < storage.num_pages()
|
||||
&& storage
|
||||
.page_size()
|
||||
.checked_sub(length)
|
||||
.map(|limit| self.byte <= limit)
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Returns the range of a valid slice.
|
||||
///
|
||||
/// The range starts at `self` with `length` bytes.
|
||||
pub fn range(
|
||||
self,
|
||||
length: usize,
|
||||
storage: &impl Storage,
|
||||
) -> StorageResult<core::ops::Range<usize>> {
|
||||
if self.is_valid(length, storage) {
|
||||
let start = self.page * storage.page_size() + self.byte;
|
||||
Ok(start..start + length)
|
||||
} else {
|
||||
Err(StorageError::OutOfBounds)
|
||||
}
|
||||
}
|
||||
}
|
||||
172
src/embedded_flash/store/bitfield.rs
Normal file
172
src/embedded_flash/store/bitfield.rs
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/// Defines a consecutive sequence of bits.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct BitRange {
|
||||
/// The first bit of the sequence.
|
||||
pub start: usize,
|
||||
|
||||
/// The length in bits of the sequence.
|
||||
pub length: usize,
|
||||
}
|
||||
|
||||
impl BitRange {
|
||||
/// Returns the first bit following a bit range.
|
||||
pub fn end(self) -> usize {
|
||||
self.start + self.length
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines a consecutive sequence of bytes.
|
||||
///
|
||||
/// The bits in those bytes are ignored which essentially creates a gap in a sequence of bits. The
|
||||
/// gap is necessarily at byte boundaries. This is used to ignore the user data in an entry
|
||||
/// essentially providing a view of the entry information (header and footer).
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ByteGap {
|
||||
pub start: usize,
|
||||
pub length: usize,
|
||||
}
|
||||
|
||||
/// Empty gap. All bits count.
|
||||
pub const NO_GAP: ByteGap = ByteGap {
|
||||
start: 0,
|
||||
length: 0,
|
||||
};
|
||||
|
||||
impl ByteGap {
|
||||
/// Translates a bit to skip the gap.
|
||||
fn shift(self, bit: usize) -> usize {
|
||||
if bit < 8 * self.start {
|
||||
bit
|
||||
} else {
|
||||
bit + 8 * self.length
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether a bit is set in a sequence of bits.
|
||||
///
|
||||
/// The sequence of bits is little-endian (both for bytes and bits) and defined by the bits that
|
||||
/// are in `data` but not in `gap`.
|
||||
pub fn is_zero(bit: usize, data: &[u8], gap: ByteGap) -> bool {
|
||||
let bit = gap.shift(bit);
|
||||
debug_assert!(bit < 8 * data.len());
|
||||
data[bit / 8] & (1 << (bit % 8)) == 0
|
||||
}
|
||||
|
||||
/// Sets a bit to zero in a sequence of bits.
|
||||
///
|
||||
/// The sequence of bits is little-endian (both for bytes and bits) and defined by the bits that
|
||||
/// are in `data` but not in `gap`.
|
||||
pub fn set_zero(bit: usize, data: &mut [u8], gap: ByteGap) {
|
||||
let bit = gap.shift(bit);
|
||||
debug_assert!(bit < 8 * data.len());
|
||||
data[bit / 8] &= !(1 << (bit % 8));
|
||||
}
|
||||
|
||||
/// Returns a little-endian value in a sequence of bits.
|
||||
///
|
||||
/// The sequence of bits is little-endian (both for bytes and bits) and defined by the bits that
|
||||
/// are in `data` but not in `gap`. The range of bits where the value is stored in defined by
|
||||
/// `range`. The value must fit in a `usize`.
|
||||
pub fn get_range(range: BitRange, data: &[u8], gap: ByteGap) -> usize {
|
||||
debug_assert!(range.length <= 8 * core::mem::size_of::<usize>());
|
||||
let mut result = 0;
|
||||
for i in 0..range.length {
|
||||
if !is_zero(range.start + i, data, gap) {
|
||||
result |= 1 << i;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Sets a little-endian value in a sequence of bits.
|
||||
///
|
||||
/// The sequence of bits is little-endian (both for bytes and bits) and defined by the bits that
|
||||
/// are in `data` but not in `gap`. The range of bits where the value is stored in defined by
|
||||
/// `range`. The bits set to 1 in `value` must also be set to `1` in the sequence of bits.
|
||||
pub fn set_range(range: BitRange, data: &mut [u8], gap: ByteGap, value: usize) {
|
||||
debug_assert!(range.length == 8 * core::mem::size_of::<usize>() || value < 1 << range.length);
|
||||
for i in 0..range.length {
|
||||
if value & 1 << i == 0 {
|
||||
set_zero(range.start + i, data, gap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests the `is_zero` and `set_zero` pair of functions.
|
||||
#[test]
|
||||
fn zero_ok() {
|
||||
const GAP: ByteGap = ByteGap {
|
||||
start: 2,
|
||||
length: 1,
|
||||
};
|
||||
for i in 0..24 {
|
||||
assert!(!is_zero(i, &[0xffu8, 0xff, 0x00, 0xff] as &[u8], GAP));
|
||||
}
|
||||
// Tests reading and setting a bit. The result should have all bits set to 1 except for the bit
|
||||
// to test and the gap.
|
||||
fn test(bit: usize, result: &[u8]) {
|
||||
assert!(is_zero(bit, result, GAP));
|
||||
let mut data = vec![0xff; result.len()];
|
||||
// Set the gap bits to 0.
|
||||
for i in 0..GAP.length {
|
||||
data[GAP.start + i] = 0x00;
|
||||
}
|
||||
set_zero(bit, &mut data, GAP);
|
||||
assert_eq!(data, result);
|
||||
}
|
||||
test(0, &[0xfe, 0xff, 0x00, 0xff]);
|
||||
test(1, &[0xfd, 0xff, 0x00, 0xff]);
|
||||
test(2, &[0xfb, 0xff, 0x00, 0xff]);
|
||||
test(7, &[0x7f, 0xff, 0x00, 0xff]);
|
||||
test(8, &[0xff, 0xfe, 0x00, 0xff]);
|
||||
test(15, &[0xff, 0x7f, 0x00, 0xff]);
|
||||
test(16, &[0xff, 0xff, 0x00, 0xfe]);
|
||||
test(17, &[0xff, 0xff, 0x00, 0xfd]);
|
||||
test(23, &[0xff, 0xff, 0x00, 0x7f]);
|
||||
}
|
||||
|
||||
/// Tests the `get_range` and `set_range` pair of functions.
|
||||
#[test]
|
||||
fn range_ok() {
|
||||
// Tests reading and setting a range. The result should have all bits set to 1 except for the
|
||||
// range to test and the gap.
|
||||
fn test(start: usize, length: usize, value: usize, result: &[u8], gap: ByteGap) {
|
||||
let range = BitRange { start, length };
|
||||
assert_eq!(get_range(range, result, gap), value);
|
||||
let mut data = vec![0xff; result.len()];
|
||||
for i in 0..gap.length {
|
||||
data[gap.start + i] = 0x00;
|
||||
}
|
||||
set_range(range, &mut data, gap, value);
|
||||
assert_eq!(data, result);
|
||||
}
|
||||
test(0, 8, 42, &[42], NO_GAP);
|
||||
test(3, 12, 0b11_0101, &[0b1010_1111, 0b1000_0001], NO_GAP);
|
||||
test(0, 16, 0x1234, &[0x34, 0x12], NO_GAP);
|
||||
test(4, 16, 0x1234, &[0x4f, 0x23, 0xf1], NO_GAP);
|
||||
let mut gap = ByteGap {
|
||||
start: 1,
|
||||
length: 1,
|
||||
};
|
||||
test(3, 12, 0b11_0101, &[0b1010_1111, 0x00, 0b1000_0001], gap);
|
||||
gap.length = 2;
|
||||
test(0, 16, 0x1234, &[0x34, 0x00, 0x00, 0x12], gap);
|
||||
gap.start = 2;
|
||||
gap.length = 1;
|
||||
test(4, 16, 0x1234, &[0x4f, 0x23, 0x00, 0xf1], gap);
|
||||
}
|
||||
514
src/embedded_flash/store/format.rs
Normal file
514
src/embedded_flash/store/format.rs
Normal file
@@ -0,0 +1,514 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::super::{Index, Storage};
|
||||
use super::{bitfield, StoreConfig, StoreEntry, StoreError};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Whether a user entry is a replace entry.
|
||||
pub enum IsReplace {
|
||||
/// This is a replace entry.
|
||||
Replace,
|
||||
|
||||
/// This is an insert entry.
|
||||
Insert,
|
||||
}
|
||||
|
||||
/// Helpers to parse the store format.
|
||||
///
|
||||
/// See the store module-level documentation for information about the format.
|
||||
pub struct Format {
|
||||
pub word_size: usize,
|
||||
pub page_size: usize,
|
||||
pub num_pages: usize,
|
||||
pub max_page_erases: usize,
|
||||
pub num_tags: usize,
|
||||
|
||||
/// Whether an entry is present.
|
||||
///
|
||||
/// - 0 for entries (user entries or internal entries).
|
||||
/// - 1 for free space until the end of the page.
|
||||
present_bit: usize,
|
||||
|
||||
/// Whether an entry is deleted.
|
||||
///
|
||||
/// - 0 for deleted entries.
|
||||
/// - 1 for alive entries.
|
||||
deleted_bit: usize,
|
||||
|
||||
/// Whether an entry is internal.
|
||||
///
|
||||
/// - 0 for internal entries.
|
||||
/// - 1 for user entries.
|
||||
internal_bit: usize,
|
||||
|
||||
/// Whether a user entry is a replace entry.
|
||||
///
|
||||
/// - 0 for replace entries.
|
||||
/// - 1 for insert entries.
|
||||
replace_bit: usize,
|
||||
|
||||
/// The data length of a user entry.
|
||||
length_range: bitfield::BitRange,
|
||||
|
||||
/// The tag of a user entry.
|
||||
tag_range: bitfield::BitRange,
|
||||
|
||||
/// The page index of a replace entry.
|
||||
replace_page_range: bitfield::BitRange,
|
||||
|
||||
/// The byte index of a replace entry.
|
||||
replace_byte_range: bitfield::BitRange,
|
||||
|
||||
/// The index of the page to erase.
|
||||
///
|
||||
/// This is only present for internal entries.
|
||||
old_page_range: bitfield::BitRange,
|
||||
|
||||
/// The current erase count of the page to erase.
|
||||
///
|
||||
/// This is only present for internal entries.
|
||||
saved_erase_count_range: bitfield::BitRange,
|
||||
|
||||
/// Whether a page is initialized.
|
||||
///
|
||||
/// - 0 for initialized pages.
|
||||
/// - 1 for uninitialized pages.
|
||||
initialized_bit: usize,
|
||||
|
||||
/// The erase count of a page.
|
||||
erase_count_range: bitfield::BitRange,
|
||||
|
||||
/// Whether a page is being compacted.
|
||||
///
|
||||
/// - 0 for pages being compacted.
|
||||
/// - 1 otherwise.
|
||||
compacting_bit: usize,
|
||||
|
||||
/// The page index to which a page is being compacted.
|
||||
new_page_range: bitfield::BitRange,
|
||||
}
|
||||
|
||||
impl Format {
|
||||
/// Returns a helper to parse the store format for a given storage and config.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `None` if any of the following conditions does not hold:
|
||||
/// - The word size must be a power of two.
|
||||
/// - The page size must be a power of two.
|
||||
/// - There should be at least 2 pages in the storage.
|
||||
/// - It should be possible to write a word at least twice.
|
||||
/// - It should be possible to erase a page at least once.
|
||||
/// - There should be at least 1 tag.
|
||||
pub fn new<S: Storage, C: StoreConfig>(storage: &S, config: &C) -> Option<Format> {
|
||||
let word_size = storage.word_size();
|
||||
let page_size = storage.page_size();
|
||||
let num_pages = storage.num_pages();
|
||||
let max_word_writes = storage.max_word_writes();
|
||||
let max_page_erases = storage.max_page_erases();
|
||||
let num_tags = config.num_tags();
|
||||
if !(word_size.is_power_of_two()
|
||||
&& page_size.is_power_of_two()
|
||||
&& num_pages > 1
|
||||
&& max_word_writes >= 2
|
||||
&& max_page_erases > 0
|
||||
&& num_tags > 0)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
// Compute how many bits we need to store the fields.
|
||||
let page_bits = num_bits(num_pages);
|
||||
let byte_bits = num_bits(page_size);
|
||||
let tag_bits = num_bits(num_tags);
|
||||
let erase_bits = num_bits(max_page_erases + 1);
|
||||
// Compute the bit position of the fields.
|
||||
let present_bit = 0;
|
||||
let deleted_bit = present_bit + 1;
|
||||
let internal_bit = deleted_bit + 1;
|
||||
let replace_bit = internal_bit + 1;
|
||||
let length_range = bitfield::BitRange {
|
||||
start: replace_bit + 1,
|
||||
length: byte_bits,
|
||||
};
|
||||
let tag_range = bitfield::BitRange {
|
||||
start: length_range.end(),
|
||||
length: tag_bits,
|
||||
};
|
||||
let replace_page_range = bitfield::BitRange {
|
||||
start: tag_range.end(),
|
||||
length: page_bits,
|
||||
};
|
||||
let replace_byte_range = bitfield::BitRange {
|
||||
start: replace_page_range.end(),
|
||||
length: byte_bits,
|
||||
};
|
||||
let old_page_range = bitfield::BitRange {
|
||||
start: internal_bit + 1,
|
||||
length: page_bits,
|
||||
};
|
||||
let saved_erase_count_range = bitfield::BitRange {
|
||||
start: old_page_range.end(),
|
||||
length: erase_bits,
|
||||
};
|
||||
let initialized_bit = 0;
|
||||
let erase_count_range = bitfield::BitRange {
|
||||
start: initialized_bit + 1,
|
||||
length: erase_bits,
|
||||
};
|
||||
let compacting_bit = erase_count_range.end();
|
||||
let new_page_range = bitfield::BitRange {
|
||||
start: compacting_bit + 1,
|
||||
length: page_bits,
|
||||
};
|
||||
let format = Format {
|
||||
word_size,
|
||||
page_size,
|
||||
num_pages,
|
||||
max_page_erases,
|
||||
num_tags,
|
||||
present_bit,
|
||||
deleted_bit,
|
||||
internal_bit,
|
||||
replace_bit,
|
||||
length_range,
|
||||
tag_range,
|
||||
replace_page_range,
|
||||
replace_byte_range,
|
||||
old_page_range,
|
||||
saved_erase_count_range,
|
||||
initialized_bit,
|
||||
erase_count_range,
|
||||
compacting_bit,
|
||||
new_page_range,
|
||||
};
|
||||
// Make sure all the following conditions hold:
|
||||
// - The page header is one word.
|
||||
// - The internal entry is one word.
|
||||
// - The entry header fits in one word.
|
||||
if format.page_header_size() != word_size
|
||||
|| format.internal_entry_size() != word_size
|
||||
|| format.header_size() > word_size
|
||||
{
|
||||
return None;
|
||||
}
|
||||
Some(format)
|
||||
}
|
||||
|
||||
/// Ensures a user entry is valid.
|
||||
pub fn validate_entry(&self, entry: StoreEntry) -> Result<(), StoreError> {
|
||||
if entry.tag >= self.num_tags {
|
||||
return Err(StoreError::InvalidTag);
|
||||
}
|
||||
if entry.data.len() >= self.page_size {
|
||||
return Err(StoreError::StoreFull);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the entry header length in bytes.
|
||||
///
|
||||
/// This is the smallest number of bytes necessary to store all fields of the entry info up to
|
||||
/// and including `length`.
|
||||
pub fn header_size(&self) -> usize {
|
||||
self.bits_to_bytes(self.length_range.end())
|
||||
}
|
||||
|
||||
/// Returns the entry info length in bytes.
|
||||
///
|
||||
/// This is the number of bytes necessary to store all fields of the entry info. This also
|
||||
/// includes the internal padding to protect the `committed` bit from the `deleted` bit.
|
||||
fn info_size(&self, is_replace: IsReplace) -> usize {
|
||||
let suffix_bits = 2; // committed + complete
|
||||
let info_bits = match is_replace {
|
||||
IsReplace::Replace => self.replace_byte_range.end() + suffix_bits,
|
||||
IsReplace::Insert => self.tag_range.end() + suffix_bits,
|
||||
};
|
||||
let info_size = self.bits_to_bytes(info_bits);
|
||||
// If the suffix bits would end up in the header, we need to add one byte for them.
|
||||
if info_size == self.header_size() {
|
||||
info_size + 1
|
||||
} else {
|
||||
info_size
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the length in bytes of an entry.
|
||||
///
|
||||
/// This depends on the length of the user data and whether the entry replaces an old entry or
|
||||
/// is an insertion. This also includes the internal padding to protect the `committed` bit from
|
||||
/// the `deleted` bit.
|
||||
pub fn entry_size(&self, is_replace: IsReplace, length: usize) -> usize {
|
||||
let mut entry_size = length + self.info_size(is_replace);
|
||||
let word_size = self.word_size;
|
||||
entry_size = self.align_word(entry_size);
|
||||
// The entry must be at least 2 words such that the `committed` and `deleted` bits are on
|
||||
// different words.
|
||||
if entry_size == word_size {
|
||||
entry_size += word_size;
|
||||
}
|
||||
entry_size
|
||||
}
|
||||
|
||||
/// Returns the length in bytes of an internal entry.
|
||||
pub fn internal_entry_size(&self) -> usize {
|
||||
let length = self.bits_to_bytes(self.saved_erase_count_range.end());
|
||||
self.align_word(length)
|
||||
}
|
||||
|
||||
pub fn is_present(&self, header: &[u8]) -> bool {
|
||||
bitfield::is_zero(self.present_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn set_present(&self, header: &mut [u8]) {
|
||||
bitfield::set_zero(self.present_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn is_deleted(&self, header: &[u8]) -> bool {
|
||||
bitfield::is_zero(self.deleted_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
/// Returns whether an entry is present and not deleted.
|
||||
pub fn is_alive(&self, header: &[u8]) -> bool {
|
||||
self.is_present(header) && !self.is_deleted(header)
|
||||
}
|
||||
|
||||
pub fn set_deleted(&self, header: &mut [u8]) {
|
||||
bitfield::set_zero(self.deleted_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn is_internal(&self, header: &[u8]) -> bool {
|
||||
bitfield::is_zero(self.internal_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn set_internal(&self, header: &mut [u8]) {
|
||||
bitfield::set_zero(self.internal_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn is_replace(&self, header: &[u8]) -> IsReplace {
|
||||
if bitfield::is_zero(self.replace_bit, header, bitfield::NO_GAP) {
|
||||
IsReplace::Replace
|
||||
} else {
|
||||
IsReplace::Insert
|
||||
}
|
||||
}
|
||||
|
||||
fn set_replace(&self, header: &mut [u8]) {
|
||||
bitfield::set_zero(self.replace_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn get_length(&self, header: &[u8]) -> usize {
|
||||
bitfield::get_range(self.length_range, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
fn set_length(&self, header: &mut [u8], length: usize) {
|
||||
bitfield::set_range(self.length_range, header, bitfield::NO_GAP, length)
|
||||
}
|
||||
|
||||
pub fn get_data<'a>(&self, entry: &'a [u8]) -> &'a [u8] {
|
||||
&entry[self.header_size()..][..self.get_length(entry)]
|
||||
}
|
||||
|
||||
/// Returns the span of user data in an entry.
|
||||
///
|
||||
/// The complement of this gap in the entry is exactly the entry info. The header is before the
|
||||
/// gap and the footer is after the gap.
|
||||
fn entry_gap(&self, entry: &[u8]) -> bitfield::ByteGap {
|
||||
let start = self.header_size();
|
||||
let length = self.get_length(entry);
|
||||
bitfield::ByteGap { start, length }
|
||||
}
|
||||
|
||||
pub fn get_tag(&self, entry: &[u8]) -> usize {
|
||||
bitfield::get_range(self.tag_range, entry, self.entry_gap(entry))
|
||||
}
|
||||
|
||||
fn set_tag(&self, entry: &mut [u8], tag: usize) {
|
||||
bitfield::set_range(self.tag_range, entry, self.entry_gap(entry), tag)
|
||||
}
|
||||
|
||||
pub fn get_replace_index(&self, entry: &[u8]) -> Index {
|
||||
let gap = self.entry_gap(entry);
|
||||
let page = bitfield::get_range(self.replace_page_range, entry, gap);
|
||||
let byte = bitfield::get_range(self.replace_byte_range, entry, gap);
|
||||
Index { page, byte }
|
||||
}
|
||||
|
||||
fn set_replace_page(&self, entry: &mut [u8], page: usize) {
|
||||
bitfield::set_range(self.replace_page_range, entry, self.entry_gap(entry), page)
|
||||
}
|
||||
|
||||
fn set_replace_byte(&self, entry: &mut [u8], byte: usize) {
|
||||
bitfield::set_range(self.replace_byte_range, entry, self.entry_gap(entry), byte)
|
||||
}
|
||||
|
||||
/// Returns the bit position of the `committed` bit.
|
||||
///
|
||||
/// This cannot be precomputed like other fields since it depends on the length of the entry.
|
||||
fn committed_bit(&self, entry: &[u8]) -> usize {
|
||||
8 * entry.len() - 2
|
||||
}
|
||||
|
||||
/// Returns the bit position of the `complete` bit.
|
||||
///
|
||||
/// This cannot be precomputed like other fields since it depends on the length of the entry.
|
||||
fn complete_bit(&self, entry: &[u8]) -> usize {
|
||||
8 * entry.len() - 1
|
||||
}
|
||||
|
||||
pub fn is_committed(&self, entry: &[u8]) -> bool {
|
||||
bitfield::is_zero(self.committed_bit(entry), entry, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn set_committed(&self, entry: &mut [u8]) {
|
||||
bitfield::set_zero(self.committed_bit(entry), entry, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn is_complete(&self, entry: &[u8]) -> bool {
|
||||
bitfield::is_zero(self.complete_bit(entry), entry, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
fn set_complete(&self, entry: &mut [u8]) {
|
||||
bitfield::set_zero(self.complete_bit(entry), entry, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn get_old_page(&self, header: &[u8]) -> usize {
|
||||
bitfield::get_range(self.old_page_range, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn set_old_page(&self, header: &mut [u8], old_page: usize) {
|
||||
bitfield::set_range(self.old_page_range, header, bitfield::NO_GAP, old_page)
|
||||
}
|
||||
|
||||
pub fn get_saved_erase_count(&self, header: &[u8]) -> usize {
|
||||
bitfield::get_range(self.saved_erase_count_range, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn set_saved_erase_count(&self, header: &mut [u8], erase_count: usize) {
|
||||
bitfield::set_range(
|
||||
self.saved_erase_count_range,
|
||||
header,
|
||||
bitfield::NO_GAP,
|
||||
erase_count,
|
||||
)
|
||||
}
|
||||
|
||||
/// Builds an entry for replace or insert operations.
|
||||
pub fn build_entry(&self, replace: Option<Index>, user_entry: StoreEntry) -> Vec<u8> {
|
||||
let StoreEntry { tag, data } = user_entry;
|
||||
let is_replace = match replace {
|
||||
None => IsReplace::Insert,
|
||||
Some(_) => IsReplace::Replace,
|
||||
};
|
||||
let entry_len = self.entry_size(is_replace, data.len());
|
||||
let mut entry = Vec::with_capacity(entry_len);
|
||||
// Build the header.
|
||||
entry.resize(self.header_size(), 0xff);
|
||||
self.set_present(&mut entry[..]);
|
||||
self.set_length(&mut entry[..], data.len());
|
||||
// Add the data.
|
||||
entry.extend_from_slice(data);
|
||||
// Build the footer.
|
||||
entry.resize(entry_len, 0xff);
|
||||
self.set_tag(&mut entry[..], tag);
|
||||
self.set_complete(&mut entry[..]);
|
||||
match replace {
|
||||
None => self.set_committed(&mut entry[..]),
|
||||
Some(Index { page, byte }) => {
|
||||
self.set_replace(&mut entry[..]);
|
||||
self.set_replace_page(&mut entry[..], page);
|
||||
self.set_replace_byte(&mut entry[..], byte);
|
||||
}
|
||||
}
|
||||
entry
|
||||
}
|
||||
|
||||
/// Builds an entry for replace or insert operations.
|
||||
pub fn build_erase_entry(&self, old_page: usize, saved_erase_count: usize) -> Vec<u8> {
|
||||
let mut entry = vec![0xff; self.internal_entry_size()];
|
||||
self.set_present(&mut entry[..]);
|
||||
self.set_internal(&mut entry[..]);
|
||||
self.set_old_page(&mut entry[..], old_page);
|
||||
self.set_saved_erase_count(&mut entry[..], saved_erase_count);
|
||||
entry
|
||||
}
|
||||
|
||||
/// Returns the length in bytes of a page header entry.
|
||||
///
|
||||
/// This includes the word padding.
|
||||
pub fn page_header_size(&self) -> usize {
|
||||
self.align_word(self.bits_to_bytes(self.erase_count_range.end()))
|
||||
}
|
||||
|
||||
pub fn is_initialized(&self, header: &[u8]) -> bool {
|
||||
bitfield::is_zero(self.initialized_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn set_initialized(&self, header: &mut [u8]) {
|
||||
bitfield::set_zero(self.initialized_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn get_erase_count(&self, header: &[u8]) -> usize {
|
||||
bitfield::get_range(self.erase_count_range, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn set_erase_count(&self, header: &mut [u8], count: usize) {
|
||||
bitfield::set_range(self.erase_count_range, header, bitfield::NO_GAP, count)
|
||||
}
|
||||
|
||||
pub fn is_compacting(&self, header: &[u8]) -> bool {
|
||||
bitfield::is_zero(self.compacting_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn set_compacting(&self, header: &mut [u8]) {
|
||||
bitfield::set_zero(self.compacting_bit, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn get_new_page(&self, header: &[u8]) -> usize {
|
||||
bitfield::get_range(self.new_page_range, header, bitfield::NO_GAP)
|
||||
}
|
||||
|
||||
pub fn set_new_page(&self, header: &mut [u8], new_page: usize) {
|
||||
bitfield::set_range(self.new_page_range, header, bitfield::NO_GAP, new_page)
|
||||
}
|
||||
|
||||
/// Returns the smallest word boundary greater or equal to a value.
|
||||
fn align_word(&self, value: usize) -> usize {
|
||||
let word_size = self.word_size;
|
||||
(value + word_size - 1) / word_size * word_size
|
||||
}
|
||||
|
||||
/// Returns the minimum number of bytes to represent a given number of bits.
|
||||
fn bits_to_bytes(&self, bits: usize) -> usize {
|
||||
(bits + 7) / 8
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of bits necessary to write numbers smaller than `x`.
|
||||
fn num_bits(x: usize) -> usize {
|
||||
x.next_power_of_two().trailing_zeros() as usize
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn num_bits_ok() {
|
||||
assert_eq!(num_bits(0), 0);
|
||||
assert_eq!(num_bits(1), 0);
|
||||
assert_eq!(num_bits(2), 1);
|
||||
assert_eq!(num_bits(3), 2);
|
||||
assert_eq!(num_bits(4), 2);
|
||||
assert_eq!(num_bits(5), 3);
|
||||
assert_eq!(num_bits(8), 3);
|
||||
assert_eq!(num_bits(9), 4);
|
||||
assert_eq!(num_bits(16), 4);
|
||||
}
|
||||
1028
src/embedded_flash/store/mod.rs
Normal file
1028
src/embedded_flash/store/mod.rs
Normal file
File diff suppressed because it is too large
Load Diff
195
src/embedded_flash/syscall.rs
Normal file
195
src/embedded_flash/syscall.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
// Copyright 2019 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{Index, Storage, StorageError, StorageResult};
|
||||
use libtock::syscalls;
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x50003;
|
||||
|
||||
mod command_nr {
|
||||
pub const GET_INFO: usize = 1;
|
||||
pub mod get_info_nr {
|
||||
pub const WORD_SIZE: usize = 0;
|
||||
pub const PAGE_SIZE: usize = 1;
|
||||
pub const MAX_WORD_WRITES: usize = 2;
|
||||
pub const MAX_PAGE_ERASES: usize = 3;
|
||||
}
|
||||
pub const WRITE_SLICE: usize = 2;
|
||||
pub const ERASE_PAGE: usize = 3;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const WRITE_SLICE: usize = 0;
|
||||
}
|
||||
|
||||
fn get_info(nr: usize) -> StorageResult<usize> {
|
||||
let code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, 0) };
|
||||
if code < 0 {
|
||||
Err(StorageError::KernelError { code })
|
||||
} else {
|
||||
Ok(code as usize)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SyscallStorage {
|
||||
word_size: usize,
|
||||
page_size: usize,
|
||||
max_word_writes: usize,
|
||||
max_page_erases: usize,
|
||||
storage: &'static mut [u8],
|
||||
}
|
||||
|
||||
impl SyscallStorage {
|
||||
/// Provides access to the embedded flash if available.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The `storage` must be in a writeable flash region.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `BadFlash` if any of the following conditions do not hold:
|
||||
/// - The word size is not a power of two.
|
||||
/// - The page size is not a power of two.
|
||||
/// - The page size is not a multiple of the word size.
|
||||
///
|
||||
/// Returns `NotAligned` if any of the following conditions do not hold:
|
||||
/// - `storage` is page-aligned.
|
||||
/// - `storage.len()` is a multiple of the page size.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate ctap2;
|
||||
/// # use ctap2::embedded_flash::SyscallStorage;
|
||||
/// # use ctap2::embedded_flash::StorageResult;
|
||||
/// # const NUM_PAGES: usize = 1;
|
||||
/// # const PAGE_SIZE: usize = 1;
|
||||
/// #[link_section = ".app_state"]
|
||||
/// static mut STORAGE: [u8; NUM_PAGES * PAGE_SIZE] = [0xff; NUM_PAGES * PAGE_SIZE];
|
||||
/// # fn foo() -> StorageResult<SyscallStorage> {
|
||||
/// // This is safe because this is the only use of `STORAGE` in the whole program and this is
|
||||
/// // called only once.
|
||||
/// unsafe { SyscallStorage::new(&mut STORAGE) }
|
||||
/// # }
|
||||
/// ```
|
||||
pub unsafe fn new(storage: &'static mut [u8]) -> StorageResult<SyscallStorage> {
|
||||
let word_size = get_info(command_nr::get_info_nr::WORD_SIZE)?;
|
||||
let page_size = get_info(command_nr::get_info_nr::PAGE_SIZE)?;
|
||||
let max_word_writes = get_info(command_nr::get_info_nr::MAX_WORD_WRITES)?;
|
||||
let max_page_erases = get_info(command_nr::get_info_nr::MAX_PAGE_ERASES)?;
|
||||
if !word_size.is_power_of_two() || !page_size.is_power_of_two() {
|
||||
return Err(StorageError::BadFlash);
|
||||
}
|
||||
let syscall = SyscallStorage {
|
||||
word_size,
|
||||
page_size,
|
||||
max_word_writes,
|
||||
max_page_erases,
|
||||
storage,
|
||||
};
|
||||
if !syscall.is_word_aligned(page_size) {
|
||||
return Err(StorageError::BadFlash);
|
||||
}
|
||||
if syscall.is_page_aligned(syscall.storage.as_ptr() as usize)
|
||||
&& syscall.is_page_aligned(syscall.storage.len())
|
||||
{
|
||||
Ok(syscall)
|
||||
} else {
|
||||
Err(StorageError::NotAligned)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_word_aligned(&self, x: usize) -> bool {
|
||||
x & (self.word_size - 1) == 0
|
||||
}
|
||||
|
||||
fn is_page_aligned(&self, x: usize) -> bool {
|
||||
x & (self.page_size - 1) == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl Storage for SyscallStorage {
|
||||
fn word_size(&self) -> usize {
|
||||
self.word_size
|
||||
}
|
||||
|
||||
fn page_size(&self) -> usize {
|
||||
self.page_size
|
||||
}
|
||||
|
||||
fn num_pages(&self) -> usize {
|
||||
self.storage.len() / self.page_size
|
||||
}
|
||||
|
||||
fn max_word_writes(&self) -> usize {
|
||||
self.max_word_writes
|
||||
}
|
||||
|
||||
fn max_page_erases(&self) -> usize {
|
||||
self.max_page_erases
|
||||
}
|
||||
|
||||
fn read_slice(&self, index: Index, length: usize) -> StorageResult<&[u8]> {
|
||||
Ok(&self.storage[index.range(length, self)?])
|
||||
}
|
||||
|
||||
fn write_slice(&mut self, index: Index, value: &[u8]) -> StorageResult<()> {
|
||||
if !self.is_word_aligned(index.byte) || !self.is_word_aligned(value.len()) {
|
||||
return Err(StorageError::NotAligned);
|
||||
}
|
||||
let range = index.range(value.len(), self)?;
|
||||
let code = unsafe {
|
||||
syscalls::allow_ptr(
|
||||
DRIVER_NUMBER,
|
||||
allow_nr::WRITE_SLICE,
|
||||
// We rely on the driver not writing to the slice. This should use read-only allow
|
||||
// when available. See https://github.com/tock/tock/issues/1274.
|
||||
value.as_ptr() as *mut u8,
|
||||
value.len(),
|
||||
)
|
||||
};
|
||||
if code < 0 {
|
||||
return Err(StorageError::KernelError { code });
|
||||
}
|
||||
let code = unsafe {
|
||||
syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::WRITE_SLICE,
|
||||
self.storage[range].as_ptr() as usize,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if code < 0 {
|
||||
return Err(StorageError::KernelError { code });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn erase_page(&mut self, page: usize) -> StorageResult<()> {
|
||||
let range = Index { page, byte: 0 }.range(self.page_size(), self)?;
|
||||
let code = unsafe {
|
||||
syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::ERASE_PAGE,
|
||||
self.storage[range].as_ptr() as usize,
|
||||
0,
|
||||
)
|
||||
};
|
||||
if code < 0 {
|
||||
return Err(StorageError::KernelError { code });
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user