Tock V2 port - rebased and updated (#620)
* Changes from #580 * fixes USB cancel panic * style fixes * Update src/env/tock/storage.rs Co-authored-by: Zach Halvorsen <zhalvorsen@google.com> --------- Co-authored-by: Zach Halvorsen <zhalvorsen@google.com>
This commit is contained in:
4
.github/workflows/cargo_bloat.yml
vendored
4
.github/workflows/cargo_bloat.yml
vendored
@@ -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
|
||||
|
||||
8
.github/workflows/cargo_clippy.yml
vendored
8
.github/workflows/cargo_clippy.yml
vendored
@@ -25,10 +25,10 @@ jobs:
|
||||
- uses: actions-rs/clippy-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
args: --all-targets --features std
|
||||
args: --features std
|
||||
- name: Deny Clippy warnings (std)
|
||||
run: cargo clippy --all-targets --features std -- -A clippy::new_without_default -D warnings
|
||||
run: cargo clippy --features std -- -D warnings
|
||||
- name: Deny Clippy warnings (all)
|
||||
run: cargo clippy --all-targets --features std,with_ctap1,ed25519,vendor_hid -- -A clippy::new_without_default -D warnings
|
||||
run: cargo clippy --features std,with_ctap1,ed25519,vendor_hid -- -D warnings
|
||||
- name: Deny Clippy warnings (all, nfc)
|
||||
run: cargo clippy --all-targets --features std,with_ctap1,with_nfc,ed25519,vendor_hid -- -A clippy::new_without_default -D warnings
|
||||
run: cargo clippy --features std,with_ctap1,with_nfc,ed25519,vendor_hid -- -D warnings
|
||||
|
||||
79
Cargo.lock
generated
79
Cargo.lock
generated
@@ -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"
|
||||
|
||||
13
Cargo.toml
13
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"]
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,15 +7,18 @@
|
||||
// Disable this attribute when documenting, as a workaround for
|
||||
// https://github.com/rust-lang/rust/issues/62184.
|
||||
#![cfg_attr(not(doc), no_main)]
|
||||
#![feature(const_in_array_repeat_expressions)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use capsules::virtual_alarm::VirtualMuxAlarm;
|
||||
use kernel::component::Component;
|
||||
use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use kernel::hil::led::LedLow;
|
||||
use kernel::hil::time::Counter;
|
||||
use kernel::platform::{KernelResources, SyscallDriverLookup, SyscallFilter};
|
||||
use kernel::scheduler::round_robin::RoundRobinSched;
|
||||
#[allow(unused_imports)]
|
||||
use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
|
||||
use kernel::{StorageLocation, StorageType};
|
||||
use nrf52840::gpio::Pin;
|
||||
use nrf52840::interrupt_service::Nrf52840DefaultPeripherals;
|
||||
use nrf52_components::{self, UartChannel, UartPins};
|
||||
@@ -56,30 +59,35 @@ static STRINGS: &'static [&'static str] = &[
|
||||
|
||||
// State for loading and holding applications.
|
||||
// How should the kernel respond when a process faults.
|
||||
const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultResponse::Panic;
|
||||
const FAULT_RESPONSE: kernel::process::PanicFaultPolicy = kernel::process::PanicFaultPolicy {};
|
||||
|
||||
// Number of concurrent processes this platform supports.
|
||||
const NUM_PROCS: usize = 8;
|
||||
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; NUM_PROCS] =
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
|
||||
[None; NUM_PROCS];
|
||||
|
||||
static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
|
||||
static mut STORAGE_LOCATIONS: [StorageLocation; 2] = [
|
||||
// We implement NUM_PAGES = 20 as 16 + 4 to satisfy the MPU.
|
||||
kernel::StorageLocation {
|
||||
StorageLocation {
|
||||
address: 0xC0000,
|
||||
size: 0x10000, // 16 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
storage_type: StorageType::Store,
|
||||
},
|
||||
kernel::StorageLocation {
|
||||
StorageLocation {
|
||||
address: 0xD0000,
|
||||
size: 0x4000, // 4 pages
|
||||
storage_type: kernel::StorageType::Store,
|
||||
storage_type: StorageType::Store,
|
||||
},
|
||||
];
|
||||
|
||||
// Static reference to chip for panic dumps
|
||||
static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
|
||||
// Static reference to process printer for panic dumps
|
||||
static mut PROCESS_PRINTER: Option<&'static kernel::process::ProcessPrinterText> = None;
|
||||
|
||||
/// Flash buffer for the custom nvmc driver
|
||||
static mut APP_FLASH_BUFFER: [u8; 0x1000] = [0; 0x1000];
|
||||
|
||||
/// Dummy buffer that causes the linker to reserve enough space for the stack.
|
||||
#[no_mangle]
|
||||
@@ -91,6 +99,7 @@ pub struct Platform {
|
||||
button: &'static capsules::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
pconsole: &'static capsules::process_console::ProcessConsole<
|
||||
'static,
|
||||
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
components::process_console::Capability,
|
||||
>,
|
||||
console: &'static capsules::console::Console<'static>,
|
||||
@@ -98,9 +107,10 @@ pub struct Platform {
|
||||
led: &'static capsules::led::LedDriver<
|
||||
'static,
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
4,
|
||||
>,
|
||||
rng: &'static capsules::rng::RngDriver<'static>,
|
||||
ipc: kernel::ipc::IPC<NUM_PROCS>,
|
||||
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
|
||||
analog_comparator: &'static capsules::analog_comparator::AnalogComparator<
|
||||
'static,
|
||||
nrf52840::acomp::Comparator<'static>,
|
||||
@@ -109,19 +119,20 @@ pub struct Platform {
|
||||
'static,
|
||||
capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
>,
|
||||
scheduler: &'static RoundRobinSched<'static>,
|
||||
systick: cortexm4::systick::SysTick,
|
||||
nvmc: &'static nrf52840::nvmc::SyscallDriver,
|
||||
usb: &'static capsules::usb::usb_ctap::CtapUsbSyscallDriver<
|
||||
'static,
|
||||
'static,
|
||||
nrf52840::usbd::Usbd<'static>,
|
||||
>,
|
||||
crp: &'static capsules::firmware_protection::FirmwareProtection<nrf52840::uicr::Uicr>,
|
||||
}
|
||||
|
||||
impl kernel::Platform for Platform {
|
||||
impl SyscallDriverLookup for Platform {
|
||||
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<&dyn kernel::Driver>) -> R,
|
||||
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
|
||||
{
|
||||
match driver_num {
|
||||
capsules::console::DRIVER_NUM => f(Some(self.console)),
|
||||
@@ -133,45 +144,88 @@ impl kernel::Platform for Platform {
|
||||
capsules::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
|
||||
nrf52840::nvmc::DRIVER_NUM => f(Some(self.nvmc)),
|
||||
capsules::usb::usb_ctap::DRIVER_NUM => f(Some(self.usb)),
|
||||
capsules::firmware_protection::DRIVER_NUM => f(Some(self.crp)),
|
||||
kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
|
||||
_ => f(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SyscallFilter for Platform {
|
||||
fn filter_syscall(
|
||||
&self,
|
||||
process: &dyn kernel::procs::ProcessType,
|
||||
process: &dyn kernel::process::Process,
|
||||
syscall: &kernel::syscall::Syscall,
|
||||
) -> Result<(), kernel::ReturnCode> {
|
||||
) -> Result<(), kernel::errorcode::ErrorCode> {
|
||||
use kernel::syscall::Syscall;
|
||||
match *syscall {
|
||||
Syscall::COMMAND {
|
||||
Syscall::Command {
|
||||
driver_number: nrf52840::nvmc::DRIVER_NUM,
|
||||
subdriver_number: cmd,
|
||||
arg0: ptr,
|
||||
arg1: len,
|
||||
} if (cmd == 2 || cmd == 3) && !process.fits_in_storage_location(ptr, len) => {
|
||||
Err(kernel::ReturnCode::EINVAL)
|
||||
Err(kernel::ErrorCode::INVAL)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Entry point in the vector table called on hard reset.
|
||||
#[no_mangle]
|
||||
pub unsafe fn reset_handler() {
|
||||
// Loads relocations and clears BSS
|
||||
nrf52840::init();
|
||||
impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
|
||||
for Platform
|
||||
{
|
||||
type SyscallDriverLookup = Self;
|
||||
type SyscallFilter = Self;
|
||||
type ProcessFault = ();
|
||||
type Scheduler = RoundRobinSched<'static>;
|
||||
type SchedulerTimer = cortexm4::systick::SysTick;
|
||||
type WatchDog = ();
|
||||
type ContextSwitchCallback = ();
|
||||
|
||||
let ppi = static_init!(nrf52840::ppi::Ppi, nrf52840::ppi::Ppi::new());
|
||||
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
|
||||
&self
|
||||
}
|
||||
fn syscall_filter(&self) -> &Self::SyscallFilter {
|
||||
&self
|
||||
}
|
||||
fn process_fault(&self) -> &Self::ProcessFault {
|
||||
&()
|
||||
}
|
||||
fn scheduler(&self) -> &Self::Scheduler {
|
||||
self.scheduler
|
||||
}
|
||||
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
|
||||
&self.systick
|
||||
}
|
||||
fn watchdog(&self) -> &Self::WatchDog {
|
||||
&()
|
||||
}
|
||||
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
/// This is in a separate, inline(never) function so that its stack frame is
|
||||
/// removed when this function returns. Otherwise, the stack space used for
|
||||
/// these static_inits is wasted.
|
||||
#[inline(never)]
|
||||
unsafe fn get_peripherals() -> &'static mut Nrf52840DefaultPeripherals<'static> {
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = static_init!(
|
||||
Nrf52840DefaultPeripherals,
|
||||
Nrf52840DefaultPeripherals::new(ppi)
|
||||
Nrf52840DefaultPeripherals::new()
|
||||
);
|
||||
|
||||
nrf52840_peripherals
|
||||
}
|
||||
|
||||
/// Main function called after RAM initialized.
|
||||
#[no_mangle]
|
||||
pub unsafe fn main() {
|
||||
nrf52840::init();
|
||||
|
||||
let nrf52840_peripherals = get_peripherals();
|
||||
|
||||
// set up circular peripheral dependencies
|
||||
nrf52840_peripherals.init();
|
||||
let base_peripherals = &nrf52840_peripherals.nrf52;
|
||||
@@ -184,6 +238,7 @@ pub unsafe fn reset_handler() {
|
||||
// GPIOs
|
||||
let gpio = components::gpio::GpioComponent::new(
|
||||
board_kernel,
|
||||
capsules::gpio::DRIVER_NUM,
|
||||
components::gpio_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
// left side of the USB plug
|
||||
@@ -219,6 +274,7 @@ pub unsafe fn reset_handler() {
|
||||
|
||||
let button = components::button::ButtonComponent::new(
|
||||
board_kernel,
|
||||
capsules::button::DRIVER_NUM,
|
||||
components::button_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
(
|
||||
@@ -230,15 +286,12 @@ pub unsafe fn reset_handler() {
|
||||
)
|
||||
.finalize(components::button_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
|
||||
let led = components::led::LedsComponent::new(components::led_component_helper!(
|
||||
let led = components::led::LedsComponent::new().finalize(components::led_component_helper!(
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin>,
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_R_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_G_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_B_PIN]),
|
||||
))
|
||||
.finalize(components::led_component_buf!(
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin>
|
||||
));
|
||||
|
||||
let chip = static_init!(
|
||||
@@ -272,11 +325,15 @@ pub unsafe fn reset_handler() {
|
||||
);
|
||||
|
||||
let rtc = &base_peripherals.rtc;
|
||||
rtc.start();
|
||||
let _ = rtc.start();
|
||||
let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
|
||||
.finalize(components::alarm_mux_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(board_kernel, mux_alarm)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(
|
||||
board_kernel,
|
||||
capsules::alarm::DRIVER_NUM,
|
||||
mux_alarm,
|
||||
)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
let uart_channel = UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD));
|
||||
let channel = nrf52_components::UartChannelComponent::new(
|
||||
uart_channel,
|
||||
@@ -293,21 +350,41 @@ pub unsafe fn reset_handler() {
|
||||
);
|
||||
DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
|
||||
|
||||
let process_printer =
|
||||
components::process_printer::ProcessPrinterTextComponent::new().finalize(());
|
||||
PROCESS_PRINTER = Some(process_printer);
|
||||
|
||||
// Create a shared UART channel for the console and for kernel debug.
|
||||
let uart_mux =
|
||||
components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller)
|
||||
.finalize(());
|
||||
|
||||
let pconsole =
|
||||
components::process_console::ProcessConsoleComponent::new(board_kernel, uart_mux)
|
||||
.finalize(());
|
||||
let pconsole = components::process_console::ProcessConsoleComponent::new(
|
||||
board_kernel,
|
||||
uart_mux,
|
||||
mux_alarm,
|
||||
process_printer,
|
||||
)
|
||||
.finalize(components::process_console_component_helper!(
|
||||
nrf52840::rtc::Rtc<'static>
|
||||
));
|
||||
|
||||
// Setup the console.
|
||||
let console = components::console::ConsoleComponent::new(board_kernel, uart_mux).finalize(());
|
||||
let console = components::console::ConsoleComponent::new(
|
||||
board_kernel,
|
||||
capsules::console::DRIVER_NUM,
|
||||
uart_mux,
|
||||
)
|
||||
.finalize(components::console_component_helper!());
|
||||
// Create the debugger object that handles calls to `debug!()`.
|
||||
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
|
||||
|
||||
let rng = components::rng::RngComponent::new(board_kernel, &base_peripherals.trng).finalize(());
|
||||
let rng = components::rng::RngComponent::new(
|
||||
board_kernel,
|
||||
capsules::rng::DRIVER_NUM,
|
||||
&base_peripherals.trng,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
// Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
|
||||
// These are hardcoded pin assignments specified in the driver
|
||||
@@ -317,6 +394,8 @@ pub unsafe fn reset_handler() {
|
||||
nrf52840::acomp::Channel,
|
||||
&nrf52840::acomp::CHANNEL_AC0
|
||||
),
|
||||
board_kernel,
|
||||
capsules::analog_comparator::DRIVER_NUM,
|
||||
)
|
||||
.finalize(components::acomp_component_buf!(
|
||||
nrf52840::acomp::Comparator
|
||||
@@ -326,8 +405,9 @@ pub unsafe fn reset_handler() {
|
||||
nrf52840::nvmc::SyscallDriver,
|
||||
nrf52840::nvmc::SyscallDriver::new(
|
||||
&base_peripherals.nvmc,
|
||||
board_kernel.create_grant(&memory_allocation_capability),
|
||||
board_kernel.create_grant(nrf52840::nvmc::DRIVER_NUM, &memory_allocation_capability),
|
||||
dynamic_deferred_caller,
|
||||
&mut APP_FLASH_BUFFER,
|
||||
)
|
||||
);
|
||||
nvmc.set_deferred_handle(
|
||||
@@ -339,24 +419,20 @@ pub unsafe fn reset_handler() {
|
||||
// Configure USB controller
|
||||
let usb = components::usb_ctap::UsbCtapComponent::new(
|
||||
board_kernel,
|
||||
capsules::usb::usb_ctap::DRIVER_NUM,
|
||||
&nrf52840_peripherals.usbd,
|
||||
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
|
||||
VENDOR_ID,
|
||||
PRODUCT_ID,
|
||||
STRINGS,
|
||||
)
|
||||
.finalize(components::usb_ctap_component_buf!(nrf52840::usbd::Usbd));
|
||||
|
||||
let crp = components::firmware_protection::FirmwareProtectionComponent::new(
|
||||
board_kernel,
|
||||
nrf52840::uicr::Uicr::new(),
|
||||
)
|
||||
.finalize(components::firmware_protection_component_helper!(
|
||||
nrf52840::uicr::Uicr
|
||||
));
|
||||
.finalize(components::usb_ctap_component_helper!(nrf52840::usbd::Usbd));
|
||||
|
||||
nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
|
||||
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
|
||||
let platform = Platform {
|
||||
button,
|
||||
pconsole,
|
||||
@@ -368,15 +444,20 @@ pub unsafe fn reset_handler() {
|
||||
analog_comparator,
|
||||
nvmc,
|
||||
usb,
|
||||
crp,
|
||||
ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability),
|
||||
ipc: kernel::ipc::IPC::new(
|
||||
board_kernel,
|
||||
kernel::ipc::DRIVER_NUM,
|
||||
&memory_allocation_capability,
|
||||
),
|
||||
scheduler,
|
||||
systick: cortexm4::systick::SysTick::new_with_calibration(6400_0000),
|
||||
};
|
||||
|
||||
platform.pconsole.start();
|
||||
let _ = platform.pconsole.start();
|
||||
debug!("Initialization complete. Entering main loop\r");
|
||||
debug!("{}", &nrf52840::ficr::FICR_INSTANCE);
|
||||
|
||||
/// These symbols are defined in the linker script.
|
||||
// These symbols are defined in the linker script.
|
||||
extern "C" {
|
||||
/// Beginning of the ROM region containing app images.
|
||||
static _sapps: u8;
|
||||
@@ -388,7 +469,7 @@ pub unsafe fn reset_handler() {
|
||||
static _eappmem: u8;
|
||||
}
|
||||
|
||||
kernel::procs::load_processes(
|
||||
kernel::process::load_processes(
|
||||
board_kernel,
|
||||
chip,
|
||||
core::slice::from_raw_parts(
|
||||
@@ -400,7 +481,7 @@ pub unsafe fn reset_handler() {
|
||||
&_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
|
||||
),
|
||||
&mut PROCESSES,
|
||||
FAULT_RESPONSE,
|
||||
&FAULT_RESPONSE,
|
||||
&process_management_capability,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
@@ -408,13 +489,5 @@ pub unsafe fn reset_handler() {
|
||||
debug!("{:?}", err);
|
||||
});
|
||||
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
board_kernel.kernel_loop(
|
||||
&platform,
|
||||
chip,
|
||||
Some(&platform.ipc),
|
||||
scheduler,
|
||||
&main_loop_capability,
|
||||
);
|
||||
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,15 @@
|
||||
// Disable this attribute when documenting, as a workaround for
|
||||
// https://github.com/rust-lang/rust/issues/62184.
|
||||
#![cfg_attr(not(doc), no_main)]
|
||||
#![feature(const_in_array_repeat_expressions)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use capsules::virtual_alarm::VirtualMuxAlarm;
|
||||
use kernel::component::Component;
|
||||
use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use kernel::hil::led::LedLow;
|
||||
use kernel::hil::time::Counter;
|
||||
use kernel::platform::{KernelResources, SyscallDriverLookup, SyscallFilter};
|
||||
use kernel::scheduler::round_robin::RoundRobinSched;
|
||||
#[allow(unused_imports)]
|
||||
use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
|
||||
use nrf52840::gpio::Pin;
|
||||
@@ -50,12 +52,12 @@ static STRINGS: &'static [&'static str] = &[
|
||||
|
||||
// State for loading and holding applications.
|
||||
// How should the kernel respond when a process faults.
|
||||
const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultResponse::Panic;
|
||||
const FAULT_RESPONSE: kernel::process::PanicFaultPolicy = kernel::process::PanicFaultPolicy {};
|
||||
|
||||
// Number of concurrent processes this platform supports.
|
||||
const NUM_PROCS: usize = 8;
|
||||
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; NUM_PROCS] =
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
|
||||
[None; NUM_PROCS];
|
||||
|
||||
static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
|
||||
@@ -74,6 +76,9 @@ static mut STORAGE_LOCATIONS: [kernel::StorageLocation; 2] = [
|
||||
|
||||
// Static reference to chip for panic dumps
|
||||
static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
|
||||
static mut PROCESS_PRINTER: Option<&'static kernel::process::ProcessPrinterText> = None;
|
||||
/// Flash buffer for the custom nvmc driver
|
||||
static mut APP_FLASH_BUFFER: [u8; 0x1000] = [0; 0x1000];
|
||||
|
||||
/// Dummy buffer that causes the linker to reserve enough space for the stack.
|
||||
#[no_mangle]
|
||||
@@ -85,16 +90,18 @@ pub struct Platform {
|
||||
button: &'static capsules::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
pconsole: &'static capsules::process_console::ProcessConsole<
|
||||
'static,
|
||||
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
components::process_console::Capability,
|
||||
>,
|
||||
console: &'static capsules::console::Console<'static>,
|
||||
gpio: &'static capsules::gpio::GPIO<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
led: &'static capsules::led::LedDriver<
|
||||
'static,
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
kernel::hil::led::LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
3,
|
||||
>,
|
||||
rng: &'static capsules::rng::RngDriver<'static>,
|
||||
ipc: kernel::ipc::IPC<NUM_PROCS>,
|
||||
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
|
||||
analog_comparator: &'static capsules::analog_comparator::AnalogComparator<
|
||||
'static,
|
||||
nrf52840::acomp::Comparator<'static>,
|
||||
@@ -103,6 +110,8 @@ pub struct Platform {
|
||||
'static,
|
||||
capsules::virtual_alarm::VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
>,
|
||||
scheduler: &'static RoundRobinSched<'static>,
|
||||
systick: cortexm4::systick::SysTick,
|
||||
nvmc: &'static nrf52840::nvmc::SyscallDriver,
|
||||
usb: &'static capsules::usb::usb_ctap::CtapUsbSyscallDriver<
|
||||
'static,
|
||||
@@ -111,10 +120,10 @@ pub struct Platform {
|
||||
>,
|
||||
}
|
||||
|
||||
impl kernel::Platform for Platform {
|
||||
impl SyscallDriverLookup for Platform {
|
||||
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<&dyn kernel::Driver>) -> R,
|
||||
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
|
||||
{
|
||||
match driver_num {
|
||||
capsules::console::DRIVER_NUM => f(Some(self.console)),
|
||||
@@ -130,39 +139,82 @@ impl kernel::Platform for Platform {
|
||||
_ => f(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SyscallFilter for Platform {
|
||||
fn filter_syscall(
|
||||
&self,
|
||||
process: &dyn kernel::procs::ProcessType,
|
||||
process: &dyn kernel::process::Process,
|
||||
syscall: &kernel::syscall::Syscall,
|
||||
) -> Result<(), kernel::ReturnCode> {
|
||||
) -> Result<(), kernel::errorcode::ErrorCode> {
|
||||
use kernel::syscall::Syscall;
|
||||
match *syscall {
|
||||
Syscall::COMMAND {
|
||||
Syscall::Command {
|
||||
driver_number: nrf52840::nvmc::DRIVER_NUM,
|
||||
subdriver_number: cmd,
|
||||
arg0: ptr,
|
||||
arg1: len,
|
||||
} if (cmd == 2 || cmd == 3) && !process.fits_in_storage_location(ptr, len) => {
|
||||
Err(kernel::ReturnCode::EINVAL)
|
||||
Err(kernel::ErrorCode::INVAL)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Entry point in the vector table called on hard reset.
|
||||
#[no_mangle]
|
||||
pub unsafe fn reset_handler() {
|
||||
// Loads relocations and clears BSS
|
||||
nrf52840::init();
|
||||
|
||||
let ppi = static_init!(nrf52840::ppi::Ppi, nrf52840::ppi::Ppi::new());
|
||||
/// This is in a separate, inline(never) function so that its stack frame is
|
||||
/// removed when this function returns. Otherwise, the stack space used for
|
||||
/// these static_inits is wasted.
|
||||
#[inline(never)]
|
||||
unsafe fn get_peripherals() -> &'static mut Nrf52840DefaultPeripherals<'static> {
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = static_init!(
|
||||
Nrf52840DefaultPeripherals,
|
||||
Nrf52840DefaultPeripherals::new(ppi)
|
||||
Nrf52840DefaultPeripherals::new()
|
||||
);
|
||||
nrf52840_peripherals
|
||||
}
|
||||
impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
|
||||
for Platform
|
||||
{
|
||||
type SyscallDriverLookup = Self;
|
||||
type SyscallFilter = Self;
|
||||
type ProcessFault = ();
|
||||
type Scheduler = RoundRobinSched<'static>;
|
||||
type SchedulerTimer = cortexm4::systick::SysTick;
|
||||
type WatchDog = ();
|
||||
type ContextSwitchCallback = ();
|
||||
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
|
||||
&self
|
||||
}
|
||||
fn syscall_filter(&self) -> &Self::SyscallFilter {
|
||||
&self
|
||||
}
|
||||
fn process_fault(&self) -> &Self::ProcessFault {
|
||||
&()
|
||||
}
|
||||
fn scheduler(&self) -> &Self::Scheduler {
|
||||
self.scheduler
|
||||
}
|
||||
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
|
||||
&self.systick
|
||||
}
|
||||
fn watchdog(&self) -> &Self::WatchDog {
|
||||
&()
|
||||
}
|
||||
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
/// Main function called after RAM initialized.
|
||||
#[no_mangle]
|
||||
pub unsafe fn main() {
|
||||
// Loads relocations and clears BSS
|
||||
nrf52840::init();
|
||||
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = get_peripherals();
|
||||
|
||||
// set up circular peripheral dependencies
|
||||
nrf52840_peripherals.init();
|
||||
@@ -175,6 +227,7 @@ pub unsafe fn reset_handler() {
|
||||
// GPIOs
|
||||
let gpio = components::gpio::GpioComponent::new(
|
||||
board_kernel,
|
||||
capsules::gpio::DRIVER_NUM,
|
||||
components::gpio_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
// left side of the USB plug. Right side is used for UART
|
||||
@@ -189,6 +242,7 @@ pub unsafe fn reset_handler() {
|
||||
|
||||
let button = components::button::ButtonComponent::new(
|
||||
board_kernel,
|
||||
capsules::button::DRIVER_NUM,
|
||||
components::button_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
(
|
||||
@@ -200,14 +254,11 @@ pub unsafe fn reset_handler() {
|
||||
)
|
||||
.finalize(components::button_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
|
||||
let led = components::led::LedsComponent::new(components::led_component_helper!(
|
||||
let led = components::led::LedsComponent::new().finalize(components::led_component_helper!(
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin>,
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_R_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_G_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_B_PIN]),
|
||||
))
|
||||
.finalize(components::led_component_buf!(
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin>
|
||||
));
|
||||
|
||||
let chip = static_init!(
|
||||
@@ -233,11 +284,15 @@ pub unsafe fn reset_handler() {
|
||||
);
|
||||
|
||||
let rtc = &base_peripherals.rtc;
|
||||
rtc.start();
|
||||
let _ = rtc.start();
|
||||
let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
|
||||
.finalize(components::alarm_mux_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(board_kernel, mux_alarm)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(
|
||||
board_kernel,
|
||||
capsules::alarm::DRIVER_NUM,
|
||||
mux_alarm,
|
||||
)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
let uart_channel = UartChannel::Pins(UartPins::new(UART_RTS, UART_TXD, UART_CTS, UART_RXD));
|
||||
let channel = nrf52_components::UartChannelComponent::new(
|
||||
uart_channel,
|
||||
@@ -253,22 +308,40 @@ pub unsafe fn reset_handler() {
|
||||
DynamicDeferredCall::new(dynamic_deferred_call_clients)
|
||||
);
|
||||
DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
|
||||
let process_printer =
|
||||
components::process_printer::ProcessPrinterTextComponent::new().finalize(());
|
||||
PROCESS_PRINTER = Some(process_printer);
|
||||
|
||||
// Create a shared UART channel for the console and for kernel debug.
|
||||
let uart_mux =
|
||||
components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller)
|
||||
.finalize(());
|
||||
|
||||
let pconsole =
|
||||
components::process_console::ProcessConsoleComponent::new(board_kernel, uart_mux)
|
||||
.finalize(());
|
||||
let pconsole = components::process_console::ProcessConsoleComponent::new(
|
||||
board_kernel,
|
||||
uart_mux,
|
||||
mux_alarm,
|
||||
process_printer,
|
||||
)
|
||||
.finalize(components::process_console_component_helper!(
|
||||
nrf52840::rtc::Rtc<'static>
|
||||
));
|
||||
|
||||
// Setup the console.
|
||||
let console = components::console::ConsoleComponent::new(board_kernel, uart_mux).finalize(());
|
||||
let console = components::console::ConsoleComponent::new(
|
||||
board_kernel,
|
||||
capsules::console::DRIVER_NUM,
|
||||
uart_mux,
|
||||
)
|
||||
.finalize(components::console_component_helper!());
|
||||
// Create the debugger object that handles calls to `debug!()`.
|
||||
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
|
||||
|
||||
let rng = components::rng::RngComponent::new(board_kernel, &base_peripherals.trng).finalize(());
|
||||
let rng = components::rng::RngComponent::new(
|
||||
board_kernel,
|
||||
capsules::rng::DRIVER_NUM,
|
||||
&base_peripherals.trng,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
// Initialize AC using AIN5 (P0.29) as VIN+ and VIN- as AIN0 (P0.02)
|
||||
// These are hardcoded pin assignments specified in the driver
|
||||
@@ -278,6 +351,8 @@ pub unsafe fn reset_handler() {
|
||||
nrf52840::acomp::Channel,
|
||||
&nrf52840::acomp::CHANNEL_AC0
|
||||
),
|
||||
board_kernel,
|
||||
capsules::analog_comparator::DRIVER_NUM,
|
||||
)
|
||||
.finalize(components::acomp_component_buf!(
|
||||
nrf52840::acomp::Comparator
|
||||
@@ -287,8 +362,9 @@ pub unsafe fn reset_handler() {
|
||||
nrf52840::nvmc::SyscallDriver,
|
||||
nrf52840::nvmc::SyscallDriver::new(
|
||||
&base_peripherals.nvmc,
|
||||
board_kernel.create_grant(&memory_allocation_capability),
|
||||
board_kernel.create_grant(nrf52840::nvmc::DRIVER_NUM, &memory_allocation_capability),
|
||||
dynamic_deferred_caller,
|
||||
&mut APP_FLASH_BUFFER,
|
||||
)
|
||||
);
|
||||
nvmc.set_deferred_handle(
|
||||
@@ -300,16 +376,20 @@ pub unsafe fn reset_handler() {
|
||||
// Configure USB controller
|
||||
let usb = components::usb_ctap::UsbCtapComponent::new(
|
||||
board_kernel,
|
||||
capsules::usb::usb_ctap::DRIVER_NUM,
|
||||
&nrf52840_peripherals.usbd,
|
||||
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
|
||||
VENDOR_ID,
|
||||
PRODUCT_ID,
|
||||
STRINGS,
|
||||
)
|
||||
.finalize(components::usb_ctap_component_buf!(nrf52840::usbd::Usbd));
|
||||
.finalize(components::usb_ctap_component_helper!(nrf52840::usbd::Usbd));
|
||||
|
||||
nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
|
||||
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
|
||||
let platform = Platform {
|
||||
button,
|
||||
pconsole,
|
||||
@@ -321,14 +401,20 @@ pub unsafe fn reset_handler() {
|
||||
analog_comparator,
|
||||
nvmc,
|
||||
usb,
|
||||
ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability),
|
||||
ipc: kernel::ipc::IPC::new(
|
||||
board_kernel,
|
||||
kernel::ipc::DRIVER_NUM,
|
||||
&memory_allocation_capability,
|
||||
),
|
||||
scheduler,
|
||||
systick: cortexm4::systick::SysTick::new_with_calibration(64000000),
|
||||
};
|
||||
|
||||
platform.pconsole.start();
|
||||
let _ = platform.pconsole.start();
|
||||
debug!("Initialization complete. Entering main loop\r");
|
||||
debug!("{}", &nrf52840::ficr::FICR_INSTANCE);
|
||||
|
||||
/// These symbols are defined in the linker script.
|
||||
// These symbols are defined in the linker script.
|
||||
extern "C" {
|
||||
/// Beginning of the ROM region containing app images.
|
||||
static _sapps: u8;
|
||||
@@ -340,7 +426,7 @@ pub unsafe fn reset_handler() {
|
||||
static _eappmem: u8;
|
||||
}
|
||||
|
||||
kernel::procs::load_processes(
|
||||
kernel::process::load_processes(
|
||||
board_kernel,
|
||||
chip,
|
||||
core::slice::from_raw_parts(
|
||||
@@ -352,7 +438,7 @@ pub unsafe fn reset_handler() {
|
||||
&_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
|
||||
),
|
||||
&mut PROCESSES,
|
||||
FAULT_RESPONSE,
|
||||
&FAULT_RESPONSE,
|
||||
&process_management_capability,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
@@ -360,13 +446,5 @@ pub unsafe fn reset_handler() {
|
||||
debug!("{:?}", err);
|
||||
});
|
||||
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
board_kernel.kernel_loop(
|
||||
&platform,
|
||||
chip,
|
||||
Some(&platform.ipc),
|
||||
scheduler,
|
||||
&main_loop_capability,
|
||||
);
|
||||
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -64,14 +64,16 @@
|
||||
// Disable this attribute when documenting, as a workaround for
|
||||
// https://github.com/rust-lang/rust/issues/62184.
|
||||
#![cfg_attr(not(doc), no_main)]
|
||||
#![feature(const_in_array_repeat_expressions)]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
use capsules::virtual_alarm::VirtualMuxAlarm;
|
||||
use core::env;
|
||||
use kernel::common::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use kernel::component::Component;
|
||||
use kernel::dynamic_deferred_call::{DynamicDeferredCall, DynamicDeferredCallClientState};
|
||||
use kernel::hil::led::LedLow;
|
||||
use kernel::hil::time::Counter;
|
||||
use kernel::platform::{KernelResources, SyscallDriverLookup, SyscallFilter};
|
||||
use kernel::scheduler::round_robin::RoundRobinSched;
|
||||
#[allow(unused_imports)]
|
||||
use kernel::{capabilities, create_capability, debug, debug_gpio, debug_verbose, static_init};
|
||||
use nrf52840::gpio::Pin;
|
||||
@@ -121,28 +123,33 @@ static STRINGS: &'static [&'static str] = &[
|
||||
|
||||
// State for loading and holding applications.
|
||||
// How should the kernel respond when a process faults.
|
||||
const FAULT_RESPONSE: kernel::procs::FaultResponse = kernel::procs::FaultResponse::Panic;
|
||||
const FAULT_RESPONSE: kernel::process::PanicFaultPolicy = kernel::process::PanicFaultPolicy {};
|
||||
|
||||
// Number of concurrent processes this platform supports.
|
||||
const NUM_PROCS: usize = 8;
|
||||
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::procs::ProcessType>; NUM_PROCS] =
|
||||
static mut PROCESSES: [Option<&'static dyn kernel::process::Process>; NUM_PROCS] =
|
||||
[None; NUM_PROCS];
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/locations.rs"));
|
||||
|
||||
static mut CHIP: Option<&'static nrf52840::chip::NRF52<Nrf52840DefaultPeripherals>> = None;
|
||||
static mut PROCESS_PRINTER: Option<&'static kernel::process::ProcessPrinterText> = None;
|
||||
|
||||
/// Flash buffer for the custom nvmc driver
|
||||
static mut APP_FLASH_BUFFER: [u8; 0x1000] = [0; 0x1000];
|
||||
|
||||
/// Dummy buffer that causes the linker to reserve enough space for the stack.
|
||||
#[no_mangle]
|
||||
#[link_section = ".stack_buffer"]
|
||||
pub static mut STACK_MEMORY: [u8; 0x1000] = [0; 0x1000];
|
||||
pub static mut STACK_MEMORY: [u8; 0x2000] = [0; 0x2000];
|
||||
|
||||
/// Supported drivers by the platform
|
||||
pub struct Platform {
|
||||
button: &'static capsules::button::Button<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
pconsole: &'static capsules::process_console::ProcessConsole<
|
||||
'static,
|
||||
VirtualMuxAlarm<'static, nrf52840::rtc::Rtc<'static>>,
|
||||
components::process_console::Capability,
|
||||
>,
|
||||
console: &'static capsules::console::Console<'static>,
|
||||
@@ -150,9 +157,10 @@ pub struct Platform {
|
||||
led: &'static capsules::led::LedDriver<
|
||||
'static,
|
||||
kernel::hil::led::LedLow<'static, nrf52840::gpio::GPIOPin<'static>>,
|
||||
4,
|
||||
>,
|
||||
rng: &'static capsules::rng::RngDriver<'static>,
|
||||
ipc: kernel::ipc::IPC<NUM_PROCS>,
|
||||
ipc: kernel::ipc::IPC<{ NUM_PROCS as u8 }>,
|
||||
analog_comparator: &'static capsules::analog_comparator::AnalogComparator<
|
||||
'static,
|
||||
nrf52840::acomp::Comparator<'static>,
|
||||
@@ -167,13 +175,14 @@ pub struct Platform {
|
||||
'static,
|
||||
nrf52840::usbd::Usbd<'static>,
|
||||
>,
|
||||
crp: &'static capsules::firmware_protection::FirmwareProtection<nrf52840::uicr::Uicr>,
|
||||
scheduler: &'static RoundRobinSched<'static>,
|
||||
systick: cortexm4::systick::SysTick,
|
||||
}
|
||||
|
||||
impl kernel::Platform for Platform {
|
||||
impl SyscallDriverLookup for Platform {
|
||||
fn with_driver<F, R>(&self, driver_num: usize, f: F) -> R
|
||||
where
|
||||
F: FnOnce(Option<&dyn kernel::Driver>) -> R,
|
||||
F: FnOnce(Option<&dyn kernel::syscall::SyscallDriver>) -> R,
|
||||
{
|
||||
match driver_num {
|
||||
capsules::console::DRIVER_NUM => f(Some(self.console)),
|
||||
@@ -185,44 +194,89 @@ impl kernel::Platform for Platform {
|
||||
capsules::analog_comparator::DRIVER_NUM => f(Some(self.analog_comparator)),
|
||||
nrf52840::nvmc::DRIVER_NUM => f(Some(self.nvmc)),
|
||||
capsules::usb::usb_ctap::DRIVER_NUM => f(Some(self.usb)),
|
||||
capsules::firmware_protection::DRIVER_NUM => f(Some(self.crp)),
|
||||
kernel::ipc::DRIVER_NUM => f(Some(&self.ipc)),
|
||||
_ => f(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SyscallFilter for Platform {
|
||||
fn filter_syscall(
|
||||
&self,
|
||||
process: &dyn kernel::procs::ProcessType,
|
||||
process: &dyn kernel::process::Process,
|
||||
syscall: &kernel::syscall::Syscall,
|
||||
) -> Result<(), kernel::ReturnCode> {
|
||||
) -> Result<(), kernel::errorcode::ErrorCode> {
|
||||
use kernel::syscall::Syscall;
|
||||
match *syscall {
|
||||
Syscall::COMMAND {
|
||||
Syscall::Command {
|
||||
driver_number: nrf52840::nvmc::DRIVER_NUM,
|
||||
subdriver_number: cmd,
|
||||
arg0: ptr,
|
||||
arg1: len,
|
||||
} if (cmd == 2 || cmd == 3) && !process.fits_in_storage_location(ptr, len) => {
|
||||
Err(kernel::ReturnCode::EINVAL)
|
||||
Err(kernel::ErrorCode::INVAL)
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Entry point in the vector table called on hard reset.
|
||||
#[no_mangle]
|
||||
pub unsafe fn reset_handler() {
|
||||
// Loads relocations and clears BSS
|
||||
nrf52840::init();
|
||||
let ppi = static_init!(nrf52840::ppi::Ppi, nrf52840::ppi::Ppi::new());
|
||||
/// This is in a separate, inline(never) function so that its stack frame is
|
||||
/// removed when this function returns. Otherwise, the stack space used for
|
||||
/// these static_inits is wasted.
|
||||
#[inline(never)]
|
||||
unsafe fn get_peripherals() -> &'static mut Nrf52840DefaultPeripherals<'static> {
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = static_init!(
|
||||
Nrf52840DefaultPeripherals,
|
||||
Nrf52840DefaultPeripherals::new(ppi)
|
||||
Nrf52840DefaultPeripherals::new()
|
||||
);
|
||||
|
||||
nrf52840_peripherals
|
||||
}
|
||||
|
||||
impl KernelResources<nrf52840::chip::NRF52<'static, Nrf52840DefaultPeripherals<'static>>>
|
||||
for Platform
|
||||
{
|
||||
type SyscallDriverLookup = Self;
|
||||
type SyscallFilter = Self;
|
||||
type ProcessFault = ();
|
||||
type Scheduler = RoundRobinSched<'static>;
|
||||
type SchedulerTimer = cortexm4::systick::SysTick;
|
||||
type WatchDog = ();
|
||||
type ContextSwitchCallback = ();
|
||||
|
||||
fn syscall_driver_lookup(&self) -> &Self::SyscallDriverLookup {
|
||||
&self
|
||||
}
|
||||
fn syscall_filter(&self) -> &Self::SyscallFilter {
|
||||
&self
|
||||
}
|
||||
fn process_fault(&self) -> &Self::ProcessFault {
|
||||
&()
|
||||
}
|
||||
fn scheduler(&self) -> &Self::Scheduler {
|
||||
self.scheduler
|
||||
}
|
||||
fn scheduler_timer(&self) -> &Self::SchedulerTimer {
|
||||
&self.systick
|
||||
}
|
||||
fn watchdog(&self) -> &Self::WatchDog {
|
||||
&()
|
||||
}
|
||||
fn context_switch_callback(&self) -> &Self::ContextSwitchCallback {
|
||||
&()
|
||||
}
|
||||
}
|
||||
|
||||
/// Main function called after RAM initialized.
|
||||
#[no_mangle]
|
||||
pub unsafe fn main() {
|
||||
// Loads relocations and clears BSS
|
||||
nrf52840::init();
|
||||
// Initialize chip peripheral drivers
|
||||
let nrf52840_peripherals = get_peripherals();
|
||||
|
||||
// set up circular peripheral dependencies
|
||||
nrf52840_peripherals.init();
|
||||
let base_peripherals = &nrf52840_peripherals.nrf52;
|
||||
@@ -249,6 +303,7 @@ pub unsafe fn reset_handler() {
|
||||
|
||||
let gpio = components::gpio::GpioComponent::new(
|
||||
board_kernel,
|
||||
capsules::gpio::DRIVER_NUM,
|
||||
components::gpio_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
0 => &nrf52840_peripherals.gpio_port[Pin::P1_01],
|
||||
@@ -273,6 +328,7 @@ pub unsafe fn reset_handler() {
|
||||
|
||||
let button = components::button::ButtonComponent::new(
|
||||
board_kernel,
|
||||
capsules::button::DRIVER_NUM,
|
||||
components::button_component_helper!(
|
||||
nrf52840::gpio::GPIOPin,
|
||||
(
|
||||
@@ -299,15 +355,12 @@ pub unsafe fn reset_handler() {
|
||||
)
|
||||
.finalize(components::button_component_buf!(nrf52840::gpio::GPIOPin));
|
||||
|
||||
let led = components::led::LedsComponent::new(components::led_component_helper!(
|
||||
let led = components::led::LedsComponent::new().finalize(components::led_component_helper!(
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin>,
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED1_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED2_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED3_PIN]),
|
||||
LedLow::new(&nrf52840_peripherals.gpio_port[LED4_PIN]),
|
||||
))
|
||||
.finalize(components::led_component_buf!(
|
||||
LedLow<'static, nrf52840::gpio::GPIOPin>
|
||||
));
|
||||
|
||||
let chip = static_init!(
|
||||
@@ -339,11 +392,15 @@ pub unsafe fn reset_handler() {
|
||||
);
|
||||
|
||||
let rtc = &base_peripherals.rtc;
|
||||
rtc.start();
|
||||
let _ = rtc.start();
|
||||
let mux_alarm = components::alarm::AlarmMuxComponent::new(rtc)
|
||||
.finalize(components::alarm_mux_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(board_kernel, mux_alarm)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
let alarm = components::alarm::AlarmDriverComponent::new(
|
||||
board_kernel,
|
||||
capsules::alarm::DRIVER_NUM,
|
||||
mux_alarm,
|
||||
)
|
||||
.finalize(components::alarm_component_helper!(nrf52840::rtc::Rtc));
|
||||
|
||||
let channel = nrf52_components::UartChannelComponent::new(
|
||||
uart_channel,
|
||||
@@ -359,22 +416,41 @@ pub unsafe fn reset_handler() {
|
||||
DynamicDeferredCall::new(dynamic_deferred_call_clients)
|
||||
);
|
||||
DynamicDeferredCall::set_global_instance(dynamic_deferred_caller);
|
||||
let process_printer =
|
||||
components::process_printer::ProcessPrinterTextComponent::new().finalize(());
|
||||
PROCESS_PRINTER = Some(process_printer);
|
||||
|
||||
// Create a shared UART channel for the console and for kernel debug.
|
||||
let uart_mux =
|
||||
components::console::UartMuxComponent::new(channel, 115200, dynamic_deferred_caller)
|
||||
.finalize(());
|
||||
|
||||
let pconsole =
|
||||
components::process_console::ProcessConsoleComponent::new(board_kernel, uart_mux)
|
||||
.finalize(());
|
||||
let pconsole = components::process_console::ProcessConsoleComponent::new(
|
||||
board_kernel,
|
||||
uart_mux,
|
||||
mux_alarm,
|
||||
process_printer,
|
||||
)
|
||||
.finalize(components::process_console_component_helper!(
|
||||
nrf52840::rtc::Rtc<'static>
|
||||
));
|
||||
|
||||
// Setup the console.
|
||||
let console = components::console::ConsoleComponent::new(board_kernel, uart_mux).finalize(());
|
||||
let console = components::console::ConsoleComponent::new(
|
||||
board_kernel,
|
||||
capsules::console::DRIVER_NUM,
|
||||
uart_mux,
|
||||
)
|
||||
.finalize(components::console_component_helper!());
|
||||
// Create the debugger object that handles calls to `debug!()`.
|
||||
components::debug_writer::DebugWriterComponent::new(uart_mux).finalize(());
|
||||
|
||||
let rng = components::rng::RngComponent::new(board_kernel, &base_peripherals.trng).finalize(());
|
||||
let rng = components::rng::RngComponent::new(
|
||||
board_kernel,
|
||||
capsules::rng::DRIVER_NUM,
|
||||
&base_peripherals.trng,
|
||||
)
|
||||
.finalize(());
|
||||
|
||||
base_peripherals.spim0.configure(
|
||||
nrf52840::pinmux::Pinmux::new(SPI_MOSI as u32),
|
||||
@@ -390,6 +466,8 @@ pub unsafe fn reset_handler() {
|
||||
nrf52840::acomp::Channel,
|
||||
&nrf52840::acomp::CHANNEL_AC0
|
||||
),
|
||||
board_kernel,
|
||||
capsules::analog_comparator::DRIVER_NUM,
|
||||
)
|
||||
.finalize(components::acomp_component_buf!(
|
||||
nrf52840::acomp::Comparator
|
||||
@@ -399,8 +477,9 @@ pub unsafe fn reset_handler() {
|
||||
nrf52840::nvmc::SyscallDriver,
|
||||
nrf52840::nvmc::SyscallDriver::new(
|
||||
&base_peripherals.nvmc,
|
||||
board_kernel.create_grant(&memory_allocation_capability),
|
||||
board_kernel.create_grant(nrf52840::nvmc::DRIVER_NUM, &memory_allocation_capability),
|
||||
dynamic_deferred_caller,
|
||||
&mut APP_FLASH_BUFFER,
|
||||
)
|
||||
);
|
||||
nvmc.set_deferred_handle(
|
||||
@@ -412,21 +491,17 @@ pub unsafe fn reset_handler() {
|
||||
// Configure USB controller
|
||||
let usb = components::usb_ctap::UsbCtapComponent::new(
|
||||
board_kernel,
|
||||
capsules::usb::usb_ctap::DRIVER_NUM,
|
||||
&nrf52840_peripherals.usbd,
|
||||
capsules::usb::usbc_client::MAX_CTRL_PACKET_SIZE_NRF52840,
|
||||
VENDOR_ID,
|
||||
PRODUCT_ID,
|
||||
STRINGS,
|
||||
)
|
||||
.finalize(components::usb_ctap_component_buf!(nrf52840::usbd::Usbd));
|
||||
.finalize(components::usb_ctap_component_helper!(nrf52840::usbd::Usbd));
|
||||
|
||||
let crp = components::firmware_protection::FirmwareProtectionComponent::new(
|
||||
board_kernel,
|
||||
nrf52840::uicr::Uicr::new(),
|
||||
)
|
||||
.finalize(components::firmware_protection_component_helper!(
|
||||
nrf52840::uicr::Uicr
|
||||
));
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
|
||||
nrf52_components::NrfClockComponent::new(&base_peripherals.clock).finalize(());
|
||||
|
||||
@@ -441,15 +516,20 @@ pub unsafe fn reset_handler() {
|
||||
analog_comparator,
|
||||
nvmc,
|
||||
usb,
|
||||
crp,
|
||||
ipc: kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability),
|
||||
ipc: kernel::ipc::IPC::new(
|
||||
board_kernel,
|
||||
kernel::ipc::DRIVER_NUM,
|
||||
&memory_allocation_capability,
|
||||
),
|
||||
scheduler,
|
||||
systick: cortexm4::systick::SysTick::new_with_calibration(64000000),
|
||||
};
|
||||
|
||||
platform.pconsole.start();
|
||||
let _ = platform.pconsole.start();
|
||||
debug!("Initialization complete. Entering main loop\r");
|
||||
debug!("{}", &nrf52840::ficr::FICR_INSTANCE);
|
||||
|
||||
/// These symbols are defined in the linker script.
|
||||
// These symbols are defined in the linker script.
|
||||
extern "C" {
|
||||
/// Beginning of the ROM region containing app images.
|
||||
static _sapps: u8;
|
||||
@@ -461,7 +541,7 @@ pub unsafe fn reset_handler() {
|
||||
static _eappmem: u8;
|
||||
}
|
||||
|
||||
kernel::procs::load_processes(
|
||||
kernel::process::load_processes(
|
||||
board_kernel,
|
||||
chip,
|
||||
core::slice::from_raw_parts(
|
||||
@@ -473,7 +553,7 @@ pub unsafe fn reset_handler() {
|
||||
&_eappmem as *const u8 as usize - &_sappmem as *const u8 as usize,
|
||||
),
|
||||
&mut PROCESSES,
|
||||
FAULT_RESPONSE,
|
||||
&FAULT_RESPONSE,
|
||||
&process_management_capability,
|
||||
)
|
||||
.unwrap_or_else(|err| {
|
||||
@@ -481,13 +561,5 @@ pub unsafe fn reset_handler() {
|
||||
debug!("{:?}", err);
|
||||
});
|
||||
|
||||
let scheduler = components::sched::round_robin::RoundRobinComponent::new(&PROCESSES)
|
||||
.finalize(components::rr_component_helper!(NUM_PROCS));
|
||||
board_kernel.kernel_loop(
|
||||
&platform,
|
||||
chip,
|
||||
Some(&platform.ipc),
|
||||
scheduler,
|
||||
&main_loop_capability,
|
||||
);
|
||||
board_kernel.kernel_loop(&platform, chip, Some(&platform.ipc), &main_loop_capability);
|
||||
}
|
||||
|
||||
4
bootloader/Cargo.lock
generated
4
bootloader/Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2021-03-25"
|
||||
channel = "nightly-2023-02-01"
|
||||
components = ["clippy", "rustfmt"]
|
||||
targets = ["thumbv7em-none-eabi"]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -40,7 +40,7 @@ impl<T> Copy for StaticRef<T> {}
|
||||
|
||||
impl<T> Deref for StaticRef<T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &'static T {
|
||||
fn deref(&self) -> &T {
|
||||
unsafe { &*self.ptr }
|
||||
}
|
||||
}
|
||||
|
||||
6
build.rs
6
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();
|
||||
}
|
||||
|
||||
29
deploy.py
29
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",
|
||||
|
||||
@@ -12,24 +12,32 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate lang_items;
|
||||
|
||||
use libtock_drivers::console::{Console, BUFFER_SIZE};
|
||||
use libtock_console::Console;
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
|
||||
libtock_core::stack_size! {0x800}
|
||||
stack_size! {0x800}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn main() {
|
||||
// Write messages of length up to the console driver's buffer size.
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut buf = [0; 1024];
|
||||
loop {
|
||||
for i in 1..buf.len() {
|
||||
for byte in buf.iter_mut().take(i) {
|
||||
*byte = b'0' + ((i % 10) as u8);
|
||||
}
|
||||
buf[i] = b'\n';
|
||||
Console::write_unbuffered(&mut buf[..(i + 1)]);
|
||||
Console::<Syscalls>::write(&buf[..(i + 1)])
|
||||
.map_err(|e| e.into())
|
||||
.flex_unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -22,29 +23,28 @@ use alloc::vec::Vec;
|
||||
use core::fmt::Write;
|
||||
use crypto::{aes256, cbc, ecdsa, sha256, Hash256};
|
||||
use ctap2::env::tock::TockRng;
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::{Console, ConsoleWriter};
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_drivers::timer;
|
||||
use libtock_drivers::timer::{Timer, Timestamp};
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
|
||||
libtock_core::stack_size! {0x800}
|
||||
stack_size! {0x2000}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn main() {
|
||||
let mut console = Console::new();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
// Setup the timer with a dummy callback (we only care about reading the current time, but the
|
||||
// API forces us to set an alarm callback too).
|
||||
let mut with_callback = timer::with_callback(|_, _| {});
|
||||
let mut with_callback = timer::with_callback(|_| {});
|
||||
let timer = with_callback.init().flex_unwrap();
|
||||
|
||||
let mut rng = TockRng {};
|
||||
let mut rng = TockRng::<Syscalls>::default();
|
||||
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
writeln!(
|
||||
console,
|
||||
"Clock frequency: {} Hz",
|
||||
timer.clock_frequency().hz()
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(console, "Clock frequency: {:?} Hz", timer.clock_frequency()).unwrap();
|
||||
|
||||
// AES
|
||||
bench(&mut console, &timer, "aes256::EncryptionKey::new", || {
|
||||
@@ -150,7 +150,7 @@ fn main() {
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
}
|
||||
|
||||
fn bench<F>(console: &mut Console, timer: &Timer, title: &str, mut f: F)
|
||||
fn bench<F>(console: &mut ConsoleWriter<Syscalls>, timer: &Timer<Syscalls>, title: &str, mut f: F)
|
||||
where
|
||||
F: FnMut(),
|
||||
{
|
||||
@@ -159,11 +159,13 @@ where
|
||||
writeln!(console, "----------------------------------------").unwrap();
|
||||
let mut count = 1;
|
||||
for _ in 0..30 {
|
||||
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||
let start =
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
|
||||
for _ in 0..count {
|
||||
f();
|
||||
}
|
||||
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||
let end =
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
|
||||
let elapsed = (end - start).ms();
|
||||
writeln!(
|
||||
console,
|
||||
@@ -173,7 +175,6 @@ where
|
||||
elapsed / (count as f64)
|
||||
)
|
||||
.unwrap();
|
||||
console.flush();
|
||||
if elapsed > 1000.0 {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -12,18 +12,25 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate lang_items;
|
||||
|
||||
use core::fmt::Write;
|
||||
use ctap2::env::tock::take_storage;
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_drivers::led;
|
||||
use libtock_console::Console;
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_leds::Leds;
|
||||
use libtock_platform as platform;
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
use persistent_store::{Storage, StorageIndex};
|
||||
use platform::DefaultConfig;
|
||||
|
||||
libtock_core::stack_size! {0x800}
|
||||
stack_size! {0x800}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
|
||||
let index = StorageIndex { page, byte: 0 };
|
||||
@@ -36,20 +43,21 @@ fn is_page_erased(storage: &dyn Storage, page: usize) -> bool {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
led::get(1).flex_unwrap().on().flex_unwrap(); // red on dongle
|
||||
let mut storage = take_storage().unwrap();
|
||||
Leds::<Syscalls>::on(1).map_err(|e| e.into()).flex_unwrap(); // red on dongle
|
||||
let mut storage = take_storage::<Syscalls, DefaultConfig>().unwrap();
|
||||
let num_pages = storage.num_pages();
|
||||
writeln!(Console::new(), "Erase {} pages of storage:", num_pages).unwrap();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
writeln!(console, "Erase {} pages of storage:", num_pages).unwrap();
|
||||
for page in 0..num_pages {
|
||||
write!(Console::new(), "- Page {} ", page).unwrap();
|
||||
write!(console, "- Page {} ", page).unwrap();
|
||||
if is_page_erased(&storage, page) {
|
||||
writeln!(Console::new(), "skipped (was already erased).").unwrap();
|
||||
writeln!(console, "skipped (was already erased).").unwrap();
|
||||
} else {
|
||||
storage.erase_page(page).unwrap();
|
||||
writeln!(Console::new(), "erased.").unwrap();
|
||||
writeln!(console, "erased.").unwrap();
|
||||
}
|
||||
}
|
||||
writeln!(Console::new(), "Done.").unwrap();
|
||||
led::get(1).flex_unwrap().off().flex_unwrap();
|
||||
led::get(0).flex_unwrap().on().flex_unwrap(); // green on dongle
|
||||
writeln!(console, "Done.").unwrap();
|
||||
Leds::<Syscalls>::on(1).map_err(|e| e.into()).flex_unwrap();
|
||||
Leds::<Syscalls>::off(0).map_err(|e| e.into()).flex_unwrap(); // green on dongle
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -5,27 +6,32 @@ extern crate lang_items;
|
||||
extern crate libtock_drivers;
|
||||
|
||||
use core::fmt::Write;
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::{Console, ConsoleWriter};
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
|
||||
libtock_core::stack_size! {0x4000}
|
||||
stack_size! {0x4000}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
#[cfg(not(feature = "with_nfc"))]
|
||||
mod example {
|
||||
use super::{Console, Write};
|
||||
use super::{ConsoleWriter, Syscalls, Write};
|
||||
|
||||
pub fn nfc(console: &mut Console) {
|
||||
pub fn nfc(console: &mut ConsoleWriter<Syscalls>) {
|
||||
writeln!(console, "NFC feature flag is missing!").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "with_nfc")]
|
||||
mod example {
|
||||
use super::{Console, Write};
|
||||
use libtock_core::result::CommandError;
|
||||
use super::{Console, ConsoleWriter, Write};
|
||||
use crate::Syscalls;
|
||||
use libtock_drivers::nfc::{NfcTag, RecvOp};
|
||||
use libtock_drivers::result::{FlexUnwrap, TockError};
|
||||
use libtock_drivers::timer;
|
||||
use libtock_drivers::timer::{Timer, Timestamp};
|
||||
use libtock_platform::{DefaultConfig, ErrorCode};
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
@@ -48,16 +54,15 @@ mod example {
|
||||
ENOSUPPORT,
|
||||
}
|
||||
|
||||
impl From<isize> for ReturnCode {
|
||||
fn from(original: isize) -> ReturnCode {
|
||||
impl From<ErrorCode> for ReturnCode {
|
||||
fn from(original: ErrorCode) -> ReturnCode {
|
||||
match original {
|
||||
0 => ReturnCode::SUCCESS,
|
||||
-1 => ReturnCode::FAIL,
|
||||
-2 => ReturnCode::EBUSY,
|
||||
-4 => ReturnCode::EOFF,
|
||||
-6 => ReturnCode::EINVAL,
|
||||
-8 => ReturnCode::ECANCEL,
|
||||
-9 => ReturnCode::ENOMEM,
|
||||
ErrorCode::Fail => ReturnCode::FAIL,
|
||||
ErrorCode::Busy => ReturnCode::EBUSY,
|
||||
ErrorCode::Off => ReturnCode::EOFF,
|
||||
ErrorCode::Invalid => ReturnCode::EINVAL,
|
||||
ErrorCode::Cancel => ReturnCode::ECANCEL,
|
||||
ErrorCode::NoMem => ReturnCode::ENOMEM,
|
||||
_ => ReturnCode::ENOSUPPORT,
|
||||
}
|
||||
}
|
||||
@@ -66,34 +71,32 @@ mod example {
|
||||
/// Helper function to write on console the received packet.
|
||||
fn print_rx_buffer(buf: &mut [u8]) {
|
||||
if let Some((last, bytes)) = buf.split_last() {
|
||||
let mut console = Console::new();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
write!(console, "RX:").unwrap();
|
||||
for byte in bytes {
|
||||
write!(console, " {:02x?}", byte).unwrap();
|
||||
}
|
||||
writeln!(console, " {:02x?}", last).unwrap();
|
||||
console.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/// Function to identify the time elapsed for a transmission request.
|
||||
fn bench_transmit(
|
||||
console: &mut Console,
|
||||
timer: &Timer,
|
||||
console: &mut ConsoleWriter<Syscalls>,
|
||||
timer: &Timer<Syscalls>,
|
||||
title: &str,
|
||||
mut buf: &mut [u8],
|
||||
buf: &mut [u8],
|
||||
) -> ReturnCode {
|
||||
let amount = buf.len();
|
||||
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||
match NfcTag::transmit(&mut buf, amount) {
|
||||
let start =
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
|
||||
match NfcTag::<Syscalls, DefaultConfig>::transmit(buf, amount as u32) {
|
||||
Ok(_) => (),
|
||||
Err(TockError::Command(CommandError {
|
||||
return_code: -8, /* ECANCEL: No Field*/
|
||||
..
|
||||
})) => return ReturnCode::ECANCEL,
|
||||
Err(_) => writeln!(Console::new(), " -- tx error!").unwrap(),
|
||||
Err(TockError::Command(ErrorCode::Cancel)) => return ReturnCode::ECANCEL,
|
||||
Err(_) => writeln!(console, " -- tx error!").unwrap(),
|
||||
}
|
||||
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
|
||||
let end =
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().flex_unwrap());
|
||||
let elapsed = (end - start).ms();
|
||||
writeln!(
|
||||
console,
|
||||
@@ -104,21 +107,21 @@ mod example {
|
||||
(amount as f64) / elapsed * 8.
|
||||
)
|
||||
.unwrap();
|
||||
console.flush();
|
||||
|
||||
ReturnCode::SUCCESS
|
||||
}
|
||||
|
||||
fn receive_packet(console: &mut Console, mut buf: &mut [u8; 256]) -> ReturnCode {
|
||||
match NfcTag::receive(&mut buf) {
|
||||
fn receive_packet(console: &mut ConsoleWriter<Syscalls>, buf: &mut [u8; 256]) -> ReturnCode {
|
||||
match NfcTag::<Syscalls, DefaultConfig>::receive(buf) {
|
||||
Ok(RecvOp {
|
||||
recv_amount: amount,
|
||||
..
|
||||
}) => {
|
||||
if amount <= buf.len() {
|
||||
print_rx_buffer(&mut buf[..amount]);
|
||||
if amount <= buf.len() as u32 {
|
||||
print_rx_buffer(&mut buf[..amount as usize]);
|
||||
}
|
||||
}
|
||||
Err(TockError::Command(CommandError { return_code, .. })) => return return_code.into(),
|
||||
Err(TockError::Command(code)) => return code.into(),
|
||||
Err(_) => {
|
||||
writeln!(console, " -- RX Err").unwrap();
|
||||
return ReturnCode::ECANCEL;
|
||||
@@ -127,54 +130,58 @@ mod example {
|
||||
ReturnCode::SUCCESS
|
||||
}
|
||||
|
||||
fn transmit_reply(mut console: &mut Console, timer: &Timer, buf: &[u8]) -> ReturnCode {
|
||||
fn transmit_reply(
|
||||
console: &mut ConsoleWriter<Syscalls>,
|
||||
timer: &Timer<Syscalls>,
|
||||
buf: &[u8],
|
||||
) -> ReturnCode {
|
||||
let mut return_code = ReturnCode::SUCCESS;
|
||||
match buf[0] {
|
||||
0xe0 /* RATS */=> {
|
||||
let mut answer_to_select = [0x05, 0x78, 0x80, 0xB1, 0x00];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: ATS", &mut answer_to_select);
|
||||
return_code = bench_transmit(console, timer, "TX: ATS", &mut answer_to_select);
|
||||
}
|
||||
0xc2 /* DESELECT */ => {
|
||||
// Ignore the request
|
||||
let mut command_error = [0x6A, 0x81];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: DESELECT", &mut command_error);
|
||||
return_code = bench_transmit(console, timer, "TX: DESELECT", &mut command_error);
|
||||
}
|
||||
0x02 | 0x03 /* APDU Prefix */ => match buf[2] {
|
||||
// If the received packet is applet selection command (FIDO 2)
|
||||
0xa4 /* SELECT */ => if buf[3] == 0x04 && buf[5] == 0x08 && buf[6] == 0xa0 {
|
||||
// Vesion: "FIDO_2_0"
|
||||
let mut reply = [buf[0], 0x46, 0x49, 0x44, 0x4f, 0x5f, 0x32, 0x5f, 0x30, 0x90, 0x00,];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: Version Str", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: Version Str", &mut reply);
|
||||
} else if (buf[6] == 0xd2 && buf[7] == 0x76) || (buf[6] == 0xe1 && (buf[7] == 0x03 || buf[7] == 0x04)){
|
||||
let mut reply = [buf[0], 0x90, 0x00];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
|
||||
} else /* Unknown file */ {
|
||||
let mut reply = [buf[0], 0x6a, 0x82];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x6A82", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: 0x6A82", &mut reply);
|
||||
}
|
||||
0xb0 /* READ */ => match buf[5] {
|
||||
0x02 => {
|
||||
let mut reply = [buf[0], 0x12, 0x90, 0x00,];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: File Size", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: File Size", &mut reply);
|
||||
}
|
||||
0x12 => {
|
||||
let mut reply = [buf[0], 0xd1, 0x01, 0x0e, 0x55, 0x77, 0x77, 0x77, 0x2e, 0x6f, 0x70, 0x65,
|
||||
0x6e, 0x73, 0x6b, 0x2e, 0x64, 0x65, 0x76, 0x90, 0x00,];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: NDEF", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: NDEF", &mut reply);
|
||||
}
|
||||
0x0f => {
|
||||
let mut reply = [buf[0], 0x00, 0x0f, 0x20, 0x00, 0x7f, 0x00, 0x7f, 0x04, 0x06, 0xe1, 0x04,
|
||||
0x00, 0x7f, 0x00, 0x00, 0x90, 0x00,];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: CC", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: CC", &mut reply);
|
||||
}
|
||||
_ => {
|
||||
let mut reply = [buf[0], 0x90, 0x00];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let mut reply = [buf[0], 0x90, 0x00];
|
||||
return_code = bench_transmit(&mut console, &timer, "TX: 0x9000", &mut reply);
|
||||
return_code = bench_transmit(console, timer, "TX: 0x9000", &mut reply);
|
||||
}
|
||||
}
|
||||
0x26 | 0x52 | 0x50 /* REQA | WUPA | Halt */ => {
|
||||
@@ -185,31 +192,27 @@ mod example {
|
||||
return_code
|
||||
}
|
||||
|
||||
pub fn nfc(mut console: &mut Console) {
|
||||
pub fn nfc(console: &mut ConsoleWriter<Syscalls>) {
|
||||
// Setup the timer with a dummy callback (we only care about reading the current time, but the
|
||||
// API forces us to set an alarm callback too).
|
||||
let mut with_callback = timer::with_callback(|_, _| {});
|
||||
let mut with_callback = timer::with_callback(|_| {});
|
||||
|
||||
let timer = with_callback.init().flex_unwrap();
|
||||
|
||||
writeln!(
|
||||
console,
|
||||
"Clock frequency: {} Hz",
|
||||
timer.clock_frequency().hz()
|
||||
)
|
||||
.unwrap();
|
||||
writeln!(console, "Clock frequency: {:?} Hz", timer.clock_frequency()).unwrap();
|
||||
|
||||
let mut state_change_counter = 0;
|
||||
loop {
|
||||
let mut rx_buf = [0; 256];
|
||||
match receive_packet(&mut console, &mut rx_buf) {
|
||||
match receive_packet(console, &mut rx_buf) {
|
||||
ReturnCode::EOFF => {
|
||||
// Not configured
|
||||
while !NfcTag::enable_emulation() {}
|
||||
while !NfcTag::<Syscalls, DefaultConfig>::enable_emulation() {}
|
||||
// Configure Type 4 tag
|
||||
while !NfcTag::configure(4) {}
|
||||
while !NfcTag::<Syscalls, DefaultConfig>::configure(4) {}
|
||||
}
|
||||
ReturnCode::ECANCEL /* field lost */ => {
|
||||
NfcTag::disable_emulation();
|
||||
NfcTag::<Syscalls, DefaultConfig>::disable_emulation();
|
||||
}
|
||||
ReturnCode::EBUSY /* awaiting select*/ => (),
|
||||
ReturnCode::ENOMEM => {
|
||||
@@ -220,9 +223,9 @@ mod example {
|
||||
ReturnCode::ENOSUPPORT => (),
|
||||
ReturnCode::SUCCESS => {
|
||||
// If the reader restarts the communication then disable the tag.
|
||||
match transmit_reply(&mut console, &timer, &rx_buf) {
|
||||
match transmit_reply(console, &timer, &rx_buf) {
|
||||
ReturnCode::ECANCEL | ReturnCode::EOFF => {
|
||||
if NfcTag::disable_emulation() {
|
||||
if NfcTag::<Syscalls, DefaultConfig>::disable_emulation() {
|
||||
writeln!(console, " -- TAG DISABLED").unwrap();
|
||||
}
|
||||
state_change_counter += 1;
|
||||
@@ -239,7 +242,7 @@ mod example {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut console = Console::new();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
writeln!(console, "nfct_test application is installed").unwrap();
|
||||
example::nfc(&mut console);
|
||||
|
||||
@@ -12,24 +12,30 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate lang_items;
|
||||
|
||||
libtock_core::stack_size! {0x800}
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Write;
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::Console;
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
|
||||
stack_size! {0x800}
|
||||
set_main! {main}
|
||||
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn main() {
|
||||
writeln!(Console::new(), "****************************************").unwrap();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
writeln!(console, "****************************************").unwrap();
|
||||
for i in 0.. {
|
||||
writeln!(Console::new(), "Allocating {} bytes...", 1 << i).unwrap();
|
||||
writeln!(console, "Allocating {} bytes...", 1 << i).unwrap();
|
||||
let x: Vec<u8> = Vec::with_capacity(1 << i);
|
||||
writeln!(Console::new(), "Allocated!").unwrap();
|
||||
writeln!(console, "Allocated!").unwrap();
|
||||
drop(x);
|
||||
writeln!(Console::new(), "Dropped!").unwrap();
|
||||
writeln!(console, "Dropped!").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!")
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
extern crate alloc;
|
||||
@@ -22,25 +23,33 @@ use alloc::vec::Vec;
|
||||
use alloc::{format, vec};
|
||||
use core::fmt::Write;
|
||||
use ctap2::env::tock::{take_storage, Storage};
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::Console;
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_drivers::timer::{self, Duration, Timer, Timestamp};
|
||||
use persistent_store::Store;
|
||||
use libtock_platform::DefaultConfig;
|
||||
use libtock_runtime::{set_main, stack_size, TockSyscalls};
|
||||
use persistent_store::{Storage as _, Store};
|
||||
|
||||
libtock_core::stack_size! {0x2000}
|
||||
stack_size! {0x800}
|
||||
set_main! {main}
|
||||
|
||||
fn timestamp(timer: &Timer) -> Timestamp<f64> {
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_clock().ok().unwrap())
|
||||
type Syscalls = TockSyscalls;
|
||||
|
||||
fn timestamp(timer: &Timer<Syscalls>) -> Timestamp<f64> {
|
||||
Timestamp::<f64>::from_clock_value(timer.get_current_counter_ticks().ok().unwrap())
|
||||
}
|
||||
|
||||
fn measure<T>(timer: &Timer, operation: impl FnOnce() -> T) -> (T, Duration<f64>) {
|
||||
fn measure<T>(timer: &Timer<Syscalls>, operation: impl FnOnce() -> T) -> (T, Duration<f64>) {
|
||||
let before = timestamp(timer);
|
||||
let result = operation();
|
||||
let after = timestamp(timer);
|
||||
(result, after - before)
|
||||
}
|
||||
|
||||
fn boot_store(mut storage: Storage, erase: bool) -> Store<Storage> {
|
||||
use persistent_store::Storage;
|
||||
fn boot_store(
|
||||
mut storage: Storage<Syscalls, DefaultConfig>,
|
||||
erase: bool,
|
||||
) -> Store<Storage<Syscalls, DefaultConfig>> {
|
||||
let num_pages = storage.num_pages();
|
||||
if erase {
|
||||
for page in 0..num_pages {
|
||||
@@ -55,8 +64,7 @@ struct StorageConfig {
|
||||
num_pages: usize,
|
||||
}
|
||||
|
||||
fn storage_config(storage: &Storage) -> StorageConfig {
|
||||
use persistent_store::Storage;
|
||||
fn storage_config(storage: &Storage<Syscalls, DefaultConfig>) -> StorageConfig {
|
||||
StorageConfig {
|
||||
num_pages: storage.num_pages(),
|
||||
}
|
||||
@@ -73,19 +81,19 @@ struct Stat {
|
||||
}
|
||||
|
||||
fn compute_latency(
|
||||
storage: Storage,
|
||||
timer: &Timer,
|
||||
storage: Storage<Syscalls, DefaultConfig>,
|
||||
timer: &Timer<Syscalls>,
|
||||
num_pages: usize,
|
||||
key_increment: usize,
|
||||
word_length: usize,
|
||||
) -> (Storage, Stat) {
|
||||
) -> (Storage<Syscalls, DefaultConfig>, Stat) {
|
||||
let mut stat = Stat {
|
||||
key_increment,
|
||||
entry_length: word_length,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut console = Console::new();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
writeln!(
|
||||
console,
|
||||
"\nLatency for key_increment={} word_length={}.",
|
||||
@@ -155,20 +163,22 @@ fn compute_latency(
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut with_callback = timer::with_callback(|_, _| {});
|
||||
let timer = with_callback.init().ok().unwrap();
|
||||
let storage = take_storage().unwrap();
|
||||
let mut with_callback = timer::with_callback::<Syscalls, DefaultConfig, _>(|_| {});
|
||||
|
||||
let timer = with_callback.init().flex_unwrap();
|
||||
let storage = take_storage::<Syscalls, DefaultConfig>().unwrap();
|
||||
let config = storage_config(&storage);
|
||||
let mut stats = Vec::new();
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
|
||||
writeln!(Console::new(), "\nRunning 2 tests...").unwrap();
|
||||
writeln!(console, "\nRunning 2 tests...").unwrap();
|
||||
// Simulate a store full of credentials (of 50 words).
|
||||
let (storage, stat) = compute_latency(storage, &timer, config.num_pages, 1, 50);
|
||||
stats.push(stat);
|
||||
// Simulate a store full of increments of a single counter.
|
||||
let (_storage, stat) = compute_latency(storage, &timer, config.num_pages, 0, 1);
|
||||
stats.push(stat);
|
||||
writeln!(Console::new(), "\nDone.\n").unwrap();
|
||||
writeln!(console, "\nDone.\n").unwrap();
|
||||
|
||||
const HEADERS: &[&str] = &[
|
||||
"Overwrite",
|
||||
@@ -189,8 +199,8 @@ fn main() {
|
||||
format!("{:.1} ms", stat.remove_ms),
|
||||
]);
|
||||
}
|
||||
writeln!(Console::new(), "Copy to examples/store_latency.rs:\n").unwrap();
|
||||
writeln!(Console::new(), "{:?}", config).unwrap();
|
||||
writeln!(console, "Copy to examples/store_latency.rs:\n").unwrap();
|
||||
writeln!(console, "{:?}", config).unwrap();
|
||||
write_matrix(matrix);
|
||||
|
||||
// Results for nrf52840dk_opensk:
|
||||
@@ -201,10 +211,11 @@ fn main() {
|
||||
}
|
||||
|
||||
fn align(x: &str, n: usize) {
|
||||
let mut console = Console::<Syscalls>::writer();
|
||||
for _ in 0..n.saturating_sub(x.len()) {
|
||||
write!(Console::new(), " ").unwrap();
|
||||
write!(console, " ").unwrap();
|
||||
}
|
||||
write!(Console::new(), "{}", x).unwrap();
|
||||
write!(console, "{}", x).unwrap();
|
||||
}
|
||||
|
||||
fn write_matrix(mut m: Vec<Vec<String>>) {
|
||||
@@ -223,6 +234,6 @@ fn write_matrix(mut m: Vec<Vec<String>>) {
|
||||
for col in 0..num_cols {
|
||||
align(&row[col], col_len[col] + 2 * (col > 0) as usize);
|
||||
}
|
||||
writeln!(Console::new()).unwrap();
|
||||
writeln!(Console::<Syscalls>::writer()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
158
layout.ld
158
layout.ld
@@ -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")
|
||||
@@ -220,7 +220,7 @@ fn send_keepalive_up_needed<E: Env>(
|
||||
match ctap_hid_connection.send_and_maybe_recv(&mut pkt, timeout_ms) {
|
||||
Ok(SendOrRecvStatus::Timeout) => {
|
||||
debug_ctap!(env, "Sending a KEEPALIVE packet timed out");
|
||||
// TODO: abort user presence test?
|
||||
// The client is likely unresponsive, but let's retry.
|
||||
}
|
||||
Err(_) => panic!("Error sending KEEPALIVE packet"),
|
||||
Ok(SendOrRecvStatus::Sent) => {
|
||||
|
||||
137
libtock_layout.ld
Normal file
137
libtock_layout.ld
Normal file
@@ -0,0 +1,137 @@
|
||||
/* Layout file for Tock process binaries that use libtock-rs. This currently
|
||||
* implements static linking, because we do not have a working
|
||||
* position-independent relocation solution. This layout works for all
|
||||
* platforms libtock-rs supports (ARM and RISC-V).
|
||||
*
|
||||
* This layout should be included by a script that defines the FLASH and RAM
|
||||
* regions for the board as well as TBF_HEADER_SIZE. Here is a an example
|
||||
* process binary linker script to get started:
|
||||
* MEMORY {
|
||||
* FLASH (X) : ORIGIN = 0x10000, LENGTH = 0x10000
|
||||
* RAM (W) : ORIGIN = 0x20000, LENGTH = 0x10000
|
||||
* }
|
||||
* TBF_HEADER_SIZE = 0x60;
|
||||
* INCLUDE ../libtock-rs/layout.ld
|
||||
*
|
||||
* FLASH refers to the area the process binary occupies in flash, including TBF
|
||||
* headers. RAM refers to the area the process will have access to in memory.
|
||||
* STACK_SIZE is the size of the process' stack (this layout file may round the
|
||||
* stack size up for alignment purposes). TBF_HEADER_SIZE must correspond to the
|
||||
* --protected-region-size flag passed to elf2tab.
|
||||
*
|
||||
* This places the flash sections in the following order:
|
||||
* 1. .rt_header -- Constants used by runtime initialization.
|
||||
* 2. .text -- Executable code.
|
||||
* 3. .rodata -- Read-only global data (e.g. most string constants).
|
||||
* 4. .data -- Read-write data, copied to RAM at runtime.
|
||||
*
|
||||
* This places the RAM sections in the following order:
|
||||
* 1. .stack -- The stack grows downward. Putting it first gives us
|
||||
* MPU-based overflow detection.
|
||||
* 2. .data -- Read-write data, initialized by copying from flash.
|
||||
* 3. .bss -- Zero-initialized read-write global data.
|
||||
* 4. Heap -- The heap (optional) comes after .bss and grows upwards to
|
||||
* the process break.
|
||||
*/
|
||||
|
||||
/* TODO: Should TBF_HEADER_SIZE be configured via a similar mechanism to the
|
||||
* stack size? We should see if that is possible.
|
||||
*/
|
||||
|
||||
/* GNU LD looks for `start` as an entry point by default, while LLVM's LLD looks
|
||||
* for `_start`. To be compatible with both, we manually specify an entry point.
|
||||
*/
|
||||
ENTRY(start)
|
||||
|
||||
SECTIONS {
|
||||
/* Sections located in FLASH at runtime.
|
||||
*/
|
||||
|
||||
/* Add a section where elf2tab will place the TBF headers, so that the rest
|
||||
* of the FLASH sections are in the right locations. */
|
||||
.tbf_header (NOLOAD) : {
|
||||
. = . + TBF_HEADER_SIZE;
|
||||
} > FLASH
|
||||
|
||||
/* Runtime header. Contains values the linker knows that the runtime needs
|
||||
* to look up.
|
||||
*/
|
||||
.start ALIGN(4) : {
|
||||
/* We combine rt_header and _start into a single section. If we don't,
|
||||
* elf2tab does not parse the ELF file correctly for unknown reasons.
|
||||
*/
|
||||
rt_header = .;
|
||||
LONG(start & 0xFFFFFFFE); /* .start w/ Thumb bit unset */
|
||||
LONG(ADDR(.bss) + SIZEOF(.bss)); /* Initial process break */
|
||||
LONG(_stack_top);
|
||||
LONG(SIZEOF(.data));
|
||||
LONG(LOADADDR(.data));
|
||||
LONG(ADDR(.data));
|
||||
LONG(SIZEOF(.bss));
|
||||
LONG(ADDR(.bss));
|
||||
|
||||
*(.start)
|
||||
} > FLASH
|
||||
|
||||
/* Text section -- the application's code. */
|
||||
.text ALIGN(4) : {
|
||||
*(.text.*)
|
||||
} > FLASH
|
||||
|
||||
/* Read-only data section. Contains strings and other global constants. */
|
||||
.rodata ALIGN(4) : {
|
||||
*(.rodata.*)
|
||||
/* .data is placed after .rodata in flash. data_flash_start is used by
|
||||
* AT() to place .data in flash as well as in rt_header.
|
||||
*/
|
||||
_data_flash_start = .;
|
||||
} > FLASH
|
||||
|
||||
/* Sections located in RAM at runtime.
|
||||
*/
|
||||
|
||||
/* Reserve space for the stack. Aligned to a multiple of 16 bytes for the
|
||||
* RISC-V calling convention:
|
||||
* https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
|
||||
*/
|
||||
.stack (NOLOAD) : {
|
||||
/* _sram_origin is used by elf2tab:
|
||||
* https://github.com/tock/elf2tab/blob/master/src/main.rs#L301
|
||||
*/
|
||||
_sram_origin = .;
|
||||
KEEP(*(.stack_buffer))
|
||||
. = ALIGN(16);
|
||||
_stack_top = .; /* Used in rt_header */
|
||||
} > RAM
|
||||
|
||||
/* Read-write data section. This is deployed as part of FLASH but is copied
|
||||
* into RAM at runtime.
|
||||
*/
|
||||
.data ALIGN(4) : AT(_data_flash_start) {
|
||||
data_ram_start = .;
|
||||
/* .sdata is the RISC-V small data section */
|
||||
*(.sdata .data)
|
||||
/* Pad to word alignment so the relocation loop can use word-sized
|
||||
* copies.
|
||||
*/
|
||||
. = ALIGN(4);
|
||||
} > RAM
|
||||
|
||||
/* BSS section. These are zero-initialized static variables. This section is
|
||||
* not copied from FLASH into RAM but rather directly initialized, and is
|
||||
* mainly put in this linker script so that we get an error if it overflows
|
||||
* the RAM region.
|
||||
*/
|
||||
.bss ALIGN(4) (NOLOAD) : {
|
||||
/* .sbss is the RISC-V small data section */
|
||||
*(.sbss .bss.*)
|
||||
} > RAM
|
||||
|
||||
_heap_start = ADDR(.bss) + SIZEOF(.bss); /* Used by rt_header */
|
||||
|
||||
/* Sections we do not need. */
|
||||
/DISCARD/ :
|
||||
{
|
||||
*(.ARM.exidx .eh_frame)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
121
patches/libtock-rs/01-alloc-init-feature.patch
Normal file
121
patches/libtock-rs/01-alloc-init-feature.patch
Normal file
@@ -0,0 +1,121 @@
|
||||
diff --git a/apis/buttons/src/lib.rs b/apis/buttons/src/lib.rs
|
||||
index 565970f..f5d08b5 100644
|
||||
--- a/apis/buttons/src/lib.rs
|
||||
+++ b/apis/buttons/src/lib.rs
|
||||
@@ -135,7 +135,7 @@ mod tests;
|
||||
// Driver number and command IDs
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
-const DRIVER_NUM: u32 = 3;
|
||||
+pub const DRIVER_NUM: u32 = 3;
|
||||
|
||||
// Command IDs
|
||||
const BUTTONS_COUNT: u32 = 0;
|
||||
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
|
||||
index a863392..3509a40 100644
|
||||
--- a/runtime/Cargo.toml
|
||||
+++ b/runtime/Cargo.toml
|
||||
@@ -14,6 +14,9 @@ libtock_platform = { path = "../platform" }
|
||||
|
||||
[features]
|
||||
|
||||
+# Initialize an allocator for access to heap memory
|
||||
+alloc_init = []
|
||||
+
|
||||
# By default, libtock_runtime looks for the LIBTOCK_PLATFORM variable to decide
|
||||
# what layout file to use. If you are providing your own linker script, set
|
||||
# no_auto_layout to disable the layout file logic.
|
||||
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
|
||||
index 1054e41..b5c519a 100644
|
||||
--- a/runtime/src/lib.rs
|
||||
+++ b/runtime/src/lib.rs
|
||||
@@ -19,6 +19,7 @@
|
||||
//! and provide its own layout file.
|
||||
|
||||
#![no_std]
|
||||
+#![feature(strict_provenance)]
|
||||
#![warn(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod startup;
|
||||
diff --git a/runtime/src/startup/mod.rs b/runtime/src/startup/mod.rs
|
||||
index 769bfe9..052479f 100644
|
||||
--- a/runtime/src/startup/mod.rs
|
||||
+++ b/runtime/src/startup/mod.rs
|
||||
@@ -10,6 +10,8 @@ core::arch::global_asm!(include_str!("asm_arm.s"));
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
core::arch::global_asm!(include_str!("asm_riscv32.s"));
|
||||
|
||||
+static APP_HEAP_SIZE: Option<&'static str> = option_env!("APP_HEAP_SIZE");
|
||||
+
|
||||
/// `set_main!` is used to tell `libtock_runtime` where the process binary's
|
||||
/// `main` function is. The process binary's `main` function must have the
|
||||
/// signature `FnOnce() -> T`, where T is some concrete type that implements
|
||||
@@ -83,6 +85,8 @@ extern "C" fn rust_start() -> ! {
|
||||
extern "Rust" {
|
||||
fn libtock_unsafe_main() -> !;
|
||||
static rt_header: RtHeader;
|
||||
+ #[cfg(feature = "alloc_init")]
|
||||
+ fn libtock_alloc_init(heap_bottom: *mut u8, heap_size: usize);
|
||||
}
|
||||
|
||||
// TODO: Implement a safe memop API in libtock_platform and migrate these
|
||||
@@ -92,16 +96,46 @@ extern "C" fn rust_start() -> ! {
|
||||
// impact the execution of this process.
|
||||
#[cfg(not(feature = "no_debug_memop"))]
|
||||
unsafe {
|
||||
+ // specify the top of the application stack which grows downwards
|
||||
TockSyscalls::syscall2::<{ syscall_class::MEMOP }>([
|
||||
10u32.into(),
|
||||
rt_header.stack_top.into(),
|
||||
]);
|
||||
+
|
||||
+ // specify the start of the application heap which grows upwards
|
||||
TockSyscalls::syscall2::<{ syscall_class::MEMOP }>([
|
||||
11u32.into(),
|
||||
rt_header.initial_break.into(),
|
||||
]);
|
||||
}
|
||||
|
||||
+ #[cfg(feature = "alloc_init")]
|
||||
+ {
|
||||
+ let app_heap_size: usize = match APP_HEAP_SIZE {
|
||||
+ Some(var) => var
|
||||
+ .parse()
|
||||
+ .ok()
|
||||
+ .expect("could not parse APP_HEAP_SIZE as usize!"),
|
||||
+ None => 9000,
|
||||
+ };
|
||||
+
|
||||
+ // the heap starts after the `bss` section
|
||||
+ let app_heap_bottom = unsafe { rt_header.bss_start.add(rt_header.bss_size) };
|
||||
+
|
||||
+ assert_eq!(app_heap_bottom.addr(), unsafe {
|
||||
+ rt_header.bss_start.addr() + rt_header.bss_size
|
||||
+ });
|
||||
+
|
||||
+ let app_heap_end = unsafe { app_heap_bottom.add(app_heap_size) };
|
||||
+
|
||||
+ unsafe {
|
||||
+ // tell the kernel the new app heap break (which is the upper address bound of the process)
|
||||
+ TockSyscalls::syscall2::<{ syscall_class::MEMOP }>([0u32.into(), app_heap_end.into()]);
|
||||
+
|
||||
+ libtock_alloc_init(app_heap_bottom, app_heap_size);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
// Safety: libtock_unsafe_main is defined by the set_main! macro, and its
|
||||
// signature matches the signature in the `extern` block in this function.
|
||||
unsafe {
|
||||
diff --git a/rust-toolchain b/rust-toolchain
|
||||
index e911a96..d139e12 100644
|
||||
--- a/rust-toolchain
|
||||
+++ b/rust-toolchain
|
||||
@@ -1,7 +1,7 @@
|
||||
[toolchain]
|
||||
# See https://rust-lang.github.io/rustup-components-history/ for a list of
|
||||
# recently nightlies and what components are available for them.
|
||||
-channel = "nightly-2022-06-10"
|
||||
+channel = "nightly-2023-02-01"
|
||||
components = ["clippy", "miri", "rustfmt"]
|
||||
targets = ["thumbv7em-none-eabi",
|
||||
"riscv32imac-unknown-none-elf",
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
@@ -1,9 +1,9 @@
|
||||
diff --git a/boards/components/src/ctap.rs b/boards/components/src/ctap.rs
|
||||
deleted file mode 100644
|
||||
index 5e30702b0..000000000
|
||||
index 87b4ded9f..000000000
|
||||
--- a/boards/components/src/ctap.rs
|
||||
+++ /dev/null
|
||||
@@ -1,127 +0,0 @@
|
||||
@@ -1,130 +0,0 @@
|
||||
-//! Component for CTAP HID over USB support.
|
||||
-//!
|
||||
-//! This provides a component for using the CTAP driver. This allows for
|
||||
@@ -57,31 +57,34 @@ index 5e30702b0..000000000
|
||||
-}
|
||||
-
|
||||
-pub struct CtapComponent<U: 'static + hil::usb::UsbController<'static>> {
|
||||
- board_kernel: &'static kernel::Kernel,
|
||||
- driver_num: usize,
|
||||
- usb: &'static U,
|
||||
- vendor_id: u16,
|
||||
- product_id: u16,
|
||||
- strings: &'static [&'static str; 3],
|
||||
- board_kernel: &'static kernel::Kernel,
|
||||
- send_buffer: &'static mut [u8; 64],
|
||||
- recv_buffer: &'static mut [u8; 64],
|
||||
-}
|
||||
-
|
||||
-impl<U: 'static + hil::usb::UsbController<'static>> CtapComponent<U> {
|
||||
- pub fn new(
|
||||
- board_kernel: &'static kernel::Kernel,
|
||||
- driver_num: usize,
|
||||
- usb: &'static U,
|
||||
- vendor_id: u16,
|
||||
- product_id: u16,
|
||||
- strings: &'static [&'static str; 3],
|
||||
- board_kernel: &'static kernel::Kernel,
|
||||
- send_buffer: &'static mut [u8; 64],
|
||||
- recv_buffer: &'static mut [u8; 64],
|
||||
- ) -> CtapComponent<U> {
|
||||
- CtapComponent {
|
||||
- board_kernel,
|
||||
- driver_num,
|
||||
- usb,
|
||||
- vendor_id,
|
||||
- product_id,
|
||||
- strings,
|
||||
- board_kernel,
|
||||
- send_buffer,
|
||||
- recv_buffer,
|
||||
- }
|
||||
@@ -122,7 +125,7 @@ index 5e30702b0..000000000
|
||||
- Some(ctap),
|
||||
- self.send_buffer,
|
||||
- self.recv_buffer,
|
||||
- self.board_kernel.create_grant(&grant_cap),
|
||||
- self.board_kernel.create_grant(self.driver_num, &grant_cap),
|
||||
- )
|
||||
- );
|
||||
-
|
||||
@@ -132,23 +135,23 @@ index 5e30702b0..000000000
|
||||
- }
|
||||
-}
|
||||
diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs
|
||||
index 95625f91f..cd542609e 100644
|
||||
index 120403652..92d45f7e3 100644
|
||||
--- a/boards/components/src/lib.rs
|
||||
+++ b/boards/components/src/lib.rs
|
||||
@@ -10,7 +10,6 @@ pub mod button;
|
||||
@@ -14,7 +14,6 @@ pub mod ccs811;
|
||||
pub mod cdc;
|
||||
pub mod console;
|
||||
pub mod crc;
|
||||
-pub mod ctap;
|
||||
pub mod debug_queue;
|
||||
pub mod debug_writer;
|
||||
pub mod firmware_protection;
|
||||
pub mod digest;
|
||||
diff --git a/capsules/src/usb/ctap.rs b/capsules/src/usb/ctap.rs
|
||||
deleted file mode 100644
|
||||
index 32cb2d6f1..000000000
|
||||
index d085b7286..000000000
|
||||
--- a/capsules/src/usb/ctap.rs
|
||||
+++ /dev/null
|
||||
@@ -1,410 +0,0 @@
|
||||
@@ -1,406 +0,0 @@
|
||||
-//! Client to Authenticator Protocol CTAPv2 over USB HID
|
||||
-//!
|
||||
-//! Based on the spec avaliable at: <https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html>
|
||||
@@ -169,11 +172,11 @@ index 32cb2d6f1..000000000
|
||||
-use super::descriptors::TransferDirection;
|
||||
-use super::usbc_client_ctrl::ClientCtrl;
|
||||
-
|
||||
-use kernel::common::cells::OptionalCell;
|
||||
-use kernel::common::cells::TakeCell;
|
||||
-use kernel::hil;
|
||||
-use kernel::hil::usb::TransferType;
|
||||
-use kernel::ReturnCode;
|
||||
-use kernel::utilities::cells::OptionalCell;
|
||||
-use kernel::utilities::cells::TakeCell;
|
||||
-use kernel::ErrorCode;
|
||||
-
|
||||
-/// Use 1 Interrupt transfer IN/OUT endpoint
|
||||
-const ENDPOINT_NUM: usize = 1;
|
||||
@@ -349,7 +352,7 @@ index 32cb2d6f1..000000000
|
||||
- fn send_buffer(
|
||||
- &'a self,
|
||||
- send: &'static mut [u8; 64],
|
||||
- ) -> Result<usize, (ReturnCode, &'static mut [u8; 64])> {
|
||||
- ) -> Result<usize, (ErrorCode, &'static mut [u8; 64])> {
|
||||
- let len = send.len();
|
||||
-
|
||||
- self.send_buffer.replace(send);
|
||||
@@ -358,17 +361,17 @@ index 32cb2d6f1..000000000
|
||||
- Ok(len)
|
||||
- }
|
||||
-
|
||||
- fn send_cancel(&'a self) -> Result<&'static mut [u8; 64], ReturnCode> {
|
||||
- fn send_cancel(&'a self) -> Result<&'static mut [u8; 64], ErrorCode> {
|
||||
- match self.send_buffer.take() {
|
||||
- Some(buf) => Ok(buf),
|
||||
- None => Err(ReturnCode::EBUSY),
|
||||
- None => Err(ErrorCode::BUSY),
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- fn receive_buffer(
|
||||
- &'a self,
|
||||
- recv: &'static mut [u8; 64],
|
||||
- ) -> Result<(), (ReturnCode, &'static mut [u8; 64])> {
|
||||
- ) -> Result<(), (ErrorCode, &'static mut [u8; 64])> {
|
||||
- self.recv_buffer.replace(recv);
|
||||
-
|
||||
- if self.saved_endpoint.is_some() {
|
||||
@@ -376,11 +379,7 @@ index 32cb2d6f1..000000000
|
||||
- if self.can_receive() {
|
||||
- self.recv_buffer.take().map(|buf| {
|
||||
- self.client.map(move |client| {
|
||||
- client.packet_received(
|
||||
- ReturnCode::SUCCESS,
|
||||
- buf,
|
||||
- self.saved_endpoint.take().unwrap(),
|
||||
- );
|
||||
- client.packet_received(Ok(()), buf, self.saved_endpoint.take().unwrap());
|
||||
- });
|
||||
- });
|
||||
- // Reset the offset
|
||||
@@ -394,11 +393,11 @@ index 32cb2d6f1..000000000
|
||||
- Ok(())
|
||||
- }
|
||||
-
|
||||
- fn receive_cancel(&'a self) -> Result<&'static mut [u8; 64], ReturnCode> {
|
||||
- fn receive_cancel(&'a self) -> Result<&'static mut [u8; 64], ErrorCode> {
|
||||
- self.saved_endpoint.take();
|
||||
- match self.recv_buffer.take() {
|
||||
- Some(buf) => Ok(buf),
|
||||
- None => Err(ReturnCode::EBUSY),
|
||||
- None => Err(ErrorCode::BUSY),
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
@@ -524,7 +523,7 @@ index 32cb2d6f1..000000000
|
||||
- if total_received_bytes >= self.recv_len.get() {
|
||||
- if self.can_receive() {
|
||||
- self.client.map(move |client| {
|
||||
- client.packet_received(ReturnCode::SUCCESS, buf, endpoint);
|
||||
- client.packet_received(Ok(()), buf, endpoint);
|
||||
- });
|
||||
- // Reset the offset
|
||||
- self.recv_offset.set(0);
|
||||
@@ -554,18 +553,18 @@ index 32cb2d6f1..000000000
|
||||
- fn packet_transmitted(&'a self, endpoint: usize) {
|
||||
- self.send_buffer.take().map(|buf| {
|
||||
- self.client.map(move |client| {
|
||||
- client.packet_transmitted(ReturnCode::SUCCESS, buf, endpoint);
|
||||
- client.packet_transmitted(Ok(()), buf, endpoint);
|
||||
- });
|
||||
- });
|
||||
- }
|
||||
-}
|
||||
diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs
|
||||
index 17cab4c23..3f3a4f646 100644
|
||||
index 6d5daa444..767f5de83 100644
|
||||
--- a/capsules/src/usb/mod.rs
|
||||
+++ b/capsules/src/usb/mod.rs
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod cdc;
|
||||
-pub mod ctap;
|
||||
pub mod descriptors;
|
||||
pub mod usb_ctap;
|
||||
pub mod usb_user;
|
||||
pub mod usbc_client;
|
||||
@@ -1,864 +0,0 @@
|
||||
diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs
|
||||
index 64fe46b7b..a3bbe724b 100644
|
||||
--- a/boards/components/src/lib.rs
|
||||
+++ b/boards/components/src/lib.rs
|
||||
@@ -52,3 +52,4 @@ pub mod tickv;
|
||||
pub mod touch;
|
||||
pub mod udp_driver;
|
||||
pub mod udp_mux;
|
||||
+pub mod usb_ctap;
|
||||
diff --git a/boards/components/src/usb_ctap.rs b/boards/components/src/usb_ctap.rs
|
||||
new file mode 100644
|
||||
index 000000000..69e95c3c7
|
||||
--- /dev/null
|
||||
+++ b/boards/components/src/usb_ctap.rs
|
||||
@@ -0,0 +1,88 @@
|
||||
+//! Component for CTAP over USB.
|
||||
+
|
||||
+use capsules::usb::usb_ctap::CtapUsbSyscallDriver;
|
||||
+use capsules::usb::usbc_ctap_hid::ClientCtapHID;
|
||||
+use core::mem::MaybeUninit;
|
||||
+use kernel::capabilities;
|
||||
+use kernel::component::Component;
|
||||
+use kernel::create_capability;
|
||||
+use kernel::hil;
|
||||
+use kernel::static_init_half;
|
||||
+
|
||||
+// Setup static space for the objects.
|
||||
+#[macro_export]
|
||||
+macro_rules! usb_ctap_component_buf {
|
||||
+ ($C:ty) => {{
|
||||
+ use capsules::usb::usb_ctap::CtapUsbSyscallDriver;
|
||||
+ use capsules::usb::usbc_ctap_hid::ClientCtapHID;
|
||||
+ use core::mem::MaybeUninit;
|
||||
+ static mut BUF1: MaybeUninit<ClientCtapHID<'static, 'static, $C>> = MaybeUninit::uninit();
|
||||
+ static mut BUF2: MaybeUninit<CtapUsbSyscallDriver<'static, 'static, $C>> =
|
||||
+ MaybeUninit::uninit();
|
||||
+ (&mut BUF1, &mut BUF2)
|
||||
+ };};
|
||||
+}
|
||||
+
|
||||
+pub struct UsbCtapComponent<C: 'static + hil::usb::UsbController<'static>> {
|
||||
+ board_kernel: &'static kernel::Kernel,
|
||||
+ controller: &'static C,
|
||||
+ max_ctrl_packet_size: u8,
|
||||
+ vendor_id: u16,
|
||||
+ product_id: u16,
|
||||
+ strings: &'static [&'static str],
|
||||
+}
|
||||
+
|
||||
+impl<C: 'static + hil::usb::UsbController<'static>> UsbCtapComponent<C> {
|
||||
+ pub fn new(
|
||||
+ board_kernel: &'static kernel::Kernel,
|
||||
+ controller: &'static C,
|
||||
+ max_ctrl_packet_size: u8,
|
||||
+ vendor_id: u16,
|
||||
+ product_id: u16,
|
||||
+ strings: &'static [&'static str],
|
||||
+ ) -> Self {
|
||||
+ Self {
|
||||
+ board_kernel,
|
||||
+ controller,
|
||||
+ max_ctrl_packet_size,
|
||||
+ vendor_id,
|
||||
+ product_id,
|
||||
+ strings,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<C: 'static + hil::usb::UsbController<'static>> Component for UsbCtapComponent<C> {
|
||||
+ type StaticInput = (
|
||||
+ &'static mut MaybeUninit<ClientCtapHID<'static, 'static, C>>,
|
||||
+ &'static mut MaybeUninit<CtapUsbSyscallDriver<'static, 'static, C>>,
|
||||
+ );
|
||||
+ type Output = &'static CtapUsbSyscallDriver<'static, 'static, C>;
|
||||
+
|
||||
+ unsafe fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
|
||||
+ let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
|
||||
+
|
||||
+ let usb_ctap = static_init_half!(
|
||||
+ static_buffer.0,
|
||||
+ ClientCtapHID<'static, 'static, C>,
|
||||
+ ClientCtapHID::new(
|
||||
+ self.controller,
|
||||
+ self.max_ctrl_packet_size,
|
||||
+ self.vendor_id,
|
||||
+ self.product_id,
|
||||
+ self.strings,
|
||||
+ )
|
||||
+ );
|
||||
+ self.controller.set_client(usb_ctap);
|
||||
+
|
||||
+ // Configure the USB userspace driver
|
||||
+ let usb_driver = static_init_half!(
|
||||
+ static_buffer.1,
|
||||
+ CtapUsbSyscallDriver<'static, 'static, C>,
|
||||
+ CtapUsbSyscallDriver::new(usb_ctap, self.board_kernel.create_grant(&grant_cap))
|
||||
+ );
|
||||
+ usb_ctap.set_client(usb_driver);
|
||||
+
|
||||
+ usb_driver
|
||||
+ }
|
||||
+}
|
||||
diff --git a/capsules/src/driver.rs b/capsules/src/driver.rs
|
||||
index 3dad0f50b..b6124c4c0 100644
|
||||
--- a/capsules/src/driver.rs
|
||||
+++ b/capsules/src/driver.rs
|
||||
@@ -26,6 +26,7 @@ pub enum NUM {
|
||||
I2cMaster = 0x20003,
|
||||
UsbUser = 0x20005,
|
||||
I2cMasterSlave = 0x20006,
|
||||
+ UsbCtap = 0x20009,
|
||||
|
||||
// Radio
|
||||
BleAdvertising = 0x30000,
|
||||
diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs
|
||||
index 6d5daa444..17cab4c23 100644
|
||||
--- a/capsules/src/usb/mod.rs
|
||||
+++ b/capsules/src/usb/mod.rs
|
||||
@@ -1,6 +1,8 @@
|
||||
pub mod cdc;
|
||||
pub mod ctap;
|
||||
pub mod descriptors;
|
||||
+pub mod usb_ctap;
|
||||
pub mod usb_user;
|
||||
pub mod usbc_client;
|
||||
pub mod usbc_client_ctrl;
|
||||
+pub mod usbc_ctap_hid;
|
||||
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
|
||||
new file mode 100644
|
||||
index 000000000..da3d16d85
|
||||
--- /dev/null
|
||||
+++ b/capsules/src/usb/usb_ctap.rs
|
||||
@@ -0,0 +1,355 @@
|
||||
+use super::usbc_ctap_hid::ClientCtapHID;
|
||||
+use kernel::hil;
|
||||
+use kernel::hil::usb::Client;
|
||||
+use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
|
||||
+
|
||||
+/// Syscall number
|
||||
+use crate::driver;
|
||||
+pub const DRIVER_NUM: usize = driver::NUM::UsbCtap as usize;
|
||||
+
|
||||
+pub const CTAP_CMD_CHECK: usize = 0;
|
||||
+pub const CTAP_CMD_CONNECT: usize = 1;
|
||||
+pub const CTAP_CMD_TRANSMIT: usize = 2;
|
||||
+pub const CTAP_CMD_RECEIVE: usize = 3;
|
||||
+pub const CTAP_CMD_TRANSMIT_OR_RECEIVE: usize = 4;
|
||||
+pub const CTAP_CMD_CANCEL: usize = 5;
|
||||
+
|
||||
+pub const CTAP_ALLOW_TRANSMIT: usize = 1;
|
||||
+pub const CTAP_ALLOW_RECEIVE: usize = 2;
|
||||
+pub const CTAP_ALLOW_TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
+
|
||||
+pub const CTAP_SUBSCRIBE_TRANSMIT: usize = 1;
|
||||
+pub const CTAP_SUBSCRIBE_RECEIVE: usize = 2;
|
||||
+pub const CTAP_SUBSCRIBE_TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
+
|
||||
+pub const CTAP_CALLBACK_TRANSMITED: usize = 1;
|
||||
+pub const CTAP_CALLBACK_RECEIVED: usize = 2;
|
||||
+
|
||||
+#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
+enum Side {
|
||||
+ Transmit,
|
||||
+ Receive,
|
||||
+ TransmitOrReceive,
|
||||
+}
|
||||
+
|
||||
+impl Side {
|
||||
+ fn can_transmit(&self) -> bool {
|
||||
+ match self {
|
||||
+ Side::Transmit | Side::TransmitOrReceive => true,
|
||||
+ Side::Receive => false,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn can_receive(&self) -> bool {
|
||||
+ match self {
|
||||
+ Side::Receive | Side::TransmitOrReceive => true,
|
||||
+ Side::Transmit => false,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[derive(Default)]
|
||||
+pub struct App {
|
||||
+ // Only one app can be connected to this driver, to avoid needing to route packets among apps.
|
||||
+ // This field tracks this status.
|
||||
+ connected: bool,
|
||||
+ // Currently enabled transaction side. Subscribing to a callback or allowing a buffer
|
||||
+ // automatically sets the corresponding side. Clearing both the callback and the buffer resets
|
||||
+ // the side to None.
|
||||
+ side: Option<Side>,
|
||||
+ callback: Option<Callback>,
|
||||
+ buffer: Option<AppSlice<Shared, u8>>,
|
||||
+ // Whether the app is waiting for the kernel signaling a packet transfer.
|
||||
+ waiting: bool,
|
||||
+}
|
||||
+
|
||||
+impl App {
|
||||
+ fn check_side(&mut self) {
|
||||
+ if self.callback.is_none() && self.buffer.is_none() && !self.waiting {
|
||||
+ self.side = None;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn set_side(&mut self, side: Side) -> bool {
|
||||
+ match self.side {
|
||||
+ None => {
|
||||
+ self.side = Some(side);
|
||||
+ true
|
||||
+ }
|
||||
+ Some(app_side) => side == app_side,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn is_ready_for_command(&self, side: Side) -> bool {
|
||||
+ self.buffer.is_some() && self.callback.is_some() && self.side == Some(side)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub trait CtapUsbClient {
|
||||
+ // Whether this client is ready to receive a packet. This must be checked before calling
|
||||
+ // packet_received().
|
||||
+ fn can_receive_packet(&self) -> bool;
|
||||
+
|
||||
+ // Signal to the client that a packet has been received.
|
||||
+ fn packet_received(&self, packet: &[u8; 64]);
|
||||
+
|
||||
+ // Signal to the client that a packet has been transmitted.
|
||||
+ fn packet_transmitted(&self);
|
||||
+}
|
||||
+
|
||||
+pub struct CtapUsbSyscallDriver<'a, 'b, C: 'a> {
|
||||
+ usb_client: &'a ClientCtapHID<'a, 'b, C>,
|
||||
+ apps: Grant<App>,
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
+ pub fn new(usb_client: &'a ClientCtapHID<'a, 'b, C>, apps: Grant<App>) -> Self {
|
||||
+ CtapUsbSyscallDriver { usb_client, apps }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
+ fn can_receive_packet(&self) -> bool {
|
||||
+ let mut result = false;
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|app, _| {
|
||||
+ if app.connected {
|
||||
+ result = app.waiting
|
||||
+ && app.side.map_or(false, |side| side.can_receive())
|
||||
+ && app.buffer.is_some();
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ result
|
||||
+ }
|
||||
+
|
||||
+ fn packet_received(&self, packet: &[u8; 64]) {
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|app, _| {
|
||||
+ if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive())
|
||||
+ {
|
||||
+ if let Some(buf) = &mut app.buffer {
|
||||
+ // Copy the packet to the app's allowed buffer.
|
||||
+ buf.as_mut().copy_from_slice(packet);
|
||||
+ app.waiting = false;
|
||||
+ // Signal to the app that a packet is ready.
|
||||
+ app.callback
|
||||
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
|
||||
+ }
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn packet_transmitted(&self) {
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|app, _| {
|
||||
+ if app.connected
|
||||
+ && app.waiting
|
||||
+ && app.side.map_or(false, |side| side.can_transmit())
|
||||
+ {
|
||||
+ app.waiting = false;
|
||||
+ // Signal to the app that the packet was sent.
|
||||
+ app.callback
|
||||
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_TRANSMITED, 0, 0));
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
+ fn allow(
|
||||
+ &self,
|
||||
+ appid: AppId,
|
||||
+ allow_num: usize,
|
||||
+ slice: Option<AppSlice<Shared, u8>>,
|
||||
+ ) -> ReturnCode {
|
||||
+ let side = match allow_num {
|
||||
+ CTAP_ALLOW_TRANSMIT => Side::Transmit,
|
||||
+ CTAP_ALLOW_RECEIVE => Side::Receive,
|
||||
+ CTAP_ALLOW_TRANSMIT_OR_RECEIVE => Side::TransmitOrReceive,
|
||||
+ _ => return ReturnCode::ENOSUPPORT,
|
||||
+ };
|
||||
+ self.apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if let Some(buf) = &slice {
|
||||
+ if buf.len() != 64 {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
+ if !app.set_side(side) {
|
||||
+ return ReturnCode::EALREADY;
|
||||
+ }
|
||||
+ app.buffer = slice;
|
||||
+ app.check_side();
|
||||
+ ReturnCode::SUCCESS
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into())
|
||||
+ }
|
||||
+
|
||||
+ fn subscribe(
|
||||
+ &self,
|
||||
+ subscribe_num: usize,
|
||||
+ callback: Option<Callback>,
|
||||
+ appid: AppId,
|
||||
+ ) -> ReturnCode {
|
||||
+ let side = match subscribe_num {
|
||||
+ CTAP_SUBSCRIBE_TRANSMIT => Side::Transmit,
|
||||
+ CTAP_SUBSCRIBE_RECEIVE => Side::Receive,
|
||||
+ CTAP_SUBSCRIBE_TRANSMIT_OR_RECEIVE => Side::TransmitOrReceive,
|
||||
+ _ => return ReturnCode::ENOSUPPORT,
|
||||
+ };
|
||||
+ self.apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if !app.set_side(side) {
|
||||
+ return ReturnCode::EALREADY;
|
||||
+ }
|
||||
+ app.callback = callback;
|
||||
+ app.check_side();
|
||||
+ ReturnCode::SUCCESS
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into())
|
||||
+ }
|
||||
+
|
||||
+ fn command(&self, cmd_num: usize, _arg1: usize, _arg2: usize, appid: AppId) -> ReturnCode {
|
||||
+ match cmd_num {
|
||||
+ CTAP_CMD_CHECK => ReturnCode::SUCCESS,
|
||||
+ CTAP_CMD_CONNECT => {
|
||||
+ // First, check if any app is already connected to this driver.
|
||||
+ let mut busy = false;
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|app, _| {
|
||||
+ busy |= app.connected;
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ self.apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if app.connected {
|
||||
+ ReturnCode::EALREADY
|
||||
+ } else if busy {
|
||||
+ ReturnCode::EBUSY
|
||||
+ } else {
|
||||
+ self.usb_client.enable();
|
||||
+ self.usb_client.attach();
|
||||
+ app.connected = true;
|
||||
+ ReturnCode::SUCCESS
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into())
|
||||
+ }
|
||||
+ CTAP_CMD_TRANSMIT => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if app.is_ready_for_command(Side::Transmit) {
|
||||
+ if app.waiting {
|
||||
+ ReturnCode::EALREADY
|
||||
+ } else if self
|
||||
+ .usb_client
|
||||
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
|
||||
+ {
|
||||
+ app.waiting = true;
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ ReturnCode::EBUSY
|
||||
+ }
|
||||
+ } else {
|
||||
+ ReturnCode::EINVAL
|
||||
+ }
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ CTAP_CMD_RECEIVE => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if app.is_ready_for_command(Side::Receive) {
|
||||
+ if app.waiting {
|
||||
+ ReturnCode::EALREADY
|
||||
+ } else {
|
||||
+ app.waiting = true;
|
||||
+ self.usb_client.receive_packet();
|
||||
+ ReturnCode::SUCCESS
|
||||
+ }
|
||||
+ } else {
|
||||
+ ReturnCode::EINVAL
|
||||
+ }
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ CTAP_CMD_TRANSMIT_OR_RECEIVE => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if app.is_ready_for_command(Side::TransmitOrReceive) {
|
||||
+ if app.waiting {
|
||||
+ ReturnCode::EALREADY
|
||||
+ } else {
|
||||
+ // Indicates to the driver that we can receive any pending packet.
|
||||
+ app.waiting = true;
|
||||
+ self.usb_client.receive_packet();
|
||||
+
|
||||
+ if !app.waiting {
|
||||
+ // The call to receive_packet() collected a pending packet.
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ // Indicates to the driver that we have a packet to send.
|
||||
+ if self
|
||||
+ .usb_client
|
||||
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
|
||||
+ {
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ ReturnCode::EBUSY
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ ReturnCode::EINVAL
|
||||
+ }
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ CTAP_CMD_CANCEL => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if app.waiting {
|
||||
+ // FIXME: if cancellation failed, the app should still wait. But that
|
||||
+ // doesn't work yet.
|
||||
+ app.waiting = false;
|
||||
+ if self.usb_client.cancel_transaction() {
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ // Cannot cancel now because the transaction is already in process.
|
||||
+ // The app should wait for the callback instead.
|
||||
+ ReturnCode::EBUSY
|
||||
+ }
|
||||
+ } else {
|
||||
+ ReturnCode::EALREADY
|
||||
+ }
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
new file mode 100644
|
||||
index 000000000..642039120
|
||||
--- /dev/null
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -0,0 +1,369 @@
|
||||
+//! A USB HID client of the USB hardware interface
|
||||
+
|
||||
+use super::descriptors;
|
||||
+use super::descriptors::Buffer64;
|
||||
+use super::descriptors::DescriptorType;
|
||||
+use super::descriptors::EndpointAddress;
|
||||
+use super::descriptors::EndpointDescriptor;
|
||||
+use super::descriptors::HIDCountryCode;
|
||||
+use super::descriptors::HIDDescriptor;
|
||||
+use super::descriptors::HIDSubordinateDescriptor;
|
||||
+use super::descriptors::InterfaceDescriptor;
|
||||
+use super::descriptors::ReportDescriptor;
|
||||
+use super::descriptors::TransferDirection;
|
||||
+use super::usb_ctap::CtapUsbClient;
|
||||
+use super::usbc_client_ctrl::ClientCtrl;
|
||||
+use core::cell::Cell;
|
||||
+use kernel::common::cells::OptionalCell;
|
||||
+use kernel::debug;
|
||||
+use kernel::hil;
|
||||
+use kernel::hil::usb::TransferType;
|
||||
+
|
||||
+static LANGUAGES: &'static [u16; 1] = &[
|
||||
+ 0x0409, // English (United States)
|
||||
+];
|
||||
+
|
||||
+const ENDPOINT_NUM: usize = 1;
|
||||
+
|
||||
+static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
|
||||
+ 0x06, 0xD0, 0xF1, // HID_UsagePage ( FIDO_USAGE_PAGE ),
|
||||
+ 0x09, 0x01, // HID_Usage ( FIDO_USAGE_CTAPHID ),
|
||||
+ 0xA1, 0x01, // HID_Collection ( HID_Application ),
|
||||
+ 0x09, 0x20, // HID_Usage ( FIDO_USAGE_DATA_IN ),
|
||||
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
|
||||
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
|
||||
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
|
||||
+ 0x95, 0x40, // HID_ReportCount ( HID_INPUT_REPORT_BYTES ),
|
||||
+ 0x81, 0x02, // HID_Input ( HID_Data | HID_Absolute | HID_Variable ),
|
||||
+ 0x09, 0x21, // HID_Usage ( FIDO_USAGE_DATA_OUT ),
|
||||
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
|
||||
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
|
||||
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
|
||||
+ 0x95, 0x40, // HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ),
|
||||
+ 0x91, 0x02, // HID_Output ( HID_Data | HID_Absolute | HID_Variable ),
|
||||
+ 0xC0, // HID_EndCollection
|
||||
+];
|
||||
+
|
||||
+static CTAP_REPORT: ReportDescriptor<'static> = ReportDescriptor {
|
||||
+ desc: CTAP_REPORT_DESCRIPTOR,
|
||||
+};
|
||||
+
|
||||
+static HID_SUB_DESCRIPTORS: &'static [HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
|
||||
+ typ: DescriptorType::Report,
|
||||
+ len: CTAP_REPORT_DESCRIPTOR.len() as u16,
|
||||
+}];
|
||||
+
|
||||
+static HID: HIDDescriptor<'static> = HIDDescriptor {
|
||||
+ hid_class: 0x0110,
|
||||
+ country_code: HIDCountryCode::NotSupported,
|
||||
+ sub_descriptors: HID_SUB_DESCRIPTORS,
|
||||
+};
|
||||
+
|
||||
+pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
+ client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
+
|
||||
+ // 64-byte buffers for the endpoint
|
||||
+ in_buffer: Buffer64,
|
||||
+ out_buffer: Buffer64,
|
||||
+
|
||||
+ // Interaction with the client
|
||||
+ client: OptionalCell<&'b dyn CtapUsbClient>,
|
||||
+ tx_packet: OptionalCell<[u8; 64]>,
|
||||
+ pending_in: Cell<bool>,
|
||||
+ pending_out: Cell<bool>,
|
||||
+ delayed_out: Cell<bool>,
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
+ pub fn new(
|
||||
+ controller: &'a C,
|
||||
+ max_ctrl_packet_size: u8,
|
||||
+ vendor_id: u16,
|
||||
+ product_id: u16,
|
||||
+ strings: &'static [&'static str],
|
||||
+ ) -> Self {
|
||||
+ let interfaces: &mut [InterfaceDescriptor] = &mut [
|
||||
+ // Interface declared in the FIDO2 specification, section 8.1.8.1
|
||||
+ InterfaceDescriptor {
|
||||
+ interface_class: 0x03, // HID
|
||||
+ interface_subclass: 0x00,
|
||||
+ interface_protocol: 0x00,
|
||||
+ ..InterfaceDescriptor::default()
|
||||
+ },
|
||||
+ ];
|
||||
+
|
||||
+ let endpoints: &[&[EndpointDescriptor]] = &[&[
|
||||
+ EndpointDescriptor {
|
||||
+ endpoint_address: EndpointAddress::new_const(
|
||||
+ ENDPOINT_NUM,
|
||||
+ TransferDirection::HostToDevice,
|
||||
+ ),
|
||||
+ transfer_type: TransferType::Interrupt,
|
||||
+ max_packet_size: 64,
|
||||
+ interval: 5,
|
||||
+ },
|
||||
+ EndpointDescriptor {
|
||||
+ endpoint_address: EndpointAddress::new_const(
|
||||
+ ENDPOINT_NUM,
|
||||
+ TransferDirection::DeviceToHost,
|
||||
+ ),
|
||||
+ transfer_type: TransferType::Interrupt,
|
||||
+ max_packet_size: 64,
|
||||
+ interval: 5,
|
||||
+ },
|
||||
+ ]];
|
||||
+
|
||||
+ let (device_descriptor_buffer, other_descriptor_buffer) =
|
||||
+ descriptors::create_descriptor_buffers(
|
||||
+ descriptors::DeviceDescriptor {
|
||||
+ vendor_id,
|
||||
+ product_id,
|
||||
+ manufacturer_string: 1,
|
||||
+ product_string: 2,
|
||||
+ serial_number_string: 3,
|
||||
+ max_packet_size_ep0: max_ctrl_packet_size,
|
||||
+ ..descriptors::DeviceDescriptor::default()
|
||||
+ },
|
||||
+ descriptors::ConfigurationDescriptor {
|
||||
+ configuration_value: 1,
|
||||
+ ..descriptors::ConfigurationDescriptor::default()
|
||||
+ },
|
||||
+ interfaces,
|
||||
+ endpoints,
|
||||
+ Some(&HID),
|
||||
+ None, // No CDC descriptor array
|
||||
+ );
|
||||
+
|
||||
+ ClientCtapHID {
|
||||
+ client_ctrl: ClientCtrl::new(
|
||||
+ controller,
|
||||
+ device_descriptor_buffer,
|
||||
+ other_descriptor_buffer,
|
||||
+ Some(&HID),
|
||||
+ Some(&CTAP_REPORT),
|
||||
+ LANGUAGES,
|
||||
+ strings,
|
||||
+ ),
|
||||
+ in_buffer: Buffer64::default(),
|
||||
+ out_buffer: Buffer64::default(),
|
||||
+ client: OptionalCell::empty(),
|
||||
+ tx_packet: OptionalCell::empty(),
|
||||
+ pending_in: Cell::new(false),
|
||||
+ pending_out: Cell::new(false),
|
||||
+ delayed_out: Cell::new(false),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_client(&'a self, client: &'b dyn CtapUsbClient) {
|
||||
+ self.client.set(client);
|
||||
+ }
|
||||
+
|
||||
+ pub fn transmit_packet(&'a self, packet: &[u8]) -> bool {
|
||||
+ if self.pending_in.get() {
|
||||
+ // The previous packet has not yet been transmitted, reject the new one.
|
||||
+ false
|
||||
+ } else {
|
||||
+ self.pending_in.set(true);
|
||||
+ let mut buf: [u8; 64] = [0; 64];
|
||||
+ buf.copy_from_slice(packet);
|
||||
+ self.tx_packet.set(buf);
|
||||
+ // Alert the controller that we now have data to send on the Interrupt IN endpoint.
|
||||
+ self.controller().endpoint_resume_in(1);
|
||||
+ true
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn receive_packet(&'a self) -> bool {
|
||||
+ if self.pending_out.get() {
|
||||
+ // The previous packet has not yet been received, reject the new one.
|
||||
+ false
|
||||
+ } else {
|
||||
+ self.pending_out.set(true);
|
||||
+ // In case we reported Delay before, send the pending packet back to the client.
|
||||
+ // Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
+ // packet arrives.
|
||||
+ if self.delayed_out.take() {
|
||||
+ if self.send_packet_to_client() {
|
||||
+ // If that succeeds, alert the controller that we can now
|
||||
+ // receive data on the Interrupt OUT endpoint.
|
||||
+ self.controller().endpoint_resume_out(1);
|
||||
+ }
|
||||
+ }
|
||||
+ true
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Send an OUT packet available in the controller back to the client.
|
||||
+ // This returns false if the client is not ready to receive a packet, and true if the client
|
||||
+ // successfully accepted the packet.
|
||||
+ fn send_packet_to_client(&'a self) -> bool {
|
||||
+ // Copy the packet into a buffer to send to the client.
|
||||
+ let mut buf: [u8; 64] = [0; 64];
|
||||
+ for (i, x) in self.out_buffer.buf.iter().enumerate() {
|
||||
+ buf[i] = x.get();
|
||||
+ }
|
||||
+
|
||||
+ assert!(!self.delayed_out.get());
|
||||
+
|
||||
+ // Notify the client
|
||||
+ if self
|
||||
+ .client
|
||||
+ .map_or(false, |client| client.can_receive_packet())
|
||||
+ {
|
||||
+ assert!(self.pending_out.take());
|
||||
+
|
||||
+ // Clear any pending packet on the transmitting side.
|
||||
+ // It's up to the client to handle the received packet and decide if this packet
|
||||
+ // should be re-transmitted or not.
|
||||
+ self.cancel_in_transaction();
|
||||
+
|
||||
+ self.client.map(|client| client.packet_received(&buf));
|
||||
+ true
|
||||
+ } else {
|
||||
+ // Cannot receive now, indicate a delay to the controller.
|
||||
+ self.delayed_out.set(true);
|
||||
+ false
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn cancel_transaction(&'a self) -> bool {
|
||||
+ self.cancel_in_transaction() | self.cancel_out_transaction()
|
||||
+ }
|
||||
+
|
||||
+ fn cancel_in_transaction(&'a self) -> bool {
|
||||
+ self.tx_packet.take();
|
||||
+ self.pending_in.take()
|
||||
+ }
|
||||
+
|
||||
+ fn cancel_out_transaction(&'a self) -> bool {
|
||||
+ self.pending_out.take()
|
||||
+ }
|
||||
+
|
||||
+ #[inline]
|
||||
+ fn controller(&'a self) -> &'a C {
|
||||
+ self.client_ctrl.controller()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtapHID<'a, 'b, C> {
|
||||
+ fn enable(&'a self) {
|
||||
+ // Set up the default control endpoint
|
||||
+ self.client_ctrl.enable();
|
||||
+
|
||||
+ // Set up the interrupt in-out endpoint
|
||||
+ self.controller()
|
||||
+ .endpoint_set_in_buffer(1, &self.in_buffer.buf);
|
||||
+ self.controller()
|
||||
+ .endpoint_set_out_buffer(1, &self.out_buffer.buf);
|
||||
+ self.controller()
|
||||
+ .endpoint_in_out_enable(TransferType::Interrupt, 1);
|
||||
+ }
|
||||
+
|
||||
+ fn attach(&'a self) {
|
||||
+ self.client_ctrl.attach();
|
||||
+ }
|
||||
+
|
||||
+ fn bus_reset(&'a self) {
|
||||
+ // Should the client initiate reconfiguration here?
|
||||
+ // For now, the hardware layer does it.
|
||||
+
|
||||
+ debug!("Bus reset");
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Control Setup transaction
|
||||
+ fn ctrl_setup(&'a self, endpoint: usize) -> hil::usb::CtrlSetupResult {
|
||||
+ self.client_ctrl.ctrl_setup(endpoint)
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Control In transaction
|
||||
+ fn ctrl_in(&'a self, endpoint: usize) -> hil::usb::CtrlInResult {
|
||||
+ self.client_ctrl.ctrl_in(endpoint)
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Control Out transaction
|
||||
+ fn ctrl_out(&'a self, endpoint: usize, packet_bytes: u32) -> hil::usb::CtrlOutResult {
|
||||
+ self.client_ctrl.ctrl_out(endpoint, packet_bytes)
|
||||
+ }
|
||||
+
|
||||
+ fn ctrl_status(&'a self, endpoint: usize) {
|
||||
+ self.client_ctrl.ctrl_status(endpoint)
|
||||
+ }
|
||||
+
|
||||
+ /// Handle the completion of a Control transfer
|
||||
+ fn ctrl_status_complete(&'a self, endpoint: usize) {
|
||||
+ self.client_ctrl.ctrl_status_complete(endpoint)
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Bulk/Interrupt IN transaction
|
||||
+ fn packet_in(&'a self, transfer_type: TransferType, endpoint: usize) -> hil::usb::InResult {
|
||||
+ match transfer_type {
|
||||
+ TransferType::Bulk => hil::usb::InResult::Error,
|
||||
+ TransferType::Interrupt => {
|
||||
+ if endpoint != 1 {
|
||||
+ return hil::usb::InResult::Error;
|
||||
+ }
|
||||
+
|
||||
+ if let Some(packet) = self.tx_packet.take() {
|
||||
+ let buf = &self.in_buffer.buf;
|
||||
+ for i in 0..64 {
|
||||
+ buf[i].set(packet[i]);
|
||||
+ }
|
||||
+
|
||||
+ hil::usb::InResult::Packet(64)
|
||||
+ } else {
|
||||
+ // Nothing to send
|
||||
+ hil::usb::InResult::Delay
|
||||
+ }
|
||||
+ }
|
||||
+ TransferType::Control | TransferType::Isochronous => unreachable!(),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Bulk/Interrupt OUT transaction
|
||||
+ fn packet_out(
|
||||
+ &'a self,
|
||||
+ transfer_type: TransferType,
|
||||
+ endpoint: usize,
|
||||
+ packet_bytes: u32,
|
||||
+ ) -> hil::usb::OutResult {
|
||||
+ match transfer_type {
|
||||
+ TransferType::Bulk => hil::usb::OutResult::Error,
|
||||
+ TransferType::Interrupt => {
|
||||
+ if endpoint != 1 {
|
||||
+ return hil::usb::OutResult::Error;
|
||||
+ }
|
||||
+
|
||||
+ if packet_bytes != 64 {
|
||||
+ // Cannot process this packet
|
||||
+ hil::usb::OutResult::Error
|
||||
+ } else {
|
||||
+ if self.send_packet_to_client() {
|
||||
+ hil::usb::OutResult::Ok
|
||||
+ } else {
|
||||
+ hil::usb::OutResult::Delay
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ TransferType::Control | TransferType::Isochronous => unreachable!(),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn packet_transmitted(&'a self, endpoint: usize) {
|
||||
+ if endpoint != 1 {
|
||||
+ panic!("Unexpected transmission on ep {}", endpoint);
|
||||
+ }
|
||||
+
|
||||
+ if self.tx_packet.is_some() {
|
||||
+ panic!("Unexpected tx_packet while a packet was being transmitted.");
|
||||
+ }
|
||||
+ self.pending_in.set(false);
|
||||
+
|
||||
+ // Clear any pending packet on the receiving side.
|
||||
+ // It's up to the client to handle the transmitted packet and decide if they want to
|
||||
+ // receive another packet.
|
||||
+ self.cancel_out_transaction();
|
||||
+
|
||||
+ // Notify the client
|
||||
+ self.client.map(|client| client.packet_transmitted());
|
||||
+ }
|
||||
+}
|
||||
1102
patches/tock/03-add-ctap-modules.patch
Normal file
1102
patches/tock/03-add-ctap-modules.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
diff --git a/capsules/Cargo.toml b/capsules/Cargo.toml
|
||||
index 680fe32b2..4f757b93d 100644
|
||||
index 65301bcf1..dc70e98b1 100644
|
||||
--- a/capsules/Cargo.toml
|
||||
+++ b/capsules/Cargo.toml
|
||||
@@ -8,3 +8,6 @@ edition = "2018"
|
||||
@@ -8,3 +8,6 @@ edition = "2021"
|
||||
kernel = { path = "../kernel" }
|
||||
enum_primitive = { path = "../libraries/enum_primitive" }
|
||||
tickv = { path = "../libraries/tickv" }
|
||||
@@ -10,10 +10,10 @@ index 680fe32b2..4f757b93d 100644
|
||||
+[features]
|
||||
+vendor_hid = []
|
||||
diff --git a/capsules/src/usb/descriptors.rs b/capsules/src/usb/descriptors.rs
|
||||
index dea2dfed9..66a16fe87 100644
|
||||
index 67f708239..c2556d3a2 100644
|
||||
--- a/capsules/src/usb/descriptors.rs
|
||||
+++ b/capsules/src/usb/descriptors.rs
|
||||
@@ -414,13 +414,14 @@ impl DescriptorBuffer {
|
||||
@@ -415,13 +415,14 @@ impl DescriptorBuffer {
|
||||
/// example, if the interface descriptor list contains `[ID1, ID2, ID3]`,
|
||||
/// and the endpoint descriptors list is `[[ED1, ED2], [ED3, ED4, ED5],
|
||||
/// [ED6]]`, then the third interface descriptor (`ID3`) has one
|
||||
@@ -30,7 +30,7 @@ index dea2dfed9..66a16fe87 100644
|
||||
cdc_descriptor: Option<&[CdcInterfaceDescriptor]>,
|
||||
) -> (DeviceBuffer, DescriptorBuffer) {
|
||||
// Create device descriptor buffer and fill.
|
||||
@@ -504,7 +505,7 @@ pub fn create_descriptor_buffers(
|
||||
@@ -505,7 +506,7 @@ pub fn create_descriptor_buffers(
|
||||
.iter()
|
||||
.map(|descs| descs.iter().map(|d| d.size()).sum::<usize>())
|
||||
.sum::<usize>()
|
||||
@@ -39,7 +39,7 @@ index dea2dfed9..66a16fe87 100644
|
||||
+ cdc_descriptor.map_or(0, |ds| ds.iter().map(|d| d.size()).sum::<usize>());
|
||||
|
||||
// Set the number of endpoints for each interface descriptor.
|
||||
@@ -521,13 +522,11 @@ pub fn create_descriptor_buffers(
|
||||
@@ -522,12 +523,10 @@ pub fn create_descriptor_buffers(
|
||||
// Add the interface descriptor.
|
||||
len += d.write_to(&other_buf.buf[len..]);
|
||||
|
||||
@@ -49,20 +49,18 @@ index dea2dfed9..66a16fe87 100644
|
||||
- // HID descriptor, if any.
|
||||
- if let Some(dh) = hid_descriptor {
|
||||
- len += dh.write_to(&other_buf.buf[len..]);
|
||||
- }
|
||||
+ // HID descriptor, if present, for this interface.
|
||||
+ if let Some(dh) = hid_descriptor {
|
||||
+ if let Some(d) = dh.get(i) {
|
||||
+ len += d.write_to(&other_buf.buf[len..]);
|
||||
+ }
|
||||
+ if let Some(d) = dh.get(i) {
|
||||
+ len += d.write_to(&other_buf.buf[len..]);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is a CDC descriptor array, we include
|
||||
diff --git a/capsules/src/usb/usbc_client_ctrl.rs b/capsules/src/usb/usbc_client_ctrl.rs
|
||||
index f7899d8c5..6956523c6 100644
|
||||
index c8e55a8a8..54c7c3bbb 100644
|
||||
--- a/capsules/src/usb/usbc_client_ctrl.rs
|
||||
+++ b/capsules/src/usb/usbc_client_ctrl.rs
|
||||
@@ -38,6 +38,12 @@ const DESCRIPTOR_BUFLEN: usize = 128;
|
||||
@@ -40,6 +40,12 @@ const DESCRIPTOR_BUFLEN: usize = 128;
|
||||
|
||||
const N_ENDPOINTS: usize = 3;
|
||||
|
||||
@@ -75,7 +73,7 @@ index f7899d8c5..6956523c6 100644
|
||||
/// Handler for USB control endpoint requests.
|
||||
pub struct ClientCtrl<'a, 'b, U: 'a> {
|
||||
/// The USB hardware controller.
|
||||
@@ -64,12 +70,12 @@ pub struct ClientCtrl<'a, 'b, U: 'a> {
|
||||
@@ -66,12 +72,12 @@ pub struct ClientCtrl<'a, 'b, U: 'a> {
|
||||
|
||||
/// An optional HID descriptor for the configuration. This can be requested
|
||||
/// separately. It must also be included in `other_descriptor_buffer` if it exists.
|
||||
@@ -90,7 +88,7 @@ index f7899d8c5..6956523c6 100644
|
||||
|
||||
/// Supported language (only one for now).
|
||||
language: &'b [u16; 1],
|
||||
@@ -104,8 +110,8 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
|
||||
@@ -106,8 +112,8 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
|
||||
controller: &'a U,
|
||||
device_descriptor_buffer: DeviceBuffer,
|
||||
other_descriptor_buffer: DescriptorBuffer,
|
||||
@@ -101,7 +99,7 @@ index f7899d8c5..6956523c6 100644
|
||||
language: &'b [u16; 1],
|
||||
strings: &'b [&'b str],
|
||||
) -> Self {
|
||||
@@ -331,28 +337,39 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
|
||||
@@ -333,28 +339,39 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
|
||||
descriptor_type,
|
||||
// TODO: use the descriptor index
|
||||
descriptor_index: _,
|
||||
@@ -155,163 +153,3 @@ index f7899d8c5..6956523c6 100644
|
||||
} else {
|
||||
hil::usb::CtrlSetupResult::ErrGeneric
|
||||
}
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
index 642039120..abf224f97 100644
|
||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -44,21 +44,59 @@ static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
|
||||
0xC0, // HID_EndCollection
|
||||
];
|
||||
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+static VENDOR_REPORT_DESCRIPTOR: &'static [u8] = &[
|
||||
+ 0x06, 0x00, 0xFF, // HID_UsagePage ( VENDOR ),
|
||||
+ 0x09, 0x01, // HID_Usage ( Unused ),
|
||||
+ 0xA1, 0x01, // HID_Collection ( HID_Application ),
|
||||
+ 0x09, 0x20, // HID_Usage ( FIDO_USAGE_DATA_IN ),
|
||||
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
|
||||
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
|
||||
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
|
||||
+ 0x95, 0x40, // HID_ReportCount ( HID_INPUT_REPORT_BYTES ),
|
||||
+ 0x81, 0x02, // HID_Input ( HID_Data | HID_Absolute | HID_Variable ),
|
||||
+ 0x09, 0x21, // HID_Usage ( FIDO_USAGE_DATA_OUT ),
|
||||
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
|
||||
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
|
||||
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
|
||||
+ 0x95, 0x40, // HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ),
|
||||
+ 0x91, 0x02, // HID_Output ( HID_Data | HID_Absolute | HID_Variable ),
|
||||
+ 0xC0, // HID_EndCollection
|
||||
+];
|
||||
+
|
||||
static CTAP_REPORT: ReportDescriptor<'static> = ReportDescriptor {
|
||||
desc: CTAP_REPORT_DESCRIPTOR,
|
||||
};
|
||||
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+static VENDOR_REPORT: ReportDescriptor<'static> = ReportDescriptor {
|
||||
+ desc: VENDOR_REPORT_DESCRIPTOR,
|
||||
+};
|
||||
+
|
||||
static HID_SUB_DESCRIPTORS: &'static [HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
|
||||
typ: DescriptorType::Report,
|
||||
len: CTAP_REPORT_DESCRIPTOR.len() as u16,
|
||||
}];
|
||||
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+static VENDOR_HID_SUB_DESCRIPTORS: &'static [HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
|
||||
+ typ: DescriptorType::Report,
|
||||
+ len: VENDOR_REPORT_DESCRIPTOR.len() as u16,
|
||||
+}];
|
||||
+
|
||||
static HID: HIDDescriptor<'static> = HIDDescriptor {
|
||||
hid_class: 0x0110,
|
||||
country_code: HIDCountryCode::NotSupported,
|
||||
sub_descriptors: HID_SUB_DESCRIPTORS,
|
||||
};
|
||||
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+static VENDOR_HID: HIDDescriptor<'static> = HIDDescriptor {
|
||||
+ hid_class: 0x0110,
|
||||
+ country_code: HIDCountryCode::NotSupported,
|
||||
+ sub_descriptors: VENDOR_HID_SUB_DESCRIPTORS,
|
||||
+};
|
||||
+
|
||||
pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
|
||||
@@ -82,6 +120,9 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
product_id: u16,
|
||||
strings: &'static [&'static str],
|
||||
) -> Self {
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ debug!("vendor_hid enabled.");
|
||||
+
|
||||
let interfaces: &mut [InterfaceDescriptor] = &mut [
|
||||
// Interface declared in the FIDO2 specification, section 8.1.8.1
|
||||
InterfaceDescriptor {
|
||||
@@ -90,9 +131,19 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
interface_protocol: 0x00,
|
||||
..InterfaceDescriptor::default()
|
||||
},
|
||||
+ // Vendor HID interface.
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ InterfaceDescriptor {
|
||||
+ interface_number: 1,
|
||||
+ interface_class: 0x03, // HID
|
||||
+ interface_subclass: 0x00,
|
||||
+ interface_protocol: 0x00,
|
||||
+ ..InterfaceDescriptor::default()
|
||||
+ },
|
||||
];
|
||||
|
||||
let endpoints: &[&[EndpointDescriptor]] = &[&[
|
||||
+ // 2 Endpoints for FIDO
|
||||
EndpointDescriptor {
|
||||
endpoint_address: EndpointAddress::new_const(
|
||||
ENDPOINT_NUM,
|
||||
@@ -110,8 +161,30 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
transfer_type: TransferType::Interrupt,
|
||||
max_packet_size: 64,
|
||||
interval: 5,
|
||||
- },
|
||||
- ]];
|
||||
+ },],
|
||||
+ // 2 Endpoints for FIDO
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ &[
|
||||
+ EndpointDescriptor {
|
||||
+ endpoint_address: EndpointAddress::new_const(
|
||||
+ ENDPOINT_NUM + 1,
|
||||
+ TransferDirection::HostToDevice,
|
||||
+ ),
|
||||
+ transfer_type: TransferType::Interrupt,
|
||||
+ max_packet_size: 64,
|
||||
+ interval: 5,
|
||||
+ },
|
||||
+ EndpointDescriptor {
|
||||
+ endpoint_address: EndpointAddress::new_const(
|
||||
+ ENDPOINT_NUM + 1,
|
||||
+ TransferDirection::DeviceToHost,
|
||||
+ ),
|
||||
+ transfer_type: TransferType::Interrupt,
|
||||
+ max_packet_size: 64,
|
||||
+ interval: 5,
|
||||
+ },
|
||||
+ ],
|
||||
+ ];
|
||||
|
||||
let (device_descriptor_buffer, other_descriptor_buffer) =
|
||||
descriptors::create_descriptor_buffers(
|
||||
@@ -130,17 +203,28 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
},
|
||||
interfaces,
|
||||
endpoints,
|
||||
- Some(&HID),
|
||||
+ Some(&[
|
||||
+ &HID,
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ &VENDOR_HID,
|
||||
+ ]),
|
||||
None, // No CDC descriptor array
|
||||
);
|
||||
-
|
||||
ClientCtapHID {
|
||||
client_ctrl: ClientCtrl::new(
|
||||
controller,
|
||||
device_descriptor_buffer,
|
||||
other_descriptor_buffer,
|
||||
- Some(&HID),
|
||||
- Some(&CTAP_REPORT),
|
||||
+ Some([
|
||||
+ &HID,
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ &VENDOR_HID,
|
||||
+ ]),
|
||||
+ Some([
|
||||
+ &CTAP_REPORT,
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ &VENDOR_REPORT,
|
||||
+ ]),
|
||||
LANGUAGES,
|
||||
strings,
|
||||
),
|
||||
@@ -1,338 +0,0 @@
|
||||
diff --git a/boards/components/src/firmware_protection.rs b/boards/components/src/firmware_protection.rs
|
||||
new file mode 100644
|
||||
index 000000000..58695af81
|
||||
--- /dev/null
|
||||
+++ b/boards/components/src/firmware_protection.rs
|
||||
@@ -0,0 +1,70 @@
|
||||
+//! Component for firmware protection syscall interface.
|
||||
+//!
|
||||
+//! This provides one Component, `FirmwareProtectionComponent`, which implements a
|
||||
+//! userspace syscall interface to enable the code readout protection.
|
||||
+//!
|
||||
+//! Usage
|
||||
+//! -----
|
||||
+//! ```rust
|
||||
+//! let crp = components::firmware_protection::FirmwareProtectionComponent::new(
|
||||
+//! board_kernel,
|
||||
+//! nrf52840::uicr::Uicr::new()
|
||||
+//! )
|
||||
+//! .finalize(
|
||||
+//! components::firmware_protection_component_helper!(uicr));
|
||||
+//! ```
|
||||
+
|
||||
+use core::mem::MaybeUninit;
|
||||
+
|
||||
+use capsules::firmware_protection;
|
||||
+use kernel::capabilities;
|
||||
+use kernel::component::Component;
|
||||
+use kernel::create_capability;
|
||||
+use kernel::hil;
|
||||
+use kernel::static_init_half;
|
||||
+
|
||||
+// Setup static space for the objects.
|
||||
+#[macro_export]
|
||||
+macro_rules! firmware_protection_component_helper {
|
||||
+ ($C:ty) => {{
|
||||
+ use capsules::firmware_protection;
|
||||
+ use core::mem::MaybeUninit;
|
||||
+ static mut BUF: MaybeUninit<firmware_protection::FirmwareProtection<$C>> =
|
||||
+ MaybeUninit::uninit();
|
||||
+ &mut BUF
|
||||
+ };};
|
||||
+}
|
||||
+
|
||||
+pub struct FirmwareProtectionComponent<C: hil::firmware_protection::FirmwareProtection> {
|
||||
+ board_kernel: &'static kernel::Kernel,
|
||||
+ crp: C,
|
||||
+}
|
||||
+
|
||||
+impl<C: 'static + hil::firmware_protection::FirmwareProtection> FirmwareProtectionComponent<C> {
|
||||
+ pub fn new(board_kernel: &'static kernel::Kernel, crp: C) -> FirmwareProtectionComponent<C> {
|
||||
+ FirmwareProtectionComponent {
|
||||
+ board_kernel: board_kernel,
|
||||
+ crp: crp,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<C: 'static + hil::firmware_protection::FirmwareProtection> Component
|
||||
+ for FirmwareProtectionComponent<C>
|
||||
+{
|
||||
+ type StaticInput = &'static mut MaybeUninit<firmware_protection::FirmwareProtection<C>>;
|
||||
+ type Output = &'static firmware_protection::FirmwareProtection<C>;
|
||||
+
|
||||
+ unsafe fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
|
||||
+ let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
|
||||
+
|
||||
+ static_init_half!(
|
||||
+ static_buffer,
|
||||
+ firmware_protection::FirmwareProtection<C>,
|
||||
+ firmware_protection::FirmwareProtection::new(
|
||||
+ self.crp,
|
||||
+ self.board_kernel.create_grant(&grant_cap),
|
||||
+ )
|
||||
+ )
|
||||
+ }
|
||||
+}
|
||||
diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs
|
||||
index a3bbe724b..95625f91f 100644
|
||||
--- a/boards/components/src/lib.rs
|
||||
+++ b/boards/components/src/lib.rs
|
||||
@@ -13,6 +13,7 @@ pub mod crc;
|
||||
pub mod ctap;
|
||||
pub mod debug_queue;
|
||||
pub mod debug_writer;
|
||||
+pub mod firmware_protection;
|
||||
pub mod ft6x06;
|
||||
pub mod fxos8700;
|
||||
pub mod gpio;
|
||||
diff --git a/capsules/src/driver.rs b/capsules/src/driver.rs
|
||||
index b6124c4c0..90a18ea47 100644
|
||||
--- a/capsules/src/driver.rs
|
||||
+++ b/capsules/src/driver.rs
|
||||
@@ -16,6 +16,7 @@ pub enum NUM {
|
||||
Adc = 0x00005,
|
||||
Dac = 0x00006,
|
||||
AnalogComparator = 0x00007,
|
||||
+ FirmwareProtection = 0x00008,
|
||||
|
||||
// Kernel
|
||||
Ipc = 0x10000,
|
||||
diff --git a/capsules/src/firmware_protection.rs b/capsules/src/firmware_protection.rs
|
||||
new file mode 100644
|
||||
index 000000000..8cf63d6a4
|
||||
--- /dev/null
|
||||
+++ b/capsules/src/firmware_protection.rs
|
||||
@@ -0,0 +1,85 @@
|
||||
+//! Provides userspace control of firmware protection on a board.
|
||||
+//!
|
||||
+//! This allows an application to enable firware readout protection,
|
||||
+//! disabling JTAG interface and other ways to read/tamper the firmware.
|
||||
+//! Of course, outside of a hardware bug, once set, the only way to enable
|
||||
+//! programming/debugging is by fully erasing the flash.
|
||||
+//!
|
||||
+//! Usage
|
||||
+//! -----
|
||||
+//!
|
||||
+//! ```rust
|
||||
+//! # use kernel::static_init;
|
||||
+//!
|
||||
+//! let crp = static_init!(
|
||||
+//! capsules::firmware_protection::FirmwareProtection,
|
||||
+//! capsules::firmware_protection::FirmwareProtection::new(
|
||||
+//! nrf52840::uicr::Uicr,
|
||||
+//! board_kernel.create_grant(&grant_cap),
|
||||
+//! );
|
||||
+//! ```
|
||||
+//!
|
||||
+//! Syscall Interface
|
||||
+//! -----------------
|
||||
+//!
|
||||
+//! - Stability: 0 - Draft
|
||||
+//!
|
||||
+//! ### Command
|
||||
+//!
|
||||
+//! Enable code readout protection on the current board.
|
||||
+//!
|
||||
+//! #### `command_num`
|
||||
+//!
|
||||
+//! - `0`: Driver check.
|
||||
+//! - `1`: Get current firmware readout protection (aka CRP) state.
|
||||
+//! - `2`: Set current firmware readout protection (aka CRP) state.
|
||||
+//!
|
||||
+
|
||||
+use kernel::hil;
|
||||
+use kernel::{AppId, Callback, Driver, Grant, ReturnCode};
|
||||
+
|
||||
+/// Syscall driver number.
|
||||
+use crate::driver;
|
||||
+pub const DRIVER_NUM: usize = driver::NUM::FirmwareProtection as usize;
|
||||
+
|
||||
+pub struct FirmwareProtection<C: hil::firmware_protection::FirmwareProtection> {
|
||||
+ crp_unit: C,
|
||||
+ apps: Grant<Option<Callback>>,
|
||||
+}
|
||||
+
|
||||
+impl<C: hil::firmware_protection::FirmwareProtection> FirmwareProtection<C> {
|
||||
+ pub fn new(crp_unit: C, apps: Grant<Option<Callback>>) -> Self {
|
||||
+ Self { crp_unit, apps }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<C: hil::firmware_protection::FirmwareProtection> Driver for FirmwareProtection<C> {
|
||||
+ ///
|
||||
+ /// ### Command numbers
|
||||
+ ///
|
||||
+ /// * `0`: Returns non-zero to indicate the driver is present.
|
||||
+ /// * `1`: Gets firmware protection state.
|
||||
+ /// * `2`: Sets firmware protection state.
|
||||
+ fn command(&self, command_num: usize, data: usize, _: usize, appid: AppId) -> ReturnCode {
|
||||
+ match command_num {
|
||||
+ // return if driver is available
|
||||
+ 0 => ReturnCode::SUCCESS,
|
||||
+
|
||||
+ 1 => self
|
||||
+ .apps
|
||||
+ .enter(appid, |_, _| ReturnCode::SuccessWithValue {
|
||||
+ value: self.crp_unit.get_protection() as usize,
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+
|
||||
+ // sets firmware protection
|
||||
+ 2 => self
|
||||
+ .apps
|
||||
+ .enter(appid, |_, _| self.crp_unit.set_protection(data.into()))
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+
|
||||
+ // default
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/capsules/src/lib.rs b/capsules/src/lib.rs
|
||||
index 925b4ba41..919638c5d 100644
|
||||
--- a/capsules/src/lib.rs
|
||||
+++ b/capsules/src/lib.rs
|
||||
@@ -25,6 +25,7 @@ pub mod ctap;
|
||||
pub mod dac;
|
||||
pub mod debug_process_restart;
|
||||
pub mod driver;
|
||||
+pub mod firmware_protection;
|
||||
pub mod fm25cl;
|
||||
pub mod ft6x06;
|
||||
pub mod fxos8700cq;
|
||||
diff --git a/chips/nrf52/src/uicr.rs b/chips/nrf52/src/uicr.rs
|
||||
index 788533210..655a98d5b 100644
|
||||
--- a/chips/nrf52/src/uicr.rs
|
||||
+++ b/chips/nrf52/src/uicr.rs
|
||||
@@ -1,12 +1,14 @@
|
||||
//! User information configuration registers
|
||||
|
||||
use enum_primitive::cast::FromPrimitive;
|
||||
+use hil::firmware_protection::ProtectionLevel;
|
||||
use kernel::common::registers::{register_bitfields, register_structs, ReadWrite};
|
||||
use kernel::common::StaticRef;
|
||||
use kernel::hil;
|
||||
use kernel::ReturnCode;
|
||||
|
||||
use crate::gpio::Pin;
|
||||
+use crate::nvmc;
|
||||
|
||||
const UICR_BASE: StaticRef<UicrRegisters> =
|
||||
unsafe { StaticRef::new(0x10001000 as *const UicrRegisters) };
|
||||
@@ -217,3 +219,49 @@ impl Uicr {
|
||||
self.registers.approtect.write(ApProtect::PALL::ENABLED);
|
||||
}
|
||||
}
|
||||
+
|
||||
+impl hil::firmware_protection::FirmwareProtection for Uicr {
|
||||
+ fn get_protection(&self) -> ProtectionLevel {
|
||||
+ let ap_protect_state = self.is_ap_protect_enabled();
|
||||
+ let cpu_debug_state = self
|
||||
+ .registers
|
||||
+ .debugctrl
|
||||
+ .matches_all(DebugControl::CPUNIDEN::ENABLED + DebugControl::CPUFPBEN::ENABLED);
|
||||
+ match (ap_protect_state, cpu_debug_state) {
|
||||
+ (false, _) => ProtectionLevel::NoProtection,
|
||||
+ (true, true) => ProtectionLevel::JtagDisabled,
|
||||
+ (true, false) => ProtectionLevel::FullyLocked,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn set_protection(&self, level: ProtectionLevel) -> ReturnCode {
|
||||
+ let current_level = self.get_protection();
|
||||
+ if current_level > level || level == ProtectionLevel::Unknown {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ }
|
||||
+ if current_level == level {
|
||||
+ return ReturnCode::EALREADY;
|
||||
+ }
|
||||
+
|
||||
+ nvmc::Nvmc::new().configure_writeable();
|
||||
+ if level >= ProtectionLevel::JtagDisabled {
|
||||
+ self.set_ap_protect();
|
||||
+ }
|
||||
+
|
||||
+ if level >= ProtectionLevel::FullyLocked {
|
||||
+ // Prevent CPU debug and flash patching. Leaving these enabled could
|
||||
+ // allow to circumvent protection.
|
||||
+ self.registers
|
||||
+ .debugctrl
|
||||
+ .write(DebugControl::CPUNIDEN::DISABLED + DebugControl::CPUFPBEN::DISABLED);
|
||||
+ // TODO(jmichel): prevent returning into bootloader if present
|
||||
+ }
|
||||
+ nvmc::Nvmc::new().configure_readonly();
|
||||
+
|
||||
+ if self.get_protection() == level {
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ ReturnCode::FAIL
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/kernel/src/hil/firmware_protection.rs b/kernel/src/hil/firmware_protection.rs
|
||||
new file mode 100644
|
||||
index 000000000..de0824646
|
||||
--- /dev/null
|
||||
+++ b/kernel/src/hil/firmware_protection.rs
|
||||
@@ -0,0 +1,48 @@
|
||||
+//! Interface for Firmware Protection, also called Code Readout Protection.
|
||||
+
|
||||
+use crate::returncode::ReturnCode;
|
||||
+
|
||||
+#[derive(PartialOrd, PartialEq)]
|
||||
+pub enum ProtectionLevel {
|
||||
+ /// Unsupported feature
|
||||
+ Unknown = 0,
|
||||
+ /// This should be the factory default for the chip.
|
||||
+ NoProtection = 1,
|
||||
+ /// At this level, only JTAG/SWD are disabled but other debugging
|
||||
+ /// features may still be enabled.
|
||||
+ JtagDisabled = 2,
|
||||
+ /// This is the maximum level of protection the chip supports.
|
||||
+ /// At this level, JTAG and all other features are expected to be
|
||||
+ /// disabled and only a full chip erase may allow to recover from
|
||||
+ /// that state.
|
||||
+ FullyLocked = 0xff,
|
||||
+}
|
||||
+
|
||||
+impl From<usize> for ProtectionLevel {
|
||||
+ fn from(value: usize) -> Self {
|
||||
+ match value {
|
||||
+ 1 => ProtectionLevel::NoProtection,
|
||||
+ 2 => ProtectionLevel::JtagDisabled,
|
||||
+ 0xff => ProtectionLevel::FullyLocked,
|
||||
+ _ => ProtectionLevel::Unknown,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub trait FirmwareProtection {
|
||||
+ /// Gets the current firmware protection level.
|
||||
+ /// This doesn't fail and always returns a value.
|
||||
+ fn get_protection(&self) -> ProtectionLevel;
|
||||
+
|
||||
+ /// Sets the firmware protection level.
|
||||
+ /// There are four valid return values:
|
||||
+ /// - SUCCESS: protection level has been set to `level`
|
||||
+ /// - FAIL: something went wrong while setting the protection
|
||||
+ /// level and the effective protection level is not the one
|
||||
+ /// that was requested.
|
||||
+ /// - EALREADY: the requested protection level is already the
|
||||
+ /// level that is set.
|
||||
+ /// - EINVAL: unsupported protection level or the requested
|
||||
+ /// protection level is lower than the currently set one.
|
||||
+ fn set_protection(&self, level: ProtectionLevel) -> ReturnCode;
|
||||
+}
|
||||
diff --git a/kernel/src/hil/mod.rs b/kernel/src/hil/mod.rs
|
||||
index f2d3629a9..d6eb39d2d 100644
|
||||
--- a/kernel/src/hil/mod.rs
|
||||
+++ b/kernel/src/hil/mod.rs
|
||||
@@ -9,6 +9,7 @@ pub mod dac;
|
||||
pub mod digest;
|
||||
pub mod eic;
|
||||
pub mod entropy;
|
||||
+pub mod firmware_protection;
|
||||
pub mod flash;
|
||||
pub mod gpio;
|
||||
pub mod gpio_async;
|
||||
24
patches/tock/05-kernel-utility-method.patch
Normal file
24
patches/tock/05-kernel-utility-method.patch
Normal file
@@ -0,0 +1,24 @@
|
||||
diff --git a/kernel/src/syscall_driver.rs b/kernel/src/syscall_driver.rs
|
||||
index 0e2943fe4..3b5b42486 100644
|
||||
--- a/kernel/src/syscall_driver.rs
|
||||
+++ b/kernel/src/syscall_driver.rs
|
||||
@@ -102,6 +102,19 @@ impl CommandReturn {
|
||||
self.0
|
||||
}
|
||||
|
||||
+ /// Check whether the inner `SyscallReturn` value is successful
|
||||
+ pub fn is_success(&self) -> bool {
|
||||
+ matches!(
|
||||
+ self.0,
|
||||
+ SyscallReturn::Success
|
||||
+ | SyscallReturn::SuccessU32(_)
|
||||
+ | SyscallReturn::SuccessU32U32(_, _)
|
||||
+ | SyscallReturn::SuccessU32U32U32(_, _, _)
|
||||
+ | SyscallReturn::SuccessU32U64(_, _)
|
||||
+ | SyscallReturn::SuccessU64(_)
|
||||
+ )
|
||||
+ }
|
||||
+
|
||||
/// Command error
|
||||
pub fn failure(rc: ErrorCode) -> Self {
|
||||
CommandReturn(SyscallReturn::Failure(rc))
|
||||
@@ -1,43 +1,53 @@
|
||||
diff --git a/chips/nrf52/src/nvmc.rs b/chips/nrf52/src/nvmc.rs
|
||||
index adbc2a2b5..4092cf346 100644
|
||||
index 61e94260e..b7c3be3f6 100644
|
||||
--- a/chips/nrf52/src/nvmc.rs
|
||||
+++ b/chips/nrf52/src/nvmc.rs
|
||||
@@ -3,15 +3,19 @@
|
||||
//! Used in order read and write to internal flash.
|
||||
|
||||
@@ -5,7 +5,14 @@
|
||||
use core::cell::Cell;
|
||||
+use core::convert::TryFrom;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use kernel::common::cells::OptionalCell;
|
||||
use kernel::common::cells::TakeCell;
|
||||
use kernel::common::cells::VolatileCell;
|
||||
use kernel::common::deferred_call::DeferredCall;
|
||||
+use kernel::common::dynamic_deferred_call::{
|
||||
use kernel::deferred_call::DeferredCall;
|
||||
+use kernel::dynamic_deferred_call::{
|
||||
+ DeferredCallHandle, DynamicDeferredCall, DynamicDeferredCallClient,
|
||||
+};
|
||||
use kernel::common::registers::{register_bitfields, ReadOnly, ReadWrite};
|
||||
use kernel::common::StaticRef;
|
||||
+use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
|
||||
use kernel::hil;
|
||||
-use kernel::ReturnCode;
|
||||
+use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
|
||||
|
||||
use crate::deferred_call_tasks::DeferredCallTask;
|
||||
|
||||
@@ -141,7 +145,13 @@ register_bitfields! [u32,
|
||||
+use kernel::process::ProcessId;
|
||||
+use kernel::processbuffer::ReadableProcessBuffer;
|
||||
+use kernel::syscall::CommandReturn;
|
||||
use kernel::utilities::cells::OptionalCell;
|
||||
use kernel::utilities::cells::TakeCell;
|
||||
use kernel::utilities::cells::VolatileCell;
|
||||
@@ -142,7 +149,13 @@ register_bitfields! [u32,
|
||||
static DEFERRED_CALL: DeferredCall<DeferredCallTask> =
|
||||
unsafe { DeferredCall::new(DeferredCallTask::Nvmc) };
|
||||
|
||||
+type WORD = u32;
|
||||
+const WORD_SIZE: usize = core::mem::size_of::<WORD>();
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
+const MAX_WORD_WRITES: usize = 2;
|
||||
+const MAX_PAGE_ERASES: usize = 10000;
|
||||
+const MAX_WORD_WRITES: u32 = 2;
|
||||
+const MAX_PAGE_ERASES: u32 = 10_000;
|
||||
+const WORD_MASK: usize = WORD_SIZE - 1;
|
||||
+const PAGE_MASK: usize = PAGE_SIZE - 1;
|
||||
|
||||
/// This is a wrapper around a u8 array that is sized to a single page for the
|
||||
/// nrf. Users of this module must pass an object of this type to use the
|
||||
@@ -217,6 +227,10 @@ impl Nvmc {
|
||||
@@ -157,13 +170,11 @@ const PAGE_SIZE: usize = 4096;
|
||||
///
|
||||
/// let pagebuffer = unsafe { static_init!(NrfPage, NrfPage::default()) };
|
||||
/// ```
|
||||
-pub struct NrfPage(pub [u8; PAGE_SIZE as usize]);
|
||||
+pub struct NrfPage(pub [u8; PAGE_SIZE]);
|
||||
|
||||
impl Default for NrfPage {
|
||||
fn default() -> Self {
|
||||
- Self {
|
||||
- 0: [0; PAGE_SIZE as usize],
|
||||
- }
|
||||
+ Self { 0: [0; PAGE_SIZE] }
|
||||
}
|
||||
}
|
||||
impl NrfPage {
|
||||
@@ -218,6 +229,10 @@ impl Nvmc {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,18 +58,15 @@ index adbc2a2b5..4092cf346 100644
|
||||
/// Configure the NVMC to allow writes to flash.
|
||||
pub fn configure_writeable(&self) {
|
||||
self.registers.config.write(Configuration::WEN::Wen);
|
||||
@@ -229,9 +243,7 @@ impl Nvmc {
|
||||
pub fn erase_uicr(&self) {
|
||||
self.registers.config.write(Configuration::WEN::Een);
|
||||
while !self.is_ready() {}
|
||||
- self.registers
|
||||
- .erasepage
|
||||
- .write(ErasePage::ERASEPAGE.val(0x10001000));
|
||||
@@ -233,6 +248,7 @@ impl Nvmc {
|
||||
self.registers
|
||||
.erasepage
|
||||
.write(ErasePage::ERASEPAGE.val(0x10001000));
|
||||
+ self.registers.eraseuicr.write(EraseUicr::ERASEUICR::ERASE);
|
||||
while !self.is_ready() {}
|
||||
}
|
||||
|
||||
@@ -319,7 +331,7 @@ impl Nvmc {
|
||||
@@ -320,7 +336,7 @@ impl Nvmc {
|
||||
// Put the NVMC in write mode.
|
||||
self.registers.config.write(Configuration::WEN::Wen);
|
||||
|
||||
@@ -68,11 +75,13 @@ index adbc2a2b5..4092cf346 100644
|
||||
let word: u32 = (data[i + 0] as u32) << 0
|
||||
| (data[i + 1] as u32) << 8
|
||||
| (data[i + 2] as u32) << 16
|
||||
@@ -387,3 +399,237 @@ impl hil::flash::Flash for Nvmc {
|
||||
@@ -388,3 +404,228 @@ impl hil::flash::Flash for Nvmc {
|
||||
self.erase_page(page_number)
|
||||
}
|
||||
}
|
||||
+
|
||||
+type NvmcDriverGrant = Grant<App, UpcallCount<1>, AllowRoCount<1>, AllowRwCount<0>>;
|
||||
+
|
||||
+/// Provides access to the writeable flash regions of the application.
|
||||
+///
|
||||
+/// The purpose of this driver is to provide low-level access to the embedded flash of nRF52 boards
|
||||
@@ -112,21 +121,17 @@ index adbc2a2b5..4092cf346 100644
|
||||
+/// - ALLOW(0): The allow slice for COMMAND(2).
|
||||
+pub struct SyscallDriver {
|
||||
+ nvmc: &'static Nvmc,
|
||||
+ apps: Grant<App>,
|
||||
+ waiting: OptionalCell<AppId>,
|
||||
+ apps: NvmcDriverGrant,
|
||||
+ waiting: OptionalCell<ProcessId>,
|
||||
+ deferred_caller: &'static DynamicDeferredCall,
|
||||
+ deferred_handle: OptionalCell<DeferredCallHandle>,
|
||||
+ buffer: TakeCell<'static, [u8]>,
|
||||
+}
|
||||
+
|
||||
+pub const DRIVER_NUM: usize = 0x50003;
|
||||
+
|
||||
+#[derive(Default)]
|
||||
+pub struct App {
|
||||
+ /// The callback for COMMAND(2) and COMMAND(3).
|
||||
+ callback: Option<Callback>,
|
||||
+ /// The allow slice for COMMAND(2).
|
||||
+ slice: Option<AppSlice<Shared, u8>>,
|
||||
+}
|
||||
+pub struct App {}
|
||||
+
|
||||
+fn is_write_needed(old: u32, new: u32) -> bool {
|
||||
+ // No need to write if it would not modify the current value.
|
||||
@@ -136,8 +141,9 @@ index adbc2a2b5..4092cf346 100644
|
||||
+impl SyscallDriver {
|
||||
+ pub fn new(
|
||||
+ nvmc: &'static Nvmc,
|
||||
+ apps: Grant<App>,
|
||||
+ apps: NvmcDriverGrant,
|
||||
+ deferred_caller: &'static DynamicDeferredCall,
|
||||
+ buffer: &'static mut [u8],
|
||||
+ ) -> SyscallDriver {
|
||||
+ nvmc.configure_readonly();
|
||||
+ SyscallDriver {
|
||||
@@ -146,6 +152,7 @@ index adbc2a2b5..4092cf346 100644
|
||||
+ waiting: OptionalCell::empty(),
|
||||
+ deferred_caller,
|
||||
+ deferred_handle: OptionalCell::empty(),
|
||||
+ buffer: TakeCell::new(buffer),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@@ -171,9 +178,9 @@ index adbc2a2b5..4092cf346 100644
|
||||
+ /// - `ptr` must be word-aligned.
|
||||
+ /// - `slice.len()` must be word-aligned.
|
||||
+ /// - The slice starting at `ptr` of length `slice.len()` must fit in the storage.
|
||||
+ fn write_slice(&self, ptr: usize, slice: &[u8]) -> ReturnCode {
|
||||
+ fn write_slice(&self, ptr: usize, slice: &[u8]) -> CommandReturn {
|
||||
+ if ptr & WORD_MASK != 0 || slice.len() & WORD_MASK != 0 {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ return CommandReturn::failure(ErrorCode::INVAL);
|
||||
+ }
|
||||
+ self.nvmc.configure_writeable();
|
||||
+ for (i, chunk) in slice.chunks(WORD_SIZE).enumerate() {
|
||||
@@ -188,7 +195,7 @@ index adbc2a2b5..4092cf346 100644
|
||||
+ self.nvmc.configure_readonly();
|
||||
+ self.deferred_handle
|
||||
+ .map(|handle| self.deferred_caller.set(*handle));
|
||||
+ ReturnCode::SUCCESS
|
||||
+ CommandReturn::success()
|
||||
+ }
|
||||
+
|
||||
+ /// Erases a page at a page-aligned address.
|
||||
@@ -198,162 +205,249 @@ index adbc2a2b5..4092cf346 100644
|
||||
+ /// Fails with `EINVAL` if any of the following conditions does not hold:
|
||||
+ /// - `ptr` must be page-aligned.
|
||||
+ /// - The slice starting at `ptr` of length `PAGE_SIZE` must fit in the storage.
|
||||
+ fn erase_page(&self, ptr: usize) -> ReturnCode {
|
||||
+ fn erase_page(&self, ptr: usize) -> CommandReturn {
|
||||
+ if ptr & PAGE_MASK != 0 {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ return CommandReturn::failure(ErrorCode::INVAL);
|
||||
+ }
|
||||
+ self.nvmc.erase_page_helper(ptr / PAGE_SIZE);
|
||||
+ self.nvmc.configure_readonly();
|
||||
+ self.deferred_handle
|
||||
+ .map(|handle| self.deferred_caller.set(*handle));
|
||||
+ ReturnCode::SUCCESS
|
||||
+ CommandReturn::success()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl DynamicDeferredCallClient for SyscallDriver {
|
||||
+ fn call(&self, _handle: DeferredCallHandle) {
|
||||
+ self.waiting.take().map(|appid| {
|
||||
+ self.apps.enter(appid, |app, _| {
|
||||
+ app.callback.map(|mut cb| {
|
||||
+ cb.schedule(0, 0, 0);
|
||||
+ });
|
||||
+ self.waiting.take().map(|process_id| {
|
||||
+ self.apps.enter(process_id, |_, kernel_data| {
|
||||
+ kernel_data.schedule_upcall(0, (0, 0, 0))
|
||||
+ })
|
||||
+ });
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Driver for SyscallDriver {
|
||||
+ fn subscribe(
|
||||
+ &self,
|
||||
+ subscribe_num: usize,
|
||||
+ callback: Option<Callback>,
|
||||
+ appid: AppId,
|
||||
+ ) -> ReturnCode {
|
||||
+ match subscribe_num {
|
||||
+ 0 => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ app.callback = callback;
|
||||
+ ReturnCode::SUCCESS
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+ }
|
||||
+impl kernel::syscall::SyscallDriver for SyscallDriver {
|
||||
+ fn allocate_grant(&self, process_id: ProcessId) -> Result<(), kernel::process::Error> {
|
||||
+ self.apps.enter(process_id, |_, _| {})
|
||||
+ }
|
||||
+
|
||||
+ fn command(&self, cmd: usize, arg0: usize, arg1: usize, appid: AppId) -> ReturnCode {
|
||||
+ match (cmd, arg0, arg1) {
|
||||
+ (0, _, _) => ReturnCode::SUCCESS,
|
||||
+ fn command(
|
||||
+ &self,
|
||||
+ command_num: usize,
|
||||
+ r2: usize,
|
||||
+ r3: usize,
|
||||
+ process_id: ProcessId,
|
||||
+ ) -> CommandReturn {
|
||||
+ match (command_num, r2, r3) {
|
||||
+ (0, _, _) => CommandReturn::success(),
|
||||
+
|
||||
+ (1, 0, _) => ReturnCode::SuccessWithValue { value: WORD_SIZE },
|
||||
+ (1, 1, _) => ReturnCode::SuccessWithValue { value: PAGE_SIZE },
|
||||
+ (1, 2, _) => ReturnCode::SuccessWithValue {
|
||||
+ value: MAX_WORD_WRITES,
|
||||
+ },
|
||||
+ (1, 3, _) => ReturnCode::SuccessWithValue {
|
||||
+ value: MAX_PAGE_ERASES,
|
||||
+ },
|
||||
+ (1, _, _) => ReturnCode::EINVAL,
|
||||
+ (1, 0, _) => CommandReturn::success_u32(WORD_SIZE.try_into().unwrap()),
|
||||
+ (1, 1, _) => CommandReturn::success_u32(PAGE_SIZE.try_into().unwrap()),
|
||||
+ (1, 2, _) => CommandReturn::success_u32(MAX_WORD_WRITES),
|
||||
+ (1, 3, _) => CommandReturn::success_u32(MAX_PAGE_ERASES),
|
||||
+ (1, _, _) => CommandReturn::failure(ErrorCode::INVAL),
|
||||
+
|
||||
+ (2, ptr, len) => self
|
||||
+ (2, ptr, _len) => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ let slice = match app.slice.take() {
|
||||
+ None => return ReturnCode::EINVAL,
|
||||
+ Some(slice) => slice,
|
||||
+ };
|
||||
+ if len != slice.len() {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ }
|
||||
+ if self.waiting.is_some() {
|
||||
+ return ReturnCode::EBUSY;
|
||||
+ }
|
||||
+ self.waiting.set(appid);
|
||||
+ self.write_slice(ptr, slice.as_ref())
|
||||
+ .enter(process_id, |_, kernel| {
|
||||
+ kernel
|
||||
+ .get_readonly_processbuffer(0)
|
||||
+ .and_then(|processbuffer| {
|
||||
+ processbuffer.enter(|app_buf| {
|
||||
+ // Copy contents to the internal buffer first
|
||||
+ self.buffer.take().map_or(
|
||||
+ CommandReturn::failure(ErrorCode::RESERVE),
|
||||
+ |buffer| {
|
||||
+ // as the drivers buffer can be bigger than the app buffer,
|
||||
+ // we choose the minimum to not copy anymore than we need
|
||||
+ let len = core::cmp::min(buffer.len(), app_buf.len());
|
||||
+
|
||||
+ // safety check when the app buffer is too large
|
||||
+ if app_buf.len() > buffer.len() {
|
||||
+ return CommandReturn::failure(ErrorCode::INVAL);
|
||||
+ }
|
||||
+
|
||||
+ let d = &app_buf[0..len];
|
||||
+ for (i, v) in buffer.as_mut()[0..len].iter_mut().enumerate()
|
||||
+ {
|
||||
+ *v = d[i].get();
|
||||
+ }
|
||||
+
|
||||
+ if self.waiting.is_some() {
|
||||
+ return CommandReturn::failure(ErrorCode::BUSY);
|
||||
+ }
|
||||
+ self.waiting.set(process_id);
|
||||
+ let result = self.write_slice(ptr, &buffer[0..len]);
|
||||
+ self.buffer.replace(buffer);
|
||||
+ result
|
||||
+ },
|
||||
+ )
|
||||
+ })
|
||||
+ })
|
||||
+ .unwrap_or(CommandReturn::failure(ErrorCode::RESERVE))
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ .unwrap_or_else(|e| CommandReturn::failure(e.into())),
|
||||
+
|
||||
+ (3, ptr, len) => {
|
||||
+ if len != PAGE_SIZE {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ return CommandReturn::failure(ErrorCode::INVAL);
|
||||
+ }
|
||||
+ if self.waiting.is_some() {
|
||||
+ return ReturnCode::EBUSY;
|
||||
+ return CommandReturn::failure(ErrorCode::BUSY);
|
||||
+ }
|
||||
+ self.waiting.set(appid);
|
||||
+ self.waiting.set(process_id);
|
||||
+ self.erase_page(ptr)
|
||||
+ }
|
||||
+
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+ _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/kernel/src/kernel.rs b/kernel/src/kernel.rs
|
||||
index a1d081ad7..583fbe8d6 100644
|
||||
--- a/kernel/src/kernel.rs
|
||||
+++ b/kernel/src/kernel.rs
|
||||
@@ -38,6 +38,20 @@ use crate::utilities::cells::NumericCellExt;
|
||||
/// is less than this threshold.
|
||||
pub(crate) const MIN_QUANTA_THRESHOLD_US: u32 = 500;
|
||||
|
||||
+/// Represents the type of a storage slice.
|
||||
+#[derive(Copy, Clone)]
|
||||
+pub enum StorageType {
|
||||
+ Store = 1,
|
||||
+ Partition = 2,
|
||||
+}
|
||||
+
|
||||
+ fn allow(
|
||||
+ &self,
|
||||
+ appid: AppId,
|
||||
+ allow_num: usize,
|
||||
+ slice: Option<AppSlice<Shared, u8>>,
|
||||
+ ) -> ReturnCode {
|
||||
+ match allow_num {
|
||||
+ 0 => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ app.slice = slice;
|
||||
+ ReturnCode::SUCCESS
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+/// Represents a storage location in flash.
|
||||
+pub struct StorageLocation {
|
||||
+ pub address: usize,
|
||||
+ pub size: usize,
|
||||
+ pub storage_type: StorageType,
|
||||
+}
|
||||
+
|
||||
/// Main object for the kernel. Each board will need to create one.
|
||||
pub struct Kernel {
|
||||
/// How many "to-do" items exist at any given time. These include
|
||||
@@ -47,6 +61,9 @@ pub struct Kernel {
|
||||
/// This holds a pointer to the static array of Process pointers.
|
||||
processes: &'static [Option<&'static dyn process::Process>],
|
||||
|
||||
+ /// List of storage locations.
|
||||
+ storage_locations: &'static [StorageLocation],
|
||||
+
|
||||
/// A counter which keeps track of how many process identifiers have been
|
||||
/// created. This is used to create new unique identifiers for processes.
|
||||
process_identifier_max: Cell<usize>,
|
||||
@@ -110,15 +127,26 @@ fn try_allocate_grant(driver: &dyn SyscallDriver, process: &dyn process::Process
|
||||
|
||||
impl Kernel {
|
||||
pub fn new(processes: &'static [Option<&'static dyn process::Process>]) -> Kernel {
|
||||
+ Kernel::new_with_storage(processes, &[])
|
||||
+ }
|
||||
+ pub fn new_with_storage(
|
||||
+ processes: &'static [Option<&'static dyn process::Process>],
|
||||
+ storage_locations: &'static [StorageLocation],
|
||||
+ ) -> Kernel {
|
||||
Kernel {
|
||||
work: Cell::new(0),
|
||||
processes,
|
||||
+ storage_locations,
|
||||
process_identifier_max: Cell::new(0),
|
||||
grant_counter: Cell::new(0),
|
||||
grants_finalized: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
+ pub fn storage_locations(&self) -> &'static [StorageLocation] {
|
||||
+ self.storage_locations
|
||||
+ }
|
||||
+
|
||||
/// Something was scheduled for a process, so there is more work to do.
|
||||
///
|
||||
/// This is only exposed in the core kernel crate.
|
||||
@@ -1374,3 +1402,14 @@ impl Kernel {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+impl TryFrom<StorageType> for u32 {
|
||||
+ type Error = ();
|
||||
+
|
||||
+ fn try_from(value: StorageType) -> Result<Self, Self::Error> {
|
||||
+ match value {
|
||||
+ StorageType::Store => Ok(StorageType::Store as u32),
|
||||
+ StorageType::Partition => Ok(StorageType::Partition as u32),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
|
||||
index 49e1e5182..807170195 100644
|
||||
index 028f30220..8880bc000 100644
|
||||
--- a/kernel/src/lib.rs
|
||||
+++ b/kernel/src/lib.rs
|
||||
@@ -122,7 +122,7 @@ pub use crate::sched::cooperative::{CoopProcessNode, CooperativeSched};
|
||||
pub use crate::sched::mlfq::{MLFQProcessNode, MLFQSched};
|
||||
pub use crate::sched::priority::PrioritySched;
|
||||
pub use crate::sched::round_robin::{RoundRobinProcessNode, RoundRobinSched};
|
||||
-pub use crate::sched::{Kernel, Scheduler};
|
||||
+pub use crate::sched::{Kernel, Scheduler, StorageLocation};
|
||||
|
||||
// Export only select items from the process module. To remove the name conflict
|
||||
// this cannot be called `process`, so we use a shortened version. These
|
||||
@@ -125,5 +125,6 @@ mod syscall_driver;
|
||||
// Core resources exposed as `kernel::Type`.
|
||||
pub use crate::errorcode::ErrorCode;
|
||||
pub use crate::kernel::Kernel;
|
||||
+pub use crate::kernel::{StorageLocation, StorageType};
|
||||
pub use crate::process::ProcessId;
|
||||
pub use crate::scheduler::Scheduler;
|
||||
diff --git a/kernel/src/memop.rs b/kernel/src/memop.rs
|
||||
index 348c746a5..5465c95f4 100644
|
||||
index 51d89f37c..45ab3856b 100644
|
||||
--- a/kernel/src/memop.rs
|
||||
+++ b/kernel/src/memop.rs
|
||||
@@ -108,6 +108,25 @@ pub(crate) fn memop(process: &dyn ProcessType, op_type: usize, r1: usize) -> Ret
|
||||
ReturnCode::SUCCESS
|
||||
@@ -107,6 +107,37 @@ pub(crate) fn memop(process: &dyn Process, op_type: usize, r1: usize) -> Syscall
|
||||
SyscallReturn::Success
|
||||
}
|
||||
|
||||
+ // Op Type 12: Number of storage locations.
|
||||
+ 12 => ReturnCode::SuccessWithValue { value: process.number_storage_locations() },
|
||||
+ // 12 - 15 are required for the custom persistent store driver
|
||||
+ // currently only implemented in the nvmc module of nrf52 series
|
||||
+ // driver number: 0x50003
|
||||
+
|
||||
+ // Op Type 13: The start address of the storage location indexed by r1.
|
||||
+ // Op Type 12: Number of storage locations
|
||||
+ 12 => SyscallReturn::SuccessU32(process.number_storage_locations() as u32),
|
||||
+
|
||||
+ // Op Type 13: The start address of the storage location indexed by r1
|
||||
+ 13 => {
|
||||
+ match process.get_storage_location(r1) {
|
||||
+ None => ReturnCode::FAIL,
|
||||
+ Some(x) => ReturnCode::SuccessWithValue { value: x.address }
|
||||
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
|
||||
+ Some(x) => SyscallReturn::SuccessU32(x.address as u32),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Op Type 14: The size of the storage location indexed by r1.
|
||||
+ 14 => {
|
||||
+ match process.get_storage_location(r1) {
|
||||
+ None => ReturnCode::FAIL,
|
||||
+ Some(x) => ReturnCode::SuccessWithValue { value: x.size }
|
||||
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
|
||||
+ Some(x) => SyscallReturn::SuccessU32(x.size as u32),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
_ => ReturnCode::ENOSUPPORT,
|
||||
+ // Op Type 15: The type of the storage location indexed by r1.
|
||||
+ 15 => {
|
||||
+ match process.get_storage_location(r1) {
|
||||
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
|
||||
+ Some(x) => SyscallReturn::SuccessU32(x.storage_type as u32)
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
_ => SyscallReturn::Failure(ErrorCode::NOSUPPORT),
|
||||
}
|
||||
}
|
||||
diff --git a/kernel/src/process.rs b/kernel/src/process.rs
|
||||
index c52754be3..ae6a58341 100644
|
||||
index c1794d9bf..4fbad3f0b 100644
|
||||
--- a/kernel/src/process.rs
|
||||
+++ b/kernel/src/process.rs
|
||||
@@ -359,6 +359,15 @@ pub trait ProcessType {
|
||||
@@ -9,7 +9,7 @@ use core::str;
|
||||
use crate::capabilities;
|
||||
use crate::errorcode::ErrorCode;
|
||||
use crate::ipc;
|
||||
-use crate::kernel::Kernel;
|
||||
+use crate::kernel::{Kernel, StorageLocation};
|
||||
use crate::platform::mpu::{self};
|
||||
use crate::processbuffer::{ReadOnlyProcessBuffer, ReadWriteProcessBuffer};
|
||||
use crate::storage_permissions;
|
||||
@@ -335,6 +335,15 @@ pub trait Process {
|
||||
/// writeable flash region.
|
||||
fn get_writeable_flash_region(&self, region_index: usize) -> (u32, u32);
|
||||
|
||||
@@ -361,7 +455,7 @@ index c52754be3..ae6a58341 100644
|
||||
+ fn number_storage_locations(&self) -> usize;
|
||||
+
|
||||
+ /// Get the i-th storage location.
|
||||
+ fn get_storage_location(&self, index: usize) -> Option<&crate::StorageLocation>;
|
||||
+ fn get_storage_location(&self, index: usize) -> Option<&StorageLocation>;
|
||||
+
|
||||
+ /// Whether a slice fits in a storage location.
|
||||
+ fn fits_in_storage_location(&self, ptr: usize, len: usize) -> bool;
|
||||
@@ -369,15 +463,28 @@ index c52754be3..ae6a58341 100644
|
||||
/// Debug function to update the kernel on where the stack starts for this
|
||||
/// process. Processes are not required to call this through the memop
|
||||
/// system call, but it aids in debugging the process.
|
||||
@@ -1048,6 +1057,35 @@ impl<C: Chip> ProcessType for Process<'_, C> {
|
||||
self.header.get_writeable_flash_region(region_index)
|
||||
diff --git a/kernel/src/process_standard.rs b/kernel/src/process_standard.rs
|
||||
index fa76e2c68..dd03e8f29 100644
|
||||
--- a/kernel/src/process_standard.rs
|
||||
+++ b/kernel/src/process_standard.rs
|
||||
@@ -14,7 +14,7 @@ use crate::collections::ring_buffer::RingBuffer;
|
||||
use crate::config;
|
||||
use crate::debug;
|
||||
use crate::errorcode::ErrorCode;
|
||||
-use crate::kernel::Kernel;
|
||||
+use crate::kernel::{Kernel, StorageLocation};
|
||||
use crate::platform::chip::Chip;
|
||||
use crate::platform::mpu::{self, MPU};
|
||||
use crate::process::{Error, FunctionCall, FunctionCallSource, Process, State, Task};
|
||||
@@ -258,6 +258,35 @@ impl<C: Chip> Process for ProcessStandard<'_, C> {
|
||||
ret
|
||||
}
|
||||
|
||||
+ fn number_storage_locations(&self) -> usize {
|
||||
+ self.kernel.storage_locations().len()
|
||||
+ }
|
||||
+
|
||||
+ fn get_storage_location(&self, index: usize) -> Option<&crate::StorageLocation> {
|
||||
+ fn get_storage_location(&self, index: usize) -> Option<&StorageLocation> {
|
||||
+ self.kernel.storage_locations().get(index)
|
||||
+ }
|
||||
+
|
||||
@@ -402,10 +509,10 @@ index c52754be3..ae6a58341 100644
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
fn update_stack_start_pointer(&self, stack_pointer: *const u8) {
|
||||
if stack_pointer >= self.mem_start() && stack_pointer < self.mem_end() {
|
||||
self.debug.map(|debug| {
|
||||
@@ -1751,6 +1789,33 @@ impl<C: 'static + Chip> Process<'_, C> {
|
||||
fn ready(&self) -> bool {
|
||||
self.tasks.map_or(false, |ring_buf| ring_buf.has_elements())
|
||||
|| self.state.get() == State::Running
|
||||
@@ -1366,6 +1395,33 @@ impl<C: 'static + Chip> ProcessStandard<'_, C> {
|
||||
return Err(ProcessLoadError::MpuInvalidFlashLength);
|
||||
}
|
||||
|
||||
@@ -436,60 +543,6 @@ index c52754be3..ae6a58341 100644
|
||||
+ return Ok((None, remaining_memory));
|
||||
+ }
|
||||
+
|
||||
// Determine how much space we need in the application's
|
||||
// memory space just for kernel and grant state. We need to make
|
||||
// sure we allocate enough memory just for that.
|
||||
diff --git a/kernel/src/sched.rs b/kernel/src/sched.rs
|
||||
index 10626a2e1..8844bc6c3 100644
|
||||
--- a/kernel/src/sched.rs
|
||||
+++ b/kernel/src/sched.rs
|
||||
@@ -118,6 +118,12 @@ pub enum SchedulingDecision {
|
||||
TrySleep,
|
||||
}
|
||||
|
||||
+/// Represents a storage location in flash.
|
||||
+pub struct StorageLocation {
|
||||
+ pub address: usize,
|
||||
+ pub size: usize,
|
||||
+}
|
||||
+
|
||||
/// Main object for the kernel. Each board will need to create one.
|
||||
pub struct Kernel {
|
||||
/// How many "to-do" items exist at any given time. These include
|
||||
@@ -127,6 +133,9 @@ pub struct Kernel {
|
||||
/// This holds a pointer to the static array of Process pointers.
|
||||
processes: &'static [Option<&'static dyn process::ProcessType>],
|
||||
|
||||
+ /// List of storage locations.
|
||||
+ storage_locations: &'static [StorageLocation],
|
||||
+
|
||||
/// A counter which keeps track of how many process identifiers have been
|
||||
/// created. This is used to create new unique identifiers for processes.
|
||||
process_identifier_max: Cell<usize>,
|
||||
@@ -170,9 +179,17 @@ pub enum StoppedExecutingReason {
|
||||
|
||||
impl Kernel {
|
||||
pub fn new(processes: &'static [Option<&'static dyn process::ProcessType>]) -> Kernel {
|
||||
+ Kernel::new_with_storage(processes, &[])
|
||||
+ }
|
||||
+
|
||||
+ pub fn new_with_storage(
|
||||
+ processes: &'static [Option<&'static dyn process::ProcessType>],
|
||||
+ storage_locations: &'static [StorageLocation],
|
||||
+ ) -> Kernel {
|
||||
Kernel {
|
||||
work: Cell::new(0),
|
||||
processes,
|
||||
+ storage_locations: storage_locations,
|
||||
process_identifier_max: Cell::new(0),
|
||||
grant_counter: Cell::new(0),
|
||||
grants_finalized: Cell::new(false),
|
||||
@@ -900,4 +917,8 @@ impl Kernel {
|
||||
|
||||
(return_reason, time_executed_us)
|
||||
}
|
||||
+
|
||||
+ pub fn storage_locations(&self) -> &'static [StorageLocation] {
|
||||
+ self.storage_locations
|
||||
+ }
|
||||
}
|
||||
// Determine how much space we need in the application's memory space
|
||||
// just for kernel and grant state. We need to make sure we allocate
|
||||
// enough memory just for that.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
//
|
||||
@@ -25,21 +25,20 @@ index e6cf3ff56..7b3ea5a59 100644
|
||||
if uicr
|
||||
.get_psel0_reset_pin()
|
||||
diff --git a/chips/nrf52/src/uicr.rs b/chips/nrf52/src/uicr.rs
|
||||
index 4924bbd44..788533210 100644
|
||||
index 3b51717d0..3086394aa 100644
|
||||
--- a/chips/nrf52/src/uicr.rs
|
||||
+++ b/chips/nrf52/src/uicr.rs
|
||||
@@ -1,38 +1,44 @@
|
||||
@@ -1,39 +1,43 @@
|
||||
//! User information configuration registers
|
||||
-//!
|
||||
-//! Minimal implementation to support activation of the reset button on
|
||||
-//! nRF52-DK.
|
||||
|
||||
use enum_primitive::cast::FromPrimitive;
|
||||
-use kernel::common::registers::{register_bitfields, ReadWrite};
|
||||
+use kernel::common::registers::{register_bitfields, register_structs, ReadWrite};
|
||||
use kernel::common::StaticRef;
|
||||
+use kernel::hil;
|
||||
+use kernel::ReturnCode;
|
||||
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
-use kernel::utilities::registers::{register_bitfields, ReadWrite};
|
||||
+use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
|
||||
use kernel::utilities::StaticRef;
|
||||
|
||||
use crate::gpio::Pin;
|
||||
|
||||
@@ -70,40 +69,41 @@ index 4924bbd44..788533210 100644
|
||||
+ unsafe { StaticRef::new(0x10001000 as *const UicrRegisters) };
|
||||
+
|
||||
+register_structs! {
|
||||
+ UicrRegisters {
|
||||
+ (0x000 => _reserved1),
|
||||
+ /// Reserved for Nordic firmware design
|
||||
+ (0x014 => nrffw: [ReadWrite<u32>; 13]),
|
||||
+ (0x048 => _reserved2),
|
||||
+ /// Reserved for Nordic hardware design
|
||||
+ (0x050 => nrfhw: [ReadWrite<u32>; 12]),
|
||||
+ /// Reserved for customer
|
||||
+ (0x080 => customer: [ReadWrite<u32>; 32]),
|
||||
+ (0x100 => _reserved3),
|
||||
+ /// Mapping of the nRESET function (see POWER chapter for details)
|
||||
+ (0x200 => pselreset0: ReadWrite<u32, Pselreset::Register>),
|
||||
+ /// Mapping of the nRESET function (see POWER chapter for details)
|
||||
+ (0x204 => pselreset1: ReadWrite<u32, Pselreset::Register>),
|
||||
+ /// Access Port protection
|
||||
+ (0x208 => approtect: ReadWrite<u32, ApProtect::Register>),
|
||||
+ /// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO
|
||||
+ /// - Address: 0x20c - 0x210
|
||||
+ (0x20c => nfcpins: ReadWrite<u32, NfcPins::Register>),
|
||||
+ (0x210 => debugctrl: ReadWrite<u32, DebugControl::Register>),
|
||||
+ (0x214 => _reserved4),
|
||||
+ /// External circuitry to be supplied from VDD pin.
|
||||
+ (0x300 => extsupply: ReadWrite<u32, ExtSupply::Register>),
|
||||
+ /// GPIO reference voltage
|
||||
+ (0x304 => regout0: ReadWrite<u32, RegOut::Register>),
|
||||
+ (0x308 => @END),
|
||||
+ }
|
||||
+ UicrRegisters {
|
||||
+ (0x000 => _reserved1),
|
||||
+ /// Reserved for Nordic firmware design
|
||||
+ (0x014 => nrffw: [ReadWrite<u32>; 13]),
|
||||
+ (0x048 => _reserved2),
|
||||
+ /// Reserved for Nordic hardware design
|
||||
+ (0x050 => nrfhw: [ReadWrite<u32>; 12]),
|
||||
+ /// Reserved for customer
|
||||
+ (0x080 => customer: [ReadWrite<u32>; 32]),
|
||||
+ (0x100 => _reserved3),
|
||||
+ /// Mapping of the nRESET function (see POWER chapter for details)
|
||||
+ (0x200 => pselreset0: ReadWrite<u32, Pselreset::Register>),
|
||||
+ /// Mapping of the nRESET function (see POWER chapter for details)
|
||||
+ (0x204 => pselreset1: ReadWrite<u32, Pselreset::Register>),
|
||||
+ /// Access Port protection
|
||||
+ (0x208 => approtect: ReadWrite<u32, ApProtect::Register>),
|
||||
+ /// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO
|
||||
+ /// - Address: 0x20c - 0x210
|
||||
+ (0x20c => nfcpins: ReadWrite<u32, NfcPins::Register>),
|
||||
+ (0x210 => debugctrl: ReadWrite<u32, DebugControl::Register>),
|
||||
+ (0x214 => _reserved4),
|
||||
+ /// External circuitry to be supplied from VDD pin.
|
||||
+ (0x300 => extsupply: ReadWrite<u32, ExtSupply::Register>),
|
||||
+ /// GPIO reference voltage
|
||||
+ (0x304 => regout0: ReadWrite<u32, RegOut::Register>),
|
||||
+ (0x308 => @END),
|
||||
+ }
|
||||
}
|
||||
|
||||
register_bitfields! [u32,
|
||||
@@ -58,6 +64,21 @@ register_bitfields! [u32,
|
||||
@@ -59,6 +63,23 @@ register_bitfields! [u32,
|
||||
DISABLED = 0xff
|
||||
]
|
||||
],
|
||||
+
|
||||
+ /// Processor debug control
|
||||
+ DebugControl [
|
||||
+ CPUNIDEN OFFSET(0) NUMBITS(8) [
|
||||
@@ -119,11 +119,12 @@ index 4924bbd44..788533210 100644
|
||||
+ DISABLED = 0x00
|
||||
+ ]
|
||||
+ ],
|
||||
+
|
||||
/// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO
|
||||
NfcPins [
|
||||
/// Setting pins dedicated to NFC functionality
|
||||
@@ -172,6 +193,18 @@ impl Uicr {
|
||||
self.registers.nfcpins.matches_all(NfcPins::PROTECT::NFC)
|
||||
@@ -169,6 +190,18 @@ impl Uicr {
|
||||
}
|
||||
}
|
||||
|
||||
+ pub fn get_dfu_params(&self) -> (u32, u32) {
|
||||
@@ -138,6 +139,6 @@ index 4924bbd44..788533210 100644
|
||||
+ self.registers.nrffw[1].set(dfu_settings_addr);
|
||||
+ }
|
||||
+
|
||||
pub fn is_ap_protect_enabled(&self) -> bool {
|
||||
// Here we compare to DISABLED value because any other value should enable the protection.
|
||||
!self
|
||||
pub fn is_nfc_pins_protection_enabled(&self) -> bool {
|
||||
self.registers.nfcpins.matches_all(NfcPins::PROTECT::NFC)
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
diff --git a/capsules/src/usb/app.rs b/capsules/src/usb/app.rs
|
||||
new file mode 100644
|
||||
index 000000000..c2f434f12
|
||||
--- /dev/null
|
||||
+++ b/capsules/src/usb/app.rs
|
||||
@@ -0,0 +1,65 @@
|
||||
+use kernel::{AppSlice, Callback, Shared};
|
||||
+
|
||||
+#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
+pub enum Side {
|
||||
+ Transmit,
|
||||
+ Receive,
|
||||
+ TransmitOrReceive,
|
||||
+}
|
||||
+
|
||||
+impl Side {
|
||||
+ pub fn can_transmit(&self) -> bool {
|
||||
+ match self {
|
||||
+ Side::Transmit | Side::TransmitOrReceive => true,
|
||||
+ Side::Receive => false,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn can_receive(&self) -> bool {
|
||||
+ match self {
|
||||
+ Side::Receive | Side::TransmitOrReceive => true,
|
||||
+ Side::Transmit => false,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[derive(Default)]
|
||||
+pub struct App {
|
||||
+ // Only one app can be connected to this driver, to avoid needing to route packets among apps.
|
||||
+ // This field tracks this status.
|
||||
+ pub connected: bool,
|
||||
+ // Currently enabled transaction side. Subscribing to a callback or allowing a buffer
|
||||
+ // automatically sets the corresponding side. Clearing both the callback and the buffer resets
|
||||
+ // the side to None.
|
||||
+ pub side: Option<Side>,
|
||||
+ pub callback: Option<Callback>,
|
||||
+ pub buffer: Option<AppSlice<Shared, u8>>,
|
||||
+ // Whether the app is waiting for the kernel signaling a packet transfer.
|
||||
+ pub waiting: bool,
|
||||
+}
|
||||
+
|
||||
+impl App {
|
||||
+ pub fn can_receive_packet(&self) -> bool {
|
||||
+ self.waiting && self.side.map_or(false, |side| side.can_receive()) && self.buffer.is_some()
|
||||
+ }
|
||||
+
|
||||
+ pub fn check_side(&mut self) {
|
||||
+ if self.callback.is_none() && self.buffer.is_none() && !self.waiting {
|
||||
+ self.side = None;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_side(&mut self, side: Side) -> bool {
|
||||
+ match self.side {
|
||||
+ None => {
|
||||
+ self.side = Some(side);
|
||||
+ true
|
||||
+ }
|
||||
+ Some(app_side) => side == app_side,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn is_ready_for_command(&self, side: Side) -> bool {
|
||||
+ self.buffer.is_some() && self.callback.is_some() && self.side == Some(side)
|
||||
+ }
|
||||
+}
|
||||
diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs
|
||||
index 3f3a4f646..cb5e0af97 100644
|
||||
--- a/capsules/src/usb/mod.rs
|
||||
+++ b/capsules/src/usb/mod.rs
|
||||
@@ -1,3 +1,4 @@
|
||||
+pub mod app;
|
||||
pub mod cdc;
|
||||
pub mod descriptors;
|
||||
pub mod usb_ctap;
|
||||
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
|
||||
index da3d16d85..e8f1a87a4 100644
|
||||
--- a/capsules/src/usb/usb_ctap.rs
|
||||
+++ b/capsules/src/usb/usb_ctap.rs
|
||||
@@ -1,7 +1,7 @@
|
||||
+use super::app::{App, Side};
|
||||
use super::usbc_ctap_hid::ClientCtapHID;
|
||||
-use kernel::hil;
|
||||
use kernel::hil::usb::Client;
|
||||
-use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
|
||||
+use kernel::{hil, AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
|
||||
|
||||
/// Syscall number
|
||||
use crate::driver;
|
||||
@@ -25,73 +25,15 @@ pub const CTAP_SUBSCRIBE_TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
pub const CTAP_CALLBACK_TRANSMITED: usize = 1;
|
||||
pub const CTAP_CALLBACK_RECEIVED: usize = 2;
|
||||
|
||||
-#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
-enum Side {
|
||||
- Transmit,
|
||||
- Receive,
|
||||
- TransmitOrReceive,
|
||||
-}
|
||||
-
|
||||
-impl Side {
|
||||
- fn can_transmit(&self) -> bool {
|
||||
- match self {
|
||||
- Side::Transmit | Side::TransmitOrReceive => true,
|
||||
- Side::Receive => false,
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- fn can_receive(&self) -> bool {
|
||||
- match self {
|
||||
- Side::Receive | Side::TransmitOrReceive => true,
|
||||
- Side::Transmit => false,
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-#[derive(Default)]
|
||||
-pub struct App {
|
||||
- // Only one app can be connected to this driver, to avoid needing to route packets among apps.
|
||||
- // This field tracks this status.
|
||||
- connected: bool,
|
||||
- // Currently enabled transaction side. Subscribing to a callback or allowing a buffer
|
||||
- // automatically sets the corresponding side. Clearing both the callback and the buffer resets
|
||||
- // the side to None.
|
||||
- side: Option<Side>,
|
||||
- callback: Option<Callback>,
|
||||
- buffer: Option<AppSlice<Shared, u8>>,
|
||||
- // Whether the app is waiting for the kernel signaling a packet transfer.
|
||||
- waiting: bool,
|
||||
-}
|
||||
-
|
||||
-impl App {
|
||||
- fn check_side(&mut self) {
|
||||
- if self.callback.is_none() && self.buffer.is_none() && !self.waiting {
|
||||
- self.side = None;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- fn set_side(&mut self, side: Side) -> bool {
|
||||
- match self.side {
|
||||
- None => {
|
||||
- self.side = Some(side);
|
||||
- true
|
||||
- }
|
||||
- Some(app_side) => side == app_side,
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- fn is_ready_for_command(&self, side: Side) -> bool {
|
||||
- self.buffer.is_some() && self.callback.is_some() && self.side == Some(side)
|
||||
- }
|
||||
-}
|
||||
-
|
||||
pub trait CtapUsbClient {
|
||||
// Whether this client is ready to receive a packet. This must be checked before calling
|
||||
- // packet_received().
|
||||
- fn can_receive_packet(&self) -> bool;
|
||||
+ // packet_received(). If App is not supplied, it will be found from the implemntation's
|
||||
+ // members.
|
||||
+ fn can_receive_packet(&self, app: &Option<&mut App>) -> bool;
|
||||
|
||||
// Signal to the client that a packet has been received.
|
||||
- fn packet_received(&self, packet: &[u8; 64]);
|
||||
+ // If App is not supplied, it will be found from the implemntation's members.
|
||||
+ fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>);
|
||||
|
||||
// Signal to the client that a packet has been transmitted.
|
||||
fn packet_transmitted(&self);
|
||||
@@ -106,38 +48,49 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
pub fn new(usb_client: &'a ClientCtapHID<'a, 'b, C>, apps: Grant<App>) -> Self {
|
||||
CtapUsbSyscallDriver { usb_client, apps }
|
||||
}
|
||||
+
|
||||
+ fn app_packet_received(&self, packet: &[u8; 64], app: &mut App) {
|
||||
+ if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive()) {
|
||||
+ if let Some(buf) = &mut app.buffer {
|
||||
+ // Copy the packet to the app's allowed buffer.
|
||||
+ buf.as_mut().copy_from_slice(packet);
|
||||
+ app.waiting = false;
|
||||
+ // Signal to the app that a packet is ready.
|
||||
+ app.callback
|
||||
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
- fn can_receive_packet(&self) -> bool {
|
||||
+ fn can_receive_packet(&self, app: &Option<&mut App>) -> bool {
|
||||
let mut result = false;
|
||||
- for app in self.apps.iter() {
|
||||
- app.enter(|app, _| {
|
||||
- if app.connected {
|
||||
- result = app.waiting
|
||||
- && app.side.map_or(false, |side| side.can_receive())
|
||||
- && app.buffer.is_some();
|
||||
+ match app {
|
||||
+ None => {
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|a, _| {
|
||||
+ if a.connected {
|
||||
+ result = a.can_receive_packet();
|
||||
+ }
|
||||
+ })
|
||||
}
|
||||
- });
|
||||
+ }
|
||||
+ Some(a) => result = a.can_receive_packet(),
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
- fn packet_received(&self, packet: &[u8; 64]) {
|
||||
- for app in self.apps.iter() {
|
||||
- app.enter(|app, _| {
|
||||
- if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive())
|
||||
- {
|
||||
- if let Some(buf) = &mut app.buffer {
|
||||
- // Copy the packet to the app's allowed buffer.
|
||||
- buf.as_mut().copy_from_slice(packet);
|
||||
- app.waiting = false;
|
||||
- // Signal to the app that a packet is ready.
|
||||
- app.callback
|
||||
- .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
|
||||
- }
|
||||
+ fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>) {
|
||||
+ match app {
|
||||
+ None => {
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|a, _| {
|
||||
+ self.app_packet_received(packet, a);
|
||||
+ })
|
||||
}
|
||||
- });
|
||||
+ }
|
||||
+ Some(a) => self.app_packet_received(packet, a),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +235,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
ReturnCode::EALREADY
|
||||
} else {
|
||||
app.waiting = true;
|
||||
- self.usb_client.receive_packet();
|
||||
+ self.usb_client.receive_packet(app);
|
||||
ReturnCode::SUCCESS
|
||||
}
|
||||
} else {
|
||||
@@ -303,7 +256,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
} else {
|
||||
// Indicates to the driver that we can receive any pending packet.
|
||||
app.waiting = true;
|
||||
- self.usb_client.receive_packet();
|
||||
+ self.usb_client.receive_packet(app);
|
||||
|
||||
if !app.waiting {
|
||||
// The call to receive_packet() collected a pending packet.
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
index abf224f97..d47e5f644 100644
|
||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -11,6 +11,7 @@ use super::descriptors::HIDSubordinateDescriptor;
|
||||
use super::descriptors::InterfaceDescriptor;
|
||||
use super::descriptors::ReportDescriptor;
|
||||
use super::descriptors::TransferDirection;
|
||||
+use super::app::App;
|
||||
use super::usb_ctap::CtapUsbClient;
|
||||
use super::usbc_client_ctrl::ClientCtrl;
|
||||
use core::cell::Cell;
|
||||
@@ -257,7 +258,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn receive_packet(&'a self) -> bool {
|
||||
+ pub fn receive_packet(&'a self, app: &mut App) -> bool {
|
||||
if self.pending_out.get() {
|
||||
// The previous packet has not yet been received, reject the new one.
|
||||
false
|
||||
@@ -267,7 +268,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
// Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
// packet arrives.
|
||||
if self.delayed_out.take() {
|
||||
- if self.send_packet_to_client() {
|
||||
+ if self.send_packet_to_client(Some(app)) {
|
||||
// If that succeeds, alert the controller that we can now
|
||||
// receive data on the Interrupt OUT endpoint.
|
||||
self.controller().endpoint_resume_out(1);
|
||||
@@ -280,7 +281,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
// Send an OUT packet available in the controller back to the client.
|
||||
// This returns false if the client is not ready to receive a packet, and true if the client
|
||||
// successfully accepted the packet.
|
||||
- fn send_packet_to_client(&'a self) -> bool {
|
||||
+ fn send_packet_to_client(&'a self, app: Option<&mut App>) -> bool {
|
||||
// Copy the packet into a buffer to send to the client.
|
||||
let mut buf: [u8; 64] = [0; 64];
|
||||
for (i, x) in self.out_buffer.buf.iter().enumerate() {
|
||||
@@ -292,7 +293,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
// Notify the client
|
||||
if self
|
||||
.client
|
||||
- .map_or(false, |client| client.can_receive_packet())
|
||||
+ .map_or(false, |client| client.can_receive_packet(&app))
|
||||
{
|
||||
assert!(self.pending_out.take());
|
||||
|
||||
@@ -301,7 +302,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
// should be re-transmitted or not.
|
||||
self.cancel_in_transaction();
|
||||
|
||||
- self.client.map(|client| client.packet_received(&buf));
|
||||
+ self.client.map(|client| client.packet_received(&buf, app));
|
||||
true
|
||||
} else {
|
||||
// Cannot receive now, indicate a delay to the controller.
|
||||
@@ -421,7 +422,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
// Cannot process this packet
|
||||
hil::usb::OutResult::Error
|
||||
} else {
|
||||
- if self.send_packet_to_client() {
|
||||
+ if self.send_packet_to_client(None) {
|
||||
hil::usb::OutResult::Ok
|
||||
} else {
|
||||
hil::usb::OutResult::Delay
|
||||
@@ -1,516 +0,0 @@
|
||||
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
|
||||
index e8f1a87a4..2c91c0968 100644
|
||||
--- a/capsules/src/usb/usb_ctap.rs
|
||||
+++ b/capsules/src/usb/usb_ctap.rs
|
||||
@@ -32,8 +32,7 @@ pub trait CtapUsbClient {
|
||||
fn can_receive_packet(&self, app: &Option<&mut App>) -> bool;
|
||||
|
||||
// Signal to the client that a packet has been received.
|
||||
- // If App is not supplied, it will be found from the implemntation's members.
|
||||
- fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>);
|
||||
+ fn packet_received(&self, packet: &[u8; 64], endpoint: usize, app: Option<&mut App>);
|
||||
|
||||
// Signal to the client that a packet has been transmitted.
|
||||
fn packet_transmitted(&self);
|
||||
@@ -49,7 +48,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
CtapUsbSyscallDriver { usb_client, apps }
|
||||
}
|
||||
|
||||
- fn app_packet_received(&self, packet: &[u8; 64], app: &mut App) {
|
||||
+ fn app_packet_received(&self, packet: &[u8; 64], endpoint: usize, app: &mut App) {
|
||||
if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive()) {
|
||||
if let Some(buf) = &mut app.buffer {
|
||||
// Copy the packet to the app's allowed buffer.
|
||||
@@ -57,7 +56,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
app.waiting = false;
|
||||
// Signal to the app that a packet is ready.
|
||||
app.callback
|
||||
- .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
|
||||
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, endpoint, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,16 +80,16 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDri
|
||||
result
|
||||
}
|
||||
|
||||
- fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>) {
|
||||
+ fn packet_received(&self, packet: &[u8; 64], endpoint: usize, app: Option<&mut App>) {
|
||||
match app {
|
||||
None => {
|
||||
for app in self.apps.iter() {
|
||||
app.enter(|a, _| {
|
||||
- self.app_packet_received(packet, a);
|
||||
+ self.app_packet_received(packet, endpoint, a);
|
||||
})
|
||||
}
|
||||
}
|
||||
- Some(a) => self.app_packet_received(packet, a),
|
||||
+ Some(a) => self.app_packet_received(packet, endpoint, a),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +172,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
.unwrap_or_else(|err| err.into())
|
||||
}
|
||||
|
||||
- fn command(&self, cmd_num: usize, _arg1: usize, _arg2: usize, appid: AppId) -> ReturnCode {
|
||||
+ fn command(&self, cmd_num: usize, endpoint: usize, _arg2: usize, appid: AppId) -> ReturnCode {
|
||||
match cmd_num {
|
||||
CTAP_CMD_CHECK => ReturnCode::SUCCESS,
|
||||
CTAP_CMD_CONNECT => {
|
||||
@@ -209,14 +208,14 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
if app.is_ready_for_command(Side::Transmit) {
|
||||
if app.waiting {
|
||||
ReturnCode::EALREADY
|
||||
- } else if self
|
||||
- .usb_client
|
||||
- .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
|
||||
- {
|
||||
- app.waiting = true;
|
||||
- ReturnCode::SUCCESS
|
||||
} else {
|
||||
- ReturnCode::EBUSY
|
||||
+ let r = self
|
||||
+ .usb_client
|
||||
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint);
|
||||
+ if r == ReturnCode::SUCCESS {
|
||||
+ app.waiting = true;
|
||||
+ }
|
||||
+ r
|
||||
}
|
||||
} else {
|
||||
ReturnCode::EINVAL
|
||||
@@ -263,14 +262,8 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
ReturnCode::SUCCESS
|
||||
} else {
|
||||
// Indicates to the driver that we have a packet to send.
|
||||
- if self
|
||||
- .usb_client
|
||||
- .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
|
||||
- {
|
||||
- ReturnCode::SUCCESS
|
||||
- } else {
|
||||
- ReturnCode::EBUSY
|
||||
- }
|
||||
+ self.usb_client
|
||||
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -289,7 +282,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
// FIXME: if cancellation failed, the app should still wait. But that
|
||||
// doesn't work yet.
|
||||
app.waiting = false;
|
||||
- if self.usb_client.cancel_transaction() {
|
||||
+ if self.usb_client.cancel_transaction(endpoint) {
|
||||
ReturnCode::SUCCESS
|
||||
} else {
|
||||
// Cannot cancel now because the transaction is already in process.
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
index d47e5f644..76f6af73b 100644
|
||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -18,13 +18,27 @@ use core::cell::Cell;
|
||||
use kernel::common::cells::OptionalCell;
|
||||
use kernel::debug;
|
||||
use kernel::hil;
|
||||
+use kernel::ReturnCode;
|
||||
use kernel::hil::usb::TransferType;
|
||||
|
||||
static LANGUAGES: &'static [u16; 1] = &[
|
||||
0x0409, // English (United States)
|
||||
];
|
||||
|
||||
+#[cfg(not(feature = "vendor_hid"))]
|
||||
+const NUM_ENDPOINTS: usize = 1;
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+const NUM_ENDPOINTS: usize = 2;
|
||||
+
|
||||
const ENDPOINT_NUM: usize = 1;
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+const VENDOR_ENDPOINT_NUM: usize = ENDPOINT_NUM + 1;
|
||||
+
|
||||
+static ENDPOINTS: &'static [usize] = &[
|
||||
+ ENDPOINT_NUM,
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ VENDOR_ENDPOINT_NUM
|
||||
+];
|
||||
|
||||
static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
|
||||
0x06, 0xD0, 0xF1, // HID_UsagePage ( FIDO_USAGE_PAGE ),
|
||||
@@ -98,21 +112,42 @@ static VENDOR_HID: HIDDescriptor<'static> = HIDDescriptor {
|
||||
sub_descriptors: VENDOR_HID_SUB_DESCRIPTORS,
|
||||
};
|
||||
|
||||
-pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
- client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
-
|
||||
- // 64-byte buffers for the endpoint
|
||||
+// The state of each endpoint.
|
||||
+struct EndpointState {
|
||||
+ endpoint: usize,
|
||||
in_buffer: Buffer64,
|
||||
out_buffer: Buffer64,
|
||||
|
||||
- // Interaction with the client
|
||||
- client: OptionalCell<&'b dyn CtapUsbClient>,
|
||||
tx_packet: OptionalCell<[u8; 64]>,
|
||||
pending_in: Cell<bool>,
|
||||
pending_out: Cell<bool>,
|
||||
+ // Is there a delayed packet?
|
||||
delayed_out: Cell<bool>,
|
||||
}
|
||||
|
||||
+impl EndpointState {
|
||||
+ pub fn new(endpoint:usize) -> Self {
|
||||
+ EndpointState{
|
||||
+ endpoint: endpoint,
|
||||
+ in_buffer: Buffer64::default(),
|
||||
+ out_buffer: Buffer64::default(),
|
||||
+ tx_packet: OptionalCell::empty(),
|
||||
+ pending_in: Cell::new(false),
|
||||
+ pending_out: Cell::new(false),
|
||||
+ delayed_out: Cell::new(false),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
+ client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
+
|
||||
+ endpoints: [EndpointState; NUM_ENDPOINTS],
|
||||
+
|
||||
+ // Interaction with the client
|
||||
+ client: OptionalCell<&'b dyn CtapUsbClient>,
|
||||
+}
|
||||
+
|
||||
impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
pub fn new(
|
||||
controller: &'a C,
|
||||
@@ -168,7 +203,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
&[
|
||||
EndpointDescriptor {
|
||||
endpoint_address: EndpointAddress::new_const(
|
||||
- ENDPOINT_NUM + 1,
|
||||
+ VENDOR_ENDPOINT_NUM,
|
||||
TransferDirection::HostToDevice,
|
||||
),
|
||||
transfer_type: TransferType::Interrupt,
|
||||
@@ -177,7 +212,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
},
|
||||
EndpointDescriptor {
|
||||
endpoint_address: EndpointAddress::new_const(
|
||||
- ENDPOINT_NUM + 1,
|
||||
+ VENDOR_ENDPOINT_NUM,
|
||||
TransferDirection::DeviceToHost,
|
||||
),
|
||||
transfer_type: TransferType::Interrupt,
|
||||
@@ -229,99 +264,135 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
LANGUAGES,
|
||||
strings,
|
||||
),
|
||||
- in_buffer: Buffer64::default(),
|
||||
- out_buffer: Buffer64::default(),
|
||||
+ endpoints: [
|
||||
+ EndpointState::new(ENDPOINT_NUM),
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ EndpointState::new(VENDOR_ENDPOINT_NUM),
|
||||
+ ],
|
||||
client: OptionalCell::empty(),
|
||||
- tx_packet: OptionalCell::empty(),
|
||||
- pending_in: Cell::new(false),
|
||||
- pending_out: Cell::new(false),
|
||||
- delayed_out: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
+ fn get_endpoint(&'a self, endpoint: usize) -> Option<&'a EndpointState> {
|
||||
+ for (i, ep) in ENDPOINTS.iter().enumerate() {
|
||||
+ if endpoint == *ep {
|
||||
+ return Some(&self.endpoints[i]);
|
||||
+ }
|
||||
+ }
|
||||
+ None
|
||||
+ }
|
||||
+
|
||||
pub fn set_client(&'a self, client: &'b dyn CtapUsbClient) {
|
||||
self.client.set(client);
|
||||
}
|
||||
|
||||
- pub fn transmit_packet(&'a self, packet: &[u8]) -> bool {
|
||||
- if self.pending_in.get() {
|
||||
- // The previous packet has not yet been transmitted, reject the new one.
|
||||
- false
|
||||
- } else {
|
||||
- self.pending_in.set(true);
|
||||
+ pub fn transmit_packet(&'a self, packet: &[u8], endpoint: usize) -> ReturnCode {
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ if s.pending_in.get() {
|
||||
+ // The previous packet has not yet been transmitted, reject the new one.
|
||||
+ return ReturnCode::EBUSY;
|
||||
+ }
|
||||
+ s.pending_in.set(true);
|
||||
let mut buf: [u8; 64] = [0; 64];
|
||||
buf.copy_from_slice(packet);
|
||||
- self.tx_packet.set(buf);
|
||||
+ s.tx_packet.set(buf);
|
||||
// Alert the controller that we now have data to send on the Interrupt IN endpoint.
|
||||
- self.controller().endpoint_resume_in(1);
|
||||
- true
|
||||
+ self.controller().endpoint_resume_in(endpoint);
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ // Unsupported endpoint
|
||||
+ ReturnCode::EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn receive_packet(&'a self, app: &mut App) -> bool {
|
||||
- if self.pending_out.get() {
|
||||
- // The previous packet has not yet been received, reject the new one.
|
||||
- false
|
||||
- } else {
|
||||
- self.pending_out.set(true);
|
||||
- // In case we reported Delay before, send the pending packet back to the client.
|
||||
- // Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
- // packet arrives.
|
||||
- if self.delayed_out.take() {
|
||||
- if self.send_packet_to_client(Some(app)) {
|
||||
- // If that succeeds, alert the controller that we can now
|
||||
- // receive data on the Interrupt OUT endpoint.
|
||||
- self.controller().endpoint_resume_out(1);
|
||||
+ pub fn receive_packet(&'a self, app: &mut App) {
|
||||
+ for (_, s) in self.endpoints.iter().enumerate() {
|
||||
+ if s.pending_out.get() {
|
||||
+ // The previous packet has not yet been received, reject the new one.
|
||||
+ continue;
|
||||
+ } else {
|
||||
+ s.pending_out.set(true);
|
||||
+ // In case we reported Delay before, send the pending packet back to the client.
|
||||
+ // Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
+ // packet arrives.
|
||||
+ if s.delayed_out.take() {
|
||||
+ if self.send_packet_to_client(s.endpoint, Some(app)) {
|
||||
+ // If that succeeds, alert the controller that we can now
|
||||
+ // receive data on the Interrupt OUT endpoint.
|
||||
+ self.controller().endpoint_resume_out(s.endpoint);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
- true
|
||||
}
|
||||
}
|
||||
|
||||
// Send an OUT packet available in the controller back to the client.
|
||||
// This returns false if the client is not ready to receive a packet, and true if the client
|
||||
// successfully accepted the packet.
|
||||
- fn send_packet_to_client(&'a self, app: Option<&mut App>) -> bool {
|
||||
- // Copy the packet into a buffer to send to the client.
|
||||
- let mut buf: [u8; 64] = [0; 64];
|
||||
- for (i, x) in self.out_buffer.buf.iter().enumerate() {
|
||||
- buf[i] = x.get();
|
||||
- }
|
||||
-
|
||||
- assert!(!self.delayed_out.get());
|
||||
-
|
||||
- // Notify the client
|
||||
- if self
|
||||
- .client
|
||||
- .map_or(false, |client| client.can_receive_packet(&app))
|
||||
- {
|
||||
- assert!(self.pending_out.take());
|
||||
-
|
||||
- // Clear any pending packet on the transmitting side.
|
||||
- // It's up to the client to handle the received packet and decide if this packet
|
||||
- // should be re-transmitted or not.
|
||||
- self.cancel_in_transaction();
|
||||
+ fn send_packet_to_client(&'a self, endpoint: usize, app: Option<&mut App>) -> bool {
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ // Copy the packet into a buffer to send to the client.
|
||||
+ let mut buf: [u8; 64] = [0; 64];
|
||||
+ for (i, x) in s.out_buffer.buf.iter().enumerate() {
|
||||
+ buf[i] = x.get();
|
||||
+ }
|
||||
|
||||
- self.client.map(|client| client.packet_received(&buf, app));
|
||||
- true
|
||||
+ assert!(!s.delayed_out.get());
|
||||
+
|
||||
+ // Notify the client
|
||||
+ if self
|
||||
+ .client
|
||||
+ .map_or(false, |client| client.can_receive_packet(&app))
|
||||
+ {
|
||||
+ assert!(s.pending_out.take());
|
||||
+
|
||||
+ // Clear any pending packet on the transmitting side.
|
||||
+ // It's up to the client to handle the received packet and decide if this packet
|
||||
+ // should be re-transmitted or not.
|
||||
+ self.cancel_in_transaction(endpoint);
|
||||
+
|
||||
+ self.client.map(|client| client.packet_received(&buf, endpoint, app));
|
||||
+ true
|
||||
+ } else {
|
||||
+ // Cannot receive now, indicate a delay to the controller.
|
||||
+ s.delayed_out.set(true);
|
||||
+ false
|
||||
+ }
|
||||
} else {
|
||||
- // Cannot receive now, indicate a delay to the controller.
|
||||
- self.delayed_out.set(true);
|
||||
+ // Unsupported endpoint
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn cancel_transaction(&'a self) -> bool {
|
||||
- self.cancel_in_transaction() | self.cancel_out_transaction()
|
||||
+ // Cancel transaction(s) in process. |endpoint| of 0 indicates all endpoints.
|
||||
+ pub fn cancel_transaction(&'a self, endpoint: usize) -> bool {
|
||||
+ if endpoint > 0 {
|
||||
+ return self.cancel_in_transaction(endpoint) | self.cancel_out_transaction(endpoint);
|
||||
+ }
|
||||
+ let mut r = false;
|
||||
+ for (_, s) in self.endpoints.iter().enumerate() {
|
||||
+ r |= self.cancel_in_transaction(s.endpoint) | self.cancel_out_transaction(s.endpoint);
|
||||
+ }
|
||||
+ r
|
||||
}
|
||||
|
||||
- fn cancel_in_transaction(&'a self) -> bool {
|
||||
- self.tx_packet.take();
|
||||
- self.pending_in.take()
|
||||
+ fn cancel_in_transaction(&'a self, endpoint: usize) -> bool {
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ s.tx_packet.take();
|
||||
+ s.pending_in.take()
|
||||
+ } else {
|
||||
+ // Unsupported endpoint
|
||||
+ false
|
||||
+ }
|
||||
}
|
||||
|
||||
- fn cancel_out_transaction(&'a self) -> bool {
|
||||
- self.pending_out.take()
|
||||
+ fn cancel_out_transaction(&'a self, endpoint: usize) -> bool {
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ s.pending_out.take()
|
||||
+ } else {
|
||||
+ // Unsupported endpoint
|
||||
+ false
|
||||
+ }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -335,13 +406,15 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
// Set up the default control endpoint
|
||||
self.client_ctrl.enable();
|
||||
|
||||
- // Set up the interrupt in-out endpoint
|
||||
- self.controller()
|
||||
- .endpoint_set_in_buffer(1, &self.in_buffer.buf);
|
||||
- self.controller()
|
||||
- .endpoint_set_out_buffer(1, &self.out_buffer.buf);
|
||||
- self.controller()
|
||||
- .endpoint_in_out_enable(TransferType::Interrupt, 1);
|
||||
+ // Set up the interrupt in-out endpoint(s).
|
||||
+ for (i, endpoint) in ENDPOINTS.iter().enumerate() {
|
||||
+ self.controller()
|
||||
+ .endpoint_set_in_buffer(*endpoint, &self.endpoints[i].in_buffer.buf);
|
||||
+ self.controller()
|
||||
+ .endpoint_set_out_buffer(*endpoint, &self.endpoints[i].out_buffer.buf);
|
||||
+ self.controller()
|
||||
+ .endpoint_in_out_enable(TransferType::Interrupt, *endpoint);
|
||||
+ }
|
||||
}
|
||||
|
||||
fn attach(&'a self) {
|
||||
@@ -384,20 +457,20 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
match transfer_type {
|
||||
TransferType::Bulk => hil::usb::InResult::Error,
|
||||
TransferType::Interrupt => {
|
||||
- if endpoint != 1 {
|
||||
- return hil::usb::InResult::Error;
|
||||
- }
|
||||
-
|
||||
- if let Some(packet) = self.tx_packet.take() {
|
||||
- let buf = &self.in_buffer.buf;
|
||||
- for i in 0..64 {
|
||||
- buf[i].set(packet[i]);
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ if let Some(packet) = s.tx_packet.take() {
|
||||
+ let buf = &s.in_buffer.buf;
|
||||
+ for i in 0..64 {
|
||||
+ buf[i].set(packet[i]);
|
||||
+ }
|
||||
+ hil::usb::InResult::Packet(64)
|
||||
+ } else {
|
||||
+ // Nothing to send
|
||||
+ hil::usb::InResult::Delay
|
||||
}
|
||||
-
|
||||
- hil::usb::InResult::Packet(64)
|
||||
} else {
|
||||
- // Nothing to send
|
||||
- hil::usb::InResult::Delay
|
||||
+ // Unsupported endpoint
|
||||
+ return hil::usb::InResult::Error
|
||||
}
|
||||
}
|
||||
TransferType::Control | TransferType::Isochronous => unreachable!(),
|
||||
@@ -414,7 +487,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
match transfer_type {
|
||||
TransferType::Bulk => hil::usb::OutResult::Error,
|
||||
TransferType::Interrupt => {
|
||||
- if endpoint != 1 {
|
||||
+ if endpoint == 0 || endpoint > NUM_ENDPOINTS {
|
||||
return hil::usb::OutResult::Error;
|
||||
}
|
||||
|
||||
@@ -422,7 +495,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
// Cannot process this packet
|
||||
hil::usb::OutResult::Error
|
||||
} else {
|
||||
- if self.send_packet_to_client(None) {
|
||||
+ if self.send_packet_to_client(endpoint, None) {
|
||||
hil::usb::OutResult::Ok
|
||||
} else {
|
||||
hil::usb::OutResult::Delay
|
||||
@@ -434,21 +507,21 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
}
|
||||
|
||||
fn packet_transmitted(&'a self, endpoint: usize) {
|
||||
- if endpoint != 1 {
|
||||
- panic!("Unexpected transmission on ep {}", endpoint);
|
||||
- }
|
||||
-
|
||||
- if self.tx_packet.is_some() {
|
||||
- panic!("Unexpected tx_packet while a packet was being transmitted.");
|
||||
- }
|
||||
- self.pending_in.set(false);
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ if s.tx_packet.is_some() {
|
||||
+ panic!("Unexpected tx_packet while a packet was being transmitted.");
|
||||
+ }
|
||||
+ s.pending_in.set(false);
|
||||
|
||||
- // Clear any pending packet on the receiving side.
|
||||
- // It's up to the client to handle the transmitted packet and decide if they want to
|
||||
- // receive another packet.
|
||||
- self.cancel_out_transaction();
|
||||
+ // Clear any pending packet on the receiving side.
|
||||
+ // It's up to the client to handle the transmitted packet and decide if they want to
|
||||
+ // receive another packet.
|
||||
+ self.cancel_out_transaction(endpoint);
|
||||
|
||||
- // Notify the client
|
||||
- self.client.map(|client| client.packet_transmitted());
|
||||
+ // Notify the client
|
||||
+ self.client.map(|client| client.packet_transmitted());
|
||||
+ } else {
|
||||
+ panic!("Unexpected transmission on ep {}", endpoint);
|
||||
+ }
|
||||
}
|
||||
-}
|
||||
+ }
|
||||
@@ -1,94 +0,0 @@
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
index 16b80cb10..949388b70 100644
|
||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -120,7 +120,6 @@ struct EndpointState {
|
||||
|
||||
tx_packet: OptionalCell<[u8; 64]>,
|
||||
pending_in: Cell<bool>,
|
||||
- pending_out: Cell<bool>,
|
||||
// Is there a delayed packet?
|
||||
delayed_out: Cell<bool>,
|
||||
}
|
||||
@@ -133,7 +132,6 @@ impl EndpointState {
|
||||
out_buffer: Buffer64::default(),
|
||||
tx_packet: OptionalCell::empty(),
|
||||
pending_in: Cell::new(false),
|
||||
- pending_out: Cell::new(false),
|
||||
delayed_out: Cell::new(false),
|
||||
}
|
||||
}
|
||||
@@ -142,6 +140,10 @@ impl EndpointState {
|
||||
pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
|
||||
+ // Is there a pending OUT transaction happening?
|
||||
+ pending_out: Cell<bool>,
|
||||
+ next_endpoint_index: Cell<usize>,
|
||||
+
|
||||
endpoints: [EndpointState; NUM_ENDPOINTS],
|
||||
|
||||
// Interaction with the client
|
||||
@@ -264,6 +266,8 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
LANGUAGES,
|
||||
strings,
|
||||
),
|
||||
+ pending_out: Cell::new(false),
|
||||
+ next_endpoint_index: Cell::new(0),
|
||||
endpoints: [
|
||||
EndpointState::new(ENDPOINT_NUM),
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
@@ -306,12 +310,13 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
}
|
||||
|
||||
pub fn receive_packet(&'a self, app: &mut App) {
|
||||
- for (_, s) in self.endpoints.iter().enumerate() {
|
||||
- if s.pending_out.get() {
|
||||
- // The previous packet has not yet been received, reject the new one.
|
||||
- continue;
|
||||
- } else {
|
||||
- s.pending_out.set(true);
|
||||
+ if self.pending_out.get() {
|
||||
+ // The previous packet has not yet been received, reject the new one.
|
||||
+ } else {
|
||||
+ self.pending_out.set(true);
|
||||
+ // Process the next endpoint that has a delayed packet.
|
||||
+ for i in self.next_endpoint_index.get()..self.next_endpoint_index.get() + NUM_ENDPOINTS {
|
||||
+ let s = &self.endpoints[i % NUM_ENDPOINTS];
|
||||
// In case we reported Delay before, send the pending packet back to the client.
|
||||
// Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
// packet arrives.
|
||||
@@ -344,7 +349,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
.client
|
||||
.map_or(false, |client| client.can_receive_packet(&app))
|
||||
{
|
||||
- assert!(s.pending_out.take());
|
||||
+ assert!(self.pending_out.take());
|
||||
|
||||
// Clear any pending packet on the transmitting side.
|
||||
// It's up to the client to handle the received packet and decide if this packet
|
||||
@@ -352,6 +357,13 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
self.cancel_in_transaction(endpoint);
|
||||
|
||||
self.client.map(|client| client.packet_received(&buf, endpoint, app));
|
||||
+ // Update next packet to send.
|
||||
+ for (i, ep) in self.endpoints.iter().enumerate() {
|
||||
+ if ep.endpoint == endpoint {
|
||||
+ self.next_endpoint_index.set((i + 1) % NUM_ENDPOINTS);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
true
|
||||
} else {
|
||||
// Cannot receive now, indicate a delay to the controller.
|
||||
@@ -387,8 +399,8 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
}
|
||||
|
||||
fn cancel_out_transaction(&'a self, endpoint: usize) -> bool {
|
||||
- if let Some(s) = self.get_endpoint(endpoint) {
|
||||
- s.pending_out.take()
|
||||
+ if let Some(_) = self.get_endpoint(endpoint) {
|
||||
+ self.pending_out.take()
|
||||
} else {
|
||||
// Unsupported endpoint
|
||||
false
|
||||
@@ -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
|
||||
@@ -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 ../..
|
||||
|
||||
2
setup.sh
2
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/
|
||||
|
||||
26
src/env/tock/buffer_upgrade_storage.rs
vendored
26
src/env/tock/buffer_upgrade_storage.rs
vendored
@@ -14,20 +14,35 @@
|
||||
|
||||
use super::storage_helper::ModRange;
|
||||
use alloc::boxed::Box;
|
||||
use core::marker::PhantomData;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::Syscalls;
|
||||
use persistent_store::{StorageError, StorageResult};
|
||||
use platform::DefaultConfig;
|
||||
|
||||
const PARTITION_LENGTH: usize = 0x41000;
|
||||
const METADATA_LENGTH: usize = 0x1000;
|
||||
|
||||
pub struct BufferUpgradeStorage {
|
||||
pub struct BufferUpgradeStorage<
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config = DefaultConfig,
|
||||
> {
|
||||
/// Content of the partition storage.
|
||||
partition: Box<[u8]>,
|
||||
s: PhantomData<S>,
|
||||
c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl BufferUpgradeStorage {
|
||||
pub fn new() -> StorageResult<BufferUpgradeStorage> {
|
||||
impl<S, C> BufferUpgradeStorage<S, C>
|
||||
where
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
{
|
||||
pub fn new() -> StorageResult<Self> {
|
||||
Ok(BufferUpgradeStorage {
|
||||
partition: vec![0xff; PARTITION_LENGTH].into_boxed_slice(),
|
||||
s: PhantomData,
|
||||
c: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -72,10 +87,11 @@ impl BufferUpgradeStorage {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use libtock_unittest::fake::Syscalls;
|
||||
|
||||
#[test]
|
||||
fn read_write_bundle() {
|
||||
let mut storage = BufferUpgradeStorage::new().unwrap();
|
||||
let mut storage = BufferUpgradeStorage::<Syscalls>::new().unwrap();
|
||||
assert_eq!(storage.read_partition(0, 2).unwrap(), &[0xFF, 0xFF]);
|
||||
assert!(storage.write_bundle(1, vec![0x88, 0x88]).is_ok());
|
||||
assert_eq!(storage.read_partition(0, 2).unwrap(), &[0xFF, 0x88]);
|
||||
@@ -108,7 +124,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn partition_slice() {
|
||||
let storage = BufferUpgradeStorage::new().unwrap();
|
||||
let storage = BufferUpgradeStorage::<Syscalls>::new().unwrap();
|
||||
assert_eq!(storage.bundle_identifier(), 0x60000);
|
||||
}
|
||||
}
|
||||
|
||||
32
src/env/tock/clock.rs
vendored
32
src/env/tock/clock.rs
vendored
@@ -12,19 +12,21 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use core::marker::PhantomData;
|
||||
use libtock_drivers::timer::{get_clock_frequency, get_ticks};
|
||||
use libtock_platform::Syscalls;
|
||||
use opensk::api::clock::Clock;
|
||||
|
||||
/// 56-bits timestamp (valid for 70k+ years)
|
||||
#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||
struct Timestamp {
|
||||
epoch: usize, // 32-bits
|
||||
tick: usize, // 24-bits (32kHz)
|
||||
epoch: u32,
|
||||
tick: u32, // 24-bits (32kHz)
|
||||
}
|
||||
|
||||
impl Timestamp {
|
||||
/// Adds (potentially more than 24 bit of) ticks to this timestamp.
|
||||
pub fn add_ticks(&mut self, ticks: usize) {
|
||||
pub fn add_ticks(&mut self, ticks: u32) {
|
||||
// Saturating should never happen, but it fails gracefully.
|
||||
let sum = self.tick.saturating_add(ticks);
|
||||
self.epoch += sum >> 24;
|
||||
@@ -42,17 +44,26 @@ pub struct TockTimer {
|
||||
/// To guarantee correctness, you have to call any of its functions at least once per full tick
|
||||
/// counter wrap. In our case, 24 bit ticks with a 32 kHz frequency wrap after 512 seconds. If you
|
||||
/// can't guarantee to regularly create or check timers, call tickle at least every 8 minutes.
|
||||
#[derive(Default)]
|
||||
pub struct TockClock {
|
||||
pub struct TockClock<S: Syscalls> {
|
||||
now: Timestamp,
|
||||
s: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl TockClock {
|
||||
impl<S: Syscalls> Default for TockClock<S> {
|
||||
fn default() -> Self {
|
||||
TockClock {
|
||||
now: Timestamp::default(),
|
||||
s: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Syscalls> TockClock<S> {
|
||||
/// Elapses timers before the clock wraps.
|
||||
///
|
||||
/// Call this regularly to timeout reliably despite wrapping clock ticks.
|
||||
pub fn tickle(&mut self) {
|
||||
let cur_tick = get_ticks().ok().unwrap();
|
||||
let cur_tick = get_ticks::<S>().ok().unwrap();
|
||||
if cur_tick < self.now.tick {
|
||||
self.now.epoch += 1;
|
||||
}
|
||||
@@ -60,12 +71,13 @@ impl TockClock {
|
||||
}
|
||||
}
|
||||
|
||||
impl Clock for TockClock {
|
||||
impl<S: Syscalls> Clock for TockClock<S> {
|
||||
type Timer = TockTimer;
|
||||
|
||||
fn make_timer(&mut self, milliseconds: usize) -> Self::Timer {
|
||||
let milliseconds = milliseconds as u32;
|
||||
self.tickle();
|
||||
let clock_frequency = get_clock_frequency().ok().unwrap();
|
||||
let clock_frequency = get_clock_frequency::<S>().ok().unwrap();
|
||||
let delta_tick = match milliseconds.checked_mul(clock_frequency) {
|
||||
Some(x) => x / 1000,
|
||||
// All CTAP timeouts are multiples of 100 so far. Worst case we timeout too early.
|
||||
@@ -83,7 +95,7 @@ impl Clock for TockClock {
|
||||
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
fn timestamp_us(&mut self) -> usize {
|
||||
let clock_frequency = get_clock_frequency().ok().unwrap();
|
||||
let clock_frequency = get_clock_frequency::<S>().ok().unwrap();
|
||||
let total_ticks = 0x100_0000u64 * self.now.epoch as u64 + self.now.tick as u64;
|
||||
(total_ticks.wrapping_mul(1_000_000u64) / clock_frequency as u64) as usize
|
||||
}
|
||||
|
||||
69
src/env/tock/commands.rs
vendored
69
src/env/tock/commands.rs
vendored
@@ -17,6 +17,7 @@ use alloc::vec;
|
||||
use alloc::vec::Vec;
|
||||
use arrayref::array_ref;
|
||||
use core::convert::TryFrom;
|
||||
use libtock_platform::Syscalls;
|
||||
use opensk::api::attestation_store::{self, Attestation, AttestationStore};
|
||||
use opensk::api::crypto::sha256::Sha256;
|
||||
use opensk::api::crypto::EC_FIELD_SIZE;
|
||||
@@ -31,15 +32,18 @@ use opensk::ctap::secret::Secret;
|
||||
use opensk::ctap::status_code::Ctap2StatusCode;
|
||||
use opensk::ctap::{cbor_read, cbor_write, Channel};
|
||||
use opensk::env::{Env, Sha};
|
||||
use sk_cbor as cbor;
|
||||
use sk_cbor::{cbor_map_options, destructure_cbor_map};
|
||||
use {libtock_platform as platform, sk_cbor as cbor};
|
||||
|
||||
const VENDOR_COMMAND_CONFIGURE: u8 = 0x40;
|
||||
const VENDOR_COMMAND_UPGRADE: u8 = 0x42;
|
||||
const VENDOR_COMMAND_UPGRADE_INFO: u8 = 0x43;
|
||||
|
||||
pub fn process_vendor_command(
|
||||
env: &mut TockEnv,
|
||||
pub fn process_vendor_command<
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
>(
|
||||
env: &mut TockEnv<S, C>,
|
||||
bytes: &[u8],
|
||||
channel: Channel,
|
||||
) -> Option<Vec<u8>> {
|
||||
@@ -50,8 +54,8 @@ pub fn process_vendor_command(
|
||||
process_cbor(env, bytes, channel).unwrap_or_else(|e| Some(vec![e as u8]))
|
||||
}
|
||||
|
||||
fn process_cbor(
|
||||
env: &mut TockEnv,
|
||||
fn process_cbor<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config>(
|
||||
env: &mut TockEnv<S, C>,
|
||||
bytes: &[u8],
|
||||
channel: Channel,
|
||||
) -> Result<Option<Vec<u8>>, Ctap2StatusCode> {
|
||||
@@ -85,8 +89,11 @@ fn encode_cbor(value: cbor::Value) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
fn process_vendor_configure(
|
||||
env: &mut TockEnv,
|
||||
fn process_vendor_configure<
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
>(
|
||||
env: &mut TockEnv<S, C>,
|
||||
params: VendorConfigureParameters,
|
||||
// Unused in std only
|
||||
_channel: Channel,
|
||||
@@ -141,12 +148,15 @@ fn process_vendor_configure(
|
||||
Ok(response)
|
||||
}
|
||||
|
||||
fn process_vendor_upgrade(
|
||||
env: &mut TockEnv,
|
||||
fn process_vendor_upgrade<
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
>(
|
||||
env: &mut TockEnv<S, C>,
|
||||
params: VendorUpgradeParameters,
|
||||
) -> Result<(), Ctap2StatusCode> {
|
||||
let VendorUpgradeParameters { offset, data, hash } = params;
|
||||
let calculated_hash = Sha::<TockEnv>::digest(&data);
|
||||
let calculated_hash = Sha::<TockEnv<S>>::digest(&data);
|
||||
if hash != calculated_hash {
|
||||
return Err(Ctap2StatusCode::CTAP2_ERR_INTEGRITY_FAILURE);
|
||||
}
|
||||
@@ -156,8 +166,11 @@ fn process_vendor_upgrade(
|
||||
.map_err(|_| Ctap2StatusCode::CTAP1_ERR_INVALID_PARAMETER)
|
||||
}
|
||||
|
||||
fn process_vendor_upgrade_info(
|
||||
env: &mut TockEnv,
|
||||
fn process_vendor_upgrade_info<
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
>(
|
||||
env: &mut TockEnv<S, C>,
|
||||
) -> Result<VendorUpgradeInfoResponse, Ctap2StatusCode> {
|
||||
let upgrade_locations = env
|
||||
.upgrade_storage()
|
||||
@@ -288,6 +301,7 @@ impl From<VendorUpgradeInfoResponse> for cbor::Value {
|
||||
mod test {
|
||||
use super::*;
|
||||
use cbor::cbor_map;
|
||||
use libtock_unittest::fake::Syscalls;
|
||||
|
||||
const DUMMY_CHANNEL: Channel = Channel::MainHid([0x12, 0x34, 0x56, 0x78]);
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
@@ -295,14 +309,14 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_process_cbor_unrelated_input() {
|
||||
let mut env = TockEnv::default();
|
||||
let mut env = TockEnv::<Syscalls>::default();
|
||||
let cbor_bytes = vec![0x01];
|
||||
assert_eq!(process_cbor(&mut env, &cbor_bytes, DUMMY_CHANNEL), Ok(None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_cbor_invalid_input() {
|
||||
let mut env = TockEnv::default();
|
||||
let mut env = TockEnv::<Syscalls>::default();
|
||||
let cbor_bytes = vec![VENDOR_COMMAND_CONFIGURE];
|
||||
assert_eq!(
|
||||
process_cbor(&mut env, &cbor_bytes, DUMMY_CHANNEL),
|
||||
@@ -312,7 +326,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_process_cbor_valid_input() {
|
||||
let mut env = TockEnv::default();
|
||||
let mut env = TockEnv::<Syscalls>::default();
|
||||
let cbor_bytes = vec![VENDOR_COMMAND_UPGRADE_INFO];
|
||||
assert!(process_cbor(&mut env, &cbor_bytes, DUMMY_CHANNEL)
|
||||
.unwrap()
|
||||
@@ -322,7 +336,7 @@ mod test {
|
||||
#[test]
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
fn test_process_command_valid_vendor_hid() {
|
||||
let mut env = TockEnv::default();
|
||||
let mut env = TockEnv::<Syscalls>::default();
|
||||
let cbor_bytes = vec![VENDOR_COMMAND_UPGRADE_INFO];
|
||||
assert!(process_cbor(&mut env, &cbor_bytes, VENDOR_CHANNEL)
|
||||
.unwrap()
|
||||
@@ -453,7 +467,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_vendor_upgrade_info() {
|
||||
let mut env = TockEnv::default();
|
||||
let mut env = TockEnv::<Syscalls>::default();
|
||||
let cbor_bytes = [VENDOR_COMMAND_UPGRADE_INFO];
|
||||
assert!(process_cbor(&mut env, &cbor_bytes, DUMMY_CHANNEL)
|
||||
.unwrap()
|
||||
@@ -462,7 +476,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_vendor_configure() {
|
||||
let mut env = TockEnv::default();
|
||||
let mut env = TockEnv::<Syscalls>::default();
|
||||
|
||||
// Nothing should be configured at the beginning
|
||||
let response = process_vendor_configure(
|
||||
@@ -538,7 +552,7 @@ mod test {
|
||||
}))
|
||||
);
|
||||
|
||||
// Now try to lock the device
|
||||
// Now try to lock the device, but that is currently not supported.
|
||||
let response = process_vendor_configure(
|
||||
&mut env,
|
||||
VendorConfigureParameters {
|
||||
@@ -549,10 +563,7 @@ mod test {
|
||||
);
|
||||
assert_eq!(
|
||||
response,
|
||||
Ok(VendorConfigureResponse {
|
||||
cert_programmed: true,
|
||||
pkey_programmed: true,
|
||||
})
|
||||
Err(Ctap2StatusCode::CTAP2_ERR_VENDOR_INTERNAL_ERROR)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -561,13 +572,13 @@ mod test {
|
||||
// The test partition storage has size 0x40000.
|
||||
// The test metadata storage has size 0x1000.
|
||||
// The test identifier matches partition B.
|
||||
let mut env = TockEnv::default();
|
||||
let mut env = TockEnv::<Syscalls>::default();
|
||||
|
||||
const METADATA_LEN: usize = 0x1000;
|
||||
let metadata = vec![0xFF; METADATA_LEN];
|
||||
let metadata_hash = Sha::<TockEnv>::digest(&metadata);
|
||||
let metadata_hash = Sha::<TockEnv<Syscalls>>::digest(&metadata);
|
||||
let data = vec![0xFF; 0x1000];
|
||||
let hash = Sha::<TockEnv>::digest(&data);
|
||||
let hash = Sha::<TockEnv<Syscalls>>::digest(&data);
|
||||
|
||||
// Write to partition.
|
||||
let response = process_vendor_upgrade(
|
||||
@@ -638,11 +649,11 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_vendor_upgrade_no_second_partition() {
|
||||
let mut env = TockEnv::default();
|
||||
let mut env = TockEnv::<Syscalls>::default();
|
||||
env.disable_upgrade_storage();
|
||||
|
||||
let data = vec![0xFF; 0x1000];
|
||||
let hash = Sha::<TockEnv>::digest(&data);
|
||||
let hash = Sha::<TockEnv<Syscalls>>::digest(&data);
|
||||
let response = process_vendor_upgrade(
|
||||
&mut env,
|
||||
VendorUpgradeParameters {
|
||||
@@ -656,7 +667,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_vendor_upgrade_info() {
|
||||
let mut env = TockEnv::default();
|
||||
let mut env = TockEnv::<Syscalls>::default();
|
||||
let bundle_identifier = env.upgrade_storage().unwrap().bundle_identifier();
|
||||
|
||||
let upgrade_info_reponse = process_vendor_upgrade_info(&mut env);
|
||||
|
||||
323
src/env/tock/mod.rs
vendored
323
src/env/tock/mod.rs
vendored
@@ -16,16 +16,18 @@ use alloc::vec::Vec;
|
||||
use clock::TockClock;
|
||||
use core::cell::Cell;
|
||||
use core::convert::TryFrom;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use core::marker::PhantomData;
|
||||
#[cfg(all(target_has_atomic = "8", not(feature = "std")))]
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use libtock_core::result::{CommandError, EALREADY};
|
||||
use libtock_drivers::buttons::{self, ButtonState};
|
||||
use libtock_drivers::console::Console;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use libtock_drivers::crp;
|
||||
use libtock_buttons::{ButtonListener, ButtonState, Buttons};
|
||||
use libtock_console::{Console, ConsoleWriter};
|
||||
use libtock_drivers::result::{FlexUnwrap, TockError};
|
||||
use libtock_drivers::timer::Duration;
|
||||
use libtock_drivers::{led, rng, timer, usb_ctap_hid};
|
||||
use libtock_drivers::usb_ctap_hid::UsbCtapHid;
|
||||
use libtock_drivers::{rng, timer, usb_ctap_hid};
|
||||
use libtock_leds::Leds;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{ErrorCode, Syscalls};
|
||||
use opensk::api::attestation_store::AttestationStore;
|
||||
use opensk::api::connection::{
|
||||
HidConnection, SendOrRecvError, SendOrRecvResult, SendOrRecvStatus, UsbEndpoint,
|
||||
@@ -38,28 +40,31 @@ use opensk::api::{attestation_store, key_store};
|
||||
use opensk::ctap::Channel;
|
||||
use opensk::env::Env;
|
||||
#[cfg(feature = "std")]
|
||||
use persistent_store::{BufferOptions, BufferStorage};
|
||||
use persistent_store::BufferOptions;
|
||||
use persistent_store::{StorageResult, Store};
|
||||
use platform::{share, DefaultConfig, Subscribe};
|
||||
use rand_core::{impls, CryptoRng, Error, RngCore};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
mod buffer_upgrade_storage;
|
||||
mod clock;
|
||||
mod commands;
|
||||
#[cfg(feature = "std")]
|
||||
mod phantom_buffer_storage;
|
||||
#[cfg(not(feature = "std"))]
|
||||
mod storage;
|
||||
mod storage_helper;
|
||||
mod upgrade_helper;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub type Storage = storage::TockStorage;
|
||||
pub type Storage<S, C> = storage::TockStorage<S, C>;
|
||||
#[cfg(feature = "std")]
|
||||
pub type Storage = BufferStorage;
|
||||
pub type Storage<S, C> = phantom_buffer_storage::PhantomBufferStorage<S, C>;
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
type UpgradeStorage = storage::TockUpgradeStorage;
|
||||
type UpgradeStorage<S, C> = storage::TockUpgradeStorage<S, C>;
|
||||
#[cfg(feature = "std")]
|
||||
type UpgradeStorage = buffer_upgrade_storage::BufferUpgradeStorage;
|
||||
type UpgradeStorage<S, C> = buffer_upgrade_storage::BufferUpgradeStorage<S, C>;
|
||||
|
||||
pub const AAGUID: &[u8; AAGUID_LENGTH] =
|
||||
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_aaguid.bin"));
|
||||
@@ -70,11 +75,21 @@ const TOCK_CUSTOMIZATION: CustomizationImpl = CustomizationImpl {
|
||||
};
|
||||
|
||||
/// RNG backed by the TockOS rng driver.
|
||||
pub struct TockRng {}
|
||||
pub struct TockRng<S: Syscalls> {
|
||||
_syscalls: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl CryptoRng for TockRng {}
|
||||
impl<S: Syscalls> Default for TockRng<S> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
_syscalls: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RngCore for TockRng {
|
||||
impl<S: Syscalls> CryptoRng for TockRng<S> {}
|
||||
|
||||
impl<S: Syscalls> RngCore for TockRng<S> {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
impls::next_u32_via_fill(self)
|
||||
}
|
||||
@@ -84,7 +99,7 @@ impl RngCore for TockRng {
|
||||
}
|
||||
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
rng::fill_buffer(dest);
|
||||
rng::Rng::<S>::fill_buffer(dest);
|
||||
}
|
||||
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> {
|
||||
@@ -93,74 +108,89 @@ impl RngCore for TockRng {
|
||||
}
|
||||
}
|
||||
|
||||
impl Rng for TockRng {}
|
||||
impl<S: Syscalls> Rng for TockRng<S> {}
|
||||
|
||||
pub struct TockHidConnection {
|
||||
pub struct TockHidConnection<S: Syscalls> {
|
||||
endpoint: UsbEndpoint,
|
||||
s: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl HidConnection for TockHidConnection {
|
||||
impl<S: Syscalls> HidConnection for TockHidConnection<S> {
|
||||
fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult {
|
||||
match usb_ctap_hid::send_or_recv_with_timeout(
|
||||
match UsbCtapHid::<S>::send_or_recv_with_timeout(
|
||||
buf,
|
||||
Duration::from_ms(timeout_ms as isize),
|
||||
self.endpoint as usize,
|
||||
self.endpoint as u32,
|
||||
) {
|
||||
Ok(usb_ctap_hid::SendOrRecvStatus::Timeout) => Ok(SendOrRecvStatus::Timeout),
|
||||
Ok(usb_ctap_hid::SendOrRecvStatus::Sent) => Ok(SendOrRecvStatus::Sent),
|
||||
Ok(usb_ctap_hid::SendOrRecvStatus::Received(recv_endpoint)) => {
|
||||
UsbEndpoint::try_from(recv_endpoint).map(SendOrRecvStatus::Received)
|
||||
UsbEndpoint::try_from(recv_endpoint as usize).map(SendOrRecvStatus::Received)
|
||||
}
|
||||
_ => Err(SendOrRecvError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TockEnv {
|
||||
rng: TockRng,
|
||||
store: Store<Storage>,
|
||||
upgrade_storage: Option<UpgradeStorage>,
|
||||
main_connection: TockHidConnection,
|
||||
pub struct TockEnv<
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config = DefaultConfig,
|
||||
> {
|
||||
rng: TockRng<S>,
|
||||
store: Store<Storage<S, C>>,
|
||||
upgrade_storage: Option<UpgradeStorage<S, C>>,
|
||||
main_connection: TockHidConnection<S>,
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
vendor_connection: TockHidConnection,
|
||||
vendor_connection: TockHidConnection<S>,
|
||||
blink_pattern: usize,
|
||||
clock: TockClock,
|
||||
clock: TockClock<S>,
|
||||
c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl Default for TockEnv {
|
||||
impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> Default
|
||||
for TockEnv<S, C>
|
||||
{
|
||||
/// Returns the unique instance of the Tock environment.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - If called a second time.
|
||||
fn default() -> Self {
|
||||
let rng = TockRng::default();
|
||||
// We rely on `take_storage` to ensure that this function is called only once.
|
||||
let storage = take_storage().unwrap();
|
||||
let storage = take_storage::<S, C>().unwrap();
|
||||
let store = Store::new(storage).ok().unwrap();
|
||||
let upgrade_storage = UpgradeStorage::new().ok();
|
||||
TockEnv {
|
||||
rng: TockRng {},
|
||||
rng,
|
||||
store,
|
||||
upgrade_storage,
|
||||
main_connection: TockHidConnection {
|
||||
endpoint: UsbEndpoint::MainHid,
|
||||
s: PhantomData,
|
||||
},
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
vendor_connection: TockHidConnection {
|
||||
endpoint: UsbEndpoint::VendorHid,
|
||||
s: PhantomData,
|
||||
},
|
||||
blink_pattern: 0,
|
||||
clock: TockClock::default(),
|
||||
c: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TockEnv {
|
||||
impl<S, C> TockEnv<S, C>
|
||||
where
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
{
|
||||
/// Returns the upgrade storage instance.
|
||||
///
|
||||
/// Upgrade storage is optional, so implementations may return `None`. However, implementations
|
||||
/// should either always return `None` or always return `Some`.
|
||||
pub fn upgrade_storage(&mut self) -> Option<&mut UpgradeStorage> {
|
||||
pub fn upgrade_storage(&mut self) -> Option<&mut UpgradeStorage<S, C>> {
|
||||
self.upgrade_storage.as_mut()
|
||||
}
|
||||
|
||||
@@ -169,39 +199,13 @@ impl TockEnv {
|
||||
}
|
||||
|
||||
pub fn lock_firmware_protection(&mut self) -> bool {
|
||||
#[cfg(not(feature = "std"))]
|
||||
{
|
||||
matches!(
|
||||
crp::set_protection(crp::ProtectionLevel::FullyLocked),
|
||||
Ok(())
|
||||
| Err(TockError::Command(CommandError {
|
||||
return_code: EALREADY,
|
||||
..
|
||||
}))
|
||||
)
|
||||
}
|
||||
#[cfg(feature = "std")]
|
||||
{
|
||||
true
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the unique storage instance.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - If called a second time.
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub fn take_storage() -> StorageResult<Storage> {
|
||||
// Make sure the storage was not already taken.
|
||||
static TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
assert!(!TAKEN.fetch_or(true, Ordering::SeqCst));
|
||||
Storage::new()
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
pub fn take_storage() -> StorageResult<Storage> {
|
||||
pub fn take_storage<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config>(
|
||||
) -> StorageResult<Storage<S, C>> {
|
||||
// Use the Nordic configuration.
|
||||
const PAGE_SIZE: usize = 0x1000;
|
||||
const NUM_PAGES: usize = 20;
|
||||
@@ -213,10 +217,54 @@ pub fn take_storage() -> StorageResult<Storage> {
|
||||
max_page_erases: 10000,
|
||||
strict_mode: true,
|
||||
};
|
||||
Ok(BufferStorage::new(store, options))
|
||||
Ok(phantom_buffer_storage::PhantomBufferStorage::new(
|
||||
store, options,
|
||||
))
|
||||
}
|
||||
|
||||
impl UserPresence for TockEnv {
|
||||
/// Returns the unique storage instance.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - If called a second time.
|
||||
#[cfg(not(feature = "std"))]
|
||||
pub fn take_storage<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config>(
|
||||
) -> StorageResult<Storage<S, C>> {
|
||||
// Make sure the storage was not already taken.
|
||||
#[cfg(target_has_atomic = "8")]
|
||||
{
|
||||
static TAKEN: AtomicBool = AtomicBool::new(false);
|
||||
assert!(!TAKEN.fetch_or(true, Ordering::SeqCst));
|
||||
}
|
||||
#[cfg(not(target_has_atomic = "8"))]
|
||||
{
|
||||
static mut TAKEN: bool = false;
|
||||
// Safety
|
||||
//
|
||||
// We can not use an AtomicBool on platforms that do not support atomics,
|
||||
// such as the whole `riscv32i[mc]` family like OpenTitan.
|
||||
// Thus, we need to use a mutable static variable which are unsafe
|
||||
// cause they could cause a data race when two threads access it
|
||||
// at the same time.
|
||||
//
|
||||
// However, as we are running an application on TockOS and because
|
||||
// of its [architecture](https://www.tockos.org/documentation/design)
|
||||
// we are running in a single-threaded event loop which means the
|
||||
// aforementioned data race is impossible. Thus, in this case, the
|
||||
// usage of a static mut is safe.
|
||||
unsafe {
|
||||
assert!(!TAKEN);
|
||||
TAKEN = true;
|
||||
}
|
||||
}
|
||||
Storage::new()
|
||||
}
|
||||
|
||||
impl<S, C> UserPresence for TockEnv<S, C>
|
||||
where
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
{
|
||||
fn check_init(&mut self) {
|
||||
self.blink_pattern = 0;
|
||||
}
|
||||
@@ -225,52 +273,77 @@ impl UserPresence for TockEnv {
|
||||
if timeout_ms == 0 {
|
||||
return Err(UserPresenceError::Timeout);
|
||||
}
|
||||
blink_leds(self.blink_pattern);
|
||||
blink_leds::<S>(self.blink_pattern);
|
||||
self.blink_pattern += 1;
|
||||
|
||||
// enable interrupts for all buttons
|
||||
let num_buttons = Buttons::<S>::count().map_err(|_| UserPresenceError::Fail)?;
|
||||
(0..num_buttons)
|
||||
.try_for_each(|n| Buttons::<S>::enable_interrupts(n))
|
||||
.map_err(|_| UserPresenceError::Fail)?;
|
||||
|
||||
let button_touched = Cell::new(false);
|
||||
let mut buttons_callback = buttons::with_callback(|_button_num, state| {
|
||||
let button_listener = ButtonListener(|_button_num, state| {
|
||||
match state {
|
||||
ButtonState::Pressed => button_touched.set(true),
|
||||
ButtonState::Released => (),
|
||||
};
|
||||
});
|
||||
let mut buttons = buttons_callback.init().flex_unwrap();
|
||||
for mut button in &mut buttons {
|
||||
button.enable().flex_unwrap();
|
||||
}
|
||||
|
||||
// Setup a keep-alive callback.
|
||||
// Setup a keep-alive callback but don't enable it yet
|
||||
let keepalive_expired = Cell::new(false);
|
||||
let mut keepalive_callback = timer::with_callback(|_, _| {
|
||||
keepalive_expired.set(true);
|
||||
});
|
||||
let mut keepalive = keepalive_callback.init().flex_unwrap();
|
||||
let keepalive_alarm = keepalive
|
||||
.set_alarm(Duration::from_ms(timeout_ms as isize))
|
||||
.flex_unwrap();
|
||||
let mut keepalive_callback =
|
||||
timer::with_callback::<S, C, _>(|_| keepalive_expired.set(true));
|
||||
share::scope::<
|
||||
(
|
||||
Subscribe<_, { libtock_buttons::DRIVER_NUM }, 0>,
|
||||
Subscribe<
|
||||
S,
|
||||
{ libtock_drivers::timer::DRIVER_NUM },
|
||||
{ libtock_drivers::timer::subscribe::CALLBACK },
|
||||
>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (sub_button, sub_timer) = handle.split();
|
||||
Buttons::<S>::register_listener(&button_listener, sub_button)
|
||||
.map_err(|_| UserPresenceError::Fail)?;
|
||||
|
||||
// Wait for a button touch or an alarm.
|
||||
libtock_drivers::util::yieldk_for(|| button_touched.get() || keepalive_expired.get());
|
||||
let mut keepalive = keepalive_callback.init().flex_unwrap();
|
||||
keepalive_callback
|
||||
.enable(sub_timer)
|
||||
.map_err(|_| UserPresenceError::Fail)?;
|
||||
keepalive
|
||||
.set_alarm(timer::Duration::from_ms(timeout_ms as isize))
|
||||
.flex_unwrap();
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match keepalive.stop_alarm(keepalive_alarm) {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(CommandError {
|
||||
return_code: EALREADY,
|
||||
..
|
||||
})) => assert!(keepalive_expired.get()),
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
// Wait for a button touch or an alarm.
|
||||
libtock_drivers::util::Util::<S>::yieldk_for(|| {
|
||||
button_touched.get() || keepalive_expired.get()
|
||||
});
|
||||
|
||||
Buttons::<S>::unregister_listener();
|
||||
|
||||
// disable event interrupts for all buttons
|
||||
(0..num_buttons)
|
||||
.try_for_each(|n| Buttons::<S>::disable_interrupts(n))
|
||||
.map_err(|_| UserPresenceError::Fail)?;
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match keepalive.stop_alarm() {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(ErrorCode::Already)) => assert!(keepalive_expired.get()),
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for mut button in &mut buttons {
|
||||
button.disable().flex_unwrap();
|
||||
}
|
||||
Ok::<(), UserPresenceError>(())
|
||||
})?;
|
||||
|
||||
if button_touched.get() {
|
||||
Ok(())
|
||||
@@ -282,13 +355,22 @@ impl UserPresence for TockEnv {
|
||||
}
|
||||
|
||||
fn check_complete(&mut self) {
|
||||
switch_off_leds();
|
||||
switch_off_leds::<S>();
|
||||
}
|
||||
}
|
||||
|
||||
impl key_store::Helper for TockEnv {}
|
||||
impl<S, C> key_store::Helper for TockEnv<S, C>
|
||||
where
|
||||
S: Syscalls,
|
||||
C: platform::allow_ro::Config + platform::subscribe::Config,
|
||||
{
|
||||
}
|
||||
|
||||
impl AttestationStore for TockEnv {
|
||||
impl<S, C> AttestationStore for TockEnv<S, C>
|
||||
where
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
{
|
||||
fn get(
|
||||
&mut self,
|
||||
id: &attestation_store::Id,
|
||||
@@ -311,16 +393,18 @@ impl AttestationStore for TockEnv {
|
||||
}
|
||||
}
|
||||
|
||||
impl Env for TockEnv {
|
||||
type Rng = TockRng;
|
||||
impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> Env
|
||||
for TockEnv<S, C>
|
||||
{
|
||||
type Rng = TockRng<S>;
|
||||
type UserPresence = Self;
|
||||
type Storage = Storage;
|
||||
type Storage = Storage<S, C>;
|
||||
type KeyStore = Self;
|
||||
type AttestationStore = Self;
|
||||
type Clock = TockClock;
|
||||
type Write = Console;
|
||||
type Clock = TockClock<S>;
|
||||
type Write = ConsoleWriter<S>;
|
||||
type Customization = CustomizationImpl;
|
||||
type HidConnection = TockHidConnection;
|
||||
type HidConnection = TockHidConnection<S>;
|
||||
type Crypto = SoftwareCrypto;
|
||||
|
||||
fn rng(&mut self) -> &mut Self::Rng {
|
||||
@@ -348,7 +432,7 @@ impl Env for TockEnv {
|
||||
}
|
||||
|
||||
fn write(&mut self) -> Self::Write {
|
||||
Console::new()
|
||||
Console::<S>::writer()
|
||||
}
|
||||
|
||||
fn customization(&self) -> &Self::Customization {
|
||||
@@ -375,17 +459,17 @@ impl Env for TockEnv {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn blink_leds(pattern_seed: usize) {
|
||||
for l in 0..led::count().flex_unwrap() {
|
||||
if (pattern_seed ^ l).count_ones() & 1 != 0 {
|
||||
led::get(l).flex_unwrap().on().flex_unwrap();
|
||||
pub fn blink_leds<S: Syscalls>(pattern_seed: usize) {
|
||||
for l in 0..Leds::<S>::count().unwrap() {
|
||||
if (pattern_seed ^ l as usize).count_ones() & 1 != 0 {
|
||||
Leds::<S>::on(l).unwrap();
|
||||
} else {
|
||||
led::get(l).flex_unwrap().off().flex_unwrap();
|
||||
Leds::<S>::off(l).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wink_leds(pattern_seed: usize) {
|
||||
pub fn wink_leds<S: Syscalls>(pattern_seed: usize) {
|
||||
// This generates a "snake" pattern circling through the LEDs.
|
||||
// Fox example with 4 LEDs the sequence of lit LEDs will be the following.
|
||||
// 0 1 2 3
|
||||
@@ -398,7 +482,7 @@ pub fn wink_leds(pattern_seed: usize) {
|
||||
// * *
|
||||
// * * *
|
||||
// * *
|
||||
let count = led::count().flex_unwrap();
|
||||
let count = Leds::<S>::count().unwrap() as usize;
|
||||
let a = (pattern_seed / 2) % count;
|
||||
let b = ((pattern_seed + 1) / 2) % count;
|
||||
let c = ((pattern_seed + 3) / 2) % count;
|
||||
@@ -411,16 +495,17 @@ pub fn wink_leds(pattern_seed: usize) {
|
||||
_ => l,
|
||||
};
|
||||
if k == a || k == b || k == c {
|
||||
led::get(l).flex_unwrap().on().flex_unwrap();
|
||||
Leds::<S>::on(l as u32).unwrap();
|
||||
} else {
|
||||
led::get(l).flex_unwrap().off().flex_unwrap();
|
||||
Leds::<S>::off(l as u32).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn switch_off_leds() {
|
||||
for l in 0..led::count().flex_unwrap() {
|
||||
led::get(l).flex_unwrap().off().flex_unwrap();
|
||||
pub fn switch_off_leds<S: Syscalls>() {
|
||||
let count = Leds::<S>::count().unwrap();
|
||||
for l in 0..count {
|
||||
Leds::<S>::off(l).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
111
src/env/tock/phantom_buffer_storage.rs
vendored
Normal file
111
src/env/tock/phantom_buffer_storage.rs
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use alloc::borrow::Cow;
|
||||
use core::marker::PhantomData;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::Syscalls;
|
||||
use persistent_store::{
|
||||
BufferCorruptFunction, BufferOptions, BufferStorage, Storage, StorageIndex, StorageResult,
|
||||
};
|
||||
|
||||
/// Wrapper with phantom data for the test storage implementation.
|
||||
pub struct PhantomBufferStorage<
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
> {
|
||||
storage: BufferStorage,
|
||||
s: PhantomData<S>,
|
||||
c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl<S, C> PhantomBufferStorage<S, C>
|
||||
where
|
||||
S: Syscalls,
|
||||
C: platform::allow_ro::Config + platform::subscribe::Config,
|
||||
{
|
||||
pub fn new(storage: Box<[u8]>, options: BufferOptions) -> Self {
|
||||
Self {
|
||||
storage: BufferStorage::new(storage, options),
|
||||
s: PhantomData,
|
||||
c: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arm_interruption(&mut self, delay: usize) {
|
||||
self.storage.arm_interruption(delay);
|
||||
}
|
||||
|
||||
pub fn disarm_interruption(&mut self) -> usize {
|
||||
self.storage.disarm_interruption()
|
||||
}
|
||||
|
||||
pub fn reset_interruption(&mut self) {
|
||||
self.storage.reset_interruption();
|
||||
}
|
||||
|
||||
pub fn corrupt_operation(&mut self, corrupt: BufferCorruptFunction) {
|
||||
self.storage.corrupt_operation(corrupt);
|
||||
}
|
||||
|
||||
pub fn get_word_writes(&self, word: usize) -> usize {
|
||||
self.storage.get_word_writes(word)
|
||||
}
|
||||
|
||||
pub fn get_page_erases(&self, page: usize) -> usize {
|
||||
self.storage.get_page_erases(page)
|
||||
}
|
||||
|
||||
pub fn set_page_erases(&mut self, page: usize, cycle: usize) {
|
||||
self.storage.set_page_erases(page, cycle);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, C> Storage for PhantomBufferStorage<S, C>
|
||||
where
|
||||
S: Syscalls,
|
||||
C: platform::allow_ro::Config + platform::subscribe::Config,
|
||||
{
|
||||
fn word_size(&self) -> usize {
|
||||
self.storage.word_size()
|
||||
}
|
||||
|
||||
fn page_size(&self) -> usize {
|
||||
self.storage.page_size()
|
||||
}
|
||||
|
||||
fn num_pages(&self) -> usize {
|
||||
self.storage.num_pages()
|
||||
}
|
||||
|
||||
fn max_word_writes(&self) -> usize {
|
||||
self.storage.max_word_writes()
|
||||
}
|
||||
|
||||
fn max_page_erases(&self) -> usize {
|
||||
self.storage.max_page_erases()
|
||||
}
|
||||
|
||||
fn read_slice(&self, index: StorageIndex, length: usize) -> StorageResult<Cow<[u8]>> {
|
||||
self.storage.read_slice(index, length)
|
||||
}
|
||||
|
||||
fn write_slice(&mut self, index: StorageIndex, value: &[u8]) -> StorageResult<()> {
|
||||
self.storage.write_slice(index, value)
|
||||
}
|
||||
|
||||
fn erase_page(&mut self, page: usize) -> StorageResult<()> {
|
||||
self.storage.erase_page(page)
|
||||
}
|
||||
}
|
||||
208
src/env/tock/storage.rs
vendored
208
src/env/tock/storage.rs
vendored
@@ -20,34 +20,37 @@ use super::TockEnv;
|
||||
use alloc::borrow::Cow;
|
||||
use alloc::vec::Vec;
|
||||
use core::cell::Cell;
|
||||
use libtock_core::{callback, syscalls};
|
||||
use core::marker::PhantomData;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{syscall_class, ErrorCode, RawSyscalls, Syscalls};
|
||||
use opensk::api::crypto::sha256::Sha256;
|
||||
use opensk::env::Sha;
|
||||
use persistent_store::{Storage, StorageError, StorageIndex, StorageResult};
|
||||
use platform::share;
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x50003;
|
||||
const DRIVER_NUMBER: u32 = 0x50003;
|
||||
|
||||
const UPGRADE_PUBLIC_KEY: &[u8; 65] =
|
||||
include_bytes!(concat!(env!("OUT_DIR"), "/opensk_upgrade_pubkey.bin"));
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const DONE: usize = 0;
|
||||
pub const DONE: u32 = 0;
|
||||
}
|
||||
|
||||
mod command_nr {
|
||||
pub const GET_INFO: usize = 1;
|
||||
pub const GET_INFO: u32 = 1;
|
||||
pub mod get_info_nr {
|
||||
pub const WORD_SIZE: usize = 0;
|
||||
pub const PAGE_SIZE: usize = 1;
|
||||
pub const MAX_WORD_WRITES: usize = 2;
|
||||
pub const MAX_PAGE_ERASES: usize = 3;
|
||||
pub const WORD_SIZE: u32 = 0;
|
||||
pub const PAGE_SIZE: u32 = 1;
|
||||
pub const MAX_WORD_WRITES: u32 = 2;
|
||||
pub const MAX_PAGE_ERASES: u32 = 3;
|
||||
}
|
||||
pub const WRITE_SLICE: usize = 2;
|
||||
pub const ERASE_PAGE: usize = 3;
|
||||
pub const WRITE_SLICE: u32 = 2;
|
||||
pub const ERASE_PAGE: u32 = 3;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const WRITE_SLICE: usize = 0;
|
||||
mod ro_allow_nr {
|
||||
pub const WRITE_SLICE: u32 = 0;
|
||||
}
|
||||
|
||||
mod memop_nr {
|
||||
@@ -58,85 +61,98 @@ mod memop_nr {
|
||||
}
|
||||
|
||||
mod storage_type {
|
||||
pub const STORE: usize = 1;
|
||||
pub const PARTITION: usize = 2;
|
||||
pub const STORE: u32 = 1;
|
||||
pub const PARTITION: u32 = 2;
|
||||
}
|
||||
|
||||
fn get_info(nr: usize, arg: usize) -> StorageResult<usize> {
|
||||
let code = syscalls::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg);
|
||||
code.map_err(|_| StorageError::CustomError)
|
||||
fn get_info<S: Syscalls>(nr: u32, arg: u32) -> StorageResult<u32> {
|
||||
let info = S::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg)
|
||||
.to_result::<u32, ErrorCode>()
|
||||
.map_err(|_| StorageError::CustomError)?;
|
||||
Ok(info)
|
||||
}
|
||||
|
||||
fn memop(nr: u32, arg: usize) -> StorageResult<usize> {
|
||||
let code = unsafe { syscalls::raw::memop(nr, arg) };
|
||||
if code < 0 {
|
||||
Err(StorageError::CustomError)
|
||||
} else {
|
||||
Ok(code as usize)
|
||||
fn memop<S: RawSyscalls>(nr: u32, arg: u32) -> StorageResult<u32> {
|
||||
let registers = unsafe { S::syscall2::<{ syscall_class::MEMOP }>([nr.into(), arg.into()]) };
|
||||
|
||||
let r0 = registers[0].as_u32();
|
||||
let r1 = registers[1].as_u32();
|
||||
|
||||
// make sure r0 is the `success with u32` (129) return variant and then return the value in r1 as u32
|
||||
// see: https://github.com/tock/tock/blob/master/doc/reference/trd104-syscalls.md#32-return-values
|
||||
match (r0, r1) {
|
||||
(129, r1) => Ok(r1),
|
||||
(_, _) => Err(StorageError::CustomError),
|
||||
}
|
||||
}
|
||||
|
||||
fn block_command(driver: usize, cmd: usize, arg1: usize, arg2: usize) -> StorageResult<()> {
|
||||
let done = Cell::new(None);
|
||||
let mut alarm = |status| done.set(Some(status));
|
||||
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::DONE,
|
||||
&mut alarm,
|
||||
);
|
||||
if subscription.is_err() {
|
||||
return Err(StorageError::CustomError);
|
||||
}
|
||||
fn block_command<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config>(
|
||||
driver: u32,
|
||||
cmd: u32,
|
||||
arg1: u32,
|
||||
arg2: u32,
|
||||
) -> StorageResult<()> {
|
||||
let called: Cell<Option<(u32,)>> = Cell::new(None);
|
||||
|
||||
let code = syscalls::command(driver, cmd, arg1, arg2);
|
||||
if code.is_err() {
|
||||
return Err(StorageError::CustomError);
|
||||
}
|
||||
|
||||
libtock_drivers::util::yieldk_for(|| done.get().is_some());
|
||||
if done.get().unwrap() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(StorageError::CustomError)
|
||||
}
|
||||
share::scope(|subscribe| {
|
||||
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::DONE }>(subscribe, &called)
|
||||
.map_err(|_| StorageError::CustomError)?;
|
||||
S::command(driver, cmd, arg1, arg2)
|
||||
.to_result::<(), ErrorCode>()
|
||||
.map_err(|_| StorageError::CustomError)?;
|
||||
libtock_drivers::util::Util::<S>::yieldk_for(|| called.get().is_some());
|
||||
if called.get().unwrap().0 == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(StorageError::CustomError)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn read_slice(address: usize, length: usize) -> &'static [u8] {
|
||||
core::slice::from_raw_parts(address as *const u8, length)
|
||||
}
|
||||
|
||||
fn write_slice(ptr: usize, value: &[u8]) -> StorageResult<()> {
|
||||
let code = unsafe {
|
||||
syscalls::raw::allow(
|
||||
fn write_slice<S: Syscalls, C: platform::allow_ro::Config + platform::subscribe::Config>(
|
||||
ptr: usize,
|
||||
value: &[u8],
|
||||
) -> StorageResult<()> {
|
||||
share::scope(|allow_ro| {
|
||||
S::allow_ro::<C, DRIVER_NUMBER, { ro_allow_nr::WRITE_SLICE }>(allow_ro, value)
|
||||
.map_err(|_| StorageError::CustomError)?;
|
||||
block_command::<S, C>(
|
||||
DRIVER_NUMBER,
|
||||
allow_nr::WRITE_SLICE,
|
||||
// We rely on the driver not writing to the slice. This should use read-only allow
|
||||
// when available. See https://github.com/tock/tock/issues/1274.
|
||||
value.as_ptr() as *mut u8,
|
||||
value.len(),
|
||||
command_nr::WRITE_SLICE,
|
||||
ptr as u32,
|
||||
value.len() as u32,
|
||||
)
|
||||
};
|
||||
if code < 0 {
|
||||
return Err(StorageError::CustomError);
|
||||
}
|
||||
|
||||
block_command(DRIVER_NUMBER, command_nr::WRITE_SLICE, ptr, value.len())
|
||||
})
|
||||
}
|
||||
|
||||
fn erase_page(ptr: usize, page_length: usize) -> StorageResult<()> {
|
||||
block_command(DRIVER_NUMBER, command_nr::ERASE_PAGE, ptr, page_length)
|
||||
fn erase_page<S: Syscalls, C: platform::allow_ro::Config + platform::subscribe::Config>(
|
||||
ptr: usize,
|
||||
page_length: usize,
|
||||
) -> StorageResult<()> {
|
||||
block_command::<S, C>(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::ERASE_PAGE,
|
||||
ptr as u32,
|
||||
page_length as u32,
|
||||
)
|
||||
}
|
||||
|
||||
pub struct TockStorage {
|
||||
pub struct TockStorage<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> {
|
||||
word_size: usize,
|
||||
page_size: usize,
|
||||
num_pages: usize,
|
||||
max_word_writes: usize,
|
||||
max_page_erases: usize,
|
||||
storage_locations: Vec<&'static [u8]>,
|
||||
s: PhantomData<S>,
|
||||
c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl TockStorage {
|
||||
impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> TockStorage<S, C> {
|
||||
/// Provides access to the embedded flash if available.
|
||||
///
|
||||
/// # Errors
|
||||
@@ -146,14 +162,16 @@ impl TockStorage {
|
||||
/// - The page size is a power of two.
|
||||
/// - The page size is a multiple of the word size.
|
||||
/// - The storage is page-aligned.
|
||||
pub fn new() -> StorageResult<TockStorage> {
|
||||
pub fn new() -> StorageResult<TockStorage<S, C>> {
|
||||
let mut syscall = TockStorage {
|
||||
word_size: get_info(command_nr::get_info_nr::WORD_SIZE, 0)?,
|
||||
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
|
||||
word_size: get_info::<S>(command_nr::get_info_nr::WORD_SIZE, 0)? as usize,
|
||||
page_size: get_info::<S>(command_nr::get_info_nr::PAGE_SIZE, 0)? as usize,
|
||||
num_pages: 0,
|
||||
max_word_writes: get_info(command_nr::get_info_nr::MAX_WORD_WRITES, 0)?,
|
||||
max_page_erases: get_info(command_nr::get_info_nr::MAX_PAGE_ERASES, 0)?,
|
||||
max_word_writes: get_info::<S>(command_nr::get_info_nr::MAX_WORD_WRITES, 0)? as usize,
|
||||
max_page_erases: get_info::<S>(command_nr::get_info_nr::MAX_PAGE_ERASES, 0)? as usize,
|
||||
storage_locations: Vec::new(),
|
||||
s: PhantomData,
|
||||
c: PhantomData,
|
||||
};
|
||||
if !syscall.word_size.is_power_of_two()
|
||||
|| !syscall.page_size.is_power_of_two()
|
||||
@@ -161,12 +179,13 @@ impl TockStorage {
|
||||
{
|
||||
return Err(StorageError::CustomError);
|
||||
}
|
||||
for i in 0..memop(memop_nr::STORAGE_CNT, 0)? {
|
||||
if memop(memop_nr::STORAGE_TYPE, i)? != storage_type::STORE {
|
||||
let num_storage_locations = memop::<S>(memop_nr::STORAGE_CNT, 0)?;
|
||||
for i in 0..num_storage_locations {
|
||||
if memop::<S>(memop_nr::STORAGE_TYPE, i)? != storage_type::STORE {
|
||||
continue;
|
||||
}
|
||||
let storage_ptr = memop(memop_nr::STORAGE_PTR, i)?;
|
||||
let storage_len = memop(memop_nr::STORAGE_LEN, i)?;
|
||||
let storage_ptr = memop::<S>(memop_nr::STORAGE_PTR, i)? as usize;
|
||||
let storage_len = memop::<S>(memop_nr::STORAGE_LEN, i)? as usize;
|
||||
if !syscall.is_page_aligned(storage_ptr) || !syscall.is_page_aligned(storage_len) {
|
||||
return Err(StorageError::CustomError);
|
||||
}
|
||||
@@ -187,7 +206,9 @@ impl TockStorage {
|
||||
}
|
||||
}
|
||||
|
||||
impl Storage for TockStorage {
|
||||
impl<S: Syscalls, C: platform::subscribe::Config + platform::allow_ro::Config> Storage
|
||||
for TockStorage<S, C>
|
||||
{
|
||||
fn word_size(&self) -> usize {
|
||||
self.word_size
|
||||
}
|
||||
@@ -218,26 +239,35 @@ impl Storage for TockStorage {
|
||||
return Err(StorageError::NotAligned);
|
||||
}
|
||||
let ptr = self.read_slice(index, value.len())?.as_ptr() as usize;
|
||||
write_slice(ptr, value)
|
||||
write_slice::<S, C>(ptr, value)
|
||||
}
|
||||
|
||||
fn erase_page(&mut self, page: usize) -> StorageResult<()> {
|
||||
let index = StorageIndex { page, byte: 0 };
|
||||
let length = self.page_size();
|
||||
let ptr = self.read_slice(index, length)?.as_ptr() as usize;
|
||||
erase_page(ptr, length)
|
||||
erase_page::<S, C>(ptr, length)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TockUpgradeStorage {
|
||||
pub struct TockUpgradeStorage<
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
> {
|
||||
page_size: usize,
|
||||
partition: Partition,
|
||||
metadata: ModRange,
|
||||
running_metadata: ModRange,
|
||||
identifier: u32,
|
||||
s: PhantomData<S>,
|
||||
c: PhantomData<C>,
|
||||
}
|
||||
|
||||
impl TockUpgradeStorage {
|
||||
impl<S, C> TockUpgradeStorage<S, C>
|
||||
where
|
||||
S: Syscalls,
|
||||
C: platform::allow_ro::Config + platform::subscribe::Config,
|
||||
{
|
||||
// Ideally, the kernel should tell us metadata and partitions directly.
|
||||
// This code only works for one layout, refactor this into the storage driver to support more.
|
||||
const METADATA_ADDRESS: usize = 0x4000;
|
||||
@@ -258,25 +288,27 @@ impl TockUpgradeStorage {
|
||||
/// Returns a `NotAligned` error if partitions or metadata ranges are
|
||||
/// - not exclusive or,
|
||||
/// - not consecutive.
|
||||
pub fn new() -> StorageResult<TockUpgradeStorage> {
|
||||
pub fn new() -> StorageResult<TockUpgradeStorage<S, C>> {
|
||||
let mut locations = TockUpgradeStorage {
|
||||
page_size: get_info(command_nr::get_info_nr::PAGE_SIZE, 0)?,
|
||||
page_size: get_info::<S>(command_nr::get_info_nr::PAGE_SIZE, 0)? as usize,
|
||||
partition: Partition::default(),
|
||||
metadata: ModRange::new_empty(),
|
||||
running_metadata: ModRange::new_empty(),
|
||||
identifier: Self::PARTITION_ADDRESS_A as u32,
|
||||
s: PhantomData,
|
||||
c: PhantomData,
|
||||
};
|
||||
if !locations.page_size.is_power_of_two() {
|
||||
return Err(StorageError::CustomError);
|
||||
}
|
||||
let mut firmware_range = ModRange::new_empty();
|
||||
for i in 0..memop(memop_nr::STORAGE_CNT, 0)? {
|
||||
let storage_type = memop(memop_nr::STORAGE_TYPE, i)?;
|
||||
for i in 0..memop::<S>(memop_nr::STORAGE_CNT, 0)? {
|
||||
let storage_type = memop::<S>(memop_nr::STORAGE_TYPE, i)?;
|
||||
if !matches!(storage_type, storage_type::PARTITION) {
|
||||
continue;
|
||||
};
|
||||
let storage_ptr = memop(memop_nr::STORAGE_PTR, i)?;
|
||||
let storage_len = memop(memop_nr::STORAGE_LEN, i)?;
|
||||
let storage_ptr = memop::<S>(memop_nr::STORAGE_PTR, i)? as usize;
|
||||
let storage_len = memop::<S>(memop_nr::STORAGE_LEN, i)? as usize;
|
||||
if !locations.is_page_aligned(storage_ptr) || !locations.is_page_aligned(storage_len) {
|
||||
return Err(StorageError::CustomError);
|
||||
}
|
||||
@@ -337,7 +369,7 @@ impl TockUpgradeStorage {
|
||||
/// Checks if the metadata's hash matches the partition's content.
|
||||
fn check_partition_hash(&self, metadata: &[u8]) -> StorageResult<()> {
|
||||
let start_address = self.metadata.start() + METADATA_SIGN_OFFSET;
|
||||
let mut hasher = Sha::<TockEnv>::new();
|
||||
let mut hasher = Sha::<TockEnv<S>>::new();
|
||||
for range in self.partition.ranges_from(start_address) {
|
||||
let partition_slice = unsafe { read_slice(range.start(), range.length()) };
|
||||
// The hash implementation handles this in chunks, so no memory issues.
|
||||
@@ -362,15 +394,15 @@ impl TockUpgradeStorage {
|
||||
let write_range = ModRange::new(address, data.len());
|
||||
if self.contains_metadata(&write_range)? {
|
||||
let new_metadata = &data[self.metadata.start() - address..][..self.metadata.length()];
|
||||
check_metadata::<TockEnv>(self, UPGRADE_PUBLIC_KEY, new_metadata)?;
|
||||
check_metadata::<TockEnv<S, C>, S, C>(self, UPGRADE_PUBLIC_KEY, new_metadata)?;
|
||||
}
|
||||
|
||||
// Erases all pages that have their first byte in the write range.
|
||||
// Since we expect calls in order, we don't want to erase half-written pages.
|
||||
for address in write_range.aligned_iter(self.page_size) {
|
||||
erase_page(address, self.page_size)?;
|
||||
erase_page::<S, C>(address, self.page_size)?;
|
||||
}
|
||||
write_slice(address, &data)?;
|
||||
write_slice::<S, C>(address, &data)?;
|
||||
let written_slice = unsafe { read_slice(address, data.len()) };
|
||||
if written_slice != data {
|
||||
return Err(StorageError::CustomError);
|
||||
@@ -378,7 +410,7 @@ impl TockUpgradeStorage {
|
||||
// Case: Last slice is written.
|
||||
if data.len() == self.partition.length() - offset {
|
||||
let metadata = unsafe { read_slice(self.metadata.start(), self.metadata.length()) };
|
||||
self.check_partition_hash(&metadata)?;
|
||||
self.check_partition_hash(metadata)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
40
src/env/tock/upgrade_helper.rs
vendored
40
src/env/tock/upgrade_helper.rs
vendored
@@ -21,6 +21,8 @@ use crate::env::tock::buffer_upgrade_storage::BufferUpgradeStorage;
|
||||
use crate::env::tock::storage::TockUpgradeStorage;
|
||||
use arrayref::array_ref;
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::Syscalls;
|
||||
use opensk::api::crypto::ecdsa::{PublicKey as _, Signature as _};
|
||||
use opensk::env::{EcdsaPk, EcdsaSignature, Env};
|
||||
use persistent_store::{StorageError, StorageResult};
|
||||
@@ -39,9 +41,13 @@ pub const METADATA_SIGN_OFFSET: usize = 0x800;
|
||||
///
|
||||
/// Checks signature correctness against the hash, and whether the partition offset matches.
|
||||
/// Whether the hash matches the partition content is not tested here!
|
||||
pub fn check_metadata<E: Env>(
|
||||
#[cfg(not(feature = "std"))] upgrade_locations: &TockUpgradeStorage,
|
||||
#[cfg(feature = "std")] upgrade_locations: &BufferUpgradeStorage,
|
||||
pub fn check_metadata<
|
||||
E: Env,
|
||||
S: Syscalls,
|
||||
C: platform::subscribe::Config + platform::allow_ro::Config,
|
||||
>(
|
||||
#[cfg(not(feature = "std"))] upgrade_locations: &TockUpgradeStorage<S, C>,
|
||||
#[cfg(feature = "std")] upgrade_locations: &BufferUpgradeStorage<S, C>,
|
||||
public_key_bytes: &[u8],
|
||||
metadata: &[u8],
|
||||
) -> StorageResult<()> {
|
||||
@@ -104,11 +110,13 @@ fn verify_signature<E: Env>(
|
||||
mod test {
|
||||
use super::*;
|
||||
use arrayref::mut_array_refs;
|
||||
use libtock_unittest::fake::Syscalls;
|
||||
use opensk::api::crypto::ecdsa::SecretKey as _;
|
||||
use opensk::api::crypto::sha256::Sha256;
|
||||
use opensk::api::crypto::{EC_FIELD_SIZE, EC_SIGNATURE_SIZE};
|
||||
use opensk::env::test::TestEnv;
|
||||
use opensk::env::{EcdsaSk, Sha};
|
||||
use platform::DefaultConfig;
|
||||
|
||||
fn to_uncompressed(public_key: &EcdsaPk<TestEnv>) -> [u8; 1 + 2 * EC_FIELD_SIZE] {
|
||||
// Formatting according to:
|
||||
@@ -147,20 +155,28 @@ mod test {
|
||||
let public_key_bytes = to_uncompressed(&public_key);
|
||||
|
||||
assert_eq!(
|
||||
check_metadata::<TestEnv>(&upgrade_locations, &public_key_bytes, &metadata),
|
||||
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
|
||||
&upgrade_locations,
|
||||
&public_key_bytes,
|
||||
&metadata
|
||||
),
|
||||
Ok(())
|
||||
);
|
||||
|
||||
// Manipulating the partition address fails.
|
||||
metadata[METADATA_SIGN_OFFSET + 8] = 0x88;
|
||||
assert_eq!(
|
||||
check_metadata::<TestEnv>(&upgrade_locations, &public_key_bytes, &metadata),
|
||||
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
|
||||
&upgrade_locations,
|
||||
&public_key_bytes,
|
||||
&metadata
|
||||
),
|
||||
Err(StorageError::CustomError)
|
||||
);
|
||||
metadata[METADATA_SIGN_OFFSET + 8] = 0x00;
|
||||
// Wrong metadata length fails.
|
||||
assert_eq!(
|
||||
check_metadata::<TestEnv>(
|
||||
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
|
||||
&upgrade_locations,
|
||||
&public_key_bytes,
|
||||
&metadata[..METADATA_LEN - 1]
|
||||
@@ -170,14 +186,22 @@ mod test {
|
||||
// Manipulating the hash fails.
|
||||
metadata[0] ^= 0x01;
|
||||
assert_eq!(
|
||||
check_metadata::<TestEnv>(&upgrade_locations, &public_key_bytes, &metadata),
|
||||
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
|
||||
&upgrade_locations,
|
||||
&public_key_bytes,
|
||||
&metadata
|
||||
),
|
||||
Err(StorageError::CustomError)
|
||||
);
|
||||
metadata[0] ^= 0x01;
|
||||
// Manipulating the signature fails.
|
||||
metadata[32] ^= 0x01;
|
||||
assert_eq!(
|
||||
check_metadata::<TestEnv>(&upgrade_locations, &public_key_bytes, &metadata),
|
||||
check_metadata::<TestEnv, Syscalls, DefaultConfig>(
|
||||
&upgrade_locations,
|
||||
&public_key_bytes,
|
||||
&metadata
|
||||
),
|
||||
Err(StorageError::CustomError)
|
||||
);
|
||||
}
|
||||
|
||||
168
src/main.rs
168
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<isize> = Duration::from_ms(1000);
|
||||
const KEEPALIVE_DELAY_MS_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
|
||||
|
||||
#[cfg(not(feature = "vendor_hid"))]
|
||||
@@ -56,19 +64,18 @@ const NUM_ENDPOINTS: usize = 2;
|
||||
// The reply/replies that are queued for each endpoint.
|
||||
struct EndpointReply {
|
||||
endpoint: UsbEndpoint,
|
||||
transport: Transport,
|
||||
reply: HidPacketIterator,
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
type SyscallImplementation = fake::Syscalls;
|
||||
#[cfg(not(feature = "std"))]
|
||||
type SyscallImplementation = TockSyscalls;
|
||||
|
||||
impl EndpointReply {
|
||||
pub fn new(endpoint: UsbEndpoint) -> Self {
|
||||
EndpointReply {
|
||||
endpoint,
|
||||
transport: match endpoint {
|
||||
UsbEndpoint::MainHid => Transport::MainHid,
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
UsbEndpoint::VendorHid => Transport::VendorHid,
|
||||
},
|
||||
reply: HidPacketIterator::none(),
|
||||
}
|
||||
}
|
||||
@@ -76,7 +83,7 @@ impl EndpointReply {
|
||||
|
||||
// A single packet to send.
|
||||
struct SendPacket {
|
||||
transport: Transport,
|
||||
endpoint: UsbEndpoint,
|
||||
packet: [u8; 64],
|
||||
}
|
||||
|
||||
@@ -99,97 +106,103 @@ impl EndpointReplies {
|
||||
for ep in self.replies.iter_mut() {
|
||||
if let Some(packet) = ep.reply.next() {
|
||||
return Some(SendPacket {
|
||||
transport: ep.transport,
|
||||
endpoint: ep.endpoint,
|
||||
packet,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, endpoint: UsbEndpoint) {
|
||||
for ep in self.replies.iter_mut() {
|
||||
if ep.endpoint == endpoint {
|
||||
*ep = EndpointReply::new(endpoint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
let mut writer = Console::<SyscallImplementation>::writer();
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
{
|
||||
writeln!(writer, "Hello world from OpenSK!").ok().unwrap();
|
||||
}
|
||||
// Setup USB driver.
|
||||
if !usb_ctap_hid::setup() {
|
||||
if !usb_ctap_hid::UsbCtapHid::<SyscallImplementation>::setup() {
|
||||
panic!("Cannot setup USB driver");
|
||||
}
|
||||
|
||||
let env = TockEnv::default();
|
||||
let env = TockEnv::<SyscallImplementation>::default();
|
||||
let mut ctap = opensk::Ctap::new(env);
|
||||
|
||||
let mut led_counter = 0;
|
||||
let mut led_blink_timer = <<TockEnv as Env>::Clock as Clock>::Timer::default();
|
||||
let mut led_blink_timer =
|
||||
<<TockEnv<SyscallImplementation> as Env>::Clock as Clock>::Timer::default();
|
||||
|
||||
let mut replies = EndpointReplies::new();
|
||||
|
||||
// Main loop. If CTAP1 is used, we register button presses for U2F while receiving and waiting.
|
||||
// The way TockOS and apps currently interact, callbacks need a yield syscall to execute,
|
||||
// making consistent blinking patterns and sending keepalives harder.
|
||||
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(writer, "Entering main ctap loop").unwrap();
|
||||
loop {
|
||||
// Create the button callback, used for CTAP1.
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
let button_touched = Cell::new(false);
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
let mut buttons_callback = buttons::with_callback(|_button_num, state| {
|
||||
match state {
|
||||
ButtonState::Pressed => button_touched.set(true),
|
||||
ButtonState::Released => (),
|
||||
};
|
||||
});
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
let mut buttons = buttons_callback.init().flex_unwrap();
|
||||
// At the moment, all buttons are accepted. You can customize your setup here.
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
for mut button in &mut buttons {
|
||||
button.enable().flex_unwrap();
|
||||
}
|
||||
let num_buttons = Buttons::<SyscallImplementation>::count().ok().unwrap();
|
||||
|
||||
// Variable for use in both the send_and_maybe_recv and recv cases.
|
||||
let mut usb_endpoint: Option<UsbEndpoint> = None;
|
||||
let mut pkt_request = [0; 64];
|
||||
|
||||
if let Some(mut packet) = replies.next_packet() {
|
||||
// send and receive.
|
||||
let hid_connection = packet.transport.hid_connection(ctap.env());
|
||||
match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT_MS) {
|
||||
Ok(SendOrRecvStatus::Timeout) => {
|
||||
if let Some(packet) = replies.next_packet() {
|
||||
match usb_ctap_hid::UsbCtapHid::<SyscallImplementation>::send(
|
||||
&packet.packet,
|
||||
SEND_TIMEOUT_MS,
|
||||
packet.endpoint as u32,
|
||||
)
|
||||
.flex_unwrap()
|
||||
{
|
||||
usb_ctap_hid::SendOrRecvStatus::Sent => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
print_packet_notice(
|
||||
"Sending packet timed out",
|
||||
print_packet_notice::<SyscallImplementation>(
|
||||
"Sent packet",
|
||||
ctap.env().clock().timestamp_us(),
|
||||
&mut writer,
|
||||
);
|
||||
// TODO: reset the ctap_hid state.
|
||||
// Since sending the packet timed out, we cancel this reply.
|
||||
break;
|
||||
}
|
||||
Ok(SendOrRecvStatus::Sent) => {
|
||||
usb_ctap_hid::SendOrRecvStatus::Timeout => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
print_packet_notice("Sent packet", ctap.env().clock().timestamp_us());
|
||||
}
|
||||
Ok(SendOrRecvStatus::Received(ep)) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
print_packet_notice(
|
||||
"Received another packet",
|
||||
print_packet_notice::<SyscallImplementation>(
|
||||
"Timeout while sending packet",
|
||||
ctap.env().clock().timestamp_us(),
|
||||
&mut writer,
|
||||
);
|
||||
usb_endpoint = Some(ep);
|
||||
|
||||
// Copy to incoming packet to local buffer to be consistent
|
||||
// with the receive flow.
|
||||
pkt_request = packet.packet;
|
||||
// The client is unresponsive, so we discard all pending packets.
|
||||
replies.clear(packet.endpoint);
|
||||
}
|
||||
Err(_) => panic!("Error sending packet"),
|
||||
}
|
||||
_ => panic!("Unexpected status on USB transmission"),
|
||||
};
|
||||
} else {
|
||||
// receive
|
||||
usb_endpoint =
|
||||
match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_MS_TOCK)
|
||||
.flex_unwrap()
|
||||
match usb_ctap_hid::UsbCtapHid::<SyscallImplementation>::recv_with_timeout(
|
||||
&mut pkt_request,
|
||||
KEEPALIVE_DELAY_MS_TOCK,
|
||||
)
|
||||
.flex_unwrap()
|
||||
{
|
||||
usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
print_packet_notice("Received packet", ctap.env().clock().timestamp_us());
|
||||
UsbEndpoint::try_from(endpoint).ok()
|
||||
print_packet_notice::<SyscallImplementation>(
|
||||
"Received packet",
|
||||
ctap.env().clock().timestamp_us(),
|
||||
&mut writer,
|
||||
);
|
||||
UsbEndpoint::try_from(endpoint as usize).ok()
|
||||
}
|
||||
usb_ctap_hid::SendOrRecvStatus::Sent => {
|
||||
panic!("Returned transmit status on receive")
|
||||
@@ -200,17 +213,10 @@ fn main() {
|
||||
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
{
|
||||
if button_touched.get() {
|
||||
let button_touched = (0..num_buttons).any(Buttons::<SyscallImplementation>::is_pressed);
|
||||
if button_touched {
|
||||
ctap.u2f_grant_user_presence();
|
||||
}
|
||||
// Cleanup button callbacks. We miss button presses while processing though.
|
||||
// Heavy computation mostly follows a registered touch luckily. Unregistering
|
||||
// callbacks is important to not clash with those from check_user_presence.
|
||||
for mut button in &mut buttons {
|
||||
button.disable().flex_unwrap();
|
||||
}
|
||||
drop(buttons);
|
||||
drop(buttons_callback);
|
||||
}
|
||||
|
||||
// This call is making sure that even for long inactivity, wrapping clock values
|
||||
@@ -231,7 +237,7 @@ fn main() {
|
||||
if ep.reply.has_data() {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
Console::<SyscallImplementation>::writer(),
|
||||
"Warning overwriting existing reply for endpoint {}",
|
||||
endpoint as usize
|
||||
)
|
||||
@@ -252,26 +258,30 @@ fn main() {
|
||||
}
|
||||
|
||||
if ctap.should_wink() {
|
||||
wink_leds(led_counter);
|
||||
wink_leds::<SyscallImplementation>(led_counter);
|
||||
} else {
|
||||
#[cfg(not(feature = "with_ctap1"))]
|
||||
switch_off_leds();
|
||||
switch_off_leds::<SyscallImplementation>();
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
if ctap.u2f_needs_user_presence() {
|
||||
// Flash the LEDs with an almost regular pattern. The inaccuracy comes from
|
||||
// delay caused by processing and sending of packets.
|
||||
blink_leds(led_counter);
|
||||
blink_leds::<SyscallImplementation>(led_counter);
|
||||
} else {
|
||||
switch_off_leds();
|
||||
switch_off_leds::<SyscallImplementation>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
fn print_packet_notice(notice_text: &str, now_us: usize) {
|
||||
fn print_packet_notice<S: libtock_platform::Syscalls>(
|
||||
notice_text: &str,
|
||||
now_us: usize,
|
||||
writer: &mut ConsoleWriter<S>,
|
||||
) {
|
||||
writeln!(
|
||||
Console::new(),
|
||||
writer,
|
||||
"{} at {}.{:06} s",
|
||||
notice_text,
|
||||
now_us / 1_000_000,
|
||||
|
||||
82
third_party/lang-items/Cargo.lock
generated
vendored
82
third_party/lang-items/Cargo.lock
generated
vendored
@@ -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"
|
||||
|
||||
16
third_party/lang-items/Cargo.toml
vendored
16
third_party/lang-items/Cargo.toml
vendored
@@ -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 = []
|
||||
|
||||
32
third_party/lang-items/src/allocator.rs
vendored
32
third_party/lang-items/src/allocator.rs
vendored
@@ -9,14 +9,17 @@ use core::sync::atomic;
|
||||
#[cfg(feature = "debug_allocations")]
|
||||
use core::sync::atomic::AtomicUsize;
|
||||
#[cfg(any(feature = "debug_allocations", feature = "panic_console"))]
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::Console;
|
||||
#[cfg(feature = "panic_console")]
|
||||
use libtock_platform::{ErrorCode, Syscalls};
|
||||
use libtock_runtime::TockSyscalls;
|
||||
use linked_list_allocator::Heap;
|
||||
|
||||
static mut HEAP: Heap = Heap::empty();
|
||||
|
||||
#[no_mangle]
|
||||
unsafe fn libtock_alloc_init(app_heap_start: usize, app_heap_size: usize) {
|
||||
HEAP.init(app_heap_start as *mut u8, app_heap_size);
|
||||
unsafe fn libtock_alloc_init(app_heap_bottom: *mut u8, app_heap_size: usize) {
|
||||
HEAP.init(app_heap_bottom, app_heap_size);
|
||||
}
|
||||
|
||||
// With the "debug_allocations" feature, we use `AtomicUsize` to store the
|
||||
@@ -54,7 +57,7 @@ unsafe impl GlobalAlloc for TockAllocator {
|
||||
self.count.fetch_add(1, atomic::Ordering::SeqCst);
|
||||
self.size.fetch_add(layout.size(), atomic::Ordering::SeqCst);
|
||||
writeln!(
|
||||
Console::new(),
|
||||
Console::<TockSyscalls>::writer(),
|
||||
"alloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
|
||||
layout.size(),
|
||||
layout.align(),
|
||||
@@ -73,7 +76,7 @@ unsafe impl GlobalAlloc for TockAllocator {
|
||||
self.count.fetch_sub(1, atomic::Ordering::SeqCst);
|
||||
self.size.fetch_sub(layout.size(), atomic::Ordering::SeqCst);
|
||||
writeln!(
|
||||
Console::new(),
|
||||
Console::<TockSyscalls>::writer(),
|
||||
"dealloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
|
||||
layout.size(),
|
||||
layout.align(),
|
||||
@@ -93,17 +96,20 @@ static ALLOCATOR: TockAllocator = TockAllocator::new();
|
||||
|
||||
#[alloc_error_handler]
|
||||
unsafe fn alloc_error_handler(_layout: Layout) -> ! {
|
||||
util::signal_oom();
|
||||
util::signal_panic();
|
||||
util::Util::<TockSyscalls>::signal_oom();
|
||||
util::Util::<TockSyscalls>::signal_panic();
|
||||
|
||||
#[cfg(feature = "panic_console")]
|
||||
{
|
||||
writeln!(Console::new(), "Couldn't allocate: {:?}", _layout).ok();
|
||||
// Force the kernel to report the panic cause, by reading an invalid address.
|
||||
// The memory protection unit should be setup by the Tock kernel to prevent apps from accessing
|
||||
// address zero.
|
||||
core::ptr::read_volatile(0 as *const usize);
|
||||
writeln!(
|
||||
Console::<TockSyscalls>::writer(),
|
||||
"Couldn't allocate: {:?}",
|
||||
_layout
|
||||
)
|
||||
.ok();
|
||||
TockSyscalls::exit_terminate(ErrorCode::Fail as u32);
|
||||
}
|
||||
|
||||
util::cycle_leds()
|
||||
#[cfg(not(feature = "panic_console"))]
|
||||
util::Util::<TockSyscalls>::cycle_leds()
|
||||
}
|
||||
|
||||
2
third_party/lang-items/src/lib.rs
vendored
2
third_party/lang-items/src/lib.rs
vendored
@@ -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!()
|
||||
}
|
||||
|
||||
29
third_party/lang-items/src/panic_handler.rs
vendored
29
third_party/lang-items/src/panic_handler.rs
vendored
@@ -1,26 +1,25 @@
|
||||
//! Custom panic handler for OpenSK
|
||||
|
||||
use crate::util;
|
||||
#[cfg(feature = "panic_console")]
|
||||
use core::fmt::Write;
|
||||
use core::panic::PanicInfo;
|
||||
#[cfg(feature = "panic_console")]
|
||||
use libtock_drivers::console::Console;
|
||||
use libtock_console::Console;
|
||||
#[allow(unused_imports)]
|
||||
use libtock_platform::{ErrorCode, Syscalls};
|
||||
use libtock_runtime::TockSyscalls;
|
||||
|
||||
#[panic_handler]
|
||||
fn panic_handler(_info: &PanicInfo) -> ! {
|
||||
util::signal_panic();
|
||||
fn panic_handler(_info: &core::panic::PanicInfo) -> ! {
|
||||
util::Util::<TockSyscalls>::signal_panic();
|
||||
|
||||
#[cfg(feature = "panic_console")]
|
||||
{
|
||||
let mut console = Console::new();
|
||||
writeln!(console, "{}", _info).ok();
|
||||
console.flush();
|
||||
// Force the kernel to report the panic cause, by reading an invalid address.
|
||||
// The memory protection unit should be setup by the Tock kernel to prevent apps from accessing
|
||||
// address zero.
|
||||
unsafe {
|
||||
core::ptr::read_volatile(0 as *const usize);
|
||||
}
|
||||
let mut writer = Console::<TockSyscalls>::writer();
|
||||
writeln!(writer, "{}", _info).ok();
|
||||
// Exit with a non-zero exit code to indicate failure.
|
||||
TockSyscalls::exit_terminate(ErrorCode::Fail as u32);
|
||||
}
|
||||
|
||||
util::flash_all_leds();
|
||||
#[cfg(not(feature = "panic_console"))]
|
||||
util::Util::<TockSyscalls>::flash_all_leds();
|
||||
}
|
||||
|
||||
86
third_party/lang-items/src/util.rs
vendored
86
third_party/lang-items/src/util.rs
vendored
@@ -1,45 +1,55 @@
|
||||
use libtock_drivers::led;
|
||||
use libtock_drivers::timer::{self, Duration};
|
||||
use libtock_drivers::timer;
|
||||
use libtock_leds::Leds;
|
||||
use libtock_low_level_debug::{AlertCode, LowLevelDebug};
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::Syscalls;
|
||||
use platform::DefaultConfig;
|
||||
|
||||
// Signal a panic using the LowLevelDebug capsule (if available).
|
||||
pub fn signal_panic() {
|
||||
let _ = libtock_core::syscalls::command1_insecure(8, 1, 1);
|
||||
}
|
||||
pub struct Util<S: Syscalls, C: platform::subscribe::Config = DefaultConfig>(S, C);
|
||||
|
||||
// Signal an out-of-memory error using the LowLevelDebug capsule (if available).
|
||||
pub fn signal_oom() {
|
||||
let _ = libtock_core::syscalls::command1_insecure(8, 2, 1);
|
||||
}
|
||||
|
||||
pub fn flash_all_leds() -> ! {
|
||||
// Flash all LEDs (if available). All errors from syscalls are ignored: we are already inside a
|
||||
// panic handler so there is nothing much to do if simple drivers (timer, LEDs) don't work.
|
||||
loop {
|
||||
if let Ok(leds) = led::all() {
|
||||
for led in leds {
|
||||
let _ = led.on();
|
||||
}
|
||||
}
|
||||
let _ = timer::sleep(Duration::from_ms(100));
|
||||
if let Ok(leds) = led::all() {
|
||||
for led in leds {
|
||||
let _ = led.off();
|
||||
}
|
||||
}
|
||||
let _ = timer::sleep(Duration::from_ms(100));
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Util<S, C> {
|
||||
/// Signal a panic using the LowLevelDebug capsule (if available).
|
||||
pub fn signal_panic() {
|
||||
LowLevelDebug::<S>::print_alert_code(AlertCode::Panic);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cycle_leds() -> ! {
|
||||
// Cycle though all LEDs (if available). All errors from syscalls are ignored: we are already
|
||||
// inside an error handler so there is nothing much to do if simple drivers (timer, LEDs) don't
|
||||
// work.
|
||||
loop {
|
||||
if let Ok(leds) = led::all() {
|
||||
for led in leds {
|
||||
let _ = led.on();
|
||||
let _ = timer::sleep(Duration::from_ms(100));
|
||||
let _ = led.off();
|
||||
/// Signal an out-of-memory error using the LowLevelDebug capsule (if available).
|
||||
pub fn signal_oom() {
|
||||
LowLevelDebug::<S>::print_alert_code(AlertCode::WrongLocation);
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn flash_all_leds() -> ! {
|
||||
// Flash all LEDs (if available). All errors from syscalls are ignored: we are already inside a
|
||||
// panic handler so there is nothing much to do if simple drivers (timer, LEDs) don't work.
|
||||
loop {
|
||||
if let Ok(led_count) = Leds::<S>::count() {
|
||||
for led in 0..led_count {
|
||||
let _ = Leds::<S>::on(led);
|
||||
}
|
||||
}
|
||||
let _ = timer::Alarm::<S, C>::sleep_for(timer::Milliseconds(100));
|
||||
if let Ok(led_count) = Leds::<S>::count() {
|
||||
for led in 0..led_count {
|
||||
let _ = Leds::<S>::off(led);
|
||||
}
|
||||
}
|
||||
let _ = timer::Alarm::<S, C>::sleep_for(timer::Milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn cycle_leds() -> ! {
|
||||
// Cycle though all LEDs (if available). All errors from syscalls are ignored: we are already
|
||||
// inside an error handler so there is nothing much to do if simple drivers (timer, LEDs) don't
|
||||
// work.
|
||||
loop {
|
||||
if let Ok(leds) = Leds::<S>::count() {
|
||||
for led in 0..leds {
|
||||
let _ = Leds::<S>::on(led);
|
||||
let _ = timer::Alarm::<S, C>::sleep_for(timer::Milliseconds(100));
|
||||
let _ = Leds::<S>::off(led);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
51
third_party/libtock-drivers/Cargo.lock
generated
vendored
51
third_party/libtock-drivers/Cargo.lock
generated
vendored
@@ -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"
|
||||
|
||||
5
third_party/libtock-drivers/Cargo.toml
vendored
5
third_party/libtock-drivers/Cargo.toml
vendored
@@ -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 = []
|
||||
|
||||
170
third_party/libtock-drivers/src/buttons.rs
vendored
170
third_party/libtock-drivers/src/buttons.rs
vendored
@@ -1,170 +0,0 @@
|
||||
use crate::result::{OtherError, TockResult};
|
||||
use core::marker::PhantomData;
|
||||
use libtock_core::callback::{CallbackSubscription, Consumer};
|
||||
use libtock_core::syscalls;
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x00003;
|
||||
|
||||
mod command_nr {
|
||||
pub const COUNT: usize = 0;
|
||||
pub const ENABLE_INTERRUPT: usize = 1;
|
||||
pub const DISABLE_INTERRUPT: usize = 2;
|
||||
pub const READ: usize = 3;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const SUBSCRIBE_CALLBACK: usize = 0;
|
||||
}
|
||||
|
||||
pub fn with_callback<CB>(callback: CB) -> WithCallback<CB> {
|
||||
WithCallback { callback }
|
||||
}
|
||||
|
||||
pub struct WithCallback<CB> {
|
||||
callback: CB,
|
||||
}
|
||||
|
||||
struct ButtonConsumer;
|
||||
|
||||
impl<CB: FnMut(usize, ButtonState)> Consumer<WithCallback<CB>> for ButtonConsumer {
|
||||
fn consume(data: &mut WithCallback<CB>, button_num: usize, state: usize, _: usize) {
|
||||
(data.callback)(button_num, state.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl<CB: FnMut(usize, ButtonState)> WithCallback<CB> {
|
||||
pub fn init(&mut self) -> TockResult<Buttons> {
|
||||
let count = syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?;
|
||||
|
||||
let subscription = syscalls::subscribe::<ButtonConsumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::SUBSCRIBE_CALLBACK,
|
||||
self,
|
||||
)?;
|
||||
|
||||
Ok(Buttons {
|
||||
count: count as usize,
|
||||
subscription,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Buttons<'a> {
|
||||
count: usize,
|
||||
#[allow(dead_code)] // Used in drop
|
||||
subscription: CallbackSubscription<'a>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ButtonsError {
|
||||
NotSupported,
|
||||
SubscriptionFailed,
|
||||
}
|
||||
|
||||
impl<'a> Buttons<'a> {
|
||||
pub fn iter_mut(&mut self) -> ButtonIter {
|
||||
ButtonIter {
|
||||
curr_button: 0,
|
||||
button_count: self.count,
|
||||
lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ButtonState {
|
||||
Pressed,
|
||||
Released,
|
||||
}
|
||||
|
||||
impl From<usize> for ButtonState {
|
||||
fn from(state: usize) -> ButtonState {
|
||||
match state {
|
||||
0 => ButtonState::Released,
|
||||
1 => ButtonState::Pressed,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b> IntoIterator for &'b mut Buttons<'a> {
|
||||
type Item = ButtonHandle<'b>;
|
||||
type IntoIter = ButtonIter<'b>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ButtonIter<'a> {
|
||||
curr_button: usize,
|
||||
button_count: usize,
|
||||
lifetime: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ButtonIter<'a> {
|
||||
type Item = ButtonHandle<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.curr_button < self.button_count {
|
||||
let item = ButtonHandle {
|
||||
button_num: self.curr_button,
|
||||
lifetime: PhantomData,
|
||||
};
|
||||
self.curr_button += 1;
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ButtonHandle<'a> {
|
||||
button_num: usize,
|
||||
lifetime: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
impl<'a> ButtonHandle<'a> {
|
||||
pub fn enable(&mut self) -> TockResult<Button> {
|
||||
syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::ENABLE_INTERRUPT,
|
||||
self.button_num,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Ok(Button { handle: self })
|
||||
}
|
||||
|
||||
pub fn disable(&mut self) -> TockResult<()> {
|
||||
syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::DISABLE_INTERRUPT,
|
||||
self.button_num,
|
||||
0,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Button<'a> {
|
||||
handle: &'a ButtonHandle<'a>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum ButtonError {
|
||||
ActivationFailed,
|
||||
}
|
||||
|
||||
impl<'a> Button<'a> {
|
||||
pub fn read(&self) -> TockResult<ButtonState> {
|
||||
let button_state =
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::READ, self.handle.button_num, 0)?;
|
||||
match button_state {
|
||||
0 => Ok(ButtonState::Released),
|
||||
1 => Ok(ButtonState::Pressed),
|
||||
_ => Err(OtherError::ButtonsDriverInvalidState.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
115
third_party/libtock-drivers/src/console.rs
vendored
115
third_party/libtock-drivers/src/console.rs
vendored
@@ -1,115 +0,0 @@
|
||||
use crate::util;
|
||||
use core::cell::Cell;
|
||||
use core::fmt;
|
||||
use libtock_core::{callback, syscalls};
|
||||
|
||||
const DRIVER_NUMBER: usize = 1;
|
||||
|
||||
mod command_nr {
|
||||
pub const WRITE: usize = 1;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const SET_ALARM: usize = 1;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const SHARE_BUFFER: usize = 1;
|
||||
}
|
||||
|
||||
pub const BUFFER_SIZE: usize = 1024;
|
||||
|
||||
pub struct Console {
|
||||
allow_buffer: [u8; BUFFER_SIZE],
|
||||
count_pending: usize,
|
||||
}
|
||||
|
||||
impl Console {
|
||||
pub fn new() -> Console {
|
||||
Console {
|
||||
allow_buffer: [0; BUFFER_SIZE],
|
||||
count_pending: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.count_pending == 0
|
||||
}
|
||||
|
||||
fn is_full(&self) -> bool {
|
||||
self.allow_buffer.len() == self.count_pending
|
||||
}
|
||||
|
||||
fn available_len(&self) -> usize {
|
||||
self.allow_buffer.len() - self.count_pending
|
||||
}
|
||||
|
||||
pub fn write<S: AsRef<[u8]>>(&mut self, text: S) {
|
||||
let mut not_written_yet = text.as_ref();
|
||||
while !not_written_yet.is_empty() {
|
||||
let num_bytes_to_print = self.available_len().min(not_written_yet.len());
|
||||
self.allow_buffer[self.count_pending..(self.count_pending + num_bytes_to_print)]
|
||||
.copy_from_slice(¬_written_yet[..num_bytes_to_print]);
|
||||
self.count_pending += num_bytes_to_print;
|
||||
|
||||
if self.is_full() {
|
||||
self.flush();
|
||||
}
|
||||
|
||||
not_written_yet = ¬_written_yet[num_bytes_to_print..];
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flush(&mut self) {
|
||||
if self.is_empty() {
|
||||
// Don't trigger any syscall if the buffer is empty.
|
||||
return;
|
||||
}
|
||||
|
||||
let count = self.count_pending;
|
||||
// Clear the buffer even in case of error, to avoid an infinite loop.
|
||||
self.count_pending = 0;
|
||||
|
||||
Console::write_unbuffered(&mut self.allow_buffer[..count]);
|
||||
}
|
||||
|
||||
pub fn write_unbuffered(buf: &mut [u8]) {
|
||||
let count = buf.len();
|
||||
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf);
|
||||
if result.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
let is_written = Cell::new(false);
|
||||
let mut is_written_alarm = || is_written.set(true);
|
||||
let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::SET_ALARM,
|
||||
&mut is_written_alarm,
|
||||
);
|
||||
if subscription.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::WRITE, count, 0);
|
||||
if result_code.is_err() {
|
||||
return;
|
||||
}
|
||||
|
||||
util::yieldk_for(|| is_written.get());
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Console {
|
||||
fn drop(&mut self) {
|
||||
self.flush();
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for Console {
|
||||
fn write_str(&mut self, string: &str) -> Result<(), fmt::Error> {
|
||||
self.write(string);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
49
third_party/libtock-drivers/src/crp.rs
vendored
49
third_party/libtock-drivers/src/crp.rs
vendored
@@ -1,15 +1,17 @@
|
||||
use crate::result::TockResult;
|
||||
use libtock_core::syscalls;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{DefaultConfig, Syscalls};
|
||||
use platform::ErrorCode;
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x00008;
|
||||
const DRIVER_NUMBER: u32 = 0x00008;
|
||||
|
||||
mod command_nr {
|
||||
pub const AVAILABLE: usize = 0;
|
||||
pub const GET_PROTECTION: usize = 1;
|
||||
pub const SET_PROTECTION: usize = 2;
|
||||
pub const AVAILABLE: u32 = 0;
|
||||
pub const GET_PROTECTION: u32 = 1;
|
||||
pub const SET_PROTECTION: u32 = 2;
|
||||
}
|
||||
|
||||
#[derive(PartialOrd, PartialEq)]
|
||||
#[derive(PartialOrd, PartialEq, Eq)]
|
||||
pub enum ProtectionLevel {
|
||||
/// Unsupported feature
|
||||
Unknown = 0,
|
||||
@@ -25,8 +27,10 @@ pub enum ProtectionLevel {
|
||||
FullyLocked = 0xff,
|
||||
}
|
||||
|
||||
impl From<usize> for ProtectionLevel {
|
||||
fn from(value: usize) -> Self {
|
||||
pub struct Crp<S: Syscalls, C: platform::subscribe::Config = DefaultConfig>(S, C);
|
||||
|
||||
impl From<u32> for ProtectionLevel {
|
||||
fn from(value: u32) -> Self {
|
||||
match value {
|
||||
1 => ProtectionLevel::NoProtection,
|
||||
2 => ProtectionLevel::JtagDisabled,
|
||||
@@ -36,17 +40,24 @@ impl From<usize> for ProtectionLevel {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_available() -> TockResult<()> {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::AVAILABLE, 0, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Crp<S, C> {
|
||||
pub fn is_available() -> TockResult<()> {
|
||||
S::command(DRIVER_NUMBER, command_nr::AVAILABLE, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
|
||||
pub fn get_protection() -> TockResult<ProtectionLevel> {
|
||||
let current_level = syscalls::command(DRIVER_NUMBER, command_nr::GET_PROTECTION, 0, 0)?;
|
||||
Ok(current_level.into())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_protection(level: ProtectionLevel) -> TockResult<()> {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::SET_PROTECTION, level as usize, 0)?;
|
||||
Ok(())
|
||||
pub fn get_protection() -> TockResult<ProtectionLevel> {
|
||||
let protection_level = S::command(DRIVER_NUMBER, command_nr::GET_PROTECTION, 0, 0)
|
||||
.to_result::<u32, ErrorCode>()?;
|
||||
|
||||
Ok(protection_level.into())
|
||||
}
|
||||
|
||||
pub fn set_protection(level: ProtectionLevel) -> TockResult<()> {
|
||||
S::command(DRIVER_NUMBER, command_nr::SET_PROTECTION, level as u32, 0)
|
||||
.to_result::<(), ErrorCode>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
84
third_party/libtock-drivers/src/led.rs
vendored
84
third_party/libtock-drivers/src/led.rs
vendored
@@ -1,84 +0,0 @@
|
||||
use crate::result::{OtherError, TockError, TockResult};
|
||||
use libtock_core::syscalls;
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x00002;
|
||||
|
||||
mod command_nr {
|
||||
pub const COUNT: usize = 0;
|
||||
pub const ON: usize = 1;
|
||||
pub const OFF: usize = 2;
|
||||
pub const TOGGLE: usize = 3;
|
||||
}
|
||||
|
||||
pub struct Led {
|
||||
led_num: usize,
|
||||
}
|
||||
|
||||
pub fn count() -> TockResult<usize> {
|
||||
let count = syscalls::command(DRIVER_NUMBER, command_nr::COUNT, 0, 0)?;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
pub fn get(led_num: usize) -> TockResult<Led> {
|
||||
let led_count = count()?;
|
||||
if led_num < led_count {
|
||||
Ok(Led { led_num })
|
||||
} else {
|
||||
Err(TockError::Other(OtherError::OutOfRange))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all() -> TockResult<LedIter> {
|
||||
let led_count = count()?;
|
||||
Ok(LedIter {
|
||||
curr_led: 0,
|
||||
led_count,
|
||||
})
|
||||
}
|
||||
|
||||
impl Led {
|
||||
pub fn set_state(&self, state: bool) -> TockResult<()> {
|
||||
if state {
|
||||
self.on()
|
||||
} else {
|
||||
self.off()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on(&self) -> TockResult<()> {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::ON, self.led_num, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn off(&self) -> TockResult<()> {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::OFF, self.led_num, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn toggle(&self) -> TockResult<()> {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::TOGGLE, self.led_num, 0)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct LedIter {
|
||||
curr_led: usize,
|
||||
led_count: usize,
|
||||
}
|
||||
|
||||
impl Iterator for LedIter {
|
||||
type Item = Led;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.curr_led < self.led_count {
|
||||
let item = Led {
|
||||
led_num: self.curr_led,
|
||||
};
|
||||
self.curr_led += 1;
|
||||
Some(item)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
3
third_party/libtock-drivers/src/lib.rs
vendored
3
third_party/libtock-drivers/src/lib.rs
vendored
@@ -1,9 +1,6 @@
|
||||
#![no_std]
|
||||
|
||||
pub mod buttons;
|
||||
pub mod console;
|
||||
pub mod crp;
|
||||
pub mod led;
|
||||
#[cfg(feature = "with_nfc")]
|
||||
pub mod nfc;
|
||||
pub mod result;
|
||||
|
||||
127
third_party/libtock-drivers/src/nfc.rs
vendored
127
third_party/libtock-drivers/src/nfc.rs
vendored
@@ -1,103 +1,124 @@
|
||||
use crate::result::TockResult;
|
||||
use crate::util;
|
||||
use core::cell::Cell;
|
||||
use core::mem;
|
||||
use libtock_core::{callback, syscalls};
|
||||
use libtock_platform as platform;
|
||||
use platform::{share, AllowRo, AllowRw, DefaultConfig, ErrorCode, Subscribe, Syscalls};
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x30003;
|
||||
const DRIVER_NUMBER: u32 = 0x30003;
|
||||
|
||||
mod command_nr {
|
||||
pub const CHECK: usize = 0;
|
||||
pub const TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const EMULATE: usize = 3;
|
||||
pub const CONFIGURE: usize = 4;
|
||||
pub const CHECK: u32 = 0;
|
||||
pub const TRANSMIT: u32 = 1;
|
||||
pub const RECEIVE: u32 = 2;
|
||||
pub const EMULATE: u32 = 3;
|
||||
pub const CONFIGURE: u32 = 4;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const TRANSMIT: u32 = 1;
|
||||
pub const RECEIVE: u32 = 2;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const TRANSMIT: u32 = 1;
|
||||
pub const RECEIVE: u32 = 2;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct RecvOp {
|
||||
pub result_code: usize,
|
||||
pub recv_amount: usize,
|
||||
pub result_code: u32,
|
||||
pub recv_amount: u32,
|
||||
}
|
||||
|
||||
pub struct NfcTag {}
|
||||
pub trait Config:
|
||||
platform::allow_rw::Config + platform::allow_ro::Config + platform::subscribe::Config
|
||||
{
|
||||
}
|
||||
impl<T: platform::allow_rw::Config + platform::allow_ro::Config + platform::subscribe::Config>
|
||||
Config for T
|
||||
{
|
||||
}
|
||||
|
||||
impl NfcTag {
|
||||
pub struct NfcTag<S: Syscalls, C: Config = DefaultConfig>(S, C);
|
||||
|
||||
impl<S: Syscalls, C: Config> NfcTag<S, C> {
|
||||
/// Check the existence of an NFC driver.
|
||||
pub fn setup() -> bool {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).is_ok()
|
||||
S::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).is_success()
|
||||
}
|
||||
|
||||
pub fn enable_emulation() -> bool {
|
||||
NfcTag::emulate(true)
|
||||
NfcTag::<S, C>::emulate(true)
|
||||
}
|
||||
|
||||
pub fn disable_emulation() -> bool {
|
||||
NfcTag::emulate(false)
|
||||
NfcTag::<S, C>::emulate(false)
|
||||
}
|
||||
|
||||
fn emulate(enabled: bool) -> bool {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::EMULATE, enabled as usize, 0).is_ok()
|
||||
S::command(DRIVER_NUMBER, command_nr::EMULATE, enabled as u32, 0).is_success()
|
||||
}
|
||||
|
||||
/// Configure the tag type command.
|
||||
pub fn configure(tag_type: u8) -> bool {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::CONFIGURE, tag_type as usize, 0).is_ok()
|
||||
S::command(DRIVER_NUMBER, command_nr::CONFIGURE, tag_type as u32, 0).is_success()
|
||||
}
|
||||
|
||||
/// 1. Share with the driver a buffer.
|
||||
/// 2. Subscribe to having a successful receive callback.
|
||||
/// 3. Issue the request for reception.
|
||||
pub fn receive(buf: &mut [u8; 256]) -> TockResult<RecvOp> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?;
|
||||
// set callback with 2 arguments, to receive ReturnCode and RX Amount
|
||||
let recv_data = Cell::new(None);
|
||||
let mut callback = |result, amount| {
|
||||
recv_data.set(Some(RecvOp {
|
||||
result_code: result,
|
||||
recv_amount: amount,
|
||||
}))
|
||||
};
|
||||
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::RECEIVE,
|
||||
&mut callback,
|
||||
)?;
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0)?;
|
||||
util::yieldk_for(|| recv_data.get().is_some());
|
||||
mem::drop(subscription);
|
||||
mem::drop(result);
|
||||
Ok(recv_data.get().unwrap())
|
||||
let recv: Cell<Option<(u32, u32)>> = Cell::new(None);
|
||||
share::scope::<
|
||||
(
|
||||
AllowRw<_, DRIVER_NUMBER, { allow_nr::RECEIVE }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow_rw, subscribe) = handle.split();
|
||||
S::allow_rw::<C, DRIVER_NUMBER, { allow_nr::RECEIVE }>(allow_rw, buf)?;
|
||||
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>(subscribe, &recv)?;
|
||||
S::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
|
||||
util::Util::<S>::yieldk_for(|| recv.get().is_some());
|
||||
|
||||
let (result_code, recv_amount) = recv.get().unwrap();
|
||||
let recv_op = RecvOp {
|
||||
result_code,
|
||||
recv_amount,
|
||||
};
|
||||
Ok(recv_op)
|
||||
})
|
||||
}
|
||||
|
||||
/// 1. Share with the driver a buffer containing the app's reply.
|
||||
/// 2. Subscribe to having a successful transmission callback.
|
||||
/// 3. Issue the request for transmitting.
|
||||
pub fn transmit(buf: &mut [u8], amount: usize) -> TockResult<usize> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT, buf)?;
|
||||
pub fn transmit(buf: &mut [u8], amount: u32) -> TockResult<u32> {
|
||||
// set callback with 1 argument, to receive ReturnCode
|
||||
let result_code = Cell::new(None);
|
||||
let mut callback = |result| result_code.set(Some(result));
|
||||
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::TRANSMIT,
|
||||
&mut callback,
|
||||
)?;
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT, amount, 0)?;
|
||||
util::yieldk_for(|| result_code.get().is_some());
|
||||
mem::drop(subscription);
|
||||
mem::drop(result);
|
||||
Ok(result_code.get().unwrap())
|
||||
let result: Cell<Option<(u32,)>> = Cell::new(None);
|
||||
share::scope::<
|
||||
(
|
||||
AllowRo<_, DRIVER_NUMBER, { allow_nr::TRANSMIT }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow_ro, subscribe) = handle.split();
|
||||
S::allow_ro::<C, DRIVER_NUMBER, { allow_nr::TRANSMIT }>(allow_ro, buf)?;
|
||||
|
||||
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>(subscribe, &result)?;
|
||||
S::command(DRIVER_NUMBER, command_nr::TRANSMIT, amount, 0)
|
||||
.to_result::<(), ErrorCode>()?;
|
||||
|
||||
util::Util::<S>::yieldk_for(|| result.get().is_some());
|
||||
|
||||
Ok(result.get().unwrap().0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
61
third_party/libtock-drivers/src/result.rs
vendored
61
third_party/libtock-drivers/src/result.rs
vendored
@@ -1,6 +1,6 @@
|
||||
use core::fmt;
|
||||
|
||||
pub use libtock_core::result::*;
|
||||
use libtock_platform::ErrorCode;
|
||||
|
||||
pub type TockResult<T> = Result<T, TockError>;
|
||||
|
||||
@@ -33,9 +33,7 @@ impl<T> FlexUnwrap<T> for TockResult<T> {
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum TockError {
|
||||
Subscribe(SubscribeError),
|
||||
Command(CommandError),
|
||||
Allow(AllowError),
|
||||
Command(ErrorCode),
|
||||
Format,
|
||||
Other(OtherError),
|
||||
}
|
||||
@@ -44,64 +42,21 @@ pub enum TockError {
|
||||
impl core::fmt::Debug for TockError {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
TockError::Subscribe(SubscribeError {
|
||||
driver_number,
|
||||
subscribe_number,
|
||||
return_code,
|
||||
}) => f
|
||||
.debug_struct("SubscribeError")
|
||||
.field("driver", driver_number)
|
||||
.field("subscribe", subscribe_number)
|
||||
.field("return_code", return_code)
|
||||
.finish(),
|
||||
TockError::Command(CommandError {
|
||||
driver_number,
|
||||
command_number,
|
||||
arg1,
|
||||
arg2,
|
||||
return_code,
|
||||
}) => f
|
||||
.debug_struct("CommandError")
|
||||
.field("driver", driver_number)
|
||||
.field("command", command_number)
|
||||
.field("arg1", arg1)
|
||||
.field("arg2", arg2)
|
||||
.field("return_code", return_code)
|
||||
.finish(),
|
||||
TockError::Allow(AllowError {
|
||||
driver_number,
|
||||
allow_number,
|
||||
return_code,
|
||||
}) => f
|
||||
.debug_struct("AllowError")
|
||||
.field("driver", driver_number)
|
||||
.field("allow", allow_number)
|
||||
.field("return_code", return_code)
|
||||
.finish(),
|
||||
TockError::Command(error_code) => {
|
||||
f.write_fmt(format_args!("CommandError: {:?}", error_code))
|
||||
}
|
||||
TockError::Format => f.write_str("TockError::Format"),
|
||||
TockError::Other(e) => e.fmt(f),
|
||||
TockError::Other(e) => f.write_fmt(format_args!("OtherError: {:?}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SubscribeError> for TockError {
|
||||
fn from(subscribe_error: SubscribeError) -> Self {
|
||||
TockError::Subscribe(subscribe_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CommandError> for TockError {
|
||||
fn from(command_error: CommandError) -> Self {
|
||||
impl From<ErrorCode> for TockError {
|
||||
fn from(command_error: ErrorCode) -> Self {
|
||||
TockError::Command(command_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AllowError> for TockError {
|
||||
fn from(allow_error: AllowError) -> Self {
|
||||
TockError::Allow(allow_error)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<fmt::Error> for TockError {
|
||||
fn from(fmt::Error: fmt::Error) -> Self {
|
||||
TockError::Format
|
||||
|
||||
95
third_party/libtock-drivers/src/rng.rs
vendored
95
third_party/libtock-drivers/src/rng.rs
vendored
@@ -1,45 +1,78 @@
|
||||
use crate::util;
|
||||
use core::cell::Cell;
|
||||
use libtock_core::{callback, syscalls};
|
||||
//! Userspace interface for easy access to the random number generator
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x40001;
|
||||
use crate::util::Util;
|
||||
use core::cell::Cell;
|
||||
use core::convert::TryInto;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{share, AllowRw, DefaultConfig, Subscribe, Syscalls};
|
||||
use platform::ErrorCode;
|
||||
|
||||
/// Driver number for the random number generator
|
||||
const DRIVER_NUMBER: u32 = 0x40001;
|
||||
|
||||
mod command_nr {
|
||||
pub const REQUEST_RNG: usize = 1;
|
||||
pub const REQUEST_RNG: u32 = 1;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const BUFFER_FILLED: usize = 0;
|
||||
pub const BUFFER_FILLED: u32 = 0;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const SHARE_BUFFER: usize = 0;
|
||||
pub const SHARE_BUFFER: u32 = 0;
|
||||
}
|
||||
|
||||
pub fn fill_buffer(buf: &mut [u8]) -> bool {
|
||||
let buf_len = buf.len();
|
||||
/// System call configuration trait for `Rng`
|
||||
pub trait Config: platform::allow_rw::Config + platform::subscribe::Config {}
|
||||
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf);
|
||||
if result.is_err() {
|
||||
return false;
|
||||
impl<T: platform::allow_rw::Config + platform::subscribe::Config> Config for T {}
|
||||
|
||||
pub struct Rng<S: Syscalls, C: Config = DefaultConfig>(S, C);
|
||||
|
||||
impl<S: Syscalls, C: Config> Rng<S, C> {
|
||||
pub fn fill_buffer(buf: &mut [u8]) -> bool {
|
||||
let buf_len = buf.len();
|
||||
let is_filled = Cell::new(false);
|
||||
|
||||
share::scope::<
|
||||
(
|
||||
AllowRw<_, DRIVER_NUMBER, { allow_nr::SHARE_BUFFER }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::BUFFER_FILLED }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow_rw, subscribe) = handle.split();
|
||||
let result = S::allow_rw::<C, DRIVER_NUMBER, { allow_nr::SHARE_BUFFER }>(allow_rw, buf);
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Automatically sets `is_filled` to true as soon as the buffer is filled.
|
||||
let subscription =
|
||||
S::subscribe::<_, _, C, DRIVER_NUMBER, { subscribe_nr::BUFFER_FILLED }>(
|
||||
subscribe, &is_filled,
|
||||
);
|
||||
if subscription.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Requests the random number generator to fill the buffer.
|
||||
let result_code: Result<(), ErrorCode> = S::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::REQUEST_RNG,
|
||||
buf_len.try_into().unwrap(),
|
||||
0,
|
||||
)
|
||||
.to_result();
|
||||
if result_code.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Yields until the buffer is filled.
|
||||
Util::<S>::yieldk_for(|| is_filled.get());
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
let is_filled = Cell::new(false);
|
||||
let mut is_filled_alarm = || is_filled.set(true);
|
||||
let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::BUFFER_FILLED,
|
||||
&mut is_filled_alarm,
|
||||
);
|
||||
if subscription.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::REQUEST_RNG, buf_len, 0);
|
||||
if result_code.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
util::yieldk_for(|| is_filled.get());
|
||||
true
|
||||
}
|
||||
|
||||
375
third_party/libtock-drivers/src/timer.rs
vendored
375
third_party/libtock-drivers/src/timer.rs
vendored
@@ -1,203 +1,269 @@
|
||||
//! The alarm driver
|
||||
//!
|
||||
//! # Example
|
||||
//! ```
|
||||
//! // Wait for timeout
|
||||
//! Alarm::sleep(Alarm::Milliseconds(2500));
|
||||
//! ```
|
||||
//!
|
||||
//! Adapted from the [libtock-rs](https://github.com/tock/libtock-rs/blob/master/apis/alarm/src/lib.rs) alarm driver interface
|
||||
|
||||
use crate::result::{FlexUnwrap, OtherError, TockError, TockResult};
|
||||
use crate::util;
|
||||
use crate::util::Util;
|
||||
use core::cell::Cell;
|
||||
use core::isize;
|
||||
use core::marker::PhantomData;
|
||||
use core::ops::{Add, AddAssign, Sub};
|
||||
use libtock_core::callback::{CallbackSubscription, Consumer};
|
||||
use libtock_core::result::{CommandError, EALREADY};
|
||||
use libtock_core::syscalls;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{share, DefaultConfig, ErrorCode, Syscalls};
|
||||
use platform::share::Handle;
|
||||
use platform::subscribe::OneId;
|
||||
use platform::{Subscribe, Upcall};
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x00000;
|
||||
pub struct Alarm<S: Syscalls, C: platform::subscribe::Config = DefaultConfig>(S, C);
|
||||
|
||||
mod command_nr {
|
||||
pub const IS_DRIVER_AVAILABLE: usize = 0;
|
||||
pub const GET_CLOCK_FREQUENCY: usize = 1;
|
||||
pub const GET_CLOCK_VALUE: usize = 2;
|
||||
pub const STOP_ALARM: usize = 3;
|
||||
pub const SET_ALARM: usize = 4;
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Hz(pub u32);
|
||||
|
||||
pub trait Convert {
|
||||
/// Converts a time unit by rounding up.
|
||||
fn to_ticks(self, freq: Hz) -> Ticks;
|
||||
}
|
||||
|
||||
mod subscribe_nr {
|
||||
pub const SUBSCRIBE_CALLBACK: usize = 0;
|
||||
}
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Ticks(pub u32);
|
||||
|
||||
pub fn sleep(duration: Duration<isize>) -> TockResult<()> {
|
||||
let expired = Cell::new(false);
|
||||
let mut with_callback = with_callback(|_, _| expired.set(true));
|
||||
|
||||
let mut timer = with_callback.init().flex_unwrap();
|
||||
let timer_alarm = timer.set_alarm(duration).flex_unwrap();
|
||||
|
||||
util::yieldk_for(|| expired.get());
|
||||
|
||||
match timer.stop_alarm(timer_alarm) {
|
||||
Ok(())
|
||||
| Err(TockError::Command(CommandError {
|
||||
return_code: EALREADY,
|
||||
..
|
||||
})) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
impl Convert for Ticks {
|
||||
fn to_ticks(self, _freq: Hz) -> Ticks {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ticks() -> TockResult<usize> {
|
||||
Ok(syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::GET_CLOCK_VALUE,
|
||||
0,
|
||||
0,
|
||||
)?)
|
||||
pub fn get_ticks<S: Syscalls>() -> TockResult<u32> {
|
||||
Ok(S::command(DRIVER_NUM, command::TIME, 0, 0).to_result::<u32, ErrorCode>()?)
|
||||
}
|
||||
|
||||
pub fn get_clock_frequency() -> TockResult<usize> {
|
||||
Ok(syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::GET_CLOCK_FREQUENCY,
|
||||
0,
|
||||
0,
|
||||
)?)
|
||||
pub fn get_clock_frequency<S: Syscalls>() -> TockResult<u32> {
|
||||
Ok(S::command(DRIVER_NUM, command::FREQUENCY, 0, 0).to_result::<u32, ErrorCode>()?)
|
||||
}
|
||||
|
||||
pub fn with_callback<CB>(callback: CB) -> WithCallback<'static, CB> {
|
||||
WithCallback {
|
||||
callback,
|
||||
clock_frequency: ClockFrequency { hz: 0 },
|
||||
phantom: PhantomData,
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Milliseconds(pub u32);
|
||||
|
||||
impl Convert for Milliseconds {
|
||||
fn to_ticks(self, freq: Hz) -> Ticks {
|
||||
// Saturating multiplication will top out at about 1 hour at 1MHz.
|
||||
// It's large enough for an alarm, and much simpler than failing
|
||||
// or losing precision for short sleeps.
|
||||
|
||||
/// u32::div_ceil is still unstable.
|
||||
fn div_ceil(a: u32, other: u32) -> u32 {
|
||||
let d = a / other;
|
||||
let m = a % other;
|
||||
if m == 0 {
|
||||
d
|
||||
} else {
|
||||
d + 1
|
||||
}
|
||||
}
|
||||
Ticks(div_ceil(self.0.saturating_mul(freq.0), 1000))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WithCallback<'a, CB> {
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Alarm<S, C> {
|
||||
/// Run a check against the console capsule to ensure it is present.
|
||||
///
|
||||
/// Returns number of concurrent notifications supported,
|
||||
/// 0 if unbounded.
|
||||
#[inline(always)]
|
||||
pub fn driver_check() -> Result<u32, ErrorCode> {
|
||||
S::command(DRIVER_NUM, command::DRIVER_CHECK, 0, 0).to_result()
|
||||
}
|
||||
|
||||
pub fn get_frequency() -> Result<Hz, ErrorCode> {
|
||||
S::command(DRIVER_NUM, command::FREQUENCY, 0, 0)
|
||||
.to_result()
|
||||
.map(Hz)
|
||||
}
|
||||
|
||||
pub fn sleep_for<T: Convert>(time: T) -> Result<(), ErrorCode> {
|
||||
let freq = Self::get_frequency()?;
|
||||
let ticks = time.to_ticks(freq);
|
||||
|
||||
let called: Cell<Option<(u32, u32)>> = Cell::new(None);
|
||||
share::scope(|subscribe| {
|
||||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::CALLBACK }>(subscribe, &called)?;
|
||||
|
||||
S::command(DRIVER_NUM, command::SET_RELATIVE, ticks.0, 0)
|
||||
.to_result()
|
||||
.map(|_when: u32| ())?;
|
||||
|
||||
loop {
|
||||
S::yield_wait();
|
||||
if let Some((_when, _ref)) = called.get() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Timer<S: Syscalls, C: platform::subscribe::Config = DefaultConfig> {
|
||||
clock_frequency: Hz,
|
||||
s: PhantomData<S>,
|
||||
c: PhantomData<C>,
|
||||
}
|
||||
|
||||
pub struct WithCallback<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)> {
|
||||
callback: CB,
|
||||
clock_frequency: ClockFrequency,
|
||||
phantom: PhantomData<&'a mut ()>,
|
||||
clock_frequency: Hz,
|
||||
s: PhantomData<S>,
|
||||
c: PhantomData<C>,
|
||||
}
|
||||
|
||||
struct TimerEventConsumer;
|
||||
|
||||
impl<CB: FnMut(ClockValue, Alarm)> Consumer<WithCallback<'_, CB>> for TimerEventConsumer {
|
||||
fn consume(data: &mut WithCallback<CB>, clock_value: usize, alarm_id: usize, _: usize) {
|
||||
(data.callback)(
|
||||
ClockValue {
|
||||
num_ticks: clock_value as isize,
|
||||
clock_frequency: data.clock_frequency,
|
||||
},
|
||||
Alarm { alarm_id },
|
||||
);
|
||||
pub fn with_callback<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)>(
|
||||
callback: CB,
|
||||
) -> TimerUpcallConsumer<S, C, CB> {
|
||||
TimerUpcallConsumer {
|
||||
data: WithCallback {
|
||||
callback,
|
||||
clock_frequency: Hz(0),
|
||||
s: PhantomData,
|
||||
c: PhantomData,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, CB: FnMut(ClockValue, Alarm)> WithCallback<'a, CB> {
|
||||
pub fn init(&'a mut self) -> TockResult<Timer<'a>> {
|
||||
let num_notifications =
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::IS_DRIVER_AVAILABLE, 0, 0)?;
|
||||
pub struct TimerUpcallConsumer<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)> {
|
||||
data: WithCallback<S, C, CB>,
|
||||
}
|
||||
|
||||
impl<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)>
|
||||
Upcall<OneId<DRIVER_NUM, { subscribe::CALLBACK }>> for TimerUpcallConsumer<S, C, CB>
|
||||
{
|
||||
fn upcall(&self, expired_tick_val: u32, _ref_tick: u32, _: u32) {
|
||||
(self.data.callback)(ClockValue::new(
|
||||
expired_tick_val as isize,
|
||||
self.data.clock_frequency,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: Syscalls, C: platform::subscribe::Config, CB: Fn(ClockValue)>
|
||||
TimerUpcallConsumer<S, C, CB>
|
||||
{
|
||||
/// Initializes the data of the containing [WithCallback], i.e. number of notifications, clock frequency.
|
||||
pub fn init(&mut self) -> TockResult<Timer<S, C>> {
|
||||
// Check if the alarm driver works.
|
||||
S::command(DRIVER_NUM, command::DRIVER_CHECK, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
// Alarm driver only returns success as only a single concurrent timer is supported.
|
||||
|
||||
let clock_frequency =
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0)?;
|
||||
S::command(DRIVER_NUM, command::FREQUENCY, 0, 0).to_result::<u32, ErrorCode>()?;
|
||||
|
||||
if clock_frequency == 0 {
|
||||
if clock_frequency < 1_000 {
|
||||
// The alarm's frequency must be at least 1 kHz.
|
||||
return Err(OtherError::TimerDriverErroneousClockFrequency.into());
|
||||
}
|
||||
|
||||
let clock_frequency = ClockFrequency {
|
||||
hz: clock_frequency,
|
||||
};
|
||||
|
||||
let subscription = syscalls::subscribe::<TimerEventConsumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::SUBSCRIBE_CALLBACK,
|
||||
self,
|
||||
)?;
|
||||
let clock_frequency = Hz(clock_frequency);
|
||||
|
||||
Ok(Timer {
|
||||
num_notifications,
|
||||
clock_frequency,
|
||||
subscription,
|
||||
c: PhantomData,
|
||||
s: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
/// Enables the timer by subscribing for the countdown.
|
||||
/// This needs to be a separate method as it needs to be called in the same `share::scope`
|
||||
pub fn enable<'share, 'a: 'share>(
|
||||
&'a self,
|
||||
handle: Handle<Subscribe<'share, S, DRIVER_NUM, { subscribe::CALLBACK }>>,
|
||||
) -> Result<(), ErrorCode> {
|
||||
// Register the upcall for the timer.
|
||||
S::subscribe::<_, _, C, DRIVER_NUM, { subscribe::CALLBACK }>(handle, self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Timer<'a> {
|
||||
num_notifications: usize,
|
||||
clock_frequency: ClockFrequency,
|
||||
#[allow(dead_code)] // Used in drop
|
||||
subscription: CallbackSubscription<'a>,
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Default for Timer<S, C> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Timer<'a> {
|
||||
pub fn num_notifications(&self) -> usize {
|
||||
self.num_notifications
|
||||
impl<S: Syscalls, C: platform::subscribe::Config> Timer<S, C> {
|
||||
pub fn new() -> Self {
|
||||
let clock_frequency = Alarm::<S, C>::get_frequency().unwrap();
|
||||
|
||||
Self {
|
||||
clock_frequency,
|
||||
s: PhantomData,
|
||||
c: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock_frequency(&self) -> ClockFrequency {
|
||||
pub fn sleep(duration: Duration<isize>) -> TockResult<()> {
|
||||
let expired = Cell::new(false);
|
||||
let mut with_callback = with_callback::<S, C, _>(|_| expired.set(true));
|
||||
|
||||
let mut timer = with_callback.init().flex_unwrap();
|
||||
timer.set_alarm(duration).flex_unwrap();
|
||||
|
||||
Util::<S>::yieldk_for(|| expired.get());
|
||||
|
||||
match timer.stop_alarm() {
|
||||
Ok(_) | Err(TockError::Command(ErrorCode::Already)) => Ok(()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the clock frequency of the timer.
|
||||
pub fn clock_frequency(&self) -> Hz {
|
||||
self.clock_frequency
|
||||
}
|
||||
|
||||
pub fn get_current_clock(&self) -> TockResult<ClockValue> {
|
||||
/// Returns the current counter tick value.
|
||||
pub fn get_current_counter_ticks(&self) -> TockResult<ClockValue> {
|
||||
let ticks = S::command(DRIVER_NUM, command::TIME, 0, 0).to_result::<u32, ErrorCode>()?;
|
||||
|
||||
Ok(ClockValue {
|
||||
num_ticks: syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0)?
|
||||
as isize,
|
||||
clock_frequency: self.clock_frequency,
|
||||
num_ticks: ticks as isize,
|
||||
clock_frequency: self.clock_frequency(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn stop_alarm(&mut self, alarm: Alarm) -> TockResult<()> {
|
||||
syscalls::command(DRIVER_NUMBER, command_nr::STOP_ALARM, alarm.alarm_id, 0)?;
|
||||
/// Stops the currently active alarm.
|
||||
pub fn stop_alarm(&mut self) -> TockResult<()> {
|
||||
S::unsubscribe(DRIVER_NUM, subscribe::CALLBACK);
|
||||
S::command(DRIVER_NUM, command::STOP, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_alarm(&mut self, duration: Duration<isize>) -> TockResult<Alarm> {
|
||||
let now = self.get_current_clock()?;
|
||||
let freq = self.clock_frequency.hz();
|
||||
let duration_ms = duration.ms() as usize;
|
||||
let ticks = match duration_ms.checked_mul(freq) {
|
||||
Some(x) => x / 1000,
|
||||
None => {
|
||||
// Divide the largest of the two operands by 1000, to improve precision of the
|
||||
// result.
|
||||
if duration_ms > freq {
|
||||
match (duration_ms / 1000).checked_mul(freq) {
|
||||
Some(y) => y,
|
||||
None => return Err(OtherError::TimerDriverDurationOutOfRange.into()),
|
||||
}
|
||||
} else {
|
||||
match (freq / 1000).checked_mul(duration_ms) {
|
||||
Some(y) => y,
|
||||
None => return Err(OtherError::TimerDriverDurationOutOfRange.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let alarm_instant = now.num_ticks() as usize + ticks;
|
||||
pub fn set_alarm(&mut self, duration: Duration<isize>) -> TockResult<()> {
|
||||
let freq = self.clock_frequency;
|
||||
let duration_ms = duration.ms() as u32;
|
||||
let ticks = Milliseconds(duration_ms).to_ticks(freq);
|
||||
|
||||
let alarm_id = syscalls::command(DRIVER_NUMBER, command_nr::SET_ALARM, alarm_instant, 0)?;
|
||||
S::command(DRIVER_NUM, command::SET_RELATIVE, ticks.0, 0)
|
||||
.to_result::<u32, ErrorCode>()
|
||||
.map(|_when| ())?;
|
||||
|
||||
Ok(Alarm { alarm_id })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct ClockFrequency {
|
||||
hz: usize,
|
||||
}
|
||||
|
||||
impl ClockFrequency {
|
||||
pub fn hz(&self) -> usize {
|
||||
self.hz
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct ClockValue {
|
||||
num_ticks: isize,
|
||||
clock_frequency: ClockFrequency,
|
||||
clock_frequency: Hz,
|
||||
}
|
||||
|
||||
impl ClockValue {
|
||||
pub const fn new(num_ticks: isize, clock_hz: usize) -> ClockValue {
|
||||
pub const fn new(num_ticks: isize, clock_hz: Hz) -> ClockValue {
|
||||
ClockValue {
|
||||
num_ticks,
|
||||
clock_frequency: ClockFrequency { hz: clock_hz },
|
||||
clock_frequency: clock_hz,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,17 +279,17 @@ impl ClockValue {
|
||||
}
|
||||
|
||||
pub fn ms(&self) -> isize {
|
||||
ClockValue::scale_int(self.num_ticks, 1000, self.clock_frequency.hz() as isize)
|
||||
ClockValue::scale_int(self.num_ticks, 1000, self.clock_frequency.0 as isize)
|
||||
}
|
||||
|
||||
pub fn ms_f64(&self) -> f64 {
|
||||
1000.0 * (self.num_ticks as f64) / (self.clock_frequency.hz() as f64)
|
||||
1000.0 * (self.num_ticks as f64) / (self.clock_frequency.0 as f64)
|
||||
}
|
||||
|
||||
pub fn wrapping_add(self, duration: Duration<isize>) -> ClockValue {
|
||||
// This is a precision preserving formula for scaling an isize.
|
||||
let duration_ticks =
|
||||
ClockValue::scale_int(duration.ms, self.clock_frequency.hz() as isize, 1000);
|
||||
ClockValue::scale_int(duration.ms, self.clock_frequency.0 as isize, 1000);
|
||||
ClockValue {
|
||||
num_ticks: self.num_ticks.wrapping_add(duration_ticks),
|
||||
clock_frequency: self.clock_frequency,
|
||||
@@ -243,16 +309,6 @@ impl ClockValue {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Alarm {
|
||||
alarm_id: usize,
|
||||
}
|
||||
|
||||
impl Alarm {
|
||||
pub fn alarm_id(&self) -> usize {
|
||||
self.alarm_id
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Duration<T> {
|
||||
ms: T,
|
||||
@@ -350,3 +406,26 @@ where
|
||||
self.ms += duration.ms();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Driver number and command IDs
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
pub const DRIVER_NUM: u32 = 0;
|
||||
|
||||
// Command IDs
|
||||
#[allow(unused)]
|
||||
mod command {
|
||||
pub const DRIVER_CHECK: u32 = 0;
|
||||
pub const FREQUENCY: u32 = 1;
|
||||
pub const TIME: u32 = 2;
|
||||
pub const STOP: u32 = 3;
|
||||
|
||||
pub const SET_RELATIVE: u32 = 5;
|
||||
pub const SET_ABSOLUTE: u32 = 6;
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub mod subscribe {
|
||||
pub const CALLBACK: u32 = 0;
|
||||
}
|
||||
|
||||
744
third_party/libtock-drivers/src/usb_ctap_hid.rs
vendored
744
third_party/libtock-drivers/src/usb_ctap_hid.rs
vendored
@@ -12,322 +12,510 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use crate::console::Console;
|
||||
use crate::result::{OutOfRangeError, TockError, TockResult};
|
||||
use crate::timer::Duration;
|
||||
use crate::util::Util;
|
||||
use crate::{timer, util};
|
||||
use core::cell::Cell;
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use core::fmt::Write;
|
||||
use libtock_core::result::{CommandError, EALREADY, EBUSY, SUCCESS};
|
||||
use libtock_core::{callback, syscalls};
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use libtock_console::Console;
|
||||
use libtock_platform as platform;
|
||||
use libtock_platform::{share, DefaultConfig, ErrorCode, Syscalls};
|
||||
use platform::share::Handle;
|
||||
use platform::subscribe::OneId;
|
||||
use platform::{AllowRo, AllowRw, Subscribe, Upcall};
|
||||
|
||||
const DRIVER_NUMBER: usize = 0x20009;
|
||||
const DRIVER_NUMBER: u32 = 0x20009;
|
||||
|
||||
/// Ids for commands
|
||||
mod command_nr {
|
||||
pub const CHECK: usize = 0;
|
||||
pub const CONNECT: usize = 1;
|
||||
pub const _TRANSMIT: usize = 2;
|
||||
pub const RECEIVE: usize = 3;
|
||||
pub const TRANSMIT_OR_RECEIVE: usize = 4;
|
||||
pub const CANCEL: usize = 5;
|
||||
pub const CHECK: u32 = 0;
|
||||
pub const CONNECT: u32 = 1;
|
||||
pub const TRANSMIT: u32 = 2;
|
||||
pub const RECEIVE: u32 = 3;
|
||||
pub const TRANSMIT_OR_RECEIVE: u32 = 4;
|
||||
pub const CANCEL: u32 = 5;
|
||||
}
|
||||
|
||||
/// Ids for subscribe numbers
|
||||
mod subscribe_nr {
|
||||
pub const _TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
pub mod callback_status {
|
||||
pub const TRANSMITTED: usize = 1;
|
||||
pub const RECEIVED: usize = 2;
|
||||
}
|
||||
pub const TRANSMIT: u32 = 0;
|
||||
pub const RECEIVE: u32 = 1;
|
||||
}
|
||||
|
||||
mod allow_nr {
|
||||
pub const _TRANSMIT: usize = 1;
|
||||
pub const RECEIVE: usize = 2;
|
||||
pub const TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
mod ro_allow_nr {
|
||||
pub const TRANSMIT: u32 = 0;
|
||||
}
|
||||
|
||||
pub fn setup() -> bool {
|
||||
let result = syscalls::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0);
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let result = syscalls::command(DRIVER_NUMBER, command_nr::CONNECT, 0, 0);
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
mod rw_allow_nr {
|
||||
pub const RECEIVE: u32 = 0;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SendOrRecvStatus {
|
||||
Timeout,
|
||||
Sent,
|
||||
Received(usize),
|
||||
Received(u32),
|
||||
}
|
||||
|
||||
/// Waits to receive a packet.
|
||||
///
|
||||
/// Returns None if the transaction timed out, else its status.
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Receiving packet with timeout of {}ms",
|
||||
timeout_delay.ms(),
|
||||
)
|
||||
.unwrap();
|
||||
pub trait Config:
|
||||
platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config
|
||||
{
|
||||
}
|
||||
|
||||
let result = recv_with_timeout_detail(buf, timeout_delay);
|
||||
impl<T: platform::allow_ro::Config + platform::allow_rw::Config + platform::subscribe::Config>
|
||||
Config for T
|
||||
{
|
||||
}
|
||||
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
if let Ok(SendOrRecvStatus::Received(endpoint)) = result {
|
||||
pub struct UsbCtapHidListener<F: Fn(u32, u32)>(pub F);
|
||||
|
||||
impl<const SUB_NUM: u32, F: Fn(u32, u32)> Upcall<OneId<DRIVER_NUMBER, SUB_NUM>>
|
||||
for UsbCtapHidListener<F>
|
||||
{
|
||||
fn upcall(&self, direction: u32, endpoint: u32, _: u32) {
|
||||
self.0(direction, endpoint)
|
||||
}
|
||||
}
|
||||
pub struct UsbCtapHid<S: Syscalls, C: Config = DefaultConfig>(S, C);
|
||||
|
||||
impl<S: Syscalls, C: Config> UsbCtapHid<S, C> {
|
||||
/// Register an listener to call with the arguments.
|
||||
///
|
||||
/// Only one listener can be registered at a time.
|
||||
fn register_listener<'share, const SUB_NUM: u32, F: Fn(u32, u32)>(
|
||||
listener: &'share UsbCtapHidListener<F>,
|
||||
subscribe: Handle<Subscribe<'share, S, DRIVER_NUMBER, SUB_NUM>>,
|
||||
) -> Result<(), ErrorCode> {
|
||||
S::subscribe::<_, _, C, DRIVER_NUMBER, SUB_NUM>(subscribe, listener)
|
||||
}
|
||||
|
||||
/// Unregisters the listener.
|
||||
///
|
||||
/// Can be called even if there was no previously registered listener.
|
||||
fn unregister_listener(subscribe_num: u32) {
|
||||
S::unsubscribe(DRIVER_NUMBER, subscribe_num);
|
||||
}
|
||||
|
||||
/// Checks whether the driver is available and tries to setup the connection.
|
||||
pub fn setup() -> bool {
|
||||
let result =
|
||||
S::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0).to_result::<(), ErrorCode>();
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let result =
|
||||
S::command(DRIVER_NUMBER, command_nr::CONNECT, 0, 0).to_result::<(), ErrorCode>();
|
||||
if result.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Waits to receive a packet.
|
||||
///
|
||||
/// Returns None if the transaction timed out, else its status.
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
buf as &[u8],
|
||||
Console::<S>::writer(),
|
||||
"Receiving packet with timeout of {} ms",
|
||||
timeout_delay.ms(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = Self::recv_with_timeout_detail(buf, timeout_delay);
|
||||
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
if let Ok(SendOrRecvStatus::Received(endpoint)) = result {
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
buf as &[u8],
|
||||
endpoint as u8,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Sends a packet to a given endpoint.
|
||||
///
|
||||
/// Returns the transmission status.
|
||||
pub fn send(
|
||||
buf: &[u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: u32,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Sending packet on endpoint {} with timeout of {} ms = {:02x?}",
|
||||
endpoint,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// Either sends or receives a packet within a given time.
|
||||
///
|
||||
/// Because USB transactions are initiated by the host, we don't decide whether an IN transaction
|
||||
/// (send for us), an OUT transaction (receive for us), or no transaction at all will happen next.
|
||||
///
|
||||
/// - If an IN transaction happens first, the initial content of buf is sent to the host and the
|
||||
/// Sent status is returned.
|
||||
/// - If an OUT transaction happens first, the content of buf is replaced by the packet received
|
||||
/// from the host and Received status is returned. In that case, the original content of buf is not
|
||||
/// sent to the host, and it's up to the caller to retry sending or to handle the packet received
|
||||
/// from the host.
|
||||
/// If the timeout elapses, return None.
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn send_or_recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: usize,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Sending packet with timeout of {}ms = {:02x?}",
|
||||
timeout_delay.ms(),
|
||||
buf as &[u8]
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = send_or_recv_with_timeout_detail(buf, timeout_delay, endpoint);
|
||||
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
if let Ok(SendOrRecvStatus::Received(received_endpoint)) = result {
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
timeout_delay.ms(),
|
||||
buf as &[u8],
|
||||
received_endpoint,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Self::send_detail(buf, timeout_delay, endpoint)
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?;
|
||||
|
||||
let status = Cell::new(None);
|
||||
let mut alarm = |direction, endpoint| {
|
||||
status.set(Some(match direction {
|
||||
subscribe_nr::callback_status::RECEIVED => Ok(SendOrRecvStatus::Received(endpoint)),
|
||||
// Unknown direction or "transmitted" sent by the kernel.
|
||||
_ => Err(OutOfRangeError.into()),
|
||||
}));
|
||||
};
|
||||
|
||||
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::RECEIVE,
|
||||
&mut alarm,
|
||||
)?;
|
||||
|
||||
// Setup a time-out callback.
|
||||
let mut timeout_callback = timer::with_callback(|_, _| {
|
||||
status.set(Some(Ok(SendOrRecvStatus::Timeout)));
|
||||
});
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
let timeout_alarm = timeout.set_alarm(timeout_delay)?;
|
||||
|
||||
// Trigger USB reception.
|
||||
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0)?;
|
||||
|
||||
util::yieldk_for(|| status.get().is_some());
|
||||
let status = status.get().unwrap();
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm(timeout_alarm) {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(CommandError {
|
||||
return_code: EALREADY,
|
||||
..
|
||||
})) => {
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"The receive timeout already expired, but the callback wasn't executed."
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
/// Either sends or receives a packet within a given time.
|
||||
///
|
||||
/// Because USB transactions are initiated by the host, we don't decide whether an IN transaction
|
||||
/// (send for us), an OUT transaction (receive for us), or no transaction at all will happen next.
|
||||
///
|
||||
/// - If an IN transaction happens first, the initial content of buf is sent to the host and the
|
||||
/// Sent status is returned.
|
||||
/// - If an OUT transaction happens first, the content of buf is replaced by the packet received
|
||||
/// from the host and Received status is returned. In that case, the original content of buf is not
|
||||
/// sent to the host, and it's up to the caller to retry sending or to handle the packet received
|
||||
/// from the host.
|
||||
/// If the timeout elapses, return None.
|
||||
#[allow(clippy::let_and_return)]
|
||||
pub fn send_or_recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: u32,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap();
|
||||
let result_code =
|
||||
unsafe { syscalls::raw::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) };
|
||||
match result_code {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
SUCCESS | EALREADY => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
EBUSY => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::new(), "Couldn't cancel the USB receive").unwrap();
|
||||
}
|
||||
_ => panic!(
|
||||
"Unexpected error when cancelling USB receive: {:?}",
|
||||
result_code
|
||||
),
|
||||
}
|
||||
}
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Sending packet with timeout of {} ms = {:02x?}",
|
||||
timeout_delay.ms(),
|
||||
buf as &[u8]
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
core::mem::drop(result);
|
||||
core::mem::drop(subscription);
|
||||
core::mem::drop(result_code);
|
||||
status
|
||||
}
|
||||
let result = Self::send_or_recv_with_timeout_detail(buf, timeout_delay, endpoint);
|
||||
|
||||
fn send_or_recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: usize,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf)?;
|
||||
|
||||
let status = Cell::new(None);
|
||||
let mut alarm = |direction, endpoint| {
|
||||
status.set(Some(match direction {
|
||||
subscribe_nr::callback_status::TRANSMITTED => Ok(SendOrRecvStatus::Sent),
|
||||
subscribe_nr::callback_status::RECEIVED => Ok(SendOrRecvStatus::Received(endpoint)),
|
||||
// Unknown direction sent by the kernel.
|
||||
_ => Err(OutOfRangeError.into()),
|
||||
}));
|
||||
};
|
||||
|
||||
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::TRANSMIT_OR_RECEIVE,
|
||||
&mut alarm,
|
||||
)?;
|
||||
|
||||
// Setup a time-out callback.
|
||||
let mut timeout_callback = timer::with_callback(|_, _| {
|
||||
status.set(Some(Ok(SendOrRecvStatus::Timeout)));
|
||||
});
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
let timeout_alarm = timeout.set_alarm(timeout_delay)?;
|
||||
|
||||
// Trigger USB transmission.
|
||||
let result_code = syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::TRANSMIT_OR_RECEIVE,
|
||||
endpoint as usize,
|
||||
0,
|
||||
)?;
|
||||
|
||||
util::yieldk_for(|| status.get().is_some());
|
||||
let status = status.get().unwrap();
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm(timeout_alarm) {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(CommandError {
|
||||
return_code: EALREADY,
|
||||
..
|
||||
})) => {
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"The send/receive timeout already expired, but the callback wasn't executed."
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap();
|
||||
let result_code = unsafe {
|
||||
syscalls::raw::command(DRIVER_NUMBER, command_nr::CANCEL, endpoint as usize, 0)
|
||||
};
|
||||
match result_code {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
SUCCESS | EALREADY => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
EBUSY => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::new(), "Couldn't cancel the transaction").unwrap();
|
||||
}
|
||||
_ => panic!(
|
||||
"Unexpected error when cancelling USB transaction: {:?}",
|
||||
result_code
|
||||
),
|
||||
if let Ok(SendOrRecvStatus::Received(received_endpoint)) = result {
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
buf as &[u8],
|
||||
received_endpoint as u8,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::new(), "Cancelled USB transaction!").unwrap();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
core::mem::drop(result);
|
||||
core::mem::drop(subscription);
|
||||
core::mem::drop(result_code);
|
||||
status
|
||||
fn recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let status: Cell<Option<SendOrRecvStatus>> = Cell::new(None);
|
||||
|
||||
let alarm = UsbCtapHidListener(|direction, endpoint| match direction {
|
||||
subscribe_nr::RECEIVE => status.set(Some(SendOrRecvStatus::Received(endpoint))),
|
||||
// Unknown direction or "transmitted" sent by the kernel
|
||||
_ => status.set(None),
|
||||
});
|
||||
|
||||
let mut timeout_callback =
|
||||
timer::with_callback::<S, C, _>(|_| status.set(Some(SendOrRecvStatus::Timeout)));
|
||||
let status = share::scope::<
|
||||
(
|
||||
AllowRw<_, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>,
|
||||
Subscribe<S, { timer::DRIVER_NUM }, { timer::subscribe::CALLBACK }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow, subscribe_recv, subscribe_timer) = handle.split();
|
||||
S::allow_rw::<C, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>(allow, buf)?;
|
||||
|
||||
Self::register_listener::<{ subscribe_nr::RECEIVE }, _>(&alarm, subscribe_recv)?;
|
||||
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
timeout_callback.enable(subscribe_timer)?;
|
||||
timeout
|
||||
.set_alarm(timeout_delay)
|
||||
.map_err(|_| ErrorCode::Fail)?;
|
||||
|
||||
S::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0).to_result::<(), ErrorCode>()?;
|
||||
|
||||
Util::<S>::yieldk_for(|| status.get().is_some());
|
||||
Self::unregister_listener(subscribe_nr::RECEIVE);
|
||||
|
||||
let status = match status.get() {
|
||||
Some(status) => Ok::<SendOrRecvStatus, TockError>(status),
|
||||
None => Err(OutOfRangeError.into()),
|
||||
}?;
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm() {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(ErrorCode::Already)) => {
|
||||
if matches!(status, SendOrRecvStatus::Timeout) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
write!(Console::<S>::writer(), ".").unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
Ok::<SendOrRecvStatus, TockError>(status)
|
||||
});
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Cancelling USB receive due to timeout"
|
||||
)
|
||||
.unwrap();
|
||||
let result =
|
||||
S::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0).to_result::<(), ErrorCode>();
|
||||
match result {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
Ok(_) | Err(ErrorCode::Already) => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
Err(ErrorCode::Busy) => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::<S>::writer(), "Couldn't cancel the USB receive").unwrap();
|
||||
}
|
||||
Err(e) => panic!("Unexpected error when cancelling USB receive: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
status
|
||||
}
|
||||
|
||||
fn send_detail(
|
||||
buf: &[u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: u32,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let status: Cell<Option<SendOrRecvStatus>> = Cell::new(None);
|
||||
let alarm = UsbCtapHidListener(|direction, _| {
|
||||
let option = match direction {
|
||||
subscribe_nr::TRANSMIT => Some(SendOrRecvStatus::Sent),
|
||||
_ => None,
|
||||
};
|
||||
status.set(option);
|
||||
});
|
||||
|
||||
let mut timeout_callback =
|
||||
timer::with_callback::<S, C, _>(|_| status.set(Some(SendOrRecvStatus::Timeout)));
|
||||
let status = share::scope::<
|
||||
(
|
||||
AllowRo<_, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>,
|
||||
Subscribe<S, { timer::DRIVER_NUM }, { timer::subscribe::CALLBACK }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow, subscribe_send, subscribe_timer) = handle.split();
|
||||
|
||||
S::allow_ro::<C, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>(allow, buf)?;
|
||||
|
||||
Self::register_listener::<{ subscribe_nr::TRANSMIT }, _>(&alarm, subscribe_send)?;
|
||||
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
timeout_callback.enable(subscribe_timer)?;
|
||||
timeout
|
||||
.set_alarm(timeout_delay)
|
||||
.map_err(|_| ErrorCode::Fail)?;
|
||||
|
||||
S::command(DRIVER_NUMBER, command_nr::TRANSMIT, endpoint as u32, 0)
|
||||
.to_result::<(), ErrorCode>()?;
|
||||
|
||||
util::Util::<S>::yieldk_for(|| status.get().is_some());
|
||||
Self::unregister_listener(subscribe_nr::TRANSMIT);
|
||||
|
||||
let status = match status.get() {
|
||||
Some(status) => Ok::<SendOrRecvStatus, TockError>(status),
|
||||
None => Err(OutOfRangeError.into()),
|
||||
}?;
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm() {
|
||||
Ok(()) => (),
|
||||
Err(TockError::Command(ErrorCode::Already)) => {
|
||||
if matches!(status, SendOrRecvStatus::Timeout) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"The send timeout already expired, but the callback wasn't executed."
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
Ok::<SendOrRecvStatus, TockError>(status)
|
||||
});
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Cancelling USB transmit due to timeout"
|
||||
)
|
||||
.unwrap();
|
||||
let result = S::command(DRIVER_NUMBER, command_nr::CANCEL, endpoint as u32, 0)
|
||||
.to_result::<(), ErrorCode>();
|
||||
match result {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
Ok(_) | Err(ErrorCode::Already) => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
Err(ErrorCode::Busy) => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::<S>::writer(), "Couldn't cancel the USB receive").unwrap();
|
||||
}
|
||||
Err(e) => panic!("Unexpected error when cancelling USB receive: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
status
|
||||
}
|
||||
|
||||
fn send_or_recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
endpoint: u32,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let status: Cell<Option<SendOrRecvStatus>> = Cell::new(None);
|
||||
let alarm = UsbCtapHidListener(|direction, endpoint| {
|
||||
let option = match direction {
|
||||
subscribe_nr::TRANSMIT => Some(SendOrRecvStatus::Sent),
|
||||
subscribe_nr::RECEIVE => Some(SendOrRecvStatus::Received(endpoint)),
|
||||
_ => None,
|
||||
};
|
||||
status.set(option);
|
||||
});
|
||||
let mut recv_buf = [0; 64];
|
||||
|
||||
// init the time-out callback but don't enable it yet
|
||||
let mut timeout_callback = timer::with_callback::<S, C, _>(|_| {
|
||||
status.set(Some(SendOrRecvStatus::Timeout));
|
||||
});
|
||||
let status = share::scope::<
|
||||
(
|
||||
AllowRo<_, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>,
|
||||
AllowRw<_, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::TRANSMIT }>,
|
||||
Subscribe<_, DRIVER_NUMBER, { subscribe_nr::RECEIVE }>,
|
||||
Subscribe<_, { timer::DRIVER_NUM }, { timer::subscribe::CALLBACK }>,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
>(|handle| {
|
||||
let (allow_ro, allow_rw, sub_send, sub_recv, sub_timer) = handle.split();
|
||||
|
||||
S::allow_ro::<C, DRIVER_NUMBER, { ro_allow_nr::TRANSMIT }>(allow_ro, buf)?;
|
||||
S::allow_rw::<C, DRIVER_NUMBER, { rw_allow_nr::RECEIVE }>(allow_rw, &mut recv_buf)?;
|
||||
|
||||
Self::register_listener::<{ subscribe_nr::TRANSMIT }, _>(&alarm, sub_send)?;
|
||||
Self::register_listener::<{ subscribe_nr::RECEIVE }, _>(&alarm, sub_recv)?;
|
||||
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
timeout_callback.enable(sub_timer)?;
|
||||
timeout.set_alarm(timeout_delay)?;
|
||||
|
||||
// Trigger USB transmission.
|
||||
S::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::TRANSMIT_OR_RECEIVE,
|
||||
endpoint as u32,
|
||||
0,
|
||||
)
|
||||
.to_result::<(), ErrorCode>()?;
|
||||
|
||||
util::Util::<S>::yieldk_for(|| status.get().is_some());
|
||||
Self::unregister_listener(subscribe_nr::TRANSMIT);
|
||||
Self::unregister_listener(subscribe_nr::RECEIVE);
|
||||
|
||||
let status = match status.get() {
|
||||
Some(status) => Ok::<SendOrRecvStatus, TockError>(status),
|
||||
None => Err(OutOfRangeError.into()),
|
||||
}?;
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm() {
|
||||
Ok(_) => (),
|
||||
Err(TockError::Command(ErrorCode::Already)) => {
|
||||
if matches!(status, SendOrRecvStatus::Timeout) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"The send/receive timeout already expired, but the callback wasn't executed."
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
panic!("Unexpected error when stopping alarm: {:?}", _e);
|
||||
#[cfg(not(feature = "debug_ctap"))]
|
||||
panic!("Unexpected error when stopping alarm: <error is only visible with the debug_ctap feature>");
|
||||
}
|
||||
}
|
||||
Ok::<SendOrRecvStatus, TockError>(status)
|
||||
});
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::<S>::writer(),
|
||||
"Cancelling USB transaction due to timeout"
|
||||
)
|
||||
.unwrap();
|
||||
let result = S::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0)
|
||||
.to_result::<(), ErrorCode>();
|
||||
match result {
|
||||
// - SUCCESS means that we successfully cancelled the transaction.
|
||||
// - EALREADY means that the transaction was already completed.
|
||||
Ok(_) | Err(ErrorCode::Already) => (),
|
||||
// - EBUSY means that the transaction is in progress.
|
||||
Err(ErrorCode::Busy) => {
|
||||
// The app should wait for it, but it may never happen if the remote app crashes.
|
||||
// We just return to avoid a deadlock.
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::<S>::writer(), "Couldn't cancel the transaction").unwrap();
|
||||
}
|
||||
Err(e) => panic!("Unexpected error when cancelling USB transaction: {:?}", e),
|
||||
}
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(Console::<S>::writer(), "Cancelled USB transaction!").unwrap();
|
||||
}
|
||||
|
||||
if matches!(status, Ok(SendOrRecvStatus::Received(_))) {
|
||||
buf.copy_from_slice(&recv_buf);
|
||||
}
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
14
third_party/libtock-drivers/src/util.rs
vendored
14
third_party/libtock-drivers/src/util.rs
vendored
@@ -1,9 +1,13 @@
|
||||
use libtock_core::syscalls;
|
||||
use libtock_platform::Syscalls;
|
||||
|
||||
pub fn yieldk_for<F: Fn() -> bool>(cond: F) {
|
||||
while !cond() {
|
||||
unsafe {
|
||||
syscalls::raw::yieldk();
|
||||
pub struct Util<S: Syscalls>(S);
|
||||
|
||||
impl<S: Syscalls> Util<S> {
|
||||
// Yielding manually is discouraged as it conflicts with Rust's safety guarantees.
|
||||
// If you need to wait for a condition, use futures::wait_until and .await.
|
||||
pub fn yieldk_for<F: Fn() -> bool>(cond: F) {
|
||||
while !cond() {
|
||||
S::yield_wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
third_party/libtock-rs
vendored
2
third_party/libtock-rs
vendored
Submodule third_party/libtock-rs updated: 15e837e449...d07eaa2df1
2
third_party/tock
vendored
2
third_party/tock
vendored
Submodule third_party/tock updated: e916444378...44f39d7c8c
Reference in New Issue
Block a user