Merge branch 'master' into client-pin-features

This commit is contained in:
kaczmarczyck
2020-08-19 19:36:14 +02:00
committed by GitHub
60 changed files with 2056 additions and 731 deletions

View File

@@ -16,6 +16,8 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: "true"
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
target: thumbv7em-none-eabi target: thumbv7em-none-eabi

View File

@@ -9,6 +9,8 @@ jobs:
if: github.repository == 'google/OpenSK' if: github.repository == 'google/OpenSK'
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: "true"
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
target: thumbv7em-none-eabi target: thumbv7em-none-eabi

View File

@@ -17,6 +17,8 @@ jobs:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: "true"
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
target: thumbv7em-none-eabi target: thumbv7em-none-eabi

View File

@@ -18,6 +18,8 @@ jobs:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: "true"
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
target: thumbv7em-none-eabi target: thumbv7em-none-eabi
@@ -41,6 +43,12 @@ jobs:
with: with:
command: fmt command: fmt
args: --manifest-path libraries/cbor/Cargo.toml --all -- --check args: --manifest-path libraries/cbor/Cargo.toml --all -- --check
- name: Cargo format libraries/cbor/fuzz
uses: actions-rs/cargo@v1
with:
command: fmt
args: --manifest-path libraries/cbor/fuzz/Cargo.toml --all -- --check
- name: Cargo format libraries/crypto - name: Cargo format libraries/crypto
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1

View File

@@ -12,6 +12,8 @@ jobs:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: "true"
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
target: thumbv7em-none-eabi target: thumbv7em-none-eabi

View File

@@ -14,6 +14,8 @@ jobs:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: "true"
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
target: thumbv7em-none-eabi target: thumbv7em-none-eabi

View File

@@ -13,6 +13,8 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: "true"
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
target: thumbv7em-none-eabi target: thumbv7em-none-eabi

View File

@@ -14,6 +14,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: "true"
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
target: thumbv7em-none-eabi target: thumbv7em-none-eabi

View File

@@ -22,7 +22,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip setuptools wheel python -m pip install --upgrade pip setuptools wheel
pip install 'tockloader==1.4' pylint pip install 'tockloader==1.5' pylint
- name: Register matcher - name: Register matcher
run: echo ::add-matcher::./.github/python_matcher.json run: echo ::add-matcher::./.github/python_matcher.json
- name: Test code with pylint - name: Test code with pylint

View File

@@ -14,6 +14,8 @@ jobs:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with:
submodules: "true"
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
target: thumbv7em-none-eabi target: thumbv7em-none-eabi

View File

@@ -10,7 +10,9 @@ license = "Apache-2.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
libtock = { path = "third_party/libtock-rs" } libtock_core = { path = "third_party/libtock-rs/core" }
libtock_drivers = { path = "third_party/libtock-drivers" }
lang_items = { path = "third_party/lang-items" }
cbor = { path = "libraries/cbor" } cbor = { path = "libraries/cbor" }
crypto = { path = "libraries/crypto" } crypto = { path = "libraries/crypto" }
byteorder = { version = "1", default-features = false } byteorder = { version = "1", default-features = false }
@@ -18,12 +20,12 @@ arrayref = "0.3.6"
subtle = { version = "2.2", default-features = false, features = ["nightly"] } subtle = { version = "2.2", default-features = false, features = ["nightly"] }
[features] [features]
debug_allocations = ["libtock/debug_allocations"] debug_allocations = ["lang_items/debug_allocations"]
debug_ctap = ["crypto/derive_debug"] debug_ctap = ["crypto/derive_debug", "libtock_drivers/debug_ctap"]
panic_console = ["libtock/panic_console"] panic_console = ["lang_items/panic_console"]
std = ["cbor/std", "crypto/std", "crypto/derive_debug"] std = ["cbor/std", "crypto/std", "crypto/derive_debug", "lang_items/std"]
ram_storage = [] ram_storage = []
verbose = ["debug_ctap"] verbose = ["debug_ctap", "libtock_drivers/verbose_usb"]
with_ctap1 = ["crypto/with_ctap1"] with_ctap1 = ["crypto/with_ctap1"]
with_ctap2_1 = [] with_ctap2_1 = []

View File

@@ -255,7 +255,6 @@ class RemoveConstAction(argparse.Action):
class OpenSKInstaller: class OpenSKInstaller:
def __init__(self, args): def __init__(self, args):
colorama.init()
self.args = args self.args = args
# Where all the TAB files should go # Where all the TAB files should go
self.tab_folder = os.path.join("target", "tab") self.tab_folder = os.path.join("target", "tab")
@@ -373,6 +372,7 @@ class OpenSKInstaller:
] ]
env = os.environ.copy() env = os.environ.copy()
env["RUSTFLAGS"] = " ".join(rust_flags) env["RUSTFLAGS"] = " ".join(rust_flags)
env["APP_HEAP_SIZE"] = str(APP_HEAP_SIZE)
command = [ command = [
"cargo", "build", "--release", "--target={}".format(props.arch), "cargo", "build", "--release", "--target={}".format(props.arch),
@@ -453,14 +453,10 @@ class OpenSKInstaller:
self.args.application, str(e))) self.args.application, str(e)))
def get_padding(self): def get_padding(self):
fake_header = tbfh.TBFHeader("") padding = tbfh.TBFHeaderPadding(
fake_header.version = 2
fake_header.fields["header_size"] = 0x10
fake_header.fields["total_size"] = (
SUPPORTED_BOARDS[self.args.board].app_address - SUPPORTED_BOARDS[self.args.board].app_address -
SUPPORTED_BOARDS[self.args.board].padding_address) SUPPORTED_BOARDS[self.args.board].padding_address)
fake_header.fields["flags"] = 0 return padding.get_binary()
return fake_header.get_binary()
def install_tock_os(self): def install_tock_os(self):
board_props = SUPPORTED_BOARDS[self.args.board] board_props = SUPPORTED_BOARDS[self.args.board]
@@ -542,7 +538,7 @@ class OpenSKInstaller:
tock.open() tock.open()
app_found = False app_found = False
with tock._start_communication_with_board(): with tock._start_communication_with_board():
apps = [app.name for app in tock._extract_all_app_headers()] apps = [app.get_name() for app in tock._extract_all_app_headers()]
app_found = expected_app in apps app_found = expected_app in apps
return app_found return app_found
@@ -581,16 +577,17 @@ class OpenSKInstaller:
"architecture {}".format(board_props.arch))) "architecture {}".format(board_props.arch)))
app_hex = intelhex.IntelHex() app_hex = intelhex.IntelHex()
app_hex.frombytes( app_hex.frombytes(
app_tab.extract_app(board_props.arch).get_binary(), app_tab.extract_app(board_props.arch).get_binary(
board_props.app_address),
offset=board_props.app_address) offset=board_props.app_address)
final_hex.merge(app_hex) final_hex.merge(app_hex)
info("Generating all-merged HEX file: {}".format(dest_file)) info("Generating all-merged HEX file: {}".format(dest_file))
final_hex.tofile(dest_file, format="hex") final_hex.tofile(dest_file, format="hex")
def check_prerequisites(self): def check_prerequisites(self):
if not tockloader.__version__.startswith("1.4."): if not tockloader.__version__.startswith("1.5."):
fatal(("Your version of tockloader seems incompatible: found {}, " fatal(("Your version of tockloader seems incompatible: found {}, "
"expected 1.4.x.".format(tockloader.__version__))) "expected 1.5.x.".format(tockloader.__version__)))
if self.args.programmer == "jlink": if self.args.programmer == "jlink":
assert_mandatory_binary("JLinkExe") assert_mandatory_binary("JLinkExe")
@@ -619,18 +616,6 @@ class OpenSKInstaller:
assert_python_library("intelhex") assert_python_library("intelhex")
def run(self): def run(self):
if self.args.listing == "boards":
print(os.linesep.join(get_supported_boards()))
return 0
if self.args.listing == "programmers":
print(os.linesep.join(PROGRAMMERS))
return 0
if self.args.listing:
# Missing check?
fatal("Listing {} is not implemented.".format(self.args.listing))
self.check_prerequisites() self.check_prerequisites()
self.update_rustc_if_needed() self.update_rustc_if_needed()
@@ -728,9 +713,23 @@ class OpenSKInstaller:
def main(args): def main(args):
colorama.init()
# Make sure the current working directory is the right one before running # Make sure the current working directory is the right one before running
os.chdir(os.path.realpath(os.path.dirname(__file__))) os.chdir(os.path.realpath(os.path.dirname(__file__)))
if args.listing == "boards":
print(os.linesep.join(get_supported_boards()))
return 0
if args.listing == "programmers":
print(os.linesep.join(PROGRAMMERS))
return 0
if args.listing:
# Missing check?
fatal("Listing {} is not implemented.".format(args.listing))
OpenSKInstaller(args).run() OpenSKInstaller(args).run()
@@ -874,7 +873,12 @@ if __name__ == "__main__":
help=("When set, the output of elf2tab is appended to this file."), help=("When set, the output of elf2tab is appended to this file."),
) )
apps_group = main_parser.add_mutually_exclusive_group(required=True) # Start parsing to know if we're going to list things or not.
partial_args, _ = main_parser.parse_known_args()
# We only need the apps_group if we have a board set
apps_group = main_parser.add_mutually_exclusive_group(
required=(partial_args.board is not None))
apps_group.add_argument( apps_group.add_argument(
"--no-app", "--no-app",
dest="application", dest="application",
@@ -893,8 +897,22 @@ if __name__ == "__main__":
dest="application", dest="application",
action="store_const", action="store_const",
const="crypto_bench", const="crypto_bench",
help=("Compiles and installs the crypto_bench example that tests " help=("Compiles and installs the crypto_bench example that benchmarks "
"the performance of the cryptographic algorithms on the board.")) "the performance of the cryptographic algorithms on the board."))
apps_group.add_argument(
"--panic_test",
dest="application",
action="store_const",
const="panic_test",
help=("Compiles and installs the panic_test example that immediately "
"triggers a panic."))
apps_group.add_argument(
"--oom_test",
dest="application",
action="store_const",
const="oom_test",
help=("Compiles and installs the oom_test example that tests the "
"allocator until an out-of-memory error occurs."))
main_parser.set_defaults(features=["with_ctap1"]) main_parser.set_defaults(features=["with_ctap1"])

View File

