Tock V2 port - rebased and updated (#620)

* Changes from #580

* fixes USB cancel panic

* style fixes

* Update src/env/tock/storage.rs

Co-authored-by: Zach Halvorsen <zhalvorsen@google.com>

---------

Co-authored-by: Zach Halvorsen <zhalvorsen@google.com>
This commit is contained in:
kaczmarczyck
2023-05-05 09:55:16 +02:00
committed by GitHub
parent 645c1ba3a7
commit f25cdd6acc
78 changed files with 4079 additions and 4699 deletions

View File

@@ -25,7 +25,7 @@ jobs:
- name: Set up OpenSK
run: ./setup.sh
- name: Run bloat on the PR
run: RUSTFLAGS="-C link-arg=-icf=all -C force-frame-pointers=no" cargo bloat --release --target=thumbv7em-none-eabi --features=with_ctap1,vendor_hid --crates >> .github/workflows/bloat_output_new.txt
run: RUSTFLAGS="-C link-arg=-icf=all -C force-frame-pointers=no -C link-arg=-Tnrf52840_layout.ld" cargo bloat --release --target=thumbv7em-none-eabi --features=with_ctap1,vendor_hid --crates >> .github/workflows/bloat_output_new.txt
# Second run: PR
- uses: actions/checkout@v2
@@ -41,7 +41,7 @@ jobs:
run: ./setup.sh
- name: Run bloat on base
working-directory: ./OpenSK_base
run: RUSTFLAGS="-C link-arg=-icf=all -C force-frame-pointers=no" cargo bloat --release --target=thumbv7em-none-eabi --features=with_ctap1,vendor_hid --crates >> "$GITHUB_WORKSPACE/.github/workflows/bloat_output_old.txt"
run: RUSTFLAGS="-C link-arg=-icf=all -C force-frame-pointers=no -C link-arg=-Tnrf52840_layout.ld" cargo bloat --release --target=thumbv7em-none-eabi --features=with_ctap1,vendor_hid --crates >> "$GITHUB_WORKSPACE/.github/workflows/bloat_output_old.txt"
- name: Run output formatter to echo workflow command
run: ./.github/workflows/bloat_formatter.sh bloat_output_new.txt bloat_output_old.txt bloat_comment.md

View File

@@ -25,10 +25,10 @@ jobs:
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --all-targets --features std
args: --features std
- name: Deny Clippy warnings (std)
run: cargo clippy --all-targets --features std -- -A clippy::new_without_default -D warnings
run: cargo clippy --features std -- -D warnings
- name: Deny Clippy warnings (all)
run: cargo clippy --all-targets --features std,with_ctap1,ed25519,vendor_hid -- -A clippy::new_without_default -D warnings
run: cargo clippy --features std,with_ctap1,ed25519,vendor_hid -- -D warnings
- name: Deny Clippy warnings (all, nfc)
run: cargo clippy --all-targets --features std,with_ctap1,with_nfc,ed25519,vendor_hid -- -A clippy::new_without_default -D warnings
run: cargo clippy --features std,with_ctap1,with_nfc,ed25519,vendor_hid -- -D warnings

79
Cargo.lock generated
View File

@@ -80,8 +80,13 @@ dependencies = [
"ed25519-compact",
"enum-iterator",
"lang_items",
"libtock_core",
"libtock_buttons",
"libtock_console",
"libtock_drivers",
"libtock_leds",
"libtock_platform",
"libtock_runtime",
"libtock_unittest",
"opensk",
"openssl",
"persistent_store",
@@ -167,8 +172,12 @@ dependencies = [
name = "lang_items"
version = "0.1.0"
dependencies = [
"libtock_core",
"libtock_console",
"libtock_drivers",
"libtock_leds",
"libtock_low_level_debug",
"libtock_platform",
"libtock_runtime",
"linked_list_allocator",
]
@@ -179,26 +188,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
[[package]]
name = "libtock_codegen"
name = "libtock_buttons"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
"libtock_platform",
]
[[package]]
name = "libtock_core"
name = "libtock_console"
version = "0.1.0"
dependencies = [
"libtock_codegen",
"libtock_platform",
]
[[package]]
name = "libtock_drivers"
version = "0.1.0"
dependencies = [
"libtock_core",
"libtock_console",
"libtock_platform",
]
[[package]]
name = "libtock_leds"
version = "0.1.0"
dependencies = [
"libtock_platform",
]
[[package]]
name = "libtock_low_level_debug"
version = "0.1.0"
dependencies = [
"libtock_platform",
]
[[package]]
name = "libtock_platform"
version = "0.1.0"
[[package]]
name = "libtock_runtime"
version = "0.1.0"
dependencies = [
"libtock_platform",
]
[[package]]
name = "libtock_unittest"
version = "0.1.0"
dependencies = [
"libtock_platform",
"thiserror",
]
[[package]]
@@ -457,6 +498,26 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "thiserror"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "unicode-ident"
version = "1.0.4"

View File

@@ -10,14 +10,23 @@ authors = [
license = "Apache-2.0"
edition = "2018"
[target.'cfg(any(target_arch = "arm", target_arch = "riscv32"))'.dependencies.libtock_runtime]
path = "third_party/libtock-rs/runtime"
default-features = false
features = ["no_auto_layout", "no_debug_memop"]
[dependencies]
libtock_core = { path = "third_party/libtock-rs/core" }
libtock_buttons = { path = "third_party/libtock-rs/apis/buttons" }
libtock_platform = { path = "third_party/libtock-rs/platform" }
libtock_drivers = { path = "third_party/libtock-drivers" }
libtock_console = { path = "third_party/libtock-rs/apis/console" }
libtock_leds = { path = "third_party/libtock-rs/apis/leds" }
lang_items = { path = "third_party/lang-items" }
opensk = { path = "libraries/opensk" }
sk-cbor = { path = "libraries/cbor" }
crypto = { path = "libraries/crypto" }
persistent_store = { path = "libraries/persistent_store" }
libtock_unittest = { path = "third_party/libtock-rs/unittest", optional = true }
byteorder = { version = "1", default-features = false }
arrayref = "0.3.6"
rand_core = "0.6.4"
@@ -27,7 +36,7 @@ ed25519-compact = { version = "1", default-features = false, optional = true }
debug_allocations = ["lang_items/debug_allocations"]
debug_ctap = ["libtock_drivers/debug_ctap", "opensk/debug_ctap"]
panic_console = ["lang_items/panic_console"]
std = ["crypto/std", "lang_items/std", "persistent_store/std", "opensk/std"]
std = ["crypto/std", "lang_items/std", "persistent_store/std", "opensk/std", "libtock_unittest"]
verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
with_ctap1 = ["crypto/with_ctap1", "opensk/with_ctap1"]
with_nfc = ["libtock_drivers/with_nfc"]

View File

@@ -7,8 +7,7 @@ use kernel::hil::led;
use kernel::hil::uart::{self, Configure};
use nrf52840::gpio::Pin;
use crate::CHIP;
use crate::PROCESSES;
use crate::{CHIP, PROCESSES, PROCESS_PRINTER};
struct Writer {
initialized: bool,
@@ -31,7 +30,7 @@ impl IoWrite for Writer {
let uart = nrf52840::uart::Uarte::new();
if !self.initialized {
self.initialized = true;
uart.configure(uart::Parameters {
let _ = uart.configure(uart::Parameters {
baud_rate: 115200,
stop_bits: uart::StopBits::One,
parity: uart::Parity::None,
@@ -64,5 +63,6 @@ pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! {
&cortexm4::support::nop,
&PROCESSES,
&CHIP,
&PROCESS_PRINTER,
)
}

View File

@@ -7,15 +7,18 @@
// Disable this attribute when documenting, as a workaround for
// https://github.com/rust-lang/rust/issues/62184.
#![cfg_attr(not(doc), no_main)]
#![feature(const_in_array_repeat_expressions)]
#![deny(missing_docs)]
use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
use capsules::virtual_alarm::VirtualMuxAlarm;
use kernel::component::Component;
use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
use kernel::hil::led::LedLow;
use kernel::hil::time::Counter;
use kernel::platform::{KernelResources, SyscallDriverLookup, SyscallFilter};
use kernel::scheduler::round_robin::RoundRobinSched;
#[allow(unused_imports)]
use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
use kernel::{StorageLocation, StorageType};
use nrf52840::gpio::Pin;
use nrf52840::interrupt_service::Nrf52840DefaultPeripherals;
use nrf52_components::{self, UartChannel, UartPins};
@@ -56,30 +59,35 @@ static STRINGS: &'static [&'static str] = &[
// State for loading and holding applications.
// How should the kernel respond when a process faults.
const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultResponse::Panic;
const FAULT_RESPONSE: kernel::process::PanicFaultPolicy = kernel::process::PanicFaultPolicy {};
// Number of concurrent processes this platform supports.
const NUM_PROCS: usize = 8;
static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; NUM_PROCS] =
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
[None; NUM_PROCS];
static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
static mut STORAGE_LOCATIONS: [StorageLocation; 2] = [
// We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU.
kernel::StorageLocation {
StorageLocation {
address: 0xC0000,
size: 0x10000, // 16 pages
storage_type: kernel::StorageType::Store,
storage_type: StorageType::Store,
},
kernel::StorageLocation {
StorageLocation {
address: 0xD0000,
size: 0x4000, // 4 pages
storage_type: kernel::StorageType::Store,
storage_type: StorageType::Store,
},
];
// Static reference to chip for panic dumps
static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
// Static reference to process printer for panic dumps
static mut PROCESS_PRINTER: Option<&'static kernel::process::ProcessPrinterText> = None;
/// Flash buffer for the custom nvmc driver
static mut APP_FLASH_BUFFER: [u8; 0x1000] = [0; 0x1000];
/// Dummy buffer that causes the linker to reserve enough space for the stack.
#[no_mangle]
@@ -91,6 +99,7 @@ pub struct Platform {
button: &'static capsules::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
pconsole: &'static capsules::process_console::ProcessConsole<
'static,
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
components::process_console::Capability,
>,
console: &'static capsules::console::Console<'static>,
@@ -98,9 +107,10 @@ pub struct Platform {
led: &'static capsules::led::LedDriver<
'static,
LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
4,
>,
rng: &'static capsules::rng::RngDriver<'static>,
ipc: kernel::ipc::IPC<NUM_PROCS>,
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
analog_comparator: &'static capsules::analog_comparator::AnalogComparator<
'static,
nrf52840::acomp::Comparator<'static>,
@@ -109,19 +119,20 @@ pub struct Platform {
'static,
capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
>,
scheduler: &'static RoundRobinSched<'static>,
systick: cortexm4::systick::SysTick,
nvmc: &'static nrf52840::nvmc::SyscallDriver,
usb: &'static capsules::usb::usb_ctap::CtapUsbSyscallDriver<
'static,
'static,
nrf52840::usbd::Usbd<'static>,
>,
crp: &'static capsules::firmware_protection::FirmwareProtection<nrf52840::uicr::Uicr>,
}
impl kernel::Platform for Platform {
impl SyscallDriverLookup for Platform {
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
where
F: FnOnce(Option<&dyn kernel::Driver>) -> R,
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
{
match driver_num {
capsules::console::DRIVER_NUM => f(Some(self.console)),
@@ -133,45 +144,88 @@ impl kernel::Platform for Platform {
capsules::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
nrf52840::nvmc::DRIVER_NUM => f(Some(self.nvmc)),
capsules::usb::usb_ctap::DRIVER_NUM => f(Some(self.usb)),
capsules::firmware_protection::DRIVER_NUM => f(Some(self.crp)),
kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
_ => f(None),
}
}
}
impl SyscallFilter for Platform {
fn filter_syscall(
&self,
process: &dyn kernel::procs::ProcessType,
process: &dyn kernel::process::Process,
syscall: &kernel::syscall::Syscall,
) -> Result<(), kernel::ReturnCode> {
) -> Result<(), kernel::errorcode::ErrorCode> {
use kernel::syscall::Syscall;
match *syscall {
Syscall::COMMAND {
Syscall::Command {
driver_number: nrf52840::nvmc::DRIVER_NUM,
subdriver_number: cmd,
arg0: ptr,
arg1: len,
} if (cmd == 2 || cmd == 3) && !process.fits_in_storage_location(ptr, len) => {
Err(kernel::ReturnCode::EINVAL)
Err(kernel::ErrorCode::INVAL)
}
_ => Ok(()),
}
}
}
/// Entry point in the vector table called on hard reset.
#[no_mangle]
pub unsafe fn reset_handler() {
// Loads relocations and clears BSS
nrf52840::init();
impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
for Platform
{
type SyscallDriverLookup = Self;
type SyscallFilter = Self;
type ProcessFault = ();
type Scheduler = RoundRobinSched<'static>;
type SchedulerTimer = cortexm4::systick::SysTick;
type WatchDog = ();
type ContextSwitchCallback = ();
let ppi = static_init!(nrf52840::ppi::Ppi, nrf52840::ppi::Ppi::new());
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
&self
}
fn syscall_filter(&self) -> &Self::SyscallFilter {
&self
}
fn process_fault(&self) -> &Self::ProcessFault {
&()
}
fn scheduler(&self) -> &Self::Scheduler {
self.scheduler
}
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
&self.systick
}
fn watchdog(&self) -> &Self::WatchDog {
&()
}
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
&()
}
}
/// This is in a separate, inline(never) function so that its stack frame is
/// removed when this function returns. Otherwise, the stack space used for
/// these static_inits is wasted.
#[inline(never)]
unsafe fn get_peripherals() -> &'static mut Nrf52840DefaultPeripherals<'static> {
// Initialize chip peripheral drivers
let nrf52840_peripherals = static_init!(
Nrf52840DefaultPeripherals,
Nrf52840DefaultPeripherals::new(ppi)
Nrf52840DefaultPeripherals::new()
);
nrf52840_peripherals
}
/// Main function called after RAM initialized.
#[no_mangle]
pub unsafe fn main() {
nrf52840::init();
let nrf52840_peripherals = get_peripherals();
// set up circular peripheral dependencies
nrf52840_peripherals.init();
let base_peripherals = &nrf52840_peripherals.nrf52;
@@ -184,6 +238,7 @@ pub unsafe fn reset_handler() {
// GPIOs
let gpio = components::gpio::GpioComponent::new(
board_kernel,
capsules::gpio::DRIVER_NUM,
components::gpio_component_helper!(
nrf52840::gpio::GPIOPin,
// left side of the USB plug
@@ -219,6 +274,7 @@ pub unsafe fn reset_handler() {
let button = components::button::ButtonComponent::new(
board_kernel,
capsules::button::DRIVER_NUM,
components::button_component_helper!(
nrf52840::gpio::GPIOPin,
(
@@ -230,15 +286,12 @@ pub unsafe fn reset_handler() {
)
.finalize(components::button_component_buf!(nrf52840::gpio::GPIOPin));
let led = components::led::LedsComponent::new(components::led_component_helper!(
let led = components::led::LedsComponent::new().finalize(components::led_component_helper!(
LedLow<'static, nrf52840::gpio::GPIOPin>,
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_PIN]),
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_R_PIN]),
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_G_PIN]),
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_B_PIN]),
))
.finalize(components::led_component_buf!(
LedLow<'static, nrf52840::gpio::GPIOPin>
));
let chip = static_init!(
@@ -272,11 +325,15 @@ pub unsafe fn reset_handler() {
);
let rtc = &base_peripherals.rtc;
rtc.start();
let _ = rtc.start();
let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
.finalize(components::alarm_mux_component_helper!(nrf52840::rtc::Rtc));
let alarm = components::alarm::AlarmDriverComponent::new(board_kernel, mux_alarm)
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
let alarm = components::alarm::AlarmDriverComponent::new(
board_kernel,
capsules::alarm::DRIVER_NUM,
mux_alarm,
)
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
let uart_channel = UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD));
let channel = nrf52_components::UartChannelComponent::new(
uart_channel,
@@ -293,21 +350,41 @@ pub unsafe fn reset_handler() {
);
DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
let process_printer =
components::process_printer::ProcessPrinterTextComponent::new().finalize(());
PROCESS_PRINTER = Some(process_printer);
// Create a shared UART channel for the console and for kernel debug.
let uart_mux =
components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller)
.finalize(());
let pconsole =
components::process_console::ProcessConsoleComponent::new(board_kernel, uart_mux)
.finalize(());
let pconsole = components::process_console::ProcessConsoleComponent::new(
board_kernel,
uart_mux,
mux_alarm,
process_printer,
)
.finalize(components::process_console_component_helper!(
nrf52840::rtc::Rtc<'static>
));
// Setup the console.
let console = components::console::ConsoleComponent::new(board_kernel, uart_mux).finalize(());
let console = components::console::ConsoleComponent::new(
board_kernel,
capsules::console::DRIVER_NUM,
uart_mux,
)
.finalize(components::console_component_helper!());
// Create the debugger object that handles calls to `debug!()`.
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
let rng = components::rng::RngComponent::new(board_kernel, &base_peripherals.trng).finalize(());
let rng = components::rng::RngComponent::new(
board_kernel,
capsules::rng::DRIVER_NUM,
&base_peripherals.trng,
)
.finalize(());
// Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
// These are hardcoded pin assignments specified in the driver
@@ -317,6 +394,8 @@ pub unsafe fn reset_handler() {
nrf52840::acomp::Channel,
&nrf52840::acomp::CHANNEL_AC0
),
board_kernel,
capsules::analog_comparator::DRIVER_NUM,
)
.finalize(components::acomp_component_buf!(
nrf52840::acomp::Comparator
@@ -326,8 +405,9 @@ pub unsafe fn reset_handler() {
nrf52840::nvmc::SyscallDriver,
nrf52840::nvmc::SyscallDriver::new(
&base_peripherals.nvmc,
board_kernel.create_grant(&memory_allocation_capability),
board_kernel.create_grant(nrf52840::nvmc::DRIVER_NUM, &memory_allocation_capability),
dynamic_deferred_caller,
&mut APP_FLASH_BUFFER,
)
);
nvmc.set_deferred_handle(
@@ -339,24 +419,20 @@ pub unsafe fn reset_handler() {
// Configure USB controller
let usb = components::usb_ctap::UsbCtapComponent::new(
board_kernel,
capsules::usb::usb_ctap::DRIVER_NUM,
&nrf52840_peripherals.usbd,
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
VENDOR_ID,
PRODUCT_ID,
STRINGS,
)
.finalize(components::usb_ctap_component_buf!(nrf52840::usbd::Usbd));
let crp = components::firmware_protection::FirmwareProtectionComponent::new(
board_kernel,
nrf52840::uicr::Uicr::new(),
)
.finalize(components::firmware_protection_component_helper!(
nrf52840::uicr::Uicr
));
.finalize(components::usb_ctap_component_helper!(nrf52840::usbd::Usbd));
nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
.finalize(components::rr_component_helper!(NUM_PROCS));
let platform = Platform {
button,
pconsole,
@@ -368,15 +444,20 @@ pub unsafe fn reset_handler() {
analog_comparator,
nvmc,
usb,
crp,
ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability),
ipc: kernel::ipc::IPC::new(
board_kernel,
kernel::ipc::DRIVER_NUM,
&memory_allocation_capability,
),
scheduler,
systick: cortexm4::systick::SysTick::new_with_calibration(6400_0000),
};
platform.pconsole.start();
let _ = platform.pconsole.start();
debug!("Initialization complete. Entering main loop\r");
debug!("{}", &nrf52840::ficr::FICR_INSTANCE);
/// These symbols are defined in the linker script.
// These symbols are defined in the linker script.
extern "C" {
/// Beginning of the ROM region containing app images.
static _sapps: u8;
@@ -388,7 +469,7 @@ pub unsafe fn reset_handler() {
static _eappmem: u8;
}
kernel::procs::load_processes(
kernel::process::load_processes(
board_kernel,
chip,
core::slice::from_raw_parts(
@@ -400,7 +481,7 @@ pub unsafe fn reset_handler() {
&_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
),
&mut PROCESSES,
FAULT_RESPONSE,
&FAULT_RESPONSE,
&process_management_capability,
)
.unwrap_or_else(|err| {
@@ -408,13 +489,5 @@ pub unsafe fn reset_handler() {
debug!("{:?}", err);
});
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
.finalize(components::rr_component_helper!(NUM_PROCS));
board_kernel.kernel_loop(
&platform,
chip,
Some(&platform.ipc),
scheduler,
&main_loop_capability,
);
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
}

View File

@@ -7,8 +7,7 @@ use kernel::hil::led;
use kernel::hil::uart::{self, Configure};
use nrf52840::gpio::Pin;
use crate::CHIP;
use crate::PROCESSES;
use crate::{CHIP, PROCESSES, PROCESS_PRINTER};
struct Writer {
initialized: bool,
@@ -31,7 +30,7 @@ impl IoWrite for Writer {
let uart = nrf52840::uart::Uarte::new();
if !self.initialized {
self.initialized = true;
uart.configure(uart::Parameters {
let _ = uart.configure(uart::Parameters {
baud_rate: 115200,
stop_bits: uart::StopBits::One,
parity: uart::Parity::None,
@@ -64,5 +63,6 @@ pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! {
&cortexm4::support::nop,
&PROCESSES,
&CHIP,
&PROCESS_PRINTER,
)
}

View File

@@ -7,13 +7,15 @@
// Disable this attribute when documenting, as a workaround for
// https://github.com/rust-lang/rust/issues/62184.
#![cfg_attr(not(doc), no_main)]
#![feature(const_in_array_repeat_expressions)]
#![deny(missing_docs)]
use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
use capsules::virtual_alarm::VirtualMuxAlarm;
use kernel::component::Component;
use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
use kernel::hil::led::LedLow;
use kernel::hil::time::Counter;
use kernel::platform::{KernelResources, SyscallDriverLookup, SyscallFilter};
use kernel::scheduler::round_robin::RoundRobinSched;
#[allow(unused_imports)]
use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
use nrf52840::gpio::Pin;
@@ -50,12 +52,12 @@ static STRINGS: &'static [&'static str] = &[
// State for loading and holding applications.
// How should the kernel respond when a process faults.
const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultResponse::Panic;
const FAULT_RESPONSE: kernel::process::PanicFaultPolicy = kernel::process::PanicFaultPolicy {};
// Number of concurrent processes this platform supports.
const NUM_PROCS: usize = 8;
static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; NUM_PROCS] =
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
[None; NUM_PROCS];
static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
@@ -74,6 +76,9 @@ static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
// Static reference to chip for panic dumps
static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
static mut PROCESS_PRINTER: Option<&'static kernel::process::ProcessPrinterText> = None;
/// Flash buffer for the custom nvmc driver
static mut APP_FLASH_BUFFER: [u8; 0x1000] = [0; 0x1000];
/// Dummy buffer that causes the linker to reserve enough space for the stack.
#[no_mangle]
@@ -85,16 +90,18 @@ pub struct Platform {
button: &'static capsules::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
pconsole: &'static capsules::process_console::ProcessConsole<
'static,
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
components::process_console::Capability,
>,
console: &'static capsules::console::Console<'static>,
gpio: &'static capsules::gpio::GPIO<'static, nrf52840::gpio::GPIOPin<'static>>,
led: &'static capsules::led::LedDriver<
'static,
LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
kernel::hil::led::LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
3,
>,
rng: &'static capsules::rng::RngDriver<'static>,
ipc: kernel::ipc::IPC<NUM_PROCS>,
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
analog_comparator: &'static capsules::analog_comparator::AnalogComparator<
'static,
nrf52840::acomp::Comparator<'static>,
@@ -103,6 +110,8 @@ pub struct Platform {
'static,
capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
>,
scheduler: &'static RoundRobinSched<'static>,
systick: cortexm4::systick::SysTick,
nvmc: &'static nrf52840::nvmc::SyscallDriver,
usb: &'static capsules::usb::usb_ctap::CtapUsbSyscallDriver<
'static,
@@ -111,10 +120,10 @@ pub struct Platform {
>,
}
impl kernel::Platform for Platform {
impl SyscallDriverLookup for Platform {
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
where
F: FnOnce(Option<&dyn kernel::Driver>) -> R,
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
{
match driver_num {
capsules::console::DRIVER_NUM => f(Some(self.console)),
@@ -130,39 +139,82 @@ impl kernel::Platform for Platform {
_ => f(None),
}
}
}
impl SyscallFilter for Platform {
fn filter_syscall(
&self,
process: &dyn kernel::procs::ProcessType,
process: &dyn kernel::process::Process,
syscall: &kernel::syscall::Syscall,
) -> Result<(), kernel::ReturnCode> {
) -> Result<(), kernel::errorcode::ErrorCode> {
use kernel::syscall::Syscall;
match *syscall {
Syscall::COMMAND {
Syscall::Command {
driver_number: nrf52840::nvmc::DRIVER_NUM,
subdriver_number: cmd,
arg0: ptr,
arg1: len,
} if (cmd == 2 || cmd == 3) && !process.fits_in_storage_location(ptr, len) => {
Err(kernel::ReturnCode::EINVAL)
Err(kernel::ErrorCode::INVAL)
}
_ => Ok(()),
}
}
}
/// Entry point in the vector table called on hard reset.
#[no_mangle]
pub unsafe fn reset_handler() {
// Loads relocations and clears BSS
nrf52840::init();
let ppi = static_init!(nrf52840::ppi::Ppi, nrf52840::ppi::Ppi::new());
/// This is in a separate, inline(never) function so that its stack frame is
/// removed when this function returns. Otherwise, the stack space used for
/// these static_inits is wasted.
#[inline(never)]
unsafe fn get_peripherals() -> &'static mut Nrf52840DefaultPeripherals<'static> {
// Initialize chip peripheral drivers
let nrf52840_peripherals = static_init!(
Nrf52840DefaultPeripherals,
Nrf52840DefaultPeripherals::new(ppi)
Nrf52840DefaultPeripherals::new()
);
nrf52840_peripherals
}
impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
for Platform
{
type SyscallDriverLookup = Self;
type SyscallFilter = Self;
type ProcessFault = ();
type Scheduler = RoundRobinSched<'static>;
type SchedulerTimer = cortexm4::systick::SysTick;
type WatchDog = ();
type ContextSwitchCallback = ();
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
&self
}
fn syscall_filter(&self) -> &Self::SyscallFilter {
&self
}
fn process_fault(&self) -> &Self::ProcessFault {
&()
}
fn scheduler(&self) -> &Self::Scheduler {
self.scheduler
}
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
&self.systick
}
fn watchdog(&self) -> &Self::WatchDog {
&()
}
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
&()
}
}
/// Main function called after RAM initialized.
#[no_mangle]
pub unsafe fn main() {
// Loads relocations and clears BSS
nrf52840::init();
// Initialize chip peripheral drivers
let nrf52840_peripherals = get_peripherals();
// set up circular peripheral dependencies
nrf52840_peripherals.init();
@@ -175,6 +227,7 @@ pub unsafe fn reset_handler() {
// GPIOs
let gpio = components::gpio::GpioComponent::new(
board_kernel,
capsules::gpio::DRIVER_NUM,
components::gpio_component_helper!(
nrf52840::gpio::GPIOPin,
// left side of the USB plug. Right side is used for UART
@@ -189,6 +242,7 @@ pub unsafe fn reset_handler() {
let button = components::button::ButtonComponent::new(
board_kernel,
capsules::button::DRIVER_NUM,
components::button_component_helper!(
nrf52840::gpio::GPIOPin,
(
@@ -200,14 +254,11 @@ pub unsafe fn reset_handler() {
)
.finalize(components::button_component_buf!(nrf52840::gpio::GPIOPin));
let led = components::led::LedsComponent::new(components::led_component_helper!(
let led = components::led::LedsComponent::new().finalize(components::led_component_helper!(
LedLow<'static, nrf52840::gpio::GPIOPin>,
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_R_PIN]),
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_G_PIN]),
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_B_PIN]),
))
.finalize(components::led_component_buf!(
LedLow<'static, nrf52840::gpio::GPIOPin>
));
let chip = static_init!(
@@ -233,11 +284,15 @@ pub unsafe fn reset_handler() {
);
let rtc = &base_peripherals.rtc;
rtc.start();
let _ = rtc.start();
let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
.finalize(components::alarm_mux_component_helper!(nrf52840::rtc::Rtc));
let alarm = components::alarm::AlarmDriverComponent::new(board_kernel, mux_alarm)
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
let alarm = components::alarm::AlarmDriverComponent::new(
board_kernel,
capsules::alarm::DRIVER_NUM,
mux_alarm,
)
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
let uart_channel = UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD));
let channel = nrf52_components::UartChannelComponent::new(
uart_channel,
@@ -253,22 +308,40 @@ pub unsafe fn reset_handler() {
DynamicDeferredCall::new(dynamic_deferred_call_clients)
);
DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
let process_printer =
components::process_printer::ProcessPrinterTextComponent::new().finalize(());
PROCESS_PRINTER = Some(process_printer);
// Create a shared UART channel for the console and for kernel debug.
let uart_mux =
components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller)
.finalize(());
let pconsole =
components::process_console::ProcessConsoleComponent::new(board_kernel, uart_mux)
.finalize(());
let pconsole = components::process_console::ProcessConsoleComponent::new(
board_kernel,
uart_mux,
mux_alarm,
process_printer,
)
.finalize(components::process_console_component_helper!(
nrf52840::rtc::Rtc<'static>
));
// Setup the console.
let console = components::console::ConsoleComponent::new(board_kernel, uart_mux).finalize(());
let console = components::console::ConsoleComponent::new(
board_kernel,
capsules::console::DRIVER_NUM,
uart_mux,
)
.finalize(components::console_component_helper!());
// Create the debugger object that handles calls to `debug!()`.
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
let rng = components::rng::RngComponent::new(board_kernel, &base_peripherals.trng).finalize(());
let rng = components::rng::RngComponent::new(
board_kernel,
capsules::rng::DRIVER_NUM,
&base_peripherals.trng,
)
.finalize(());
// Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
// These are hardcoded pin assignments specified in the driver
@@ -278,6 +351,8 @@ pub unsafe fn reset_handler() {
nrf52840::acomp::Channel,
&nrf52840::acomp::CHANNEL_AC0
),
board_kernel,
capsules::analog_comparator::DRIVER_NUM,
)
.finalize(components::acomp_component_buf!(
nrf52840::acomp::Comparator
@@ -287,8 +362,9 @@ pub unsafe fn reset_handler() {
nrf52840::nvmc::SyscallDriver,
nrf52840::nvmc::SyscallDriver::new(
&base_peripherals.nvmc,
board_kernel.create_grant(&memory_allocation_capability),
board_kernel.create_grant(nrf52840::nvmc::DRIVER_NUM, &memory_allocation_capability),
dynamic_deferred_caller,
&mut APP_FLASH_BUFFER,
)
);
nvmc.set_deferred_handle(
@@ -300,16 +376,20 @@ pub unsafe fn reset_handler() {
// Configure USB controller
let usb = components::usb_ctap::UsbCtapComponent::new(
board_kernel,
capsules::usb::usb_ctap::DRIVER_NUM,
&nrf52840_peripherals.usbd,
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
VENDOR_ID,
PRODUCT_ID,
STRINGS,
)
.finalize(components::usb_ctap_component_buf!(nrf52840::usbd::Usbd));
.finalize(components::usb_ctap_component_helper!(nrf52840::usbd::Usbd));
nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
.finalize(components::rr_component_helper!(NUM_PROCS));
let platform = Platform {
button,
pconsole,
@@ -321,14 +401,20 @@ pub unsafe fn reset_handler() {
analog_comparator,
nvmc,
usb,
ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability),
ipc: kernel::ipc::IPC::new(
board_kernel,
kernel::ipc::DRIVER_NUM,
&memory_allocation_capability,
),
scheduler,
systick: cortexm4::systick::SysTick::new_with_calibration(64000000),
};
platform.pconsole.start();
let _ = platform.pconsole.start();
debug!("Initialization complete. Entering main loop\r");
debug!("{}", &nrf52840::ficr::FICR_INSTANCE);
/// These symbols are defined in the linker script.
// These symbols are defined in the linker script.
extern "C" {
/// Beginning of the ROM region containing app images.
static _sapps: u8;
@@ -340,7 +426,7 @@ pub unsafe fn reset_handler() {
static _eappmem: u8;
}
kernel::procs::load_processes(
kernel::process::load_processes(
board_kernel,
chip,
core::slice::from_raw_parts(
@@ -352,7 +438,7 @@ pub unsafe fn reset_handler() {
&_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
),
&mut PROCESSES,
FAULT_RESPONSE,
&FAULT_RESPONSE,
&process_management_capability,
)
.unwrap_or_else(|err| {
@@ -360,13 +446,5 @@ pub unsafe fn reset_handler() {
debug!("{:?}", err);
});
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
.finalize(components::rr_component_helper!(NUM_PROCS));
board_kernel.kernel_loop(
&platform,
chip,
Some(&platform.ipc),
scheduler,
&main_loop_capability,
);
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
}

View File

@@ -10,6 +10,7 @@ use nrf52840::gpio::Pin;
use crate::CHIP;
use crate::PROCESSES;
use crate::PROCESS_PRINTER;
enum Writer {
WriterUart(/* initialized */ bool),
@@ -48,7 +49,7 @@ impl IoWrite for Writer {
let uart = nrf52840::uart::Uarte::new();
if !*initialized {
*initialized = true;
uart.configure(uart::Parameters {
let _ = uart.configure(uart::Parameters {
baud_rate: 115200,
stop_bits: uart::StopBits::One,
parity: uart::Parity::None,
@@ -102,5 +103,6 @@ pub unsafe extern "C" fn panic_fmt(pi: &PanicInfo) -> ! {
&cortexm4::support::nop,
&PROCESSES,
&CHIP,
&PROCESS_PRINTER,
)
}

View File

@@ -64,14 +64,16 @@
// Disable this attribute when documenting, as a workaround for
// https://github.com/rust-lang/rust/issues/62184.
#![cfg_attr(not(doc), no_main)]
#![feature(const_in_array_repeat_expressions)]
#![deny(missing_docs)]
use capsules::virtual_alarm::VirtualMuxAlarm;
use core::env;
use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
use kernel::component::Component;
use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
use kernel::hil::led::LedLow;
use kernel::hil::time::Counter;
use kernel::platform::{KernelResources, SyscallDriverLookup, SyscallFilter};
use kernel::scheduler::round_robin::RoundRobinSched;
#[allow(unused_imports)]
use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
use nrf52840::gpio::Pin;
@@ -121,28 +123,33 @@ static STRINGS: &'static [&'static str] = &[
// State for loading and holding applications.
// How should the kernel respond when a process faults.
const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultResponse::Panic;
const FAULT_RESPONSE: kernel::process::PanicFaultPolicy = kernel::process::PanicFaultPolicy {};
// Number of concurrent processes this platform supports.
const NUM_PROCS: usize = 8;
static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; NUM_PROCS] =
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
[None; NUM_PROCS];
include!(concat!(env!("OUT_DIR"), "/locations.rs"));
static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
static mut PROCESS_PRINTER: Option<&'static kernel::process::ProcessPrinterText> = None;
/// Flash buffer for the custom nvmc driver
static mut APP_FLASH_BUFFER: [u8; 0x1000] = [0; 0x1000];
/// Dummy buffer that causes the linker to reserve enough space for the stack.
#[no_mangle]
#[link_section = ".stack_buffer"]
pub static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
pub static mut STACK_MEMORY: [u8; 0x2000] = [0; 0x2000];
/// Supported drivers by the platform
pub struct Platform {
button: &'static capsules::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
pconsole: &'static capsules::process_console::ProcessConsole<
'static,
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
components::process_console::Capability,
>,
console: &'static capsules::console::Console<'static>,
@@ -150,9 +157,10 @@ pub struct Platform {
led: &'static capsules::led::LedDriver<
'static,
kernel::hil::led::LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
4,
>,
rng: &'static capsules::rng::RngDriver<'static>,
ipc: kernel::ipc::IPC<NUM_PROCS>,
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
analog_comparator: &'static capsules::analog_comparator::AnalogComparator<
'static,
nrf52840::acomp::Comparator<'static>,
@@ -167,13 +175,14 @@ pub struct Platform {
'static,
nrf52840::usbd::Usbd<'static>,
>,
crp: &'static capsules::firmware_protection::FirmwareProtection<nrf52840::uicr::Uicr>,
scheduler: &'static RoundRobinSched<'static>,
systick: cortexm4::systick::SysTick,
}
impl kernel::Platform for Platform {
impl SyscallDriverLookup for Platform {
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
where
F: FnOnce(Option<&dyn kernel::Driver>) -> R,
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
{
match driver_num {
capsules::console::DRIVER_NUM => f(Some(self.console)),
@@ -185,44 +194,89 @@ impl kernel::Platform for Platform {
capsules::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
nrf52840::nvmc::DRIVER_NUM => f(Some(self.nvmc)),
capsules::usb::usb_ctap::DRIVER_NUM => f(Some(self.usb)),
capsules::firmware_protection::DRIVER_NUM => f(Some(self.crp)),
kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
_ => f(None),
}
}
}
impl SyscallFilter for Platform {
fn filter_syscall(
&self,
process: &dyn kernel::procs::ProcessType,
process: &dyn kernel::process::Process,
syscall: &kernel::syscall::Syscall,
) -> Result<(), kernel::ReturnCode> {
) -> Result<(), kernel::errorcode::ErrorCode> {
use kernel::syscall::Syscall;
match *syscall {
Syscall::COMMAND {
Syscall::Command {
driver_number: nrf52840::nvmc::DRIVER_NUM,
subdriver_number: cmd,
arg0: ptr,
arg1: len,
} if (cmd == 2 || cmd == 3) && !process.fits_in_storage_location(ptr, len) => {
Err(kernel::ReturnCode::EINVAL)
Err(kernel::ErrorCode::INVAL)
}
_ => Ok(()),
}
}
}
/// Entry point in the vector table called on hard reset.
#[no_mangle]
pub unsafe fn reset_handler() {
// Loads relocations and clears BSS
nrf52840::init();
let ppi = static_init!(nrf52840::ppi::Ppi, nrf52840::ppi::Ppi::new());
/// This is in a separate, inline(never) function so that its stack frame is
/// removed when this function returns. Otherwise, the stack space used for
/// these static_inits is wasted.
#[inline(never)]
unsafe fn get_peripherals() -> &'static mut Nrf52840DefaultPeripherals<'static> {
// Initialize chip peripheral drivers
let nrf52840_peripherals = static_init!(
Nrf52840DefaultPeripherals,
Nrf52840DefaultPeripherals::new(ppi)
Nrf52840DefaultPeripherals::new()
);
nrf52840_peripherals
}
impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
for Platform
{
type SyscallDriverLookup = Self;
type SyscallFilter = Self;
type ProcessFault = ();
type Scheduler = RoundRobinSched<'static>;
type SchedulerTimer = cortexm4::systick::SysTick;
type WatchDog = ();
type ContextSwitchCallback = ();
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
&self
}
fn syscall_filter(&self) -> &Self::SyscallFilter {
&self
}
fn process_fault(&self) -> &Self::ProcessFault {
&()
}
fn scheduler(&self) -> &Self::Scheduler {
self.scheduler
}
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
&self.systick
}
fn watchdog(&self) -> &Self::WatchDog {
&()
}
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
&()
}
}
/// Main function called after RAM initialized.
#[no_mangle]
pub unsafe fn main() {
// Loads relocations and clears BSS
nrf52840::init();
// Initialize chip peripheral drivers
let nrf52840_peripherals = get_peripherals();
// set up circular peripheral dependencies
nrf52840_peripherals.init();
let base_peripherals = &nrf52840_peripherals.nrf52;
@@ -249,6 +303,7 @@ pub unsafe fn reset_handler() {
let gpio = components::gpio::GpioComponent::new(
board_kernel,
capsules::gpio::DRIVER_NUM,
components::gpio_component_helper!(
nrf52840::gpio::GPIOPin,
0 => &nrf52840_peripherals.gpio_port[Pin::P1_01],
@@ -273,6 +328,7 @@ pub unsafe fn reset_handler() {
let button = components::button::ButtonComponent::new(
board_kernel,
capsules::button::DRIVER_NUM,
components::button_component_helper!(
nrf52840::gpio::GPIOPin,
(
@@ -299,15 +355,12 @@ pub unsafe fn reset_handler() {
)
.finalize(components::button_component_buf!(nrf52840::gpio::GPIOPin));
let led = components::led::LedsComponent::new(components::led_component_helper!(
let led = components::led::LedsComponent::new().finalize(components::led_component_helper!(
LedLow<'static, nrf52840::gpio::GPIOPin>,
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_PIN]),
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_PIN]),
LedLow::new(&nrf52840_peripherals.gpio_port[LED3_PIN]),
LedLow::new(&nrf52840_peripherals.gpio_port[LED4_PIN]),
))
.finalize(components::led_component_buf!(
LedLow<'static, nrf52840::gpio::GPIOPin>
));
let chip = static_init!(
@@ -339,11 +392,15 @@ pub unsafe fn reset_handler() {
);
let rtc = &base_peripherals.rtc;
rtc.start();
let _ = rtc.start();
let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
.finalize(components::alarm_mux_component_helper!(nrf52840::rtc::Rtc));
let alarm = components::alarm::AlarmDriverComponent::new(board_kernel, mux_alarm)
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
let alarm = components::alarm::AlarmDriverComponent::new(
board_kernel,
capsules::alarm::DRIVER_NUM,
mux_alarm,
)
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
let channel = nrf52_components::UartChannelComponent::new(
uart_channel,
@@ -359,22 +416,41 @@ pub unsafe fn reset_handler() {
DynamicDeferredCall::new(dynamic_deferred_call_clients)
);
DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
let process_printer =
components::process_printer::ProcessPrinterTextComponent::new().finalize(());
PROCESS_PRINTER = Some(process_printer);
// Create a shared UART channel for the console and for kernel debug.
let uart_mux =
components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller)
.finalize(());
let pconsole =
components::process_console::ProcessConsoleComponent::new(board_kernel, uart_mux)
.finalize(());
let pconsole = components::process_console::ProcessConsoleComponent::new(
board_kernel,
uart_mux,
mux_alarm,
process_printer,
)
.finalize(components::process_console_component_helper!(
nrf52840::rtc::Rtc<'static>
));
// Setup the console.
let console = components::console::ConsoleComponent::new(board_kernel, uart_mux).finalize(());
let console = components::console::ConsoleComponent::new(
board_kernel,
capsules::console::DRIVER_NUM,
uart_mux,
)
.finalize(components::console_component_helper!());
// Create the debugger object that handles calls to `debug!()`.
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
let rng = components::rng::RngComponent::new(board_kernel, &base_peripherals.trng).finalize(());
let rng = components::rng::RngComponent::new(
board_kernel,
capsules::rng::DRIVER_NUM,
&base_peripherals.trng,
)
.finalize(());
base_peripherals.spim0.configure(
nrf52840::pinmux::Pinmux::new(SPI_MOSI as u32),
@@ -390,6 +466,8 @@ pub unsafe fn reset_handler() {
nrf52840::acomp::Channel,
&nrf52840::acomp::CHANNEL_AC0
),
board_kernel,
capsules::analog_comparator::DRIVER_NUM,
)
.finalize(components::acomp_component_buf!(
nrf52840::acomp::Comparator
@@ -399,8 +477,9 @@ pub unsafe fn reset_handler() {
nrf52840::nvmc::SyscallDriver,
nrf52840::nvmc::SyscallDriver::new(
&base_peripherals.nvmc,
board_kernel.create_grant(&memory_allocation_capability),
board_kernel.create_grant(nrf52840::nvmc::DRIVER_NUM, &memory_allocation_capability),
dynamic_deferred_caller,
&mut APP_FLASH_BUFFER,
)
);
nvmc.set_deferred_handle(
@@ -412,21 +491,17 @@ pub unsafe fn reset_handler() {
// Configure USB controller
let usb = components::usb_ctap::UsbCtapComponent::new(
board_kernel,
capsules::usb::usb_ctap::DRIVER_NUM,
&nrf52840_peripherals.usbd,
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
VENDOR_ID,
PRODUCT_ID,
STRINGS,
)
.finalize(components::usb_ctap_component_buf!(nrf52840::usbd::Usbd));
.finalize(components::usb_ctap_component_helper!(nrf52840::usbd::Usbd));
let crp = components::firmware_protection::FirmwareProtectionComponent::new(
board_kernel,
nrf52840::uicr::Uicr::new(),
)
.finalize(components::firmware_protection_component_helper!(
nrf52840::uicr::Uicr
));
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
.finalize(components::rr_component_helper!(NUM_PROCS));
nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
@@ -441,15 +516,20 @@ pub unsafe fn reset_handler() {
analog_comparator,
nvmc,
usb,
crp,
ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability),
ipc: kernel::ipc::IPC::new(
board_kernel,
kernel::ipc::DRIVER_NUM,
&memory_allocation_capability,
),
scheduler,
systick: cortexm4::systick::SysTick::new_with_calibration(64000000),
};
platform.pconsole.start();
let _ = platform.pconsole.start();
debug!("Initialization complete. Entering main loop\r");
debug!("{}", &nrf52840::ficr::FICR_INSTANCE);
/// These symbols are defined in the linker script.
// These symbols are defined in the linker script.
extern "C" {
/// Beginning of the ROM region containing app images.
static _sapps: u8;
@@ -461,7 +541,7 @@ pub unsafe fn reset_handler() {
static _eappmem: u8;
}
kernel::procs::load_processes(
kernel::process::load_processes(
board_kernel,
chip,
core::slice::from_raw_parts(
@@ -473,7 +553,7 @@ pub unsafe fn reset_handler() {
&_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
),
&mut PROCESSES,
FAULT_RESPONSE,
&FAULT_RESPONSE,
&process_management_capability,
)
.unwrap_or_else(|err| {
@@ -481,13 +561,5 @@ pub unsafe fn reset_handler() {
debug!("{:?}", err);
});
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
.finalize(components::rr_component_helper!(NUM_PROCS));
board_kernel.kernel_loop(
&platform,
chip,
Some(&platform.ipc),
scheduler,
&main_loop_capability,
);
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
}

4
bootloader/Cargo.lock generated
View File

@@ -232,9 +232,9 @@ dependencies = [
[[package]]
name = "tock-registers"
version = "0.6.0"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f521a79accce68c417c9c77ce22108056b626126da1932f7e2e9b5bbffee0cea"
checksum = "4ee8fba06c1f4d0b396ef61a54530bb6b28f0dc61c38bc8bc5a5a48161e6282e"
[[package]]
name = "typenum"

View File

@@ -15,7 +15,7 @@ cortex-m-rt = "*"
cortex-m-rt-macros = "*"
panic-abort = "0.3.2"
rtt-target = { version = "*", features = ["cortex-m"] }
tock-registers = { version = "0.6.0", features = ["no_std_unit_tests"] }
tock-registers = "0.7.0"
[profile.dev]
panic = "abort"

View File

@@ -1,4 +1,4 @@
[toolchain]
channel = "nightly-2021-03-25"
channel = "nightly-2023-02-01"
components = ["clippy", "rustfmt"]
targets = ["thumbv7em-none-eabi"]

View File

@@ -26,6 +26,7 @@ use super::static_ref::StaticRef;
use core::cell::Cell;
#[cfg(debug_assertions)]
use rtt_target::rprintln;
use tock_registers::interfaces::{Readable, Writeable};
const SHA256_INIT_VALUE: [u32; 8] = [
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19,

View File

@@ -40,7 +40,7 @@ impl<T> Copy for StaticRef<T> {}
impl<T> Deref for StaticRef<T> {
type Target = T;
fn deref(&self) -> &'static T {
fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}

View File

@@ -24,7 +24,7 @@ use uuid::Uuid;
fn main() {
const UPGRADE_FILE: &str = "crypto_data/opensk_upgrade_pub.pem";
println!("cargo:rerun-if-changed=crypto_data/aaguid.txt");
println!("cargo:rerun-if-changed={}", UPGRADE_FILE);
println!("cargo:rerun-if-changed={UPGRADE_FILE}");
println!("cargo:rerun-if-changed=layout.ld");
println!("cargo:rerun-if-changed=nrf52840_layout.ld");
println!("cargo:rerun-if-changed=nrf52840_layout_a.ld");
@@ -33,7 +33,7 @@ fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let aaguid_bin_path = Path::new(&out_dir).join("opensk_aaguid.bin");
let mut aaguid_bin_file = File::create(&aaguid_bin_path).unwrap();
let mut aaguid_bin_file = File::create(aaguid_bin_path).unwrap();
let mut aaguid_txt_file = File::open("crypto_data/aaguid.txt").unwrap();
let mut content = String::new();
aaguid_txt_file.read_to_string(&mut content).unwrap();
@@ -52,6 +52,6 @@ fn main() {
.to_bytes(&group, conversion_form, &mut ctx)
.unwrap();
let upgrade_pubkey_path = Path::new(&out_dir).join("opensk_upgrade_pubkey.bin");
let mut upgrade_pub_bin_file = File::create(&upgrade_pubkey_path).unwrap();
let mut upgrade_pub_bin_file = File::create(upgrade_pubkey_path).unwrap();
upgrade_pub_bin_file.write_all(&raw_bytes).unwrap();
}

View File

@@ -158,7 +158,7 @@ SUPPORTED_BOARDS = {
# The following value must match the one used in the file
# `src/entry_point.rs`
APP_HEAP_SIZE = 90000
APP_HEAP_SIZE = 90_000
CARGO_TARGET_DIR = os.environ.get("CARGO_TARGET_DIR", "target")
@@ -502,14 +502,16 @@ class OpenSKInstaller:
elf2tab_ver = self.checked_command_output(
["elf2tab/bin/elf2tab", "--version"]).split(
"\n", maxsplit=1)[0]
if elf2tab_ver != "elf2tab 0.7.0":
error(("Detected unsupported elf2tab version {elf2tab_ver!a}. The "
"following commands may fail. Please use 0.7.0 instead."))
if elf2tab_ver != "elf2tab 0.10.2":
error(("Detected unsupported elf2tab version {elf2tab_ver!a}! The "
"following commands may fail. Please use 0.10.2 instead."))
os.makedirs(self.tab_folder, exist_ok=True)
tab_filename = os.path.join(self.tab_folder, f"{self.args.application}.tab")
supported_kernel = (2, 1)
elf2tab_args = [
"elf2tab/bin/elf2tab", "--deterministic", "--package-name",
self.args.application, "-o", tab_filename
self.args.application, f"--kernel-major={supported_kernel[0]}",
f"--kernel-minor={supported_kernel[1]}", "-o", tab_filename
]
if self.args.verbose_build:
elf2tab_args.append("--verbose")
@@ -527,10 +529,11 @@ class OpenSKInstaller:
stack_sizes.add(required_stack_size)
if len(stack_sizes) != 1:
error("Detected different stack sizes across tab files.")
# `protected-region-size` must match the `TBF_HEADER_SIZE`
# (currently 0x60 = 96 bytes)
elf2tab_args.extend([
f"--stack={stack_sizes.pop()}", f"--app-heap={APP_HEAP_SIZE}",
"--kernel-heap=1024", "--protected-region-size=64"
"--kernel-heap=1024", "--protected-region-size=96"
])
if self.args.elf2tab_output:
output = self.checked_command_output(elf2tab_args)
@@ -712,10 +715,11 @@ class OpenSKInstaller:
fatal(("It seems that the TAB file was not produced for the "
"architecture {board_props.arch}"))
app_hex = intelhex.IntelHex()
app_hex.frombytes(
app_tab.extract_app(board_props.arch).get_binary(
board_props.app_address),
offset=board_props.app_address)
tab_bytes = app_tab.extract_app(board_props.arch).get_binary(
board_props.app_address)
if tab_bytes is None:
fatal("The extracted bytes from the TAB file are none")
app_hex.frombytes(tab_bytes, offset=board_props.app_address)
final_hex.merge(app_hex)
info(f"Generating all-merged HEX file: {dest_file}")
final_hex.tofile(dest_file, format="hex")
@@ -990,7 +994,8 @@ if __name__ == "__main__":
dest="lock_device",
help=("Try to disable JTAG at the end of the operations. This "
"operation may fail if the device is already locked or if "
"the certificate/private key are not programmed."),
"the certificate/private key are not programmed."
"Currently not implemented on nrf52840 and always fails."),
)
main_parser.add_argument(
"--inject-certificate",

View File

@@ -12,24 +12,32 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate lang_items;
use libtock_drivers::console::{Console, BUFFER_SIZE};
use libtock_console::Console;
use libtock_drivers::result::FlexUnwrap;
use libtock_runtime::{set_main, stack_size, TockSyscalls};
libtock_core::stack_size! {0x800}
stack_size! {0x800}
set_main! {main}
type Syscalls = TockSyscalls;
fn main() {
// Write messages of length up to the console driver's buffer size.
let mut buf = [0; BUFFER_SIZE];
let mut buf = [0; 1024];
loop {
for i in 1..buf.len() {
for byte in buf.iter_mut().take(i) {
*byte = b'0' + ((i % 10) as u8);
}
buf[i] = b'\n';
Console::write_unbuffered(&mut buf[..(i + 1)]);
Console::<Syscalls>::write(&buf[..(i + 1)])
.map_err(|e| e.into())
.flex_unwrap();
}
}
}

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate alloc;
@@ -22,29 +23,28 @@ use alloc::vec::Vec;
use core::fmt::Write;
use crypto::{aes256, cbc, ecdsa, sha256, Hash256};
use ctap2::env::tock::TockRng;
use libtock_drivers::console::Console;
use libtock_console::{Console, ConsoleWriter};
use libtock_drivers::result::FlexUnwrap;
use libtock_drivers::timer;
use libtock_drivers::timer::{Timer, Timestamp};
use libtock_runtime::{set_main, stack_size, TockSyscalls};
libtock_core::stack_size! {0x800}
stack_size! {0x2000}
set_main! {main}
type Syscalls = TockSyscalls;
fn main() {
let mut console = Console::new();
let mut console = Console::<Syscalls>::writer();
// Setup the timer with a dummy callback (we only care about reading the current time, but the
// API forces us to set an alarm callback too).
let mut with_callback = timer::with_callback(|_, _| {});
let mut with_callback = timer::with_callback(|_| {});
let timer = with_callback.init().flex_unwrap();
let mut rng = TockRng {};
let mut rng = TockRng::<Syscalls>::default();
writeln!(console, "****************************************").unwrap();
writeln!(
console,
"Clock frequency: {} Hz",
timer.clock_frequency().hz()
)
.unwrap();
writeln!(console, "Clock frequency: {:?} Hz", timer.clock_frequency()).unwrap();
// AES
bench(&mut console, &timer, "aes256::EncryptionKey::new", || {
@@ -150,7 +150,7 @@ fn main() {
writeln!(console, "****************************************").unwrap();
}
fn bench<F>(console: &mut Console, timer: &Timer, title: &str, mut f: F)
fn bench<F>(console: &mut ConsoleWriter<Syscalls>, timer: &Timer<Syscalls>, title: &str, mut f: F)
where
F: FnMut(),
{
@@ -159,11 +159,13 @@ where
writeln!(console, "----------------------------------------").unwrap();
let mut count = 1;
for _ in 0..30 {
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
let start =
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
for _ in 0..count {
f();
}
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
let end =
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
let elapsed = (end - start).ms();
writeln!(
console,
@@ -173,7 +175,6 @@ where
elapsed / (count as f64)
)
.unwrap();
console.flush();
if elapsed > 1000.0 {
break;
}

View File

@@ -12,18 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate lang_items;
use core::fmt::Write;
use ctap2::env::tock::take_storage;
use libtock_drivers::console::Console;
use libtock_drivers::led;
use libtock_console::Console;
use libtock_drivers::result::FlexUnwrap;
use libtock_leds::Leds;
use libtock_platform as platform;
use libtock_runtime::{set_main, stack_size, TockSyscalls};
use persistent_store::{Storage, StorageIndex};
use platform::DefaultConfig;
libtock_core::stack_size! {0x800}
stack_size! {0x800}
set_main! {main}
type Syscalls = TockSyscalls;
fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
let index = StorageIndex { page, byte: 0 };
@@ -36,20 +43,21 @@ fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
}
fn main() {
led::get(1).flex_unwrap().on().flex_unwrap(); // red on dongle
let mut storage = take_storage().unwrap();
Leds::<Syscalls>::on(1).map_err(|e| e.into()).flex_unwrap(); // red on dongle
let mut storage = take_storage::<Syscalls, DefaultConfig>().unwrap();
let num_pages = storage.num_pages();
writeln!(Console::new(), "Erase {} pages of storage:", num_pages).unwrap();
let mut console = Console::<Syscalls>::writer();
writeln!(console, "Erase {} pages of storage:", num_pages).unwrap();
for page in 0..num_pages {
write!(Console::new(), "- Page {} ", page).unwrap();
write!(console, "- Page {} ", page).unwrap();
if is_page_erased(&storage, page) {
writeln!(Console::new(), "skipped (was already erased).").unwrap();
writeln!(console, "skipped (was already erased).").unwrap();
} else {
storage.erase_page(page).unwrap();
writeln!(Console::new(), "erased.").unwrap();
writeln!(console, "erased.").unwrap();
}
}
writeln!(Console::new(), "Done.").unwrap();
led::get(1).flex_unwrap().off().flex_unwrap();
led::get(0).flex_unwrap().on().flex_unwrap(); // green on dongle
writeln!(console, "Done.").unwrap();
Leds::<Syscalls>::on(1).map_err(|e| e.into()).flex_unwrap();
Leds::<Syscalls>::off(0).map_err(|e| e.into()).flex_unwrap(); // green on dongle
}

View File

@@ -1,3 +1,4 @@
#![no_main]
#![no_std]
extern crate alloc;
@@ -5,27 +6,32 @@ extern crate lang_items;
extern crate libtock_drivers;
use core::fmt::Write;
use libtock_drivers::console::Console;
use libtock_console::{Console, ConsoleWriter};
use libtock_runtime::{set_main, stack_size, TockSyscalls};
libtock_core::stack_size! {0x4000}
stack_size! {0x4000}
set_main! {main}
type Syscalls = TockSyscalls;
#[cfg(not(feature = "with_nfc"))]
mod example {
use super::{Console, Write};
use super::{ConsoleWriter, Syscalls, Write};
pub fn nfc(console: &mut Console) {
pub fn nfc(console: &mut ConsoleWriter<Syscalls>) {
writeln!(console, "NFC feature flag is missing!").unwrap();
}
}
#[cfg(feature = "with_nfc")]
mod example {
use super::{Console, Write};
use libtock_core::result::CommandError;
use super::{Console, ConsoleWriter, Write};
use crate::Syscalls;
use libtock_drivers::nfc::{NfcTag, RecvOp};
use libtock_drivers::result::{FlexUnwrap, TockError};
use libtock_drivers::timer;
use libtock_drivers::timer::{Timer, Timestamp};
use libtock_platform::{DefaultConfig, ErrorCode};
#[derive(Copy, Clone, Debug, PartialEq)]
#[allow(clippy::upper_case_acronyms)]
@@ -48,16 +54,15 @@ mod example {
ENOSUPPORT,
}
impl From<isize> for ReturnCode {
fn from(original: isize) -> ReturnCode {
impl From<ErrorCode> for ReturnCode {
fn from(original: ErrorCode) -> ReturnCode {
match original {
0 => ReturnCode::SUCCESS,
-1 => ReturnCode::FAIL,
-2 => ReturnCode::EBUSY,
-4 => ReturnCode::EOFF,
-6 => ReturnCode::EINVAL,
-8 => ReturnCode::ECANCEL,
-9 => ReturnCode::ENOMEM,
ErrorCode::Fail => ReturnCode::FAIL,
ErrorCode::Busy => ReturnCode::EBUSY,
ErrorCode::Off => ReturnCode::EOFF,
ErrorCode::Invalid => ReturnCode::EINVAL,
ErrorCode::Cancel => ReturnCode::ECANCEL,
ErrorCode::NoMem => ReturnCode::ENOMEM,
_ => ReturnCode::ENOSUPPORT,
}
}
@@ -66,34 +71,32 @@ mod example {
/// Helper function to write on console the received packet.
fn print_rx_buffer(buf: &mut [u8]) {
if let Some((last, bytes)) = buf.split_last() {
let mut console = Console::new();
let mut console = Console::<Syscalls>::writer();
write!(console, "RX:").unwrap();
for byte in bytes {
write!(console, " {:02x?}", byte).unwrap();
}
writeln!(console, " {:02x?}", last).unwrap();
console.flush();
}
}
/// Function to identify the time elapsed for a transmission request.
fn bench_transmit(
console: &mut Console,
timer: &Timer,
console: &mut ConsoleWriter<Syscalls>,
timer: &Timer<Syscalls>,
title: &str,
mut buf: &mut [u8],
buf: &mut [u8],
) -> ReturnCode {
let amount = buf.len();
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
match NfcTag::transmit(&mut buf, amount) {
let start =
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
match NfcTag::<Syscalls, DefaultConfig>::transmit(buf, amount as u32) {
Ok(_) => (),
Err(TockError::Command(CommandError {
return_code: -8, /* ECANCEL: No Field*/
..
})) => return ReturnCode::ECANCEL,
Err(_) => writeln!(Console::new(), " -- tx error!").unwrap(),
Err(TockError::Command(ErrorCode::Cancel)) => return ReturnCode::ECANCEL,
Err(_) => writeln!(console, " -- tx error!").unwrap(),
}
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
let end =
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
let elapsed = (end - start).ms();
writeln!(
console,
@@ -104,21 +107,21 @@ mod example {
(amount as f64) / elapsed * 8.
)
.unwrap();
console.flush();
ReturnCode::SUCCESS
}
fn receive_packet(console: &mut Console, mut buf: &mut [u8; 256]) -> ReturnCode {
match NfcTag::receive(&mut buf) {
fn receive_packet(console: &mut ConsoleWriter<Syscalls>, buf: &mut [u8; 256]) -> ReturnCode {
match NfcTag::<Syscalls, DefaultConfig>::receive(buf) {
Ok(RecvOp {
recv_amount: amount,
..
}) => {
if amount <= buf.len() {
print_rx_buffer(&mut buf[..amount]);
if amount <= buf.len() as u32 {
print_rx_buffer(&mut buf[..amount as usize]);
}
}
Err(TockError::Command(CommandError { return_code, .. })) => return return_code.into(),
Err(TockError::Command(code)) => return code.into(),
Err(_) => {
writeln!(console, " -- RX Err").unwrap();
return ReturnCode::ECANCEL;
@@ -127,54 +130,58 @@ mod example {
ReturnCode::SUCCESS
}
fn transmit_reply(mut console: &mut Console, timer: &Timer, buf: &[u8]) -> ReturnCode {
fn transmit_reply(
console: &mut ConsoleWriter<Syscalls>,
timer: &Timer<Syscalls>,
buf: &[u8],
) -> ReturnCode {
let mut return_code = ReturnCode::SUCCESS;
match buf[0] {
0xe0 /* RATS */=> {
let mut answer_to_select = [0x05, 0x78, 0x80, 0xB1, 0x00];
return_code = bench_transmit(&mut console, &timer, "TX: ATS", &mut answer_to_select);
return_code = bench_transmit(console, timer, "TX: ATS", &mut answer_to_select);
}
0xc2 /* DESELECT */ => {
// Ignore the request
let mut command_error = [0x6A, 0x81];
return_code = bench_transmit(&mut console, &timer, "TX: DESELECT", &mut command_error);
return_code = bench_transmit(console, timer, "TX: DESELECT", &mut command_error);
}
0x02 | 0x03 /* APDU Prefix */ => match buf[2] {
// If the received packet is applet selection command (FIDO 2)
0xa4 /* SELECT */ => if buf[3] == 0x04 && buf[5] == 0x08 && buf[6] == 0xa0 {
// Vesion: "FIDO_2_0"
let mut reply = [buf[0], 0x46, 0x49, 0x44, 0x4f, 0x5f, 0x32, 0x5f, 0x30, 0x90, 0x00,];
return_code = bench_transmit(&mut console, &timer, "TX: Version Str", &mut reply);
return_code = bench_transmit(console, timer, "TX: Version Str", &mut reply);
} else if (buf[6] == 0xd2 && buf[7] == 0x76) || (buf[6] == 0xe1 && (buf[7] == 0x03 || buf[7] == 0x04)){
let mut reply = [buf[0], 0x90, 0x00];
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
} else /* Unknown file */ {
let mut reply = [buf[0], 0x6a, 0x82];
return_code = bench_transmit(&mut console, &timer, "TX: 0x6A82", &mut reply);
return_code = bench_transmit(console, timer, "TX: 0x6A82", &mut reply);
}
0xb0 /* READ */ => match buf[5] {
0x02 => {
let mut reply = [buf[0], 0x12, 0x90, 0x00,];
return_code = bench_transmit(&mut console, &timer, "TX: File Size", &mut reply);
return_code = bench_transmit(console, timer, "TX: File Size", &mut reply);
}
0x12 => {
let mut reply = [buf[0], 0xd1, 0x01, 0x0e, 0x55, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65,
0x6e, 0x73, 0x6b, 0x2e, 0x64, 0x65, 0x76, 0x90, 0x00,];
return_code = bench_transmit(&mut console, &timer, "TX: NDEF", &mut reply);
return_code = bench_transmit(console, timer, "TX: NDEF", &mut reply);
}
0x0f => {
let mut reply = [buf[0], 0x00, 0x0f, 0x20, 0x00, 0x7f, 0x00, 0x7f, 0x04, 0x06, 0xe1, 0x04,
0x00, 0x7f, 0x00, 0x00, 0x90, 0x00,];
return_code = bench_transmit(&mut console, &timer, "TX: CC", &mut reply);
return_code = bench_transmit(console, timer, "TX: CC", &mut reply);
}
_ => {
let mut reply = [buf[0], 0x90, 0x00];
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
}
}
_ => {
let mut reply = [buf[0], 0x90, 0x00];
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
}
}
0x26 | 0x52 | 0x50 /* REQA | WUPA | Halt */ => {
@@ -185,31 +192,27 @@ mod example {
return_code
}
pub fn nfc(mut console: &mut Console) {
pub fn nfc(console: &mut ConsoleWriter<Syscalls>) {
// Setup the timer with a dummy callback (we only care about reading the current time, but the
// API forces us to set an alarm callback too).
let mut with_callback = timer::with_callback(|_, _| {});
let mut with_callback = timer::with_callback(|_| {});
let timer = with_callback.init().flex_unwrap();
writeln!(
console,
"Clock frequency: {} Hz",
timer.clock_frequency().hz()
)
.unwrap();
writeln!(console, "Clock frequency: {:?} Hz", timer.clock_frequency()).unwrap();
let mut state_change_counter = 0;
loop {
let mut rx_buf = [0; 256];
match receive_packet(&mut console, &mut rx_buf) {
match receive_packet(console, &mut rx_buf) {
ReturnCode::EOFF => {
// Not configured
while !NfcTag::enable_emulation() {}
while !NfcTag::<Syscalls, DefaultConfig>::enable_emulation() {}
// Configure Type 4 tag
while !NfcTag::configure(4) {}
while !NfcTag::<Syscalls, DefaultConfig>::configure(4) {}
}
ReturnCode::ECANCEL /* field lost */ => {
NfcTag::disable_emulation();
NfcTag::<Syscalls, DefaultConfig>::disable_emulation();
}
ReturnCode::EBUSY /* awaiting select*/ => (),
ReturnCode::ENOMEM => {
@@ -220,9 +223,9 @@ mod example {
ReturnCode::ENOSUPPORT => (),
ReturnCode::SUCCESS => {
// If the reader restarts the communication then disable the tag.
match transmit_reply(&mut console, &timer, &rx_buf) {
match transmit_reply(console, &timer, &rx_buf) {
ReturnCode::ECANCEL | ReturnCode::EOFF => {
if NfcTag::disable_emulation() {
if NfcTag::<Syscalls, DefaultConfig>::disable_emulation() {
writeln!(console, " -- TAG DISABLED").unwrap();
}
state_change_counter += 1;
@@ -239,7 +242,7 @@ mod example {
}
fn main() {
let mut console = Console::new();
let mut console = Console::<Syscalls>::writer();
writeln!(console, "****************************************").unwrap();
writeln!(console, "nfct_test application is installed").unwrap();
example::nfc(&mut console);

View File

@@ -12,24 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate alloc;
extern crate lang_items;
libtock_core::stack_size! {0x800}
use alloc::vec::Vec;
use core::fmt::Write;
use libtock_drivers::console::Console;
use libtock_console::Console;
use libtock_runtime::{set_main, stack_size, TockSyscalls};
stack_size! {0x800}
set_main! {main}
type Syscalls = TockSyscalls;
fn main() {
writeln!(Console::new(), "****************************************").unwrap();
let mut console = Console::<Syscalls>::writer();
writeln!(console, "****************************************").unwrap();
for i in 0.. {
writeln!(Console::new(), "Allocating {} bytes...", 1 << i).unwrap();
writeln!(console, "Allocating {} bytes...", 1 << i).unwrap();
let x: Vec<u8> = Vec::with_capacity(1 << i);
writeln!(Console::new(), "Allocated!").unwrap();
writeln!(console, "Allocated!").unwrap();
drop(x);
writeln!(Console::new(), "Dropped!").unwrap();
writeln!(console, "Dropped!").unwrap();
}
}

View File

@@ -12,11 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate lang_items;
libtock_core::stack_size! {0x800}
use libtock_runtime::{set_main, stack_size};
stack_size! {0x800}
set_main! {main}
fn main() {
panic!("Bye world!")

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#![no_main]
#![no_std]
extern crate alloc;
@@ -22,25 +23,33 @@ use alloc::vec::Vec;
use alloc::{format, vec};
use core::fmt::Write;
use ctap2::env::tock::{take_storage, Storage};
use libtock_drivers::console::Console;
use libtock_console::Console;
use libtock_drivers::result::FlexUnwrap;
use libtock_drivers::timer::{self, Duration, Timer, Timestamp};
use persistent_store::Store;
use libtock_platform::DefaultConfig;
use libtock_runtime::{set_main, stack_size, TockSyscalls};
use persistent_store::{Storage as _, Store};
libtock_core::stack_size! {0x2000}
stack_size! {0x800}
set_main! {main}
fn timestamp(timer: &Timer) -> Timestamp<f64> {
Timestamp::<f64>::from_clock_value(timer.get_current_clock().ok().unwrap())
type Syscalls = TockSyscalls;
fn timestamp(timer: &Timer<Syscalls>) -> Timestamp<f64> {
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().ok().unwrap())
}
fn measure<T>(timer: &Timer, operation: impl FnOnce() -> T) -> (T, Duration<f64>) {
fn measure<T>(timer: &Timer<Syscalls>, operation: impl FnOnce() -> T) -> (T, Duration<f64>) {
let before = timestamp(timer);
let result = operation();
let after = timestamp(timer);
(result, after - before)
}
fn boot_store(mut storage: Storage, erase: bool) -> Store<Storage> {
use persistent_store::Storage;
fn boot_store(
mut storage: Storage<Syscalls, DefaultConfig>,
erase: bool,
) -> Store<Storage<Syscalls, DefaultConfig>> {
let num_pages = storage.num_pages();
if erase {
for page in 0..num_pages {
@@ -55,8 +64,7 @@ struct StorageConfig {
num_pages: usize,
}
fn storage_config(storage: &Storage) -> StorageConfig {
use persistent_store::Storage;
fn storage_config(storage: &Storage<Syscalls, DefaultConfig>) -> StorageConfig {
StorageConfig {
num_pages: storage.num_pages(),
}
@@ -73,19 +81,19 @@ struct Stat {
}
fn compute_latency(
storage: Storage,
timer: &Timer,
storage: Storage<Syscalls, DefaultConfig>,
timer: &Timer<Syscalls>,
num_pages: usize,
key_increment: usize,
word_length: usize,
) -> (Storage, Stat) {
) -> (Storage<Syscalls, DefaultConfig>, Stat) {
let mut stat = Stat {
key_increment,
entry_length: word_length,
..Default::default()
};
let mut console = Console::new();
let mut console = Console::<Syscalls>::writer();
writeln!(
console,
"\nLatency for key_increment={} word_length={}.",
@@ -155,20 +163,22 @@ fn compute_latency(
}
fn main() {
let mut with_callback = timer::with_callback(|_, _| {});
let timer = with_callback.init().ok().unwrap();
let storage = take_storage().unwrap();
let mut with_callback = timer::with_callback::<Syscalls, DefaultConfig, _>(|_| {});
let timer = with_callback.init().flex_unwrap();
let storage = take_storage::<Syscalls, DefaultConfig>().unwrap();
let config = storage_config(&storage);
let mut stats = Vec::new();
let mut console = Console::<Syscalls>::writer();
writeln!(Console::new(), "\nRunning 2 tests...").unwrap();
writeln!(console, "\nRunning 2 tests...").unwrap();
// Simulate a store full of credentials (of 50 words).
let (storage, stat) = compute_latency(storage, &timer, config.num_pages, 1, 50);
stats.push(stat);
// Simulate a store full of increments of a single counter.
let (_storage, stat) = compute_latency(storage, &timer, config.num_pages, 0, 1);
stats.push(stat);
writeln!(Console::new(), "\nDone.\n").unwrap();
writeln!(console, "\nDone.\n").unwrap();
const HEADERS: &[&str] = &[
"Overwrite",
@@ -189,8 +199,8 @@ fn main() {
format!("{:.1} ms", stat.remove_ms),
]);
}
writeln!(Console::new(), "Copy to examples/store_latency.rs:\n").unwrap();
writeln!(Console::new(), "{:?}", config).unwrap();
writeln!(console, "Copy to examples/store_latency.rs:\n").unwrap();
writeln!(console, "{:?}", config).unwrap();
write_matrix(matrix);
// Results for nrf52840dk_opensk:
@@ -201,10 +211,11 @@ fn main() {
}
fn align(x: &str, n: usize) {
let mut console = Console::<Syscalls>::writer();
for _ in 0..n.saturating_sub(x.len()) {
write!(Console::new(), " ").unwrap();
write!(console, " ").unwrap();
}
write!(Console::new(), "{}", x).unwrap();
write!(console, "{}", x).unwrap();
}
fn write_matrix(mut m: Vec<Vec<String>>) {
@@ -223,6 +234,6 @@ fn write_matrix(mut m: Vec<Vec<String>>) {
for col in 0..num_cols {
align(&row[col], col_len[col] + 2 * (col > 0) as usize);
}
writeln!(Console::new()).unwrap();
writeln!(Console::<Syscalls>::writer()).unwrap();
}
}

158
layout.ld
View File

@@ -1,158 +0,0 @@
/* Userland Generic Layout
*
* Currently, due to incomplete ROPI-RWPI support in rustc (see
* https://github.com/tock/libtock-rs/issues/28), this layout implements static
* linking. An application init script must define the FLASH and SRAM address
* ranges as well as MPU_MIN_ALIGN before including this layout file.
*
* Here is a an example application linker script to get started:
* MEMORY {
* /* FLASH memory region must start immediately *after* the Tock
* * Binary Format headers, which means you need to offset the
* * beginning of FLASH memory region relative to where the
* * application is loaded.
* FLASH (rx) : ORIGIN = 0x10030, LENGTH = 0x0FFD0
* SRAM (RWX) : ORIGIN = 0x20000, LENGTH = 0x10000
* }
* MPU_MIN_ALIGN = 8K;
* INCLUDE ../libtock-rs/layout.ld
*/
ENTRY(_start)
SECTIONS {
/* Section for just the app crt0 header.
* This must be first so that the app can find it.
*/
/* Runtime setup logic. The crt0_header symbol is used by the entry point
* assembly. We have to include start here rather than .text because
* otherwise elf2tab fails to recognize that the process binary's flash
* region should start at the beginning of .start.
*/
.start :
{
_beginning = .; /* Start of the app in flash. */
crt0_header = .;
/**
* Populate the header expected by `crt0`:
*
* struct hdr {
* uint32_t got_sym_start;
* uint32_t got_start;
* uint32_t got_size;
* uint32_t data_sym_start;
* uint32_t data_start;
* uint32_t data_size;
* uint32_t bss_start;
* uint32_t bss_size;
* uint32_t reldata_start;
* uint32_t stack_size;
* };
*/
/* Offset of GOT symbols in flash */
LONG(LOADADDR(.got) - _beginning);
/* Offset of GOT section in memory */
LONG(_got);
/* Size of GOT section */
LONG(SIZEOF(.got));
/* Offset of data symbols in flash */
LONG(LOADADDR(.data) - _beginning);
/* Offset of data section in memory */
LONG(_data);
/* Size of data section */
LONG(SIZEOF(.data));
/* Offset of BSS section in memory */
LONG(_bss);
/* Size of BSS section */
LONG(SIZEOF(.bss));
/* First address offset after program flash, where elf2tab places
* .rel.data section */
LONG(LOADADDR(.endflash) - _beginning);
/* The size of the stack requested by this application */
LONG(_stack_top_aligned - _sstack);
/* Pad the header out to a multiple of 32 bytes so there is not a gap
* between the header and subsequent .data section. It's unclear why,
* but LLD is aligning sections to a multiple of 32 bytes. */
. = ALIGN(32);
*(.start)
} > FLASH =0xFFFFFFFF
/* Text section, Code! */
.text :
{
. = ALIGN(4);
_text = .;
*(.text*)
*(.rodata*)
KEEP (*(.syscalls))
*(.ARM.extab*)
. = ALIGN(4); /* Make sure we're word-aligned here */
_etext = .;
} > FLASH =0xFFFFFFFF
/* Application stack */
.stack (NOLOAD) :
{
/* elf2tab requires that the `_sram_origin` symbol be present to
* mark the first address in the SRAM memory. Since ELF files do
* not really need to specify this address as they only care about
* loading into flash, we need to manually mark this address for
* elf2tab. elf2tab will use it to add a fixed address header in the
* TBF header if needed.
*/
_sram_origin = .;
_sstack = .;
KEEP(*(.stack_buffer))
_stack_top_unaligned = .;
. = ALIGN(8);
_stack_top_aligned = .;
} > SRAM
/* Data section, static initialized variables
* Note: This is placed in Flash after the text section, but needs to be
* moved to SRAM at runtime
*/
.data : AT (_etext)
{
. = ALIGN(4); /* Make sure we're word-aligned here */
_data = .;
KEEP(*(.data*))
*(.sdata*) /* RISC-V small-pointer data section */
. = ALIGN(4); /* Make sure we're word-aligned at the end of flash */
} > SRAM
/* Global Offset Table */
.got :
{
. = ALIGN(4); /* Make sure we're word-aligned here */
_got = .;
*(.got*)
*(.got.plt*)
. = ALIGN(4);
} > SRAM
/* BSS section, static uninitialized variables */
.bss :
{
. = ALIGN(4); /* Make sure we're word-aligned here */
_bss = .;
KEEP(*(.bss* .sbss*))
*(COMMON)
. = ALIGN(4);
} > SRAM
/* End of flash. */
.endflash :
{
} > FLASH
/* Sections we do not need. */
/DISCARD/ :
{
*(.ARM.exidx .eh_frame)
}
}
ASSERT((_stack_top_aligned - _stack_top_unaligned) == 0, "
STACK_SIZE must be 8 byte multiple")

View File

@@ -220,7 +220,7 @@ fn send_keepalive_up_needed<E: Env>(
match ctap_hid_connection.send_and_maybe_recv(&mut pkt, timeout_ms) {
Ok(SendOrRecvStatus::Timeout) => {
debug_ctap!(env, "Sending a KEEPALIVE packet timed out");
// TODO: abort user presence test?
// The client is likely unresponsive, but let's retry.
}
Err(_) => panic!("Error sending KEEPALIVE packet"),
Ok(SendOrRecvStatus::Sent) => {

137
libtock_layout.ld Normal file
View File

@@ -0,0 +1,137 @@
/* Layout file for Tock process binaries that use libtock-rs. This currently
* implements static linking, because we do not have a working
* position-independent relocation solution. This layout works for all
* platforms libtock-rs supports (ARM and RISC-V).
*
* This layout should be included by a script that defines the FLASH and RAM
* regions for the board as well as TBF_HEADER_SIZE. Here is a an example
* process binary linker script to get started:
* MEMORY {
* FLASH (X) : ORIGIN = 0x10000, LENGTH = 0x10000
* RAM (W) : ORIGIN = 0x20000, LENGTH = 0x10000
* }
* TBF_HEADER_SIZE = 0x60;
* INCLUDE ../libtock-rs/layout.ld
*
* FLASH refers to the area the process binary occupies in flash, including TBF
* headers. RAM refers to the area the process will have access to in memory.
* STACK_SIZE is the size of the process' stack (this layout file may round the
* stack size up for alignment purposes). TBF_HEADER_SIZE must correspond to the
* --protected-region-size flag passed to elf2tab.
*
* This places the flash sections in the following order:
* 1. .rt_header -- Constants used by runtime initialization.
* 2. .text -- Executable code.
* 3. .rodata -- Read-only global data (e.g. most string constants).
* 4. .data -- Read-write data, copied to RAM at runtime.
*
* This places the RAM sections in the following order:
* 1. .stack -- The stack grows downward. Putting it first gives us
* MPU-based overflow detection.
* 2. .data -- Read-write data, initialized by copying from flash.
* 3. .bss -- Zero-initialized read-write global data.
* 4. Heap -- The heap (optional) comes after .bss and grows upwards to
* the process break.
*/
/* TODO: Should TBF_HEADER_SIZE be configured via a similar mechanism to the
* stack size? We should see if that is possible.
*/
/* GNU LD looks for `start` as an entry point by default, while LLVM's LLD looks
* for `_start`. To be compatible with both, we manually specify an entry point.
*/
ENTRY(start)
SECTIONS {
/* Sections located in FLASH at runtime.
*/
/* Add a section where elf2tab will place the TBF headers, so that the rest
* of the FLASH sections are in the right locations. */
.tbf_header (NOLOAD) : {
. = . + TBF_HEADER_SIZE;
} > FLASH
/* Runtime header. Contains values the linker knows that the runtime needs
* to look up.
*/
.start ALIGN(4) : {
/* We combine rt_header and _start into a single section. If we don't,
* elf2tab does not parse the ELF file correctly for unknown reasons.
*/
rt_header = .;
LONG(start & 0xFFFFFFFE); /* .start w/ Thumb bit unset */
LONG(ADDR(.bss) + SIZEOF(.bss)); /* Initial process break */
LONG(_stack_top);
LONG(SIZEOF(.data));
LONG(LOADADDR(.data));
LONG(ADDR(.data));
LONG(SIZEOF(.bss));
LONG(ADDR(.bss));
*(.start)
} > FLASH
/* Text section -- the application's code. */
.text ALIGN(4) : {
*(.text.*)
} > FLASH
/* Read-only data section. Contains strings and other global constants. */
.rodata ALIGN(4) : {
*(.rodata.*)
/* .data is placed after .rodata in flash. data_flash_start is used by
* AT() to place .data in flash as well as in rt_header.
*/
_data_flash_start = .;
} > FLASH
/* Sections located in RAM at runtime.
*/
/* Reserve space for the stack. Aligned to a multiple of 16 bytes for the
* RISC-V calling convention:
* https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
*/
.stack (NOLOAD) : {
/* _sram_origin is used by elf2tab:
* https://github.com/tock/elf2tab/blob/master/src/main.rs#L301
*/
_sram_origin = .;
KEEP(*(.stack_buffer))
. = ALIGN(16);
_stack_top = .; /* Used in rt_header */
} > RAM
/* Read-write data section. This is deployed as part of FLASH but is copied
* into RAM at runtime.
*/
.data ALIGN(4) : AT(_data_flash_start) {
data_ram_start = .;
/* .sdata is the RISC-V small data section */
*(.sdata .data)
/* Pad to word alignment so the relocation loop can use word-sized
* copies.
*/
. = ALIGN(4);
} > RAM
/* BSS section. These are zero-initialized static variables. This section is
* not copied from FLASH into RAM but rather directly initialized, and is
* mainly put in this linker script so that we get an error if it overflows
* the RAM region.
*/
.bss ALIGN(4) (NOLOAD) : {
/* .sbss is the RISC-V small data section */
*(.sbss .bss.*)
} > RAM
_heap_start = ADDR(.bss) + SIZEOF(.bss); /* Used by rt_header */
/* Sections we do not need. */
/DISCARD/ :
{
*(.ARM.exidx .eh_frame)
}
}

View File

@@ -6,16 +6,9 @@ MEMORY {
/* The application region is 64 bytes (0x40) and we reserve 0x40000 at the end
* of the flash for the persistent storage.
*/
FLASH (rx) : ORIGIN = 0x00040040, LENGTH = 0x0007FFC0
SRAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K
FLASH (X) : ORIGIN = 0x00040040, LENGTH = 0x0007FFC0
RAM (W) : ORIGIN = 0x20020000, LENGTH = 128K
}
/*
* Any change to STACK_SIZE should be accompanied by a corresponding change to
* `elf2tab`'s `--stack` option
*/
STACK_SIZE = 16384;
MPU_MIN_ALIGN = 8K;
INCLUDE layout.ld
TBF_HEADER_SIZE = 0x60;
INCLUDE libtock_layout.ld

View File

@@ -6,16 +6,9 @@ MEMORY {
/* The application region is 64 bytes (0x40) and we reserve 0x40000 at the end
* of the flash for the persistent storage.
*/
FLASH (rx) : ORIGIN = 0x00040040, LENGTH = 0x0001FFC0
SRAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K
FLASH (X) : ORIGIN = 0x00040040, LENGTH = 0x0001FFC0
RAM (W) : ORIGIN = 0x20020000, LENGTH = 128K
}
/*
* Any change to STACK_SIZE should be accompanied by a corresponding change to
* `elf2tab`'s `--stack` option
*/
STACK_SIZE = 16384;
MPU_MIN_ALIGN = 8K;
INCLUDE layout.ld
TBF_HEADER_SIZE = 0x60;
INCLUDE libtock_layout.ld

View File

@@ -6,16 +6,9 @@ MEMORY {
/* The application region is 64 bytes (0x40) and we reserve 0x40000 at the end
* of the flash for the persistent storage.
*/
FLASH (rx) : ORIGIN = 0x00080040, LENGTH = 0x0001FFC0
SRAM (rwx) : ORIGIN = 0x20020000, LENGTH = 128K
FLASH (X) : ORIGIN = 0x00080040, LENGTH = 0x0001FFC0
RAM (W) : ORIGIN = 0x20020000, LENGTH = 128K
}
/*
* Any change to STACK_SIZE should be accompanied by a corresponding change to
* `elf2tab`'s `--stack` option
*/
STACK_SIZE = 16384;
MPU_MIN_ALIGN = 8K;
INCLUDE layout.ld
TBF_HEADER_SIZE = 0x60;
INCLUDE libtock_layout.ld

View File

@@ -0,0 +1,121 @@
diff --git a/apis/buttons/src/lib.rs b/apis/buttons/src/lib.rs
index 565970f..f5d08b5 100644
--- a/apis/buttons/src/lib.rs
+++ b/apis/buttons/src/lib.rs
@@ -135,7 +135,7 @@ mod tests;
// Driver number and command IDs
// -----------------------------------------------------------------------------
-const DRIVER_NUM: u32 = 3;
+pub const DRIVER_NUM: u32 = 3;
// Command IDs
const BUTTONS_COUNT: u32 = 0;
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index a863392..3509a40 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -14,6 +14,9 @@ libtock_platform = { path = "../platform" }
[features]
+# Initialize an allocator for access to heap memory
+alloc_init = []
+
# By default, libtock_runtime looks for the LIBTOCK_PLATFORM variable to decide
# what layout file to use. If you are providing your own linker script, set
# no_auto_layout to disable the layout file logic.
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
index 1054e41..b5c519a 100644
--- a/runtime/src/lib.rs
+++ b/runtime/src/lib.rs
@@ -19,6 +19,7 @@
//! and provide its own layout file.
#![no_std]
+#![feature(strict_provenance)]
#![warn(unsafe_op_in_unsafe_fn)]
pub mod startup;
diff --git a/runtime/src/startup/mod.rs b/runtime/src/startup/mod.rs
index 769bfe9..052479f 100644
--- a/runtime/src/startup/mod.rs
+++ b/runtime/src/startup/mod.rs
@@ -10,6 +10,8 @@ core::arch::global_asm!(include_str!("asm_arm.s"));
#[cfg(target_arch = "riscv32")]
core::arch::global_asm!(include_str!("asm_riscv32.s"));
+static APP_HEAP_SIZE: Option<&'static str> = option_env!("APP_HEAP_SIZE");
+
/// `set_main!` is used to tell `libtock_runtime` where the process binary's
/// `main` function is. The process binary's `main` function must have the
/// signature `FnOnce() -> T`, where T is some concrete type that implements
@@ -83,6 +85,8 @@ extern "C" fn rust_start() -> ! {
extern "Rust" {
fn libtock_unsafe_main() -> !;
static rt_header: RtHeader;
+ #[cfg(feature = "alloc_init")]
+ fn libtock_alloc_init(heap_bottom: *mut u8, heap_size: usize);
}
// TODO: Implement a safe memop API in libtock_platform and migrate these
@@ -92,16 +96,46 @@ extern "C" fn rust_start() -> ! {
// impact the execution of this process.
#[cfg(not(feature = "no_debug_memop"))]
unsafe {
+ // specify the top of the application stack which grows downwards
TockSyscalls::syscall2::<{ syscall_class::MEMOP }>([
10u32.into(),
rt_header.stack_top.into(),
]);
+
+ // specify the start of the application heap which grows upwards
TockSyscalls::syscall2::<{ syscall_class::MEMOP }>([
11u32.into(),
rt_header.initial_break.into(),
]);
}
+ #[cfg(feature = "alloc_init")]
+ {
+ let app_heap_size: usize = match APP_HEAP_SIZE {
+ Some(var) => var
+ .parse()
+ .ok()
+ .expect("could not parse APP_HEAP_SIZE as usize!"),
+ None => 9000,
+ };
+
+ // the heap starts after the `bss` section
+ let app_heap_bottom = unsafe { rt_header.bss_start.add(rt_header.bss_size) };
+
+ assert_eq!(app_heap_bottom.addr(), unsafe {
+ rt_header.bss_start.addr() + rt_header.bss_size
+ });
+
+ let app_heap_end = unsafe { app_heap_bottom.add(app_heap_size) };
+
+ unsafe {
+ // tell the kernel the new app heap break (which is the upper address bound of the process)
+ TockSyscalls::syscall2::<{ syscall_class::MEMOP }>([0u32.into(), app_heap_end.into()]);
+
+ libtock_alloc_init(app_heap_bottom, app_heap_size);
+ }
+ }
+
// Safety: libtock_unsafe_main is defined by the set_main! macro, and its
// signature matches the signature in the `extern` block in this function.
unsafe {
diff --git a/rust-toolchain b/rust-toolchain
index e911a96..d139e12 100644
--- a/rust-toolchain
+++ b/rust-toolchain
@@ -1,7 +1,7 @@
[toolchain]
# See https://rust-lang.github.io/rustup-components-history/ for a list of
# recently nightlies and what components are available for them.
-channel = "nightly-2022-06-10"
+channel = "nightly-2023-02-01"
components = ["clippy", "miri", "rustfmt"]
targets = ["thumbv7em-none-eabi",
"riscv32imac-unknown-none-elf",

View File

@@ -1,12 +0,0 @@
diff --git a/core/src/stack_size.rs b/core/src/stack_size.rs
index 9145393..ef55383 100644
--- a/core/src/stack_size.rs
+++ b/core/src/stack_size.rs
@@ -14,6 +14,7 @@ macro_rules! stack_size {
{$size:expr} => {
#[no_mangle]
#[link_section = ".stack_buffer"]
+ #[cfg(not(target_os = "macos"))]
pub static mut STACK_MEMORY: [u8; $size] = [0; $size];
}
}

View File

@@ -1,8 +1,8 @@
diff --git a/Cargo.toml b/Cargo.toml
index 06acc26d2..bd1bbd58f 100644
index 04cb39334..596cacda4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,7 +20,13 @@ members = [
@@ -23,7 +23,13 @@ members = [
"boards/msp_exp432p401r",
"boards/microbit_v2",
"boards/nordic/nrf52840dk",

View File

@@ -1,9 +1,9 @@
diff --git a/boards/components/src/ctap.rs b/boards/components/src/ctap.rs
deleted file mode 100644
index 5e30702b0..000000000
index 87b4ded9f..000000000
--- a/boards/components/src/ctap.rs
+++ /dev/null
@@ -1,127 +0,0 @@
@@ -1,130 +0,0 @@
-//! Component for CTAP HID over USB support.
-//!
-//! This provides a component for using the CTAP driver. This allows for
@@ -57,31 +57,34 @@ index 5e30702b0..000000000
-}
-
-pub struct CtapComponent<U: 'static + hil::usb::UsbController<'static>> {
- board_kernel: &'static kernel::Kernel,
- driver_num: usize,
- usb: &'static U,
- vendor_id: u16,
- product_id: u16,
- strings: &'static [&'static str; 3],
- board_kernel: &'static kernel::Kernel,
- send_buffer: &'static mut [u8; 64],
- recv_buffer: &'static mut [u8; 64],
-}
-
-impl<U: 'static + hil::usb::UsbController<'static>> CtapComponent<U> {
- pub fn new(
- board_kernel: &'static kernel::Kernel,
- driver_num: usize,
- usb: &'static U,
- vendor_id: u16,
- product_id: u16,
- strings: &'static [&'static str; 3],
- board_kernel: &'static kernel::Kernel,
- send_buffer: &'static mut [u8; 64],
- recv_buffer: &'static mut [u8; 64],
- ) -> CtapComponent<U> {
- CtapComponent {
- board_kernel,
- driver_num,
- usb,
- vendor_id,
- product_id,
- strings,
- board_kernel,
- send_buffer,
- recv_buffer,
- }
@@ -122,7 +125,7 @@ index 5e30702b0..000000000
- Some(ctap),
- self.send_buffer,
- self.recv_buffer,
- self.board_kernel.create_grant(&grant_cap),
- self.board_kernel.create_grant(self.driver_num, &grant_cap),
- )
- );
-
@@ -132,23 +135,23 @@ index 5e30702b0..000000000
- }
-}
diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs
index 95625f91f..cd542609e 100644
index 120403652..92d45f7e3 100644
--- a/boards/components/src/lib.rs
+++ b/boards/components/src/lib.rs
@@ -10,7 +10,6 @@ pub mod button;
@@ -14,7 +14,6 @@ pub mod ccs811;
pub mod cdc;
pub mod console;
pub mod crc;
-pub mod ctap;
pub mod debug_queue;
pub mod debug_writer;
pub mod firmware_protection;
pub mod digest;
diff --git a/capsules/src/usb/ctap.rs b/capsules/src/usb/ctap.rs
deleted file mode 100644
index 32cb2d6f1..000000000
index d085b7286..000000000
--- a/capsules/src/usb/ctap.rs
+++ /dev/null
@@ -1,410 +0,0 @@
@@ -1,406 +0,0 @@
-//! Client to Authenticator Protocol CTAPv2 over USB HID
-//!
-//! Based on the spec avaliable at: <https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html>
@@ -169,11 +172,11 @@ index 32cb2d6f1..000000000
-use super::descriptors::TransferDirection;
-use super::usbc_client_ctrl::ClientCtrl;
-
-use kernel::common::cells::OptionalCell;
-use kernel::common::cells::TakeCell;
-use kernel::hil;
-use kernel::hil::usb::TransferType;
-use kernel::ReturnCode;
-use kernel::utilities::cells::OptionalCell;
-use kernel::utilities::cells::TakeCell;
-use kernel::ErrorCode;
-
-/// Use 1 Interrupt transfer IN/OUT endpoint
-const ENDPOINT_NUM: usize = 1;
@@ -349,7 +352,7 @@ index 32cb2d6f1..000000000
- fn send_buffer(
- &'a self,
- send: &'static mut [u8; 64],
- ) -> Result<usize, (ReturnCode, &'static mut [u8; 64])> {
- ) -> Result<usize, (ErrorCode, &'static mut [u8; 64])> {
- let len = send.len();
-
- self.send_buffer.replace(send);
@@ -358,17 +361,17 @@ index 32cb2d6f1..000000000
- Ok(len)
- }
-
- fn send_cancel(&'a self) -> Result<&'static mut [u8; 64], ReturnCode> {
- fn send_cancel(&'a self) -> Result<&'static mut [u8; 64], ErrorCode> {
- match self.send_buffer.take() {
- Some(buf) => Ok(buf),
- None => Err(ReturnCode::EBUSY),
- None => Err(ErrorCode::BUSY),
- }
- }
-
- fn receive_buffer(
- &'a self,
- recv: &'static mut [u8; 64],
- ) -> Result<(), (ReturnCode, &'static mut [u8; 64])> {
- ) -> Result<(), (ErrorCode, &'static mut [u8; 64])> {
- self.recv_buffer.replace(recv);
-
- if self.saved_endpoint.is_some() {
@@ -376,11 +379,7 @@ index 32cb2d6f1..000000000
- if self.can_receive() {
- self.recv_buffer.take().map(|buf| {
- self.client.map(move |client| {
- client.packet_received(
- ReturnCode::SUCCESS,
- buf,
- self.saved_endpoint.take().unwrap(),
- );
- client.packet_received(Ok(()), buf, self.saved_endpoint.take().unwrap());
- });
- });
- // Reset the offset
@@ -394,11 +393,11 @@ index 32cb2d6f1..000000000
- Ok(())
- }
-
- fn receive_cancel(&'a self) -> Result<&'static mut [u8; 64], ReturnCode> {
- fn receive_cancel(&'a self) -> Result<&'static mut [u8; 64], ErrorCode> {
- self.saved_endpoint.take();
- match self.recv_buffer.take() {
- Some(buf) => Ok(buf),
- None => Err(ReturnCode::EBUSY),
- None => Err(ErrorCode::BUSY),
- }
- }
-}
@@ -524,7 +523,7 @@ index 32cb2d6f1..000000000
- if total_received_bytes >= self.recv_len.get() {
- if self.can_receive() {
- self.client.map(move |client| {
- client.packet_received(ReturnCode::SUCCESS, buf, endpoint);
- client.packet_received(Ok(()), buf, endpoint);
- });
- // Reset the offset
- self.recv_offset.set(0);
@@ -554,18 +553,18 @@ index 32cb2d6f1..000000000
- fn packet_transmitted(&'a self, endpoint: usize) {
- self.send_buffer.take().map(|buf| {
- self.client.map(move |client| {
- client.packet_transmitted(ReturnCode::SUCCESS, buf, endpoint);
- client.packet_transmitted(Ok(()), buf, endpoint);
- });
- });
- }
-}
diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs
index 17cab4c23..3f3a4f646 100644
index 6d5daa444..767f5de83 100644
--- a/capsules/src/usb/mod.rs
+++ b/capsules/src/usb/mod.rs
@@ -1,5 +1,4 @@
pub mod cdc;
-pub mod ctap;
pub mod descriptors;
pub mod usb_ctap;
pub mod usb_user;
pub mod usbc_client;

View File

@@ -1,864 +0,0 @@
diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs
index 64fe46b7b..a3bbe724b 100644
--- a/boards/components/src/lib.rs
+++ b/boards/components/src/lib.rs
@@ -52,3 +52,4 @@ pub mod tickv;
pub mod touch;
pub mod udp_driver;
pub mod udp_mux;
+pub mod usb_ctap;
diff --git a/boards/components/src/usb_ctap.rs b/boards/components/src/usb_ctap.rs
new file mode 100644
index 000000000..69e95c3c7
--- /dev/null
+++ b/boards/components/src/usb_ctap.rs
@@ -0,0 +1,88 @@
+//! Component for CTAP over USB.
+
+use capsules::usb::usb_ctap::CtapUsbSyscallDriver;
+use capsules::usb::usbc_ctap_hid::ClientCtapHID;
+use core::mem::MaybeUninit;
+use kernel::capabilities;
+use kernel::component::Component;
+use kernel::create_capability;
+use kernel::hil;
+use kernel::static_init_half;
+
+// Setup static space for the objects.
+#[macro_export]
+macro_rules! usb_ctap_component_buf {
+ ($C:ty) => {{
+ use capsules::usb::usb_ctap::CtapUsbSyscallDriver;
+ use capsules::usb::usbc_ctap_hid::ClientCtapHID;
+ use core::mem::MaybeUninit;
+ static mut BUF1: MaybeUninit<ClientCtapHID<'static, 'static, $C>> = MaybeUninit::uninit();
+ static mut BUF2: MaybeUninit<CtapUsbSyscallDriver<'static, 'static, $C>> =
+ MaybeUninit::uninit();
+ (&mut BUF1, &mut BUF2)
+ };};
+}
+
+pub struct UsbCtapComponent<C: 'static + hil::usb::UsbController<'static>> {
+ board_kernel: &'static kernel::Kernel,
+ controller: &'static C,
+ max_ctrl_packet_size: u8,
+ vendor_id: u16,
+ product_id: u16,
+ strings: &'static [&'static str],
+}
+
+impl<C: 'static + hil::usb::UsbController<'static>> UsbCtapComponent<C> {
+ pub fn new(
+ board_kernel: &'static kernel::Kernel,
+ controller: &'static C,
+ max_ctrl_packet_size: u8,
+ vendor_id: u16,
+ product_id: u16,
+ strings: &'static [&'static str],
+ ) -> Self {
+ Self {
+ board_kernel,
+ controller,
+ max_ctrl_packet_size,
+ vendor_id,
+ product_id,
+ strings,
+ }
+ }
+}
+
+impl<C: 'static + hil::usb::UsbController<'static>> Component for UsbCtapComponent<C> {
+ type StaticInput = (
+ &'static mut MaybeUninit<ClientCtapHID<'static, 'static, C>>,
+ &'static mut MaybeUninit<CtapUsbSyscallDriver<'static, 'static, C>>,
+ );
+ type Output = &'static CtapUsbSyscallDriver<'static, 'static, C>;
+
+ unsafe fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
+ let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
+
+ let usb_ctap = static_init_half!(
+ static_buffer.0,
+ ClientCtapHID<'static, 'static, C>,
+ ClientCtapHID::new(
+ self.controller,
+ self.max_ctrl_packet_size,
+ self.vendor_id,
+ self.product_id,
+ self.strings,
+ )
+ );
+ self.controller.set_client(usb_ctap);
+
+ // Configure the USB userspace driver
+ let usb_driver = static_init_half!(
+ static_buffer.1,
+ CtapUsbSyscallDriver<'static, 'static, C>,
+ CtapUsbSyscallDriver::new(usb_ctap, self.board_kernel.create_grant(&grant_cap))
+ );
+ usb_ctap.set_client(usb_driver);
+
+ usb_driver
+ }
+}
diff --git a/capsules/src/driver.rs b/capsules/src/driver.rs
index 3dad0f50b..b6124c4c0 100644
--- a/capsules/src/driver.rs
+++ b/capsules/src/driver.rs
@@ -26,6 +26,7 @@ pub enum NUM {
I2cMaster = 0x20003,
UsbUser = 0x20005,
I2cMasterSlave = 0x20006,
+ UsbCtap = 0x20009,
// Radio
BleAdvertising = 0x30000,
diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs
index 6d5daa444..17cab4c23 100644
--- a/capsules/src/usb/mod.rs
+++ b/capsules/src/usb/mod.rs
@@ -1,6 +1,8 @@
pub mod cdc;
pub mod ctap;
pub mod descriptors;
+pub mod usb_ctap;
pub mod usb_user;
pub mod usbc_client;
pub mod usbc_client_ctrl;
+pub mod usbc_ctap_hid;
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
new file mode 100644
index 000000000..da3d16d85
--- /dev/null
+++ b/capsules/src/usb/usb_ctap.rs
@@ -0,0 +1,355 @@
+use super::usbc_ctap_hid::ClientCtapHID;
+use kernel::hil;
+use kernel::hil::usb::Client;
+use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
+
+/// Syscall number
+use crate::driver;
+pub const DRIVER_NUM: usize = driver::NUM::UsbCtap as usize;
+
+pub const CTAP_CMD_CHECK: usize = 0;
+pub const CTAP_CMD_CONNECT: usize = 1;
+pub const CTAP_CMD_TRANSMIT: usize = 2;
+pub const CTAP_CMD_RECEIVE: usize = 3;
+pub const CTAP_CMD_TRANSMIT_OR_RECEIVE: usize = 4;
+pub const CTAP_CMD_CANCEL: usize = 5;
+
+pub const CTAP_ALLOW_TRANSMIT: usize = 1;
+pub const CTAP_ALLOW_RECEIVE: usize = 2;
+pub const CTAP_ALLOW_TRANSMIT_OR_RECEIVE: usize = 3;
+
+pub const CTAP_SUBSCRIBE_TRANSMIT: usize = 1;
+pub const CTAP_SUBSCRIBE_RECEIVE: usize = 2;
+pub const CTAP_SUBSCRIBE_TRANSMIT_OR_RECEIVE: usize = 3;
+
+pub const CTAP_CALLBACK_TRANSMITED: usize = 1;
+pub const CTAP_CALLBACK_RECEIVED: usize = 2;
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+enum Side {
+ Transmit,
+ Receive,
+ TransmitOrReceive,
+}
+
+impl Side {
+ fn can_transmit(&self) -> bool {
+ match self {
+ Side::Transmit | Side::TransmitOrReceive => true,
+ Side::Receive => false,
+ }
+ }
+
+ fn can_receive(&self) -> bool {
+ match self {
+ Side::Receive | Side::TransmitOrReceive => true,
+ Side::Transmit => false,
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct App {
+ // Only one app can be connected to this driver, to avoid needing to route packets among apps.
+ // This field tracks this status.
+ connected: bool,
+ // Currently enabled transaction side. Subscribing to a callback or allowing a buffer
+ // automatically sets the corresponding side. Clearing both the callback and the buffer resets
+ // the side to None.
+ side: Option<Side>,
+ callback: Option<Callback>,
+ buffer: Option<AppSlice<Shared, u8>>,
+ // Whether the app is waiting for the kernel signaling a packet transfer.
+ waiting: bool,
+}
+
+impl App {
+ fn check_side(&mut self) {
+ if self.callback.is_none() && self.buffer.is_none() && !self.waiting {
+ self.side = None;
+ }
+ }
+
+ fn set_side(&mut self, side: Side) -> bool {
+ match self.side {
+ None => {
+ self.side = Some(side);
+ true
+ }
+ Some(app_side) => side == app_side,
+ }
+ }
+
+ fn is_ready_for_command(&self, side: Side) -> bool {
+ self.buffer.is_some() && self.callback.is_some() && self.side == Some(side)
+ }
+}
+
+pub trait CtapUsbClient {
+ // Whether this client is ready to receive a packet. This must be checked before calling
+ // packet_received().
+ fn can_receive_packet(&self) -> bool;
+
+ // Signal to the client that a packet has been received.
+ fn packet_received(&self, packet: &[u8; 64]);
+
+ // Signal to the client that a packet has been transmitted.
+ fn packet_transmitted(&self);
+}
+
+pub struct CtapUsbSyscallDriver<'a, 'b, C: 'a> {
+ usb_client: &'a ClientCtapHID<'a, 'b, C>,
+ apps: Grant<App>,
+}
+
+impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
+ pub fn new(usb_client: &'a ClientCtapHID<'a, 'b, C>, apps: Grant<App>) -> Self {
+ CtapUsbSyscallDriver { usb_client, apps }
+ }
+}
+
+impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDriver<'a, 'b, C> {
+ fn can_receive_packet(&self) -> bool {
+ let mut result = false;
+ for app in self.apps.iter() {
+ app.enter(|app, _| {
+ if app.connected {
+ result = app.waiting
+ && app.side.map_or(false, |side| side.can_receive())
+ && app.buffer.is_some();
+ }
+ });
+ }
+ result
+ }
+
+ fn packet_received(&self, packet: &[u8; 64]) {
+ for app in self.apps.iter() {
+ app.enter(|app, _| {
+ if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive())
+ {
+ if let Some(buf) = &mut app.buffer {
+ // Copy the packet to the app's allowed buffer.
+ buf.as_mut().copy_from_slice(packet);
+ app.waiting = false;
+ // Signal to the app that a packet is ready.
+ app.callback
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
+ }
+ }
+ });
+ }
+ }
+
+ fn packet_transmitted(&self) {
+ for app in self.apps.iter() {
+ app.enter(|app, _| {
+ if app.connected
+ && app.waiting
+ && app.side.map_or(false, |side| side.can_transmit())
+ {
+ app.waiting = false;
+ // Signal to the app that the packet was sent.
+ app.callback
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_TRANSMITED, 0, 0));
+ }
+ });
+ }
+ }
+}
+
+impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a, 'b, C> {
+ fn allow(
+ &self,
+ appid: AppId,
+ allow_num: usize,
+ slice: Option<AppSlice<Shared, u8>>,
+ ) -> ReturnCode {
+ let side = match allow_num {
+ CTAP_ALLOW_TRANSMIT => Side::Transmit,
+ CTAP_ALLOW_RECEIVE => Side::Receive,
+ CTAP_ALLOW_TRANSMIT_OR_RECEIVE => Side::TransmitOrReceive,
+ _ => return ReturnCode::ENOSUPPORT,
+ };
+ self.apps
+ .enter(appid, |app, _| {
+ if !app.connected {
+ ReturnCode::ERESERVE
+ } else {
+ if let Some(buf) = &slice {
+ if buf.len() != 64 {
+ return ReturnCode::EINVAL;
+ }
+ }
+ if !app.set_side(side) {
+ return ReturnCode::EALREADY;
+ }
+ app.buffer = slice;
+ app.check_side();
+ ReturnCode::SUCCESS
+ }
+ })
+ .unwrap_or_else(|err| err.into())
+ }
+
+ fn subscribe(
+ &self,
+ subscribe_num: usize,
+ callback: Option<Callback>,
+ appid: AppId,
+ ) -> ReturnCode {
+ let side = match subscribe_num {
+ CTAP_SUBSCRIBE_TRANSMIT => Side::Transmit,
+ CTAP_SUBSCRIBE_RECEIVE => Side::Receive,
+ CTAP_SUBSCRIBE_TRANSMIT_OR_RECEIVE => Side::TransmitOrReceive,
+ _ => return ReturnCode::ENOSUPPORT,
+ };
+ self.apps
+ .enter(appid, |app, _| {
+ if !app.connected {
+ ReturnCode::ERESERVE
+ } else {
+ if !app.set_side(side) {
+ return ReturnCode::EALREADY;
+ }
+ app.callback = callback;
+ app.check_side();
+ ReturnCode::SUCCESS
+ }
+ })
+ .unwrap_or_else(|err| err.into())
+ }
+
+ fn command(&self, cmd_num: usize, _arg1: usize, _arg2: usize, appid: AppId) -> ReturnCode {
+ match cmd_num {
+ CTAP_CMD_CHECK => ReturnCode::SUCCESS,
+ CTAP_CMD_CONNECT => {
+ // First, check if any app is already connected to this driver.
+ let mut busy = false;
+ for app in self.apps.iter() {
+ app.enter(|app, _| {
+ busy |= app.connected;
+ });
+ }
+
+ self.apps
+ .enter(appid, |app, _| {
+ if app.connected {
+ ReturnCode::EALREADY
+ } else if busy {
+ ReturnCode::EBUSY
+ } else {
+ self.usb_client.enable();
+ self.usb_client.attach();
+ app.connected = true;
+ ReturnCode::SUCCESS
+ }
+ })
+ .unwrap_or_else(|err| err.into())
+ }
+ CTAP_CMD_TRANSMIT => self
+ .apps
+ .enter(appid, |app, _| {
+ if !app.connected {
+ ReturnCode::ERESERVE
+ } else {
+ if app.is_ready_for_command(Side::Transmit) {
+ if app.waiting {
+ ReturnCode::EALREADY
+ } else if self
+ .usb_client
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
+ {
+ app.waiting = true;
+ ReturnCode::SUCCESS
+ } else {
+ ReturnCode::EBUSY
+ }
+ } else {
+ ReturnCode::EINVAL
+ }
+ }
+ })
+ .unwrap_or_else(|err| err.into()),
+ CTAP_CMD_RECEIVE => self
+ .apps
+ .enter(appid, |app, _| {
+ if !app.connected {
+ ReturnCode::ERESERVE
+ } else {
+ if app.is_ready_for_command(Side::Receive) {
+ if app.waiting {
+ ReturnCode::EALREADY
+ } else {
+ app.waiting = true;
+ self.usb_client.receive_packet();
+ ReturnCode::SUCCESS
+ }
+ } else {
+ ReturnCode::EINVAL
+ }
+ }
+ })
+ .unwrap_or_else(|err| err.into()),
+ CTAP_CMD_TRANSMIT_OR_RECEIVE => self
+ .apps
+ .enter(appid, |app, _| {
+ if !app.connected {
+ ReturnCode::ERESERVE
+ } else {
+ if app.is_ready_for_command(Side::TransmitOrReceive) {
+ if app.waiting {
+ ReturnCode::EALREADY
+ } else {
+ // Indicates to the driver that we can receive any pending packet.
+ app.waiting = true;
+ self.usb_client.receive_packet();
+
+ if !app.waiting {
+ // The call to receive_packet() collected a pending packet.
+ ReturnCode::SUCCESS
+ } else {
+ // Indicates to the driver that we have a packet to send.
+ if self
+ .usb_client
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
+ {
+ ReturnCode::SUCCESS
+ } else {
+ ReturnCode::EBUSY
+ }
+ }
+ }
+ } else {
+ ReturnCode::EINVAL
+ }
+ }
+ })
+ .unwrap_or_else(|err| err.into()),
+ CTAP_CMD_CANCEL => self
+ .apps
+ .enter(appid, |app, _| {
+ if !app.connected {
+ ReturnCode::ERESERVE
+ } else {
+ if app.waiting {
+ // FIXME: if cancellation failed, the app should still wait. But that
+ // doesn't work yet.
+ app.waiting = false;
+ if self.usb_client.cancel_transaction() {
+ ReturnCode::SUCCESS
+ } else {
+ // Cannot cancel now because the transaction is already in process.
+ // The app should wait for the callback instead.
+ ReturnCode::EBUSY
+ }
+ } else {
+ ReturnCode::EALREADY
+ }
+ }
+ })
+ .unwrap_or_else(|err| err.into()),
+ _ => ReturnCode::ENOSUPPORT,
+ }
+ }
+}
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
new file mode 100644
index 000000000..642039120
--- /dev/null
+++ b/capsules/src/usb/usbc_ctap_hid.rs
@@ -0,0 +1,369 @@
+//! A USB HID client of the USB hardware interface
+
+use super::descriptors;
+use super::descriptors::Buffer64;
+use super::descriptors::DescriptorType;
+use super::descriptors::EndpointAddress;
+use super::descriptors::EndpointDescriptor;
+use super::descriptors::HIDCountryCode;
+use super::descriptors::HIDDescriptor;
+use super::descriptors::HIDSubordinateDescriptor;
+use super::descriptors::InterfaceDescriptor;
+use super::descriptors::ReportDescriptor;
+use super::descriptors::TransferDirection;
+use super::usb_ctap::CtapUsbClient;
+use super::usbc_client_ctrl::ClientCtrl;
+use core::cell::Cell;
+use kernel::common::cells::OptionalCell;
+use kernel::debug;
+use kernel::hil;
+use kernel::hil::usb::TransferType;
+
+static LANGUAGES: &'static [u16; 1] = &[
+ 0x0409, // English (United States)
+];
+
+const ENDPOINT_NUM: usize = 1;
+
+static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
+ 0x06, 0xD0, 0xF1, // HID_UsagePage ( FIDO_USAGE_PAGE ),
+ 0x09, 0x01, // HID_Usage ( FIDO_USAGE_CTAPHID ),
+ 0xA1, 0x01, // HID_Collection ( HID_Application ),
+ 0x09, 0x20, // HID_Usage ( FIDO_USAGE_DATA_IN ),
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
+ 0x95, 0x40, // HID_ReportCount ( HID_INPUT_REPORT_BYTES ),
+ 0x81, 0x02, // HID_Input ( HID_Data | HID_Absolute | HID_Variable ),
+ 0x09, 0x21, // HID_Usage ( FIDO_USAGE_DATA_OUT ),
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
+ 0x95, 0x40, // HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ),
+ 0x91, 0x02, // HID_Output ( HID_Data | HID_Absolute | HID_Variable ),
+ 0xC0, // HID_EndCollection
+];
+
+static CTAP_REPORT: ReportDescriptor<'static> = ReportDescriptor {
+ desc: CTAP_REPORT_DESCRIPTOR,
+};
+
+static HID_SUB_DESCRIPTORS: &'static [HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
+ typ: DescriptorType::Report,
+ len: CTAP_REPORT_DESCRIPTOR.len() as u16,
+}];
+
+static HID: HIDDescriptor<'static> = HIDDescriptor {
+ hid_class: 0x0110,
+ country_code: HIDCountryCode::NotSupported,
+ sub_descriptors: HID_SUB_DESCRIPTORS,
+};
+
+pub struct ClientCtapHID<'a, 'b, C: 'a> {
+ client_ctrl: ClientCtrl<'a, 'static, C>,
+
+ // 64-byte buffers for the endpoint
+ in_buffer: Buffer64,
+ out_buffer: Buffer64,
+
+ // Interaction with the client
+ client: OptionalCell<&'b dyn CtapUsbClient>,
+ tx_packet: OptionalCell<[u8; 64]>,
+ pending_in: Cell<bool>,
+ pending_out: Cell<bool>,
+ delayed_out: Cell<bool>,
+}
+
+impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
+ pub fn new(
+ controller: &'a C,
+ max_ctrl_packet_size: u8,
+ vendor_id: u16,
+ product_id: u16,
+ strings: &'static [&'static str],
+ ) -> Self {
+ let interfaces: &mut [InterfaceDescriptor] = &mut [
+ // Interface declared in the FIDO2 specification, section 8.1.8.1
+ InterfaceDescriptor {
+ interface_class: 0x03, // HID
+ interface_subclass: 0x00,
+ interface_protocol: 0x00,
+ ..InterfaceDescriptor::default()
+ },
+ ];
+
+ let endpoints: &[&[EndpointDescriptor]] = &[&[
+ EndpointDescriptor {
+ endpoint_address: EndpointAddress::new_const(
+ ENDPOINT_NUM,
+ TransferDirection::HostToDevice,
+ ),
+ transfer_type: TransferType::Interrupt,
+ max_packet_size: 64,
+ interval: 5,
+ },
+ EndpointDescriptor {
+ endpoint_address: EndpointAddress::new_const(
+ ENDPOINT_NUM,
+ TransferDirection::DeviceToHost,
+ ),
+ transfer_type: TransferType::Interrupt,
+ max_packet_size: 64,
+ interval: 5,
+ },
+ ]];
+
+ let (device_descriptor_buffer, other_descriptor_buffer) =
+ descriptors::create_descriptor_buffers(
+ descriptors::DeviceDescriptor {
+ vendor_id,
+ product_id,
+ manufacturer_string: 1,
+ product_string: 2,
+ serial_number_string: 3,
+ max_packet_size_ep0: max_ctrl_packet_size,
+ ..descriptors::DeviceDescriptor::default()
+ },
+ descriptors::ConfigurationDescriptor {
+ configuration_value: 1,
+ ..descriptors::ConfigurationDescriptor::default()
+ },
+ interfaces,
+ endpoints,
+ Some(&HID),
+ None, // No CDC descriptor array
+ );
+
+ ClientCtapHID {
+ client_ctrl: ClientCtrl::new(
+ controller,
+ device_descriptor_buffer,
+ other_descriptor_buffer,
+ Some(&HID),
+ Some(&CTAP_REPORT),
+ LANGUAGES,
+ strings,
+ ),
+ in_buffer: Buffer64::default(),
+ out_buffer: Buffer64::default(),
+ client: OptionalCell::empty(),
+ tx_packet: OptionalCell::empty(),
+ pending_in: Cell::new(false),
+ pending_out: Cell::new(false),
+ delayed_out: Cell::new(false),
+ }
+ }
+
+ pub fn set_client(&'a self, client: &'b dyn CtapUsbClient) {
+ self.client.set(client);
+ }
+
+ pub fn transmit_packet(&'a self, packet: &[u8]) -> bool {
+ if self.pending_in.get() {
+ // The previous packet has not yet been transmitted, reject the new one.
+ false
+ } else {
+ self.pending_in.set(true);
+ let mut buf: [u8; 64] = [0; 64];
+ buf.copy_from_slice(packet);
+ self.tx_packet.set(buf);
+ // Alert the controller that we now have data to send on the Interrupt IN endpoint.
+ self.controller().endpoint_resume_in(1);
+ true
+ }
+ }
+
+ pub fn receive_packet(&'a self) -> bool {
+ if self.pending_out.get() {
+ // The previous packet has not yet been received, reject the new one.
+ false
+ } else {
+ self.pending_out.set(true);
+ // In case we reported Delay before, send the pending packet back to the client.
+ // Otherwise, there's nothing to do, the controller will send us a packet_out when a
+ // packet arrives.
+ if self.delayed_out.take() {
+ if self.send_packet_to_client() {
+ // If that succeeds, alert the controller that we can now
+ // receive data on the Interrupt OUT endpoint.
+ self.controller().endpoint_resume_out(1);
+ }
+ }
+ true
+ }
+ }
+
+ // Send an OUT packet available in the controller back to the client.
+ // This returns false if the client is not ready to receive a packet, and true if the client
+ // successfully accepted the packet.
+ fn send_packet_to_client(&'a self) -> bool {
+ // Copy the packet into a buffer to send to the client.
+ let mut buf: [u8; 64] = [0; 64];
+ for (i, x) in self.out_buffer.buf.iter().enumerate() {
+ buf[i] = x.get();
+ }
+
+ assert!(!self.delayed_out.get());
+
+ // Notify the client
+ if self
+ .client
+ .map_or(false, |client| client.can_receive_packet())
+ {
+ assert!(self.pending_out.take());
+
+ // Clear any pending packet on the transmitting side.
+ // It's up to the client to handle the received packet and decide if this packet
+ // should be re-transmitted or not.
+ self.cancel_in_transaction();
+
+ self.client.map(|client| client.packet_received(&buf));
+ true
+ } else {
+ // Cannot receive now, indicate a delay to the controller.
+ self.delayed_out.set(true);
+ false
+ }
+ }
+
+ pub fn cancel_transaction(&'a self) -> bool {
+ self.cancel_in_transaction() | self.cancel_out_transaction()
+ }
+
+ fn cancel_in_transaction(&'a self) -> bool {
+ self.tx_packet.take();
+ self.pending_in.take()
+ }
+
+ fn cancel_out_transaction(&'a self) -> bool {
+ self.pending_out.take()
+ }
+
+ #[inline]
+ fn controller(&'a self) -> &'a C {
+ self.client_ctrl.controller()
+ }
+}
+
+impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtapHID<'a, 'b, C> {
+ fn enable(&'a self) {
+ // Set up the default control endpoint
+ self.client_ctrl.enable();
+
+ // Set up the interrupt in-out endpoint
+ self.controller()
+ .endpoint_set_in_buffer(1, &self.in_buffer.buf);
+ self.controller()
+ .endpoint_set_out_buffer(1, &self.out_buffer.buf);
+ self.controller()
+ .endpoint_in_out_enable(TransferType::Interrupt, 1);
+ }
+
+ fn attach(&'a self) {
+ self.client_ctrl.attach();
+ }
+
+ fn bus_reset(&'a self) {
+ // Should the client initiate reconfiguration here?
+ // For now, the hardware layer does it.
+
+ debug!("Bus reset");
+ }
+
+ /// Handle a Control Setup transaction
+ fn ctrl_setup(&'a self, endpoint: usize) -> hil::usb::CtrlSetupResult {
+ self.client_ctrl.ctrl_setup(endpoint)
+ }
+
+ /// Handle a Control In transaction
+ fn ctrl_in(&'a self, endpoint: usize) -> hil::usb::CtrlInResult {
+ self.client_ctrl.ctrl_in(endpoint)
+ }
+
+ /// Handle a Control Out transaction
+ fn ctrl_out(&'a self, endpoint: usize, packet_bytes: u32) -> hil::usb::CtrlOutResult {
+ self.client_ctrl.ctrl_out(endpoint, packet_bytes)
+ }
+
+ fn ctrl_status(&'a self, endpoint: usize) {
+ self.client_ctrl.ctrl_status(endpoint)
+ }
+
+ /// Handle the completion of a Control transfer
+ fn ctrl_status_complete(&'a self, endpoint: usize) {
+ self.client_ctrl.ctrl_status_complete(endpoint)
+ }
+
+ /// Handle a Bulk/Interrupt IN transaction
+ fn packet_in(&'a self, transfer_type: TransferType, endpoint: usize) -> hil::usb::InResult {
+ match transfer_type {
+ TransferType::Bulk => hil::usb::InResult::Error,
+ TransferType::Interrupt => {
+ if endpoint != 1 {
+ return hil::usb::InResult::Error;
+ }
+
+ if let Some(packet) = self.tx_packet.take() {
+ let buf = &self.in_buffer.buf;
+ for i in 0..64 {
+ buf[i].set(packet[i]);
+ }
+
+ hil::usb::InResult::Packet(64)
+ } else {
+ // Nothing to send
+ hil::usb::InResult::Delay
+ }
+ }
+ TransferType::Control | TransferType::Isochronous => unreachable!(),
+ }
+ }
+
+ /// Handle a Bulk/Interrupt OUT transaction
+ fn packet_out(
+ &'a self,
+ transfer_type: TransferType,
+ endpoint: usize,
+ packet_bytes: u32,
+ ) -> hil::usb::OutResult {
+ match transfer_type {
+ TransferType::Bulk => hil::usb::OutResult::Error,
+ TransferType::Interrupt => {
+ if endpoint != 1 {
+ return hil::usb::OutResult::Error;
+ }
+
+ if packet_bytes != 64 {
+ // Cannot process this packet
+ hil::usb::OutResult::Error
+ } else {
+ if self.send_packet_to_client() {
+ hil::usb::OutResult::Ok
+ } else {
+ hil::usb::OutResult::Delay
+ }
+ }
+ }
+ TransferType::Control | TransferType::Isochronous => unreachable!(),
+ }
+ }
+
+ fn packet_transmitted(&'a self, endpoint: usize) {
+ if endpoint != 1 {
+ panic!("Unexpected transmission on ep {}", endpoint);
+ }
+
+ if self.tx_packet.is_some() {
+ panic!("Unexpected tx_packet while a packet was being transmitted.");
+ }
+ self.pending_in.set(false);
+
+ // Clear any pending packet on the receiving side.
+ // It's up to the client to handle the transmitted packet and decide if they want to
+ // receive another packet.
+ self.cancel_out_transaction();
+
+ // Notify the client
+ self.client.map(|client| client.packet_transmitted());
+ }
+}

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
diff --git a/capsules/Cargo.toml b/capsules/Cargo.toml
index 680fe32b2..4f757b93d 100644
index 65301bcf1..dc70e98b1 100644
--- a/capsules/Cargo.toml
+++ b/capsules/Cargo.toml
@@ -8,3 +8,6 @@ edition = "2018"
@@ -8,3 +8,6 @@ edition = "2021"
kernel = { path = "../kernel" }
enum_primitive = { path = "../libraries/enum_primitive" }
tickv = { path = "../libraries/tickv" }
@@ -10,10 +10,10 @@ index 680fe32b2..4f757b93d 100644
+[features]
+vendor_hid = []
diff --git a/capsules/src/usb/descriptors.rs b/capsules/src/usb/descriptors.rs
index dea2dfed9..66a16fe87 100644
index 67f708239..c2556d3a2 100644
--- a/capsules/src/usb/descriptors.rs
+++ b/capsules/src/usb/descriptors.rs
@@ -414,13 +414,14 @@ impl DescriptorBuffer {
@@ -415,13 +415,14 @@ impl DescriptorBuffer {
/// example, if the interface descriptor list contains `[ID1, ID2, ID3]`,
/// and the endpoint descriptors list is `[[ED1, ED2], [ED3, ED4, ED5],
/// [ED6]]`, then the third interface descriptor (`ID3`) has one
@@ -30,7 +30,7 @@ index dea2dfed9..66a16fe87 100644
cdc_descriptor: Option<&[CdcInterfaceDescriptor]>,
) -> (DeviceBuffer, DescriptorBuffer) {
// Create device descriptor buffer and fill.
@@ -504,7 +505,7 @@ pub fn create_descriptor_buffers(
@@ -505,7 +506,7 @@ pub fn create_descriptor_buffers(
.iter()
.map(|descs| descs.iter().map(|d| d.size()).sum::<usize>())
.sum::<usize>()
@@ -39,7 +39,7 @@ index dea2dfed9..66a16fe87 100644
+ cdc_descriptor.map_or(0, |ds| ds.iter().map(|d| d.size()).sum::<usize>());
// Set the number of endpoints for each interface descriptor.
@@ -521,13 +522,11 @@ pub fn create_descriptor_buffers(
@@ -522,12 +523,10 @@ pub fn create_descriptor_buffers(
// Add the interface descriptor.
len += d.write_to(&other_buf.buf[len..]);
@@ -49,20 +49,18 @@ index dea2dfed9..66a16fe87 100644
- // HID descriptor, if any.
- if let Some(dh) = hid_descriptor {
- len += dh.write_to(&other_buf.buf[len..]);
- }
+ // HID descriptor, if present, for this interface.
+ if let Some(dh) = hid_descriptor {
+ if let Some(d) = dh.get(i) {
+ len += d.write_to(&other_buf.buf[len..]);
+ }
+ if let Some(d) = dh.get(i) {
+ len += d.write_to(&other_buf.buf[len..]);
}
}
// If there is a CDC descriptor array, we include
diff --git a/capsules/src/usb/usbc_client_ctrl.rs b/capsules/src/usb/usbc_client_ctrl.rs
index f7899d8c5..6956523c6 100644
index c8e55a8a8..54c7c3bbb 100644
--- a/capsules/src/usb/usbc_client_ctrl.rs
+++ b/capsules/src/usb/usbc_client_ctrl.rs
@@ -38,6 +38,12 @@ const DESCRIPTOR_BUFLEN: usize = 128;
@@ -40,6 +40,12 @@ const DESCRIPTOR_BUFLEN: usize = 128;
const N_ENDPOINTS: usize = 3;
@@ -75,7 +73,7 @@ index f7899d8c5..6956523c6 100644
/// Handler for USB control endpoint requests.
pub struct ClientCtrl<'a, 'b, U: 'a> {
/// The USB hardware controller.
@@ -64,12 +70,12 @@ pub struct ClientCtrl<'a, 'b, U: 'a> {
@@ -66,12 +72,12 @@ pub struct ClientCtrl<'a, 'b, U: 'a> {
/// An optional HID descriptor for the configuration. This can be requested
/// separately. It must also be included in `other_descriptor_buffer` if it exists.
@@ -90,7 +88,7 @@ index f7899d8c5..6956523c6 100644
/// Supported language (only one for now).
language: &'b [u16; 1],
@@ -104,8 +110,8 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
@@ -106,8 +112,8 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
controller: &'a U,
device_descriptor_buffer: DeviceBuffer,
other_descriptor_buffer: DescriptorBuffer,
@@ -101,7 +99,7 @@ index f7899d8c5..6956523c6 100644
language: &'b [u16; 1],
strings: &'b [&'b str],
) -> Self {
@@ -331,28 +337,39 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
@@ -333,28 +339,39 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
descriptor_type,
// TODO: use the descriptor index
descriptor_index: _,
@@ -155,163 +153,3 @@ index f7899d8c5..6956523c6 100644
} else {
hil::usb::CtrlSetupResult::ErrGeneric
}
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
index 642039120..abf224f97 100644
--- a/capsules/src/usb/usbc_ctap_hid.rs
+++ b/capsules/src/usb/usbc_ctap_hid.rs
@@ -44,21 +44,59 @@ static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
0xC0, // HID_EndCollection
];
+#[cfg(feature = "vendor_hid")]
+static VENDOR_REPORT_DESCRIPTOR: &'static [u8] = &[
+ 0x06, 0x00, 0xFF, // HID_UsagePage ( VENDOR ),
+ 0x09, 0x01, // HID_Usage ( Unused ),
+ 0xA1, 0x01, // HID_Collection ( HID_Application ),
+ 0x09, 0x20, // HID_Usage ( FIDO_USAGE_DATA_IN ),
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
+ 0x95, 0x40, // HID_ReportCount ( HID_INPUT_REPORT_BYTES ),
+ 0x81, 0x02, // HID_Input ( HID_Data | HID_Absolute | HID_Variable ),
+ 0x09, 0x21, // HID_Usage ( FIDO_USAGE_DATA_OUT ),
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
+ 0x95, 0x40, // HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ),
+ 0x91, 0x02, // HID_Output ( HID_Data | HID_Absolute | HID_Variable ),
+ 0xC0, // HID_EndCollection
+];
+
static CTAP_REPORT: ReportDescriptor<'static> = ReportDescriptor {
desc: CTAP_REPORT_DESCRIPTOR,
};
+#[cfg(feature = "vendor_hid")]
+static VENDOR_REPORT: ReportDescriptor<'static> = ReportDescriptor {
+ desc: VENDOR_REPORT_DESCRIPTOR,
+};
+
static HID_SUB_DESCRIPTORS: &'static [HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
typ: DescriptorType::Report,
len: CTAP_REPORT_DESCRIPTOR.len() as u16,
}];
+#[cfg(feature = "vendor_hid")]
+static VENDOR_HID_SUB_DESCRIPTORS: &'static [HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
+ typ: DescriptorType::Report,
+ len: VENDOR_REPORT_DESCRIPTOR.len() as u16,
+}];
+
static HID: HIDDescriptor<'static> = HIDDescriptor {
hid_class: 0x0110,
country_code: HIDCountryCode::NotSupported,
sub_descriptors: HID_SUB_DESCRIPTORS,
};
+#[cfg(feature = "vendor_hid")]
+static VENDOR_HID: HIDDescriptor<'static> = HIDDescriptor {
+ hid_class: 0x0110,
+ country_code: HIDCountryCode::NotSupported,
+ sub_descriptors: VENDOR_HID_SUB_DESCRIPTORS,
+};
+
pub struct ClientCtapHID<'a, 'b, C: 'a> {
client_ctrl: ClientCtrl<'a, 'static, C>,
@@ -82,6 +120,9 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
product_id: u16,
strings: &'static [&'static str],
) -> Self {
+ #[cfg(feature = "vendor_hid")]
+ debug!("vendor_hid enabled.");
+
let interfaces: &mut [InterfaceDescriptor] = &mut [
// Interface declared in the FIDO2 specification, section 8.1.8.1
InterfaceDescriptor {
@@ -90,9 +131,19 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
interface_protocol: 0x00,
..InterfaceDescriptor::default()
},
+ // Vendor HID interface.
+ #[cfg(feature = "vendor_hid")]
+ InterfaceDescriptor {
+ interface_number: 1,
+ interface_class: 0x03, // HID
+ interface_subclass: 0x00,
+ interface_protocol: 0x00,
+ ..InterfaceDescriptor::default()
+ },
];
let endpoints: &[&[EndpointDescriptor]] = &[&[
+ // 2 Endpoints for FIDO
EndpointDescriptor {
endpoint_address: EndpointAddress::new_const(
ENDPOINT_NUM,
@@ -110,8 +161,30 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
transfer_type: TransferType::Interrupt,
max_packet_size: 64,
interval: 5,
- },
- ]];
+ },],
+ // 2 Endpoints for FIDO
+ #[cfg(feature = "vendor_hid")]
+ &[
+ EndpointDescriptor {
+ endpoint_address: EndpointAddress::new_const(
+ ENDPOINT_NUM + 1,
+ TransferDirection::HostToDevice,
+ ),
+ transfer_type: TransferType::Interrupt,
+ max_packet_size: 64,
+ interval: 5,
+ },
+ EndpointDescriptor {
+ endpoint_address: EndpointAddress::new_const(
+ ENDPOINT_NUM + 1,
+ TransferDirection::DeviceToHost,
+ ),
+ transfer_type: TransferType::Interrupt,
+ max_packet_size: 64,
+ interval: 5,
+ },
+ ],
+ ];
let (device_descriptor_buffer, other_descriptor_buffer) =
descriptors::create_descriptor_buffers(
@@ -130,17 +203,28 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
},
interfaces,
endpoints,
- Some(&HID),
+ Some(&[
+ &HID,
+ #[cfg(feature = "vendor_hid")]
+ &VENDOR_HID,
+ ]),
None, // No CDC descriptor array
);
-
ClientCtapHID {
client_ctrl: ClientCtrl::new(
controller,
device_descriptor_buffer,
other_descriptor_buffer,
- Some(&HID),
- Some(&CTAP_REPORT),
+ Some([
+ &HID,
+ #[cfg(feature = "vendor_hid")]
+ &VENDOR_HID,
+ ]),
+ Some([
+ &CTAP_REPORT,
+ #[cfg(feature = "vendor_hid")]
+ &VENDOR_REPORT,
+ ]),
LANGUAGES,
strings,
),

View File

@@ -1,338 +0,0 @@
diff --git a/boards/components/src/firmware_protection.rs b/boards/components/src/firmware_protection.rs
new file mode 100644
index 000000000..58695af81
--- /dev/null
+++ b/boards/components/src/firmware_protection.rs
@@ -0,0 +1,70 @@
+//! Component for firmware protection syscall interface.
+//!
+//! This provides one Component, `FirmwareProtectionComponent`, which implements a
+//! userspace syscall interface to enable the code readout protection.
+//!
+//! Usage
+//! -----
+//! ```rust
+//! let crp = components::firmware_protection::FirmwareProtectionComponent::new(
+//! board_kernel,
+//! nrf52840::uicr::Uicr::new()
+//! )
+//! .finalize(
+//! components::firmware_protection_component_helper!(uicr));
+//! ```
+
+use core::mem::MaybeUninit;
+
+use capsules::firmware_protection;
+use kernel::capabilities;
+use kernel::component::Component;
+use kernel::create_capability;
+use kernel::hil;
+use kernel::static_init_half;
+
+// Setup static space for the objects.
+#[macro_export]
+macro_rules! firmware_protection_component_helper {
+ ($C:ty) => {{
+ use capsules::firmware_protection;
+ use core::mem::MaybeUninit;
+ static mut BUF: MaybeUninit<firmware_protection::FirmwareProtection<$C>> =
+ MaybeUninit::uninit();
+ &mut BUF
+ };};
+}
+
+pub struct FirmwareProtectionComponent<C: hil::firmware_protection::FirmwareProtection> {
+ board_kernel: &'static kernel::Kernel,
+ crp: C,
+}
+
+impl<C: 'static + hil::firmware_protection::FirmwareProtection> FirmwareProtectionComponent<C> {
+ pub fn new(board_kernel: &'static kernel::Kernel, crp: C) -> FirmwareProtectionComponent<C> {
+ FirmwareProtectionComponent {
+ board_kernel: board_kernel,
+ crp: crp,
+ }
+ }
+}
+
+impl<C: 'static + hil::firmware_protection::FirmwareProtection> Component
+ for FirmwareProtectionComponent<C>
+{
+ type StaticInput = &'static mut MaybeUninit<firmware_protection::FirmwareProtection<C>>;
+ type Output = &'static firmware_protection::FirmwareProtection<C>;
+
+ unsafe fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
+ let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
+
+ static_init_half!(
+ static_buffer,
+ firmware_protection::FirmwareProtection<C>,
+ firmware_protection::FirmwareProtection::new(
+ self.crp,
+ self.board_kernel.create_grant(&grant_cap),
+ )
+ )
+ }
+}
diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs
index a3bbe724b..95625f91f 100644
--- a/boards/components/src/lib.rs
+++ b/boards/components/src/lib.rs
@@ -13,6 +13,7 @@ pub mod crc;
pub mod ctap;
pub mod debug_queue;
pub mod debug_writer;
+pub mod firmware_protection;
pub mod ft6x06;
pub mod fxos8700;
pub mod gpio;
diff --git a/capsules/src/driver.rs b/capsules/src/driver.rs
index b6124c4c0..90a18ea47 100644
--- a/capsules/src/driver.rs
+++ b/capsules/src/driver.rs
@@ -16,6 +16,7 @@ pub enum NUM {
Adc = 0x00005,
Dac = 0x00006,
AnalogComparator = 0x00007,
+ FirmwareProtection = 0x00008,
// Kernel
Ipc = 0x10000,
diff --git a/capsules/src/firmware_protection.rs b/capsules/src/firmware_protection.rs
new file mode 100644
index 000000000..8cf63d6a4
--- /dev/null
+++ b/capsules/src/firmware_protection.rs
@@ -0,0 +1,85 @@
+//! Provides userspace control of firmware protection on a board.
+//!
+//! This allows an application to enable firware readout protection,
+//! disabling JTAG interface and other ways to read/tamper the firmware.
+//! Of course, outside of a hardware bug, once set, the only way to enable
+//! programming/debugging is by fully erasing the flash.
+//!
+//! Usage
+//! -----
+//!
+//! ```rust
+//! # use kernel::static_init;
+//!
+//! let crp = static_init!(
+//! capsules::firmware_protection::FirmwareProtection,
+//! capsules::firmware_protection::FirmwareProtection::new(
+//! nrf52840::uicr::Uicr,
+//! board_kernel.create_grant(&grant_cap),
+//! );
+//! ```
+//!
+//! Syscall Interface
+//! -----------------
+//!
+//! - Stability: 0 - Draft
+//!
+//! ### Command
+//!
+//! Enable code readout protection on the current board.
+//!
+//! #### `command_num`
+//!
+//! - `0`: Driver check.
+//! - `1`: Get current firmware readout protection (aka CRP) state.
+//! - `2`: Set current firmware readout protection (aka CRP) state.
+//!
+
+use kernel::hil;
+use kernel::{AppId, Callback, Driver, Grant, ReturnCode};
+
+/// Syscall driver number.
+use crate::driver;
+pub const DRIVER_NUM: usize = driver::NUM::FirmwareProtection as usize;
+
+pub struct FirmwareProtection<C: hil::firmware_protection::FirmwareProtection> {
+ crp_unit: C,
+ apps: Grant<Option<Callback>>,
+}
+
+impl<C: hil::firmware_protection::FirmwareProtection> FirmwareProtection<C> {
+ pub fn new(crp_unit: C, apps: Grant<Option<Callback>>) -> Self {
+ Self { crp_unit, apps }
+ }
+}
+
+impl<C: hil::firmware_protection::FirmwareProtection> Driver for FirmwareProtection<C> {
+ ///
+ /// ### Command numbers
+ ///
+ /// * `0`: Returns non-zero to indicate the driver is present.
+ /// * `1`: Gets firmware protection state.
+ /// * `2`: Sets firmware protection state.
+ fn command(&self, command_num: usize, data: usize, _: usize, appid: AppId) -> ReturnCode {
+ match command_num {
+ // return if driver is available
+ 0 => ReturnCode::SUCCESS,
+
+ 1 => self
+ .apps
+ .enter(appid, |_, _| ReturnCode::SuccessWithValue {
+ value: self.crp_unit.get_protection() as usize,
+ })
+ .unwrap_or_else(|err| err.into()),
+
+ // sets firmware protection
+ 2 => self
+ .apps
+ .enter(appid, |_, _| self.crp_unit.set_protection(data.into()))
+ .unwrap_or_else(|err| err.into()),
+
+ // default
+ _ => ReturnCode::ENOSUPPORT,
+ }
+ }
+}
diff --git a/capsules/src/lib.rs b/capsules/src/lib.rs
index 925b4ba41..919638c5d 100644
--- a/capsules/src/lib.rs
+++ b/capsules/src/lib.rs
@@ -25,6 +25,7 @@ pub mod ctap;
pub mod dac;
pub mod debug_process_restart;
pub mod driver;
+pub mod firmware_protection;
pub mod fm25cl;
pub mod ft6x06;
pub mod fxos8700cq;
diff --git a/chips/nrf52/src/uicr.rs b/chips/nrf52/src/uicr.rs
index 788533210..655a98d5b 100644
--- a/chips/nrf52/src/uicr.rs
+++ b/chips/nrf52/src/uicr.rs
@@ -1,12 +1,14 @@
//! User information configuration registers
use enum_primitive::cast::FromPrimitive;
+use hil::firmware_protection::ProtectionLevel;
use kernel::common::registers::{register_bitfields, register_structs, ReadWrite};
use kernel::common::StaticRef;
use kernel::hil;
use kernel::ReturnCode;
use crate::gpio::Pin;
+use crate::nvmc;
const UICR_BASE: StaticRef<UicrRegisters> =
unsafe { StaticRef::new(0x10001000 as *const UicrRegisters) };
@@ -217,3 +219,49 @@ impl Uicr {
self.registers.approtect.write(ApProtect::PALL::ENABLED);
}
}
+
+impl hil::firmware_protection::FirmwareProtection for Uicr {
+ fn get_protection(&self) -> ProtectionLevel {
+ let ap_protect_state = self.is_ap_protect_enabled();
+ let cpu_debug_state = self
+ .registers
+ .debugctrl
+ .matches_all(DebugControl::CPUNIDEN::ENABLED + DebugControl::CPUFPBEN::ENABLED);
+ match (ap_protect_state, cpu_debug_state) {
+ (false, _) => ProtectionLevel::NoProtection,
+ (true, true) => ProtectionLevel::JtagDisabled,
+ (true, false) => ProtectionLevel::FullyLocked,
+ }
+ }
+
+ fn set_protection(&self, level: ProtectionLevel) -> ReturnCode {
+ let current_level = self.get_protection();
+ if current_level > level || level == ProtectionLevel::Unknown {
+ return ReturnCode::EINVAL;
+ }
+ if current_level == level {
+ return ReturnCode::EALREADY;
+ }
+
+ nvmc::Nvmc::new().configure_writeable();
+ if level >= ProtectionLevel::JtagDisabled {
+ self.set_ap_protect();
+ }
+
+ if level >= ProtectionLevel::FullyLocked {
+ // Prevent CPU debug and flash patching. Leaving these enabled could
+ // allow to circumvent protection.
+ self.registers
+ .debugctrl
+ .write(DebugControl::CPUNIDEN::DISABLED + DebugControl::CPUFPBEN::DISABLED);
+ // TODO(jmichel): prevent returning into bootloader if present
+ }
+ nvmc::Nvmc::new().configure_readonly();
+
+ if self.get_protection() == level {
+ ReturnCode::SUCCESS
+ } else {
+ ReturnCode::FAIL
+ }
+ }
+}
diff --git a/kernel/src/hil/firmware_protection.rs b/kernel/src/hil/firmware_protection.rs
new file mode 100644
index 000000000..de0824646
--- /dev/null
+++ b/kernel/src/hil/firmware_protection.rs
@@ -0,0 +1,48 @@
+//! Interface for Firmware Protection, also called Code Readout Protection.
+
+use crate::returncode::ReturnCode;
+
+#[derive(PartialOrd, PartialEq)]
+pub enum ProtectionLevel {
+ /// Unsupported feature
+ Unknown = 0,
+ /// This should be the factory default for the chip.
+ NoProtection = 1,
+ /// At this level, only JTAG/SWD are disabled but other debugging
+ /// features may still be enabled.
+ JtagDisabled = 2,
+ /// This is the maximum level of protection the chip supports.
+ /// At this level, JTAG and all other features are expected to be
+ /// disabled and only a full chip erase may allow to recover from
+ /// that state.
+ FullyLocked = 0xff,
+}
+
+impl From<usize> for ProtectionLevel {
+ fn from(value: usize) -> Self {
+ match value {
+ 1 => ProtectionLevel::NoProtection,
+ 2 => ProtectionLevel::JtagDisabled,
+ 0xff => ProtectionLevel::FullyLocked,
+ _ => ProtectionLevel::Unknown,
+ }
+ }
+}
+
+pub trait FirmwareProtection {
+ /// Gets the current firmware protection level.
+ /// This doesn't fail and always returns a value.
+ fn get_protection(&self) -> ProtectionLevel;
+
+ /// Sets the firmware protection level.
+ /// There are four valid return values:
+ /// - SUCCESS: protection level has been set to `level`
+ /// - FAIL: something went wrong while setting the protection
+ /// level and the effective protection level is not the one
+ /// that was requested.
+ /// - EALREADY: the requested protection level is already the
+ /// level that is set.
+ /// - EINVAL: unsupported protection level or the requested
+ /// protection level is lower than the currently set one.
+ fn set_protection(&self, level: ProtectionLevel) -> ReturnCode;
+}
diff --git a/kernel/src/hil/mod.rs b/kernel/src/hil/mod.rs
index f2d3629a9..d6eb39d2d 100644
--- a/kernel/src/hil/mod.rs
+++ b/kernel/src/hil/mod.rs
@@ -9,6 +9,7 @@ pub mod dac;
pub mod digest;
pub mod eic;
pub mod entropy;
+pub mod firmware_protection;
pub mod flash;
pub mod gpio;
pub mod gpio_async;

View File

@@ -0,0 +1,24 @@
diff --git a/kernel/src/syscall_driver.rs b/kernel/src/syscall_driver.rs
index 0e2943fe4..3b5b42486 100644
--- a/kernel/src/syscall_driver.rs
+++ b/kernel/src/syscall_driver.rs
@@ -102,6 +102,19 @@ impl CommandReturn {
self.0
}
+ /// Check whether the inner `SyscallReturn` value is successful
+ pub fn is_success(&self) -> bool {
+ matches!(
+ self.0,
+ SyscallReturn::Success
+ | SyscallReturn::SuccessU32(_)
+ | SyscallReturn::SuccessU32U32(_, _)
+ | SyscallReturn::SuccessU32U32U32(_, _, _)
+ | SyscallReturn::SuccessU32U64(_, _)
+ | SyscallReturn::SuccessU64(_)
+ )
+ }
+
/// Command error
pub fn failure(rc: ErrorCode) -> Self {
CommandReturn(SyscallReturn::Failure(rc))

View File

@@ -1,43 +1,53 @@
diff --git a/chips/nrf52/src/nvmc.rs b/chips/nrf52/src/nvmc.rs
index adbc2a2b5..4092cf346 100644
index 61e94260e..b7c3be3f6 100644
--- a/chips/nrf52/src/nvmc.rs
+++ b/chips/nrf52/src/nvmc.rs
@@ -3,15 +3,19 @@
//! Used in order read and write to internal flash.
@@ -5,7 +5,14 @@
use core::cell::Cell;
+use core::convert::TryFrom;
use core::ops::{Index, IndexMut};
use kernel::common::cells::OptionalCell;
use kernel::common::cells::TakeCell;
use kernel::common::cells::VolatileCell;
use kernel::common::deferred_call::DeferredCall;
+use kernel::common::dynamic_deferred_call::{
use kernel::deferred_call::DeferredCall;
+use kernel::dynamic_deferred_call::{
+ DeferredCallHandle, DynamicDeferredCall, DynamicDeferredCallClient,
+};
use kernel::common::registers::{register_bitfields, ReadOnly, ReadWrite};
use kernel::common::StaticRef;
+use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
use kernel::hil;
-use kernel::ReturnCode;
+use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
use crate::deferred_call_tasks::DeferredCallTask;
@@ -141,7 +145,13 @@ register_bitfields! [u32,
+use kernel::process::ProcessId;
+use kernel::processbuffer::ReadableProcessBuffer;
+use kernel::syscall::CommandReturn;
use kernel::utilities::cells::OptionalCell;
use kernel::utilities::cells::TakeCell;
use kernel::utilities::cells::VolatileCell;
@@ -142,7 +149,13 @@ register_bitfields! [u32,
static DEFERRED_CALL: DeferredCall<DeferredCallTask> =
unsafe { DeferredCall::new(DeferredCallTask::Nvmc) };
+type WORD = u32;
+const WORD_SIZE: usize = core::mem::size_of::<WORD>();
const PAGE_SIZE: usize = 4096;
+const MAX_WORD_WRITES: usize = 2;
+const MAX_PAGE_ERASES: usize = 10000;
+const MAX_WORD_WRITES: u32 = 2;
+const MAX_PAGE_ERASES: u32 = 10_000;
+const WORD_MASK: usize = WORD_SIZE - 1;
+const PAGE_MASK: usize = PAGE_SIZE - 1;
/// This is a wrapper around a u8 array that is sized to a single page for the
/// nrf. Users of this module must pass an object of this type to use the
@@ -217,6 +227,10 @@ impl Nvmc {
@@ -157,13 +170,11 @@ const PAGE_SIZE: usize = 4096;
///
/// let pagebuffer = unsafe { static_init!(NrfPage, NrfPage::default()) };
/// ```
-pub struct NrfPage(pub [u8; PAGE_SIZE as usize]);
+pub struct NrfPage(pub [u8; PAGE_SIZE]);
impl Default for NrfPage {
fn default() -> Self {
- Self {
- 0: [0; PAGE_SIZE as usize],
- }
+ Self { 0: [0; PAGE_SIZE] }
}
}
impl NrfPage {
@@ -218,6 +229,10 @@ impl Nvmc {
}
}
@@ -48,18 +58,15 @@ index adbc2a2b5..4092cf346 100644
/// Configure the NVMC to allow writes to flash.
pub fn configure_writeable(&self) {
self.registers.config.write(Configuration::WEN::Wen);
@@ -229,9 +243,7 @@ impl Nvmc {
pub fn erase_uicr(&self) {
self.registers.config.write(Configuration::WEN::Een);
while !self.is_ready() {}
- self.registers
- .erasepage
- .write(ErasePage::ERASEPAGE.val(0x10001000));
@@ -233,6 +248,7 @@ impl Nvmc {
self.registers
.erasepage
.write(ErasePage::ERASEPAGE.val(0x10001000));
+ self.registers.eraseuicr.write(EraseUicr::ERASEUICR::ERASE);
while !self.is_ready() {}
}
@@ -319,7 +331,7 @@ impl Nvmc {
@@ -320,7 +336,7 @@ impl Nvmc {
// Put the NVMC in write mode.
self.registers.config.write(Configuration::WEN::Wen);
@@ -68,11 +75,13 @@ index adbc2a2b5..4092cf346 100644
let word: u32 = (data[i + 0] as u32) << 0
| (data[i + 1] as u32) << 8
| (data[i + 2] as u32) << 16
@@ -387,3 +399,237 @@ impl hil::flash::Flash for Nvmc {
@@ -388,3 +404,228 @@ impl hil::flash::Flash for Nvmc {
self.erase_page(page_number)
}
}
+
+type NvmcDriverGrant = Grant<App, UpcallCount<1>, AllowRoCount<1>, AllowRwCount<0>>;
+
+/// Provides access to the writeable flash regions of the application.
+///
+/// The purpose of this driver is to provide low-level access to the embedded flash of nRF52 boards
@@ -112,21 +121,17 @@ index adbc2a2b5..4092cf346 100644
+/// - ALLOW(0): The allow slice for COMMAND(2).
+pub struct SyscallDriver {
+ nvmc: &'static Nvmc,
+ apps: Grant<App>,
+ waiting: OptionalCell<AppId>,
+ apps: NvmcDriverGrant,
+ waiting: OptionalCell<ProcessId>,
+ deferred_caller: &'static DynamicDeferredCall,
+ deferred_handle: OptionalCell<DeferredCallHandle>,
+ buffer: TakeCell<'static, [u8]>,
+}
+
+pub const DRIVER_NUM: usize = 0x50003;
+
+#[derive(Default)]
+pub struct App {
+ /// The callback for COMMAND(2) and COMMAND(3).
+ callback: Option<Callback>,
+ /// The allow slice for COMMAND(2).
+ slice: Option<AppSlice<Shared, u8>>,
+}
+pub struct App {}
+
+fn is_write_needed(old: u32, new: u32) -> bool {
+ // No need to write if it would not modify the current value.
@@ -136,8 +141,9 @@ index adbc2a2b5..4092cf346 100644
+impl SyscallDriver {
+ pub fn new(
+ nvmc: &'static Nvmc,
+ apps: Grant<App>,
+ apps: NvmcDriverGrant,
+ deferred_caller: &'static DynamicDeferredCall,
+ buffer: &'static mut [u8],
+ ) -> SyscallDriver {
+ nvmc.configure_readonly();
+ SyscallDriver {
@@ -146,6 +152,7 @@ index adbc2a2b5..4092cf346 100644
+ waiting: OptionalCell::empty(),
+ deferred_caller,
+ deferred_handle: OptionalCell::empty(),
+ buffer: TakeCell::new(buffer),
+ }
+ }
+
@@ -171,9 +178,9 @@ index adbc2a2b5..4092cf346 100644
+ /// - `ptr` must be word-aligned.
+ /// - `slice.len()` must be word-aligned.
+ /// - The slice starting at `ptr` of length `slice.len()` must fit in the storage.
+ fn write_slice(&self, ptr: usize, slice: &[u8]) -> ReturnCode {
+ fn write_slice(&self, ptr: usize, slice: &[u8]) -> CommandReturn {
+ if ptr & WORD_MASK != 0 || slice.len() & WORD_MASK != 0 {
+ return ReturnCode::EINVAL;
+ return CommandReturn::failure(ErrorCode::INVAL);
+ }
+ self.nvmc.configure_writeable();
+ for (i, chunk) in slice.chunks(WORD_SIZE).enumerate() {
@@ -188,7 +195,7 @@ index adbc2a2b5..4092cf346 100644
+ self.nvmc.configure_readonly();
+ self.deferred_handle
+ .map(|handle| self.deferred_caller.set(*handle));
+ ReturnCode::SUCCESS
+ CommandReturn::success()
+ }
+
+ /// Erases a page at a page-aligned address.
@@ -198,162 +205,249 @@ index adbc2a2b5..4092cf346 100644
+ /// Fails with `EINVAL` if any of the following conditions does not hold:
+ /// - `ptr` must be page-aligned.
+ /// - The slice starting at `ptr` of length `PAGE_SIZE` must fit in the storage.
+ fn erase_page(&self, ptr: usize) -> ReturnCode {
+ fn erase_page(&self, ptr: usize) -> CommandReturn {
+ if ptr & PAGE_MASK != 0 {
+ return ReturnCode::EINVAL;
+ return CommandReturn::failure(ErrorCode::INVAL);
+ }
+ self.nvmc.erase_page_helper(ptr / PAGE_SIZE);
+ self.nvmc.configure_readonly();
+ self.deferred_handle
+ .map(|handle| self.deferred_caller.set(*handle));
+ ReturnCode::SUCCESS
+ CommandReturn::success()
+ }
+}
+
+impl DynamicDeferredCallClient for SyscallDriver {
+ fn call(&self, _handle: DeferredCallHandle) {
+ self.waiting.take().map(|appid| {
+ self.apps.enter(appid, |app, _| {
+ app.callback.map(|mut cb| {
+ cb.schedule(0, 0, 0);
+ });
+ self.waiting.take().map(|process_id| {
+ self.apps.enter(process_id, |_, kernel_data| {
+ kernel_data.schedule_upcall(0, (0, 0, 0))
+ })
+ });
+ }
+}
+
+impl Driver for SyscallDriver {
+ fn subscribe(
+ &self,
+ subscribe_num: usize,
+ callback: Option<Callback>,
+ appid: AppId,
+ ) -> ReturnCode {
+ match subscribe_num {
+ 0 => self
+ .apps
+ .enter(appid, |app, _| {
+ app.callback = callback;
+ ReturnCode::SUCCESS
+ })
+ .unwrap_or_else(|err| err.into()),
+ _ => ReturnCode::ENOSUPPORT,
+ }
+impl kernel::syscall::SyscallDriver for SyscallDriver {
+ fn allocate_grant(&self, process_id: ProcessId) -> Result<(), kernel::process::Error> {
+ self.apps.enter(process_id, |_, _| {})
+ }
+
+ fn command(&self, cmd: usize, arg0: usize, arg1: usize, appid: AppId) -> ReturnCode {
+ match (cmd, arg0, arg1) {
+ (0, _, _) => ReturnCode::SUCCESS,
+ fn command(
+ &self,
+ command_num: usize,
+ r2: usize,
+ r3: usize,
+ process_id: ProcessId,
+ ) -> CommandReturn {
+ match (command_num, r2, r3) {
+ (0, _, _) => CommandReturn::success(),
+
+ (1, 0, _) => ReturnCode::SuccessWithValue { value: WORD_SIZE },
+ (1, 1, _) => ReturnCode::SuccessWithValue { value: PAGE_SIZE },
+ (1, 2, _) => ReturnCode::SuccessWithValue {
+ value: MAX_WORD_WRITES,
+ },
+ (1, 3, _) => ReturnCode::SuccessWithValue {
+ value: MAX_PAGE_ERASES,
+ },
+ (1, _, _) => ReturnCode::EINVAL,
+ (1, 0, _) => CommandReturn::success_u32(WORD_SIZE.try_into().unwrap()),
+ (1, 1, _) => CommandReturn::success_u32(PAGE_SIZE.try_into().unwrap()),
+ (1, 2, _) => CommandReturn::success_u32(MAX_WORD_WRITES),
+ (1, 3, _) => CommandReturn::success_u32(MAX_PAGE_ERASES),
+ (1, _, _) => CommandReturn::failure(ErrorCode::INVAL),
+
+ (2, ptr, len) => self
+ (2, ptr, _len) => self
+ .apps
+ .enter(appid, |app, _| {
+ let slice = match app.slice.take() {
+ None => return ReturnCode::EINVAL,
+ Some(slice) => slice,
+ };
+ if len != slice.len() {
+ return ReturnCode::EINVAL;
+ }
+ if self.waiting.is_some() {
+ return ReturnCode::EBUSY;
+ }
+ self.waiting.set(appid);
+ self.write_slice(ptr, slice.as_ref())
+ .enter(process_id, |_, kernel| {
+ kernel
+ .get_readonly_processbuffer(0)
+ .and_then(|processbuffer| {
+ processbuffer.enter(|app_buf| {
+ // Copy contents to the internal buffer first
+ self.buffer.take().map_or(
+ CommandReturn::failure(ErrorCode::RESERVE),
+ |buffer| {
+ // as the drivers buffer can be bigger than the app buffer,
+ // we choose the minimum to not copy anymore than we need
+ let len = core::cmp::min(buffer.len(), app_buf.len());
+
+ // safety check when the app buffer is too large
+ if app_buf.len() > buffer.len() {
+ return CommandReturn::failure(ErrorCode::INVAL);
+ }
+
+ let d = &app_buf[0..len];
+ for (i, v) in buffer.as_mut()[0..len].iter_mut().enumerate()
+ {
+ *v = d[i].get();
+ }
+
+ if self.waiting.is_some() {
+ return CommandReturn::failure(ErrorCode::BUSY);
+ }
+ self.waiting.set(process_id);
+ let result = self.write_slice(ptr, &buffer[0..len]);
+ self.buffer.replace(buffer);
+ result
+ },
+ )
+ })
+ })
+ .unwrap_or(CommandReturn::failure(ErrorCode::RESERVE))
+ })
+ .unwrap_or_else(|err| err.into()),
+ .unwrap_or_else(|e| CommandReturn::failure(e.into())),
+
+ (3, ptr, len) => {
+ if len != PAGE_SIZE {
+ return ReturnCode::EINVAL;
+ return CommandReturn::failure(ErrorCode::INVAL);
+ }
+ if self.waiting.is_some() {
+ return ReturnCode::EBUSY;
+ return CommandReturn::failure(ErrorCode::BUSY);
+ }
+ self.waiting.set(appid);
+ self.waiting.set(process_id);
+ self.erase_page(ptr)
+ }
+
+ _ => ReturnCode::ENOSUPPORT,
+ _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
+ }
+ }
+}
diff --git a/kernel/src/kernel.rs b/kernel/src/kernel.rs
index a1d081ad7..583fbe8d6 100644
--- a/kernel/src/kernel.rs
+++ b/kernel/src/kernel.rs
@@ -38,6 +38,20 @@ use crate::utilities::cells::NumericCellExt;
/// is less than this threshold.
pub(crate) const MIN_QUANTA_THRESHOLD_US: u32 = 500;
+/// Represents the type of a storage slice.
+#[derive(Copy, Clone)]
+pub enum StorageType {
+ Store = 1,
+ Partition = 2,
+}
+
+ fn allow(
+ &self,
+ appid: AppId,
+ allow_num: usize,
+ slice: Option<AppSlice<Shared, u8>>,
+ ) -> ReturnCode {
+ match allow_num {
+ 0 => self
+ .apps
+ .enter(appid, |app, _| {
+ app.slice = slice;
+ ReturnCode::SUCCESS
+ })
+ .unwrap_or_else(|err| err.into()),
+ _ => ReturnCode::ENOSUPPORT,
+/// Represents a storage location in flash.
+pub struct StorageLocation {
+ pub address: usize,
+ pub size: usize,
+ pub storage_type: StorageType,
+}
+
/// Main object for the kernel. Each board will need to create one.
pub struct Kernel {
/// How many "to-do" items exist at any given time. These include
@@ -47,6 +61,9 @@ pub struct Kernel {
/// This holds a pointer to the static array of Process pointers.
processes: &'static [Option<&'static dyn process::Process>],
+ /// List of storage locations.
+ storage_locations: &'static [StorageLocation],
+
/// A counter which keeps track of how many process identifiers have been
/// created. This is used to create new unique identifiers for processes.
process_identifier_max: Cell<usize>,
@@ -110,15 +127,26 @@ fn try_allocate_grant(driver: &dyn SyscallDriver, process: &dyn process::Process
impl Kernel {
pub fn new(processes: &'static [Option<&'static dyn process::Process>]) -> Kernel {
+ Kernel::new_with_storage(processes, &[])
+ }
+ pub fn new_with_storage(
+ processes: &'static [Option<&'static dyn process::Process>],
+ storage_locations: &'static [StorageLocation],
+ ) -> Kernel {
Kernel {
work: Cell::new(0),
processes,
+ storage_locations,
process_identifier_max: Cell::new(0),
grant_counter: Cell::new(0),
grants_finalized: Cell::new(false),
}
}
+ pub fn storage_locations(&self) -> &'static [StorageLocation] {
+ self.storage_locations
+ }
+
/// Something was scheduled for a process, so there is more work to do.
///
/// This is only exposed in the core kernel crate.
@@ -1374,3 +1402,14 @@ impl Kernel {
}
}
}
+
+impl TryFrom<StorageType> for u32 {
+ type Error = ();
+
+ fn try_from(value: StorageType) -> Result<Self, Self::Error> {
+ match value {
+ StorageType::Store => Ok(StorageType::Store as u32),
+ StorageType::Partition => Ok(StorageType::Partition as u32),
+ }
+ }
+}
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
index 49e1e5182..807170195 100644
index 028f30220..8880bc000 100644
--- a/kernel/src/lib.rs
+++ b/kernel/src/lib.rs
@@ -122,7 +122,7 @@ pub use crate::sched::cooperative::{CoopProcessNode, CooperativeSched};
pub use crate::sched::mlfq::{MLFQProcessNode, MLFQSched};
pub use crate::sched::priority::PrioritySched;
pub use crate::sched::round_robin::{RoundRobinProcessNode, RoundRobinSched};
-pub use crate::sched::{Kernel, Scheduler};
+pub use crate::sched::{Kernel, Scheduler, StorageLocation};
// Export only select items from the process module. To remove the name conflict
// this cannot be called `process`, so we use a shortened version. These
@@ -125,5 +125,6 @@ mod syscall_driver;
// Core resources exposed as `kernel::Type`.
pub use crate::errorcode::ErrorCode;
pub use crate::kernel::Kernel;
+pub use crate::kernel::{StorageLocation, StorageType};
pub use crate::process::ProcessId;
pub use crate::scheduler::Scheduler;
diff --git a/kernel/src/memop.rs b/kernel/src/memop.rs
index 348c746a5..5465c95f4 100644
index 51d89f37c..45ab3856b 100644
--- a/kernel/src/memop.rs
+++ b/kernel/src/memop.rs
@@ -108,6 +108,25 @@ pub(crate) fn memop(process: &dyn ProcessType, op_type: usize, r1: usize) -> Ret
ReturnCode::SUCCESS
@@ -107,6 +107,37 @@ pub(crate) fn memop(process: &dyn Process, op_type: usize, r1: usize) -> Syscall
SyscallReturn::Success
}
+ // Op Type 12: Number of storage locations.
+ 12 => ReturnCode::SuccessWithValue { value: process.number_storage_locations() },
+ // 12 - 15 are required for the custom persistent store driver
+ // currently only implemented in the nvmc module of nrf52 series
+ // driver number: 0x50003
+
+ // Op Type 13: The start address of the storage location indexed by r1.
+ // Op Type 12: Number of storage locations
+ 12 => SyscallReturn::SuccessU32(process.number_storage_locations() as u32),
+
+ // Op Type 13: The start address of the storage location indexed by r1
+ 13 => {
+ match process.get_storage_location(r1) {
+ None => ReturnCode::FAIL,
+ Some(x) => ReturnCode::SuccessWithValue { value: x.address }
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
+ Some(x) => SyscallReturn::SuccessU32(x.address as u32),
+ }
+ }
+
+ // Op Type 14: The size of the storage location indexed by r1.
+ 14 => {
+ match process.get_storage_location(r1) {
+ None => ReturnCode::FAIL,
+ Some(x) => ReturnCode::SuccessWithValue { value: x.size }
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
+ Some(x) => SyscallReturn::SuccessU32(x.size as u32),
+ }
+ }
+
_ => ReturnCode::ENOSUPPORT,
+ // Op Type 15: The type of the storage location indexed by r1.
+ 15 => {
+ match process.get_storage_location(r1) {
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
+ Some(x) => SyscallReturn::SuccessU32(x.storage_type as u32)
+ }
+ }
+
_ => SyscallReturn::Failure(ErrorCode::NOSUPPORT),
}
}
diff --git a/kernel/src/process.rs b/kernel/src/process.rs
index c52754be3..ae6a58341 100644
index c1794d9bf..4fbad3f0b 100644
--- a/kernel/src/process.rs
+++ b/kernel/src/process.rs
@@ -359,6 +359,15 @@ pub trait ProcessType {
@@ -9,7 +9,7 @@ use core::str;
use crate::capabilities;
use crate::errorcode::ErrorCode;
use crate::ipc;
-use crate::kernel::Kernel;
+use crate::kernel::{Kernel, StorageLocation};
use crate::platform::mpu::{self};
use crate::processbuffer::{ReadOnlyProcessBuffer, ReadWriteProcessBuffer};
use crate::storage_permissions;
@@ -335,6 +335,15 @@ pub trait Process {
/// writeable flash region.
fn get_writeable_flash_region(&self, region_index: usize) -> (u32, u32);
@@ -361,7 +455,7 @@ index c52754be3..ae6a58341 100644
+ fn number_storage_locations(&self) -> usize;
+
+ /// Get the i-th storage location.
+ fn get_storage_location(&self, index: usize) -> Option<&crate::StorageLocation>;
+ fn get_storage_location(&self, index: usize) -> Option<&StorageLocation>;
+
+ /// Whether a slice fits in a storage location.
+ fn fits_in_storage_location(&self, ptr: usize, len: usize) -> bool;
@@ -369,15 +463,28 @@ index c52754be3..ae6a58341 100644
/// Debug function to update the kernel on where the stack starts for this
/// process. Processes are not required to call this through the memop
/// system call, but it aids in debugging the process.
@@ -1048,6 +1057,35 @@ impl<C: Chip> ProcessType for Process<'_, C> {
self.header.get_writeable_flash_region(region_index)
diff --git a/kernel/src/process_standard.rs b/kernel/src/process_standard.rs
index fa76e2c68..dd03e8f29 100644
--- a/kernel/src/process_standard.rs
+++ b/kernel/src/process_standard.rs
@@ -14,7 +14,7 @@ use crate::collections::ring_buffer::RingBuffer;
use crate::config;
use crate::debug;
use crate::errorcode::ErrorCode;
-use crate::kernel::Kernel;
+use crate::kernel::{Kernel, StorageLocation};
use crate::platform::chip::Chip;
use crate::platform::mpu::{self, MPU};
use crate::process::{Error, FunctionCall, FunctionCallSource, Process, State, Task};
@@ -258,6 +258,35 @@ impl<C: Chip> Process for ProcessStandard<'_, C> {
ret
}
+ fn number_storage_locations(&self) -> usize {
+ self.kernel.storage_locations().len()
+ }
+
+ fn get_storage_location(&self, index: usize) -> Option<&crate::StorageLocation> {
+ fn get_storage_location(&self, index: usize) -> Option<&StorageLocation> {
+ self.kernel.storage_locations().get(index)
+ }
+
@@ -402,10 +509,10 @@ index c52754be3..ae6a58341 100644
+ })
+ }
+
fn update_stack_start_pointer(&self, stack_pointer: *const u8) {
if stack_pointer >= self.mem_start() && stack_pointer < self.mem_end() {
self.debug.map(|debug| {
@@ -1751,6 +1789,33 @@ impl<C: 'static + Chip> Process<'_, C> {
fn ready(&self) -> bool {
self.tasks.map_or(false, |ring_buf| ring_buf.has_elements())
|| self.state.get() == State::Running
@@ -1366,6 +1395,33 @@ impl<C: 'static + Chip> ProcessStandard<'_, C> {
return Err(ProcessLoadError::MpuInvalidFlashLength);
}
@@ -436,60 +543,6 @@ index c52754be3..ae6a58341 100644
+ return Ok((None, remaining_memory));
+ }
+
// Determine how much space we need in the application's
// memory space just for kernel and grant state. We need to make
// sure we allocate enough memory just for that.
diff --git a/kernel/src/sched.rs b/kernel/src/sched.rs
index 10626a2e1..8844bc6c3 100644
--- a/kernel/src/sched.rs
+++ b/kernel/src/sched.rs
@@ -118,6 +118,12 @@ pub enum SchedulingDecision {
TrySleep,
}
+/// Represents a storage location in flash.
+pub struct StorageLocation {
+ pub address: usize,
+ pub size: usize,
+}
+
/// Main object for the kernel. Each board will need to create one.
pub struct Kernel {
/// How many "to-do" items exist at any given time. These include
@@ -127,6 +133,9 @@ pub struct Kernel {
/// This holds a pointer to the static array of Process pointers.
processes: &'static [Option<&'static dyn process::ProcessType>],
+ /// List of storage locations.
+ storage_locations: &'static [StorageLocation],
+
/// A counter which keeps track of how many process identifiers have been
/// created. This is used to create new unique identifiers for processes.
process_identifier_max: Cell<usize>,
@@ -170,9 +179,17 @@ pub enum StoppedExecutingReason {
impl Kernel {
pub fn new(processes: &'static [Option<&'static dyn process::ProcessType>]) -> Kernel {
+ Kernel::new_with_storage(processes, &[])
+ }
+
+ pub fn new_with_storage(
+ processes: &'static [Option<&'static dyn process::ProcessType>],
+ storage_locations: &'static [StorageLocation],
+ ) -> Kernel {
Kernel {
work: Cell::new(0),
processes,
+ storage_locations: storage_locations,
process_identifier_max: Cell::new(0),
grant_counter: Cell::new(0),
grants_finalized: Cell::new(false),
@@ -900,4 +917,8 @@ impl Kernel {
(return_reason, time_executed_us)
}
+
+ pub fn storage_locations(&self) -> &'static [StorageLocation] {
+ self.storage_locations
+ }
}
// Determine how much space we need in the application's memory space
// just for kernel and grant state. We need to make sure we allocate
// enough memory just for that.

View File

@@ -1,55 +0,0 @@
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
index 807170195..a13413791 100644
--- a/kernel/src/lib.rs
+++ b/kernel/src/lib.rs
@@ -122,7 +122,7 @@ pub use crate::sched::cooperative::{CoopProcessNode, CooperativeSched};
pub use crate::sched::mlfq::{MLFQProcessNode, MLFQSched};
pub use crate::sched::priority::PrioritySched;
pub use crate::sched::round_robin::{RoundRobinProcessNode, RoundRobinSched};
-pub use crate::sched::{Kernel, Scheduler, StorageLocation};
+pub use crate::sched::{Kernel, Scheduler, StorageLocation, StorageType};
// Export only select items from the process module. To remove the name conflict
// this cannot be called `process`, so we use a shortened version. These
diff --git a/kernel/src/memop.rs b/kernel/src/memop.rs
index 5465c95f4..e596648f7 100644
--- a/kernel/src/memop.rs
+++ b/kernel/src/memop.rs
@@ -127,6 +127,14 @@ pub(crate) fn memop(process: &dyn ProcessType, op_type: usize, r1: usize) -> Ret
}
}
+ // Op Type 15: The type of the storage location indexed by r1.
+ 15 => {
+ match process.get_storage_location(r1) {
+ None => ReturnCode::FAIL,
+ Some(x) => ReturnCode::SuccessWithValue { value: x.storage_type as usize }
+ }
+ }
+
_ => ReturnCode::ENOSUPPORT,
}
}
diff --git a/kernel/src/sched.rs b/kernel/src/sched.rs
index 8844bc6c3..00c13a7c6 100644
--- a/kernel/src/sched.rs
+++ b/kernel/src/sched.rs
@@ -118,10 +118,18 @@ pub enum SchedulingDecision {
TrySleep,
}
+/// Represents the type of a storage slice.
+#[derive(Copy, Clone)]
+pub enum StorageType {
+ Store = 1,
+ Partition = 2,
+}
+
/// Represents a storage location in flash.
pub struct StorageLocation {
pub address: usize,
pub size: usize,
+ pub storage_type: StorageType,
}
/// Main object for the kernel. Each board will need to create one.

View File

@@ -1,13 +0,0 @@
diff --git a/arch/cortex-m/src/syscall.rs b/arch/cortex-m/src/syscall.rs
index c78b1c9fb..2769d0138 100644
--- a/arch/cortex-m/src/syscall.rs
+++ b/arch/cortex-m/src/syscall.rs
@@ -65,7 +65,7 @@ impl kernel::syscall::UserspaceKernelBoundary for SysCall {
//
// The 1.x Tock kernel allocates at least 3 kB to processes, and we need
// to ensure that happens as userspace may expect it.
- 3 * 1024
+ 16 * 1024
// TOCK 2.0
//

View File

@@ -25,21 +25,20 @@ index e6cf3ff56..7b3ea5a59 100644
if uicr
.get_psel0_reset_pin()
diff --git a/chips/nrf52/src/uicr.rs b/chips/nrf52/src/uicr.rs
index 4924bbd44..788533210 100644
index 3b51717d0..3086394aa 100644
--- a/chips/nrf52/src/uicr.rs
+++ b/chips/nrf52/src/uicr.rs
@@ -1,38 +1,44 @@
@@ -1,39 +1,43 @@
//! User information configuration registers
-//!
-//! Minimal implementation to support activation of the reset button on
-//! nRF52-DK.
use enum_primitive::cast::FromPrimitive;
-use kernel::common::registers::{register_bitfields, ReadWrite};
+use kernel::common::registers::{register_bitfields, register_structs, ReadWrite};
use kernel::common::StaticRef;
+use kernel::hil;
+use kernel::ReturnCode;
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
-use kernel::utilities::registers::{register_bitfields, ReadWrite};
+use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
use kernel::utilities::StaticRef;
use crate::gpio::Pin;
@@ -70,40 +69,41 @@ index 4924bbd44..788533210 100644
+ unsafe { StaticRef::new(0x10001000 as *const UicrRegisters) };
+
+register_structs! {
+ UicrRegisters {
+ (0x000 => _reserved1),
+ /// Reserved for Nordic firmware design
+ (0x014 => nrffw: [ReadWrite<u32>; 13]),
+ (0x048 => _reserved2),
+ /// Reserved for Nordic hardware design
+ (0x050 => nrfhw: [ReadWrite<u32>; 12]),
+ /// Reserved for customer
+ (0x080 => customer: [ReadWrite<u32>; 32]),
+ (0x100 => _reserved3),
+ /// Mapping of the nRESET function (see POWER chapter for details)
+ (0x200 => pselreset0: ReadWrite<u32, Pselreset::Register>),
+ /// Mapping of the nRESET function (see POWER chapter for details)
+ (0x204 => pselreset1: ReadWrite<u32, Pselreset::Register>),
+ /// Access Port protection
+ (0x208 => approtect: ReadWrite<u32, ApProtect::Register>),
+ /// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO
+ /// - Address: 0x20c - 0x210
+ (0x20c => nfcpins: ReadWrite<u32, NfcPins::Register>),
+ (0x210 => debugctrl: ReadWrite<u32, DebugControl::Register>),
+ (0x214 => _reserved4),
+ /// External circuitry to be supplied from VDD pin.
+ (0x300 => extsupply: ReadWrite<u32, ExtSupply::Register>),
+ /// GPIO reference voltage
+ (0x304 => regout0: ReadWrite<u32, RegOut::Register>),
+ (0x308 => @END),
+ }
+ UicrRegisters {
+ (0x000 => _reserved1),
+ /// Reserved for Nordic firmware design
+ (0x014 => nrffw: [ReadWrite<u32>; 13]),
+ (0x048 => _reserved2),
+ /// Reserved for Nordic hardware design
+ (0x050 => nrfhw: [ReadWrite<u32>; 12]),
+ /// Reserved for customer
+ (0x080 => customer: [ReadWrite<u32>; 32]),
+ (0x100 => _reserved3),
+ /// Mapping of the nRESET function (see POWER chapter for details)
+ (0x200 => pselreset0: ReadWrite<u32, Pselreset::Register>),
+ /// Mapping of the nRESET function (see POWER chapter for details)
+ (0x204 => pselreset1: ReadWrite<u32, Pselreset::Register>),
+ /// Access Port protection
+ (0x208 => approtect: ReadWrite<u32, ApProtect::Register>),
+ /// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO
+ /// - Address: 0x20c - 0x210
+ (0x20c => nfcpins: ReadWrite<u32, NfcPins::Register>),
+ (0x210 => debugctrl: ReadWrite<u32, DebugControl::Register>),
+ (0x214 => _reserved4),
+ /// External circuitry to be supplied from VDD pin.
+ (0x300 => extsupply: ReadWrite<u32, ExtSupply::Register>),
+ /// GPIO reference voltage
+ (0x304 => regout0: ReadWrite<u32, RegOut::Register>),
+ (0x308 => @END),
+ }
}
register_bitfields! [u32,
@@ -58,6 +64,21 @@ register_bitfields! [u32,
@@ -59,6 +63,23 @@ register_bitfields! [u32,
DISABLED = 0xff
]
],
+
+ /// Processor debug control
+ DebugControl [
+ CPUNIDEN OFFSET(0) NUMBITS(8) [
@@ -119,11 +119,12 @@ index 4924bbd44..788533210 100644
+ DISABLED = 0x00
+ ]
+ ],
+
/// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO
NfcPins [
/// Setting pins dedicated to NFC functionality
@@ -172,6 +193,18 @@ impl Uicr {
self.registers.nfcpins.matches_all(NfcPins::PROTECT::NFC)
@@ -169,6 +190,18 @@ impl Uicr {
}
}
+ pub fn get_dfu_params(&self) -> (u32, u32) {
@@ -138,6 +139,6 @@ index 4924bbd44..788533210 100644
+ self.registers.nrffw[1].set(dfu_settings_addr);
+ }
+
pub fn is_ap_protect_enabled(&self) -> bool {
// Here we compare to DISABLED value because any other value should enable the protection.
!self
pub fn is_nfc_pins_protection_enabled(&self) -> bool {
self.registers.nfcpins.matches_all(NfcPins::PROTECT::NFC)
}

View File

@@ -1,329 +0,0 @@
diff --git a/capsules/src/usb/app.rs b/capsules/src/usb/app.rs
new file mode 100644
index 000000000..c2f434f12
--- /dev/null
+++ b/capsules/src/usb/app.rs
@@ -0,0 +1,65 @@
+use kernel::{AppSlice, Callback, Shared};
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum Side {
+ Transmit,
+ Receive,
+ TransmitOrReceive,
+}
+
+impl Side {
+ pub fn can_transmit(&self) -> bool {
+ match self {
+ Side::Transmit | Side::TransmitOrReceive => true,
+ Side::Receive => false,
+ }
+ }
+
+ pub fn can_receive(&self) -> bool {
+ match self {
+ Side::Receive | Side::TransmitOrReceive => true,
+ Side::Transmit => false,
+ }
+ }
+}
+
+#[derive(Default)]
+pub struct App {
+ // Only one app can be connected to this driver, to avoid needing to route packets among apps.
+ // This field tracks this status.
+ pub connected: bool,
+ // Currently enabled transaction side. Subscribing to a callback or allowing a buffer
+ // automatically sets the corresponding side. Clearing both the callback and the buffer resets
+ // the side to None.
+ pub side: Option<Side>,
+ pub callback: Option<Callback>,
+ pub buffer: Option<AppSlice<Shared, u8>>,
+ // Whether the app is waiting for the kernel signaling a packet transfer.
+ pub waiting: bool,
+}
+
+impl App {
+ pub fn can_receive_packet(&self) -> bool {
+ self.waiting && self.side.map_or(false, |side| side.can_receive()) && self.buffer.is_some()
+ }
+
+ pub fn check_side(&mut self) {
+ if self.callback.is_none() && self.buffer.is_none() && !self.waiting {
+ self.side = None;
+ }
+ }
+
+ pub fn set_side(&mut self, side: Side) -> bool {
+ match self.side {
+ None => {
+ self.side = Some(side);
+ true
+ }
+ Some(app_side) => side == app_side,
+ }
+ }
+
+ pub fn is_ready_for_command(&self, side: Side) -> bool {
+ self.buffer.is_some() && self.callback.is_some() && self.side == Some(side)
+ }
+}
diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs
index 3f3a4f646..cb5e0af97 100644
--- a/capsules/src/usb/mod.rs
+++ b/capsules/src/usb/mod.rs
@@ -1,3 +1,4 @@
+pub mod app;
pub mod cdc;
pub mod descriptors;
pub mod usb_ctap;
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
index da3d16d85..e8f1a87a4 100644
--- a/capsules/src/usb/usb_ctap.rs
+++ b/capsules/src/usb/usb_ctap.rs
@@ -1,7 +1,7 @@
+use super::app::{App, Side};
use super::usbc_ctap_hid::ClientCtapHID;
-use kernel::hil;
use kernel::hil::usb::Client;
-use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
+use kernel::{hil, AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
/// Syscall number
use crate::driver;
@@ -25,73 +25,15 @@ pub const CTAP_SUBSCRIBE_TRANSMIT_OR_RECEIVE: usize = 3;
pub const CTAP_CALLBACK_TRANSMITED: usize = 1;
pub const CTAP_CALLBACK_RECEIVED: usize = 2;
-#[derive(Clone, Copy, PartialEq, Eq)]
-enum Side {
- Transmit,
- Receive,
- TransmitOrReceive,
-}
-
-impl Side {
- fn can_transmit(&self) -> bool {
- match self {
- Side::Transmit | Side::TransmitOrReceive => true,
- Side::Receive => false,
- }
- }
-
- fn can_receive(&self) -> bool {
- match self {
- Side::Receive | Side::TransmitOrReceive => true,
- Side::Transmit => false,
- }
- }
-}
-
-#[derive(Default)]
-pub struct App {
- // Only one app can be connected to this driver, to avoid needing to route packets among apps.
- // This field tracks this status.
- connected: bool,
- // Currently enabled transaction side. Subscribing to a callback or allowing a buffer
- // automatically sets the corresponding side. Clearing both the callback and the buffer resets
- // the side to None.
- side: Option<Side>,
- callback: Option<Callback>,
- buffer: Option<AppSlice<Shared, u8>>,
- // Whether the app is waiting for the kernel signaling a packet transfer.
- waiting: bool,
-}
-
-impl App {
- fn check_side(&mut self) {
- if self.callback.is_none() && self.buffer.is_none() && !self.waiting {
- self.side = None;
- }
- }
-
- fn set_side(&mut self, side: Side) -> bool {
- match self.side {
- None => {
- self.side = Some(side);
- true
- }
- Some(app_side) => side == app_side,
- }
- }
-
- fn is_ready_for_command(&self, side: Side) -> bool {
- self.buffer.is_some() && self.callback.is_some() && self.side == Some(side)
- }
-}
-
pub trait CtapUsbClient {
// Whether this client is ready to receive a packet. This must be checked before calling
- // packet_received().
- fn can_receive_packet(&self) -> bool;
+ // packet_received(). If App is not supplied, it will be found from the implemntation's
+ // members.
+ fn can_receive_packet(&self, app: &Option<&mut App>) -> bool;
// Signal to the client that a packet has been received.
- fn packet_received(&self, packet: &[u8; 64]);
+ // If App is not supplied, it will be found from the implemntation's members.
+ fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>);
// Signal to the client that a packet has been transmitted.
fn packet_transmitted(&self);
@@ -106,38 +48,49 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
pub fn new(usb_client: &'a ClientCtapHID<'a, 'b, C>, apps: Grant<App>) -> Self {
CtapUsbSyscallDriver { usb_client, apps }
}
+
+ fn app_packet_received(&self, packet: &[u8; 64], app: &mut App) {
+ if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive()) {
+ if let Some(buf) = &mut app.buffer {
+ // Copy the packet to the app's allowed buffer.
+ buf.as_mut().copy_from_slice(packet);
+ app.waiting = false;
+ // Signal to the app that a packet is ready.
+ app.callback
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
+ }
+ }
+ }
}
impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDriver<'a, 'b, C> {
- fn can_receive_packet(&self) -> bool {
+ fn can_receive_packet(&self, app: &Option<&mut App>) -> bool {
let mut result = false;
- for app in self.apps.iter() {
- app.enter(|app, _| {
- if app.connected {
- result = app.waiting
- && app.side.map_or(false, |side| side.can_receive())
- && app.buffer.is_some();
+ match app {
+ None => {
+ for app in self.apps.iter() {
+ app.enter(|a, _| {
+ if a.connected {
+ result = a.can_receive_packet();
+ }
+ })
}
- });
+ }
+ Some(a) => result = a.can_receive_packet(),
}
result
}
- fn packet_received(&self, packet: &[u8; 64]) {
- for app in self.apps.iter() {
- app.enter(|app, _| {
- if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive())
- {
- if let Some(buf) = &mut app.buffer {
- // Copy the packet to the app's allowed buffer.
- buf.as_mut().copy_from_slice(packet);
- app.waiting = false;
- // Signal to the app that a packet is ready.
- app.callback
- .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
- }
+ fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>) {
+ match app {
+ None => {
+ for app in self.apps.iter() {
+ app.enter(|a, _| {
+ self.app_packet_received(packet, a);
+ })
}
- });
+ }
+ Some(a) => self.app_packet_received(packet, a),
}
}
@@ -282,7 +235,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
ReturnCode::EALREADY
} else {
app.waiting = true;
- self.usb_client.receive_packet();
+ self.usb_client.receive_packet(app);
ReturnCode::SUCCESS
}
} else {
@@ -303,7 +256,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
} else {
// Indicates to the driver that we can receive any pending packet.
app.waiting = true;
- self.usb_client.receive_packet();
+ self.usb_client.receive_packet(app);
if !app.waiting {
// The call to receive_packet() collected a pending packet.
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
index abf224f97..d47e5f644 100644
--- a/capsules/src/usb/usbc_ctap_hid.rs
+++ b/capsules/src/usb/usbc_ctap_hid.rs
@@ -11,6 +11,7 @@ use super::descriptors::HIDSubordinateDescriptor;
use super::descriptors::InterfaceDescriptor;
use super::descriptors::ReportDescriptor;
use super::descriptors::TransferDirection;
+use super::app::App;
use super::usb_ctap::CtapUsbClient;
use super::usbc_client_ctrl::ClientCtrl;
use core::cell::Cell;
@@ -257,7 +258,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
}
}
- pub fn receive_packet(&'a self) -> bool {
+ pub fn receive_packet(&'a self, app: &mut App) -> bool {
if self.pending_out.get() {
// The previous packet has not yet been received, reject the new one.
false
@@ -267,7 +268,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
// Otherwise, there's nothing to do, the controller will send us a packet_out when a
// packet arrives.
if self.delayed_out.take() {
- if self.send_packet_to_client() {
+ if self.send_packet_to_client(Some(app)) {
// If that succeeds, alert the controller that we can now
// receive data on the Interrupt OUT endpoint.
self.controller().endpoint_resume_out(1);
@@ -280,7 +281,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
// Send an OUT packet available in the controller back to the client.
// This returns false if the client is not ready to receive a packet, and true if the client
// successfully accepted the packet.
- fn send_packet_to_client(&'a self) -> bool {
+ fn send_packet_to_client(&'a self, app: Option<&mut App>) -> bool {
// Copy the packet into a buffer to send to the client.
let mut buf: [u8; 64] = [0; 64];
for (i, x) in self.out_buffer.buf.iter().enumerate() {
@@ -292,7 +293,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
// Notify the client
if self
.client
- .map_or(false, |client| client.can_receive_packet())
+ .map_or(false, |client| client.can_receive_packet(&app))
{
assert!(self.pending_out.take());
@@ -301,7 +302,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
// should be re-transmitted or not.
self.cancel_in_transaction();
- self.client.map(|client| client.packet_received(&buf));
+ self.client.map(|client| client.packet_received(&buf, app));
true
} else {
// Cannot receive now, indicate a delay to the controller.
@@ -421,7 +422,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
// Cannot process this packet
hil::usb::OutResult::Error
} else {
- if self.send_packet_to_client() {
+ if self.send_packet_to_client(None) {
hil::usb::OutResult::Ok
} else {
hil::usb::OutResult::Delay

View File

@@ -1,516 +0,0 @@
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
index e8f1a87a4..2c91c0968 100644
--- a/capsules/src/usb/usb_ctap.rs
+++ b/capsules/src/usb/usb_ctap.rs
@@ -32,8 +32,7 @@ pub trait CtapUsbClient {
fn can_receive_packet(&self, app: &Option<&mut App>) -> bool;
// Signal to the client that a packet has been received.
- // If App is not supplied, it will be found from the implemntation's members.
- fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>);
+ fn packet_received(&self, packet: &[u8; 64], endpoint: usize, app: Option<&mut App>);
// Signal to the client that a packet has been transmitted.
fn packet_transmitted(&self);
@@ -49,7 +48,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
CtapUsbSyscallDriver { usb_client, apps }
}
- fn app_packet_received(&self, packet: &[u8; 64], app: &mut App) {
+ fn app_packet_received(&self, packet: &[u8; 64], endpoint: usize, app: &mut App) {
if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive()) {
if let Some(buf) = &mut app.buffer {
// Copy the packet to the app's allowed buffer.
@@ -57,7 +56,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
app.waiting = false;
// Signal to the app that a packet is ready.
app.callback
- .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, endpoint, 0));
}
}
}
@@ -81,16 +80,16 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDri
result
}
- fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>) {
+ fn packet_received(&self, packet: &[u8; 64], endpoint: usize, app: Option<&mut App>) {
match app {
None => {
for app in self.apps.iter() {
app.enter(|a, _| {
- self.app_packet_received(packet, a);
+ self.app_packet_received(packet, endpoint, a);
})
}
}
- Some(a) => self.app_packet_received(packet, a),
+ Some(a) => self.app_packet_received(packet, endpoint, a),
}
}
@@ -173,7 +172,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
.unwrap_or_else(|err| err.into())
}
- fn command(&self, cmd_num: usize, _arg1: usize, _arg2: usize, appid: AppId) -> ReturnCode {
+ fn command(&self, cmd_num: usize, endpoint: usize, _arg2: usize, appid: AppId) -> ReturnCode {
match cmd_num {
CTAP_CMD_CHECK => ReturnCode::SUCCESS,
CTAP_CMD_CONNECT => {
@@ -209,14 +208,14 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
if app.is_ready_for_command(Side::Transmit) {
if app.waiting {
ReturnCode::EALREADY
- } else if self
- .usb_client
- .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
- {
- app.waiting = true;
- ReturnCode::SUCCESS
} else {
- ReturnCode::EBUSY
+ let r = self
+ .usb_client
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint);
+ if r == ReturnCode::SUCCESS {
+ app.waiting = true;
+ }
+ r
}
} else {
ReturnCode::EINVAL
@@ -263,14 +262,8 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
ReturnCode::SUCCESS
} else {
// Indicates to the driver that we have a packet to send.
- if self
- .usb_client
- .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
- {
- ReturnCode::SUCCESS
- } else {
- ReturnCode::EBUSY
- }
+ self.usb_client
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint)
}
}
} else {
@@ -289,7 +282,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
// FIXME: if cancellation failed, the app should still wait. But that
// doesn't work yet.
app.waiting = false;
- if self.usb_client.cancel_transaction() {
+ if self.usb_client.cancel_transaction(endpoint) {
ReturnCode::SUCCESS
} else {
// Cannot cancel now because the transaction is already in process.
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
index d47e5f644..76f6af73b 100644
--- a/capsules/src/usb/usbc_ctap_hid.rs
+++ b/capsules/src/usb/usbc_ctap_hid.rs
@@ -18,13 +18,27 @@ use core::cell::Cell;
use kernel::common::cells::OptionalCell;
use kernel::debug;
use kernel::hil;
+use kernel::ReturnCode;
use kernel::hil::usb::TransferType;
static LANGUAGES: &'static [u16; 1] = &[
0x0409, // English (United States)
];
+#[cfg(not(feature = "vendor_hid"))]
+const NUM_ENDPOINTS: usize = 1;
+#[cfg(feature = "vendor_hid")]
+const NUM_ENDPOINTS: usize = 2;
+
const ENDPOINT_NUM: usize = 1;
+#[cfg(feature = "vendor_hid")]
+const VENDOR_ENDPOINT_NUM: usize = ENDPOINT_NUM + 1;
+
+static ENDPOINTS: &'static [usize] = &[
+ ENDPOINT_NUM,
+ #[cfg(feature = "vendor_hid")]
+ VENDOR_ENDPOINT_NUM
+];
static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
0x06, 0xD0, 0xF1, // HID_UsagePage ( FIDO_USAGE_PAGE ),
@@ -98,21 +112,42 @@ static VENDOR_HID: HIDDescriptor<'static> = HIDDescriptor {
sub_descriptors: VENDOR_HID_SUB_DESCRIPTORS,
};
-pub struct ClientCtapHID<'a, 'b, C: 'a> {
- client_ctrl: ClientCtrl<'a, 'static, C>,
-
- // 64-byte buffers for the endpoint
+// The state of each endpoint.
+struct EndpointState {
+ endpoint: usize,
in_buffer: Buffer64,
out_buffer: Buffer64,
- // Interaction with the client
- client: OptionalCell<&'b dyn CtapUsbClient>,
tx_packet: OptionalCell<[u8; 64]>,
pending_in: Cell<bool>,
pending_out: Cell<bool>,
+ // Is there a delayed packet?
delayed_out: Cell<bool>,
}
+impl EndpointState {
+ pub fn new(endpoint:usize) -> Self {
+ EndpointState{
+ endpoint: endpoint,
+ in_buffer: Buffer64::default(),
+ out_buffer: Buffer64::default(),
+ tx_packet: OptionalCell::empty(),
+ pending_in: Cell::new(false),
+ pending_out: Cell::new(false),
+ delayed_out: Cell::new(false),
+ }
+ }
+}
+
+pub struct ClientCtapHID<'a, 'b, C: 'a> {
+ client_ctrl: ClientCtrl<'a, 'static, C>,
+
+ endpoints: [EndpointState; NUM_ENDPOINTS],
+
+ // Interaction with the client
+ client: OptionalCell<&'b dyn CtapUsbClient>,
+}
+
impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
pub fn new(
controller: &'a C,
@@ -168,7 +203,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
&[
EndpointDescriptor {
endpoint_address: EndpointAddress::new_const(
- ENDPOINT_NUM + 1,
+ VENDOR_ENDPOINT_NUM,
TransferDirection::HostToDevice,
),
transfer_type: TransferType::Interrupt,
@@ -177,7 +212,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
},
EndpointDescriptor {
endpoint_address: EndpointAddress::new_const(
- ENDPOINT_NUM + 1,
+ VENDOR_ENDPOINT_NUM,
TransferDirection::DeviceToHost,
),
transfer_type: TransferType::Interrupt,
@@ -229,99 +264,135 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
LANGUAGES,
strings,
),
- in_buffer: Buffer64::default(),
- out_buffer: Buffer64::default(),
+ endpoints: [
+ EndpointState::new(ENDPOINT_NUM),
+ #[cfg(feature = "vendor_hid")]
+ EndpointState::new(VENDOR_ENDPOINT_NUM),
+ ],
client: OptionalCell::empty(),
- tx_packet: OptionalCell::empty(),
- pending_in: Cell::new(false),
- pending_out: Cell::new(false),
- delayed_out: Cell::new(false),
}
}
+ fn get_endpoint(&'a self, endpoint: usize) -> Option<&'a EndpointState> {
+ for (i, ep) in ENDPOINTS.iter().enumerate() {
+ if endpoint == *ep {
+ return Some(&self.endpoints[i]);
+ }
+ }
+ None
+ }
+
pub fn set_client(&'a self, client: &'b dyn CtapUsbClient) {
self.client.set(client);
}
- pub fn transmit_packet(&'a self, packet: &[u8]) -> bool {
- if self.pending_in.get() {
- // The previous packet has not yet been transmitted, reject the new one.
- false
- } else {
- self.pending_in.set(true);
+ pub fn transmit_packet(&'a self, packet: &[u8], endpoint: usize) -> ReturnCode {
+ if let Some(s) = self.get_endpoint(endpoint) {
+ if s.pending_in.get() {
+ // The previous packet has not yet been transmitted, reject the new one.
+ return ReturnCode::EBUSY;
+ }
+ s.pending_in.set(true);
let mut buf: [u8; 64] = [0; 64];
buf.copy_from_slice(packet);
- self.tx_packet.set(buf);
+ s.tx_packet.set(buf);
// Alert the controller that we now have data to send on the Interrupt IN endpoint.
- self.controller().endpoint_resume_in(1);
- true
+ self.controller().endpoint_resume_in(endpoint);
+ ReturnCode::SUCCESS
+ } else {
+ // Unsupported endpoint
+ ReturnCode::EINVAL
}
}
- pub fn receive_packet(&'a self, app: &mut App) -> bool {
- if self.pending_out.get() {
- // The previous packet has not yet been received, reject the new one.
- false
- } else {
- self.pending_out.set(true);
- // In case we reported Delay before, send the pending packet back to the client.
- // Otherwise, there's nothing to do, the controller will send us a packet_out when a
- // packet arrives.
- if self.delayed_out.take() {
- if self.send_packet_to_client(Some(app)) {
- // If that succeeds, alert the controller that we can now
- // receive data on the Interrupt OUT endpoint.
- self.controller().endpoint_resume_out(1);
+ pub fn receive_packet(&'a self, app: &mut App) {
+ for (_, s) in self.endpoints.iter().enumerate() {
+ if s.pending_out.get() {
+ // The previous packet has not yet been received, reject the new one.
+ continue;
+ } else {
+ s.pending_out.set(true);
+ // In case we reported Delay before, send the pending packet back to the client.
+ // Otherwise, there's nothing to do, the controller will send us a packet_out when a
+ // packet arrives.
+ if s.delayed_out.take() {
+ if self.send_packet_to_client(s.endpoint, Some(app)) {
+ // If that succeeds, alert the controller that we can now
+ // receive data on the Interrupt OUT endpoint.
+ self.controller().endpoint_resume_out(s.endpoint);
+ }
}
}
- true
}
}
// Send an OUT packet available in the controller back to the client.
// This returns false if the client is not ready to receive a packet, and true if the client
// successfully accepted the packet.
- fn send_packet_to_client(&'a self, app: Option<&mut App>) -> bool {
- // Copy the packet into a buffer to send to the client.
- let mut buf: [u8; 64] = [0; 64];
- for (i, x) in self.out_buffer.buf.iter().enumerate() {
- buf[i] = x.get();
- }
-
- assert!(!self.delayed_out.get());
-
- // Notify the client
- if self
- .client
- .map_or(false, |client| client.can_receive_packet(&app))
- {
- assert!(self.pending_out.take());
-
- // Clear any pending packet on the transmitting side.
- // It's up to the client to handle the received packet and decide if this packet
- // should be re-transmitted or not.
- self.cancel_in_transaction();
+ fn send_packet_to_client(&'a self, endpoint: usize, app: Option<&mut App>) -> bool {
+ if let Some(s) = self.get_endpoint(endpoint) {
+ // Copy the packet into a buffer to send to the client.
+ let mut buf: [u8; 64] = [0; 64];
+ for (i, x) in s.out_buffer.buf.iter().enumerate() {
+ buf[i] = x.get();
+ }
- self.client.map(|client| client.packet_received(&buf, app));
- true
+ assert!(!s.delayed_out.get());
+
+ // Notify the client
+ if self
+ .client
+ .map_or(false, |client| client.can_receive_packet(&app))
+ {
+ assert!(s.pending_out.take());
+
+ // Clear any pending packet on the transmitting side.
+ // It's up to the client to handle the received packet and decide if this packet
+ // should be re-transmitted or not.
+ self.cancel_in_transaction(endpoint);
+
+ self.client.map(|client| client.packet_received(&buf, endpoint, app));
+ true
+ } else {
+ // Cannot receive now, indicate a delay to the controller.
+ s.delayed_out.set(true);
+ false
+ }
} else {
- // Cannot receive now, indicate a delay to the controller.
- self.delayed_out.set(true);
+ // Unsupported endpoint
false
}
}
- pub fn cancel_transaction(&'a self) -> bool {
- self.cancel_in_transaction() | self.cancel_out_transaction()
+ // Cancel transaction(s) in process. |endpoint| of 0 indicates all endpoints.
+ pub fn cancel_transaction(&'a self, endpoint: usize) -> bool {
+ if endpoint > 0 {
+ return self.cancel_in_transaction(endpoint) | self.cancel_out_transaction(endpoint);
+ }
+ let mut r = false;
+ for (_, s) in self.endpoints.iter().enumerate() {
+ r |= self.cancel_in_transaction(s.endpoint) | self.cancel_out_transaction(s.endpoint);
+ }
+ r
}
- fn cancel_in_transaction(&'a self) -> bool {
- self.tx_packet.take();
- self.pending_in.take()
+ fn cancel_in_transaction(&'a self, endpoint: usize) -> bool {
+ if let Some(s) = self.get_endpoint(endpoint) {
+ s.tx_packet.take();
+ s.pending_in.take()
+ } else {
+ // Unsupported endpoint
+ false
+ }
}
- fn cancel_out_transaction(&'a self) -> bool {
- self.pending_out.take()
+ fn cancel_out_transaction(&'a self, endpoint: usize) -> bool {
+ if let Some(s) = self.get_endpoint(endpoint) {
+ s.pending_out.take()
+ } else {
+ // Unsupported endpoint
+ false
+ }
}
#[inline]
@@ -335,13 +406,15 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
// Set up the default control endpoint
self.client_ctrl.enable();
- // Set up the interrupt in-out endpoint
- self.controller()
- .endpoint_set_in_buffer(1, &self.in_buffer.buf);
- self.controller()
- .endpoint_set_out_buffer(1, &self.out_buffer.buf);
- self.controller()
- .endpoint_in_out_enable(TransferType::Interrupt, 1);
+ // Set up the interrupt in-out endpoint(s).
+ for (i, endpoint) in ENDPOINTS.iter().enumerate() {
+ self.controller()
+ .endpoint_set_in_buffer(*endpoint, &self.endpoints[i].in_buffer.buf);
+ self.controller()
+ .endpoint_set_out_buffer(*endpoint, &self.endpoints[i].out_buffer.buf);
+ self.controller()
+ .endpoint_in_out_enable(TransferType::Interrupt, *endpoint);
+ }
}
fn attach(&'a self) {
@@ -384,20 +457,20 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
match transfer_type {
TransferType::Bulk => hil::usb::InResult::Error,
TransferType::Interrupt => {
- if endpoint != 1 {
- return hil::usb::InResult::Error;
- }
-
- if let Some(packet) = self.tx_packet.take() {
- let buf = &self.in_buffer.buf;
- for i in 0..64 {
- buf[i].set(packet[i]);
+ if let Some(s) = self.get_endpoint(endpoint) {
+ if let Some(packet) = s.tx_packet.take() {
+ let buf = &s.in_buffer.buf;
+ for i in 0..64 {
+ buf[i].set(packet[i]);
+ }
+ hil::usb::InResult::Packet(64)
+ } else {
+ // Nothing to send
+ hil::usb::InResult::Delay
}
-
- hil::usb::InResult::Packet(64)
} else {
- // Nothing to send
- hil::usb::InResult::Delay
+ // Unsupported endpoint
+ return hil::usb::InResult::Error
}
}
TransferType::Control | TransferType::Isochronous => unreachable!(),
@@ -414,7 +487,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
match transfer_type {
TransferType::Bulk => hil::usb::OutResult::Error,
TransferType::Interrupt => {
- if endpoint != 1 {
+ if endpoint == 0 || endpoint > NUM_ENDPOINTS {
return hil::usb::OutResult::Error;
}
@@ -422,7 +495,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
// Cannot process this packet
hil::usb::OutResult::Error
} else {
- if self.send_packet_to_client(None) {
+ if self.send_packet_to_client(endpoint, None) {
hil::usb::OutResult::Ok
} else {
hil::usb::OutResult::Delay
@@ -434,21 +507,21 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
}
fn packet_transmitted(&'a self, endpoint: usize) {
- if endpoint != 1 {
- panic!("Unexpected transmission on ep {}", endpoint);
- }
-
- if self.tx_packet.is_some() {
- panic!("Unexpected tx_packet while a packet was being transmitted.");
- }
- self.pending_in.set(false);
+ if let Some(s) = self.get_endpoint(endpoint) {
+ if s.tx_packet.is_some() {
+ panic!("Unexpected tx_packet while a packet was being transmitted.");
+ }
+ s.pending_in.set(false);
- // Clear any pending packet on the receiving side.
- // It's up to the client to handle the transmitted packet and decide if they want to
- // receive another packet.
- self.cancel_out_transaction();
+ // Clear any pending packet on the receiving side.
+ // It's up to the client to handle the transmitted packet and decide if they want to
+ // receive another packet.
+ self.cancel_out_transaction(endpoint);
- // Notify the client
- self.client.map(|client| client.packet_transmitted());
+ // Notify the client
+ self.client.map(|client| client.packet_transmitted());
+ } else {
+ panic!("Unexpected transmission on ep {}", endpoint);
+ }
}
-}
+ }

View File

@@ -1,94 +0,0 @@
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
index 16b80cb10..949388b70 100644
--- a/capsules/src/usb/usbc_ctap_hid.rs
+++ b/capsules/src/usb/usbc_ctap_hid.rs
@@ -120,7 +120,6 @@ struct EndpointState {
tx_packet: OptionalCell<[u8; 64]>,
pending_in: Cell<bool>,
- pending_out: Cell<bool>,
// Is there a delayed packet?
delayed_out: Cell<bool>,
}
@@ -133,7 +132,6 @@ impl EndpointState {
out_buffer: Buffer64::default(),
tx_packet: OptionalCell::empty(),
pending_in: Cell::new(false),
- pending_out: Cell::new(false),
delayed_out: Cell::new(false),
}
}
@@ -142,6 +140,10 @@ impl EndpointState {
pub struct ClientCtapHID<'a, 'b, C: 'a> {
client_ctrl: ClientCtrl<'a, 'static, C>,
+ // Is there a pending OUT transaction happening?
+ pending_out: Cell<bool>,
+ next_endpoint_index: Cell<usize>,
+
endpoints: [EndpointState; NUM_ENDPOINTS],
// Interaction with the client
@@ -264,6 +266,8 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
LANGUAGES,
strings,
),
+ pending_out: Cell::new(false),
+ next_endpoint_index: Cell::new(0),
endpoints: [
EndpointState::new(ENDPOINT_NUM),
#[cfg(feature = "vendor_hid")]
@@ -306,12 +310,13 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
}
pub fn receive_packet(&'a self, app: &mut App) {
- for (_, s) in self.endpoints.iter().enumerate() {
- if s.pending_out.get() {
- // The previous packet has not yet been received, reject the new one.
- continue;
- } else {
- s.pending_out.set(true);
+ if self.pending_out.get() {
+ // The previous packet has not yet been received, reject the new one.
+ } else {
+ self.pending_out.set(true);
+ // Process the next endpoint that has a delayed packet.
+ for i in self.next_endpoint_index.get()..self.next_endpoint_index.get() + NUM_ENDPOINTS {
+ let s = &self.endpoints[i % NUM_ENDPOINTS];
// In case we reported Delay before, send the pending packet back to the client.
// Otherwise, there's nothing to do, the controller will send us a packet_out when a
// packet arrives.
@@ -344,7 +349,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
.client
.map_or(false, |client| client.can_receive_packet(&app))
{
- assert!(s.pending_out.take());
+ assert!(self.pending_out.take());
// Clear any pending packet on the transmitting side.
// It's up to the client to handle the received packet and decide if this packet
@@ -352,6 +357,13 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
self.cancel_in_transaction(endpoint);
self.client.map(|client| client.packet_received(&buf, endpoint, app));
+ // Update next packet to send.
+ for (i, ep) in self.endpoints.iter().enumerate() {
+ if ep.endpoint == endpoint {
+ self.next_endpoint_index.set((i + 1) % NUM_ENDPOINTS);
+ break;
+ }
+ }
true
} else {
// Cannot receive now, indicate a delay to the controller.
@@ -387,8 +399,8 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
}
fn cancel_out_transaction(&'a self, endpoint: usize) -> bool {
- if let Some(s) = self.get_endpoint(endpoint) {
- s.pending_out.take()
+ if let Some(_) = self.get_endpoint(endpoint) {
+ self.pending_out.take()
} else {
// Unsupported endpoint
false

View File

@@ -1,32 +0,0 @@
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
index 2c91c0968..ea8111069 100644
--- a/capsules/src/usb/usb_ctap.rs
+++ b/capsules/src/usb/usb_ctap.rs
@@ -253,18 +253,19 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
if app.waiting {
ReturnCode::EALREADY
} else {
+ // Indicates to the driver that we have a packet to send.
+ let r = self
+ .usb_client
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint);
+ if r != ReturnCode::SUCCESS {
+ return r;
+ }
// Indicates to the driver that we can receive any pending packet.
app.waiting = true;
self.usb_client.receive_packet(app);
- if !app.waiting {
- // The call to receive_packet() collected a pending packet.
- ReturnCode::SUCCESS
- } else {
- // Indicates to the driver that we have a packet to send.
- self.usb_client
- .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint)
- }
+ ReturnCode::SUCCESS
+
}
} else {
ReturnCode::EINVAL

View File

@@ -37,9 +37,9 @@ cargo fmt -- --check
cd ..
echo "Running Clippy lints..."
cargo clippy --all-targets --features std -- -D warnings
cargo clippy --all-targets --features std,with_ctap1,ed25519,vendor_hid -- -D warnings
cargo clippy --all-targets --features std,with_ctap1,with_nfc,ed25519,vendor_hid -- -D warnings
cargo clippy --lib --tests --bins --benches --features std -- -D warnings
cargo clippy --lib --tests --bins --benches --features std,with_ctap1,ed25519,vendor_hid -- -D warnings
cargo clippy --lib --tests --bins --benches --features std,with_ctap1,with_nfc,ed25519,vendor_hid -- -D warnings
echo "Building sha256sum tool..."
cargo build --manifest-path third_party/tock/tools/sha256sum/Cargo.toml
@@ -104,8 +104,8 @@ echo "Checking deployment of other boards..."
if [ -z "${TRAVIS_OS_NAME}" -o "${TRAVIS_OS_NAME}" = "linux" ]
then
echo "Running unit tests on the desktop (release mode)..."
cargo test --release --features std
cargo test --release --all-features
cargo test --lib --tests --bins --benches --release --features std
cargo test --lib --tests --bins --benches --release --all-features
cd libraries/cbor
cargo test --release
cd ../..
@@ -114,8 +114,8 @@ then
cd ../..
echo "Running unit tests on the desktop (debug mode)..."
cargo test --features std
cargo test --release --all-features
cargo test --lib --tests --bins --benches --features std
cargo test --lib --tests --bins --benches --all-features
cd libraries/cbor
cargo test
cd ../..

View File

@@ -45,4 +45,4 @@ pip3 install --upgrade -r requirements.txt
# Install dependency to create applications.
mkdir -p elf2tab
rustup install stable
cargo +stable install elf2tab --version 0.7.0 --root elf2tab/
cargo +stable install elf2tab --version 0.10.2 --root elf2tab/

View File

@@ -14,20 +14,35 @@
use super::storage_helper::ModRange;
use alloc::boxed::Box;
use core::marker::PhantomData;
use libtock_platform as platform;
use libtock_platform::Syscalls;
use persistent_store::{StorageError, StorageResult};
use platform::DefaultConfig;
const PARTITION_LENGTH: usize = 0x41000;
const METADATA_LENGTH: usize = 0x1000;
pub struct BufferUpgradeStorage {
pub struct BufferUpgradeStorage<
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config = DefaultConfig,
> {
/// Content of the partition storage.
partition: Box<[u8]>,
s: PhantomData<S>,
c: PhantomData<C>,
}
impl BufferUpgradeStorage {
pub fn new() -> StorageResult<BufferUpgradeStorage> {
impl<S, C> BufferUpgradeStorage<S, C>
where
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
{
pub fn new() -> StorageResult<Self> {
Ok(BufferUpgradeStorage {
partition: vec![0xff; PARTITION_LENGTH].into_boxed_slice(),
s: PhantomData,
c: PhantomData,
})
}
@@ -72,10 +87,11 @@ impl BufferUpgradeStorage {
#[cfg(test)]
mod tests {
use super::*;
use libtock_unittest::fake::Syscalls;
#[test]
fn read_write_bundle() {
let mut storage = BufferUpgradeStorage::new().unwrap();
let mut storage = BufferUpgradeStorage::<Syscalls>::new().unwrap();
assert_eq!(storage.read_partition(0, 2).unwrap(), &[0xFF, 0xFF]);
assert!(storage.write_bundle(1, vec![0x88, 0x88]).is_ok());
assert_eq!(storage.read_partition(0, 2).unwrap(), &[0xFF, 0x88]);
@@ -108,7 +124,7 @@ mod tests {
#[test]
fn partition_slice() {
let storage = BufferUpgradeStorage::new().unwrap();
let storage = BufferUpgradeStorage::<Syscalls>::new().unwrap();
assert_eq!(storage.bundle_identifier(), 0x60000);
}
}

32
src/env/tock/clock.rs vendored
View File

@@ -12,19 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use core::marker::PhantomData;
use libtock_drivers::timer::{get_clock_frequency, get_ticks};
use libtock_platform::Syscalls;
use opensk::api::clock::Clock;
/// 56-bits timestamp (valid for 70k+ years)
#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
struct Timestamp {
epoch: usize, // 32-bits
tick: usize, // 24-bits (32kHz)
epoch: u32,
tick: u32, // 24-bits (32kHz)
}
impl Timestamp {
/// Adds (potentially more than 24 bit of) ticks to this timestamp.
pub fn add_ticks(&mut self, ticks: usize) {
pub fn add_ticks(&mut self, ticks: u32) {
// Saturating should never happen, but it fails gracefully.
let sum = self.tick.saturating_add(ticks);
self.epoch += sum >> 24;
@@ -42,17 +44,26 @@ pub struct TockTimer {
/// To guarantee correctness, you have to call any of its functions at least once per full tick
/// counter wrap. In our case, 24 bit ticks with a 32 kHz frequency wrap after 512 seconds. If you
/// can't guarantee to regularly create or check timers, call tickle at least every 8 minutes.
#[derive(Default)]
pub struct TockClock {
pub struct TockClock<S: Syscalls> {
now: Timestamp,
s: PhantomData<S>,
}
impl TockClock {
impl<S: Syscalls> Default for TockClock<S> {
fn default() -> Self {
TockClock {
now: Timestamp::default(),
s: PhantomData,
}
}
}
impl<S: Syscalls> TockClock<S> {
/// Elapses timers before the clock wraps.
///
/// Call this regularly to timeout reliably despite wrapping clock ticks.
pub fn tickle(&mut self) {
let cur_tick = get_ticks().ok().unwrap();
let cur_tick = get_ticks::<S>().ok().unwrap();
if cur_tick < self.now.tick {
self.now.epoch += 1;
}
@@ -60,12 +71,13 @@ impl TockClock {
}
}
impl Clock for TockClock {
impl<S: Syscalls> Clock for TockClock<S> {
type Timer = TockTimer;
fn make_timer(&mut self, milliseconds: usize) -> Self::Timer {
let milliseconds = milliseconds as u32;
self.tickle();
let clock_frequency = get_clock_frequency().ok().unwrap();
let clock_frequency = get_clock_frequency::<S>().ok().unwrap();
let delta_tick = match milliseconds.checked_mul(clock_frequency) {
Some(x) => x / 1000,
// All CTAP timeouts are multiples of 100 so far. Worst case we timeout too early.
@@ -83,7 +95,7 @@ impl Clock for TockClock {
#[cfg(feature = "debug_ctap")]
fn timestamp_us(&mut self) -> usize {
let clock_frequency = get_clock_frequency().ok().unwrap();
let clock_frequency = get_clock_frequency::<S>().ok().unwrap();
let total_ticks = 0x100_0000u64 * self.now.epoch as u64 + self.now.tick as u64;
(total_ticks.wrapping_mul(1_000_000u64) / clock_frequency as u64) as usize
}

View File

@@ -17,6 +17,7 @@ use alloc::vec;
use alloc::vec::Vec;
use arrayref::array_ref;
use core::convert::TryFrom;
use libtock_platform::Syscalls;
use opensk::api::attestation_store::{self, Attestation, AttestationStore};
use opensk::api::crypto::sha256::Sha256;
use opensk::api::crypto::EC_FIELD_SIZE;
@@ -31,15 +32,18 @@ use opensk::ctap::secret::Secret;
use opensk::ctap::status_code::Ctap2StatusCode;
use opensk::ctap::{cbor_read, cbor_write, Channel};
use opensk::env::{Env, Sha};
use sk_cbor as cbor;
use sk_cbor::{cbor_map_options, destructure_cbor_map};
use {libtock_platform as platform, sk_cbor as cbor};
const VENDOR_COMMAND_CONFIGURE: u8 = 0x40;
const VENDOR_COMMAND_UPGRADE: u8 = 0x42;
const VENDOR_COMMAND_UPGRADE_INFO: u8 = 0x43;
pub fn process_vendor_command(
env: &mut TockEnv,
pub fn process_vendor_command<
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
>(
env: &mut TockEnv<S, C>,
bytes: &[u8],
channel: Channel,
) -> Option<Vec<u8>> {
@@ -50,8 +54,8 @@ pub fn process_vendor_command(
process_cbor(env, bytes, channel).unwrap_or_else(|e| Some(vec![e as u8]))
}
fn process_cbor(
env: &mut TockEnv,
fn process_cbor<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config>(
env: &mut TockEnv<S, C>,
bytes: &[u8],
channel: Channel,
) -> Result<Option<Vec<u8>>, Ctap2StatusCode> {
@@ -85,8 +89,11 @@ fn encode_cbor(value: cbor::Value) -> Vec<u8> {
}
}
fn process_vendor_configure(
env: &mut TockEnv,
fn process_vendor_configure<
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
>(
env: &mut TockEnv<S, C>,
params: VendorConfigureParameters,
// Unused in std only
_channel: Channel,
@@ -141,12 +148,15 @@ fn process_vendor_configure(
Ok(response)
}
fn process_vendor_upgrade(
env: &mut TockEnv,
fn process_vendor_upgrade<
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
>(
env: &mut TockEnv<S, C>,
params: VendorUpgradeParameters,
) -> Result<(), Ctap2StatusCode> {
let VendorUpgradeParameters { offset, data, hash } = params;
let calculated_hash = Sha::<TockEnv>::digest(&data);
let calculated_hash = Sha::<TockEnv<S>>::digest(&data);
if hash != calculated_hash {
return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE);
}
@@ -156,8 +166,11 @@ fn process_vendor_upgrade(
.map_err(|_| Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
}
fn process_vendor_upgrade_info(
env: &mut TockEnv,
fn process_vendor_upgrade_info<
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
>(
env: &mut TockEnv<S, C>,
) -> Result<VendorUpgradeInfoResponse, Ctap2StatusCode> {
let upgrade_locations = env
.upgrade_storage()
@@ -288,6 +301,7 @@ impl From<VendorUpgradeInfoResponse> for cbor::Value {
mod test {
use super::*;
use cbor::cbor_map;
use libtock_unittest::fake::Syscalls;
const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]);
#[cfg(feature = "vendor_hid")]
@@ -295,14 +309,14 @@ mod test {
#[test]
fn test_process_cbor_unrelated_input() {
let mut env = TockEnv::default();
let mut env = TockEnv::<Syscalls>::default();
let cbor_bytes = vec![0x01];
assert_eq!(process_cbor(&mut env, &cbor_bytes, DUMMY_CHANNEL), Ok(None));
}
#[test]
fn test_process_cbor_invalid_input() {
let mut env = TockEnv::default();
let mut env = TockEnv::<Syscalls>::default();
let cbor_bytes = vec![VENDOR_COMMAND_CONFIGURE];
assert_eq!(
process_cbor(&mut env, &cbor_bytes, DUMMY_CHANNEL),
@@ -312,7 +326,7 @@ mod test {
#[test]
fn test_process_cbor_valid_input() {
let mut env = TockEnv::default();
let mut env = TockEnv::<Syscalls>::default();
let cbor_bytes = vec![VENDOR_COMMAND_UPGRADE_INFO];
assert!(process_cbor(&mut env, &cbor_bytes, DUMMY_CHANNEL)
.unwrap()
@@ -322,7 +336,7 @@ mod test {
#[test]
#[cfg(feature = "vendor_hid")]
fn test_process_command_valid_vendor_hid() {
let mut env = TockEnv::default();
let mut env = TockEnv::<Syscalls>::default();
let cbor_bytes = vec![VENDOR_COMMAND_UPGRADE_INFO];
assert!(process_cbor(&mut env, &cbor_bytes, VENDOR_CHANNEL)
.unwrap()
@@ -453,7 +467,7 @@ mod test {
#[test]
fn test_deserialize_vendor_upgrade_info() {
let mut env = TockEnv::default();
let mut env = TockEnv::<Syscalls>::default();
let cbor_bytes = [VENDOR_COMMAND_UPGRADE_INFO];
assert!(process_cbor(&mut env, &cbor_bytes, DUMMY_CHANNEL)
.unwrap()
@@ -462,7 +476,7 @@ mod test {
#[test]
fn test_vendor_configure() {
let mut env = TockEnv::default();
let mut env = TockEnv::<Syscalls>::default();
// Nothing should be configured at the beginning
let response = process_vendor_configure(
@@ -538,7 +552,7 @@ mod test {
}))
);
// Now try to lock the device
// Now try to lock the device, but that is currently not supported.
let response = process_vendor_configure(
&mut env,
VendorConfigureParameters {
@@ -549,10 +563,7 @@ mod test {
);
assert_eq!(
response,
Ok(VendorConfigureResponse {
cert_programmed: true,
pkey_programmed: true,
})
Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)
);
}
@@ -561,13 +572,13 @@ mod test {
// The test partition storage has size 0x40000.
// The test metadata storage has size 0x1000.
// The test identifier matches partition B.
let mut env = TockEnv::default();
let mut env = TockEnv::<Syscalls>::default();
const METADATA_LEN: usize = 0x1000;
let metadata = vec![0xFF; METADATA_LEN];
let metadata_hash = Sha::<TockEnv>::digest(&metadata);
let metadata_hash = Sha::<TockEnv<Syscalls>>::digest(&metadata);
let data = vec![0xFF; 0x1000];
let hash = Sha::<TockEnv>::digest(&data);
let hash = Sha::<TockEnv<Syscalls>>::digest(&data);
// Write to partition.
let response = process_vendor_upgrade(
@@ -638,11 +649,11 @@ mod test {
#[test]
fn test_vendor_upgrade_no_second_partition() {
let mut env = TockEnv::default();
let mut env = TockEnv::<Syscalls>::default();
env.disable_upgrade_storage();
let data = vec![0xFF; 0x1000];
let hash = Sha::<TockEnv>::digest(&data);
let hash = Sha::<TockEnv<Syscalls>>::digest(&data);
let response = process_vendor_upgrade(
&mut env,
VendorUpgradeParameters {
@@ -656,7 +667,7 @@ mod test {
#[test]
fn test_vendor_upgrade_info() {
let mut env = TockEnv::default();
let mut env = TockEnv::<Syscalls>::default();
let bundle_identifier = env.upgrade_storage().unwrap().bundle_identifier();
let upgrade_info_reponse = process_vendor_upgrade_info(&mut env);

323
src/env/tock/mod.rs vendored
View File

@@ -16,16 +16,18 @@ use alloc::vec::Vec;
use clock::TockClock;
use core::cell::Cell;
use core::convert::TryFrom;
#[cfg(not(feature = "std"))]
use core::marker::PhantomData;
#[cfg(all(target_has_atomic = "8", not(feature = "std")))]
use core::sync::atomic::{AtomicBool, Ordering};
use libtock_core::result::{CommandError, EALREADY};
use libtock_drivers::buttons::{self, ButtonState};
use libtock_drivers::console::Console;
#[cfg(not(feature = "std"))]
use libtock_drivers::crp;
use libtock_buttons::{ButtonListener, ButtonState, Buttons};
use libtock_console::{Console, ConsoleWriter};
use libtock_drivers::result::{FlexUnwrap, TockError};
use libtock_drivers::timer::Duration;
use libtock_drivers::{led, rng, timer, usb_ctap_hid};
use libtock_drivers::usb_ctap_hid::UsbCtapHid;
use libtock_drivers::{rng, timer, usb_ctap_hid};
use libtock_leds::Leds;
use libtock_platform as platform;
use libtock_platform::{ErrorCode, Syscalls};
use opensk::api::attestation_store::AttestationStore;
use opensk::api::connection::{
HidConnection, SendOrRecvError, SendOrRecvResult, SendOrRecvStatus, UsbEndpoint,
@@ -38,28 +40,31 @@ use opensk::api::{attestation_store, key_store};
use opensk::ctap::Channel;
use opensk::env::Env;
#[cfg(feature = "std")]
use persistent_store::{BufferOptions, BufferStorage};
use persistent_store::BufferOptions;
use persistent_store::{StorageResult, Store};
use platform::{share, DefaultConfig, Subscribe};
use rand_core::{impls, CryptoRng, Error, RngCore};
#[cfg(feature = "std")]
mod buffer_upgrade_storage;
mod clock;
mod commands;
#[cfg(feature = "std")]
mod phantom_buffer_storage;
#[cfg(not(feature = "std"))]
mod storage;
mod storage_helper;
mod upgrade_helper;
#[cfg(not(feature = "std"))]
pub type Storage = storage::TockStorage;
pub type Storage<S, C> = storage::TockStorage<S, C>;
#[cfg(feature = "std")]
pub type Storage = BufferStorage;
pub type Storage<S, C> = phantom_buffer_storage::PhantomBufferStorage<S, C>;
#[cfg(not(feature = "std"))]
type UpgradeStorage = storage::TockUpgradeStorage;
type UpgradeStorage<S, C> = storage::TockUpgradeStorage<S, C>;
#[cfg(feature = "std")]
type UpgradeStorage = buffer_upgrade_storage::BufferUpgradeStorage;
type UpgradeStorage<S, C> = buffer_upgrade_storage::BufferUpgradeStorage<S, C>;
pub const AAGUID: &[u8; AAGUID_LENGTH] =
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin"));
@@ -70,11 +75,21 @@ const TOCK_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
};
/// RNG backed by the TockOS rng driver.
pub struct TockRng {}
pub struct TockRng<S: Syscalls> {
_syscalls: PhantomData<S>,
}
impl CryptoRng for TockRng {}
impl<S: Syscalls> Default for TockRng<S> {
fn default() -> Self {
Self {
_syscalls: PhantomData,
}
}
}
impl RngCore for TockRng {
impl<S: Syscalls> CryptoRng for TockRng<S> {}
impl<S: Syscalls> RngCore for TockRng<S> {
fn next_u32(&mut self) -> u32 {
impls::next_u32_via_fill(self)
}
@@ -84,7 +99,7 @@ impl RngCore for TockRng {
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
rng::fill_buffer(dest);
rng::Rng::<S>::fill_buffer(dest);
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
@@ -93,74 +108,89 @@ impl RngCore for TockRng {
}
}
impl Rng for TockRng {}
impl<S: Syscalls> Rng for TockRng<S> {}
pub struct TockHidConnection {
pub struct TockHidConnection<S: Syscalls> {
endpoint: UsbEndpoint,
s: PhantomData<S>,
}
impl HidConnection for TockHidConnection {
impl<S: Syscalls> HidConnection for TockHidConnection<S> {
fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult {
match usb_ctap_hid::send_or_recv_with_timeout(
match UsbCtapHid::<S>::send_or_recv_with_timeout(
buf,
Duration::from_ms(timeout_ms as isize),
self.endpoint as usize,
self.endpoint as u32,
) {
Ok(usb_ctap_hid::SendOrRecvStatus::Timeout) => Ok(SendOrRecvStatus::Timeout),
Ok(usb_ctap_hid::SendOrRecvStatus::Sent) => Ok(SendOrRecvStatus::Sent),
Ok(usb_ctap_hid::SendOrRecvStatus::Received(recv_endpoint)) => {
UsbEndpoint::try_from(recv_endpoint).map(SendOrRecvStatus::Received)
UsbEndpoint::try_from(recv_endpoint as usize).map(SendOrRecvStatus::Received)
}
_ => Err(SendOrRecvError),
}
}
}
pub struct TockEnv {
rng: TockRng,
store: Store<Storage>,
upgrade_storage: Option<UpgradeStorage>,
main_connection: TockHidConnection,
pub struct TockEnv<
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config = DefaultConfig,
> {
rng: TockRng<S>,
store: Store<Storage<S, C>>,
upgrade_storage: Option<UpgradeStorage<S, C>>,
main_connection: TockHidConnection<S>,
#[cfg(feature = "vendor_hid")]
vendor_connection: TockHidConnection,
vendor_connection: TockHidConnection<S>,
blink_pattern: usize,
clock: TockClock,
clock: TockClock<S>,
c: PhantomData<C>,
}
impl Default for TockEnv {
impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> Default
for TockEnv<S, C>
{
/// Returns the unique instance of the Tock environment.
///
/// # Panics
///
/// - If called a second time.
fn default() -> Self {
let rng = TockRng::default();
// We rely on `take_storage` to ensure that this function is called only once.
let storage = take_storage().unwrap();
let storage = take_storage::<S, C>().unwrap();
let store = Store::new(storage).ok().unwrap();
let upgrade_storage = UpgradeStorage::new().ok();
TockEnv {
rng: TockRng {},
rng,
store,
upgrade_storage,
main_connection: TockHidConnection {
endpoint: UsbEndpoint::MainHid,
s: PhantomData,
},
#[cfg(feature = "vendor_hid")]
vendor_connection: TockHidConnection {
endpoint: UsbEndpoint::VendorHid,
s: PhantomData,
},
blink_pattern: 0,
clock: TockClock::default(),
c: PhantomData,
}
}
}
impl TockEnv {
impl<S, C> TockEnv<S, C>
where
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
{
/// Returns the upgrade storage instance.
///
/// Upgrade storage is optional, so implementations may return `None`. However, implementations
/// should either always return `None` or always return `Some`.
pub fn upgrade_storage(&mut self) -> Option<&mut UpgradeStorage> {
pub fn upgrade_storage(&mut self) -> Option<&mut UpgradeStorage<S, C>> {
self.upgrade_storage.as_mut()
}
@@ -169,39 +199,13 @@ impl TockEnv {
}
pub fn lock_firmware_protection(&mut self) -> bool {
#[cfg(not(feature = "std"))]
{
matches!(
crp::set_protection(crp::ProtectionLevel::FullyLocked),
Ok(())
| Err(TockError::Command(CommandError {
return_code: EALREADY,
..
}))
)
}
#[cfg(feature = "std")]
{
true
}
false
}
}
/// Returns the unique storage instance.
///
/// # Panics
///
/// - If called a second time.
#[cfg(not(feature = "std"))]
pub fn take_storage() -> StorageResult<Storage> {
// Make sure the storage was not already taken.
static TAKEN: AtomicBool = AtomicBool::new(false);
assert!(!TAKEN.fetch_or(true, Ordering::SeqCst));
Storage::new()
}
#[cfg(feature = "std")]
pub fn take_storage() -> StorageResult<Storage> {
pub fn take_storage<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config>(
) -> StorageResult<Storage<S, C>> {
// Use the Nordic configuration.
const PAGE_SIZE: usize = 0x1000;
const NUM_PAGES: usize = 20;
@@ -213,10 +217,54 @@ pub fn take_storage() -> StorageResult<Storage> {
max_page_erases: 10000,
strict_mode: true,
};
Ok(BufferStorage::new(store, options))
Ok(phantom_buffer_storage::PhantomBufferStorage::new(
store, options,
))
}
impl UserPresence for TockEnv {
/// Returns the unique storage instance.
///
/// # Panics
///
/// - If called a second time.
#[cfg(not(feature = "std"))]
pub fn take_storage<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config>(
) -> StorageResult<Storage<S, C>> {
// Make sure the storage was not already taken.
#[cfg(target_has_atomic = "8")]
{
static TAKEN: AtomicBool = AtomicBool::new(false);
assert!(!TAKEN.fetch_or(true, Ordering::SeqCst));
}
#[cfg(not(target_has_atomic = "8"))]
{
static mut TAKEN: bool = false;
// Safety
//
// We can not use an AtomicBool on platforms that do not support atomics,
// such as the whole `riscv32i[mc]` family like OpenTitan.
// Thus, we need to use a mutable static variable which are unsafe
// cause they could cause a data race when two threads access it
// at the same time.
//
// However, as we are running an application on TockOS and because
// of its [architecture](https://www.tockos.org/documentation/design)
// we are running in a single-threaded event loop which means the
// aforementioned data race is impossible. Thus, in this case, the
// usage of a static mut is safe.
unsafe {
assert!(!TAKEN);
TAKEN = true;
}
}
Storage::new()
}
impl<S, C> UserPresence for TockEnv<S, C>
where
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
{
fn check_init(&mut self) {
self.blink_pattern = 0;
}
@@ -225,52 +273,77 @@ impl UserPresence for TockEnv {
if timeout_ms == 0 {
return Err(UserPresenceError::Timeout);
}
blink_leds(self.blink_pattern);
blink_leds::<S>(self.blink_pattern);
self.blink_pattern += 1;
// enable interrupts for all buttons
let num_buttons = Buttons::<S>::count().map_err(|_| UserPresenceError::Fail)?;
(0..num_buttons)
.try_for_each(|n| Buttons::<S>::enable_interrupts(n))
.map_err(|_| UserPresenceError::Fail)?;
let button_touched = Cell::new(false);
let mut buttons_callback = buttons::with_callback(|_button_num, state| {
let button_listener = ButtonListener(|_button_num, state| {
match state {
ButtonState::Pressed => button_touched.set(true),
ButtonState::Released => (),
};
});
let mut buttons = buttons_callback.init().flex_unwrap();
for mut button in &mut buttons {
button.enable().flex_unwrap();
}
// Setup a keep-alive callback.
// Setup a keep-alive callback but don't enable it yet
let keepalive_expired = Cell::new(false);
let mut keepalive_callback = timer::with_callback(|_, _| {
keepalive_expired.set(true);
});
let mut keepalive = keepalive_callback.init().flex_unwrap();
let keepalive_alarm = keepalive
.set_alarm(Duration::from_ms(timeout_ms as isize))
.flex_unwrap();
let mut keepalive_callback =
timer::with_callback::<S, C, _>(|_| keepalive_expired.set(true));
share::scope::<
(
Subscribe<_, { libtock_buttons::DRIVER_NUM }, 0>,
Subscribe<
S,
{ libtock_drivers::timer::DRIVER_NUM },
{ libtock_drivers::timer::subscribe::CALLBACK },
>,
),
_,
_,
>(|handle| {
let (sub_button, sub_timer) = handle.split();
Buttons::<S>::register_listener(&button_listener, sub_button)
.map_err(|_| UserPresenceError::Fail)?;
// Wait for a button touch or an alarm.
libtock_drivers::util::yieldk_for(|| button_touched.get() || keepalive_expired.get());
let mut keepalive = keepalive_callback.init().flex_unwrap();
keepalive_callback
.enable(sub_timer)
.map_err(|_| UserPresenceError::Fail)?;
keepalive
.set_alarm(timer::Duration::from_ms(timeout_ms as isize))
.flex_unwrap();
// Cleanup alarm callback.
match keepalive.stop_alarm(keepalive_alarm) {
Ok(()) => (),
Err(TockError::Command(CommandError {
return_code: EALREADY,
..
})) => assert!(keepalive_expired.get()),
Err(_e) => {
#[cfg(feature = "debug_ctap")]
panic!("Unexpected error when stopping alarm: {:?}", _e);
#[cfg(not(feature = "debug_ctap"))]
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
// Wait for a button touch or an alarm.
libtock_drivers::util::Util::<S>::yieldk_for(|| {
button_touched.get() || keepalive_expired.get()
});
Buttons::<S>::unregister_listener();
// disable event interrupts for all buttons
(0..num_buttons)
.try_for_each(|n| Buttons::<S>::disable_interrupts(n))
.map_err(|_| UserPresenceError::Fail)?;
// Cleanup alarm callback.
match keepalive.stop_alarm() {
Ok(()) => (),
Err(TockError::Command(ErrorCode::Already)) => assert!(keepalive_expired.get()),
Err(_e) => {
#[cfg(feature = "debug_ctap")]
panic!("Unexpected error when stopping alarm: {:?}", _e);
#[cfg(not(feature = "debug_ctap"))]
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
}
}
}
for mut button in &mut buttons {
button.disable().flex_unwrap();
}
Ok::<(), UserPresenceError>(())
})?;
if button_touched.get() {
Ok(())
@@ -282,13 +355,22 @@ impl UserPresence for TockEnv {
}
fn check_complete(&mut self) {
switch_off_leds();
switch_off_leds::<S>();
}
}
impl key_store::Helper for TockEnv {}
impl<S, C> key_store::Helper for TockEnv<S, C>
where
S: Syscalls,
C: platform::allow_ro::Config + platform::subscribe::Config,
{
}
impl AttestationStore for TockEnv {
impl<S, C> AttestationStore for TockEnv<S, C>
where
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
{
fn get(
&mut self,
id: &attestation_store::Id,
@@ -311,16 +393,18 @@ impl AttestationStore for TockEnv {
}
}
impl Env for TockEnv {
type Rng = TockRng;
impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> Env
for TockEnv<S, C>
{
type Rng = TockRng<S>;
type UserPresence = Self;
type Storage = Storage;
type Storage = Storage<S, C>;
type KeyStore = Self;
type AttestationStore = Self;
type Clock = TockClock;
type Write = Console;
type Clock = TockClock<S>;
type Write = ConsoleWriter<S>;
type Customization = CustomizationImpl;
type HidConnection = TockHidConnection;
type HidConnection = TockHidConnection<S>;
type Crypto = SoftwareCrypto;
fn rng(&mut self) -> &mut Self::Rng {
@@ -348,7 +432,7 @@ impl Env for TockEnv {
}
fn write(&mut self) -> Self::Write {
Console::new()
Console::<S>::writer()
}
fn customization(&self) -> &Self::Customization {
@@ -375,17 +459,17 @@ impl Env for TockEnv {
}
}
pub fn blink_leds(pattern_seed: usize) {
for l in 0..led::count().flex_unwrap() {
if (pattern_seed ^ l).count_ones() & 1 != 0 {
led::get(l).flex_unwrap().on().flex_unwrap();
pub fn blink_leds<S: Syscalls>(pattern_seed: usize) {
for l in 0..Leds::<S>::count().unwrap() {
if (pattern_seed ^ l as usize).count_ones() & 1 != 0 {
Leds::<S>::on(l).unwrap();
} else {
led::get(l).flex_unwrap().off().flex_unwrap();
Leds::<S>::off(l).unwrap();
}
}
}
pub fn wink_leds(pattern_seed: usize) {
pub fn wink_leds<S: Syscalls>(pattern_seed: usize) {
// This generates a "snake" pattern circling through the LEDs.
// Fox example with 4 LEDs the sequence of lit LEDs will be the following.
// 0 1 2 3
@@ -398,7 +482,7 @@ pub fn wink_leds(pattern_seed: usize) {
// * *
// * * *
// * *
let count = led::count().flex_unwrap();
let count = Leds::<S>::count().unwrap() as usize;
let a = (pattern_seed / 2) % count;
let b = ((pattern_seed + 1) / 2) % count;
let c = ((pattern_seed + 3) / 2) % count;
@@ -411,16 +495,17 @@ pub fn wink_leds(pattern_seed: usize) {
_ => l,
};
if k == a || k == b || k == c {
led::get(l).flex_unwrap().on().flex_unwrap();
Leds::<S>::on(l as u32).unwrap();
} else {
led::get(l).flex_unwrap().off().flex_unwrap();
Leds::<S>::off(l as u32).unwrap();
}
}
}
pub fn switch_off_leds() {
for l in 0..led::count().flex_unwrap() {
led::get(l).flex_unwrap().off().flex_unwrap();
pub fn switch_off_leds<S: Syscalls>() {
let count = Leds::<S>::count().unwrap();
for l in 0..count {
Leds::<S>::off(l).unwrap();
}
}

111
src/env/tock/phantom_buffer_storage.rs vendored Normal file
View File

@@ -0,0 +1,111 @@
// 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 alloc::borrow::Cow;
use core::marker::PhantomData;
use libtock_platform as platform;
use libtock_platform::Syscalls;
use persistent_store::{
BufferCorruptFunction, BufferOptions, BufferStorage, Storage, StorageIndex, StorageResult,
};
/// Wrapper with phantom data for the test storage implementation.
pub struct PhantomBufferStorage<
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
> {
storage: BufferStorage,
s: PhantomData<S>,
c: PhantomData<C>,
}
impl<S, C> PhantomBufferStorage<S, C>
where
S: Syscalls,
C: platform::allow_ro::Config + platform::subscribe::Config,
{
pub fn new(storage: Box<[u8]>, options: BufferOptions) -> Self {
Self {
storage: BufferStorage::new(storage, options),
s: PhantomData,
c: PhantomData,
}
}
pub fn arm_interruption(&mut self, delay: usize) {
self.storage.arm_interruption(delay);
}
pub fn disarm_interruption(&mut self) -> usize {
self.storage.disarm_interruption()
}
pub fn reset_interruption(&mut self) {
self.storage.reset_interruption();
}
pub fn corrupt_operation(&mut self, corrupt: BufferCorruptFunction) {
self.storage.corrupt_operation(corrupt);
}
pub fn get_word_writes(&self, word: usize) -> usize {
self.storage.get_word_writes(word)
}
pub fn get_page_erases(&self, page: usize) -> usize {
self.storage.get_page_erases(page)
}
pub fn set_page_erases(&mut self, page: usize, cycle: usize) {
self.storage.set_page_erases(page, cycle);
}
}
impl<S, C> Storage for PhantomBufferStorage<S, C>
where
S: Syscalls,
C: platform::allow_ro::Config + platform::subscribe::Config,
{
fn word_size(&self) -> usize {
self.storage.word_size()
}
fn page_size(&self) -> usize {
self.storage.page_size()
}
fn num_pages(&self) -> usize {
self.storage.num_pages()
}
fn max_word_writes(&self) -> usize {
self.storage.max_word_writes()
}
fn max_page_erases(&self) -> usize {
self.storage.max_page_erases()
}
fn read_slice(&self, index: StorageIndex, length: usize) -> StorageResult<Cow<[u8]>> {
self.storage.read_slice(index, length)
}
fn write_slice(&mut self, index: StorageIndex, value: &[u8]) -> StorageResult<()> {
self.storage.write_slice(index, value)
}
fn erase_page(&mut self, page: usize) -> StorageResult<()> {
self.storage.erase_page(page)
}
}

View File

@@ -20,34 +20,37 @@ use super::TockEnv;
use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::cell::Cell;
use libtock_core::{callback, syscalls};
use core::marker::PhantomData;
use libtock_platform as platform;
use libtock_platform::{syscall_class, ErrorCode, RawSyscalls, Syscalls};
use opensk::api::crypto::sha256::Sha256;
use opensk::env::Sha;
use persistent_store::{Storage, StorageError, StorageIndex, StorageResult};
use platform::share;
const DRIVER_NUMBER: usize = 0x50003;
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: usize = 0;
pub const DONE: u32 = 0;
}
mod command_nr {
pub const GET_INFO: usize = 1;
pub const GET_INFO: u32 = 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 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: usize = 2;
pub const ERASE_PAGE: usize = 3;
pub const WRITE_SLICE: u32 = 2;
pub const ERASE_PAGE: u32 = 3;
}
mod allow_nr {
pub const WRITE_SLICE: usize = 0;
mod ro_allow_nr {
pub const WRITE_SLICE: u32 = 0;
}
mod memop_nr {
@@ -58,85 +61,98 @@ mod memop_nr {
}
mod storage_type {
pub const STORE: usize = 1;
pub const PARTITION: usize = 2;
pub const STORE: u32 = 1;
pub const PARTITION: u32 = 2;
}
fn get_info(nr: usize, arg: usize) -> StorageResult<usize> {
let code = syscalls::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg);
code.map_err(|_| StorageError::CustomError)
fn get_info<S: Syscalls>(nr: u32, arg: u32) -> StorageResult<u32> {
let info = S::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg)
.to_result::<u32, ErrorCode>()
.map_err(|_| StorageError::CustomError)?;
Ok(info)
}
fn memop(nr: u32, arg: usize) -> StorageResult<usize> {
let code = unsafe { syscalls::raw::memop(nr, arg) };
if code < 0 {
Err(StorageError::CustomError)
} else {
Ok(code as usize)
fn memop<S: RawSyscalls>(nr: u32, arg: u32) -> StorageResult<u32> {
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: usize, cmd: usize, arg1: usize, arg2: usize) -> StorageResult<()> {
let done = Cell::new(None);
let mut alarm = |status| done.set(Some(status));
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::DONE,
&mut alarm,
);
if subscription.is_err() {
return Err(StorageError::CustomError);
}
fn block_command<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config>(
driver: u32,
cmd: u32,
arg1: u32,
arg2: u32,
) -> StorageResult<()> {
let called: Cell<Option<(u32,)>> = Cell::new(None);
let code = syscalls::command(driver, cmd, arg1, arg2);
if code.is_err() {
return Err(StorageError::CustomError);
}
libtock_drivers::util::yieldk_for(|| done.get().is_some());
if done.get().unwrap() == 0 {
Ok(())
} else {
Err(StorageError::CustomError)
}
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::<S>::yieldk_for(|| called.get().is_some());
if called.get().unwrap().0 == 0 {
Ok(())
} else {
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<()> {
let code = unsafe {
syscalls::raw::allow(
fn write_slice<S: Syscalls, C: platform::allow_ro::Config + platform::subscribe::Config>(
ptr: usize,
value: &[u8],
) -> StorageResult<()> {
share::scope(|allow_ro| {
S::allow_ro::<C, DRIVER_NUMBER, { ro_allow_nr::WRITE_SLICE }>(allow_ro, value)
.map_err(|_| StorageError::CustomError)?;
block_command::<S, C>(
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(),
command_nr::WRITE_SLICE,
ptr as u32,
value.len() as u32,
)
};
if code < 0 {
return Err(StorageError::CustomError);
}
block_command(DRIVER_NUMBER, command_nr::WRITE_SLICE, ptr, value.len())
})
}
fn erase_page(ptr: usize, page_length: usize) -> StorageResult<()> {
block_command(DRIVER_NUMBER, command_nr::ERASE_PAGE, ptr, page_length)
fn erase_page<S: Syscalls, C: platform::allow_ro::Config + platform::subscribe::Config>(
ptr: usize,
page_length: usize,
) -> StorageResult<()> {
block_command::<S, C>(
DRIVER_NUMBER,
command_nr::ERASE_PAGE,
ptr as u32,
page_length as u32,
)
}
pub struct TockStorage {
pub struct TockStorage<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> {
word_size: usize,
page_size: usize,
num_pages: usize,
max_word_writes: usize,
max_page_erases: usize,
storage_locations: Vec<&'static [u8]>,
s: PhantomData<S>,
c: PhantomData<C>,
}
impl TockStorage {
impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> TockStorage<S, C> {
/// Provides access to the embedded flash if available.
///
/// # Errors
@@ -146,14 +162,16 @@ impl TockStorage {
/// - The page size is a power of two.
/// - The page size is a multiple of the word size.
/// - The storage is page-aligned.
pub fn new() -> StorageResult<TockStorage> {
pub fn new() -> StorageResult<TockStorage<S, C>> {
let mut syscall = TockStorage {
word_size: get_info(command_nr::get_info_nr::WORD_SIZE, 0)?,
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
word_size: get_info::<S>(command_nr::get_info_nr::WORD_SIZE, 0)? as usize,
page_size: get_info::<S>(command_nr::get_info_nr::PAGE_SIZE, 0)? as usize,
num_pages: 0,
max_word_writes: get_info(command_nr::get_info_nr::MAX_WORD_WRITES, 0)?,
max_page_erases: get_info(command_nr::get_info_nr::MAX_PAGE_ERASES, 0)?,
max_word_writes: get_info::<S>(command_nr::get_info_nr::MAX_WORD_WRITES, 0)? as usize,
max_page_erases: get_info::<S>(command_nr::get_info_nr::MAX_PAGE_ERASES, 0)? as usize,
storage_locations: Vec::new(),
s: PhantomData,
c: PhantomData,
};
if !syscall.word_size.is_power_of_two()
|| !syscall.page_size.is_power_of_two()
@@ -161,12 +179,13 @@ impl TockStorage {
{
return Err(StorageError::CustomError);
}
for i in 0..memop(memop_nr::STORAGE_CNT, 0)? {
if memop(memop_nr::STORAGE_TYPE, i)? != storage_type::STORE {
let num_storage_locations = memop::<S>(memop_nr::STORAGE_CNT, 0)?;
for i in 0..num_storage_locations {
if memop::<S>(memop_nr::STORAGE_TYPE, i)? != storage_type::STORE {
continue;
}
let storage_ptr = memop(memop_nr::STORAGE_PTR, i)?;
let storage_len = memop(memop_nr::STORAGE_LEN, i)?;
let storage_ptr = memop::<S>(memop_nr::STORAGE_PTR, i)? as usize;
let storage_len = memop::<S>(memop_nr::STORAGE_LEN, i)? as usize;
if !syscall.is_page_aligned(storage_ptr) || !syscall.is_page_aligned(storage_len) {
return Err(StorageError::CustomError);
}
@@ -187,7 +206,9 @@ impl TockStorage {
}
}
impl Storage for TockStorage {
impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> Storage
for TockStorage<S, C>
{
fn word_size(&self) -> usize {
self.word_size
}
@@ -218,26 +239,35 @@ impl Storage for TockStorage {
return Err(StorageError::NotAligned);
}
let ptr = self.read_slice(index, value.len())?.as_ptr() as usize;
write_slice(ptr, value)
write_slice::<S, C>(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)
erase_page::<S, C>(ptr, length)
}
}
pub struct TockUpgradeStorage {
pub struct TockUpgradeStorage<
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
> {
page_size: usize,
partition: Partition,
metadata: ModRange,
running_metadata: ModRange,
identifier: u32,
s: PhantomData<S>,
c: PhantomData<C>,
}
impl TockUpgradeStorage {
impl<S, C> TockUpgradeStorage<S, C>
where
S: Syscalls,
C: platform::allow_ro::Config + platform::subscribe::Config,
{
// Ideally, the kernel should tell us metadata and partitions directly.
// This code only works for one layout, refactor this into the storage driver to support more.
const METADATA_ADDRESS: usize = 0x4000;
@@ -258,25 +288,27 @@ impl TockUpgradeStorage {
/// Returns a `NotAligned` error if partitions or metadata ranges are
/// - not exclusive or,
/// - not consecutive.
pub fn new() -> StorageResult<TockUpgradeStorage> {
pub fn new() -> StorageResult<TockUpgradeStorage<S, C>> {
let mut locations = TockUpgradeStorage {
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
page_size: get_info::<S>(command_nr::get_info_nr::PAGE_SIZE, 0)? as usize,
partition: Partition::default(),
metadata: ModRange::new_empty(),
running_metadata: ModRange::new_empty(),
identifier: Self::PARTITION_ADDRESS_A as u32,
s: PhantomData,
c: PhantomData,
};
if !locations.page_size.is_power_of_two() {
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)?;
for i in 0..memop::<S>(memop_nr::STORAGE_CNT, 0)? {
let storage_type = memop::<S>(memop_nr::STORAGE_TYPE, i)?;
if !matches!(storage_type, storage_type::PARTITION) {
continue;
};
let storage_ptr = memop(memop_nr::STORAGE_PTR, i)?;
let storage_len = memop(memop_nr::STORAGE_LEN, i)?;
let storage_ptr = memop::<S>(memop_nr::STORAGE_PTR, i)? as usize;
let storage_len = memop::<S>(memop_nr::STORAGE_LEN, i)? as usize;
if !locations.is_page_aligned(storage_ptr) || !locations.is_page_aligned(storage_len) {
return Err(StorageError::CustomError);
}
@@ -337,7 +369,7 @@ impl TockUpgradeStorage {
/// Checks if the metadata's hash matches the partition's content.
fn check_partition_hash(&self, metadata: &[u8]) -> StorageResult<()> {
let start_address = self.metadata.start() + METADATA_SIGN_OFFSET;
let mut hasher = Sha::<TockEnv>::new();
let mut hasher = Sha::<TockEnv<S>>::new();
for range in self.partition.ranges_from(start_address) {
let partition_slice = unsafe { read_slice(range.start(), range.length()) };
// The hash implementation handles this in chunks, so no memory issues.
@@ -362,15 +394,15 @@ impl TockUpgradeStorage {
let write_range = ModRange::new(address, data.len());
if self.contains_metadata(&write_range)? {
let new_metadata = &data[self.metadata.start() - address..][..self.metadata.length()];
check_metadata::<TockEnv>(self, UPGRADE_PUBLIC_KEY, new_metadata)?;
check_metadata::<TockEnv<S, C>, S, C>(self, UPGRADE_PUBLIC_KEY, new_metadata)?;
}
// 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)?;
erase_page::<S, C>(address, self.page_size)?;
}
write_slice(address, &data)?;
write_slice::<S, C>(address, &data)?;
let written_slice = unsafe { read_slice(address, data.len()) };
if written_slice != data {
return Err(StorageError::CustomError);
@@ -378,7 +410,7 @@ impl TockUpgradeStorage {
// Case: Last slice is written.
if data.len() == self.partition.length() - offset {
let metadata = unsafe { read_slice(self.metadata.start(), self.metadata.length()) };
self.check_partition_hash(&metadata)?;
self.check_partition_hash(metadata)?;
}
Ok(())
}

View File

@@ -21,6 +21,8 @@ use crate::env::tock::buffer_upgrade_storage::BufferUpgradeStorage;
use crate::env::tock::storage::TockUpgradeStorage;
use arrayref::array_ref;
use byteorder::{ByteOrder, LittleEndian};
use libtock_platform as platform;
use libtock_platform::Syscalls;
use opensk::api::crypto::ecdsa::{PublicKey as _, Signature as _};
use opensk::env::{EcdsaPk, EcdsaSignature, Env};
use persistent_store::{StorageError, StorageResult};
@@ -39,9 +41,13 @@ pub const METADATA_SIGN_OFFSET: usize = 0x800;
///
/// Checks signature correctness against the hash, and whether the partition offset matches.
/// Whether the hash matches the partition content is not tested here!
pub fn check_metadata<E: Env>(
#[cfg(not(feature = "std"))] upgrade_locations: &TockUpgradeStorage,
#[cfg(feature = "std")] upgrade_locations: &BufferUpgradeStorage,
pub fn check_metadata<
E: Env,
S: Syscalls,
C: platform::subscribe::Config + platform::allow_ro::Config,
>(
#[cfg(not(feature = "std"))] upgrade_locations: &TockUpgradeStorage<S, C>,
#[cfg(feature = "std")] upgrade_locations: &BufferUpgradeStorage<S, C>,
public_key_bytes: &[u8],
metadata: &[u8],
) -> StorageResult<()> {
@@ -104,11 +110,13 @@ fn verify_signature<E: Env>(
mod test {
use super::*;
use arrayref::mut_array_refs;
use libtock_unittest::fake::Syscalls;
use opensk::api::crypto::ecdsa::SecretKey as _;
use opensk::api::crypto::sha256::Sha256;
use opensk::api::crypto::{EC_FIELD_SIZE, EC_SIGNATURE_SIZE};
use opensk::env::test::TestEnv;
use opensk::env::{EcdsaSk, Sha};
use platform::DefaultConfig;
fn to_uncompressed(public_key: &EcdsaPk<TestEnv>) -> [u8; 1 + 2 * EC_FIELD_SIZE] {
// Formatting according to:
@@ -147,20 +155,28 @@ mod test {
let public_key_bytes = to_uncompressed(&public_key);
assert_eq!(
check_metadata::<TestEnv>(&upgrade_locations, &public_key_bytes, &metadata),
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
&upgrade_locations,
&public_key_bytes,
&metadata
),
Ok(())
);
// Manipulating the partition address fails.
metadata[METADATA_SIGN_OFFSET + 8] = 0x88;
assert_eq!(
check_metadata::<TestEnv>(&upgrade_locations, &public_key_bytes, &metadata),
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
&upgrade_locations,
&public_key_bytes,
&metadata
),
Err(StorageError::CustomError)
);
metadata[METADATA_SIGN_OFFSET + 8] = 0x00;
// Wrong metadata length fails.
assert_eq!(
check_metadata::<TestEnv>(
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
&upgrade_locations,
&public_key_bytes,
&metadata[..METADATA_LEN - 1]
@@ -170,14 +186,22 @@ mod test {
// Manipulating the hash fails.
metadata[0] ^= 0x01;
assert_eq!(
check_metadata::<TestEnv>(&upgrade_locations, &public_key_bytes, &metadata),
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
&upgrade_locations,
&public_key_bytes,
&metadata
),
Err(StorageError::CustomError)
);
metadata[0] ^= 0x01;
// Manipulating the signature fails.
metadata[32] ^= 0x01;
assert_eq!(
check_metadata::<TestEnv>(&upgrade_locations, &public_key_bytes, &metadata),
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
&upgrade_locations,
&public_key_bytes,
&metadata
),
Err(StorageError::CustomError)
);
}

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(feature = "std"), no_main)]
extern crate alloc;
extern crate arrayref;
@@ -21,8 +22,6 @@ extern crate byteorder;
extern crate core;
extern crate lang_items;
#[cfg(feature = "with_ctap1")]
use core::cell::Cell;
use core::convert::TryFrom;
#[cfg(feature = "debug_ctap")]
use core::fmt::Write;
@@ -30,22 +29,31 @@ use core::fmt::Write;
use ctap2::env::tock::blink_leds;
use ctap2::env::tock::{switch_off_leds, wink_leds, TockEnv};
#[cfg(feature = "with_ctap1")]
use libtock_drivers::buttons::{self, ButtonState};
use libtock_buttons::Buttons;
#[cfg(feature = "debug_ctap")]
use libtock_drivers::console::Console;
use libtock_console::Console;
#[cfg(feature = "debug_ctap")]
use libtock_console::ConsoleWriter;
use libtock_drivers::result::FlexUnwrap;
use libtock_drivers::timer::Duration;
use libtock_drivers::usb_ctap_hid;
#[cfg(not(feature = "std"))]
use libtock_runtime::{set_main, stack_size, TockSyscalls};
#[cfg(feature = "std")]
use libtock_unittest::fake;
use opensk::api::clock::Clock;
use opensk::api::connection::{HidConnection, SendOrRecvStatus, UsbEndpoint};
use opensk::api::connection::UsbEndpoint;
use opensk::ctap::hid::HidPacketIterator;
use opensk::ctap::KEEPALIVE_DELAY_MS;
use opensk::env::Env;
use opensk::Transport;
libtock_core::stack_size! {0x4000}
#[cfg(not(feature = "std"))]
stack_size! {0x4000}
#[cfg(not(feature = "std"))]
set_main! {main}
const SEND_TIMEOUT_MS: usize = 1000;
const SEND_TIMEOUT_MS: Duration<isize> = Duration::from_ms(1000);
const KEEPALIVE_DELAY_MS_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
#[cfg(not(feature = "vendor_hid"))]
@@ -56,19 +64,18 @@ const NUM_ENDPOINTS: usize = 2;
// The reply/replies that are queued for each endpoint.
struct EndpointReply {
endpoint: UsbEndpoint,
transport: Transport,
reply: HidPacketIterator,
}
#[cfg(feature = "std")]
type SyscallImplementation = fake::Syscalls;
#[cfg(not(feature = "std"))]
type SyscallImplementation = TockSyscalls;
impl EndpointReply {
pub fn new(endpoint: UsbEndpoint) -> Self {
EndpointReply {
endpoint,
transport: match endpoint {
UsbEndpoint::MainHid => Transport::MainHid,
#[cfg(feature = "vendor_hid")]
UsbEndpoint::VendorHid => Transport::VendorHid,
},
reply: HidPacketIterator::none(),
}
}
@@ -76,7 +83,7 @@ impl EndpointReply {
// A single packet to send.
struct SendPacket {
transport: Transport,
endpoint: UsbEndpoint,
packet: [u8; 64],
}
@@ -99,97 +106,103 @@ impl EndpointReplies {
for ep in self.replies.iter_mut() {
if let Some(packet) = ep.reply.next() {
return Some(SendPacket {
transport: ep.transport,
endpoint: ep.endpoint,
packet,
});
}
}
None
}
pub fn clear(&mut self, endpoint: UsbEndpoint) {
for ep in self.replies.iter_mut() {
if ep.endpoint == endpoint {
*ep = EndpointReply::new(endpoint);
break;
}
}
}
}
fn main() {
#[cfg(feature = "debug_ctap")]
let mut writer = Console::<SyscallImplementation>::writer();
#[cfg(feature = "debug_ctap")]
{
writeln!(writer, "Hello world from OpenSK!").ok().unwrap();
}
// Setup USB driver.
if !usb_ctap_hid::setup() {
if !usb_ctap_hid::UsbCtapHid::<SyscallImplementation>::setup() {
panic!("Cannot setup USB driver");
}
let env = TockEnv::default();
let env = TockEnv::<SyscallImplementation>::default();
let mut ctap = opensk::Ctap::new(env);
let mut led_counter = 0;
let mut led_blink_timer = <<TockEnv as Env>::Clock as Clock>::Timer::default();
let mut led_blink_timer =
<<TockEnv<SyscallImplementation> as Env>::Clock as Clock>::Timer::default();
let mut replies = EndpointReplies::new();
// Main loop. If CTAP1 is used, we register button presses for U2F while receiving and waiting.
// The way TockOS and apps currently interact, callbacks need a yield syscall to execute,
// making consistent blinking patterns and sending keepalives harder.
#[cfg(feature = "debug_ctap")]
writeln!(writer, "Entering main ctap loop").unwrap();
loop {
// Create the button callback, used for CTAP1.
#[cfg(feature = "with_ctap1")]
let button_touched = Cell::new(false);
#[cfg(feature = "with_ctap1")]
let mut buttons_callback = buttons::with_callback(|_button_num, state| {
match state {
ButtonState::Pressed => button_touched.set(true),
ButtonState::Released => (),
};
});
#[cfg(feature = "with_ctap1")]
let mut buttons = buttons_callback.init().flex_unwrap();
// At the moment, all buttons are accepted. You can customize your setup here.
#[cfg(feature = "with_ctap1")]
for mut button in &mut buttons {
button.enable().flex_unwrap();
}
let num_buttons = Buttons::<SyscallImplementation>::count().ok().unwrap();
// Variable for use in both the send_and_maybe_recv and recv cases.
let mut usb_endpoint: Option<UsbEndpoint> = None;
let mut pkt_request = [0; 64];
if let Some(mut packet) = replies.next_packet() {
// send and receive.
let hid_connection = packet.transport.hid_connection(ctap.env());
match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT_MS) {
Ok(SendOrRecvStatus::Timeout) => {
if let Some(packet) = replies.next_packet() {
match usb_ctap_hid::UsbCtapHid::<SyscallImplementation>::send(
&packet.packet,
SEND_TIMEOUT_MS,
packet.endpoint as u32,
)
.flex_unwrap()
{
usb_ctap_hid::SendOrRecvStatus::Sent => {
#[cfg(feature = "debug_ctap")]
print_packet_notice(
"Sending packet timed out",
print_packet_notice::<SyscallImplementation>(
"Sent packet",
ctap.env().clock().timestamp_us(),
&mut writer,
);
// TODO: reset the ctap_hid state.
// Since sending the packet timed out, we cancel this reply.
break;
}
Ok(SendOrRecvStatus::Sent) => {
usb_ctap_hid::SendOrRecvStatus::Timeout => {
#[cfg(feature = "debug_ctap")]
print_packet_notice("Sent packet", ctap.env().clock().timestamp_us());
}
Ok(SendOrRecvStatus::Received(ep)) => {
#[cfg(feature = "debug_ctap")]
print_packet_notice(
"Received another packet",
print_packet_notice::<SyscallImplementation>(
"Timeout while sending packet",
ctap.env().clock().timestamp_us(),
&mut writer,
);
usb_endpoint = Some(ep);
// Copy to incoming packet to local buffer to be consistent
// with the receive flow.
pkt_request = packet.packet;
// The client is unresponsive, so we discard all pending packets.
replies.clear(packet.endpoint);
}
Err(_) => panic!("Error sending packet"),
}
_ => panic!("Unexpected status on USB transmission"),
};
} else {
// receive
usb_endpoint =
match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_MS_TOCK)
.flex_unwrap()
match usb_ctap_hid::UsbCtapHid::<SyscallImplementation>::recv_with_timeout(
&mut pkt_request,
KEEPALIVE_DELAY_MS_TOCK,
)
.flex_unwrap()
{
usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => {
#[cfg(feature = "debug_ctap")]
print_packet_notice("Received packet", ctap.env().clock().timestamp_us());
UsbEndpoint::try_from(endpoint).ok()
print_packet_notice::<SyscallImplementation>(
"Received packet",
ctap.env().clock().timestamp_us(),
&mut writer,
);
UsbEndpoint::try_from(endpoint as usize).ok()
}
usb_ctap_hid::SendOrRecvStatus::Sent => {
panic!("Returned transmit status on receive")
@@ -200,17 +213,10 @@ fn main() {
#[cfg(feature = "with_ctap1")]
{
if button_touched.get() {
let button_touched = (0..num_buttons).any(Buttons::<SyscallImplementation>::is_pressed);
if button_touched {
ctap.u2f_grant_user_presence();
}
// Cleanup button callbacks. We miss button presses while processing though.
// Heavy computation mostly follows a registered touch luckily. Unregistering
// callbacks is important to not clash with those from check_user_presence.
for mut button in &mut buttons {
button.disable().flex_unwrap();
}
drop(buttons);
drop(buttons_callback);
}
// This call is making sure that even for long inactivity, wrapping clock values
@@ -231,7 +237,7 @@ fn main() {
if ep.reply.has_data() {
#[cfg(feature = "debug_ctap")]
writeln!(
Console::new(),
Console::<SyscallImplementation>::writer(),
"Warning overwriting existing reply for endpoint {}",
endpoint as usize
)
@@ -252,26 +258,30 @@ fn main() {
}
if ctap.should_wink() {
wink_leds(led_counter);
wink_leds::<SyscallImplementation>(led_counter);
} else {
#[cfg(not(feature = "with_ctap1"))]
switch_off_leds();
switch_off_leds::<SyscallImplementation>();
#[cfg(feature = "with_ctap1")]
if ctap.u2f_needs_user_presence() {
// Flash the LEDs with an almost regular pattern. The inaccuracy comes from
// delay caused by processing and sending of packets.
blink_leds(led_counter);
blink_leds::<SyscallImplementation>(led_counter);
} else {
switch_off_leds();
switch_off_leds::<SyscallImplementation>();
}
}
}
}
#[cfg(feature = "debug_ctap")]
fn print_packet_notice(notice_text: &str, now_us: usize) {
fn print_packet_notice<S: libtock_platform::Syscalls>(
notice_text: &str,
now_us: usize,
writer: &mut ConsoleWriter<S>,
) {
writeln!(
Console::new(),
writer,
"{} at {}.{:06} s",
notice_text,
now_us / 1_000_000,

82
third_party/lang-items/Cargo.lock generated vendored
View File

@@ -6,32 +6,53 @@ version = 3
name = "lang_items"
version = "0.1.0"
dependencies = [
"libtock_core",
"libtock_console",
"libtock_drivers",
"libtock_leds",
"libtock_low_level_debug",
"libtock_platform",
"libtock_runtime",
"linked_list_allocator",
]
[[package]]
name = "libtock_codegen"
name = "libtock_console"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "libtock_core"
version = "0.1.0"
dependencies = [
"libtock_codegen",
"libtock_platform",
]
[[package]]
name = "libtock_drivers"
version = "0.1.0"
dependencies = [
"libtock_core",
"libtock_console",
"libtock_platform",
]
[[package]]
name = "libtock_leds"
version = "0.1.0"
dependencies = [
"libtock_platform",
]
[[package]]
name = "libtock_low_level_debug"
version = "0.1.0"
dependencies = [
"libtock_platform",
]
[[package]]
name = "libtock_platform"
version = "0.1.0"
[[package]]
name = "libtock_runtime"
version = "0.1.0"
dependencies = [
"libtock_platform",
]
[[package]]
@@ -39,38 +60,3 @@ name = "linked_list_allocator"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e322f259d225fbae43a1b053b2dc6a5968a6bdf8b205f5de684dab485b95030e"
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"

View File

@@ -8,10 +8,22 @@ authors = [
license = "MIT/Apache-2.0"
edition = "2018"
[target.'cfg(any(target_arch = "arm", target_arch = "riscv32"))'.dependencies.libtock_runtime]
path = "../../third_party/libtock-rs/runtime"
default-features = false
features = ["no_auto_layout", "alloc_init"]
[dependencies]
libtock_core = { path = "../../third_party/libtock-rs/core", default-features = false, features = ["alloc_init", "custom_panic_handler", "custom_alloc_error_handler"] }
libtock_drivers = { path = "../libtock-drivers" }
linked_list_allocator = { version = "0.10.2", default-features = false, features = ["const_mut_refs"] }
libtock_platform = { path = "../../third_party/libtock-rs/platform" }
libtock_low_level_debug = { path = "../../third_party/libtock-rs/apis/low_level_debug" }
libtock_leds = { path = "../../third_party/libtock-rs/apis/leds" }
libtock_console = { path = "../../third_party/libtock-rs/apis/console" }
[dependencies.linked_list_allocator]
version = "0.10.4"
default-features = false
features = ["const_mut_refs"]
[features]
debug_allocations = []

View File

@@ -9,14 +9,17 @@ use core::sync::atomic;
#[cfg(feature = "debug_allocations")]
use core::sync::atomic::AtomicUsize;
#[cfg(any(feature = "debug_allocations", feature = "panic_console"))]
use libtock_drivers::console::Console;
use libtock_console::Console;
#[cfg(feature = "panic_console")]
use libtock_platform::{ErrorCode, Syscalls};
use libtock_runtime::TockSyscalls;
use linked_list_allocator::Heap;
static mut HEAP: Heap = Heap::empty();
#[no_mangle]
unsafe fn libtock_alloc_init(app_heap_start: usize, app_heap_size: usize) {
HEAP.init(app_heap_start as *mut u8, app_heap_size);
unsafe fn libtock_alloc_init(app_heap_bottom: *mut u8, app_heap_size: usize) {
HEAP.init(app_heap_bottom, app_heap_size);
}
// With the "debug_allocations" feature, we use `AtomicUsize` to store the
@@ -54,7 +57,7 @@ unsafe impl GlobalAlloc for TockAllocator {
self.count.fetch_add(1, atomic::Ordering::SeqCst);
self.size.fetch_add(layout.size(), atomic::Ordering::SeqCst);
writeln!(
Console::new(),
Console::<TockSyscalls>::writer(),
"alloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
layout.size(),
layout.align(),
@@ -73,7 +76,7 @@ unsafe impl GlobalAlloc for TockAllocator {
self.count.fetch_sub(1, atomic::Ordering::SeqCst);
self.size.fetch_sub(layout.size(), atomic::Ordering::SeqCst);
writeln!(
Console::new(),
Console::<TockSyscalls>::writer(),
"dealloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
layout.size(),
layout.align(),
@@ -93,17 +96,20 @@ static ALLOCATOR: TockAllocator = TockAllocator::new();
#[alloc_error_handler]
unsafe fn alloc_error_handler(_layout: Layout) -> ! {
util::signal_oom();
util::signal_panic();
util::Util::<TockSyscalls>::signal_oom();
util::Util::<TockSyscalls>::signal_panic();
#[cfg(feature = "panic_console")]
{
writeln!(Console::new(), "Couldn't allocate: {:?}", _layout).ok();
// Force the kernel to report the panic cause, by reading an invalid address.
// The memory protection unit should be setup by the Tock kernel to prevent apps from accessing
// address zero.
core::ptr::read_volatile(0 as *const usize);
writeln!(
Console::<TockSyscalls>::writer(),
"Couldn't allocate: {:?}",
_layout
)
.ok();
TockSyscalls::exit_terminate(ErrorCode::Fail as u32);
}
util::cycle_leds()
#[cfg(not(feature = "panic_console"))]
util::Util::<TockSyscalls>::cycle_leds()
}

View File

@@ -10,7 +10,7 @@ mod util;
#[cfg(feature = "std")]
#[no_mangle]
unsafe fn libtock_alloc_init(_app_heap_start: usize, _app_heap_size: usize) {
unsafe fn libtock_alloc_init(_app_heap_bottom: *mut u8, _app_heap_size: usize) {
// Stub so that the symbol is present.
unimplemented!()
}

View File

@@ -1,26 +1,25 @@
//! Custom panic handler for OpenSK
use crate::util;
#[cfg(feature = "panic_console")]
use core::fmt::Write;
use core::panic::PanicInfo;
#[cfg(feature = "panic_console")]
use libtock_drivers::console::Console;
use libtock_console::Console;
#[allow(unused_imports)]
use libtock_platform::{ErrorCode, Syscalls};
use libtock_runtime::TockSyscalls;
#[panic_handler]
fn panic_handler(_info: &PanicInfo) -> ! {
util::signal_panic();
fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
util::Util::<TockSyscalls>::signal_panic();
#[cfg(feature = "panic_console")]
{
let mut console = Console::new();
writeln!(console, "{}", _info).ok();
console.flush();
// Force the kernel to report the panic cause, by reading an invalid address.
// The memory protection unit should be setup by the Tock kernel to prevent apps from accessing
// address zero.
unsafe {
core::ptr::read_volatile(0 as *const usize);
}
let mut writer = Console::<TockSyscalls>::writer();
writeln!(writer, "{}", _info).ok();
// Exit with a non-zero exit code to indicate failure.
TockSyscalls::exit_terminate(ErrorCode::Fail as u32);
}
util::flash_all_leds();
#[cfg(not(feature = "panic_console"))]
util::Util::<TockSyscalls>::flash_all_leds();
}

View File

@@ -1,45 +1,55 @@
use libtock_drivers::led;
use libtock_drivers::timer::{self, Duration};
use libtock_drivers::timer;
use libtock_leds::Leds;
use libtock_low_level_debug::{AlertCode, LowLevelDebug};
use libtock_platform as platform;
use libtock_platform::Syscalls;
use platform::DefaultConfig;
// Signal a panic using the LowLevelDebug capsule (if available).
pub fn signal_panic() {
let _ = libtock_core::syscalls::command1_insecure(8, 1, 1);
}
pub struct Util<S: Syscalls, C: platform::subscribe::Config = DefaultConfig>(S, C);
// Signal an out-of-memory error using the LowLevelDebug capsule (if available).
pub fn signal_oom() {
let _ = libtock_core::syscalls::command1_insecure(8, 2, 1);
}
pub fn flash_all_leds() -> ! {
// Flash all LEDs (if available). All errors from syscalls are ignored: we are already inside a
// panic handler so there is nothing much to do if simple drivers (timer, LEDs) don't work.
loop {
if let Ok(leds) = led::all() {
for led in leds {
let _ = led.on();
}
}
let _ = timer::sleep(Duration::from_ms(100));
if let Ok(leds) = led::all() {
for led in leds {
let _ = led.off();
}
}
let _ = timer::sleep(Duration::from_ms(100));
impl<S: Syscalls, C: platform::subscribe::Config> Util<S, C> {
/// Signal a panic using the LowLevelDebug capsule (if available).
pub fn signal_panic() {
LowLevelDebug::<S>::print_alert_code(AlertCode::Panic);
}
}
pub fn cycle_leds() -> ! {
// Cycle though all LEDs (if available). All errors from syscalls are ignored: we are already
// inside an error handler so there is nothing much to do if simple drivers (timer, LEDs) don't
// work.
loop {
if let Ok(leds) = led::all() {
for led in leds {
let _ = led.on();
let _ = timer::sleep(Duration::from_ms(100));
let _ = led.off();
/// Signal an out-of-memory error using the LowLevelDebug capsule (if available).
pub fn signal_oom() {
LowLevelDebug::<S>::print_alert_code(AlertCode::WrongLocation);
}
#[allow(dead_code)]
pub fn flash_all_leds() -> ! {
// Flash all LEDs (if available). All errors from syscalls are ignored: we are already inside a
// panic handler so there is nothing much to do if simple drivers (timer, LEDs) don't work.
loop {
if let Ok(led_count) = Leds::<S>::count() {
for led in 0..led_count {
let _ = Leds::<S>::on(led);
}
}
let _ = timer::Alarm::<S, C>::sleep_for(timer::Milliseconds(100));
if let Ok(led_count) = Leds::<S>::count() {
for led in 0..led_count {
let _ = Leds::<S>::off(led);
}
}
let _ = timer::Alarm::<S, C>::sleep_for(timer::Milliseconds(100));
}
}
#[allow(dead_code)]
pub fn cycle_leds() -> ! {
// Cycle though all LEDs (if available). All errors from syscalls are ignored: we are already
// inside an error handler so there is nothing much to do if simple drivers (timer, LEDs) don't
// work.
loop {
if let Ok(leds) = Leds::<S>::count() {
for led in 0..leds {
let _ = Leds::<S>::on(led);
let _ = timer::Alarm::<S, C>::sleep_for(timer::Milliseconds(100));
let _ = Leds::<S>::off(led);
}
}
}
}

View File

@@ -3,59 +3,20 @@
version = 3
[[package]]
name = "libtock_codegen"
name = "libtock_console"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "libtock_core"
version = "0.1.0"
dependencies = [
"libtock_codegen",
"libtock_platform",
]
[[package]]
name = "libtock_drivers"
version = "0.1.0"
dependencies = [
"libtock_core",
"libtock_console",
"libtock_platform",
]
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "syn"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
name = "libtock_platform"
version = "0.1.0"

View File

@@ -9,9 +9,10 @@ license = "MIT/Apache-2.0"
edition = "2018"
[dependencies]
libtock_core = { path = "../../third_party/libtock-rs/core" }
libtock_console = { path = "../../third_party/libtock-rs/apis/console" }
libtock_platform = { path = "../../third_party/libtock-rs/platform" }
[features]
debug_ctap = []
verbose_usb = ["debug_ctap"]
with_nfc=[]
with_nfc = []

View File

@@ -1,170 +0,0 @@
use crate::result::{OtherError, TockResult};
use core::marker::PhantomData;
use libtock_core::callback::{CallbackSubscription, Consumer};
use libtock_core::syscalls;
const DRIVER_NUMBER: usize = 0x00003;
mod command_nr {
pub const COUNT: usize = 0;
pub const ENABLE_INTERRUPT: usize = 1;
pub const DISABLE_INTERRUPT: usize = 2;
pub const READ: usize = 3;
}
mod subscribe_nr {
pub const SUBSCRIBE_CALLBACK: usize = 0;
}
pub fn with_callback<CB>(callback: CB) -> WithCallback<CB> {
WithCallback { callback }
}
pub struct WithCallback<CB> {
callback: CB,
}
struct ButtonConsumer;
impl<CB: FnMut(usize, ButtonState)> Consumer<WithCallback<CB>> for ButtonConsumer {
fn consume(data: &mut WithCallback<CB>, button_num: usize, state: usize, _: usize) {
(data.callback)(button_num, state.into());
}
}
impl<CB: FnMut(usize, ButtonState)> WithCallback<CB> {
pub fn init(&mut self) -> TockResult<Buttons> {
let count = syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?;
let subscription = syscalls::subscribe::<ButtonConsumer, _>(
DRIVER_NUMBER,
subscribe_nr::SUBSCRIBE_CALLBACK,
self,
)?;
Ok(Buttons {
count: count as usize,
subscription,
})
}
}
pub struct Buttons<'a> {
count: usize,
#[allow(dead_code)] // Used in drop
subscription: CallbackSubscription<'a>,
}
#[derive(Copy, Clone, Debug)]
pub enum ButtonsError {
NotSupported,
SubscriptionFailed,
}
impl<'a> Buttons<'a> {
pub fn iter_mut(&mut self) -> ButtonIter {
ButtonIter {
curr_button: 0,
button_count: self.count,
lifetime: PhantomData,
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum ButtonState {
Pressed,
Released,
}
impl From<usize> for ButtonState {
fn from(state: usize) -> ButtonState {
match state {
0 => ButtonState::Released,
1 => ButtonState::Pressed,
_ => unreachable!(),
}
}
}
impl<'a, 'b> IntoIterator for &'b mut Buttons<'a> {
type Item = ButtonHandle<'b>;
type IntoIter = ButtonIter<'b>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
pub struct ButtonIter<'a> {
curr_button: usize,
button_count: usize,
lifetime: PhantomData<&'a ()>,
}
impl<'a> Iterator for ButtonIter<'a> {
type Item = ButtonHandle<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.curr_button < self.button_count {
let item = ButtonHandle {
button_num: self.curr_button,
lifetime: PhantomData,
};
self.curr_button += 1;
Some(item)
} else {
None
}
}
}
pub struct ButtonHandle<'a> {
button_num: usize,
lifetime: PhantomData<&'a ()>,
}
impl<'a> ButtonHandle<'a> {
pub fn enable(&mut self) -> TockResult<Button> {
syscalls::command(
DRIVER_NUMBER,
command_nr::ENABLE_INTERRUPT,
self.button_num,
0,
)?;
Ok(Button { handle: self })
}
pub fn disable(&mut self) -> TockResult<()> {
syscalls::command(
DRIVER_NUMBER,
command_nr::DISABLE_INTERRUPT,
self.button_num,
0,
)?;
Ok(())
}
}
pub struct Button<'a> {
handle: &'a ButtonHandle<'a>,
}
#[derive(Copy, Clone, Debug)]
pub enum ButtonError {
ActivationFailed,
}
impl<'a> Button<'a> {
pub fn read(&self) -> TockResult<ButtonState> {
let button_state =
syscalls::command(DRIVER_NUMBER, command_nr::READ, self.handle.button_num, 0)?;
match button_state {
0 => Ok(ButtonState::Released),
1 => Ok(ButtonState::Pressed),
_ => Err(OtherError::ButtonsDriverInvalidState.into()),
}
}
}

View File

@@ -1,115 +0,0 @@
use crate::util;
use core::cell::Cell;
use core::fmt;
use libtock_core::{callback, syscalls};
const DRIVER_NUMBER: usize = 1;
mod command_nr {
pub const WRITE: usize = 1;
}
mod subscribe_nr {
pub const SET_ALARM: usize = 1;
}
mod allow_nr {
pub const SHARE_BUFFER: usize = 1;
}
pub const BUFFER_SIZE: usize = 1024;
pub struct Console {
allow_buffer: [u8; BUFFER_SIZE],
count_pending: usize,
}
impl Console {
pub fn new() -> Console {
Console {
allow_buffer: [0; BUFFER_SIZE],
count_pending: 0,
}
}
fn is_empty(&self) -> bool {
self.count_pending == 0
}
fn is_full(&self) -> bool {
self.allow_buffer.len() == self.count_pending
}
fn available_len(&self) -> usize {
self.allow_buffer.len() - self.count_pending
}
pub fn write<S: AsRef<[u8]>>(&mut self, text: S) {
let mut not_written_yet = text.as_ref();
while !not_written_yet.is_empty() {
let num_bytes_to_print = self.available_len().min(not_written_yet.len());
self.allow_buffer[self.count_pending..(self.count_pending + num_bytes_to_print)]
.copy_from_slice(&not_written_yet[..num_bytes_to_print]);
self.count_pending += num_bytes_to_print;
if self.is_full() {
self.flush();
}
not_written_yet = &not_written_yet[num_bytes_to_print..];
}
}
pub fn flush(&mut self) {
if self.is_empty() {
// Don't trigger any syscall if the buffer is empty.
return;
}
let count = self.count_pending;
// Clear the buffer even in case of error, to avoid an infinite loop.
self.count_pending = 0;
Console::write_unbuffered(&mut self.allow_buffer[..count]);
}
pub fn write_unbuffered(buf: &mut [u8]) {
let count = buf.len();
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf);
if result.is_err() {
return;
}
let is_written = Cell::new(false);
let mut is_written_alarm = || is_written.set(true);
let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::SET_ALARM,
&mut is_written_alarm,
);
if subscription.is_err() {
return;
}
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::WRITE, count, 0);
if result_code.is_err() {
return;
}
util::yieldk_for(|| is_written.get());
}
}
impl Drop for Console {
fn drop(&mut self) {
self.flush();
}
}
impl fmt::Write for Console {
fn write_str(&mut self, string: &str) -> Result<(), fmt::Error> {
self.write(string);
Ok(())
}
}

View File

@@ -1,15 +1,17 @@
use crate::result::TockResult;
use libtock_core::syscalls;
use libtock_platform as platform;
use libtock_platform::{DefaultConfig, Syscalls};
use platform::ErrorCode;
const DRIVER_NUMBER: usize = 0x00008;
const DRIVER_NUMBER: u32 = 0x00008;
mod command_nr {
pub const AVAILABLE: usize = 0;
pub const GET_PROTECTION: usize = 1;
pub const SET_PROTECTION: usize = 2;
pub const AVAILABLE: u32 = 0;
pub const GET_PROTECTION: u32 = 1;
pub const SET_PROTECTION: u32 = 2;
}
#[derive(PartialOrd, PartialEq)]
#[derive(PartialOrd, PartialEq, Eq)]
pub enum ProtectionLevel {
/// Unsupported feature
Unknown = 0,
@@ -25,8 +27,10 @@ pub enum ProtectionLevel {
FullyLocked = 0xff,
}
impl From<usize> for ProtectionLevel {
fn from(value: usize) -> Self {
pub struct Crp<S: Syscalls, C: platform::subscribe::Config = DefaultConfig>(S, C);
impl From<u32> for ProtectionLevel {
fn from(value: u32) -> Self {
match value {
1 => ProtectionLevel::NoProtection,
2 => ProtectionLevel::JtagDisabled,
@@ -36,17 +40,24 @@ impl From<usize> for ProtectionLevel {
}
}
pub fn is_available() -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::AVAILABLE, 0, 0)?;
Ok(())
}
impl<S: Syscalls, C: platform::subscribe::Config> Crp<S, C> {
pub fn is_available() -> TockResult<()> {
S::command(DRIVER_NUMBER, command_nr::AVAILABLE, 0, 0).to_result::<(), ErrorCode>()?;
pub fn get_protection() -> TockResult<ProtectionLevel> {
let current_level = syscalls::command(DRIVER_NUMBER, command_nr::GET_PROTECTION, 0, 0)?;
Ok(current_level.into())
}
Ok(())
}
pub fn set_protection(level: ProtectionLevel) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::SET_PROTECTION, level as usize, 0)?;
Ok(())
pub fn get_protection() -> TockResult<ProtectionLevel> {
let protection_level = S::command(DRIVER_NUMBER, command_nr::GET_PROTECTION, 0, 0)
.to_result::<u32, ErrorCode>()?;
Ok(protection_level.into())
}
pub fn set_protection(level: ProtectionLevel) -> TockResult<()> {
S::command(DRIVER_NUMBER, command_nr::SET_PROTECTION, level as u32, 0)
.to_result::<(), ErrorCode>()?;
Ok(())
}
}

View File

@@ -1,84 +0,0 @@
use crate::result::{OtherError, TockError, TockResult};
use libtock_core::syscalls;
const DRIVER_NUMBER: usize = 0x00002;
mod command_nr {
pub const COUNT: usize = 0;
pub const ON: usize = 1;
pub const OFF: usize = 2;
pub const TOGGLE: usize = 3;
}
pub struct Led {
led_num: usize,
}
pub fn count() -> TockResult<usize> {
let count = syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?;
Ok(count)
}
pub fn get(led_num: usize) -> TockResult<Led> {
let led_count = count()?;
if led_num < led_count {
Ok(Led { led_num })
} else {
Err(TockError::Other(OtherError::OutOfRange))
}
}
pub fn all() -> TockResult<LedIter> {
let led_count = count()?;
Ok(LedIter {
curr_led: 0,
led_count,
})
}
impl Led {
pub fn set_state(&self, state: bool) -> TockResult<()> {
if state {
self.on()
} else {
self.off()
}
}
pub fn on(&self) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::ON, self.led_num, 0)?;
Ok(())
}
pub fn off(&self) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::OFF, self.led_num, 0)?;
Ok(())
}
pub fn toggle(&self) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::TOGGLE, self.led_num, 0)?;
Ok(())
}
}
#[derive(Copy, Clone)]
pub struct LedIter {
curr_led: usize,
led_count: usize,
}
impl Iterator for LedIter {
type Item = Led;
fn next(&mut self) -> Option<Self::Item> {
if self.curr_led < self.led_count {
let item = Led {
led_num: self.curr_led,
};
self.curr_led += 1;
Some(item)
} else {
None
}
}
}

View File

@@ -1,9 +1,6 @@
#![no_std]
pub mod buttons;
pub mod console;
pub mod crp;
pub mod led;
#[cfg(feature = "with_nfc")]
pub mod nfc;
pub mod result;

View File

@@ -1,103 +1,124 @@
use crate::result::TockResult;
use crate::util;
use core::cell::Cell;
use core::mem;
use libtock_core::{callback, syscalls};
use libtock_platform as platform;
use platform::{share, AllowRo, AllowRw, DefaultConfig, ErrorCode, Subscribe, Syscalls};
const DRIVER_NUMBER: usize = 0x30003;
const DRIVER_NUMBER: u32 = 0x30003;
mod command_nr {
pub const CHECK: usize = 0;
pub const TRANSMIT: usize = 1;
pub const RECEIVE: usize = 2;
pub const EMULATE: usize = 3;
pub const CONFIGURE: usize = 4;
pub const CHECK: u32 = 0;
pub const TRANSMIT: u32 = 1;
pub const RECEIVE: u32 = 2;
pub const EMULATE: u32 = 3;
pub const CONFIGURE: u32 = 4;
}
mod subscribe_nr {
pub const TRANSMIT: usize = 1;
pub const RECEIVE: usize = 2;
pub const TRANSMIT: u32 = 1;
pub const RECEIVE: u32 = 2;
}
mod allow_nr {
pub const TRANSMIT: usize = 1;
pub const RECEIVE: usize = 2;
pub const TRANSMIT: u32 = 1;
pub const RECEIVE: u32 = 2;
}
#[allow(dead_code)]
#[derive(Clone, Copy)]
pub struct RecvOp {
pub result_code: usize,
pub recv_amount: usize,
pub result_code: u32,
pub recv_amount: u32,
}
pub struct NfcTag {}
pub trait Config:
platform::allow_rw::Config + platform::allow_ro::Config + platform::subscribe::Config
{
}
impl<T: platform::allow_rw::Config + platform::allow_ro::Config + platform::subscribe::Config>
Config for T
{
}
impl NfcTag {
pub struct NfcTag<S: Syscalls, C: Config = DefaultConfig>(S, C);
impl<S: Syscalls, C: Config> NfcTag<S, C> {
/// Check the existence of an NFC driver.
pub fn setup() -> bool {
syscalls::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).is_ok()
S::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).is_success()
}
pub fn enable_emulation() -> bool {
NfcTag::emulate(true)
NfcTag::<S, C>::emulate(true)
}
pub fn disable_emulation() -> bool {
NfcTag::emulate(false)
NfcTag::<S, C>::emulate(false)
}
fn emulate(enabled: bool) -> bool {
syscalls::command(DRIVER_NUMBER, command_nr::EMULATE, enabled as usize, 0).is_ok()
S::command(DRIVER_NUMBER, command_nr::EMULATE, enabled as u32, 0).is_success()
}
/// Configure the tag type command.
pub fn configure(tag_type: u8) -> bool {
syscalls::command(DRIVER_NUMBER, command_nr::CONFIGURE, tag_type as usize, 0).is_ok()
S::command(DRIVER_NUMBER, command_nr::CONFIGURE, tag_type as u32, 0).is_success()
}
/// 1. Share with the driver a buffer.
/// 2. Subscribe to having a successful receive callback.
/// 3. Issue the request for reception.
pub fn receive(buf: &mut [u8; 256]) -> TockResult<RecvOp> {
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?;
// set callback with 2 arguments, to receive ReturnCode and RX Amount
let recv_data = Cell::new(None);
let mut callback = |result, amount| {
recv_data.set(Some(RecvOp {
result_code: result,
recv_amount: amount,
}))
};
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::RECEIVE,
&mut callback,
)?;
syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0)?;
util::yieldk_for(|| recv_data.get().is_some());
mem::drop(subscription);
mem::drop(result);
Ok(recv_data.get().unwrap())
let recv: Cell<Option<(u32, u32)>> = Cell::new(None);
share::scope::<
(
AllowRw<_, DRIVER_NUMBER, { allow_nr::RECEIVE }>,
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>,
),
_,
_,
>(|handle| {
let (allow_rw, subscribe) = handle.split();
S::allow_rw::<C, DRIVER_NUMBER, { allow_nr::RECEIVE }>(allow_rw, buf)?;
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>(subscribe, &recv)?;
S::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0).to_result::<(), ErrorCode>()?;
util::Util::<S>::yieldk_for(|| recv.get().is_some());
let (result_code, recv_amount) = recv.get().unwrap();
let recv_op = RecvOp {
result_code,
recv_amount,
};
Ok(recv_op)
})
}
/// 1. Share with the driver a buffer containing the app's reply.
/// 2. Subscribe to having a successful transmission callback.
/// 3. Issue the request for transmitting.
pub fn transmit(buf: &mut [u8], amount: usize) -> TockResult<usize> {
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT, buf)?;
pub fn transmit(buf: &mut [u8], amount: u32) -> TockResult<u32> {
// set callback with 1 argument, to receive ReturnCode
let result_code = Cell::new(None);
let mut callback = |result| result_code.set(Some(result));
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::TRANSMIT,
&mut callback,
)?;
syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT, amount, 0)?;
util::yieldk_for(|| result_code.get().is_some());
mem::drop(subscription);
mem::drop(result);
Ok(result_code.get().unwrap())
let result: Cell<Option<(u32,)>> = Cell::new(None);
share::scope::<
(
AllowRo<_, DRIVER_NUMBER, { allow_nr::TRANSMIT }>,
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>,
),
_,
_,
>(|handle| {
let (allow_ro, subscribe) = handle.split();
S::allow_ro::<C, DRIVER_NUMBER, { allow_nr::TRANSMIT }>(allow_ro, buf)?;
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>(subscribe, &result)?;
S::command(DRIVER_NUMBER, command_nr::TRANSMIT, amount, 0)
.to_result::<(), ErrorCode>()?;
util::Util::<S>::yieldk_for(|| result.get().is_some());
Ok(result.get().unwrap().0)
})
}
}

View File

@@ -1,6 +1,6 @@
use core::fmt;
pub use libtock_core::result::*;
use libtock_platform::ErrorCode;
pub type TockResult<T> = Result<T, TockError>;
@@ -33,9 +33,7 @@ impl<T> FlexUnwrap<T> for TockResult<T> {
#[derive(Copy, Clone)]
pub enum TockError {
Subscribe(SubscribeError),
Command(CommandError),
Allow(AllowError),
Command(ErrorCode),
Format,
Other(OtherError),
}
@@ -44,64 +42,21 @@ pub enum TockError {
impl core::fmt::Debug for TockError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
TockError::Subscribe(SubscribeError {
driver_number,
subscribe_number,
return_code,
}) => f
.debug_struct("SubscribeError")
.field("driver", driver_number)
.field("subscribe", subscribe_number)
.field("return_code", return_code)
.finish(),
TockError::Command(CommandError {
driver_number,
command_number,
arg1,
arg2,
return_code,
}) => f
.debug_struct("CommandError")
.field("driver", driver_number)
.field("command", command_number)
.field("arg1", arg1)
.field("arg2", arg2)
.field("return_code", return_code)
.finish(),
TockError::Allow(AllowError {
driver_number,
allow_number,
return_code,
}) => f
.debug_struct("AllowError")
.field("driver", driver_number)
.field("allow", allow_number)
.field("return_code", return_code)
.finish(),
TockError::Command(error_code) => {
f.write_fmt(format_args!("CommandError: {:?}", error_code))
}
TockError::Format => f.write_str("TockError::Format"),
TockError::Other(e) => e.fmt(f),
TockError::Other(e) => f.write_fmt(format_args!("OtherError: {:?}", e)),
}
}
}
impl From<SubscribeError> for TockError {
fn from(subscribe_error: SubscribeError) -> Self {
TockError::Subscribe(subscribe_error)
}
}
impl From<CommandError> for TockError {
fn from(command_error: CommandError) -> Self {
impl From<ErrorCode> for TockError {
fn from(command_error: ErrorCode) -> Self {
TockError::Command(command_error)
}
}
impl From<AllowError> for TockError {
fn from(allow_error: AllowError) -> Self {
TockError::Allow(allow_error)
}
}
impl From<fmt::Error> for TockError {
fn from(fmt::Error: fmt::Error) -> Self {
TockError::Format

View File

@@ -1,45 +1,78 @@
use crate::util;
use core::cell::Cell;
use libtock_core::{callback, syscalls};
//! Userspace interface for easy access to the random number generator
const DRIVER_NUMBER: usize = 0x40001;
use crate::util::Util;
use core::cell::Cell;
use core::convert::TryInto;
use libtock_platform as platform;
use libtock_platform::{share, AllowRw, DefaultConfig, Subscribe, Syscalls};
use platform::ErrorCode;
/// Driver number for the random number generator
const DRIVER_NUMBER: u32 = 0x40001;
mod command_nr {
pub const REQUEST_RNG: usize = 1;
pub const REQUEST_RNG: u32 = 1;
}
mod subscribe_nr {
pub const BUFFER_FILLED: usize = 0;
pub const BUFFER_FILLED: u32 = 0;
}
mod allow_nr {
pub const SHARE_BUFFER: usize = 0;
pub const SHARE_BUFFER: u32 = 0;
}
pub fn fill_buffer(buf: &mut [u8]) -> bool {
let buf_len = buf.len();
/// System call configuration trait for `Rng`
pub trait Config: platform::allow_rw::Config + platform::subscribe::Config {}
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf);
if result.is_err() {
return false;
impl<T: platform::allow_rw::Config + platform::subscribe::Config> Config for T {}
pub struct Rng<S: Syscalls, C: Config = DefaultConfig>(S, C);
impl<S: Syscalls, C: Config> Rng<S, C> {
pub fn fill_buffer(buf: &mut [u8]) -> bool {
let buf_len = buf.len();
let is_filled = Cell::new(false);
share::scope::<
(
AllowRw<_, DRIVER_NUMBER, { allow_nr::SHARE_BUFFER }>,
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::BUFFER_FILLED }>,
),
_,
_,
>(|handle| {
let (allow_rw, subscribe) = handle.split();
let result = S::allow_rw::<C, DRIVER_NUMBER, { allow_nr::SHARE_BUFFER }>(allow_rw, buf);
if result.is_err() {
return false;
}
// Automatically sets `is_filled` to true as soon as the buffer is filled.
let subscription =
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::BUFFER_FILLED }>(
subscribe, &is_filled,
);
if subscription.is_err() {
return false;
}
// Requests the random number generator to fill the buffer.
let result_code: Result<(), ErrorCode> = S::command(
DRIVER_NUMBER,
command_nr::REQUEST_RNG,
buf_len.try_into().unwrap(),
0,
)
.to_result();
if result_code.is_err() {
return false;
}
// Yields until the buffer is filled.
Util::<S>::yieldk_for(|| is_filled.get());
true
})
}
let is_filled = Cell::new(false);
let mut is_filled_alarm = || is_filled.set(true);
let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::BUFFER_FILLED,
&mut is_filled_alarm,
);
if subscription.is_err() {
return false;
}
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::REQUEST_RNG, buf_len, 0);
if result_code.is_err() {
return false;
}
util::yieldk_for(|| is_filled.get());
true
}

View File

@@ -1,203 +1,269 @@
//! The alarm driver
//!
//! # Example
//! ```
//! // Wait for timeout
//! Alarm::sleep(Alarm::Milliseconds(2500));
//! ```
//!
//! Adapted from the [libtock-rs](https://github.com/tock/libtock-rs/blob/master/apis/alarm/src/lib.rs) alarm driver interface
use crate::result::{FlexUnwrap, OtherError, TockError, TockResult};
use crate::util;
use crate::util::Util;
use core::cell::Cell;
use core::isize;
use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Sub};
use libtock_core::callback::{CallbackSubscription, Consumer};
use libtock_core::result::{CommandError, EALREADY};
use libtock_core::syscalls;
use libtock_platform as platform;
use libtock_platform::{share, DefaultConfig, ErrorCode, Syscalls};
use platform::share::Handle;
use platform::subscribe::OneId;
use platform::{Subscribe, Upcall};
const DRIVER_NUMBER: usize = 0x00000;
pub struct Alarm<S: Syscalls, C: platform::subscribe::Config = DefaultConfig>(S, C);
mod command_nr {
pub const IS_DRIVER_AVAILABLE: usize = 0;
pub const GET_CLOCK_FREQUENCY: usize = 1;
pub const GET_CLOCK_VALUE: usize = 2;
pub const STOP_ALARM: usize = 3;
pub const SET_ALARM: usize = 4;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Hz(pub u32);
pub trait Convert {
/// Converts a time unit by rounding up.
fn to_ticks(self, freq: Hz) -> Ticks;
}
mod subscribe_nr {
pub const SUBSCRIBE_CALLBACK: usize = 0;
}
#[derive(Copy, Clone, Debug)]
pub struct Ticks(pub u32);
pub fn sleep(duration: Duration<isize>) -> TockResult<()> {
let expired = Cell::new(false);
let mut with_callback = with_callback(|_, _| expired.set(true));
let mut timer = with_callback.init().flex_unwrap();
let timer_alarm = timer.set_alarm(duration).flex_unwrap();
util::yieldk_for(|| expired.get());
match timer.stop_alarm(timer_alarm) {
Ok(())
| Err(TockError::Command(CommandError {
return_code: EALREADY,
..
})) => Ok(()),
Err(e) => Err(e),
impl Convert for Ticks {
fn to_ticks(self, _freq: Hz) -> Ticks {
self
}
}
pub fn get_ticks() -> TockResult<usize> {
Ok(syscalls::command(
DRIVER_NUMBER,
command_nr::GET_CLOCK_VALUE,
0,
0,
)?)
pub fn get_ticks<S: Syscalls>() -> TockResult<u32> {
Ok(S::command(DRIVER_NUM, command::TIME, 0, 0).to_result::<u32, ErrorCode>()?)
}
pub fn get_clock_frequency() -> TockResult<usize> {
Ok(syscalls::command(
DRIVER_NUMBER,
command_nr::GET_CLOCK_FREQUENCY,
0,
0,
)?)
pub fn get_clock_frequency<S: Syscalls>() -> TockResult<u32> {
Ok(S::command(DRIVER_NUM, command::FREQUENCY, 0, 0).to_result::<u32, ErrorCode>()?)
}
pub fn with_callback<CB>(callback: CB) -> WithCallback<'static, CB> {
WithCallback {
callback,
clock_frequency: ClockFrequency { hz: 0 },
phantom: PhantomData,
#[derive(Copy, Clone)]
pub struct Milliseconds(pub u32);
impl Convert for Milliseconds {
fn to_ticks(self, freq: Hz) -> Ticks {
// Saturating multiplication will top out at about 1 hour at 1MHz.
// It's large enough for an alarm, and much simpler than failing
// or losing precision for short sleeps.
/// u32::div_ceil is still unstable.
fn div_ceil(a: u32, other: u32) -> u32 {
let d = a / other;
let m = a % other;
if m == 0 {
d
} else {
d + 1
}
}
Ticks(div_ceil(self.0.saturating_mul(freq.0), 1000))
}
}
pub struct WithCallback<'a, CB> {
impl<S: Syscalls, C: platform::subscribe::Config> Alarm<S, C> {
/// Run a check against the console capsule to ensure it is present.
///
/// Returns number of concurrent notifications supported,
/// 0 if unbounded.
#[inline(always)]
pub fn driver_check() -> Result<u32, ErrorCode> {
S::command(DRIVER_NUM, command::DRIVER_CHECK, 0, 0).to_result()
}
pub fn get_frequency() -> Result<Hz, ErrorCode> {
S::command(DRIVER_NUM, command::FREQUENCY, 0, 0)
.to_result()
.map(Hz)
}
pub fn sleep_for<T: Convert>(time: T) -> Result<(), ErrorCode> {
let freq = Self::get_frequency()?;
let ticks = time.to_ticks(freq);
let called: Cell<Option<(u32, u32)>> = Cell::new(None);
share::scope(|subscribe| {
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::CALLBACK }>(subscribe, &called)?;
S::command(DRIVER_NUM, command::SET_RELATIVE, ticks.0, 0)
.to_result()
.map(|_when: u32| ())?;
loop {
S::yield_wait();
if let Some((_when, _ref)) = called.get() {
return Ok(());
}
}
})
}
}
pub struct Timer<S: Syscalls, C: platform::subscribe::Config = DefaultConfig> {
clock_frequency: Hz,
s: PhantomData<S>,
c: PhantomData<C>,
}
pub struct WithCallback<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)> {
callback: CB,
clock_frequency: ClockFrequency,
phantom: PhantomData<&'a mut ()>,
clock_frequency: Hz,
s: PhantomData<S>,
c: PhantomData<C>,
}
struct TimerEventConsumer;
impl<CB: FnMut(ClockValue, Alarm)> Consumer<WithCallback<'_, CB>> for TimerEventConsumer {
fn consume(data: &mut WithCallback<CB>, clock_value: usize, alarm_id: usize, _: usize) {
(data.callback)(
ClockValue {
num_ticks: clock_value as isize,
clock_frequency: data.clock_frequency,
},
Alarm { alarm_id },
);
pub fn with_callback<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)>(
callback: CB,
) -> TimerUpcallConsumer<S, C, CB> {
TimerUpcallConsumer {
data: WithCallback {
callback,
clock_frequency: Hz(0),
s: PhantomData,
c: PhantomData,
},
}
}
impl<'a, CB: FnMut(ClockValue, Alarm)> WithCallback<'a, CB> {
pub fn init(&'a mut self) -> TockResult<Timer<'a>> {
let num_notifications =
syscalls::command(DRIVER_NUMBER, command_nr::IS_DRIVER_AVAILABLE, 0, 0)?;
pub struct TimerUpcallConsumer<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)> {
data: WithCallback<S, C, CB>,
}
impl<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)>
Upcall<OneId<DRIVER_NUM, { subscribe::CALLBACK }>> for TimerUpcallConsumer<S, C, CB>
{
fn upcall(&self, expired_tick_val: u32, _ref_tick: u32, _: u32) {
(self.data.callback)(ClockValue::new(
expired_tick_val as isize,
self.data.clock_frequency,
))
}
}
impl<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)>
TimerUpcallConsumer<S, C, CB>
{
/// Initializes the data of the containing [WithCallback], i.e. number of notifications, clock frequency.
pub fn init(&mut self) -> TockResult<Timer<S, C>> {
// Check if the alarm driver works.
S::command(DRIVER_NUM, command::DRIVER_CHECK, 0, 0).to_result::<(), ErrorCode>()?;
// Alarm driver only returns success as only a single concurrent timer is supported.
let clock_frequency =
syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0)?;
S::command(DRIVER_NUM, command::FREQUENCY, 0, 0).to_result::<u32, ErrorCode>()?;
if clock_frequency == 0 {
if clock_frequency < 1_000 {
// The alarm's frequency must be at least 1 kHz.
return Err(OtherError::TimerDriverErroneousClockFrequency.into());
}
let clock_frequency = ClockFrequency {
hz: clock_frequency,
};
let subscription = syscalls::subscribe::<TimerEventConsumer, _>(
DRIVER_NUMBER,
subscribe_nr::SUBSCRIBE_CALLBACK,
self,
)?;
let clock_frequency = Hz(clock_frequency);
Ok(Timer {
num_notifications,
clock_frequency,
subscription,
c: PhantomData,
s: PhantomData,
})
}
/// Enables the timer by subscribing for the countdown.
/// This needs to be a separate method as it needs to be called in the same `share::scope`
pub fn enable<'share, 'a: 'share>(
&'a self,
handle: Handle<Subscribe<'share, S, DRIVER_NUM, { subscribe::CALLBACK }>>,
) -> Result<(), ErrorCode> {
// Register the upcall for the timer.
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::CALLBACK }>(handle, self)
}
}
pub struct Timer<'a> {
num_notifications: usize,
clock_frequency: ClockFrequency,
#[allow(dead_code)] // Used in drop
subscription: CallbackSubscription<'a>,
impl<S: Syscalls, C: platform::subscribe::Config> Default for Timer<S, C> {
fn default() -> Self {
Self::new()
}
}
impl<'a> Timer<'a> {
pub fn num_notifications(&self) -> usize {
self.num_notifications
impl<S: Syscalls, C: platform::subscribe::Config> Timer<S, C> {
pub fn new() -> Self {
let clock_frequency = Alarm::<S, C>::get_frequency().unwrap();
Self {
clock_frequency,
s: PhantomData,
c: PhantomData,
}
}
pub fn clock_frequency(&self) -> ClockFrequency {
pub fn sleep(duration: Duration<isize>) -> TockResult<()> {
let expired = Cell::new(false);
let mut with_callback = with_callback::<S, C, _>(|_| expired.set(true));
let mut timer = with_callback.init().flex_unwrap();
timer.set_alarm(duration).flex_unwrap();
Util::<S>::yieldk_for(|| expired.get());
match timer.stop_alarm() {
Ok(_) | Err(TockError::Command(ErrorCode::Already)) => Ok(()),
Err(e) => Err(e),
}
}
/// Returns the clock frequency of the timer.
pub fn clock_frequency(&self) -> Hz {
self.clock_frequency
}
pub fn get_current_clock(&self) -> TockResult<ClockValue> {
/// Returns the current counter tick value.
pub fn get_current_counter_ticks(&self) -> TockResult<ClockValue> {
let ticks = S::command(DRIVER_NUM, command::TIME, 0, 0).to_result::<u32, ErrorCode>()?;
Ok(ClockValue {
num_ticks: syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0)?
as isize,
clock_frequency: self.clock_frequency,
num_ticks: ticks as isize,
clock_frequency: self.clock_frequency(),
})
}
pub fn stop_alarm(&mut self, alarm: Alarm) -> TockResult<()> {
syscalls::command(DRIVER_NUMBER, command_nr::STOP_ALARM, alarm.alarm_id, 0)?;
/// Stops the currently active alarm.
pub fn stop_alarm(&mut self) -> TockResult<()> {
S::unsubscribe(DRIVER_NUM, subscribe::CALLBACK);
S::command(DRIVER_NUM, command::STOP, 0, 0).to_result::<(), ErrorCode>()?;
Ok(())
}
pub fn set_alarm(&mut self, duration: Duration<isize>) -> TockResult<Alarm> {
let now = self.get_current_clock()?;
let freq = self.clock_frequency.hz();
let duration_ms = duration.ms() as usize;
let ticks = match duration_ms.checked_mul(freq) {
Some(x) => x / 1000,
None => {
// Divide the largest of the two operands by 1000, to improve precision of the
// result.
if duration_ms > freq {
match (duration_ms / 1000).checked_mul(freq) {
Some(y) => y,
None => return Err(OtherError::TimerDriverDurationOutOfRange.into()),
}
} else {
match (freq / 1000).checked_mul(duration_ms) {
Some(y) => y,
None => return Err(OtherError::TimerDriverDurationOutOfRange.into()),
}
}
}
};
let alarm_instant = now.num_ticks() as usize + ticks;
pub fn set_alarm(&mut self, duration: Duration<isize>) -> TockResult<()> {
let freq = self.clock_frequency;
let duration_ms = duration.ms() as u32;
let ticks = Milliseconds(duration_ms).to_ticks(freq);
let alarm_id = syscalls::command(DRIVER_NUMBER, command_nr::SET_ALARM, alarm_instant, 0)?;
S::command(DRIVER_NUM, command::SET_RELATIVE, ticks.0, 0)
.to_result::<u32, ErrorCode>()
.map(|_when| ())?;
Ok(Alarm { alarm_id })
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ClockFrequency {
hz: usize,
}
impl ClockFrequency {
pub fn hz(&self) -> usize {
self.hz
Ok(())
}
}
#[derive(Copy, Clone, Debug)]
pub struct ClockValue {
num_ticks: isize,
clock_frequency: ClockFrequency,
clock_frequency: Hz,
}
impl ClockValue {
pub const fn new(num_ticks: isize, clock_hz: usize) -> ClockValue {
pub const fn new(num_ticks: isize, clock_hz: Hz) -> ClockValue {
ClockValue {
num_ticks,
clock_frequency: ClockFrequency { hz: clock_hz },
clock_frequency: clock_hz,
}
}
@@ -213,17 +279,17 @@ impl ClockValue {
}
pub fn ms(&self) -> isize {
ClockValue::scale_int(self.num_ticks, 1000, self.clock_frequency.hz() as isize)
ClockValue::scale_int(self.num_ticks, 1000, self.clock_frequency.0 as isize)
}
pub fn ms_f64(&self) -> f64 {
1000.0 * (self.num_ticks as f64) / (self.clock_frequency.hz() as f64)
1000.0 * (self.num_ticks as f64) / (self.clock_frequency.0 as f64)
}
pub fn wrapping_add(self, duration: Duration<isize>) -> ClockValue {
// This is a precision preserving formula for scaling an isize.
let duration_ticks =
ClockValue::scale_int(duration.ms, self.clock_frequency.hz() as isize, 1000);
ClockValue::scale_int(duration.ms, self.clock_frequency.0 as isize, 1000);
ClockValue {
num_ticks: self.num_ticks.wrapping_add(duration_ticks),
clock_frequency: self.clock_frequency,
@@ -243,16 +309,6 @@ impl ClockValue {
}
}
pub struct Alarm {
alarm_id: usize,
}
impl Alarm {
pub fn alarm_id(&self) -> usize {
self.alarm_id
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Duration<T> {
ms: T,
@@ -350,3 +406,26 @@ where
self.ms += duration.ms();
}
}
// -----------------------------------------------------------------------------
// Driver number and command IDs
// -----------------------------------------------------------------------------
pub const DRIVER_NUM: u32 = 0;
// Command IDs
#[allow(unused)]
mod command {
pub const DRIVER_CHECK: u32 = 0;
pub const FREQUENCY: u32 = 1;
pub const TIME: u32 = 2;
pub const STOP: u32 = 3;
pub const SET_RELATIVE: u32 = 5;
pub const SET_ABSOLUTE: u32 = 6;
}
#[allow(unused)]
pub mod subscribe {
pub const CALLBACK: u32 = 0;
}

View File

@@ -12,322 +12,510 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#[cfg(feature = "debug_ctap")]
use crate::console::Console;
use crate::result::{OutOfRangeError, TockError, TockResult};
use crate::timer::Duration;
use crate::util::Util;
use crate::{timer, util};
use core::cell::Cell;
#[cfg(feature = "debug_ctap")]
use core::fmt::Write;
use libtock_core::result::{CommandError, EALREADY, EBUSY, SUCCESS};
use libtock_core::{callback, syscalls};
#[cfg(feature = "debug_ctap")]
use libtock_console::Console;
use libtock_platform as platform;
use libtock_platform::{share, DefaultConfig, ErrorCode, Syscalls};
use platform::share::Handle;
use platform::subscribe::OneId;
use platform::{AllowRo, AllowRw, Subscribe, Upcall};
const DRIVER_NUMBER: usize = 0x20009;
const DRIVER_NUMBER: u32 = 0x20009;
/// Ids for commands
mod command_nr {
pub const CHECK: usize = 0;
pub const CONNECT: usize = 1;
pub const _TRANSMIT: usize = 2;
pub const RECEIVE: usize = 3;
pub const TRANSMIT_OR_RECEIVE: usize = 4;
pub const CANCEL: usize = 5;
pub const CHECK: u32 = 0;
pub const CONNECT: u32 = 1;
pub const TRANSMIT: u32 = 2;
pub const RECEIVE: u32 = 3;
pub const TRANSMIT_OR_RECEIVE: u32 = 4;
pub const CANCEL: u32 = 5;
}
/// Ids for subscribe numbers
mod subscribe_nr {
pub const _TRANSMIT: usize = 1;
pub const RECEIVE: usize = 2;
pub const TRANSMIT_OR_RECEIVE: usize = 3;
pub mod callback_status {
pub const TRANSMITTED: usize = 1;
pub const RECEIVED: usize = 2;
}
pub const TRANSMIT: u32 = 0;
pub const RECEIVE: u32 = 1;
}
mod allow_nr {
pub const _TRANSMIT: usize = 1;
pub const RECEIVE: usize = 2;
pub const TRANSMIT_OR_RECEIVE: usize = 3;
mod ro_allow_nr {
pub const TRANSMIT: u32 = 0;
}
pub fn setup() -> bool {
let result = syscalls::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0);
if result.is_err() {
return false;
}
let result = syscalls::command(DRIVER_NUMBER, command_nr::CONNECT, 0, 0);
if result.is_err() {
return false;
}
true
mod rw_allow_nr {
pub const RECEIVE: u32 = 0;
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum SendOrRecvStatus {
Timeout,
Sent,
Received(usize),
Received(u32),
}
/// Waits to receive a packet.
///
/// Returns None if the transaction timed out, else its status.
#[allow(clippy::let_and_return)]
pub fn recv_with_timeout(
buf: &mut [u8; 64],
timeout_delay: Duration<isize>,
) -> TockResult<SendOrRecvStatus> {
#[cfg(feature = "verbose_usb")]
writeln!(
Console::new(),
"Receiving packet with timeout of {}ms",
timeout_delay.ms(),
)
.unwrap();
pub trait Config:
platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config
{
}
let result = recv_with_timeout_detail(buf, timeout_delay);
impl<T: platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config>
Config for T
{
}
#[cfg(feature = "verbose_usb")]
if let Ok(SendOrRecvStatus::Received(endpoint)) = result {
pub struct UsbCtapHidListener<F: Fn(u32, u32)>(pub F);
impl<const SUB_NUM: u32, F: Fn(u32, u32)> Upcall<OneId<DRIVER_NUMBER, SUB_NUM>>
for UsbCtapHidListener<F>
{
fn upcall(&self, direction: u32, endpoint: u32, _: u32) {
self.0(direction, endpoint)
}
}
pub struct UsbCtapHid<S: Syscalls, C: Config = DefaultConfig>(S, C);
impl<S: Syscalls, C: Config> UsbCtapHid<S, C> {
/// Register an listener to call with the arguments.
///
/// Only one listener can be registered at a time.
fn register_listener<'share, const SUB_NUM: u32, F: Fn(u32, u32)>(
listener: &'share UsbCtapHidListener<F>,
subscribe: Handle<Subscribe<'share, S, DRIVER_NUMBER, SUB_NUM>>,
) -> Result<(), ErrorCode> {
S::subscribe::<_, _, C, DRIVER_NUMBER, SUB_NUM>(subscribe, listener)
}
/// Unregisters the listener.
///
/// Can be called even if there was no previously registered listener.
fn unregister_listener(subscribe_num: u32) {
S::unsubscribe(DRIVER_NUMBER, subscribe_num);
}
/// Checks whether the driver is available and tries to setup the connection.
pub fn setup() -> bool {
let result =
S::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).to_result::<(), ErrorCode>();
if result.is_err() {
return false;
}
let result =
S::command(DRIVER_NUMBER, command_nr::CONNECT, 0, 0).to_result::<(), ErrorCode>();
if result.is_err() {
return false;
}
true
}
/// Waits to receive a packet.
///
/// Returns None if the transaction timed out, else its status.
#[allow(clippy::let_and_return)]
pub fn recv_with_timeout(
buf: &mut [u8; 64],
timeout_delay: Duration<isize>,
) -> TockResult<SendOrRecvStatus> {
#[cfg(feature = "verbose_usb")]
writeln!(
Console::new(),
"Received packet = {:02x?} on endpoint {}",
buf as &[u8],
Console::<S>::writer(),
"Receiving packet with timeout of {} ms",
timeout_delay.ms(),
)
.unwrap();
let result = Self::recv_with_timeout_detail(buf, timeout_delay);
#[cfg(feature = "verbose_usb")]
if let Ok(SendOrRecvStatus::Received(endpoint)) = result {
writeln!(
Console::<S>::writer(),
"Received packet = {:02x?} on endpoint {}",
buf as &[u8],
endpoint as u8,
)
.unwrap();
}
result
}
/// Sends a packet to a given endpoint.
///
/// Returns the transmission status.
pub fn send(
buf: &[u8; 64],
timeout_delay: Duration<isize>,
endpoint: u32,
) -> TockResult<SendOrRecvStatus> {
#[cfg(feature = "verbose_usb")]
writeln!(
Console::<S>::writer(),
"Sending packet on endpoint {} with timeout of {} ms = {:02x?}",
endpoint,
)
.unwrap();
}
result
}
/// Either sends or receives a packet within a given time.
///
/// Because USB transactions are initiated by the host, we don't decide whether an IN transaction
/// (send for us), an OUT transaction (receive for us), or no transaction at all will happen next.
///
/// - If an IN transaction happens first, the initial content of buf is sent to the host and the
/// Sent status is returned.
/// - If an OUT transaction happens first, the content of buf is replaced by the packet received
/// from the host and Received status is returned. In that case, the original content of buf is not
/// sent to the host, and it's up to the caller to retry sending or to handle the packet received
/// from the host.
/// If the timeout elapses, return None.
#[allow(clippy::let_and_return)]
pub fn send_or_recv_with_timeout(
buf: &mut [u8; 64],
timeout_delay: Duration<isize>,
endpoint: usize,
) -> TockResult<SendOrRecvStatus> {
#[cfg(feature = "verbose_usb")]
writeln!(
Console::new(),
"Sending packet with timeout of {}ms = {:02x?}",
timeout_delay.ms(),
buf as &[u8]
)
.unwrap();
let result = send_or_recv_with_timeout_detail(buf, timeout_delay, endpoint);
#[cfg(feature = "verbose_usb")]
if let Ok(SendOrRecvStatus::Received(received_endpoint)) = result {
writeln!(
Console::new(),
"Received packet = {:02x?} on endpoint {}",
timeout_delay.ms(),
buf as &[u8],
received_endpoint,
)
.unwrap();
Self::send_detail(buf, timeout_delay, endpoint)
}
result
}
fn recv_with_timeout_detail(
buf: &mut [u8; 64],
timeout_delay: Duration<isize>,
) -> TockResult<SendOrRecvStatus> {
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?;
let status = Cell::new(None);
let mut alarm = |direction, endpoint| {
status.set(Some(match direction {
subscribe_nr::callback_status::RECEIVED => Ok(SendOrRecvStatus::Received(endpoint)),
// Unknown direction or "transmitted" sent by the kernel.
_ => Err(OutOfRangeError.into()),
}));
};
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::RECEIVE,
&mut alarm,
)?;
// Setup a time-out callback.
let mut timeout_callback = timer::with_callback(|_, _| {
status.set(Some(Ok(SendOrRecvStatus::Timeout)));
});
let mut timeout = timeout_callback.init()?;
let timeout_alarm = timeout.set_alarm(timeout_delay)?;
// Trigger USB reception.
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0)?;
util::yieldk_for(|| status.get().is_some());
let status = status.get().unwrap();
// Cleanup alarm callback.
match timeout.stop_alarm(timeout_alarm) {
Ok(()) => (),
Err(TockError::Command(CommandError {
return_code: EALREADY,
..
})) => {
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "debug_ctap")]
writeln!(
Console::new(),
"The receive timeout already expired, but the callback wasn't executed."
)
.unwrap();
}
}
Err(_e) => {
#[cfg(feature = "debug_ctap")]
panic!("Unexpected error when stopping alarm: {:?}", _e);
#[cfg(not(feature = "debug_ctap"))]
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
}
}
// Cancel USB transaction if necessary.
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
/// Either sends or receives a packet within a given time.
///
/// Because USB transactions are initiated by the host, we don't decide whether an IN transaction
/// (send for us), an OUT transaction (receive for us), or no transaction at all will happen next.
///
/// - If an IN transaction happens first, the initial content of buf is sent to the host and the
/// Sent status is returned.
/// - If an OUT transaction happens first, the content of buf is replaced by the packet received
/// from the host and Received status is returned. In that case, the original content of buf is not
/// sent to the host, and it's up to the caller to retry sending or to handle the packet received
/// from the host.
/// If the timeout elapses, return None.
#[allow(clippy::let_and_return)]
pub fn send_or_recv_with_timeout(
buf: &mut [u8; 64],
timeout_delay: Duration<isize>,
endpoint: u32,
) -> TockResult<SendOrRecvStatus> {
#[cfg(feature = "verbose_usb")]
writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap();
let result_code =
unsafe { syscalls::raw::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) };
match result_code {
// - SUCCESS means that we successfully cancelled the transaction.
// - EALREADY means that the transaction was already completed.
SUCCESS | EALREADY => (),
// - EBUSY means that the transaction is in progress.
EBUSY => {
// The app should wait for it, but it may never happen if the remote app crashes.
// We just return to avoid a deadlock.
#[cfg(feature = "debug_ctap")]
writeln!(Console::new(), "Couldn't cancel the USB receive").unwrap();
}
_ => panic!(
"Unexpected error when cancelling USB receive: {:?}",
result_code
),
}
}
writeln!(
Console::<S>::writer(),
"Sending packet with timeout of {} ms = {:02x?}",
timeout_delay.ms(),
buf as &[u8]
)
.unwrap();
core::mem::drop(result);
core::mem::drop(subscription);
core::mem::drop(result_code);
status
}
let result = Self::send_or_recv_with_timeout_detail(buf, timeout_delay, endpoint);
fn send_or_recv_with_timeout_detail(
buf: &mut [u8; 64],
timeout_delay: Duration<isize>,
endpoint: usize,
) -> TockResult<SendOrRecvStatus> {
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf)?;
let status = Cell::new(None);
let mut alarm = |direction, endpoint| {
status.set(Some(match direction {
subscribe_nr::callback_status::TRANSMITTED => Ok(SendOrRecvStatus::Sent),
subscribe_nr::callback_status::RECEIVED => Ok(SendOrRecvStatus::Received(endpoint)),
// Unknown direction sent by the kernel.
_ => Err(OutOfRangeError.into()),
}));
};
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::TRANSMIT_OR_RECEIVE,
&mut alarm,
)?;
// Setup a time-out callback.
let mut timeout_callback = timer::with_callback(|_, _| {
status.set(Some(Ok(SendOrRecvStatus::Timeout)));
});
let mut timeout = timeout_callback.init()?;
let timeout_alarm = timeout.set_alarm(timeout_delay)?;
// Trigger USB transmission.
let result_code = syscalls::command(
DRIVER_NUMBER,
command_nr::TRANSMIT_OR_RECEIVE,
endpoint as usize,
0,
)?;
util::yieldk_for(|| status.get().is_some());
let status = status.get().unwrap();
// Cleanup alarm callback.
match timeout.stop_alarm(timeout_alarm) {
Ok(()) => (),
Err(TockError::Command(CommandError {
return_code: EALREADY,
..
})) => {
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "debug_ctap")]
writeln!(
Console::new(),
"The send/receive timeout already expired, but the callback wasn't executed."
)
.unwrap();
}
}
Err(_e) => {
#[cfg(feature = "debug_ctap")]
panic!("Unexpected error when stopping alarm: {:?}", _e);
#[cfg(not(feature = "debug_ctap"))]
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
}
}
// Cancel USB transaction if necessary.
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "verbose_usb")]
writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap();
let result_code = unsafe {
syscalls::raw::command(DRIVER_NUMBER, command_nr::CANCEL, endpoint as usize, 0)
};
match result_code {
// - SUCCESS means that we successfully cancelled the transaction.
// - EALREADY means that the transaction was already completed.
SUCCESS | EALREADY => (),
// - EBUSY means that the transaction is in progress.
EBUSY => {
// The app should wait for it, but it may never happen if the remote app crashes.
// We just return to avoid a deadlock.
#[cfg(feature = "debug_ctap")]
writeln!(Console::new(), "Couldn't cancel the transaction").unwrap();
}
_ => panic!(
"Unexpected error when cancelling USB transaction: {:?}",
result_code
),
if let Ok(SendOrRecvStatus::Received(received_endpoint)) = result {
writeln!(
Console::<S>::writer(),
"Received packet = {:02x?} on endpoint {}",
buf as &[u8],
received_endpoint as u8,
)
.unwrap();
}
#[cfg(feature = "debug_ctap")]
writeln!(Console::new(), "Cancelled USB transaction!").unwrap();
result
}
core::mem::drop(result);
core::mem::drop(subscription);
core::mem::drop(result_code);
status
fn recv_with_timeout_detail(
buf: &mut [u8; 64],
timeout_delay: Duration<isize>,
) -> TockResult<SendOrRecvStatus> {
let status: Cell<Option<SendOrRecvStatus>> = Cell::new(None);
let alarm = UsbCtapHidListener(|direction, endpoint| match direction {
subscribe_nr::RECEIVE => status.set(Some(SendOrRecvStatus::Received(endpoint))),
// Unknown direction or "transmitted" sent by the kernel
_ => status.set(None),
});
let mut timeout_callback =
timer::with_callback::<S, C, _>(|_| status.set(Some(SendOrRecvStatus::Timeout)));
let status = share::scope::<
(
AllowRw<_, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>,
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>,
Subscribe<S, { timer::DRIVER_NUM }, { timer::subscribe::CALLBACK }>,
),
_,
_,
>(|handle| {
let (allow, subscribe_recv, subscribe_timer) = handle.split();
S::allow_rw::<C, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>(allow, buf)?;
Self::register_listener::<{ subscribe_nr::RECEIVE }, _>(&alarm, subscribe_recv)?;
let mut timeout = timeout_callback.init()?;
timeout_callback.enable(subscribe_timer)?;
timeout
.set_alarm(timeout_delay)
.map_err(|_| ErrorCode::Fail)?;
S::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0).to_result::<(), ErrorCode>()?;
Util::<S>::yieldk_for(|| status.get().is_some());
Self::unregister_listener(subscribe_nr::RECEIVE);
let status = match status.get() {
Some(status) => Ok::<SendOrRecvStatus, TockError>(status),
None => Err(OutOfRangeError.into()),
}?;
// Cleanup alarm callback.
match timeout.stop_alarm() {
Ok(()) => (),
Err(TockError::Command(ErrorCode::Already)) => {
if matches!(status, SendOrRecvStatus::Timeout) {
#[cfg(feature = "debug_ctap")]
write!(Console::<S>::writer(), ".").unwrap();
}
}
Err(_e) => {
#[cfg(feature = "debug_ctap")]
panic!("Unexpected error when stopping alarm: {:?}", _e);
#[cfg(not(feature = "debug_ctap"))]
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
}
}
Ok::<SendOrRecvStatus, TockError>(status)
});
// Cancel USB transaction if necessary.
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "verbose_usb")]
writeln!(
Console::<S>::writer(),
"Cancelling USB receive due to timeout"
)
.unwrap();
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.
Ok(_) | Err(ErrorCode::Already) => (),
// - EBUSY means that the transaction is in progress.
Err(ErrorCode::Busy) => {
// The app should wait for it, but it may never happen if the remote app crashes.
// We just return to avoid a deadlock.
#[cfg(feature = "debug_ctap")]
writeln!(Console::<S>::writer(), "Couldn't cancel the USB receive").unwrap();
}
Err(e) => panic!("Unexpected error when cancelling USB receive: {:?}", e),
}
}
status
}
fn send_detail(
buf: &[u8; 64],
timeout_delay: Duration<isize>,
endpoint: u32,
) -> TockResult<SendOrRecvStatus> {
let status: Cell<Option<SendOrRecvStatus>> = Cell::new(None);
let alarm = UsbCtapHidListener(|direction, _| {
let option = match direction {
subscribe_nr::TRANSMIT => Some(SendOrRecvStatus::Sent),
_ => None,
};
status.set(option);
});
let mut timeout_callback =
timer::with_callback::<S, C, _>(|_| status.set(Some(SendOrRecvStatus::Timeout)));
let status = share::scope::<
(
AllowRo<_, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>,
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>,
Subscribe<S, { timer::DRIVER_NUM }, { timer::subscribe::CALLBACK }>,
),
_,
_,
>(|handle| {
let (allow, subscribe_send, subscribe_timer) = handle.split();
S::allow_ro::<C, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>(allow, buf)?;
Self::register_listener::<{ subscribe_nr::TRANSMIT }, _>(&alarm, subscribe_send)?;
let mut timeout = timeout_callback.init()?;
timeout_callback.enable(subscribe_timer)?;
timeout
.set_alarm(timeout_delay)
.map_err(|_| ErrorCode::Fail)?;
S::command(DRIVER_NUMBER, command_nr::TRANSMIT, endpoint as u32, 0)
.to_result::<(), ErrorCode>()?;
util::Util::<S>::yieldk_for(|| status.get().is_some());
Self::unregister_listener(subscribe_nr::TRANSMIT);
let status = match status.get() {
Some(status) => Ok::<SendOrRecvStatus, TockError>(status),
None => Err(OutOfRangeError.into()),
}?;
// Cleanup alarm callback.
match timeout.stop_alarm() {
Ok(()) => (),
Err(TockError::Command(ErrorCode::Already)) => {
if matches!(status, SendOrRecvStatus::Timeout) {
#[cfg(feature = "debug_ctap")]
writeln!(
Console::<S>::writer(),
"The send timeout already expired, but the callback wasn't executed."
)
.unwrap();
}
}
Err(_e) => {
#[cfg(feature = "debug_ctap")]
panic!("Unexpected error when stopping alarm: {:?}", _e);
#[cfg(not(feature = "debug_ctap"))]
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
}
}
Ok::<SendOrRecvStatus, TockError>(status)
});
// Cancel USB transaction if necessary.
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "verbose_usb")]
writeln!(
Console::<S>::writer(),
"Cancelling USB transmit due to timeout"
)
.unwrap();
let result = S::command(DRIVER_NUMBER, command_nr::CANCEL, endpoint as u32, 0)
.to_result::<(), ErrorCode>();
match result {
// - SUCCESS means that we successfully cancelled the transaction.
// - EALREADY means that the transaction was already completed.
Ok(_) | Err(ErrorCode::Already) => (),
// - EBUSY means that the transaction is in progress.
Err(ErrorCode::Busy) => {
// The app should wait for it, but it may never happen if the remote app crashes.
// We just return to avoid a deadlock.
#[cfg(feature = "debug_ctap")]
writeln!(Console::<S>::writer(), "Couldn't cancel the USB receive").unwrap();
}
Err(e) => panic!("Unexpected error when cancelling USB receive: {:?}", e),
}
}
status
}
fn send_or_recv_with_timeout_detail(
buf: &mut [u8; 64],
timeout_delay: Duration<isize>,
endpoint: u32,
) -> TockResult<SendOrRecvStatus> {
let status: Cell<Option<SendOrRecvStatus>> = Cell::new(None);
let alarm = UsbCtapHidListener(|direction, endpoint| {
let option = match direction {
subscribe_nr::TRANSMIT => Some(SendOrRecvStatus::Sent),
subscribe_nr::RECEIVE => Some(SendOrRecvStatus::Received(endpoint)),
_ => None,
};
status.set(option);
});
let mut recv_buf = [0; 64];
// init the time-out callback but don't enable it yet
let mut timeout_callback = timer::with_callback::<S, C, _>(|_| {
status.set(Some(SendOrRecvStatus::Timeout));
});
let status = share::scope::<
(
AllowRo<_, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>,
AllowRw<_, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>,
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>,
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>,
Subscribe<_, { timer::DRIVER_NUM }, { timer::subscribe::CALLBACK }>,
),
_,
_,
>(|handle| {
let (allow_ro, allow_rw, sub_send, sub_recv, sub_timer) = handle.split();
S::allow_ro::<C, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>(allow_ro, buf)?;
S::allow_rw::<C, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>(allow_rw, &mut recv_buf)?;
Self::register_listener::<{ subscribe_nr::TRANSMIT }, _>(&alarm, sub_send)?;
Self::register_listener::<{ subscribe_nr::RECEIVE }, _>(&alarm, sub_recv)?;
let mut timeout = timeout_callback.init()?;
timeout_callback.enable(sub_timer)?;
timeout.set_alarm(timeout_delay)?;
// Trigger USB transmission.
S::command(
DRIVER_NUMBER,
command_nr::TRANSMIT_OR_RECEIVE,
endpoint as u32,
0,
)
.to_result::<(), ErrorCode>()?;
util::Util::<S>::yieldk_for(|| status.get().is_some());
Self::unregister_listener(subscribe_nr::TRANSMIT);
Self::unregister_listener(subscribe_nr::RECEIVE);
let status = match status.get() {
Some(status) => Ok::<SendOrRecvStatus, TockError>(status),
None => Err(OutOfRangeError.into()),
}?;
// Cleanup alarm callback.
match timeout.stop_alarm() {
Ok(_) => (),
Err(TockError::Command(ErrorCode::Already)) => {
if matches!(status, SendOrRecvStatus::Timeout) {
#[cfg(feature = "debug_ctap")]
writeln!(
Console::<S>::writer(),
"The send/receive timeout already expired, but the callback wasn't executed."
)
.unwrap();
}
}
Err(_e) => {
#[cfg(feature = "debug_ctap")]
panic!("Unexpected error when stopping alarm: {:?}", _e);
#[cfg(not(feature = "debug_ctap"))]
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
}
}
Ok::<SendOrRecvStatus, TockError>(status)
});
// Cancel USB transaction if necessary.
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "verbose_usb")]
writeln!(
Console::<S>::writer(),
"Cancelling USB transaction due to timeout"
)
.unwrap();
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.
Ok(_) | Err(ErrorCode::Already) => (),
// - EBUSY means that the transaction is in progress.
Err(ErrorCode::Busy) => {
// The app should wait for it, but it may never happen if the remote app crashes.
// We just return to avoid a deadlock.
#[cfg(feature = "debug_ctap")]
writeln!(Console::<S>::writer(), "Couldn't cancel the transaction").unwrap();
}
Err(e) => panic!("Unexpected error when cancelling USB transaction: {:?}", e),
}
#[cfg(feature = "debug_ctap")]
writeln!(Console::<S>::writer(), "Cancelled USB transaction!").unwrap();
}
if matches!(status, Ok(SendOrRecvStatus::Received(_))) {
buf.copy_from_slice(&recv_buf);
}
status
}
}

View File

@@ -1,9 +1,13 @@
use libtock_core::syscalls;
use libtock_platform::Syscalls;
pub fn yieldk_for<F: Fn() -> bool>(cond: F) {
while !cond() {
unsafe {
syscalls::raw::yieldk();
pub struct Util<S: Syscalls>(S);
impl<S: Syscalls> Util<S> {
// Yielding manually is discouraged as it conflicts with Rust's safety guarantees.
// If you need to wait for a condition, use futures::wait_until and .await.
pub fn yieldk_for<F: Fn() -> bool>(cond: F) {
while !cond() {
S::yield_wait();
}
}
}