Initial commit

This commit is contained in:
Jean-Michel Picod
2020-01-28 15:09:10 +01:00
commit f91d2fd3db
90 changed files with 31123 additions and 0 deletions

View 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(())
}
}