diff --git a/.github/workflows/cargo_bloat.yml b/.github/workflows/cargo_bloat.yml index 55f9f48..deac05e 100644 --- a/.github/workflows/cargo_bloat.yml +++ b/.github/workflows/cargo_bloat.yml @@ -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 diff --git a/.github/workflows/cargo_clippy.yml b/.github/workflows/cargo_clippy.yml index f859872..c815ec1 100644 --- a/.github/workflows/cargo_clippy.yml +++ b/.github/workflows/cargo_clippy.yml @@ -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 diff --git a/Cargo.lock b/Cargo.lock index 3fc41aa..11dd7d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index ac45dda..2a534e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] diff --git a/boards/nordic/nrf52840_dongle_opensk/src/io.rs b/boards/nordic/nrf52840_dongle_opensk/src/io.rs index f242235..a1f4429 100644 --- a/boards/nordic/nrf52840_dongle_opensk/src/io.rs +++ b/boards/nordic/nrf52840_dongle_opensk/src/io.rs @@ -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, ) } diff --git a/boards/nordic/nrf52840_dongle_opensk/src/main.rs b/boards/nordic/nrf52840_dongle_opensk/src/main.rs index a71f3ec..1761bf0 100644 --- a/boards/nordic/nrf52840_dongle_opensk/src/main.rs +++ b/boards/nordic/nrf52840_dongle_opensk/src/main.rs @@ -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> = 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, + 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, } -impl kernel::Platform for Platform { +impl SyscallDriverLookup for Platform { fn with_driver(&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>> + 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); } diff --git a/boards/nordic/nrf52840_mdk_dfu/src/io.rs b/boards/nordic/nrf52840_mdk_dfu/src/io.rs index dc25d10..60f88d0 100644 --- a/boards/nordic/nrf52840_mdk_dfu/src/io.rs +++ b/boards/nordic/nrf52840_mdk_dfu/src/io.rs @@ -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, ) } diff --git a/boards/nordic/nrf52840_mdk_dfu/src/main.rs b/boards/nordic/nrf52840_mdk_dfu/src/main.rs index f3d2922..e599d46 100644 --- a/boards/nordic/nrf52840_mdk_dfu/src/main.rs +++ b/boards/nordic/nrf52840_mdk_dfu/src/main.rs @@ -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> = 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, + 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(&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>> + 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); } diff --git a/boards/nordic/nrf52840dk_opensk/src/io.rs b/boards/nordic/nrf52840dk_opensk/src/io.rs index bd4d455..a3aa356 100644 --- a/boards/nordic/nrf52840dk_opensk/src/io.rs +++ b/boards/nordic/nrf52840dk_opensk/src/io.rs @@ -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, ) } diff --git a/boards/nordic/nrf52840dk_opensk/src/main.rs b/boards/nordic/nrf52840dk_opensk/src/main.rs index 7132beb..21d22c0 100644 --- a/boards/nordic/nrf52840dk_opensk/src/main.rs +++ b/boards/nordic/nrf52840dk_opensk/src/main.rs @@ -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> = 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, + 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, + scheduler: &'static RoundRobinSched<'static>, + systick: cortexm4::systick::SysTick, } -impl kernel::Platform for Platform { +impl SyscallDriverLookup for Platform { fn with_driver(&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>> + 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); } diff --git a/bootloader/Cargo.lock b/bootloader/Cargo.lock index 04fce6e..8307a58 100644 --- a/bootloader/Cargo.lock +++ b/bootloader/Cargo.lock @@ -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" diff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml index b93d4c1..8861fde 100644 --- a/bootloader/Cargo.toml +++ b/bootloader/Cargo.toml @@ -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" diff --git a/bootloader/rust-toolchain b/bootloader/rust-toolchain index 645c2cb..b3bb5dd 100644 --- a/bootloader/rust-toolchain +++ b/bootloader/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2021-03-25" +channel = "nightly-2023-02-01" components = ["clippy", "rustfmt"] targets = ["thumbv7em-none-eabi"] diff --git a/bootloader/src/crypto_cell.rs b/bootloader/src/crypto_cell.rs index 7cbd01e..9a77b32 100644 --- a/bootloader/src/crypto_cell.rs +++ b/bootloader/src/crypto_cell.rs @@ -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, diff --git a/bootloader/src/static_ref.rs b/bootloader/src/static_ref.rs index 4fc27f4..30a7894 100644 --- a/bootloader/src/static_ref.rs +++ b/bootloader/src/static_ref.rs @@ -40,7 +40,7 @@ impl Copy for StaticRef {} impl Deref for StaticRef { type Target = T; - fn deref(&self) -> &'static T { + fn deref(&self) -> &T { unsafe { &*self.ptr } } } diff --git a/build.rs b/build.rs index d0700a5..4acc966 100644 --- a/build.rs +++ b/build.rs @@ -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(); } diff --git a/deploy.py b/deploy.py index a5eaa58..a235e51 100755 --- a/deploy.py +++ b/deploy.py @@ -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", diff --git a/examples/console_test.rs b/examples/console_test.rs index a9ed8da..9bd6a01 100644 --- a/examples/console_test.rs +++ b/examples/console_test.rs @@ -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::::write(&buf[..(i + 1)]) + .map_err(|e| e.into()) + .flex_unwrap(); } } } diff --git a/examples/crypto_bench.rs b/examples/crypto_bench.rs index a327546..9ff9326 100644 --- a/examples/crypto_bench.rs +++ b/examples/crypto_bench.rs @@ -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::::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::::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(console: &mut Console, timer: &Timer, title: &str, mut f: F) +fn bench(console: &mut ConsoleWriter, timer: &Timer, 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::::from_clock_value(timer.get_current_clock().flex_unwrap()); + let start = + Timestamp::::from_clock_value(timer.get_current_counter_ticks().flex_unwrap()); for _ in 0..count { f(); } - let end = Timestamp::::from_clock_value(timer.get_current_clock().flex_unwrap()); + let end = + Timestamp::::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; } diff --git a/examples/erase_storage.rs b/examples/erase_storage.rs index 0c8791f..728c97d 100644 --- a/examples/erase_storage.rs +++ b/examples/erase_storage.rs @@ -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::::on(1).map_err(|e| e.into()).flex_unwrap(); // red on dongle + let mut storage = take_storage::().unwrap(); let num_pages = storage.num_pages(); - writeln!(Console::new(), "Erase {} pages of storage:", num_pages).unwrap(); + let mut console = Console::::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::::on(1).map_err(|e| e.into()).flex_unwrap(); + Leds::::off(0).map_err(|e| e.into()).flex_unwrap(); // green on dongle } diff --git a/examples/nfct_test.rs b/examples/nfct_test.rs index 3483a92..2a2d756 100644 --- a/examples/nfct_test.rs +++ b/examples/nfct_test.rs @@ -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) { 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 for ReturnCode { - fn from(original: isize) -> ReturnCode { + impl From 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::::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, + timer: &Timer, title: &str, - mut buf: &mut [u8], + buf: &mut [u8], ) -> ReturnCode { let amount = buf.len(); - let start = Timestamp::::from_clock_value(timer.get_current_clock().flex_unwrap()); - match NfcTag::transmit(&mut buf, amount) { + let start = + Timestamp::::from_clock_value(timer.get_current_counter_ticks().flex_unwrap()); + match NfcTag::::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::::from_clock_value(timer.get_current_clock().flex_unwrap()); + let end = + Timestamp::::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, buf: &mut [u8; 256]) -> ReturnCode { + match NfcTag::::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, + timer: &Timer, + 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) { // 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::::enable_emulation() {} // Configure Type 4 tag - while !NfcTag::configure(4) {} + while !NfcTag::::configure(4) {} } ReturnCode::ECANCEL /* field lost */ => { - NfcTag::disable_emulation(); + NfcTag::::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::::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::::writer(); writeln!(console, "****************************************").unwrap(); writeln!(console, "nfct_test application is installed").unwrap(); example::nfc(&mut console); diff --git a/examples/oom_test.rs b/examples/oom_test.rs index 2248abc..11b2d0b 100644 --- a/examples/oom_test.rs +++ b/examples/oom_test.rs @@ -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::::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 = 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(); } } diff --git a/examples/panic_test.rs b/examples/panic_test.rs index e98314c..156f1bf 100644 --- a/examples/panic_test.rs +++ b/examples/panic_test.rs @@ -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!") diff --git a/examples/store_latency.rs b/examples/store_latency.rs index 06c978e..72ce486 100644 --- a/examples/store_latency.rs +++ b/examples/store_latency.rs @@ -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 { - Timestamp::::from_clock_value(timer.get_current_clock().ok().unwrap()) +type Syscalls = TockSyscalls; + +fn timestamp(timer: &Timer) -> Timestamp { + Timestamp::::from_clock_value(timer.get_current_counter_ticks().ok().unwrap()) } -fn measure(timer: &Timer, operation: impl FnOnce() -> T) -> (T, Duration) { +fn measure(timer: &Timer, operation: impl FnOnce() -> T) -> (T, Duration) { let before = timestamp(timer); let result = operation(); let after = timestamp(timer); (result, after - before) } -fn boot_store(mut storage: Storage, erase: bool) -> Store { - use persistent_store::Storage; +fn boot_store( + mut storage: Storage, + erase: bool, +) -> Store> { 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) -> StorageConfig { StorageConfig { num_pages: storage.num_pages(), } @@ -73,19 +81,19 @@ struct Stat { } fn compute_latency( - storage: Storage, - timer: &Timer, + storage: Storage, + timer: &Timer, num_pages: usize, key_increment: usize, word_length: usize, -) -> (Storage, Stat) { +) -> (Storage, Stat) { let mut stat = Stat { key_increment, entry_length: word_length, ..Default::default() }; - let mut console = Console::new(); + let mut console = Console::::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::(|_| {}); + + let timer = with_callback.init().flex_unwrap(); + let storage = take_storage::().unwrap(); let config = storage_config(&storage); let mut stats = Vec::new(); + let mut console = Console::::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::::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>) { @@ -223,6 +234,6 @@ fn write_matrix(mut m: Vec>) { for col in 0..num_cols { align(&row[col], col_len[col] + 2 * (col > 0) as usize); } - writeln!(Console::new()).unwrap(); + writeln!(Console::::writer()).unwrap(); } } diff --git a/layout.ld b/layout.ld deleted file mode 100644 index 64aab2a..0000000 --- a/layout.ld +++ /dev/null @@ -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") diff --git a/libraries/opensk/src/ctap/mod.rs b/libraries/opensk/src/ctap/mod.rs index aad8bbb..4340e98 100644 --- a/libraries/opensk/src/ctap/mod.rs +++ b/libraries/opensk/src/ctap/mod.rs @@ -220,7 +220,7 @@ fn send_keepalive_up_needed( 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) => { diff --git a/libtock_layout.ld b/libtock_layout.ld new file mode 100644 index 0000000..5463f26 --- /dev/null +++ b/libtock_layout.ld @@ -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) + } +} diff --git a/nrf52840_layout.ld b/nrf52840_layout.ld index 8292738..df4fe83 100644 --- a/nrf52840_layout.ld +++ b/nrf52840_layout.ld @@ -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 diff --git a/nrf52840_layout_a.ld b/nrf52840_layout_a.ld index 81c6ed2..1649620 100644 --- a/nrf52840_layout_a.ld +++ b/nrf52840_layout_a.ld @@ -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 diff --git a/nrf52840_layout_b.ld b/nrf52840_layout_b.ld index 5d76bc9..019074b 100644 --- a/nrf52840_layout_b.ld +++ b/nrf52840_layout_b.ld @@ -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 diff --git a/patches/libtock-rs/01-alloc-init-feature.patch b/patches/libtock-rs/01-alloc-init-feature.patch new file mode 100644 index 0000000..216da6c --- /dev/null +++ b/patches/libtock-rs/01-alloc-init-feature.patch @@ -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", diff --git a/patches/libtock-rs/01-macos.patch b/patches/libtock-rs/01-macos.patch deleted file mode 100644 index 5f25664..0000000 --- a/patches/libtock-rs/01-macos.patch +++ /dev/null @@ -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]; - } - } diff --git a/patches/tock/03-additional-boards.patch b/patches/tock/01-nrf52-opensk-boards.patch similarity index 89% rename from patches/tock/03-additional-boards.patch rename to patches/tock/01-nrf52-opensk-boards.patch index 97774b9..bb272c9 100644 --- a/patches/tock/03-additional-boards.patch +++ b/patches/tock/01-nrf52-opensk-boards.patch @@ -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", diff --git a/patches/tock/08-remove-unused-ctap-module.patch b/patches/tock/02-remove-ctap-modules.patch similarity index 94% rename from patches/tock/08-remove-unused-ctap-module.patch rename to patches/tock/02-remove-ctap-modules.patch index 11191e8..72014ec 100644 --- a/patches/tock/08-remove-unused-ctap-module.patch +++ b/patches/tock/02-remove-ctap-modules.patch @@ -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> { +- 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> CtapComponent { - 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 { - 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: @@ -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 { +- ) -> Result { - 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; diff --git a/patches/tock/02-usb.patch b/patches/tock/02-usb.patch deleted file mode 100644 index 78f7a7b..0000000 --- a/patches/tock/02-usb.patch +++ /dev/null @@ -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> = MaybeUninit::uninit(); -+ static mut BUF2: MaybeUninit> = -+ MaybeUninit::uninit(); -+ (&mut BUF1, &mut BUF2) -+ };}; -+} -+ -+pub struct UsbCtapComponent> { -+ board_kernel: &'static kernel::Kernel, -+ controller: &'static C, -+ max_ctrl_packet_size: u8, -+ vendor_id: u16, -+ product_id: u16, -+ strings: &'static [&'static str], -+} -+ -+impl> UsbCtapComponent { -+ 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> Component for UsbCtapComponent { -+ type StaticInput = ( -+ &'static mut MaybeUninit>, -+ &'static mut MaybeUninit>, -+ ); -+ 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, -+ callback: Option, -+ buffer: Option>, -+ // 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, -+} -+ -+impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> { -+ pub fn new(usb_client: &'a ClientCtapHID<'a, 'b, C>, apps: Grant) -> 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>, -+ ) -> 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, -+ 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, -+ pending_out: Cell, -+ delayed_out: Cell, -+} -+ -+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()); -+ } -+} diff --git a/patches/tock/03-add-ctap-modules.patch b/patches/tock/03-add-ctap-modules.patch new file mode 100644 index 0000000..5ffd5eb --- /dev/null +++ b/patches/tock/03-add-ctap-modules.patch @@ -0,0 +1,1102 @@ +diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs +index 92d45f7e3..51041545b 100644 +--- a/boards/components/src/lib.rs ++++ b/boards/components/src/lib.rs +@@ -64,3 +64,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..eed34a268 +--- /dev/null ++++ b/boards/components/src/usb_ctap.rs +@@ -0,0 +1,87 @@ ++//! 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; ++ ++// Setup static space for the objects. ++#[macro_export] ++macro_rules! usb_ctap_component_helper { ++ ($C:ty $(,)?) => {{ ++ use capsules::usb::usb_ctap::CtapUsbSyscallDriver; ++ use capsules::usb::usbc_ctap_hid::ClientCtapHID; ++ use core::mem::MaybeUninit; ++ ++ static mut hid: MaybeUninit> = MaybeUninit::uninit(); ++ static mut driver: MaybeUninit> = ++ MaybeUninit::uninit(); ++ ++ (&mut hid, &mut driver) ++ };}; ++} ++ ++pub struct UsbCtapComponent> { ++ board_kernel: &'static kernel::Kernel, ++ driver_num: usize, ++ controller: &'static C, ++ max_ctrl_packet_size: u8, ++ vendor_id: u16, ++ product_id: u16, ++ strings: &'static [&'static str], ++} ++ ++impl> UsbCtapComponent { ++ pub fn new( ++ board_kernel: &'static kernel::Kernel, ++ driver_num: usize, ++ controller: &'static C, ++ max_ctrl_packet_size: u8, ++ vendor_id: u16, ++ product_id: u16, ++ strings: &'static [&'static str], ++ ) -> Self { ++ Self { ++ board_kernel, ++ driver_num, ++ controller, ++ max_ctrl_packet_size, ++ vendor_id, ++ product_id, ++ strings, ++ } ++ } ++} ++ ++impl> Component for UsbCtapComponent { ++ type StaticInput = ( ++ &'static mut MaybeUninit>, ++ &'static mut MaybeUninit>, ++ ); ++ type Output = &'static CtapUsbSyscallDriver<'static, 'static, C>; ++ ++ unsafe fn finalize(self, s: Self::StaticInput) -> Self::Output { ++ let grant_cap = create_capability!(capabilities::MemoryAllocationCapability); ++ ++ let usb_ctap = s.0.write(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 = s.1.write(CtapUsbSyscallDriver::new( ++ usb_ctap, ++ self.board_kernel.create_grant(self.driver_num, &grant_cap), ++ )); ++ usb_ctap.set_client(usb_driver); ++ ++ usb_driver ++ } ++} +diff --git a/capsules/src/driver.rs b/capsules/src/driver.rs +index f189dfd09..c48c7e094 100644 +--- a/capsules/src/driver.rs ++++ b/capsules/src/driver.rs +@@ -29,6 +29,8 @@ pub enum NUM { + UsbUser = 0x20005, + I2cMasterSlave = 0x20006, + ++ UsbCtap = 0x20009, ++ + // Radio + BleAdvertising = 0x30000, + Ieee802154 = 0x30001, +diff --git a/capsules/src/usb/app.rs b/capsules/src/usb/app.rs +new file mode 100644 +index 000000000..f2e248bc9 +--- /dev/null ++++ b/capsules/src/usb/app.rs +@@ -0,0 +1,61 @@ ++#[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, ++ // 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()) ++ } ++ ++ pub fn check_side(&mut self) { ++ if !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.side == Some(side) ++ } ++} +diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs +index 767f5de83..cb5e0af97 100644 +--- a/capsules/src/usb/mod.rs ++++ b/capsules/src/usb/mod.rs +@@ -1,5 +1,8 @@ ++pub mod app; + pub mod cdc; + 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..30cac1323 +--- /dev/null ++++ b/capsules/src/usb/usb_ctap.rs +@@ -0,0 +1,343 @@ ++use super::app::{App, Side}; ++use super::usbc_ctap_hid::ClientCtapHID; ++use kernel::errorcode::ErrorCode; ++use kernel::grant::{AllowRoCount, AllowRwCount, Grant, GrantKernelData, UpcallCount}; ++use kernel::hil; ++use kernel::hil::usb::Client; ++use kernel::processbuffer::{ReadableProcessBuffer, WriteableProcessBuffer}; ++use kernel::syscall::{CommandReturn, SyscallDriver}; ++use kernel::ProcessId; ++ ++/// 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; ++ ++/// Ids for read-only allow buffers ++mod ro_allow { ++ pub const TRANSMIT: usize = 0; ++ pub const COUNT: u8 = 1; ++} ++ ++/// Ids for read-write allow buffers ++mod rw_allow { ++ pub const RECEIVE: usize = 0; ++ pub const COUNT: u8 = 1; ++} ++ ++/// Ids for scheduling the upcalls ++/// ++/// They **must** match the the subscribe numbers which were used by the process to ++/// subscribe to the upcall. ++mod upcalls { ++ pub const TRANSMITTED: usize = 0; ++ pub const RECEIVED: usize = 1; ++ pub const COUNT: u8 = 2; ++} ++ ++type CtabUsbDriverGrant = Grant< ++ App, ++ UpcallCount<{ upcalls::COUNT }>, ++ AllowRoCount<{ ro_allow::COUNT }>, ++ AllowRwCount<{ rw_allow::COUNT }>, ++>; ++ ++pub trait CtapUsbClient { ++ // Whether this client is ready to receive a packet. This must be checked before calling ++ // 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], ++ endpoint: usize, ++ app_data: (Option<&mut App>, Option<&GrantKernelData>), ++ ); ++ ++ // 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: CtabUsbDriverGrant, ++} ++ ++impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> { ++ pub fn new(usb_client: &'a ClientCtapHID<'a, 'b, C>, apps: CtabUsbDriverGrant) -> Self { ++ CtapUsbSyscallDriver { usb_client, apps } ++ } ++ ++ fn app_packet_received( ++ &self, ++ packet: &[u8; 64], ++ endpoint: usize, ++ app: &mut App, ++ kernel: &GrantKernelData, ++ ) { ++ if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive()) { ++ let _ = kernel ++ .get_readwrite_processbuffer(rw_allow::RECEIVE) ++ .and_then(|buf| buf.mut_enter(|dest| { ++ if dest.len() >= 64 { ++ dest.copy_from_slice(packet); ++ } else { ++ panic!(); ++ } ++ })); ++ app.waiting = false; ++ // reset the client state ++ app.check_side(); ++ // Signal to the app that a packet is ready. ++ // TODO: passing the upcallid again in the registers is not needed anymore with Tock 2.0, ++ // but is currently still there for backwards compatibility ++ kernel ++ .schedule_upcall(upcalls::RECEIVED, (upcalls::RECEIVED, endpoint, 0)) ++ .ok(); ++ } ++ } ++} ++ ++impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDriver<'a, 'b, C> { ++ fn can_receive_packet(&self, app: &Option<&mut App>) -> bool { ++ let mut result = false; ++ 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], ++ endpoint: usize, ++ app_data: (Option<&mut App>, Option<&GrantKernelData>), ++ ) { ++ match app_data { ++ (None, _) => { ++ for app in self.apps.iter() { ++ app.enter(|a, kernel_grant| { ++ self.app_packet_received(packet, endpoint, a, kernel_grant); ++ }) ++ } ++ } ++ (Some(app), Some(kernel_grant)) => { ++ self.app_packet_received(packet, endpoint, app, kernel_grant) ++ } ++ // this should never happen as having a valid app always results ++ // in also having the grant data ++ _ => panic!("invalid app_data combination!"), ++ } ++ } ++ ++ fn packet_transmitted(&self) { ++ for app in self.apps.iter() { ++ app.enter(|app, kernel_data| { ++ if app.connected ++ && app.waiting ++ && app.side.map_or(false, |side| side.can_transmit()) ++ { ++ app.waiting = false; ++ // reset the client state ++ app.check_side(); ++ // Signal to the app that the packet was sent. ++ kernel_data ++ .schedule_upcall(upcalls::TRANSMITTED, (upcalls::TRANSMITTED, 0, 0)) ++ .unwrap(); ++ } ++ }); ++ } ++ } ++} ++ ++impl<'a, 'b, C: hil::usb::UsbController<'a>> SyscallDriver for CtapUsbSyscallDriver<'a, 'b, C> { ++ fn allocate_grant(&self, process_id: ProcessId) -> Result<(), kernel::process::Error> { ++ self.apps.enter(process_id, |_, _| {}) ++ } ++ ++ fn command( ++ &self, ++ cmd_num: usize, ++ endpoint: usize, ++ _arg2: usize, ++ process_id: ProcessId, ++ ) -> CommandReturn { ++ match cmd_num { ++ CTAP_CMD_CHECK => CommandReturn::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(process_id, |app, _| { ++ if app.connected { ++ CommandReturn::failure(ErrorCode::ALREADY) ++ } else if busy { ++ CommandReturn::failure(ErrorCode::BUSY) ++ } else { ++ self.usb_client.enable(); ++ self.usb_client.attach(); ++ app.connected = true; ++ CommandReturn::success() ++ } ++ }) ++ .unwrap_or_else(|err| err.into()) ++ } ++ CTAP_CMD_TRANSMIT => self ++ .apps ++ .enter(process_id, |app, kernel| { ++ if !app.connected { ++ CommandReturn::failure(ErrorCode::RESERVE) ++ } else { ++ // set the client state to transmit packets ++ if !app.set_side(Side::Transmit) { ++ return CommandReturn::failure(ErrorCode::INVAL); ++ } ++ ++ if app.is_ready_for_command(Side::Transmit) { ++ if app.waiting { ++ CommandReturn::failure(ErrorCode::ALREADY) ++ } else { ++ kernel ++ .get_readonly_processbuffer(ro_allow::TRANSMIT) ++ .and_then(|buffer| { ++ buffer.enter(|buf| { ++ let mut packet: [u8; 64] = [0; 64]; ++ buf.copy_to_slice(&mut packet); ++ let r = ++ self.usb_client.transmit_packet(&packet, endpoint); ++ ++ if r.is_success() { ++ app.waiting = true; ++ } ++ ++ r ++ }) ++ }) ++ .unwrap_or(CommandReturn::failure(ErrorCode::FAIL)) ++ } ++ } else { ++ CommandReturn::failure(ErrorCode::INVAL) ++ } ++ } ++ }) ++ .unwrap_or_else(|err| err.into()), ++ CTAP_CMD_RECEIVE => self ++ .apps ++ .enter(process_id, |app, kernel_grant| { ++ if !app.connected { ++ CommandReturn::failure(ErrorCode::RESERVE) ++ } else { ++ // set the client state to recive packets ++ if !app.set_side(Side::Receive) { ++ return CommandReturn::failure(ErrorCode::INVAL); ++ } ++ if app.is_ready_for_command(Side::Receive) { ++ if app.waiting { ++ CommandReturn::failure(ErrorCode::ALREADY) ++ } else { ++ app.waiting = true; ++ self.usb_client.receive_packet(app, kernel_grant); ++ CommandReturn::success() ++ } ++ } else { ++ CommandReturn::failure(ErrorCode::INVAL) ++ } ++ } ++ }) ++ .unwrap_or_else(|err| err.into()), ++ CTAP_CMD_TRANSMIT_OR_RECEIVE => self ++ .apps ++ .enter(process_id, |app, kernel_grant| { ++ if !app.connected { ++ CommandReturn::failure(ErrorCode::RESERVE) ++ } else { ++ if !app.set_side(Side::TransmitOrReceive) { ++ return CommandReturn::failure(ErrorCode::INVAL); ++ } ++ ++ if app.is_ready_for_command(Side::TransmitOrReceive) { ++ if app.waiting { ++ CommandReturn::failure(ErrorCode::ALREADY) ++ } else { ++ // Indicates to the driver that we can receive any pending packet. ++ app.waiting = true; ++ self.usb_client.receive_packet(app, kernel_grant); ++ if !app.waiting { ++ return CommandReturn::success(); ++ } ++ ++ let r = kernel_grant ++ .get_readonly_processbuffer(ro_allow::TRANSMIT) ++ .and_then(|process_buffer| { ++ process_buffer.enter(|buf| { ++ let mut packet: [u8; 64] = [0; 64]; ++ buf.copy_to_slice(&mut packet); ++ ++ // Indicates to the driver that we have a packet to send. ++ self.usb_client.transmit_packet(&packet, endpoint) ++ }) ++ }) ++ .unwrap_or(CommandReturn::failure(ErrorCode::FAIL)); ++ if !r.is_success() { ++ return r; ++ } ++ ++ CommandReturn::success() ++ } ++ } else { ++ CommandReturn::failure(ErrorCode::INVAL) ++ } ++ } ++ }) ++ .unwrap_or_else(|err| err.into()), ++ CTAP_CMD_CANCEL => self ++ .apps ++ .enter(process_id, |app, _| { ++ if !app.connected { ++ CommandReturn::failure(ErrorCode::RESERVE) ++ } else { ++ if app.waiting { ++ // FIXME: if cancellation failed, the app should still wait. But that ++ // doesn't work yet. ++ app.waiting = false; ++ app.check_side(); ++ if self.usb_client.cancel_transaction(endpoint) { ++ CommandReturn::success() ++ } else { ++ // Cannot cancel now because the transaction is already in process. ++ // The app should wait for the callback instead. ++ CommandReturn::failure(ErrorCode::BUSY) ++ } ++ } else { ++ CommandReturn::failure(ErrorCode::ALREADY) ++ } ++ } ++ }) ++ .unwrap_or_else(|err| err.into()), ++ _ => CommandReturn::failure(ErrorCode::NOSUPPORT), ++ } ++ } ++} +diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs +new file mode 100644 +index 000000000..e074eb7a6 +--- /dev/null ++++ b/capsules/src/usb/usbc_ctap_hid.rs +@@ -0,0 +1,552 @@ ++//! A USB HID client of the USB hardware interface ++ ++use super::app::App; ++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::debug; ++use kernel::grant::GrantKernelData; ++use kernel::hil; ++use kernel::hil::usb::TransferType; ++use kernel::syscall::CommandReturn; ++use kernel::utilities::cells::OptionalCell; ++ ++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 ), ++ 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 ++]; ++ ++#[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, ++}; ++ ++/// The state of each endpoint ++struct EndpointState { ++ endpoint: usize, ++ in_buffer: Buffer64, ++ out_buffer: Buffer64, ++ tx_packet: OptionalCell<[u8; 64]>, ++ pending_in: Cell, ++ /// Is there a delayed packet? ++ delayed_out: Cell, ++} ++ ++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), ++ 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>, ++ ++ // Is there a pending OUT transaction happening? ++ pending_out: Cell, ++ next_endpoint_index: Cell, ++} ++ ++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 { ++ #[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 { ++ interface_number: 0, ++ interface_class: 0x03, // HID ++ interface_subclass: 0x00, // no subcall ++ interface_protocol: 0x00, // no protocol ++ ..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, ++ 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, ++ }, ++ ], ++ // 2 Endpoints for FIDO ++ #[cfg(feature = "vendor_hid")] ++ &[ ++ EndpointDescriptor { ++ endpoint_address: EndpointAddress::new_const( ++ VENDOR_ENDPOINT_NUM, ++ TransferDirection::HostToDevice, ++ ), ++ transfer_type: TransferType::Interrupt, ++ max_packet_size: 64, ++ interval: 5, ++ }, ++ EndpointDescriptor { ++ endpoint_address: EndpointAddress::new_const( ++ VENDOR_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, ++ class: 0x03, // HID class for all interfaces ++ max_packet_size_ep0: max_ctrl_packet_size, ++ ..descriptors::DeviceDescriptor::default() ++ }, ++ descriptors::ConfigurationDescriptor { ++ configuration_value: 1, ++ ..descriptors::ConfigurationDescriptor::default() ++ }, ++ interfaces, ++ endpoints, ++ 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, ++ #[cfg(feature = "vendor_hid")] ++ &VENDOR_HID, ++ ]), ++ Some([ ++ &CTAP_REPORT, ++ #[cfg(feature = "vendor_hid")] ++ &VENDOR_REPORT, ++ ]), ++ LANGUAGES, ++ strings, ++ ), ++ pending_out: Cell::new(false), ++ next_endpoint_index: Cell::new(0), ++ endpoints: [ ++ EndpointState::new(ENDPOINT_NUM), ++ #[cfg(feature = "vendor_hid")] ++ EndpointState::new(VENDOR_ENDPOINT_NUM), ++ ], ++ client: OptionalCell::empty(), ++ } ++ } ++ ++ 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], endpoint: usize) -> CommandReturn { ++ 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 CommandReturn::failure(kernel::ErrorCode::BUSY); ++ } ++ s.pending_in.set(true); ++ let mut buf: [u8; 64] = [0; 64]; ++ buf.copy_from_slice(packet); ++ 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(endpoint); ++ CommandReturn::success() ++ } else { ++ // unsupported endpoint ++ CommandReturn::failure(kernel::ErrorCode::INVAL) ++ } ++ } ++ ++ pub fn receive_packet(&'a self, app: &mut App, kernel_grant: &GrantKernelData) { ++ 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. ++ if s.delayed_out.take() { ++ if self.send_packet_to_client(s.endpoint, Some(app), Some(kernel_grant)) { ++ // If that succeeds, alert the controller that we can now ++ // receive data on the Interrupt OUT endpoint. ++ self.controller().endpoint_resume_out(s.endpoint); ++ } ++ } ++ } ++ } ++ } ++ ++ // 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, ++ endpoint: usize, ++ app: Option<&mut App>, ++ kernel_grant: Option<&GrantKernelData>, ++ ) -> 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(); ++ } ++ ++ assert!(!s.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(endpoint); ++ ++ self.client ++ .map(|client| client.packet_received(&buf, endpoint, (app, kernel_grant))); ++ // 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. ++ s.delayed_out.set(true); ++ false ++ } ++ } else { ++ // unsupported endpoint ++ false ++ } ++ } ++ ++ /// 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, 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, endpoint: usize) -> bool { ++ if let Some(_) = self.get_endpoint(endpoint) { ++ self.pending_out.take() ++ } else { ++ // Unsupported endpoint ++ false ++ } ++ } ++ ++ #[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(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) { ++ 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 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 ++ } ++ } else { ++ // Unsupported endpoint ++ return hil::usb::InResult::Error; ++ } ++ } ++ 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 == 0 || endpoint > NUM_ENDPOINTS { ++ return hil::usb::OutResult::Error; ++ } ++ ++ if packet_bytes != 64 { ++ // Cannot process this packet ++ hil::usb::OutResult::Error ++ } else { ++ if self.send_packet_to_client(endpoint, None, None) { ++ hil::usb::OutResult::Ok ++ } else { ++ hil::usb::OutResult::Delay ++ } ++ } ++ } ++ TransferType::Control | TransferType::Isochronous => unreachable!(), ++ } ++ } ++ ++ fn packet_transmitted(&'a self, endpoint: usize) { ++ 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(endpoint); ++ ++ // Notify the client ++ self.client.map(|client| client.packet_transmitted()); ++ } else { ++ panic!("Unexpected transmission on ep {}", endpoint); ++ } ++ } ++} diff --git a/patches/tock/09-add-vendor-hid-usb-interface.patch b/patches/tock/04-vendor-hid.patch similarity index 50% rename from patches/tock/09-add-vendor-hid-usb-interface.patch rename to patches/tock/04-vendor-hid.patch index 2681b07..7f65e3f 100644 --- a/patches/tock/09-add-vendor-hid-usb-interface.patch +++ b/patches/tock/04-vendor-hid.patch @@ -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::()) .sum::() @@ -39,7 +39,7 @@ index dea2dfed9..66a16fe87 100644 + cdc_descriptor.map_or(0, |ds| ds.iter().map(|d| d.size()).sum::()); // 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, - ), diff --git a/patches/tock/05-firmware-protect.patch b/patches/tock/05-firmware-protect.patch deleted file mode 100644 index 3322080..0000000 --- a/patches/tock/05-firmware-protect.patch +++ /dev/null @@ -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> = -+ MaybeUninit::uninit(); -+ &mut BUF -+ };}; -+} -+ -+pub struct FirmwareProtectionComponent { -+ board_kernel: &'static kernel::Kernel, -+ crp: C, -+} -+ -+impl FirmwareProtectionComponent { -+ pub fn new(board_kernel: &'static kernel::Kernel, crp: C) -> FirmwareProtectionComponent { -+ FirmwareProtectionComponent { -+ board_kernel: board_kernel, -+ crp: crp, -+ } -+ } -+} -+ -+impl Component -+ for FirmwareProtectionComponent -+{ -+ type StaticInput = &'static mut MaybeUninit>; -+ type Output = &'static firmware_protection::FirmwareProtection; -+ -+ 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, -+ 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 { -+ crp_unit: C, -+ apps: Grant>, -+} -+ -+impl FirmwareProtection { -+ pub fn new(crp_unit: C, apps: Grant>) -> Self { -+ Self { crp_unit, apps } -+ } -+} -+ -+impl Driver for FirmwareProtection { -+ /// -+ /// ### 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 = - 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 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; diff --git a/patches/tock/05-kernel-utility-method.patch b/patches/tock/05-kernel-utility-method.patch new file mode 100644 index 0000000..a26d140 --- /dev/null +++ b/patches/tock/05-kernel-utility-method.patch @@ -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)) diff --git a/patches/tock/01-persistent-storage.patch b/patches/tock/06-persistent-storage.patch similarity index 55% rename from patches/tock/01-persistent-storage.patch rename to patches/tock/06-persistent-storage.patch index 105ee46..d769bfa 100644 --- a/patches/tock/01-persistent-storage.patch +++ b/patches/tock/06-persistent-storage.patch @@ -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 = unsafe { DeferredCall::new(DeferredCallTask::Nvmc) }; +type WORD = u32; +const WORD_SIZE: usize = core::mem::size_of::(); 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, 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, -+ waiting: OptionalCell, ++ apps: NvmcDriverGrant, ++ waiting: OptionalCell, + deferred_caller: &'static DynamicDeferredCall, + deferred_handle: OptionalCell, ++ 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, -+ /// The allow slice for COMMAND(2). -+ slice: Option>, -+} ++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, ++ 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, -+ 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>, -+ ) -> 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, +@@ -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 for u32 { ++ type Error = (); ++ ++ fn try_from(value: StorageType) -> Result { ++ 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 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 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 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 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, -@@ -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. diff --git a/patches/tock/06-upgrade-partitions.patch b/patches/tock/06-upgrade-partitions.patch deleted file mode 100644 index 41367f0..0000000 --- a/patches/tock/06-upgrade-partitions.patch +++ /dev/null @@ -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. diff --git a/patches/tock/07-app-break-fix.patch b/patches/tock/07-app-break-fix.patch deleted file mode 100644 index 738cf23..0000000 --- a/patches/tock/07-app-break-fix.patch +++ /dev/null @@ -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 - // diff --git a/patches/tock/04-update-uicr.patch b/patches/tock/07-update-uicr.patch similarity index 65% rename from patches/tock/04-update-uicr.patch rename to patches/tock/07-update-uicr.patch index 55e176c..2ed8eb1 100644 --- a/patches/tock/04-update-uicr.patch +++ b/patches/tock/07-update-uicr.patch @@ -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; 13]), -+ (0x048 => _reserved2), -+ /// Reserved for Nordic hardware design -+ (0x050 => nrfhw: [ReadWrite; 12]), -+ /// Reserved for customer -+ (0x080 => customer: [ReadWrite; 32]), -+ (0x100 => _reserved3), -+ /// Mapping of the nRESET function (see POWER chapter for details) -+ (0x200 => pselreset0: ReadWrite), -+ /// Mapping of the nRESET function (see POWER chapter for details) -+ (0x204 => pselreset1: ReadWrite), -+ /// Access Port protection -+ (0x208 => approtect: ReadWrite), -+ /// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO -+ /// - Address: 0x20c - 0x210 -+ (0x20c => nfcpins: ReadWrite), -+ (0x210 => debugctrl: ReadWrite), -+ (0x214 => _reserved4), -+ /// External circuitry to be supplied from VDD pin. -+ (0x300 => extsupply: ReadWrite), -+ /// GPIO reference voltage -+ (0x304 => regout0: ReadWrite), -+ (0x308 => @END), -+ } ++ UicrRegisters { ++ (0x000 => _reserved1), ++ /// Reserved for Nordic firmware design ++ (0x014 => nrffw: [ReadWrite; 13]), ++ (0x048 => _reserved2), ++ /// Reserved for Nordic hardware design ++ (0x050 => nrfhw: [ReadWrite; 12]), ++ /// Reserved for customer ++ (0x080 => customer: [ReadWrite; 32]), ++ (0x100 => _reserved3), ++ /// Mapping of the nRESET function (see POWER chapter for details) ++ (0x200 => pselreset0: ReadWrite), ++ /// Mapping of the nRESET function (see POWER chapter for details) ++ (0x204 => pselreset1: ReadWrite), ++ /// Access Port protection ++ (0x208 => approtect: ReadWrite), ++ /// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO ++ /// - Address: 0x20c - 0x210 ++ (0x20c => nfcpins: ReadWrite), ++ (0x210 => debugctrl: ReadWrite), ++ (0x214 => _reserved4), ++ /// External circuitry to be supplied from VDD pin. ++ (0x300 => extsupply: ReadWrite), ++ /// GPIO reference voltage ++ (0x304 => regout0: ReadWrite), ++ (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) + } diff --git a/patches/tock/10-avoid-app-reentry.patch b/patches/tock/10-avoid-app-reentry.patch deleted file mode 100644 index fdf3132..0000000 --- a/patches/tock/10-avoid-app-reentry.patch +++ /dev/null @@ -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, -+ pub callback: Option, -+ pub buffer: Option>, -+ // 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, -- callback: Option, -- buffer: Option>, -- // 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) -> 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 diff --git a/patches/tock/11-connect-vendor-hid-usb-interface.patch b/patches/tock/11-connect-vendor-hid-usb-interface.patch deleted file mode 100644 index d18d0b0..0000000 --- a/patches/tock/11-connect-vendor-hid-usb-interface.patch +++ /dev/null @@ -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, - pending_out: Cell, -+ // Is there a delayed packet? - delayed_out: Cell, - } - -+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); -+ } - } --} -+ } diff --git a/patches/tock/12-pending-deadlock.patch b/patches/tock/12-pending-deadlock.patch deleted file mode 100644 index cffc665..0000000 --- a/patches/tock/12-pending-deadlock.patch +++ /dev/null @@ -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, -- pending_out: Cell, - // Is there a delayed packet? - delayed_out: Cell, - } -@@ -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, -+ next_endpoint_index: Cell, -+ - 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 diff --git a/patches/tock/13-send-packet-before-receiving.patch b/patches/tock/13-send-packet-before-receiving.patch deleted file mode 100644 index d629b61..0000000 --- a/patches/tock/13-send-packet-before-receiving.patch +++ /dev/null @@ -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 diff --git a/run_desktop_tests.sh b/run_desktop_tests.sh index 3db89dc..dd21229 100755 --- a/run_desktop_tests.sh +++ b/run_desktop_tests.sh @@ -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 ../.. diff --git a/setup.sh b/setup.sh index 72842b6..360c16b 100755 --- a/setup.sh +++ b/setup.sh @@ -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/ diff --git a/src/env/tock/buffer_upgrade_storage.rs b/src/env/tock/buffer_upgrade_storage.rs index 27c9726..e721448 100644 --- a/src/env/tock/buffer_upgrade_storage.rs +++ b/src/env/tock/buffer_upgrade_storage.rs @@ -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, + c: PhantomData, } -impl BufferUpgradeStorage { - pub fn new() -> StorageResult { +impl BufferUpgradeStorage +where + S: Syscalls, + C: platform::subscribe::Config + platform::allow_ro::Config, +{ + pub fn new() -> StorageResult { 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::::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::::new().unwrap(); assert_eq!(storage.bundle_identifier(), 0x60000); } } diff --git a/src/env/tock/clock.rs b/src/env/tock/clock.rs index a0a34aa..589a4ee 100644 --- a/src/env/tock/clock.rs +++ b/src/env/tock/clock.rs @@ -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 { now: Timestamp, + s: PhantomData, } -impl TockClock { +impl Default for TockClock { + fn default() -> Self { + TockClock { + now: Timestamp::default(), + s: PhantomData, + } + } +} + +impl TockClock { /// 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::().ok().unwrap(); if cur_tick < self.now.tick { self.now.epoch += 1; } @@ -60,12 +71,13 @@ impl TockClock { } } -impl Clock for TockClock { +impl Clock for TockClock { 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::().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::().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 } diff --git a/src/env/tock/commands.rs b/src/env/tock/commands.rs index d376558..818013f 100644 --- a/src/env/tock/commands.rs +++ b/src/env/tock/commands.rs @@ -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, bytes: &[u8], channel: Channel, ) -> Option> { @@ -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( + env: &mut TockEnv, bytes: &[u8], channel: Channel, ) -> Result>, Ctap2StatusCode> { @@ -85,8 +89,11 @@ fn encode_cbor(value: cbor::Value) -> Vec { } } -fn process_vendor_configure( - env: &mut TockEnv, +fn process_vendor_configure< + S: Syscalls, + C: platform::subscribe::Config + platform::allow_ro::Config, +>( + env: &mut TockEnv, 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, params: VendorUpgradeParameters, ) -> Result<(), Ctap2StatusCode> { let VendorUpgradeParameters { offset, data, hash } = params; - let calculated_hash = Sha::::digest(&data); + let calculated_hash = Sha::>::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, ) -> Result { let upgrade_locations = env .upgrade_storage() @@ -288,6 +301,7 @@ impl From 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::::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::::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::::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::::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::::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::::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::::default(); const METADATA_LEN: usize = 0x1000; let metadata = vec![0xFF; METADATA_LEN]; - let metadata_hash = Sha::::digest(&metadata); + let metadata_hash = Sha::>::digest(&metadata); let data = vec![0xFF; 0x1000]; - let hash = Sha::::digest(&data); + let hash = Sha::>::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::::default(); env.disable_upgrade_storage(); let data = vec![0xFF; 0x1000]; - let hash = Sha::::digest(&data); + let hash = Sha::>::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::::default(); let bundle_identifier = env.upgrade_storage().unwrap().bundle_identifier(); let upgrade_info_reponse = process_vendor_upgrade_info(&mut env); diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index 6027906..ffbc1ac 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -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 = storage::TockStorage; #[cfg(feature = "std")] -pub type Storage = BufferStorage; +pub type Storage = phantom_buffer_storage::PhantomBufferStorage; #[cfg(not(feature = "std"))] -type UpgradeStorage = storage::TockUpgradeStorage; +type UpgradeStorage = storage::TockUpgradeStorage; #[cfg(feature = "std")] -type UpgradeStorage = buffer_upgrade_storage::BufferUpgradeStorage; +type UpgradeStorage = buffer_upgrade_storage::BufferUpgradeStorage; 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 { + _syscalls: PhantomData, +} -impl CryptoRng for TockRng {} +impl Default for TockRng { + fn default() -> Self { + Self { + _syscalls: PhantomData, + } + } +} -impl RngCore for TockRng { +impl CryptoRng for TockRng {} + +impl RngCore for TockRng { 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::::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 Rng for TockRng {} -pub struct TockHidConnection { +pub struct TockHidConnection { endpoint: UsbEndpoint, + s: PhantomData, } -impl HidConnection for TockHidConnection { +impl HidConnection for TockHidConnection { 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::::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, - upgrade_storage: Option, - main_connection: TockHidConnection, +pub struct TockEnv< + S: Syscalls, + C: platform::subscribe::Config + platform::allow_ro::Config = DefaultConfig, +> { + rng: TockRng, + store: Store>, + upgrade_storage: Option>, + main_connection: TockHidConnection, #[cfg(feature = "vendor_hid")] - vendor_connection: TockHidConnection, + vendor_connection: TockHidConnection, blink_pattern: usize, - clock: TockClock, + clock: TockClock, + c: PhantomData, } -impl Default for TockEnv { +impl Default + for TockEnv +{ /// 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::().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 TockEnv +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> { 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 { - // 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 { +pub fn take_storage( +) -> StorageResult> { // Use the Nordic configuration. const PAGE_SIZE: usize = 0x1000; const NUM_PAGES: usize = 20; @@ -213,10 +217,54 @@ pub fn take_storage() -> StorageResult { 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( +) -> StorageResult> { + // 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 UserPresence for TockEnv +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::(self.blink_pattern); self.blink_pattern += 1; + // enable interrupts for all buttons + let num_buttons = Buttons::::count().map_err(|_| UserPresenceError::Fail)?; + (0..num_buttons) + .try_for_each(|n| Buttons::::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::(|_| 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::::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: "); + // Wait for a button touch or an alarm. + libtock_drivers::util::Util::::yieldk_for(|| { + button_touched.get() || keepalive_expired.get() + }); + + Buttons::::unregister_listener(); + + // disable event interrupts for all buttons + (0..num_buttons) + .try_for_each(|n| Buttons::::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: "); + } } - } - 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::(); } } -impl key_store::Helper for TockEnv {} +impl key_store::Helper for TockEnv +where + S: Syscalls, + C: platform::allow_ro::Config + platform::subscribe::Config, +{ +} -impl AttestationStore for TockEnv { +impl AttestationStore for TockEnv +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 Env + for TockEnv +{ + type Rng = TockRng; type UserPresence = Self; - type Storage = Storage; + type Storage = Storage; type KeyStore = Self; type AttestationStore = Self; - type Clock = TockClock; - type Write = Console; + type Clock = TockClock; + type Write = ConsoleWriter; type Customization = CustomizationImpl; - type HidConnection = TockHidConnection; + type HidConnection = TockHidConnection; 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::::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(pattern_seed: usize) { + for l in 0..Leds::::count().unwrap() { + if (pattern_seed ^ l as usize).count_ones() & 1 != 0 { + Leds::::on(l).unwrap(); } else { - led::get(l).flex_unwrap().off().flex_unwrap(); + Leds::::off(l).unwrap(); } } } -pub fn wink_leds(pattern_seed: usize) { +pub fn wink_leds(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::::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::::on(l as u32).unwrap(); } else { - led::get(l).flex_unwrap().off().flex_unwrap(); + Leds::::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() { + let count = Leds::::count().unwrap(); + for l in 0..count { + Leds::::off(l).unwrap(); } } diff --git a/src/env/tock/phantom_buffer_storage.rs b/src/env/tock/phantom_buffer_storage.rs new file mode 100644 index 0000000..130f4e7 --- /dev/null +++ b/src/env/tock/phantom_buffer_storage.rs @@ -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, + c: PhantomData, +} + +impl PhantomBufferStorage +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 Storage for PhantomBufferStorage +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> { + 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) + } +} diff --git a/src/env/tock/storage.rs b/src/env/tock/storage.rs index d1dd57e..15c5b42 100644 --- a/src/env/tock/storage.rs +++ b/src/env/tock/storage.rs @@ -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 { - let code = syscalls::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg); - code.map_err(|_| StorageError::CustomError) +fn get_info(nr: u32, arg: u32) -> StorageResult { + let info = S::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg) + .to_result::() + .map_err(|_| StorageError::CustomError)?; + Ok(info) } -fn memop(nr: u32, arg: usize) -> StorageResult { - let code = unsafe { syscalls::raw::memop(nr, arg) }; - if code < 0 { - Err(StorageError::CustomError) - } else { - Ok(code as usize) +fn memop(nr: u32, arg: u32) -> StorageResult { + let registers = unsafe { S::syscall2::<{ syscall_class::MEMOP }>([nr.into(), arg.into()]) }; + + let r0 = registers[0].as_u32(); + let r1 = registers[1].as_u32(); + + // make sure r0 is the `success with u32` (129) return variant and then return the value in r1 as u32 + // see: https://github.com/tock/tock/blob/master/doc/reference/trd104-syscalls.md#32-return-values + match (r0, r1) { + (129, r1) => Ok(r1), + (_, _) => Err(StorageError::CustomError), } } -fn block_command(driver: 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::( - DRIVER_NUMBER, - subscribe_nr::DONE, - &mut alarm, - ); - if subscription.is_err() { - return Err(StorageError::CustomError); - } +fn block_command( + driver: u32, + cmd: u32, + arg1: u32, + arg2: u32, +) -> StorageResult<()> { + let called: Cell> = 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::::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( + ptr: usize, + value: &[u8], +) -> StorageResult<()> { + share::scope(|allow_ro| { + S::allow_ro::(allow_ro, value) + .map_err(|_| StorageError::CustomError)?; + block_command::( 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( + ptr: usize, + page_length: usize, +) -> StorageResult<()> { + block_command::( + DRIVER_NUMBER, + command_nr::ERASE_PAGE, + ptr as u32, + page_length as u32, + ) } -pub struct TockStorage { +pub struct TockStorage { word_size: usize, page_size: usize, num_pages: usize, max_word_writes: usize, max_page_erases: usize, storage_locations: Vec<&'static [u8]>, + s: PhantomData, + c: PhantomData, } -impl TockStorage { +impl TockStorage { /// 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 { + pub fn new() -> StorageResult> { 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::(command_nr::get_info_nr::WORD_SIZE, 0)? as usize, + page_size: get_info::(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::(command_nr::get_info_nr::MAX_WORD_WRITES, 0)? as usize, + max_page_erases: get_info::(command_nr::get_info_nr::MAX_PAGE_ERASES, 0)? as usize, 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::(memop_nr::STORAGE_CNT, 0)?; + for i in 0..num_storage_locations { + if memop::(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::(memop_nr::STORAGE_PTR, i)? as usize; + let storage_len = memop::(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 Storage + for TockStorage +{ 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::(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::(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, + c: PhantomData, } -impl TockUpgradeStorage { +impl TockUpgradeStorage +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 { + pub fn new() -> StorageResult> { let mut locations = TockUpgradeStorage { - page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?, + page_size: get_info::(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::(memop_nr::STORAGE_CNT, 0)? { + let storage_type = memop::(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::(memop_nr::STORAGE_PTR, i)? as usize; + let storage_len = memop::(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::::new(); + let mut hasher = Sha::>::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::(self, UPGRADE_PUBLIC_KEY, new_metadata)?; + check_metadata::, 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::(address, self.page_size)?; } - write_slice(address, &data)?; + write_slice::(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(()) } diff --git a/src/env/tock/upgrade_helper.rs b/src/env/tock/upgrade_helper.rs index f9947bc..7f6936e 100644 --- a/src/env/tock/upgrade_helper.rs +++ b/src/env/tock/upgrade_helper.rs @@ -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( - #[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, + #[cfg(feature = "std")] upgrade_locations: &BufferUpgradeStorage, public_key_bytes: &[u8], metadata: &[u8], ) -> StorageResult<()> { @@ -104,11 +110,13 @@ fn verify_signature( 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) -> [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::(&upgrade_locations, &public_key_bytes, &metadata), + check_metadata::( + &upgrade_locations, + &public_key_bytes, + &metadata + ), Ok(()) ); // Manipulating the partition address fails. metadata[METADATA_SIGN_OFFSET + 8] = 0x88; assert_eq!( - check_metadata::(&upgrade_locations, &public_key_bytes, &metadata), + check_metadata::( + &upgrade_locations, + &public_key_bytes, + &metadata + ), Err(StorageError::CustomError) ); metadata[METADATA_SIGN_OFFSET + 8] = 0x00; // Wrong metadata length fails. assert_eq!( - check_metadata::( + check_metadata::( &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::(&upgrade_locations, &public_key_bytes, &metadata), + check_metadata::( + &upgrade_locations, + &public_key_bytes, + &metadata + ), Err(StorageError::CustomError) ); metadata[0] ^= 0x01; // Manipulating the signature fails. metadata[32] ^= 0x01; assert_eq!( - check_metadata::(&upgrade_locations, &public_key_bytes, &metadata), + check_metadata::( + &upgrade_locations, + &public_key_bytes, + &metadata + ), Err(StorageError::CustomError) ); } diff --git a/src/main.rs b/src/main.rs index 015f943..a71c8ff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 = Duration::from_ms(1000); const KEEPALIVE_DELAY_MS_TOCK: Duration = 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::::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::::setup() { panic!("Cannot setup USB driver"); } - let env = TockEnv::default(); + let env = TockEnv::::default(); let mut ctap = opensk::Ctap::new(env); let mut led_counter = 0; - let mut led_blink_timer = <::Clock as Clock>::Timer::default(); + let mut led_blink_timer = + < 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::::count().ok().unwrap(); // Variable for use in both the send_and_maybe_recv and recv cases. let mut usb_endpoint: Option = 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::::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::( + "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::( + "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::::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::( + "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::::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::::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::(led_counter); } else { #[cfg(not(feature = "with_ctap1"))] - switch_off_leds(); + switch_off_leds::(); #[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::(led_counter); } else { - switch_off_leds(); + switch_off_leds::(); } } } } #[cfg(feature = "debug_ctap")] -fn print_packet_notice(notice_text: &str, now_us: usize) { +fn print_packet_notice( + notice_text: &str, + now_us: usize, + writer: &mut ConsoleWriter, +) { writeln!( - Console::new(), + writer, "{} at {}.{:06} s", notice_text, now_us / 1_000_000, diff --git a/third_party/lang-items/Cargo.lock b/third_party/lang-items/Cargo.lock index f6e0d5b..ef0b8e0 100644 --- a/third_party/lang-items/Cargo.lock +++ b/third_party/lang-items/Cargo.lock @@ -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" diff --git a/third_party/lang-items/Cargo.toml b/third_party/lang-items/Cargo.toml index 442a314..60ba0f6 100644 --- a/third_party/lang-items/Cargo.toml +++ b/third_party/lang-items/Cargo.toml @@ -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 = [] diff --git a/third_party/lang-items/src/allocator.rs b/third_party/lang-items/src/allocator.rs index 3184f7f..0dd99e2 100644 --- a/third_party/lang-items/src/allocator.rs +++ b/third_party/lang-items/src/allocator.rs @@ -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::::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::::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::::signal_oom(); + util::Util::::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::::writer(), + "Couldn't allocate: {:?}", + _layout + ) + .ok(); + TockSyscalls::exit_terminate(ErrorCode::Fail as u32); } - util::cycle_leds() + #[cfg(not(feature = "panic_console"))] + util::Util::::cycle_leds() } diff --git a/third_party/lang-items/src/lib.rs b/third_party/lang-items/src/lib.rs index 3d517ac..4f5edbf 100644 --- a/third_party/lang-items/src/lib.rs +++ b/third_party/lang-items/src/lib.rs @@ -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!() } diff --git a/third_party/lang-items/src/panic_handler.rs b/third_party/lang-items/src/panic_handler.rs index abf0a6e..5a0e161 100644 --- a/third_party/lang-items/src/panic_handler.rs +++ b/third_party/lang-items/src/panic_handler.rs @@ -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::::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::::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::::flash_all_leds(); } diff --git a/third_party/lang-items/src/util.rs b/third_party/lang-items/src/util.rs index c1a70fd..390d8a6 100644 --- a/third_party/lang-items/src/util.rs +++ b/third_party/lang-items/src/util.rs @@ -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, 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 Util { + /// Signal a panic using the LowLevelDebug capsule (if available). + pub fn signal_panic() { + LowLevelDebug::::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::::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::::count() { + for led in 0..led_count { + let _ = Leds::::on(led); + } + } + let _ = timer::Alarm::::sleep_for(timer::Milliseconds(100)); + if let Ok(led_count) = Leds::::count() { + for led in 0..led_count { + let _ = Leds::::off(led); + } + } + let _ = timer::Alarm::::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::::count() { + for led in 0..leds { + let _ = Leds::::on(led); + let _ = timer::Alarm::::sleep_for(timer::Milliseconds(100)); + let _ = Leds::::off(led); + } } } } diff --git a/third_party/libtock-drivers/Cargo.lock b/third_party/libtock-drivers/Cargo.lock index 1f3e213..c722fca 100644 --- a/third_party/libtock-drivers/Cargo.lock +++ b/third_party/libtock-drivers/Cargo.lock @@ -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" diff --git a/third_party/libtock-drivers/Cargo.toml b/third_party/libtock-drivers/Cargo.toml index 41e5c32..65c3356 100644 --- a/third_party/libtock-drivers/Cargo.toml +++ b/third_party/libtock-drivers/Cargo.toml @@ -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 = [] diff --git a/third_party/libtock-drivers/src/buttons.rs b/third_party/libtock-drivers/src/buttons.rs deleted file mode 100644 index 5ab985f..0000000 --- a/third_party/libtock-drivers/src/buttons.rs +++ /dev/null @@ -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(callback: CB) -> WithCallback { - WithCallback { callback } -} - -pub struct WithCallback { - callback: CB, -} - -struct ButtonConsumer; - -impl Consumer> for ButtonConsumer { - fn consume(data: &mut WithCallback, button_num: usize, state: usize, _: usize) { - (data.callback)(button_num, state.into()); - } -} - -impl WithCallback { - pub fn init(&mut self) -> TockResult { - let count = syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?; - - let subscription = syscalls::subscribe::( - 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 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 { - 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