Initial commit
This commit is contained in:
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