Clock trait (#596)
* adds generic Env parameters * adds Clock type to Env * use new Clock * TockTimer improvements * new Clock interface * addressed comments * renames constants to milliseconds, other style fixes * removes all cargo fmt artifacts
This commit is contained in:
5
src/env/mod.rs
vendored
5
src/env/mod.rs
vendored
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Google LLC
|
||||
// Copyright 2022-2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
use crate::api::attestation_store::AttestationStore;
|
||||
use crate::api::clock::Clock;
|
||||
use crate::api::connection::HidConnection;
|
||||
use crate::api::customization::Customization;
|
||||
use crate::api::firmware_protection::FirmwareProtection;
|
||||
@@ -38,12 +39,14 @@ pub trait Env {
|
||||
type Customization: Customization;
|
||||
type HidConnection: HidConnection;
|
||||
type AttestationStore: AttestationStore;
|
||||
type Clock: Clock;
|
||||
|
||||
fn rng(&mut self) -> &mut Self::Rng;
|
||||
fn user_presence(&mut self) -> &mut Self::UserPresence;
|
||||
fn store(&mut self) -> &mut Store<Self::Storage>;
|
||||
fn key_store(&mut self) -> &mut Self::KeyStore;
|
||||
fn attestation_store(&mut self) -> &mut Self::AttestationStore;
|
||||
fn clock(&mut self) -> &mut Self::Clock;
|
||||
|
||||
/// Returns the upgrade storage instance.
|
||||
///
|
||||
|
||||
73
src/env/test/mod.rs
vendored
73
src/env/test/mod.rs
vendored
@@ -14,15 +14,14 @@
|
||||
|
||||
use self::upgrade_storage::BufferUpgradeStorage;
|
||||
use crate::api::attestation_store::AttestationStore;
|
||||
use crate::api::clock::Clock;
|
||||
use crate::api::connection::{HidConnection, SendOrRecvResult, SendOrRecvStatus};
|
||||
use crate::api::customization::DEFAULT_CUSTOMIZATION;
|
||||
use crate::api::firmware_protection::FirmwareProtection;
|
||||
use crate::api::user_presence::{UserPresence, UserPresenceResult};
|
||||
use crate::api::{attestation_store, key_store};
|
||||
use crate::clock::ClockInt;
|
||||
use crate::env::Env;
|
||||
use customization::TestCustomization;
|
||||
use embedded_time::duration::Milliseconds;
|
||||
use persistent_store::{BufferOptions, BufferStorage, Store};
|
||||
use rand::rngs::StdRng;
|
||||
use rand::{Rng, SeedableRng};
|
||||
@@ -37,6 +36,7 @@ pub struct TestEnv {
|
||||
store: Store<BufferStorage>,
|
||||
upgrade_storage: Option<BufferUpgradeStorage>,
|
||||
customization: TestCustomization,
|
||||
clock: TestClock,
|
||||
}
|
||||
|
||||
pub struct TestRng256 {
|
||||
@@ -57,6 +57,43 @@ impl Rng256 for TestRng256 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct TestTimer {
|
||||
end_ms: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TestClock {
|
||||
/// The current time, as advanced, in milliseconds.
|
||||
now_ms: usize,
|
||||
}
|
||||
|
||||
impl TestClock {
|
||||
pub fn advance(&mut self, milliseconds: usize) {
|
||||
self.now_ms += milliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
impl Clock for TestClock {
|
||||
type Timer = TestTimer;
|
||||
|
||||
fn make_timer(&mut self, milliseconds: usize) -> Self::Timer {
|
||||
TestTimer {
|
||||
end_ms: self.now_ms + milliseconds,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_elapsed(&mut self, timer: &Self::Timer) -> bool {
|
||||
self.now_ms >= timer.end_ms
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
fn timestamp_us(&mut self) -> usize {
|
||||
// Unused, but let's implement something because it's easy.
|
||||
self.now_ms * 1000
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestUserPresence {
|
||||
check: Box<dyn Fn() -> UserPresenceResult>,
|
||||
}
|
||||
@@ -85,11 +122,7 @@ fn new_storage() -> BufferStorage {
|
||||
}
|
||||
|
||||
impl HidConnection for TestEnv {
|
||||
fn send_and_maybe_recv(
|
||||
&mut self,
|
||||
_buf: &mut [u8; 64],
|
||||
_timeout: Milliseconds<ClockInt>,
|
||||
) -> SendOrRecvResult {
|
||||
fn send_and_maybe_recv(&mut self, _buf: &mut [u8; 64], _timeout_ms: usize) -> SendOrRecvResult {
|
||||
// TODO: Implement I/O from canned requests/responses for integration testing.
|
||||
Ok(SendOrRecvStatus::Sent)
|
||||
}
|
||||
@@ -107,12 +140,14 @@ impl TestEnv {
|
||||
let store = Store::new(storage).ok().unwrap();
|
||||
let upgrade_storage = Some(BufferUpgradeStorage::new().unwrap());
|
||||
let customization = DEFAULT_CUSTOMIZATION.into();
|
||||
let clock = TestClock::default();
|
||||
TestEnv {
|
||||
rng,
|
||||
user_presence,
|
||||
store,
|
||||
upgrade_storage,
|
||||
customization,
|
||||
clock,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +172,7 @@ impl TestUserPresence {
|
||||
|
||||
impl UserPresence for TestUserPresence {
|
||||
fn check_init(&mut self) {}
|
||||
fn wait_with_timeout(&mut self, _timeout: Milliseconds<ClockInt>) -> UserPresenceResult {
|
||||
fn wait_with_timeout(&mut self, _timeout_ms: usize) -> UserPresenceResult {
|
||||
(self.check)()
|
||||
}
|
||||
fn check_complete(&mut self) {}
|
||||
@@ -174,6 +209,7 @@ impl Env for TestEnv {
|
||||
type Storage = BufferStorage;
|
||||
type KeyStore = Self;
|
||||
type AttestationStore = Self;
|
||||
type Clock = TestClock;
|
||||
type UpgradeStorage = BufferUpgradeStorage;
|
||||
type FirmwareProtection = Self;
|
||||
type Write = TestWrite;
|
||||
@@ -200,6 +236,10 @@ impl Env for TestEnv {
|
||||
self
|
||||
}
|
||||
|
||||
fn clock(&mut self) -> &mut Self::Clock {
|
||||
&mut self.clock
|
||||
}
|
||||
|
||||
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
||||
self.upgrade_storage.as_mut()
|
||||
}
|
||||
@@ -225,3 +265,20 @@ impl Env for TestEnv {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::module_inception)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_clock() {
|
||||
let mut clock = TestClock::default();
|
||||
let timer = clock.make_timer(3);
|
||||
assert!(!clock.is_elapsed(&timer));
|
||||
clock.advance(2);
|
||||
assert!(!clock.is_elapsed(&timer));
|
||||
clock.advance(1);
|
||||
assert!(clock.is_elapsed(&timer));
|
||||
}
|
||||
}
|
||||
|
||||
118
src/env/tock/clock.rs
vendored
Normal file
118
src/env/tock/clock.rs
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright 2022-2023 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::api::clock::Clock;
|
||||
use libtock_drivers::timer::{get_clock_frequency, get_ticks};
|
||||
|
||||
/// 56-bits timestamp (valid for 70k+ years)
|
||||
#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
|
||||
struct Timestamp {
|
||||
epoch: usize, // 32-bits
|
||||
tick: usize, // 24-bits (32kHz)
|
||||
}
|
||||
|
||||
impl Timestamp {
|
||||
/// Adds (potentially more than 24 bit of) ticks to this timestamp.
|
||||
pub fn add_ticks(&mut self, ticks: usize) {
|
||||
// Saturating should never happen, but it fails gracefully.
|
||||
let sum = self.tick.saturating_add(ticks);
|
||||
self.epoch += sum >> 24;
|
||||
self.tick = sum & 0xff_ffff;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TockTimer {
|
||||
deadline: Timestamp,
|
||||
}
|
||||
|
||||
/// Clock that produces timers through Tock syscalls.
|
||||
///
|
||||
/// To guarantee correctness, you have to call any of its functions at least once per full tick
|
||||
/// counter wrap. In our case, 24 bit ticks with a 32 kHz frequency wrap after 512 seconds. If you
|
||||
/// can't guarantee to regularly create or check timers, call tickle at least every 8 minutes.
|
||||
#[derive(Default)]
|
||||
pub struct TockClock {
|
||||
now: Timestamp,
|
||||
}
|
||||
|
||||
impl TockClock {
|
||||
/// Elapses timers before the clock wraps.
|
||||
///
|
||||
/// Call this regularly to timeout reliably despite wrapping clock ticks.
|
||||
pub fn tickle(&mut self) {
|
||||
let cur_tick = get_ticks().ok().unwrap();
|
||||
if cur_tick < self.now.tick {
|
||||
self.now.epoch += 1;
|
||||
}
|
||||
self.now.tick = cur_tick;
|
||||
}
|
||||
}
|
||||
|
||||
impl Clock for TockClock {
|
||||
type Timer = TockTimer;
|
||||
|
||||
fn make_timer(&mut self, milliseconds: usize) -> Self::Timer {
|
||||
self.tickle();
|
||||
let clock_frequency = get_clock_frequency().ok().unwrap();
|
||||
let delta_tick = match milliseconds.checked_mul(clock_frequency) {
|
||||
Some(x) => x / 1000,
|
||||
// All CTAP timeouts are multiples of 100 so far. Worst case we timeout too early.
|
||||
None => (milliseconds / 100).saturating_mul(clock_frequency / 10),
|
||||
};
|
||||
let mut deadline = self.now;
|
||||
deadline.add_ticks(delta_tick);
|
||||
Self::Timer { deadline }
|
||||
}
|
||||
|
||||
fn is_elapsed(&mut self, timer: &Self::Timer) -> bool {
|
||||
self.tickle();
|
||||
self.now >= timer.deadline
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
fn timestamp_us(&mut self) -> usize {
|
||||
let clock_frequency = get_clock_frequency().ok().unwrap();
|
||||
let total_ticks = 0x100_0000u64 * self.now.epoch as u64 + self.now.tick as u64;
|
||||
(total_ticks.wrapping_mul(1_000_000u64) / clock_frequency as u64) as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_timestamp_add_ticks() {
|
||||
let mut timestamp = Timestamp::default();
|
||||
timestamp.add_ticks(1);
|
||||
let expected = Timestamp { epoch: 0, tick: 1 };
|
||||
assert_eq!(timestamp, expected);
|
||||
timestamp.add_ticks(0xff_ffff);
|
||||
let expected = Timestamp { epoch: 1, tick: 0 };
|
||||
assert_eq!(timestamp, expected);
|
||||
timestamp.add_ticks(0x100_0000);
|
||||
let expected = Timestamp { epoch: 2, tick: 0 };
|
||||
assert_eq!(timestamp, expected);
|
||||
timestamp.add_ticks(0x1ff_ffff);
|
||||
let expected = Timestamp {
|
||||
epoch: 3,
|
||||
tick: 0xff_ffff,
|
||||
};
|
||||
assert_eq!(timestamp, expected);
|
||||
timestamp.add_ticks(1);
|
||||
let expected = Timestamp { epoch: 4, tick: 0 };
|
||||
assert_eq!(timestamp, expected);
|
||||
}
|
||||
}
|
||||
29
src/env/tock/mod.rs
vendored
29
src/env/tock/mod.rs
vendored
@@ -19,12 +19,10 @@ use crate::api::customization::{CustomizationImpl, DEFAULT_CUSTOMIZATION};
|
||||
use crate::api::firmware_protection::FirmwareProtection;
|
||||
use crate::api::user_presence::{UserPresence, UserPresenceError, UserPresenceResult};
|
||||
use crate::api::{attestation_store, key_store};
|
||||
use crate::clock::{ClockInt, KEEPALIVE_DELAY_MS};
|
||||
use crate::env::Env;
|
||||
use clock::TockClock;
|
||||
use core::cell::Cell;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use embedded_time::duration::Milliseconds;
|
||||
use embedded_time::fixed_point::FixedPoint;
|
||||
use libtock_core::result::{CommandError, EALREADY};
|
||||
use libtock_drivers::buttons::{self, ButtonState};
|
||||
use libtock_drivers::console::Console;
|
||||
@@ -35,6 +33,7 @@ use libtock_drivers::{crp, led, timer};
|
||||
use persistent_store::{StorageResult, Store};
|
||||
use rng256::TockRng256;
|
||||
|
||||
mod clock;
|
||||
mod storage;
|
||||
|
||||
pub struct TockHidConnection {
|
||||
@@ -42,14 +41,10 @@ pub struct TockHidConnection {
|
||||
}
|
||||
|
||||
impl HidConnection for TockHidConnection {
|
||||
fn send_and_maybe_recv(
|
||||
&mut self,
|
||||
buf: &mut [u8; 64],
|
||||
timeout: Milliseconds<ClockInt>,
|
||||
) -> SendOrRecvResult {
|
||||
fn send_and_maybe_recv(&mut self, buf: &mut [u8; 64], timeout_ms: usize) -> SendOrRecvResult {
|
||||
match usb_ctap_hid::send_or_recv_with_timeout(
|
||||
buf,
|
||||
timer::Duration::from_ms(timeout.integer() as isize),
|
||||
Duration::from_ms(timeout_ms as isize),
|
||||
self.endpoint,
|
||||
) {
|
||||
Ok(usb_ctap_hid::SendOrRecvStatus::Timeout) => Ok(SendOrRecvStatus::Timeout),
|
||||
@@ -70,6 +65,7 @@ pub struct TockEnv {
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
vendor_connection: TockHidConnection,
|
||||
blink_pattern: usize,
|
||||
clock: TockClock,
|
||||
}
|
||||
|
||||
impl TockEnv {
|
||||
@@ -95,6 +91,7 @@ impl TockEnv {
|
||||
endpoint: UsbEndpoint::VendorHid,
|
||||
},
|
||||
blink_pattern: 0,
|
||||
clock: TockClock::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -115,8 +112,9 @@ impl UserPresence for TockEnv {
|
||||
fn check_init(&mut self) {
|
||||
self.blink_pattern = 0;
|
||||
}
|
||||
fn wait_with_timeout(&mut self, timeout: Milliseconds<ClockInt>) -> UserPresenceResult {
|
||||
if timeout.integer() == 0 {
|
||||
|
||||
fn wait_with_timeout(&mut self, timeout_ms: usize) -> UserPresenceResult {
|
||||
if timeout_ms == 0 {
|
||||
return Err(UserPresenceError::Timeout);
|
||||
}
|
||||
blink_leds(self.blink_pattern);
|
||||
@@ -141,7 +139,7 @@ impl UserPresence for TockEnv {
|
||||
});
|
||||
let mut keepalive = keepalive_callback.init().flex_unwrap();
|
||||
let keepalive_alarm = keepalive
|
||||
.set_alarm(timer::Duration::from_ms(timeout.integer() as isize))
|
||||
.set_alarm(Duration::from_ms(timeout_ms as isize))
|
||||
.flex_unwrap();
|
||||
|
||||
// Wait for a button touch or an alarm.
|
||||
@@ -224,6 +222,7 @@ impl Env for TockEnv {
|
||||
type Storage = TockStorage;
|
||||
type KeyStore = Self;
|
||||
type AttestationStore = Self;
|
||||
type Clock = TockClock;
|
||||
type UpgradeStorage = TockUpgradeStorage;
|
||||
type FirmwareProtection = Self;
|
||||
type Write = Console;
|
||||
@@ -250,6 +249,10 @@ impl Env for TockEnv {
|
||||
self
|
||||
}
|
||||
|
||||
fn clock(&mut self) -> &mut Self::Clock {
|
||||
&mut self.clock
|
||||
}
|
||||
|
||||
fn upgrade_storage(&mut self) -> Option<&mut Self::UpgradeStorage> {
|
||||
self.upgrade_storage.as_mut()
|
||||
}
|
||||
@@ -324,5 +327,3 @@ pub fn switch_off_leds() {
|
||||
led::get(l).flex_unwrap().off().flex_unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub const KEEPALIVE_DELAY_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
|
||||
|
||||
Reference in New Issue
Block a user