@@ -17,24 +17,26 @@
#[macro_use] #[macro_use]
extern crate alloc; extern crate alloc;
extern crate crypto; extern crate crypto;
extern crate libtock; extern crate lang_items;
extern crate libtock_drivers;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::fmt::Write; use core::fmt::Write;
use crypto::{ use crypto::{
aes256, cbc, ecdsa, rng256, sha256, Decrypt16BytesBlock, Encrypt16BytesBlock, Hash256, aes256, cbc, ecdsa, rng256, sha256, Decrypt16BytesBlock, Encrypt16BytesBlock, Hash256,
}; };
use libtock::console::Console; use libtock_drivers::console::Console;
use libtock::timer; use libtock_drivers::result::FlexUnwrap;
use libtock::timer::Timer; use libtock_drivers::timer;
use libtock::timer::Timestamp; use libtock_drivers::timer::Timer;
use libtock_drivers::timer::Timestamp;
fn main() { fn main() {
let mut console = Console::new(); let mut console = Console::new();
// Setup the timer with a dummy callback (we only care about reading the current time, but the // 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). // 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().unwrap(); let timer = with_callback.init().flex_unwrap();
let mut rng = rng256::TockRng256 {}; let mut rng = rng256::TockRng256 {};
@@ -158,11 +160,11 @@ where
writeln!(console, "----------------------------------------").unwrap(); writeln!(console, "----------------------------------------").unwrap();
let mut count = 1; let mut count = 1;
for _ in 0..30 { for _ in 0..30 {
let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock()); let start = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
for _ in 0..count { for _ in 0..count {
f(); f();
} }
let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock()); let end = Timestamp::<f64>::from_clock_value(timer.get_current_clock().flex_unwrap());
let elapsed = (end - start).ms(); let elapsed = (end - start).ms();
writeln!( writeln!(
console, console,
@@ -172,6 +174,7 @@ where
elapsed / (count as f64) elapsed / (count as f64)
) )
.unwrap(); .unwrap();
console.flush();
if elapsed > 1000.0 { if elapsed > 1000.0 {
break; break;
} }

35
examples/oom_test.rs Normal file
View File

@@ -0,0 +1,35 @@
// Copyright 2020 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.
#![no_std]
extern crate alloc;
extern crate crypto;
extern crate lang_items;
extern crate libtock_drivers;
use alloc::vec::Vec;
use core::fmt::Write;
use libtock_drivers::console::Console;
fn main() {
writeln!(Console::new(), "****************************************").unwrap();
for i in 0.. {
writeln!(Console::new(), "Allocating {} bytes...", 1 << i).unwrap();
let x: Vec<u8> = Vec::with_capacity(1 << i);
writeln!(Console::new(), "Allocated!").unwrap();
drop(x);
writeln!(Console::new(), "Dropped!").unwrap();
}
}

24
examples/panic_test.rs Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2020 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.
#![no_std]
extern crate alloc;
extern crate crypto;
extern crate lang_items;
extern crate libtock_drivers;
fn main() {
panic!("Bye world!")
}

View File

@@ -0,0 +1,26 @@
[package]
name = "cbor-fuzz"
version = "0.0.0"
authors = ["Automatically generated"]
publish = false
edition = "2018"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.3"
[dependencies.cbor]
path = ".."
# Prevent this from interfering with workspaces
[workspace]
members = ["."]
[[bin]]
name = "fuzz_target_cbor"
path = "fuzz_targets/fuzz_target_cbor.rs"
test = false
doc = false

View File

@@ -0,0 +1,15 @@
#![no_main]
#[macro_use]
extern crate libfuzzer_sys;
extern crate alloc;
extern crate cbor;
use alloc::vec::Vec;
fuzz_target!(|data: &[u8]| {
if let Ok(value) = cbor::read(data) {
let mut result = Vec::new();
assert!(cbor::write(value, &mut result));
assert_eq!(result, data);
};
});

View File

@@ -10,10 +10,10 @@ license = "Apache-2.0"
edition = "2018" edition = "2018"
[dependencies] [dependencies]
libtock = { path = "../../third_party/libtock-rs" } libtock_drivers = { path = "../../third_party/libtock-drivers" }
cbor = { path = "../cbor" } cbor = { path = "../cbor" }
arrayref = "0.3.6" arrayref = "0.3.6"
subtle = { version = "2.2", default-features = false, features = ["nightly"] } subtle = { version = "2.2.3", default-features = false, features = ["nightly"] }
byteorder = { version = "1", default-features = false } byteorder = { version = "1", default-features = false }
hex = { version = "0.3.2", default-features = false, optional = true } hex = { version = "0.3.2", default-features = false, optional = true }
ring = { version = "0.16.11", optional = true } ring = { version = "0.16.11", optional = true }

View File

@@ -21,7 +21,6 @@ extern crate subtle;
#[macro_use] #[macro_use]
extern crate arrayref; extern crate arrayref;
extern crate byteorder; extern crate byteorder;
extern crate libtock;
#[macro_use] #[macro_use]
extern crate cbor; extern crate cbor;

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use libtock::rng; use libtock_drivers::rng;
// Lightweight RNG trait to generate uniformly distributed 256 bits. // Lightweight RNG trait to generate uniformly distributed 256 bits.
pub trait Rng256 { pub trait Rng256 {

View File

@@ -1,13 +0,0 @@
diff --git a/Cargo.toml b/Cargo.toml
index 29d9b79..30081f3 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT/Apache-2.0"
edition = "2018"
[dependencies]
-linked_list_allocator = "0.6.4"
+linked_list_allocator = "0.6.6"
[dev-dependencies]
corepack = { version = "0.4.0", default-features = false, features = ["alloc"] }

View File

@@ -1,81 +0,0 @@
diff --git a/Cargo.toml b/Cargo.toml
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,6 +7,9 @@
[dependencies]
linked_list_allocator = "0.6.6"
+
+[features]
+panic_console = []
[dev-dependencies]
corepack = { version = "0.4.0", default-features = false, features = ["alloc"] }
diff --git a/src/lang_items.rs b/src/lang_items.rs
index ab2c945..deef73b 100644
--- a/src/lang_items.rs
+++ b/src/lang_items.rs
@@ -18,10 +18,14 @@
//! `rustc_main`. That's covered by the `_start` function in the root of this
//! crate.
+#[cfg(feature = "panic_console")]
+use crate::console::Console;
use crate::led;
use crate::timer;
use crate::timer::Duration;
use core::alloc::Layout;
+#[cfg(feature = "panic_console")]
+use core::fmt::Write;
use core::panic::PanicInfo;
#[lang = "start"]
@@ -42,11 +46,7 @@
}
}
-#[panic_handler]
-fn panic_handler(_info: &PanicInfo) -> ! {
+fn flash_all_leds() -> ! {
- // Signal a panic using the LowLevelDebug capsule (if available).
- super::debug::low_level_status_code(1);
-
// Flash all LEDs (if available).
loop {
for led in led::all() {
@@ -60,6 +59,34 @@
}
}
+#[cfg(feature = "panic_console")]
+#[panic_handler]
+fn panic_handler(info: &PanicInfo) -> ! {
+ // Signal a panic using the LowLevelDebug capsule (if available).
+ super::debug::low_level_status_code(1);
+
+ let mut console = Console::new();
+ writeln!(console, "{}", info).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.
+ unsafe {
+ core::ptr::read_volatile(0 as *const usize);
+ }
+ // We still flash the LEDs in case for some reason the previous line didn't cause a crash in
+ // the kernel.
+ flash_all_leds();
+}
+
+#[cfg(not(feature = "panic_console"))]
+#[panic_handler]
+fn panic_handler(_info: &PanicInfo) -> ! {
+ // Signal a panic using the LowLevelDebug capsule (if available).
+ super::debug::low_level_status_code(1);
+
+ flash_all_leds();
+}
+
#[alloc_error_handler]
fn cycle_leds(_: Layout) -> ! {
loop {

View File

@@ -1,72 +0,0 @@
diff --git a/src/timer.rs b/src/timer.rs
index ae60b07..2e7d544 100644
--- a/src/timer.rs
+++ b/src/timer.rs
@@ -178,7 +178,7 @@ impl<'a> Timer<'a> {
}
}
-#[derive(Copy, Clone, Debug)]
+#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ClockFrequency {
hz: usize,
}
@@ -196,21 +196,53 @@ pub struct ClockValue {
}
impl ClockValue {
+ pub const fn new(num_ticks: isize, clock_hz: usize) -> ClockValue {
+ ClockValue {
+ num_ticks,
+ clock_frequency: ClockFrequency { hz: clock_hz },
+ }
+ }
+
pub fn num_ticks(&self) -> isize {
self.num_ticks
}
+ // Computes (value * factor) / divisor, even when value * factor >= isize::MAX.
+ fn scale_int(value: isize, factor: isize, divisor: isize) -> isize {
+ // As long as isize is not i64, this should be fine. If not, this is an alternative:
+ // factor * (value / divisor) + ((value % divisor) * factor) / divisor
+ ((value as i64 * factor as i64) / divisor as i64) as isize
+ }
+
pub fn ms(&self) -> isize {
- if self.num_ticks.abs() < isize::MAX / 1000 {
- (1000 * self.num_ticks) / self.clock_frequency.hz() as isize
- } else {
- 1000 * (self.num_ticks / self.clock_frequency.hz() as isize)
- }
+ ClockValue::scale_int(self.num_ticks, 1000, self.clock_frequency.hz() as isize)
}
pub fn ms_f64(&self) -> f64 {
1000.0 * (self.num_ticks as f64) / (self.clock_frequency.hz() 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 {
+ num_ticks: self.num_ticks.wrapping_add(duration_ticks),
+ clock_frequency: self.clock_frequency,
+ }
+ }
+
+ pub fn wrapping_sub(self, other: ClockValue) -> Option<Duration<isize>> {
+ if self.clock_frequency == other.clock_frequency {
+ let clock_duration = ClockValue {
+ num_ticks: self.num_ticks - other.num_ticks,
+ clock_frequency: self.clock_frequency,
+ };
+ Some(Duration::from_ms(clock_duration.ms()))
+ } else {
+ None
+ }
+ }
}
pub struct Alarm {

View File

@@ -1,13 +0,0 @@
diff --git a/src/lib.rs b/src/lib.rs
index 1d4f26b..c1483e9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -42,7 +42,7 @@ pub mod syscalls;
#[cfg(not(any(target_arch = "arm", target_arch = "riscv32")))]
#[path = "syscalls_mock.rs"]
-mod syscalls;
+pub mod syscalls;
#[cfg(any(target_arch = "arm", target_arch = "riscv32"))]
#[global_allocator]

View File

@@ -1,13 +0,0 @@
diff --git a/src/entry_point.rs b/src/entry_point.rs
index a24958c..e907e33 100644
--- a/src/entry_point.rs
+++ b/src/entry_point.rs
@@ -348,7 +348,7 @@ pub unsafe extern "C" fn rust_start(app_start: usize, stacktop: usize, app_heap_
// Heap size is set using `elf2tab` with `--app-heap` option, which is
// currently at 1024. If you change the `elf2tab` heap size, make sure to
// make the corresponding change here.
- const HEAP_SIZE: usize = 1024;
+ const HEAP_SIZE: usize = 90000;
// we could have also bss_end for app_heap_start
let app_heap_start = app_heap_break;

View File

@@ -1,77 +0,0 @@
diff --git a/Cargo.toml b/Cargo.toml
index 844e424..386a9ed 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ license = "MIT/Apache-2.0"
edition = "2018"
[dependencies]
-linked_list_allocator = "0.6.6"
+linked_list_allocator = { version = "0.6.6", default-features = false }
[features]
panic_console = []
diff --git a/src/entry_point.rs b/src/entry_point.rs
index 15e1d03..2fe5c40 100644
--- a/src/entry_point.rs
+++ b/src/entry_point.rs
@@ -1,8 +1,10 @@
use crate::memop;
use crate::syscalls;
-use crate::ALLOCATOR;
+use core::alloc::{GlobalAlloc, Layout};
use core::intrinsics;
use core::ptr;
+use core::ptr::NonNull;
+use linked_list_allocator::Heap;
// _start and rust_start are the first two procedures executed when a Tock
// application starts. _start is invoked directly by the Tock kernel; it
@@ -357,7 +359,7 @@ pub unsafe extern "C" fn rust_start(app_start: usize, stacktop: usize, app_heap_
// Tell the kernel the new app heap break.
memop::set_brk(app_heap_end as *const u8);
- ALLOCATOR.lock().init(app_heap_start, HEAP_SIZE);
+ HEAP.init(app_heap_start, HEAP_SIZE);
main(0, ptr::null());
@@ -365,3 +367,23 @@ pub unsafe extern "C" fn rust_start(app_start: usize, stacktop: usize, app_heap_
syscalls::yieldk();
}
}
+
+#[cfg(any(target_arch = "arm", target_arch = "riscv32"))]
+#[global_allocator]
+static ALLOCATOR: TockAllocator = TockAllocator;
+
+static mut HEAP: Heap = Heap::empty();
+
+struct TockAllocator;
+
+unsafe impl GlobalAlloc for TockAllocator {
+ unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+ HEAP.allocate_first_fit(layout)
+ .ok()
+ .map_or(0 as *mut u8, |allocation| allocation.as_ptr())
+ }
+
+ unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ HEAP.deallocate(NonNull::new_unchecked(ptr), layout)
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index c9001bf..2f9c1c0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -44,10 +44,6 @@ pub mod syscalls;
#[path = "syscalls_mock.rs"]
pub mod syscalls;
-#[cfg(any(target_arch = "arm", target_arch = "riscv32"))]
-#[global_allocator]
-static ALLOCATOR: linked_list_allocator::LockedHeap = linked_list_allocator::LockedHeap::empty();
-
// Dummy structure to force importing the panic_handler and other no_std elements when nothing else
// is imported.
pub struct LibTock;

View File

@@ -1,103 +0,0 @@
diff --git a/Cargo.toml b/Cargo.toml
index 386a9ed..af3c5db 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ linked_list_allocator = { version = "0.6.6", default-features = false }
[features]
panic_console = []
+debug_allocations = []
[dev-dependencies]
corepack = { version = "0.4.0", default-features = false, features = ["alloc"] }
diff --git a/src/entry_point.rs b/src/entry_point.rs
index 2fe5c40..545d163 100644
--- a/src/entry_point.rs
+++ b/src/entry_point.rs
@@ -368,22 +368,82 @@ pub unsafe extern "C" fn rust_start(app_start: usize, stacktop: usize, app_heap_
}
}
+#[cfg(feature = "debug_allocations")]
+use core::fmt::Write;
+#[cfg(feature = "debug_allocations")]
+use core::sync::atomic;
+#[cfg(feature = "debug_allocations")]
+use core::sync::atomic::AtomicUsize;
+
#[cfg(any(target_arch = "arm", target_arch = "riscv32"))]
#[global_allocator]
-static ALLOCATOR: TockAllocator = TockAllocator;
+static ALLOCATOR: TockAllocator = TockAllocator::new();
static mut HEAP: Heap = Heap::empty();
-struct TockAllocator;
+// With the "debug_allocations" feature, we use `AtomicUsize` to store the
+// statistics because:
+// - it is `Sync`, so we can use it in a static object (the allocator),
+// - it implements interior mutability, so we can use it in the allocator
+// methods (that take an immutable `&self` reference).
+struct TockAllocator {
+ #[cfg(feature = "debug_allocations")]
+ count: AtomicUsize,
+ #[cfg(feature = "debug_allocations")]
+ size: AtomicUsize,
+}
+
+impl TockAllocator {
+ const fn new() -> TockAllocator {
+ TockAllocator {
+ #[cfg(feature = "debug_allocations")]
+ count: AtomicUsize::new(0),
+ #[cfg(feature = "debug_allocations")]
+ size: AtomicUsize::new(0),
+ }
+ }
+}
unsafe impl GlobalAlloc for TockAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
- HEAP.allocate_first_fit(layout)
+ let ptr = HEAP
+ .allocate_first_fit(layout)
.ok()
- .map_or(0 as *mut u8, |allocation| allocation.as_ptr())
+ .map_or(ptr::null_mut(), NonNull::as_ptr);
+ #[cfg(feature = "debug_allocations")]
+ {
+ self.count.fetch_add(1, atomic::Ordering::SeqCst);
+ self.size.fetch_add(layout.size(), atomic::Ordering::SeqCst);
+ writeln!(
+ crate::console::Console::new(),
+ "alloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
+ layout.size(),
+ layout.align(),
+ ptr,
+ self.count.load(atomic::Ordering::SeqCst),
+ self.size.load(atomic::Ordering::SeqCst)
+ )
+ .unwrap();
+ }
+ ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+ #[cfg(feature = "debug_allocations")]
+ {
+ self.count.fetch_sub(1, atomic::Ordering::SeqCst);
+ self.size.fetch_sub(layout.size(), atomic::Ordering::SeqCst);
+ writeln!(
+ crate::console::Console::new(),
+ "dealloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
+ layout.size(),
+ layout.align(),
+ ptr,
+ self.count.load(atomic::Ordering::SeqCst),
+ self.size.load(atomic::Ordering::SeqCst)
+ )
+ .unwrap();
+ }
HEAP.deallocate(NonNull::new_unchecked(ptr), layout)
}
}

View File

@@ -1,98 +0,0 @@
diff --git a/src/console.rs b/src/console.rs
index ecd7ad1..ae1b826 100644
--- a/src/console.rs
+++ b/src/console.rs
@@ -16,33 +16,63 @@ mod allow_nr {
pub const SHARE_BUFFER: usize = 1;
}
+const BUFFER_SIZE: usize = 1024;
+
pub struct Console {
- allow_buffer: [u8; 64],
+ allow_buffer: [u8; BUFFER_SIZE],
+ count_pending: usize,
}
impl Console {
pub fn new() -> Console {
Console {
- allow_buffer: [0; 64],
+ 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.allow_buffer.len().min(not_written_yet.len());
- self.allow_buffer[..num_bytes_to_print]
+ let num_bytes_to_print = self.available_len().min(not_written_yet.len());
+ self.allow_buffer[self.count_pending..(self.count_pending + num_bytes_to_print)]
.copy_from_slice(&not_written_yet[..num_bytes_to_print]);
- self.flush(num_bytes_to_print);
+ self.count_pending += num_bytes_to_print;
+
+ if self.is_full() {
+ self.flush();
+ }
+
not_written_yet = &not_written_yet[num_bytes_to_print..];
}
}
- fn flush(&mut self, num_bytes_to_print: usize) {
+ 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;
+
let result = syscalls::allow(
DRIVER_NUMBER,
allow_nr::SHARE_BUFFER,
- &mut self.allow_buffer[..num_bytes_to_print],
+ &mut self.allow_buffer[..count],
);
if result.is_err() {
return;
@@ -59,8 +89,7 @@ impl Console {
return;
}
- let result_code =
- unsafe { syscalls::command(DRIVER_NUMBER, command_nr::WRITE, num_bytes_to_print, 0) };
+ let result_code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::WRITE, count, 0) };
if result_code < 0 {
return;
}
@@ -69,6 +98,12 @@ impl Console {
}
}
+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);

View File

@@ -1,9 +1,9 @@
0b54df6d548849e24d67b9b022ca09cb33c51f078ce85d0c9c4635ffc69902e1 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin 91a98f475cb3042dd5184598a8292edb2a414df8d967a35c8f2295826b5a161b third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin
949d005f9a356a031dcb0a92a703c87377489ca66869dda490d9a687c77dd723 target/nrf52840dk_merged.hex 33164f39a0b5354cdf61236c301242476284c6b96d55275aa603734054ca7928 target/nrf52840dk_merged.hex
052eec0ae526038352b9f7573468d0cf7fb5ec331d4dc1a2df75fdbd514ea5ca third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin a5943c5311158b0f99370246d37782eb9b12fc36c56387eadb6587a3a4fe8fd5 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin
fbf5e36aa4c71a77af5d04392faf29fd6491f0597f0fa35e6c0edeb5d3c8ad26 target/nrf52840_dongle_merged.hex 1232b44947f302900291692690f2e94cdfb165e00e74c682433100882754a516 target/nrf52840_dongle_merged.hex
908d7f4f40936d968b91ab6e19b2406612fe8c2c273d9c0b71ef1f55116780e0 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin 663297e3e29b9e2a972b68cea1592aaf965d797242579bb5bca09cd73cdfb637 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin
1ad0c691e8c0b4df8051f0738502e34cb9dd57d449d0c01f050dae577746c3ac target/nrf52840_dongle_dfu_merged.hex b95ce848465523e98cf0c30f94f6430e99dc8ac4b33da5bc0d0f643523ff4b50 target/nrf52840_dongle_dfu_merged.hex
34ecbecaebf1188277f2310fe769c8c60310d8576493242712854deb4ba1036e third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin 162a05d056aafc16d4868d5c3aa10518e41299dddd60608f96954dc9cf964cd3 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin
2e0bdaf152933e7bca99c2c13e13bb14da26e71e0845e09bf97d611df34768c3 target/nrf52840_mdk_dfu_merged.hex 1085e1789c4429430c47d28b23a975223717eddd7c8aa23114acbc3ec2ec7080 target/nrf52840_mdk_dfu_merged.hex
6f72b3e5c35c3d73c7274b0736c4969e2bd566c77815a8e7cdd407d9edb67180 target/tab/ctap2.tab 5bd063ce44e9ddcad8c4d17165a247387e4f1a9c6db81060fbb97244be1929b8 target/tab/ctap2.tab

View File

@@ -1,9 +1,9 @@
29382e72d0f3c6a72ce9517211952ff29ea270193d7f0ddc48ca69009ee29925 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin 3feb5d29a3d669107b460a00391440be4ebc5e50461f9ef3248714f4f99c070e third_party/tock/target/thumbv7em-none-eabi/release/nrf52840dk.bin
2c0bdb663edc88ae168ecd12b71730ab26bdc6d23b9fa832acc63cc4c91461ac target/nrf52840dk_merged.hex a02f078e165373113adbaf7fa5d272e7e01134061e8212331d54f0b0a8809aaa target/nrf52840dk_merged.hex
30f239390ae9bef0825731e4c82d40470fc5e9bded2bf0d942e92dbb5d4faba1 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin 8eebe1c1dfe22003466c2570b3735c54c58ae91b8168582ad363ab79c9230a15 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle.bin
81564eab5c20f186c0583e0a31f26fcc50ec0ebd997bff6109e663cbb7d59966 target/nrf52840_dongle_merged.hex 973bf7d0b6ddb37bb9698cf8f2ef3c2a3dd27cd482b7a4c02e452902394ffa37 target/nrf52840_dongle_merged.hex
e3acf15d5ae3a22aecff6cc58db5fc311f538f47328d348b7ad7db7f9ab5e72c third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin 779d77071d1e629f92210ac313e230bcaea6d77c710210c1ac4b40f8085cdad7 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_dongle_dfu.bin
25b10d9d80d4961ea0d8373d64e883279baeff6997eb8e541d36930ec423b88b target/nrf52840_dongle_dfu_merged.hex d0e7ecc1d2a45ef4c77b39720b95b3e349a0d48d7b9ca99fa591019a9f2cafee target/nrf52840_dongle_dfu_merged.hex
cae312a26a513ada6c198fdc59b2bba3860c51726b817a9fd17a4331ee12c882 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin f466490d6498f6c06c7c4a717eb437ba2fb06d1985532c23f145d38b9daa8259 third_party/tock/target/thumbv7em-none-eabi/release/nrf52840_mdk_dfu.bin
1077f1acf2c0d65eeda2056d907ee449bfbf7f783c253c1f92838f7ac5e11d99 target/nrf52840_mdk_dfu_merged.hex d3d4a9d3442bb8cf924f553f8df7085e3d6331f1b6d9557115d485e584285d68 target/nrf52840_mdk_dfu_merged.hex
3c8dc97b68c5ce5030f0af3879c5f9531d69ea1404899e9dfe306d02d638e0cc target/tab/ctap2.tab 6cda1346503867ef18d3fe7a3d32de6e22585c6134ef3347877894c5469390f5 target/tab/ctap2.tab

View File

@@ -1,12 +1,12 @@
======================================== ========================================
Board: nrf52840dk Board: nrf52840dk
---------------------------------------- ----------------------------------------
Min RAM size from sections in ELF: 16 bytes Min RAM size from sections in ELF: 20 bytes
Number of writeable flash regions: 0 Number of writeable flash regions: 0
Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes.
Entry point is in .text section Entry point is in .text section
Adding .text section. Offset: 128 (0x80). Length: 175972 (0x2af64) bytes. Adding .text section. Offset: 128 (0x80). Length: 187792 (0x2dd90) bytes.
Adding .stack section. Offset: 176100 (0x2afe4). Length: 16384 (0x4000) bytes. Adding .stack section. Offset: 187920 (0x2de10). Length: 16384 (0x4000) bytes.
Searching for .rel.X sections to add. Searching for .rel.X sections to add.
TBF Header: TBF Header:
version: 2 0x2 version: 2 0x2
@@ -16,16 +16,16 @@ TBF Header:
init_fn_offset: 85 0x55 init_fn_offset: 85 0x55
protected_size: 20 0x14 protected_size: 20 0x14
minimum_ram_size: 107424 0x1A3A0 minimum_ram_size: 107428 0x1A3A4
======================================== ========================================
Board: nrf52840_dongle Board: nrf52840_dongle
---------------------------------------- ----------------------------------------
Min RAM size from sections in ELF: 16 bytes Min RAM size from sections in ELF: 20 bytes
Number of writeable flash regions: 0 Number of writeable flash regions: 0
Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes.
Entry point is in .text section Entry point is in .text section
Adding .text section. Offset: 128 (0x80). Length: 175972 (0x2af64) bytes. Adding .text section. Offset: 128 (0x80). Length: 187792 (0x2dd90) bytes.
Adding .stack section. Offset: 176100 (0x2afe4). Length: 16384 (0x4000) bytes. Adding .stack section. Offset: 187920 (0x2de10). Length: 16384 (0x4000) bytes.
Searching for .rel.X sections to add. Searching for .rel.X sections to add.
TBF Header: TBF Header:
version: 2 0x2 version: 2 0x2
@@ -35,16 +35,16 @@ TBF Header:
init_fn_offset: 85 0x55 init_fn_offset: 85 0x55
protected_size: 20 0x14 protected_size: 20 0x14
minimum_ram_size: 107424 0x1A3A0 minimum_ram_size: 107428 0x1A3A4
======================================== ========================================
Board: nrf52840_dongle_dfu Board: nrf52840_dongle_dfu
---------------------------------------- ----------------------------------------
Min RAM size from sections in ELF: 16 bytes Min RAM size from sections in ELF: 20 bytes
Number of writeable flash regions: 0 Number of writeable flash regions: 0
Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes.
Entry point is in .text section Entry point is in .text section
Adding .text section. Offset: 128 (0x80). Length: 175972 (0x2af64) bytes. Adding .text section. Offset: 128 (0x80). Length: 187792 (0x2dd90) bytes.
Adding .stack section. Offset: 176100 (0x2afe4). Length: 16384 (0x4000) bytes. Adding .stack section. Offset: 187920 (0x2de10). Length: 16384 (0x4000) bytes.
Searching for .rel.X sections to add. Searching for .rel.X sections to add.
TBF Header: TBF Header:
version: 2 0x2 version: 2 0x2
@@ -54,16 +54,16 @@ TBF Header:
init_fn_offset: 85 0x55 init_fn_offset: 85 0x55
protected_size: 20 0x14 protected_size: 20 0x14
minimum_ram_size: 107424 0x1A3A0 minimum_ram_size: 107428 0x1A3A4
======================================== ========================================
Board: nrf52840_mdk_dfu Board: nrf52840_mdk_dfu
---------------------------------------- ----------------------------------------
Min RAM size from sections in ELF: 16 bytes Min RAM size from sections in ELF: 20 bytes
Number of writeable flash regions: 0 Number of writeable flash regions: 0
Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes.
Entry point is in .text section Entry point is in .text section
Adding .text section. Offset: 128 (0x80). Length: 175972 (0x2af64) bytes. Adding .text section. Offset: 128 (0x80). Length: 187792 (0x2dd90) bytes.
Adding .stack section. Offset: 176100 (0x2afe4). Length: 16384 (0x4000) bytes. Adding .stack section. Offset: 187920 (0x2de10). Length: 16384 (0x4000) bytes.
Searching for .rel.X sections to add. Searching for .rel.X sections to add.
TBF Header: TBF Header:
version: 2 0x2 version: 2 0x2
@@ -73,4 +73,4 @@ TBF Header:
init_fn_offset: 85 0x55 init_fn_offset: 85 0x55
protected_size: 20 0x14 protected_size: 20 0x14
minimum_ram_size: 107424 0x1A3A0 minimum_ram_size: 107428 0x1A3A4

View File

@@ -1,12 +1,12 @@
======================================== ========================================
Board: nrf52840dk Board: nrf52840dk
---------------------------------------- ----------------------------------------
Min RAM size from sections in ELF: 16 bytes Min RAM size from sections in ELF: 20 bytes
Number of writeable flash regions: 0 Number of writeable flash regions: 0
Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes.
Entry point is in .text section Entry point is in .text section
Adding .text section. Offset: 128 (0x80). Length: 176516 (0x2b184) bytes. Adding .text section. Offset: 128 (0x80). Length: 187736 (0x2dd58) bytes.
Adding .stack section. Offset: 176644 (0x2b204). Length: 16384 (0x4000) bytes. Adding .stack section. Offset: 187864 (0x2ddd8). Length: 16384 (0x4000) bytes.
Searching for .rel.X sections to add. Searching for .rel.X sections to add.
TBF Header: TBF Header:
version: 2 0x2 version: 2 0x2
@@ -16,16 +16,16 @@ TBF Header:
init_fn_offset: 85 0x55 init_fn_offset: 85 0x55
protected_size: 20 0x14 protected_size: 20 0x14
minimum_ram_size: 107424 0x1A3A0 minimum_ram_size: 107428 0x1A3A4
======================================== ========================================
Board: nrf52840_dongle Board: nrf52840_dongle
---------------------------------------- ----------------------------------------
Min RAM size from sections in ELF: 16 bytes Min RAM size from sections in ELF: 20 bytes
Number of writeable flash regions: 0 Number of writeable flash regions: 0
Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes.
Entry point is in .text section Entry point is in .text section
Adding .text section. Offset: 128 (0x80). Length: 176516 (0x2b184) bytes. Adding .text section. Offset: 128 (0x80). Length: 187736 (0x2dd58) bytes.
Adding .stack section. Offset: 176644 (0x2b204). Length: 16384 (0x4000) bytes. Adding .stack section. Offset: 187864 (0x2ddd8). Length: 16384 (0x4000) bytes.
Searching for .rel.X sections to add. Searching for .rel.X sections to add.
TBF Header: TBF Header:
version: 2 0x2 version: 2 0x2
@@ -35,16 +35,16 @@ TBF Header:
init_fn_offset: 85 0x55 init_fn_offset: 85 0x55
protected_size: 20 0x14 protected_size: 20 0x14
minimum_ram_size: 107424 0x1A3A0 minimum_ram_size: 107428 0x1A3A4
======================================== ========================================
Board: nrf52840_dongle_dfu Board: nrf52840_dongle_dfu
---------------------------------------- ----------------------------------------
Min RAM size from sections in ELF: 16 bytes Min RAM size from sections in ELF: 20 bytes
Number of writeable flash regions: 0 Number of writeable flash regions: 0
Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes.
Entry point is in .text section Entry point is in .text section
Adding .text section. Offset: 128 (0x80). Length: 176516 (0x2b184) bytes. Adding .text section. Offset: 128 (0x80). Length: 187736 (0x2dd58) bytes.
Adding .stack section. Offset: 176644 (0x2b204). Length: 16384 (0x4000) bytes. Adding .stack section. Offset: 187864 (0x2ddd8). Length: 16384 (0x4000) bytes.
Searching for .rel.X sections to add. Searching for .rel.X sections to add.
TBF Header: TBF Header:
version: 2 0x2 version: 2 0x2
@@ -54,16 +54,16 @@ TBF Header:
init_fn_offset: 85 0x55 init_fn_offset: 85 0x55
protected_size: 20 0x14 protected_size: 20 0x14
minimum_ram_size: 107424 0x1A3A0 minimum_ram_size: 107428 0x1A3A4
======================================== ========================================
Board: nrf52840_mdk_dfu Board: nrf52840_mdk_dfu
---------------------------------------- ----------------------------------------
Min RAM size from sections in ELF: 16 bytes Min RAM size from sections in ELF: 20 bytes
Number of writeable flash regions: 0 Number of writeable flash regions: 0
Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes. Adding .crt0_header section. Offset: 64 (0x40). Length: 64 (0x40) bytes.
Entry point is in .text section Entry point is in .text section
Adding .text section. Offset: 128 (0x80). Length: 176516 (0x2b184) bytes. Adding .text section. Offset: 128 (0x80). Length: 187736 (0x2dd58) bytes.
Adding .stack section. Offset: 176644 (0x2b204). Length: 16384 (0x4000) bytes. Adding .stack section. Offset: 187864 (0x2ddd8). Length: 16384 (0x4000) bytes.
Searching for .rel.X sections to add. Searching for .rel.X sections to add.
TBF Header: TBF Header:
version: 2 0x2 version: 2 0x2
@@ -73,4 +73,4 @@ TBF Header:
init_fn_offset: 85 0x55 init_fn_offset: 85 0x55
protected_size: 20 0x14 protected_size: 20 0x14
minimum_ram_size: 107424 0x1A3A0 minimum_ram_size: 107428 0x1A3A4

View File

@@ -1 +0,0 @@
nightly-2020-02-03

1
rust-toolchain Symbolic link
View File

@@ -0,0 +1 @@
third_party/libtock-rs/rust-toolchain

74
setup-submodules.sh Executable file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Copyright 2020 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.
# Ensure the script doesn't fail on Github workflows
export TERM=${TERM:-vt100}
done_text="$(tput bold)DONE.$(tput sgr0)"
set -e
# Ensure the submodules are pulled and up-to-date
git submodule update --init
patch_conflict_detected () {
cat <<EOF
This script cannot be run twice without reverting the patches.
To do so, follow these instructions:
1. Commit any changes you want to save.
2. Run the ./reset.sh script to revert all uncommitted changes.
3. Run the ./setup.sh script again.
EOF
exit 1
}
# Copy additional boards to the kernel.
echo -n '[-] Copying additional boards to Tock... '
cp -r boards/* third_party/tock/boards
echo $done_text
# Apply patches to kernel. Do that in a sub-shell.
(
cd third_party/tock/ && \
for p in ../../patches/tock/[0-9][0-9]-*.patch
do
echo -n '[-] Applying patch "'$(basename $p)'"... '
if git apply "$p"
then
echo $done_text
else
patch_conflict_detected
fi
done
)
# Now apply patches to libtock-rs. Do that in a sub-shell.
#
# Commented out as there are not patches at the moment, and the pattern fails in
# that case.
#(
# cd third_party/libtock-rs/ && \
# for p in ../../patches/libtock-rs/[0-9][0-9]-*.patch
# do
# echo -n '[-] Applying patch "'$(basename $p)'"... '
# if git apply "$p"
# then
# echo $done_text
# else
# patch_conflict_detected
# fi
# done
#)

View File

@@ -19,6 +19,9 @@ done_text="$(tput bold)DONE.$(tput sgr0)"
set -e set -e
# Ensure the submodules are pulled and up-to-date, and apply patches
./setup-submodules.sh
# Check that rustup and pip3 are installed # Check that rustup and pip3 are installed
check_command () { check_command () {
if ! which "$1" >/dev/null if ! which "$1" >/dev/null
@@ -30,63 +33,12 @@ check_command () {
check_command rustup " Follow the steps under https://rustup.rs/ to install it." check_command rustup " Follow the steps under https://rustup.rs/ to install it."
check_command pip3 check_command pip3
# Ensure the submodules are pulled and up-to-date
git submodule update --init
patch_conflict_detected () {
cat <<EOF
This script cannot be run twice without reverting the patches.
To do so, follow these instructions:
1. Commit any changes you want to save.
2. Run the ./reset.sh script to revert all uncommitted changes.
3. Run the ./setup.sh script again.
EOF
exit 1
}
# Copy additional boards to the kernel.
echo -n '[-] Copying additional boards to Tock... '
cp -r boards/* third_party/tock/boards
echo $done_text
# Apply patches to kernel. Do that in a sub-shell
(
cd third_party/tock/ && \
for p in ../../patches/tock/[0-9][0-9]-*.patch
do
echo -n '[-] Applying patch "'$(basename $p)'"... '
if git apply "$p"
then
echo $done_text
else
patch_conflict_detected
fi
done
)
# Now apply patches to libtock-rs. Do that in a sub-shell
(
cd third_party/libtock-rs/ && \
for p in ../../patches/libtock-rs/[0-9][0-9]-*.patch
do
echo -n '[-] Applying patch "'$(basename $p)'"... '
if git apply "$p"
then
echo $done_text
else
patch_conflict_detected
fi
done
)
# Ensure we have certificates, keys, etc. so that the tests can run # Ensure we have certificates, keys, etc. so that the tests can run
source tools/gen_key_materials.sh source tools/gen_key_materials.sh
generate_crypto_materials N generate_crypto_materials N
rustup install $(head -n 1 rust-toolchain) rustup install $(head -n 1 rust-toolchain)
pip3 install --user --upgrade 'tockloader==1.4' six intelhex pip3 install --user --upgrade 'tockloader==1.5' six intelhex
rustup target add thumbv7em-none-eabi rustup target add thumbv7em-none-eabi
# Install dependency to create applications. # Install dependency to create applications.

View File

@@ -28,7 +28,7 @@ use alloc::vec::Vec;
use core::fmt::Write; use core::fmt::Write;
use crypto::rng256::Rng256; use crypto::rng256::Rng256;
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use libtock::console::Console; use libtock_drivers::console::Console;
// CTAP specification (version 20190130) section 8.1 // CTAP specification (version 20190130) section 8.1
// TODO: Channel allocation, section 8.1.3? // TODO: Channel allocation, section 8.1.3?

View File

@@ -61,8 +61,8 @@ use crypto::rng256::Rng256;
use crypto::sha256::Sha256; use crypto::sha256::Sha256;
use crypto::Hash256; use crypto::Hash256;
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use libtock::console::Console; use libtock_drivers::console::Console;
use libtock::timer::{Duration, Timestamp}; use libtock_drivers::timer::{Duration, Timestamp};
// This flag enables or disables basic attestation for FIDO2. U2F is unaffected by // This flag enables or disables basic attestation for FIDO2. U2F is unaffected by
// this setting. The basic attestation uses the signing key from key_material.rs // this setting. The basic attestation uses the signing key from key_material.rs
@@ -688,7 +688,7 @@ where
key_id: credential.credential_id.clone(), key_id: credential.credential_id.clone(),
transports: None, // You can set USB as a hint here. transports: None, // You can set USB as a hint here.
}; };
let user = if flags & UV_FLAG != 0 { let user = if (flags & UV_FLAG != 0) && (credential.user_handle.len() > 0) {
Some(PublicKeyCredentialUserEntity { Some(PublicKeyCredentialUserEntity {
user_id: credential.user_handle.clone(), user_id: credential.user_handle.clone(),
user_name: None, user_name: None,

View File

@@ -14,7 +14,7 @@
use super::{Index, Storage, StorageError, StorageResult}; use super::{Index, Storage, StorageError, StorageResult};
use alloc::vec::Vec; use alloc::vec::Vec;
use libtock::syscalls; use libtock_core::syscalls;
const DRIVER_NUMBER: usize = 0x50003; const DRIVER_NUMBER: usize = 0x50003;
@@ -41,16 +41,14 @@ mod memop_nr {
} }
fn get_info(nr: usize, arg: usize) -> StorageResult<usize> { fn get_info(nr: usize, arg: usize) -> StorageResult<usize> {
let code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg) }; let code = syscalls::command(DRIVER_NUMBER, command_nr::GET_INFO, nr, arg);
if code < 0 { code.map_err(|e| StorageError::KernelError {
Err(StorageError::KernelError { code }) code: e.return_code,
} else { })
Ok(code as usize)
}
} }
fn memop(nr: u32, arg: usize) -> StorageResult<usize> { fn memop(nr: u32, arg: usize) -> StorageResult<usize> {
let code = unsafe { syscalls::memop(nr, arg) }; let code = unsafe { syscalls::raw::memop(nr, arg) };
if code < 0 { if code < 0 {
Err(StorageError::KernelError { code }) Err(StorageError::KernelError { code })
} else { } else {
@@ -153,8 +151,9 @@ impl Storage for SyscallStorage {
return Err(StorageError::NotAligned); return Err(StorageError::NotAligned);
} }
let ptr = self.read_slice(index, value.len())?.as_ptr() as usize; let ptr = self.read_slice(index, value.len())?.as_ptr() as usize;
let code = unsafe { let code = unsafe {
syscalls::allow_ptr( syscalls::raw::allow(
DRIVER_NUMBER, DRIVER_NUMBER,
allow_nr::WRITE_SLICE, allow_nr::WRITE_SLICE,
// We rely on the driver not writing to the slice. This should use read-only allow // We rely on the driver not writing to the slice. This should use read-only allow
@@ -166,11 +165,14 @@ impl Storage for SyscallStorage {
if code < 0 { if code < 0 {
return Err(StorageError::KernelError { code }); return Err(StorageError::KernelError { code });
} }
let code =
unsafe { syscalls::command(DRIVER_NUMBER, command_nr::WRITE_SLICE, ptr, value.len()) }; let code = syscalls::command(DRIVER_NUMBER, command_nr::WRITE_SLICE, ptr, value.len());
if code < 0 { if let Err(e) = code {
return Err(StorageError::KernelError { code }); return Err(StorageError::KernelError {
code: e.return_code,
});
} }
Ok(()) Ok(())
} }
@@ -178,9 +180,11 @@ impl Storage for SyscallStorage {
let index = Index { page, byte: 0 }; let index = Index { page, byte: 0 };
let length = self.page_size(); let length = self.page_size();
let ptr = self.read_slice(index, length)?.as_ptr() as usize; let ptr = self.read_slice(index, length)?.as_ptr() as usize;
let code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::ERASE_PAGE, ptr, length) }; let code = syscalls::command(DRIVER_NUMBER, command_nr::ERASE_PAGE, ptr, length);
if code < 0 { if let Err(e) = code {
return Err(StorageError::KernelError { code }); return Err(StorageError::KernelError {
code: e.return_code,
});
} }
Ok(()) Ok(())
} }

View File

@@ -16,6 +16,8 @@
#[macro_use] #[macro_use]
extern crate alloc; extern crate alloc;
extern crate libtock; extern crate lang_items;
extern crate libtock_core;
extern crate libtock_drivers;
pub mod embedded_flash; pub mod embedded_flash;

View File

@@ -22,14 +22,12 @@ extern crate byteorder;
#[cfg(feature = "std")] #[cfg(feature = "std")]
extern crate core; extern crate core;
extern crate ctap2; extern crate ctap2;
extern crate libtock;
extern crate subtle; extern crate subtle;
#[macro_use] #[macro_use]
extern crate cbor; extern crate cbor;
extern crate crypto; extern crate crypto;
mod ctap; mod ctap;
mod usb_ctap_hid;
use core::cell::Cell; use core::cell::Cell;
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
@@ -38,17 +36,18 @@ use crypto::rng256::TockRng256;
use ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket}; use ctap::hid::{ChannelID, CtapHid, KeepaliveStatus, ProcessedPacket};
use ctap::status_code::Ctap2StatusCode; use ctap::status_code::Ctap2StatusCode;
use ctap::CtapState; use ctap::CtapState;
use libtock::buttons; use libtock_core::result::{CommandError, EALREADY};
use libtock::buttons::ButtonState; use libtock_drivers::buttons;
use libtock_drivers::buttons::ButtonState;
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use libtock::console::Console; use libtock_drivers::console::Console;
use libtock::led; use libtock_drivers::led;
use libtock::result::TockValue; use libtock_drivers::result::{FlexUnwrap, TockError};
use libtock::syscalls; use libtock_drivers::timer;
use libtock::timer;
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use libtock::timer::Timer; use libtock_drivers::timer::Timer;
use libtock::timer::{Duration, StopAlarmError, Timestamp}; use libtock_drivers::timer::{Duration, Timestamp};
use libtock_drivers::usb_ctap_hid;
const KEEPALIVE_DELAY_MS: isize = 100; const KEEPALIVE_DELAY_MS: isize = 100;
const KEEPALIVE_DELAY: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS); const KEEPALIVE_DELAY: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS);
@@ -58,7 +57,7 @@ fn main() {
// Setup the timer with a dummy callback (we only care about reading the current time, but the // 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). // 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().unwrap(); let timer = with_callback.init().flex_unwrap();
// Setup USB driver. // Setup USB driver.
if !usb_ctap_hid::setup() { if !usb_ctap_hid::setup() {
@@ -70,7 +69,7 @@ fn main() {
let mut ctap_hid = CtapHid::new(); let mut ctap_hid = CtapHid::new();
let mut led_counter = 0; let mut led_counter = 0;
let mut last_led_increment = timer.get_current_clock(); let mut last_led_increment = timer.get_current_clock().flex_unwrap();
// Main loop. If CTAP1 is used, we register button presses for U2F while receiving and waiting. // 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, // The way TockOS and apps currently interact, callbacks need a yield syscall to execute,
@@ -87,11 +86,11 @@ fn main() {
}; };
}); });
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
let mut buttons = buttons_callback.init().unwrap(); let mut buttons = buttons_callback.init().flex_unwrap();
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
// At the moment, all buttons are accepted. You can customize your setup here. // At the moment, all buttons are accepted. You can customize your setup here.
for mut button in &mut buttons { for mut button in &mut buttons {
button.enable().unwrap(); button.enable().flex_unwrap();
} }
let mut pkt_request = [0; 64]; let mut pkt_request = [0; 64];
@@ -105,7 +104,7 @@ fn main() {
None => false, None => false,
}; };
let now = timer.get_current_clock(); let now = timer.get_current_clock().flex_unwrap();
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
{ {
if button_touched.get() { if button_touched.get() {
@@ -115,7 +114,7 @@ fn main() {
// Heavy computation mostly follows a registered touch luckily. Unregistering // Heavy computation mostly follows a registered touch luckily. Unregistering
// callbacks is important to not clash with those from check_user_presence. // callbacks is important to not clash with those from check_user_presence.
for mut button in &mut buttons { for mut button in &mut buttons {
button.disable().unwrap(); button.disable().flex_unwrap();
} }
drop(buttons); drop(buttons);
drop(buttons_callback); drop(buttons_callback);
@@ -153,7 +152,7 @@ fn main() {
} }
} }
let now = timer.get_current_clock(); let now = timer.get_current_clock().flex_unwrap();
if let Some(wait_duration) = now.wrapping_sub(last_led_increment) { if let Some(wait_duration) = now.wrapping_sub(last_led_increment) {
if wait_duration > KEEPALIVE_DELAY { if wait_duration > KEEPALIVE_DELAY {
// Loops quickly when waiting for U2F user presence, so the next LED blink // Loops quickly when waiting for U2F user presence, so the next LED blink
@@ -188,8 +187,8 @@ fn main() {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
fn print_packet_notice(notice_text: &str, timer: &Timer) { fn print_packet_notice(notice_text: &str, timer: &Timer) {
let now_us = let now = timer.get_current_clock().flex_unwrap();
(Timestamp::<f64>::from_clock_value(timer.get_current_clock()).ms() * 1000.0) as u64; let now_us = (Timestamp::<f64>::from_clock_value(now).ms() * 1000.0) as u64;
writeln!( writeln!(
Console::new(), Console::new(),
"{} at {}.{:06} s", "{} at {}.{:06} s",
@@ -264,17 +263,17 @@ fn send_keepalive_up_needed(
Ok(()) Ok(())
} }
fn blink_leds(pattern_seed: isize) { fn blink_leds(pattern_seed: usize) {
for l in 0..led::count() { for l in 0..led::count().flex_unwrap() {
if (pattern_seed ^ l).count_ones() & 1 != 0 { if (pattern_seed ^ l).count_ones() & 1 != 0 {
led::get(l).unwrap().on(); led::get(l).flex_unwrap().on().flex_unwrap();
} else { } else {
led::get(l).unwrap().off(); led::get(l).flex_unwrap().off().flex_unwrap();
} }
} }
} }
fn wink_leds(pattern_seed: isize) { fn wink_leds(pattern_seed: usize) {
// This generates a "snake" pattern circling through the LEDs. // This generates a "snake" pattern circling through the LEDs.
// Fox example with 4 LEDs the sequence of lit LEDs will be the following. // Fox example with 4 LEDs the sequence of lit LEDs will be the following.
// 0 1 2 3 // 0 1 2 3
@@ -287,7 +286,7 @@ fn wink_leds(pattern_seed: isize) {
// * * // * *
// * * * // * * *
// * * // * *
let count = led::count(); let count = led::count().flex_unwrap();
let a = (pattern_seed / 2) % count; let a = (pattern_seed / 2) % count;
let b = ((pattern_seed + 1) / 2) % count; let b = ((pattern_seed + 1) / 2) % count;
let c = ((pattern_seed + 3) / 2) % count; let c = ((pattern_seed + 3) / 2) % count;
@@ -300,22 +299,22 @@ fn wink_leds(pattern_seed: isize) {
_ => l, _ => l,
}; };
if k == a || k == b || k == c { if k == a || k == b || k == c {
led::get(l).unwrap().on(); led::get(l).flex_unwrap().on().flex_unwrap();
} else { } else {
led::get(l).unwrap().off(); led::get(l).flex_unwrap().off().flex_unwrap();
} }
} }
} }
fn switch_off_leds() { fn switch_off_leds() {
for l in 0..led::count() { for l in 0..led::count().flex_unwrap() {
led::get(l).unwrap().off(); led::get(l).flex_unwrap().off().flex_unwrap();
} }
} }
fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> { fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> {
// The timeout is N times the keepalive delay. // The timeout is N times the keepalive delay.
const TIMEOUT_ITERATIONS: isize = ctap::TOUCH_TIMEOUT_MS / KEEPALIVE_DELAY_MS; const TIMEOUT_ITERATIONS: usize = ctap::TOUCH_TIMEOUT_MS as usize / KEEPALIVE_DELAY_MS as usize;
// First, send a keep-alive packet to notify that the keep-alive status has changed. // First, send a keep-alive packet to notify that the keep-alive status has changed.
send_keepalive_up_needed(cid, KEEPALIVE_DELAY)?; send_keepalive_up_needed(cid, KEEPALIVE_DELAY)?;
@@ -328,10 +327,10 @@ fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> {
ButtonState::Released => (), ButtonState::Released => (),
}; };
}); });
let mut buttons = buttons_callback.init().unwrap(); let mut buttons = buttons_callback.init().flex_unwrap();
// At the moment, all buttons are accepted. You can customize your setup here. // At the moment, all buttons are accepted. You can customize your setup here.
for mut button in &mut buttons { for mut button in &mut buttons {
button.enable().unwrap(); button.enable().flex_unwrap();
} }
let mut keepalive_response = Ok(()); let mut keepalive_response = Ok(());
@@ -343,19 +342,25 @@ fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> {
let mut keepalive_callback = timer::with_callback(|_, _| { let mut keepalive_callback = timer::with_callback(|_, _| {
keepalive_expired.set(true); keepalive_expired.set(true);
}); });
let mut keepalive = keepalive_callback.init().unwrap(); let mut keepalive = keepalive_callback.init().flex_unwrap();
let keepalive_alarm = keepalive.set_alarm(KEEPALIVE_DELAY).unwrap(); let keepalive_alarm = keepalive.set_alarm(KEEPALIVE_DELAY).flex_unwrap();
// Wait for a button touch or an alarm. // Wait for a button touch or an alarm.
syscalls::yieldk_for(|| button_touched.get() || keepalive_expired.get()); libtock_drivers::util::yieldk_for(|| button_touched.get() || keepalive_expired.get());
// Cleanup alarm callback. // Cleanup alarm callback.
match keepalive.stop_alarm(keepalive_alarm) { match keepalive.stop_alarm(keepalive_alarm) {
Ok(()) => (), Ok(()) => (),
Err(TockValue::Expected(StopAlarmError::AlreadyDisabled)) => { Err(TockError::Command(CommandError {
assert!(keepalive_expired.get()) 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>");
} }
Err(e) => panic!("Unexpected error when stopping alarm: {:?}", e),
} }
// TODO: this may take arbitrary time. The keepalive_delay should be adjusted accordingly, // TODO: this may take arbitrary time. The keepalive_delay should be adjusted accordingly,
@@ -374,7 +379,7 @@ fn check_user_presence(cid: ChannelID) -> Result<(), Ctap2StatusCode> {
// Cleanup button callbacks. // Cleanup button callbacks.
for mut button in &mut buttons { for mut button in &mut buttons {
button.disable().unwrap(); button.disable().flex_unwrap();
} }
// Returns whether the user was present. // Returns whether the user was present.

19
third_party/lang-items/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,19 @@
[package]
name = "lang_items"
version = "0.1.0"
authors = [
"Tock Project Developers <tock-dev@googlegroups.com>",
"Guillaume Endignoux <guillaumee@google.com>",
]
license = "MIT/Apache-2.0"
edition = "2018"
[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.8.1", default-features = false }
[features]
debug_allocations = []
panic_console = []
std = []

202
third_party/lang-items/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

25
third_party/lang-items/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2016 The Tock Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

109
third_party/lang-items/src/allocator.rs vendored Normal file
View File

@@ -0,0 +1,109 @@
use crate::util;
use core::alloc::GlobalAlloc;
use core::alloc::Layout;
#[cfg(any(feature = "debug_allocations", feature = "panic_console"))]
use core::fmt::Write;
use core::ptr;
use core::ptr::NonNull;
#[cfg(feature = "debug_allocations")]
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 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, app_heap_size);
}
// With the "debug_allocations" feature, we use `AtomicUsize` to store the
// statistics because:
// - it is `Sync`, so we can use it in a static object (the allocator),
// - it implements interior mutability, so we can use it in the allocator
// methods (that take an immutable `&self` reference).
struct TockAllocator {
#[cfg(feature = "debug_allocations")]
count: AtomicUsize,
#[cfg(feature = "debug_allocations")]
size: AtomicUsize,
}
impl TockAllocator {
const fn new() -> TockAllocator {
TockAllocator {
#[cfg(feature = "debug_allocations")]
count: AtomicUsize::new(0),
#[cfg(feature = "debug_allocations")]
size: AtomicUsize::new(0),
}
}
}
unsafe impl GlobalAlloc for TockAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
let ptr = HEAP
.allocate_first_fit(layout)
.ok()
.map_or(ptr::null_mut(), NonNull::as_ptr);
#[cfg(feature = "debug_allocations")]
{
self.count.fetch_add(1, atomic::Ordering::SeqCst);
self.size.fetch_add(layout.size(), atomic::Ordering::SeqCst);
writeln!(
Console::new(),
"alloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
layout.size(),
layout.align(),
ptr,
self.count.load(atomic::Ordering::SeqCst),
self.size.load(atomic::Ordering::SeqCst)
)
.unwrap();
}
ptr
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
#[cfg(feature = "debug_allocations")]
{
self.count.fetch_sub(1, atomic::Ordering::SeqCst);
self.size.fetch_sub(layout.size(), atomic::Ordering::SeqCst);
writeln!(
Console::new(),
"dealloc[{}, {}] = {:?} ({} ptrs, {} bytes)",
layout.size(),
layout.align(),
ptr,
self.count.load(atomic::Ordering::SeqCst),
self.size.load(atomic::Ordering::SeqCst)
)
.unwrap();
}
HEAP.deallocate(NonNull::new_unchecked(ptr), layout)
}
}
#[cfg(any(target_arch = "arm", target_arch = "riscv32"))]
#[global_allocator]
static ALLOCATOR: TockAllocator = TockAllocator::new();
#[alloc_error_handler]
unsafe fn alloc_error_handler(_layout: Layout) -> ! {
util::signal_oom();
util::signal_panic();
#[cfg(feature = "panic_console")]
{
writeln!(Console::new(), "Couldn't allocate: {:?}", _layout).ok();
// Force the kernel to report the panic cause, by reading an invalid address.
// The memory protection unit should be setup by the Tock kernel to prevent apps from accessing
// address zero.
core::ptr::read_volatile(0 as *const usize);
}
util::cycle_leds()
}

9
third_party/lang-items/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,9 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![feature(alloc_error_handler)]
#[cfg(not(feature = "std"))]
mod allocator;
#[cfg(not(feature = "std"))]
mod panic_handler;
#[cfg(not(feature = "std"))]
mod util;

View File

@@ -0,0 +1,26 @@
use crate::util;
#[cfg(feature = "panic_console")]
use core::fmt::Write;
use core::panic::PanicInfo;
#[cfg(feature = "panic_console")]
use libtock_drivers::console::Console;
#[panic_handler]
fn panic_handler(_info: &PanicInfo) -> ! {
util::signal_panic();
#[cfg(feature = "panic_console")]
{
let mut console = Console::new();
writeln!(console, "{}", _info).ok();
console.flush();
// Force the kernel to report the panic cause, by reading an invalid address.
// The memory protection unit should be setup by the Tock kernel to prevent apps from accessing
// address zero.
unsafe {
core::ptr::read_volatile(0 as *const usize);
}
}
util::flash_all_leds();
}

46
third_party/lang-items/src/util.rs vendored Normal file
View File

@@ -0,0 +1,46 @@
use libtock_drivers::led;
use libtock_drivers::timer::{self, Duration};
// Signal a panic using the LowLevelDebug capsule (if available).
pub fn signal_panic() {
let _ = libtock_core::syscalls::command1_insecure(8, 1, 1);
}
// 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));
}
}
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();
}
}
}
}

16
third_party/libtock-drivers/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,16 @@
[package]
name = "libtock_drivers"
version = "0.1.0"
authors = [
"Tock Project Developers <tock-dev@googlegroups.com>",
"Guillaume Endignoux <guillaumee@google.com>",
]
license = "MIT/Apache-2.0"
edition = "2018"
[dependencies]
libtock_core = { path = "../../third_party/libtock-rs/core" }
[features]
debug_ctap = []
verbose_usb = ["debug_ctap"]

View File

@@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

25
third_party/libtock-drivers/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (c) 2016 The Tock Project Developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

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

View File

@@ -0,0 +1,113 @@
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;
}
const BUFFER_SIZE: usize = 1024;
pub struct Console {
allow_buffer: [u8; BUFFER_SIZE],
count_pending: usize,
}
impl Console {
pub fn new() -> Console {
Console {
allow_buffer: [0; BUFFER_SIZE],
count_pending: 0,
}
}
fn is_empty(&self) -> bool {
self.count_pending == 0
}
fn is_full(&self) -> bool {
self.allow_buffer.len() == self.count_pending
}
fn available_len(&self) -> usize {
self.allow_buffer.len() - self.count_pending
}
pub fn write<S: AsRef<[u8]>>(&mut self, text: S) {
let mut not_written_yet = text.as_ref();
while !not_written_yet.is_empty() {
let num_bytes_to_print = self.available_len().min(not_written_yet.len());
self.allow_buffer[self.count_pending..(self.count_pending + num_bytes_to_print)]
.copy_from_slice(&not_written_yet[..num_bytes_to_print]);
self.count_pending += num_bytes_to_print;
if self.is_full() {
self.flush();
}
not_written_yet = &not_written_yet[num_bytes_to_print..];
}
}
pub fn flush(&mut self) {
if self.is_empty() {
// Don't trigger any syscall if the buffer is empty.
return;
}
let count = self.count_pending;
// Clear the buffer even in case of error, to avoid an infinite loop.
self.count_pending = 0;
let result = syscalls::allow(
DRIVER_NUMBER,
allow_nr::SHARE_BUFFER,
&mut self.allow_buffer[..count],
);
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(())
}
}

84
third_party/libtock-drivers/src/led.rs vendored Normal file
View File

@@ -0,0 +1,84 @@
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
}
}
}

10
third_party/libtock-drivers/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,10 @@
#![no_std]
pub mod buttons;
pub mod console;
pub mod led;
pub mod result;
pub mod rng;
pub mod timer;
pub mod usb_ctap_hid;
pub mod util;

View File

@@ -0,0 +1,133 @@
use core::fmt;
pub use libtock_core::result::*;
pub type TockResult<T> = Result<T, TockError>;
// We sometimes need to handle errors in a `TockResult` by calling `unwrap`. However,
// `Result::unwrap` requires that the error type implements `core::fmt::Debug`. Under the hood,
// this requires dynamic dispatch, which has non-negligible overhead on code size. Therefore errors
// don't derive from `Debug` in libtock-rs.
//
// Instead one can call `.ok().unwrap()` which relies on `Option::unwrap` and doesn't require any
// debugging of the error type.
//
// This trait allows to flexibly use `Result::unwrap` or `Option::unwrap` and is configured to do
// so depending on the `debug_ctap` feature.
pub trait FlexUnwrap<T> {
fn flex_unwrap(self) -> T;
}
impl<T> FlexUnwrap<T> for TockResult<T> {
#[cfg(feature = "debug_ctap")]
fn flex_unwrap(self) -> T {
self.unwrap()
}
#[cfg(not(feature = "debug_ctap"))]
fn flex_unwrap(self) -> T {
self.ok().unwrap()
}
}
#[derive(Copy, Clone)]
pub enum TockError {
Subscribe(SubscribeError),
Command(CommandError),
Allow(AllowError),
Format,
Other(OtherError),
}
#[cfg(feature = "debug_ctap")]
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::Format => f.write_str("TockError::Format"),
TockError::Other(e) => e.fmt(f),
}
}
}
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 {
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
}
}
#[derive(Copy, Clone)]
#[cfg_attr(feature = "debug_ctap", derive(Debug))]
pub enum OtherError {
ButtonsDriverInvalidState,
GpioDriverInvalidState,
TimerDriverDurationOutOfRange,
TimerDriverErroneousClockFrequency,
DriversAlreadyTaken,
OutOfRange,
}
impl From<OtherError> for TockError {
fn from(other: OtherError) -> Self {
TockError::Other(other)
}
}
pub struct OutOfRangeError;
impl From<OutOfRangeError> for TockError {
fn from(_: OutOfRangeError) -> Self {
TockError::Other(OtherError::OutOfRange)
}
}

45
third_party/libtock-drivers/src/rng.rs vendored Normal file
View File

@@ -0,0 +1,45 @@
use crate::util;
use core::cell::Cell;
use libtock_core::{callback, syscalls};
const DRIVER_NUMBER: usize = 0x40001;
mod command_nr {
pub const REQUEST_RNG: usize = 1;
}
mod subscribe_nr {
pub const BUFFER_FILLED: usize = 0;
}
mod allow_nr {
pub const SHARE_BUFFER: usize = 0;
}
pub fn fill_buffer(buf: &mut [u8]) -> bool {
let buf_len = buf.len();
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::SHARE_BUFFER, buf);
if result.is_err() {
return false;
}
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());
return true;
}

334
third_party/libtock-drivers/src/timer.rs vendored Normal file
View File

@@ -0,0 +1,334 @@
use crate::result::{FlexUnwrap, OtherError, TockError, TockResult};
use crate::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;
const DRIVER_NUMBER: usize = 0x00000;
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;
}
mod subscribe_nr {
pub const SUBSCRIBE_CALLBACK: usize = 0;
}
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),
}
}
pub fn with_callback<CB>(callback: CB) -> WithCallback<'static, CB> {
WithCallback {
callback,
clock_frequency: ClockFrequency { hz: 0 },
phantom: PhantomData,
}
}
pub struct WithCallback<'a, CB> {
callback: CB,
clock_frequency: ClockFrequency,
phantom: PhantomData<&'a mut ()>,
}
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 },
);
}
}
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)?;
let clock_frequency =
syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_FREQUENCY, 0, 0)?;
if clock_frequency == 0 {
return Err(OtherError::TimerDriverErroneousClockFrequency.into());
}
let clock_frequency = ClockFrequency {
hz: clock_frequency,
};
let subscription = syscalls::subscribe::<TimerEventConsumer, _>(
DRIVER_NUMBER,
subscribe_nr::SUBSCRIBE_CALLBACK,
self,
)?;
Ok(Timer {
num_notifications,
clock_frequency,
subscription,
})
}
}
pub struct Timer<'a> {
num_notifications: usize,
clock_frequency: ClockFrequency,
#[allow(dead_code)] // Used in drop
subscription: CallbackSubscription<'a>,
}
impl<'a> Timer<'a> {
pub fn num_notifications(&self) -> usize {
self.num_notifications
}
pub fn clock_frequency(&self) -> ClockFrequency {
self.clock_frequency
}
pub fn get_current_clock(&self) -> TockResult<ClockValue> {
Ok(ClockValue {
num_ticks: syscalls::command(DRIVER_NUMBER, command_nr::GET_CLOCK_VALUE, 0, 0)?
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)?;
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;
let alarm_id = syscalls::command(DRIVER_NUMBER, command_nr::SET_ALARM, alarm_instant, 0)?;
Ok(Alarm { alarm_id })
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct ClockFrequency {
hz: usize,
}
impl ClockFrequency {
pub fn hz(&self) -> usize {
self.hz
}
}
#[derive(Copy, Clone, Debug)]
pub struct ClockValue {
num_ticks: isize,
clock_frequency: ClockFrequency,
}
impl ClockValue {
pub const fn new(num_ticks: isize, clock_hz: usize) -> ClockValue {
ClockValue {
num_ticks,
clock_frequency: ClockFrequency { hz: clock_hz },
}
}
pub fn num_ticks(&self) -> isize {
self.num_ticks
}
// Computes (value * factor) / divisor, even when value * factor >= isize::MAX.
fn scale_int(value: isize, factor: isize, divisor: isize) -> isize {
// As long as isize is not i64, this should be fine. If not, this is an alternative:
// factor * (value / divisor) + ((value % divisor) * factor) / divisor
((value as i64 * factor as i64) / divisor as i64) as isize
}
pub fn ms(&self) -> isize {
ClockValue::scale_int(self.num_ticks, 1000, self.clock_frequency.hz() as isize)
}
pub fn ms_f64(&self) -> f64 {
1000.0 * (self.num_ticks as f64) / (self.clock_frequency.hz() 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 {
num_ticks: self.num_ticks.wrapping_add(duration_ticks),
clock_frequency: self.clock_frequency,
}
}
pub fn wrapping_sub(self, other: ClockValue) -> Option<Duration<isize>> {
if self.clock_frequency == other.clock_frequency {
let clock_duration = ClockValue {
num_ticks: self.num_ticks - other.num_ticks,
clock_frequency: self.clock_frequency,
};
Some(Duration::from_ms(clock_duration.ms()))
} else {
None
}
}
}
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,
}
impl<T> Duration<T> {
pub const fn from_ms(ms: T) -> Duration<T> {
Duration { ms }
}
}
impl<T> Duration<T>
where
T: Copy,
{
pub fn ms(&self) -> T {
self.ms
}
}
impl<T> Sub for Duration<T>
where
T: Sub<Output = T>,
{
type Output = Duration<T>;
fn sub(self, other: Duration<T>) -> Duration<T> {
Duration {
ms: self.ms - other.ms,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Timestamp<T> {
ms: T,
}
impl<T> Timestamp<T> {
pub const fn from_ms(ms: T) -> Timestamp<T> {
Timestamp { ms }
}
}
impl<T> Timestamp<T>
where
T: Copy,
{
pub fn ms(&self) -> T {
self.ms
}
}
impl Timestamp<isize> {
pub fn from_clock_value(value: ClockValue) -> Timestamp<isize> {
Timestamp { ms: value.ms() }
}
}
impl Timestamp<f64> {
pub fn from_clock_value(value: ClockValue) -> Timestamp<f64> {
Timestamp { ms: value.ms_f64() }
}
}
impl<T> Sub for Timestamp<T>
where
T: Sub<Output = T>,
{
type Output = Duration<T>;
fn sub(self, other: Timestamp<T>) -> Duration<T> {
Duration::from_ms(self.ms - other.ms)
}
}
impl<T> Add<Duration<T>> for Timestamp<T>
where
T: Copy + Add<Output = T>,
{
type Output = Timestamp<T>;
fn add(self, duration: Duration<T>) -> Timestamp<T> {
Timestamp {
ms: self.ms + duration.ms(),
}
}
}
impl<T> AddAssign<Duration<T>> for Timestamp<T>
where
T: Copy + AddAssign,
{
fn add_assign(&mut self, duration: Duration<T>) {
self.ms += duration.ms();
}
}

View File

@@ -12,16 +12,17 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
#[cfg(feature = "debug_ctap")]
use crate::console::Console;
use crate::result::TockError;
use crate::timer;
use crate::timer::Duration;
use crate::util;
use core::cell::Cell; use core::cell::Cell;
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use core::fmt::Write; use core::fmt::Write;
#[cfg(feature = "debug_ctap")] use libtock_core::result::{CommandError, EALREADY, EBUSY, SUCCESS};
use libtock::console::Console; use libtock_core::{callback, syscalls};
use libtock::result::TockValue;
use libtock::result::{EALREADY, EBUSY, SUCCESS};
use libtock::syscalls;
use libtock::timer;
use libtock::timer::{Duration, StopAlarmError};
const DRIVER_NUMBER: usize = 0x20009; const DRIVER_NUMBER: usize = 0x20009;
@@ -51,13 +52,13 @@ mod allow_nr {
} }
pub fn setup() -> bool { pub fn setup() -> bool {
let result = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0) }; let result = syscalls::command(DRIVER_NUMBER, command_nr::CHECK, 0, 0);
if result != 0 { if result.is_err() {
return false; return false;
} }
let result = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::CONNECT, 0, 0) }; let result = syscalls::command(DRIVER_NUMBER, command_nr::CONNECT, 0, 0);
if result != 0 { if result.is_err() {
return false; return false;
} }
@@ -72,18 +73,22 @@ pub fn recv(buf: &mut [u8; 64]) -> bool {
} }
let done = Cell::new(false); let done = Cell::new(false);
let mut alarm = |_, _, _| done.set(true); let mut alarm = || done.set(true);
let subscription = syscalls::subscribe(DRIVER_NUMBER, subscribe_nr::RECEIVE, &mut alarm); let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::RECEIVE,
&mut alarm,
);
if subscription.is_err() { if subscription.is_err() {
return false; return false;
} }
let result_code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0) }; let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0);
if result_code != 0 { if result_code.is_err() {
return false; return false;
} }
syscalls::yieldk_for(|| done.get()); util::yieldk_for(|| done.get());
true true
} }
@@ -95,18 +100,22 @@ pub fn send(buf: &mut [u8; 64]) -> bool {
} }
let done = Cell::new(false); let done = Cell::new(false);
let mut alarm = |_, _, _| done.set(true); let mut alarm = || done.set(true);
let subscription = syscalls::subscribe(DRIVER_NUMBER, subscribe_nr::TRANSMIT, &mut alarm); let subscription = syscalls::subscribe::<callback::Identity0Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::TRANSMIT,
&mut alarm,
);
if subscription.is_err() { if subscription.is_err() {
return false; return false;
} }
let result_code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT, 0, 0) }; let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT, 0, 0);
if result_code != 0 { if result_code.is_err() {
return false; return false;
} }
syscalls::yieldk_for(|| done.get()); util::yieldk_for(|| done.get());
true true
} }
@@ -135,7 +144,7 @@ pub fn send_or_recv(buf: &mut [u8; 64]) -> SendOrRecvStatus {
} }
let status = Cell::new(None); let status = Cell::new(None);
let mut alarm = |direction, _, _| { let mut alarm = |direction| {
status.set(Some(match direction { status.set(Some(match direction {
subscribe_nr::callback_status::TRANSMITTED => SendOrRecvStatus::Sent, subscribe_nr::callback_status::TRANSMITTED => SendOrRecvStatus::Sent,
subscribe_nr::callback_status::RECEIVED => SendOrRecvStatus::Received, subscribe_nr::callback_status::RECEIVED => SendOrRecvStatus::Received,
@@ -144,19 +153,21 @@ pub fn send_or_recv(buf: &mut [u8; 64]) -> SendOrRecvStatus {
})); }));
}; };
let subscription = let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
syscalls::subscribe(DRIVER_NUMBER, subscribe_nr::TRANSMIT_OR_RECEIVE, &mut alarm); DRIVER_NUMBER,
subscribe_nr::TRANSMIT_OR_RECEIVE,
&mut alarm,
);
if subscription.is_err() { if subscription.is_err() {
return SendOrRecvStatus::Error; return SendOrRecvStatus::Error;
} }
let result_code = let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT_OR_RECEIVE, 0, 0);
unsafe { syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT_OR_RECEIVE, 0, 0) }; if result_code.is_err() {
if result_code != 0 {
return SendOrRecvStatus::Error; return SendOrRecvStatus::Error;
} }
syscalls::yieldk_for(|| status.get().is_some()); util::yieldk_for(|| status.get().is_some());
status.get().unwrap() status.get().unwrap()
} }
@@ -166,7 +177,7 @@ pub fn recv_with_timeout(
buf: &mut [u8; 64], buf: &mut [u8; 64],
timeout_delay: Duration<isize>, timeout_delay: Duration<isize>,
) -> Option<SendOrRecvStatus> { ) -> Option<SendOrRecvStatus> {
#[cfg(feature = "verbose")] #[cfg(feature = "verbose_usb")]
writeln!( writeln!(
Console::new(), Console::new(),
"Receiving packet with timeout of {}ms", "Receiving packet with timeout of {}ms",
@@ -176,7 +187,7 @@ pub fn recv_with_timeout(
let result = recv_with_timeout_detail(buf, timeout_delay); let result = recv_with_timeout_detail(buf, timeout_delay);
#[cfg(feature = "verbose")] #[cfg(feature = "verbose_usb")]
{ {
if let Some(SendOrRecvStatus::Received) = result { if let Some(SendOrRecvStatus::Received) = result {
writeln!(Console::new(), "Received packet = {:02x?}", buf as &[u8]).unwrap(); writeln!(Console::new(), "Received packet = {:02x?}", buf as &[u8]).unwrap();
@@ -192,7 +203,7 @@ pub fn send_or_recv_with_timeout(
buf: &mut [u8; 64], buf: &mut [u8; 64],
timeout_delay: Duration<isize>, timeout_delay: Duration<isize>,
) -> Option<SendOrRecvStatus> { ) -> Option<SendOrRecvStatus> {
#[cfg(feature = "verbose")] #[cfg(feature = "verbose_usb")]
writeln!( writeln!(
Console::new(), Console::new(),
"Sending packet with timeout of {}ms = {:02x?}", "Sending packet with timeout of {}ms = {:02x?}",
@@ -203,7 +214,7 @@ pub fn send_or_recv_with_timeout(
let result = send_or_recv_with_timeout_detail(buf, timeout_delay); let result = send_or_recv_with_timeout_detail(buf, timeout_delay);
#[cfg(feature = "verbose")] #[cfg(feature = "verbose_usb")]
{ {
if let Some(SendOrRecvStatus::Received) = result { if let Some(SendOrRecvStatus::Received) = result {
writeln!(Console::new(), "Received packet = {:02x?}", buf as &[u8]).unwrap(); writeln!(Console::new(), "Received packet = {:02x?}", buf as &[u8]).unwrap();
@@ -223,7 +234,7 @@ fn recv_with_timeout_detail(
} }
let status = Cell::new(None); let status = Cell::new(None);
let mut alarm = |direction, _, _| { let mut alarm = |direction| {
status.set(Some(match direction { status.set(Some(match direction {
subscribe_nr::callback_status::RECEIVED => SendOrRecvStatus::Received, subscribe_nr::callback_status::RECEIVED => SendOrRecvStatus::Received,
// Unknown direction or "transmitted" sent by the kernel. // Unknown direction or "transmitted" sent by the kernel.
@@ -231,7 +242,11 @@ fn recv_with_timeout_detail(
})); }));
}; };
let subscription = syscalls::subscribe(DRIVER_NUMBER, subscribe_nr::RECEIVE, &mut alarm); let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
DRIVER_NUMBER,
subscribe_nr::RECEIVE,
&mut alarm,
);
if subscription.is_err() { if subscription.is_err() {
return Some(SendOrRecvStatus::Error); return Some(SendOrRecvStatus::Error);
} }
@@ -251,17 +266,20 @@ fn recv_with_timeout_detail(
}; };
// Trigger USB reception. // Trigger USB reception.
let result_code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0) }; let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0);
if result_code != 0 { if result_code.is_err() {
return Some(SendOrRecvStatus::Error); return Some(SendOrRecvStatus::Error);
} }
syscalls::yieldk_for(|| status.get().is_some() || timeout_expired.get()); util::yieldk_for(|| status.get().is_some() || timeout_expired.get());
// Cleanup alarm callback. // Cleanup alarm callback.
match timeout.stop_alarm(timeout_alarm) { match timeout.stop_alarm(timeout_alarm) {
Ok(()) => (), Ok(()) => (),
Err(TockValue::Expected(StopAlarmError::AlreadyDisabled)) => { Err(TockError::Command(CommandError {
return_code: EALREADY,
..
})) => {
if !timeout_expired.get() { if !timeout_expired.get() {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
writeln!( writeln!(
@@ -271,14 +289,20 @@ fn recv_with_timeout_detail(
.unwrap(); .unwrap();
} }
} }
Err(e) => panic!("Unexpected error when stopping alarm: {:?}", e), 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. // Cancel USB transaction if necessary.
if status.get().is_none() { if status.get().is_none() {
#[cfg(feature = "verbose")] #[cfg(feature = "verbose_usb")]
writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap(); writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap();
let result_code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) }; let result_code =
unsafe { syscalls::raw::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) };
match result_code { match result_code {
// - SUCCESS means that we successfully cancelled the transaction. // - SUCCESS means that we successfully cancelled the transaction.
// - EALREADY means that the transaction was already completed. // - EALREADY means that the transaction was already completed.
@@ -310,7 +334,7 @@ fn send_or_recv_with_timeout_detail(
} }
let status = Cell::new(None); let status = Cell::new(None);
let mut alarm = |direction, _, _| { let mut alarm = |direction| {
status.set(Some(match direction { status.set(Some(match direction {
subscribe_nr::callback_status::TRANSMITTED => SendOrRecvStatus::Sent, subscribe_nr::callback_status::TRANSMITTED => SendOrRecvStatus::Sent,
subscribe_nr::callback_status::RECEIVED => SendOrRecvStatus::Received, subscribe_nr::callback_status::RECEIVED => SendOrRecvStatus::Received,
@@ -319,8 +343,11 @@ fn send_or_recv_with_timeout_detail(
})); }));
}; };
let subscription = let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
syscalls::subscribe(DRIVER_NUMBER, subscribe_nr::TRANSMIT_OR_RECEIVE, &mut alarm); DRIVER_NUMBER,
subscribe_nr::TRANSMIT_OR_RECEIVE,
&mut alarm,
);
if subscription.is_err() { if subscription.is_err() {
return Some(SendOrRecvStatus::Error); return Some(SendOrRecvStatus::Error);
} }
@@ -340,18 +367,20 @@ fn send_or_recv_with_timeout_detail(
}; };
// Trigger USB transmission. // Trigger USB transmission.
let result_code = let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT_OR_RECEIVE, 0, 0);
unsafe { syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT_OR_RECEIVE, 0, 0) }; if result_code.is_err() {
if result_code != 0 {
return Some(SendOrRecvStatus::Error); return Some(SendOrRecvStatus::Error);
} }
syscalls::yieldk_for(|| status.get().is_some() || timeout_expired.get()); util::yieldk_for(|| status.get().is_some() || timeout_expired.get());
// Cleanup alarm callback. // Cleanup alarm callback.
match timeout.stop_alarm(timeout_alarm) { match timeout.stop_alarm(timeout_alarm) {
Ok(()) => (), Ok(()) => (),
Err(TockValue::Expected(StopAlarmError::AlreadyDisabled)) => { Err(TockError::Command(CommandError {
return_code: EALREADY,
..
})) => {
if !timeout_expired.get() { if !timeout_expired.get() {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
writeln!( writeln!(
@@ -361,14 +390,20 @@ fn send_or_recv_with_timeout_detail(
.unwrap(); .unwrap();
} }
} }
Err(e) => panic!("Unexpected error when stopping alarm: {:?}", e), 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. // Cancel USB transaction if necessary.
if status.get().is_none() { if status.get().is_none() {
#[cfg(feature = "verbose")] #[cfg(feature = "verbose_usb")]
writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap(); writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap();
let result_code = unsafe { syscalls::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) }; let result_code =
unsafe { syscalls::raw::command(DRIVER_NUMBER, command_nr::CANCEL, 0, 0) };
match result_code { match result_code {
// - SUCCESS means that we successfully cancelled the transaction. // - SUCCESS means that we successfully cancelled the transaction.
// - EALREADY means that the transaction was already completed. // - EALREADY means that the transaction was already completed.

View File

@@ -0,0 +1,9 @@
use libtock_core::syscalls;
pub fn yieldk_for<F: Fn() -> bool>(cond: F) {
while !cond() {
unsafe {
syscalls::raw::yieldk();
}
}
}