diff --git a/src/env/tock/storage.rs b/src/env/tock/storage.rs index 15c5b426..669612be 100644 --- a/src/env/tock/storage.rs +++ b/src/env/tock/storage.rs @@ -19,128 +19,26 @@ use super::upgrade_helper::{ use super::TockEnv; use alloc::borrow::Cow; use alloc::vec::Vec; -use core::cell::Cell; use core::marker::PhantomData; +use libtock_drivers::result::TockResult; +use libtock_drivers::storage::{Storage as LibtockStorage, StorageType}; use libtock_platform as platform; -use libtock_platform::{syscall_class, ErrorCode, RawSyscalls, Syscalls}; +use libtock_platform::Syscalls; use opensk::api::crypto::sha256::Sha256; use opensk::env::Sha; use persistent_store::{Storage, StorageError, StorageIndex, StorageResult}; -use platform::share; - -const DRIVER_NUMBER: u32 = 0x50003; const UPGRADE_PUBLIC_KEY: &[u8; 65] = include_bytes!(concat!(env!("OUT_DIR"), "/opensk_upgrade_pubkey.bin")); -mod subscribe_nr { - pub const DONE: u32 = 0; -} - -mod command_nr { - pub const GET_INFO: u32 = 1; - pub mod get_info_nr { - pub const WORD_SIZE: u32 = 0; - pub const PAGE_SIZE: u32 = 1; - pub const MAX_WORD_WRITES: u32 = 2; - pub const MAX_PAGE_ERASES: u32 = 3; - } - pub const WRITE_SLICE: u32 = 2; - pub const ERASE_PAGE: u32 = 3; -} - -mod ro_allow_nr { - pub const WRITE_SLICE: u32 = 0; -} - -mod memop_nr { - pub const STORAGE_CNT: u32 = 12; - pub const STORAGE_PTR: u32 = 13; - pub const STORAGE_LEN: u32 = 14; - pub const STORAGE_TYPE: u32 = 15; -} - -mod storage_type { - pub const STORE: u32 = 1; - pub const PARTITION: u32 = 2; -} - -fn get_info(nr: u32, arg: u32) -> StorageResult { - let info = S::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg) - .to_result::() - .map_err(|_| StorageError::CustomError)?; - Ok(info) -} - -fn memop(nr: u32, arg: u32) -> StorageResult { - let registers = unsafe { S::syscall2::<{ syscall_class::MEMOP }>([nr.into(), arg.into()]) }; - - let r0 = registers[0].as_u32(); - let r1 = registers[1].as_u32(); - - // make sure r0 is the `success with u32` (129) return variant and then return the value in r1 as u32 - // see: https://github.com/tock/tock/blob/master/doc/reference/trd104-syscalls.md#32-return-values - match (r0, r1) { - (129, r1) => Ok(r1), - (_, _) => Err(StorageError::CustomError), - } -} - -fn block_command( - driver: u32, - cmd: u32, - arg1: u32, - arg2: u32, -) -> StorageResult<()> { - let called: Cell> = Cell::new(None); - - share::scope(|subscribe| { - S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::DONE }>(subscribe, &called) - .map_err(|_| StorageError::CustomError)?; - S::command(driver, cmd, arg1, arg2) - .to_result::<(), ErrorCode>() - .map_err(|_| StorageError::CustomError)?; - libtock_drivers::util::Util::::yieldk_for(|| called.get().is_some()); - if called.get().unwrap().0 == 0 { - Ok(()) - } else { - Err(StorageError::CustomError) - } - }) +fn to_storage_result(result: TockResult) -> StorageResult { + result.map_err(|_| StorageError::CustomError) } unsafe fn read_slice(address: usize, length: usize) -> &'static [u8] { core::slice::from_raw_parts(address as *const u8, length) } -fn write_slice( - ptr: usize, - value: &[u8], -) -> StorageResult<()> { - share::scope(|allow_ro| { - S::allow_ro::(allow_ro, value) - .map_err(|_| StorageError::CustomError)?; - block_command::( - DRIVER_NUMBER, - command_nr::WRITE_SLICE, - ptr as u32, - value.len() as u32, - ) - }) -} - -fn erase_page( - ptr: usize, - page_length: usize, -) -> StorageResult<()> { - block_command::( - DRIVER_NUMBER, - command_nr::ERASE_PAGE, - ptr as u32, - page_length as u32, - ) -} - pub struct TockStorage { word_size: usize, page_size: usize, @@ -163,12 +61,16 @@ impl T /// - The page size is a multiple of the word size. /// - The storage is page-aligned. pub fn new() -> StorageResult> { + let word_size = to_storage_result(LibtockStorage::::word_size())?; + let page_size = to_storage_result(LibtockStorage::::page_size())?; + let max_word_writes = to_storage_result(LibtockStorage::::max_word_writes())?; + let max_page_erases = to_storage_result(LibtockStorage::::max_page_erases())?; let mut syscall = TockStorage { - word_size: get_info::(command_nr::get_info_nr::WORD_SIZE, 0)? as usize, - page_size: get_info::(command_nr::get_info_nr::PAGE_SIZE, 0)? as usize, + word_size, + page_size, num_pages: 0, - max_word_writes: get_info::(command_nr::get_info_nr::MAX_WORD_WRITES, 0)? as usize, - max_page_erases: get_info::(command_nr::get_info_nr::MAX_PAGE_ERASES, 0)? as usize, + max_word_writes, + max_page_erases, storage_locations: Vec::new(), s: PhantomData, c: PhantomData, @@ -179,13 +81,14 @@ impl T { return Err(StorageError::CustomError); } - let num_storage_locations = memop::(memop_nr::STORAGE_CNT, 0)?; + let num_storage_locations = to_storage_result(LibtockStorage::::storage_cnt())?; for i in 0..num_storage_locations { - if memop::(memop_nr::STORAGE_TYPE, i)? != storage_type::STORE { + let storage_type = to_storage_result(LibtockStorage::::storage_type(i))?; + if !matches!(storage_type, StorageType::Store) { continue; } - let storage_ptr = memop::(memop_nr::STORAGE_PTR, i)? as usize; - let storage_len = memop::(memop_nr::STORAGE_LEN, i)? as usize; + let storage_ptr = to_storage_result(LibtockStorage::::storage_ptr())?; + let storage_len = to_storage_result(LibtockStorage::::storage_len())?; if !syscall.is_page_aligned(storage_ptr) || !syscall.is_page_aligned(storage_len) { return Err(StorageError::CustomError); } @@ -239,14 +142,14 @@ impl S return Err(StorageError::NotAligned); } let ptr = self.read_slice(index, value.len())?.as_ptr() as usize; - write_slice::(ptr, value) + to_storage_result(LibtockStorage::::write_slice(ptr, value)) } fn erase_page(&mut self, page: usize) -> StorageResult<()> { let index = StorageIndex { page, byte: 0 }; let length = self.page_size(); let ptr = self.read_slice(index, length)?.as_ptr() as usize; - erase_page::(ptr, length) + to_storage_result(LibtockStorage::::erase_page(ptr, length)) } } @@ -290,7 +193,7 @@ where /// - not consecutive. pub fn new() -> StorageResult> { let mut locations = TockUpgradeStorage { - page_size: get_info::(command_nr::get_info_nr::PAGE_SIZE, 0)? as usize, + page_size: to_storage_result(LibtockStorage::::page_size())?, partition: Partition::default(), metadata: ModRange::new_empty(), running_metadata: ModRange::new_empty(), @@ -302,13 +205,13 @@ where return Err(StorageError::CustomError); } let mut firmware_range = ModRange::new_empty(); - for i in 0..memop::(memop_nr::STORAGE_CNT, 0)? { - let storage_type = memop::(memop_nr::STORAGE_TYPE, i)?; - if !matches!(storage_type, storage_type::PARTITION) { + for i in 0..to_storage_result(LibtockStorage::::storage_cnt())? { + let storage_type = to_storage_result(LibtockStorage::::storage_type(i))?; + if !matches!(storage_type, StorageType::Partition) { continue; }; - let storage_ptr = memop::(memop_nr::STORAGE_PTR, i)? as usize; - let storage_len = memop::(memop_nr::STORAGE_LEN, i)? as usize; + let storage_ptr = to_storage_result(LibtockStorage::::storage_ptr())?; + let storage_len = to_storage_result(LibtockStorage::::storage_len())?; if !locations.is_page_aligned(storage_ptr) || !locations.is_page_aligned(storage_len) { return Err(StorageError::CustomError); } @@ -400,9 +303,9 @@ where // Erases all pages that have their first byte in the write range. // Since we expect calls in order, we don't want to erase half-written pages. for address in write_range.aligned_iter(self.page_size) { - erase_page::(address, self.page_size)?; + to_storage_result(LibtockStorage::::erase_page(address, self.page_size))?; } - write_slice::(address, &data)?; + to_storage_result(LibtockStorage::::write_slice(address, &data))?; let written_slice = unsafe { read_slice(address, data.len()) }; if written_slice != data { return Err(StorageError::CustomError); diff --git a/third_party/libtock-drivers/src/lib.rs b/third_party/libtock-drivers/src/lib.rs index 27c63617..7fb1af6b 100644 --- a/third_party/libtock-drivers/src/lib.rs +++ b/third_party/libtock-drivers/src/lib.rs @@ -5,6 +5,7 @@ pub mod crp; pub mod nfc; pub mod result; pub mod rng; +pub mod storage; pub mod timer; pub mod usb_ctap_hid; pub mod util; diff --git a/third_party/libtock-drivers/src/storage.rs b/third_party/libtock-drivers/src/storage.rs new file mode 100644 index 00000000..78b43942 --- /dev/null +++ b/third_party/libtock-drivers/src/storage.rs @@ -0,0 +1,161 @@ +// Copyright 2023 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 crate::result::{TockError, TockResult}; +use crate::util::Util; +use core::cell::Cell; +use core::convert::TryFrom; +use libtock_platform as platform; +use libtock_platform::{share, syscall_class, DefaultConfig, ErrorCode, Syscalls}; + +const DRIVER_NUMBER: u32 = 0x50003; + +mod subscribe_nr { + pub const DONE: u32 = 0; +} + +mod command_nr { + pub const GET_INFO: u32 = 1; + pub mod get_info_nr { + pub const WORD_SIZE: u32 = 0; + pub const PAGE_SIZE: u32 = 1; + pub const MAX_WORD_WRITES: u32 = 2; + pub const MAX_PAGE_ERASES: u32 = 3; + } + pub const WRITE_SLICE: u32 = 2; + pub const ERASE_PAGE: u32 = 3; +} + +mod ro_allow_nr { + pub const WRITE_SLICE: u32 = 0; +} + +mod memop_nr { + pub const STORAGE_CNT: u32 = 12; + pub const STORAGE_PTR: u32 = 13; + pub const STORAGE_LEN: u32 = 14; + pub const STORAGE_TYPE: u32 = 15; +} + +pub enum StorageType { + Store = 1, + Partition = 2, +} + +impl TryFrom for StorageType { + type Error = TockError; + + fn try_from(number: u32) -> Result { + match number { + 1 => Ok(StorageType::Store), + 2 => Ok(StorageType::Partition), + _ => Err(TockError::from(ErrorCode::Fail)), + } + } +} + +pub struct Storage< + S: Syscalls, + C: platform::subscribe::Config + platform::allow_ro::Config = DefaultConfig, +>(S, C); + +impl Storage { + fn get_info(nr: u32, arg: u32) -> TockResult { + Ok(S::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg) + .to_result::()?) + } + + fn memop(nr: u32, arg: u32) -> TockResult { + let registers = unsafe { S::syscall2::<{ syscall_class::MEMOP }>([nr.into(), arg.into()]) }; + + let r0 = registers[0].as_u32(); + let r1 = registers[1].as_u32(); + + // Make sure r0 is the `success with u32` (129) return variant. + // Then return the value in r1 as u32. See: + // https://github.com/tock/tock/blob/master/doc/reference/trd104-syscalls.md#32-return-values + match (r0, r1) { + (129, r1) => Ok(r1), + (_, _) => Err(TockError::from(ErrorCode::Fail)), + } + } + + fn block_command(driver: u32, cmd: u32, arg1: u32, arg2: u32) -> TockResult<()> { + let called: Cell> = Cell::new(None); + + Ok(share::scope(|subscribe| { + S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::DONE }>(subscribe, &called)?; + S::command(driver, cmd, arg1, arg2).to_result::<(), ErrorCode>()?; + Util::::yieldk_for(|| called.get().is_some()); + if called.get().unwrap().0 == 0 { + Ok(()) + } else { + Err(ErrorCode::Fail) + } + })?) + } + + pub fn write_slice(ptr: usize, value: &[u8]) -> TockResult<()> { + share::scope(|allow_ro| { + S::allow_ro::(allow_ro, value)?; + Self::block_command( + DRIVER_NUMBER, + command_nr::WRITE_SLICE, + ptr as u32, + value.len() as u32, + ) + }) + } + + pub fn erase_page(ptr: usize, page_length: usize) -> TockResult<()> { + Self::block_command( + DRIVER_NUMBER, + command_nr::ERASE_PAGE, + ptr as u32, + page_length as u32, + ) + } + + pub fn word_size() -> TockResult { + Ok(Self::get_info(command_nr::get_info_nr::WORD_SIZE, 0)? as usize) + } + + pub fn page_size() -> TockResult { + Ok(Self::get_info(command_nr::get_info_nr::PAGE_SIZE, 0)? as usize) + } + + pub fn max_word_writes() -> TockResult { + Ok(Self::get_info(command_nr::get_info_nr::MAX_WORD_WRITES, 0)? as usize) + } + + pub fn max_page_erases() -> TockResult { + Ok(Self::get_info(command_nr::get_info_nr::MAX_PAGE_ERASES, 0)? as usize) + } + + pub fn storage_cnt() -> TockResult { + Ok(Self::memop(memop_nr::STORAGE_CNT, 0)? as usize) + } + + pub fn storage_ptr() -> TockResult { + Ok(Self::memop(memop_nr::STORAGE_PTR, 0)? as usize) + } + + pub fn storage_len() -> TockResult { + Ok(Self::memop(memop_nr::STORAGE_LEN, 0)? as usize) + } + + pub fn storage_type(index: usize) -> TockResult { + StorageType::try_from(Self::memop(memop_nr::STORAGE_TYPE, index as u32)?) + } +} diff --git a/third_party/libtock-drivers/src/usb_ctap_hid.rs b/third_party/libtock-drivers/src/usb_ctap_hid.rs index 0029cf21..0faaefad 100644 --- a/third_party/libtock-drivers/src/usb_ctap_hid.rs +++ b/third_party/libtock-drivers/src/usb_ctap_hid.rs @@ -494,8 +494,8 @@ impl UsbCtapHid { "Cancelling USB transaction due to timeout" ) .unwrap(); - let result = S::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) - .to_result::<(), ErrorCode>(); + let result = + S::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0).to_result::<(), ErrorCode>(); match result { // - SUCCESS means that we successfully cancelled the transaction. // - EALREADY means that the transaction was already completed.