Rewrite timer subsystem

pull/171/head
Andre Richter 2 years ago
parent c8c422e995
commit b7b2d31c24
No known key found for this signature in database
GPG Key ID: 2116C1AB102F615E

@ -182,7 +182,7 @@ diff -uNr 06_uart_chainloader/src/_arch/aarch64/cpu/boot.s 07_timestamps/src/_ar
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
@@ -48,35 +37,23 @@
@@ -48,35 +37,31 @@
// If execution reaches here, it is the boot core.
// Initialize DRAM.
@ -220,6 +220,14 @@ diff -uNr 06_uart_chainloader/src/_arch/aarch64/cpu/boot.s 07_timestamps/src/_ar
- // Jump to the relocated Rust code.
- ADR_ABS x1, _start_rust
- br x1
+ // Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
+ // Abort if the frequency read back as 0.
+ ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
+ mrs x2, CNTFRQ_EL0
+ cmp x2, xzr
+ b.eq .L_parking_loop
+ str w2, [x1]
+
+ // Jump to Rust code.
+ b _start_rust
@ -249,7 +257,7 @@ diff -uNr 06_uart_chainloader/src/_arch/aarch64/cpu.rs 07_timestamps/src/_arch/a
diff -uNr 06_uart_chainloader/src/_arch/aarch64/time.rs 07_timestamps/src/_arch/aarch64/time.rs
--- 06_uart_chainloader/src/_arch/aarch64/time.rs
+++ 07_timestamps/src/_arch/aarch64/time.rs
@@ -0,0 +1,121 @@
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com>
@ -263,114 +271,155 @@ diff -uNr 06_uart_chainloader/src/_arch/aarch64/time.rs 07_timestamps/src/_arch/
+//!
+//! crate::time::arch_time
+
+use crate::{time, warn};
+use core::time::Duration;
+use crate::warn;
+use core::{
+ num::{NonZeroU128, NonZeroU32, NonZeroU64},
+ ops::{Add, Div},
+ time::Duration,
+};
+use cortex_a::{asm::barrier, registers::*};
+use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
+use tock_registers::interfaces::Readable;
+
+//--------------------------------------------------------------------------------------------------
+// Private Definitions
+//--------------------------------------------------------------------------------------------------
+
+const NS_PER_S: u64 = 1_000_000_000;
+const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
+
+/// ARMv8 Generic Timer.
+struct GenericTimer;
+#[derive(Copy, Clone, PartialOrd, PartialEq)]
+struct GenericTimerCounterValue(u64);
+
+//--------------------------------------------------------------------------------------------------
+// Global instances
+//--------------------------------------------------------------------------------------------------
+
+static TIME_MANAGER: GenericTimer = GenericTimer;
+/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
+/// executed. This given value here is just a (safe) dummy.
+#[no_mangle]
+static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
+
+//--------------------------------------------------------------------------------------------------
+// Private Code
+//--------------------------------------------------------------------------------------------------
+
+impl GenericTimer {
+ #[inline(always)]
+ fn read_cntpct(&self) -> u64 {
+ // Prevent that the counter is read ahead of time due to out-of-order execution.
+ unsafe { barrier::isb(barrier::SY) };
+ CNTPCT_EL0.get()
+ }
+impl GenericTimerCounterValue {
+ pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+impl Add for GenericTimerCounterValue {
+ type Output = Self;
+
+/// Return a reference to the time manager.
+pub fn time_manager() -> &'static impl time::interface::TimeManager {
+ &TIME_MANAGER
+ fn add(self, other: Self) -> Self {
+ GenericTimerCounterValue(self.0.wrapping_add(other.0))
+ }
+}
+
+//------------------------------------------------------------------------------
+// OS Interface Code
+//------------------------------------------------------------------------------
+impl From<GenericTimerCounterValue> for Duration {
+ fn from(counter_value: GenericTimerCounterValue) -> Self {
+ if counter_value.0 == 0 {
+ return Duration::ZERO;
+ }
+
+impl time::interface::TimeManager for GenericTimer {
+ fn resolution(&self) -> Duration {
+ Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
+ // Read volatile is needed here to prevent the compiler from optimizing
+ // ARCH_TIMER_COUNTER_FREQUENCY away.
+ //
+ // This is safe, because all the safety requirements as stated in read_volatile()'s
+ // documentation are fulfilled.
+ let frequency: NonZeroU64 =
+ unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
+
+ // Div<NonZeroU64> implementation for u64 cannot panic.
+ let secs = counter_value.0.div(frequency);
+
+ // This is safe, because frequency can never be greater than u32::MAX, which means the
+ // largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
+ // (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
+ //
+ // The subsequent division ensures the result fits into u32, since the max result is smaller
+ // than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
+ let sub_second_counter_value = counter_value.0 modulo frequency;
+ let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
+ .div(frequency) as u32;
+
+ Duration::new(secs, nanos)
+ }
+}
+
+ fn uptime(&self) -> Duration {
+ let current_count: u64 = self.read_cntpct() * NS_PER_S;
+ let frq: u64 = CNTFRQ_EL0.get() as u64;
+fn max_duration() -> Duration {
+ Duration::from(GenericTimerCounterValue::MAX)
+}
+
+ Duration::from_nanos(current_count / frq)
+ }
+impl TryFrom<Duration> for GenericTimerCounterValue {
+ type Error = &'static str;
+
+ fn spin_for(&self, duration: Duration) {
+ // Instantly return on zero.
+ if duration.as_nanos() == 0 {
+ return;
+ fn try_from(duration: Duration) -> Result<Self, Self::Error> {
+ if duration < resolution() {
+ return Ok(GenericTimerCounterValue(0));
+ }
+
+ // Calculate the register compare value.
+ let frq = CNTFRQ_EL0.get();
+ let x = match frq.checked_mul(duration.as_nanos() as u64) {
+ #[allow(unused_imports)]
+ None => {
+ warn!("Spin duration too long, skipping");
+ return;
+ }
+ Some(val) => val,
+ };
+ let tval = x / NS_PER_S;
+
+ // Check if it is within supported bounds.
+ let warn: Option<&str> = if tval == 0 {
+ Some("smaller")
+ // The upper 32 bits of CNTP_TVAL_EL0 are reserved.
+ } else if tval > u32::max_value().into() {
+ Some("bigger")
+ } else {
+ None
+ };
+
+ #[allow(unused_imports)]
+ if let Some(w) = warn {
+ warn!(
+ "Spin duration {} than architecturally supported, skipping",
+ w
+ );
+ return;
+ if duration > max_duration() {
+ return Err("Conversion error. Duration too big");
+ }
+
+ // Set the compare value register.
+ CNTP_TVAL_EL0.set(tval);
+ // This is safe, because all the safety requirements as stated in read_volatile()'s
+ // documentation are fulfilled.
+ let frequency: u128 =
+ unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
+ let duration: u128 = duration.as_nanos();
+
+ // Kick off the counting. // Disable timer interrupt.
+ CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
+ // This is safe, because frequency can never be greater than u32::MAX, and
+ // (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
+ let counter_value =
+ unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
+
+ // ISTATUS will be '1' when cval ticks have passed. Busy-check it.
+ while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
+
+ // Disable counting again.
+ CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
+ // Since we checked above that we are <= max_duration(), just cast to u64.
+ Ok(GenericTimerCounterValue(counter_value as u64))
+ }
+}
+
+#[inline(always)]
+fn read_cntpct() -> GenericTimerCounterValue {
+ // Prevent that the counter is read ahead of time due to out-of-order execution.
+ unsafe { barrier::isb(barrier::SY) };
+ let cnt = CNTPCT_EL0.get();
+
+ GenericTimerCounterValue(cnt)
+}
+
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+/// The timer's resolution.
+pub fn resolution() -> Duration {
+ Duration::from(GenericTimerCounterValue(1))
+}
+
+/// The uptime since power-on of the device.
+///
+/// This includes time consumed by firmware and bootloaders.
+pub fn uptime() -> Duration {
+ read_cntpct().into()
+}
+
+/// Spin for a given duration.
+pub fn spin_for(duration: Duration) {
+ let curr_counter_value = read_cntpct();
+
+ let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
+ Err(msg) => {
+ warn!("spin_for: {}. Skipping", msg);
+ return;
+ }
+ Ok(val) => val,
+ };
+ let counter_value_target = curr_counter_value + counter_value_delta;
+
+ // Busy wait.
+ //
+ // Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
+ while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
+}
diff -uNr 06_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 07_timestamps/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
--- 06_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs
@ -380,7 +429,7 @@ diff -uNr 06_uart_chainloader/src/bsp/device_driver/bcm/bcm2xxx_gpio.rs 07_times
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
- use crate::cpu;
+ use crate::{time, time::interface::TimeManager};
+ use crate::time;
+ use core::time::Duration;
- // Make an educated guess for a good delay value (Sequence described in the BCM2837
@ -534,7 +583,20 @@ diff -uNr 06_uart_chainloader/src/cpu.rs 07_timestamps/src/cpu.rs
diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
--- 06_uart_chainloader/src/main.rs
+++ 07_timestamps/src/main.rs
@@ -121,6 +121,7 @@
@@ -108,9 +108,12 @@
#![allow(clippy::upper_case_acronyms)]
#![feature(asm_const)]
+#![feature(const_option)]
#![feature(format_args_nl)]
+#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
+#![feature(unchecked_math)]
#![no_main]
#![no_std]
@@ -121,6 +124,7 @@
mod panic_wait;
mod print;
mod synchronization;
@ -542,7 +604,7 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
/// Early init code.
///
@@ -143,55 +144,38 @@
@@ -143,55 +147,37 @@
kernel_main()
}
@ -558,7 +620,6 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
- use console::console;
+ use core::time::Duration;
+ use driver::interface::DriverManager;
+ use time::interface::TimeManager;
- println!("{}", MINILOAD_LOGO);
- println!("{:^37}", bsp::board_name());
@ -630,12 +691,7 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
diff -uNr 06_uart_chainloader/src/panic_wait.rs 07_timestamps/src/panic_wait.rs
--- 06_uart_chainloader/src/panic_wait.rs
+++ 07_timestamps/src/panic_wait.rs
@@ -42,18 +42,23 @@
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
+ use crate::time::interface::TimeManager;
+
@@ -45,15 +45,18 @@
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();
@ -659,7 +715,7 @@ diff -uNr 06_uart_chainloader/src/panic_wait.rs 07_timestamps/src/panic_wait.rs
diff -uNr 06_uart_chainloader/src/print.rs 07_timestamps/src/print.rs
--- 06_uart_chainloader/src/print.rs
+++ 07_timestamps/src/print.rs
@@ -34,3 +34,59 @@
@@ -34,3 +34,51 @@
$crate::print::_print(format_args_nl!($($arg)*));
})
}
@ -668,8 +724,6 @@ diff -uNr 06_uart_chainloader/src/print.rs 07_timestamps/src/print.rs
+#[macro_export]
+macro_rules! info {
+ ($string:expr) => ({
+ use $crate::time::interface::TimeManager;
+
+ let timestamp = $crate::time::time_manager().uptime();
+
+ $crate::print::_print(format_args_nl!(
@ -679,8 +733,6 @@ diff -uNr 06_uart_chainloader/src/print.rs 07_timestamps/src/print.rs
+ ));
+ });
+ ($format_string:expr, $($arg:tt)*) => ({
+ use $crate::time::interface::TimeManager;
+
+ let timestamp = $crate::time::time_manager().uptime();
+
+ $crate::print::_print(format_args_nl!(
@ -696,8 +748,6 @@ diff -uNr 06_uart_chainloader/src/print.rs 07_timestamps/src/print.rs
+#[macro_export]
+macro_rules! warn {
+ ($string:expr) => ({
+ use $crate::time::interface::TimeManager;
+
+ let timestamp = $crate::time::time_manager().uptime();
+
+ $crate::print::_print(format_args_nl!(
@ -707,8 +757,6 @@ diff -uNr 06_uart_chainloader/src/print.rs 07_timestamps/src/print.rs
+ ));
+ });
+ ($format_string:expr, $($arg:tt)*) => ({
+ use $crate::time::interface::TimeManager;
+
+ let timestamp = $crate::time::time_manager().uptime();
+
+ $crate::print::_print(format_args_nl!(
@ -723,7 +771,7 @@ diff -uNr 06_uart_chainloader/src/print.rs 07_timestamps/src/print.rs
diff -uNr 06_uart_chainloader/src/time.rs 07_timestamps/src/time.rs
--- 06_uart_chainloader/src/time.rs
+++ 07_timestamps/src/time.rs
@@ -0,0 +1,37 @@
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0
+//
+// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com>
@ -734,31 +782,51 @@ diff -uNr 06_uart_chainloader/src/time.rs 07_timestamps/src/time.rs
+#[path = "_arch/aarch64/time.rs"]
+mod arch_time;
+
+use core::time::Duration;
+
+//--------------------------------------------------------------------------------------------------
+// Architectural Public Reexports
+// Public Definitions
+//--------------------------------------------------------------------------------------------------
+pub use arch_time::time_manager;
+
+/// Provides time management functions.
+pub struct TimeManager;
+
+//--------------------------------------------------------------------------------------------------
+// Public Definitions
+// Global instances
+//--------------------------------------------------------------------------------------------------
+
+/// Timekeeping interfaces.
+pub mod interface {
+ use core::time::Duration;
+static TIME_MANAGER: TimeManager = TimeManager::new();
+
+ /// Time management functions.
+ pub trait TimeManager {
+ /// The timer's resolution.
+ fn resolution(&self) -> Duration;
+//--------------------------------------------------------------------------------------------------
+// Public Code
+//--------------------------------------------------------------------------------------------------
+
+ /// The uptime since power-on of the device.
+ ///
+ /// This includes time consumed by firmware and bootloaders.
+ fn uptime(&self) -> Duration;
+/// Return a reference to the global TimeManager.
+pub fn time_manager() -> &'static TimeManager {
+ &TIME_MANAGER
+}
+
+impl TimeManager {
+ /// Create an instance.
+ pub const fn new() -> Self {
+ Self
+ }
+
+ /// The timer's resolution.
+ pub fn resolution(&self) -> Duration {
+ arch_time::resolution()
+ }
+
+ /// The uptime since power-on of the device.
+ ///
+ /// This includes time consumed by firmware and bootloaders.
+ pub fn uptime(&self) -> Duration {
+ arch_time::uptime()
+ }
+
+ /// Spin for a given duration.
+ fn spin_for(&self, duration: Duration);
+ /// Spin for a given duration.
+ pub fn spin_for(&self, duration: Duration) {
+ arch_time::spin_for(duration)
+ }
+}

@ -52,6 +52,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x2, CNTFRQ_EL0
cmp x2, xzr
b.eq .L_parking_loop
str w2, [x1]
// Jump to Rust code.
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -140,7 +140,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -108,9 +108,12 @@
#![allow(clippy::upper_case_acronyms)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(format_args_nl)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_main]
#![no_std]
@ -148,7 +151,6 @@ unsafe fn kernel_init() -> ! {
fn kernel_main() -> ! {
use core::time::Duration;
use driver::interface::DriverManager;
use time::interface::TimeManager;
info!(
"{} version {}",

@ -42,8 +42,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -52,6 +52,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x2, CNTFRQ_EL0
cmp x2, xzr
b.eq .L_parking_loop
str w2, [x1]
// Jump to Rust code.
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -140,7 +140,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -108,9 +108,12 @@
#![allow(clippy::upper_case_acronyms)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(format_args_nl)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_main]
#![no_std]
@ -148,7 +151,6 @@ unsafe fn kernel_init() -> ! {
fn kernel_main() -> ! {
use core::time::Duration;
use driver::interface::DriverManager;
use time::interface::TimeManager;
info!(
"{} version {}",

@ -42,8 +42,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -303,7 +303,7 @@ diff -uNr 08_hw_debug_JTAG/src/_arch/aarch64/cpu/boot.s 09_privilege_level/src/_
// Only proceed on the boot core. Park it otherwise.
mrs x1, MPIDR_EL1
and x1, x1, {CONST_CORE_ID_MASK}
@@ -48,11 +53,11 @@
@@ -48,7 +53,7 @@
// Prepare the jump to Rust code.
.L_prepare_rust:
@ -312,6 +312,10 @@ diff -uNr 08_hw_debug_JTAG/src/_arch/aarch64/cpu/boot.s 09_privilege_level/src/_
ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0
@@ -60,7 +65,7 @@
b.eq .L_parking_loop
str w2, [x1]
- // Jump to Rust code.
+ // Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust
@ -498,7 +502,7 @@ diff -uNr 08_hw_debug_JTAG/src/exception.rs 09_privilege_level/src/exception.rs
diff -uNr 08_hw_debug_JTAG/src/main.rs 09_privilege_level/src/main.rs
--- 08_hw_debug_JTAG/src/main.rs
+++ 09_privilege_level/src/main.rs
@@ -118,6 +118,7 @@
@@ -121,6 +121,7 @@
mod console;
mod cpu;
mod driver;
@ -506,15 +510,15 @@ diff -uNr 08_hw_debug_JTAG/src/main.rs 09_privilege_level/src/main.rs
mod panic_wait;
mod print;
mod synchronization;
@@ -146,6 +147,7 @@
@@ -149,6 +150,7 @@
/// The main function running after the early init.
fn kernel_main() -> ! {
+ use console::console;
use core::time::Duration;
use driver::interface::DriverManager;
use time::interface::TimeManager;
@@ -157,6 +159,12 @@
@@ -159,6 +161,12 @@
);
info!("Booting on: {}", bsp::board_name());
@ -527,7 +531,7 @@ diff -uNr 08_hw_debug_JTAG/src/main.rs 09_privilege_level/src/main.rs
info!(
"Architectural timer resolution: {} ns",
time::time_manager().resolution().as_nanos()
@@ -171,11 +179,15 @@
@@ -173,11 +181,15 @@
info!(" {}. {}", i + 1, driver.compatible());
}

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x2, CNTFRQ_EL0
cmp x2, xzr
b.eq .L_parking_loop
str w2, [x1]
// Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -140,7 +140,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -108,9 +108,12 @@
#![allow(clippy::upper_case_acronyms)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(format_args_nl)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_main]
#![no_std]
@ -150,7 +153,6 @@ fn kernel_main() -> ! {
use console::console;
use core::time::Duration;
use driver::interface::DriverManager;
use time::interface::TimeManager;
info!(
"{} version {}",

@ -42,8 +42,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -1109,18 +1109,20 @@ diff -uNr 09_privilege_level/src/common.rs 10_virtual_mem_part1_identity_mapping
diff -uNr 09_privilege_level/src/main.rs 10_virtual_mem_part1_identity_mapping/src/main.rs
--- 09_privilege_level/src/main.rs
+++ 10_virtual_mem_part1_identity_mapping/src/main.rs
@@ -107,18 +107,23 @@
@@ -107,9 +107,12 @@
//! 2. Once finished with architectural setup, the arch code calls `kernel_init()`.
#![allow(clippy::upper_case_acronyms)]
+#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
+#![feature(core_intrinsics)]
#![feature(format_args_nl)]
+#![feature(int_roundings)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![no_main]
@@ -118,10 +121,12 @@
#![no_std]
mod bsp;
@ -1133,7 +1135,7 @@ diff -uNr 09_privilege_level/src/main.rs 10_virtual_mem_part1_identity_mapping/s
mod panic_wait;
mod print;
mod synchronization;
@@ -129,9 +134,17 @@
@@ -132,9 +137,17 @@
/// # Safety
///
/// - Only a single core must be active and running this function.
@ -1152,7 +1154,7 @@ diff -uNr 09_privilege_level/src/main.rs 10_virtual_mem_part1_identity_mapping/s
for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() {
@@ -147,7 +160,7 @@
@@ -150,7 +163,7 @@
/// The main function running after the early init.
fn kernel_main() -> ! {
@ -1160,8 +1162,8 @@ diff -uNr 09_privilege_level/src/main.rs 10_virtual_mem_part1_identity_mapping/s
+ use console::{console, interface::Write};
use core::time::Duration;
use driver::interface::DriverManager;
use time::interface::TimeManager;
@@ -159,6 +172,9 @@
@@ -161,6 +174,9 @@
);
info!("Booting on: {}", bsp::board_name());
@ -1171,7 +1173,7 @@ diff -uNr 09_privilege_level/src/main.rs 10_virtual_mem_part1_identity_mapping/s
let (_, privilege_level) = exception::current_privilege_level();
info!("Current privilege level: {}", privilege_level);
@@ -182,6 +198,13 @@
@@ -184,6 +200,13 @@
info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1));

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x2, CNTFRQ_EL0
cmp x2, xzr
b.eq .L_parking_loop
str w2, [x1]
// Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -140,7 +140,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -109,11 +109,14 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(int_roundings)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_main]
#![no_std]
@ -163,7 +166,6 @@ fn kernel_main() -> ! {
use console::{console, interface::Write};
use core::time::Duration;
use driver::interface::DriverManager;
use time::interface::TimeManager;
info!(
"{} version {}",

@ -42,8 +42,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -1024,7 +1024,7 @@ diff -uNr 10_virtual_mem_part1_identity_mapping/src/exception.rs 11_exceptions_p
diff -uNr 10_virtual_mem_part1_identity_mapping/src/main.rs 11_exceptions_part1_groundwork/src/main.rs
--- 10_virtual_mem_part1_identity_mapping/src/main.rs
+++ 11_exceptions_part1_groundwork/src/main.rs
@@ -142,6 +142,8 @@
@@ -145,6 +145,8 @@
use driver::interface::DriverManager;
use memory::mmu::interface::MMU;
@ -1033,7 +1033,7 @@ diff -uNr 10_virtual_mem_part1_identity_mapping/src/main.rs 11_exceptions_part1_
if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
panic!("MMU: {}", string);
}
@@ -160,7 +162,7 @@
@@ -163,7 +165,7 @@
/// The main function running after the early init.
fn kernel_main() -> ! {
@ -1041,8 +1041,8 @@ diff -uNr 10_virtual_mem_part1_identity_mapping/src/main.rs 11_exceptions_part1_
+ use console::console;
use core::time::Duration;
use driver::interface::DriverManager;
use time::interface::TimeManager;
@@ -198,13 +200,28 @@
@@ -200,13 +202,28 @@
info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1));

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x2, CNTFRQ_EL0
cmp x2, xzr
b.eq .L_parking_loop
str w2, [x1]
// Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -140,7 +140,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -109,11 +109,14 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(int_roundings)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_main]
#![no_std]
@ -165,7 +168,6 @@ fn kernel_main() -> ! {
use console::console;
use core::time::Duration;
use driver::interface::DriverManager;
use time::interface::TimeManager;
info!(
"{} version {}",

@ -42,8 +42,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -622,7 +622,7 @@ your test code into individual chunks. For example, take a look at `tests/01_tim
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, time, time::interface::TimeManager};
use libkernel::{bsp, cpu, driver, exception, time};
use test_macros::kernel_test;
#[no_mangle]
@ -643,6 +643,7 @@ unsafe fn kernel_init() -> ! {
#[kernel_test]
fn timer_is_counting() {
assert!(time::time_manager().uptime().as_nanos() > 0)
assert!(time::time_manager().resolution().as_nanos() < 100)
}
/// Timer resolution must be sufficient.

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x2, CNTFRQ_EL0
cmp x2, xzr
b.eq .L_parking_loop
str w2, [x1]
// Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -140,7 +140,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -111,12 +111,15 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(int_roundings)]
#![feature(linkage)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_std]
// Testing
#![cfg_attr(test, no_main)]

@ -59,8 +59,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
// Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter();

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, time, time::interface::TimeManager};
use libkernel::{bsp, cpu, driver, exception, time};
use test_macros::kernel_test;
#[no_mangle]
@ -37,6 +37,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient.
#[kernel_test]
fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100)
}

@ -2385,7 +2385,7 @@ diff -uNr 12_integrated_testing/kernel/src/exception/asynchronous.rs 13_exceptio
diff -uNr 12_integrated_testing/kernel/src/lib.rs 13_exceptions_part2_peripheral_IRQs/kernel/src/lib.rs
--- 12_integrated_testing/kernel/src/lib.rs
+++ 13_exceptions_part2_peripheral_IRQs/kernel/src/lib.rs
@@ -135,6 +135,7 @@
@@ -138,6 +138,7 @@
pub mod exception;
pub mod memory;
pub mod print;
@ -2474,10 +2474,10 @@ diff -uNr 12_integrated_testing/kernel/src/panic_wait.rs 13_exceptions_part2_per
use core::panic::PanicInfo;
//--------------------------------------------------------------------------------------------------
@@ -61,6 +61,8 @@
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
@@ -59,6 +59,8 @@
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
+ exception::asynchronous::local_irq_mask();
+
// Protect against panic infinite loops if any of the following code panics itself.

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x2, CNTFRQ_EL0
cmp x2, xzr
b.eq .L_parking_loop
str w2, [x1]
// Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -140,7 +140,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -111,12 +111,15 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(int_roundings)]
#![feature(linkage)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_std]
// Testing
#![cfg_attr(test, no_main)]

@ -59,8 +59,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
exception::asynchronous::local_irq_mask();
// Protect against panic infinite loops if any of the following code panics itself.

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, time, time::interface::TimeManager};
use libkernel::{bsp, cpu, driver, exception, time};
use test_macros::kernel_test;
#[no_mangle]
@ -37,6 +37,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient.
#[kernel_test]
fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100)
}

@ -2326,20 +2326,21 @@ diff -uNr 13_exceptions_part2_peripheral_IRQs/kernel/src/exception/asynchronous.
diff -uNr 13_exceptions_part2_peripheral_IRQs/kernel/src/lib.rs 14_virtual_mem_part2_mmio_remap/kernel/src/lib.rs
--- 13_exceptions_part2_peripheral_IRQs/kernel/src/lib.rs
+++ 14_virtual_mem_part2_mmio_remap/kernel/src/lib.rs
@@ -113,9 +113,12 @@
#![feature(asm_const)]
@@ -114,10 +114,13 @@
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
+#![feature(generic_const_exprs)]
#![feature(int_roundings)]
+#![feature(is_sorted)]
#![feature(linkage)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
+#![feature(step_trait)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_std]
// Testing
@@ -183,6 +186,17 @@
@@ -186,6 +189,17 @@
use driver::interface::DriverManager;
exception::handling_init();
@ -3821,8 +3822,8 @@ diff -uNr 13_exceptions_part2_peripheral_IRQs/kernel/tests/01_timer_sanity.rs 14
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
-use libkernel::{bsp, cpu, driver, exception, time, time::interface::TimeManager};
+use libkernel::{bsp, cpu, driver, exception, memory, time, time::interface::TimeManager};
-use libkernel::{bsp, cpu, driver, exception, time};
+use libkernel::{bsp, cpu, driver, exception, memory, time};
use test_macros::kernel_test;
#[no_mangle]

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x2, CNTFRQ_EL0
cmp x2, xzr
b.eq .L_parking_loop
str w2, [x1]
// Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -144,7 +144,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -111,15 +111,18 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(generic_const_exprs)]
#![feature(int_roundings)]
#![feature(is_sorted)]
#![feature(linkage)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(step_trait)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_std]
// Testing
#![cfg_attr(test, no_main)]

@ -59,8 +59,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
exception::asynchronous::local_irq_mask();
// Protect against panic infinite loops if any of the following code panics itself.

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, memory, time, time::interface::TimeManager};
use libkernel::{bsp, cpu, driver, exception, memory, time};
use test_macros::kernel_test;
#[no_mangle]
@ -48,6 +48,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient.
#[kernel_test]
fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100)
}

@ -841,7 +841,7 @@ diff -uNr 14_virtual_mem_part2_mmio_remap/kernel/src/_arch/aarch64/cpu/boot.rs 1
diff -uNr 14_virtual_mem_part2_mmio_remap/kernel/src/_arch/aarch64/cpu/boot.s 15_virtual_mem_part3_precomputed_tables/kernel/src/_arch/aarch64/cpu/boot.s
--- 14_virtual_mem_part2_mmio_remap/kernel/src/_arch/aarch64/cpu/boot.s
+++ 15_virtual_mem_part3_precomputed_tables/kernel/src/_arch/aarch64/cpu/boot.s
@@ -53,11 +53,14 @@
@@ -53,19 +53,22 @@
// Prepare the jump to Rust code.
.L_prepare_rust:
@ -854,6 +854,18 @@ diff -uNr 14_virtual_mem_part2_mmio_remap/kernel/src/_arch/aarch64/cpu/boot.s 15
+ ADR_REL x1, __boot_core_stack_end_exclusive
+ mov sp, x1
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
- ADR_REL x1, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
- mrs x2, CNTFRQ_EL0
- cmp x2, xzr
+ ADR_REL x2, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
+ mrs x3, CNTFRQ_EL0
+ cmp x3, xzr
b.eq .L_parking_loop
- str w2, [x1]
+ str w3, [x2]
- // Jump to Rust code. x0 holds the function argument provided to _start_rust().
+ // Jump to Rust code. x0 and x1 hold the function arguments provided to _start_rust().
b _start_rust
@ -1324,7 +1336,7 @@ diff -uNr 14_virtual_mem_part2_mmio_remap/kernel/src/bsp/raspberrypi/memory/mmu.
diff -uNr 14_virtual_mem_part2_mmio_remap/kernel/src/lib.rs 15_virtual_mem_part3_precomputed_tables/kernel/src/lib.rs
--- 14_virtual_mem_part2_mmio_remap/kernel/src/lib.rs
+++ 15_virtual_mem_part3_precomputed_tables/kernel/src/lib.rs
@@ -186,17 +186,7 @@
@@ -189,17 +189,7 @@
use driver::interface::DriverManager;
exception::handling_init();

@ -60,6 +60,14 @@ _start:
ADR_REL x1, __boot_core_stack_end_exclusive
mov sp, x1
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x2, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x3, CNTFRQ_EL0
cmp x3, xzr
b.eq .L_parking_loop
str w3, [x2]
// Jump to Rust code. x0 and x1 hold the function arguments provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -144,7 +144,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -111,15 +111,18 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(generic_const_exprs)]
#![feature(int_roundings)]
#![feature(is_sorted)]
#![feature(linkage)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(step_trait)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_std]
// Testing
#![cfg_attr(test, no_main)]

@ -59,8 +59,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
exception::asynchronous::local_irq_mask();
// Protect against panic infinite loops if any of the following code panics itself.

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, memory, time, time::interface::TimeManager};
use libkernel::{bsp, cpu, driver, exception, memory, time};
use test_macros::kernel_test;
#[no_mangle]
@ -38,6 +38,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient.
#[kernel_test]
fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100)
}

@ -412,7 +412,7 @@ diff -uNr 15_virtual_mem_part3_precomputed_tables/kernel/src/_arch/aarch64/cpu/b
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
@@ -56,11 +68,23 @@
@@ -56,19 +68,31 @@
// Load the base address of the kernel's translation tables.
ldr x0, PHYS_KERNEL_TABLES_BASE_ADDR // provided by bsp/__board_name__/memory/mmu.rs
@ -435,6 +435,18 @@ diff -uNr 15_virtual_mem_part3_precomputed_tables/kernel/src/_arch/aarch64/cpu/b
+ ADR_REL x4, __boot_core_stack_end_exclusive
+ mov sp, x4
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
- ADR_REL x2, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
- mrs x3, CNTFRQ_EL0
- cmp x3, xzr
+ ADR_REL x5, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
+ mrs x6, CNTFRQ_EL0
+ cmp x6, xzr
b.eq .L_parking_loop
- str w3, [x2]
+ str w6, [x5]
- // Jump to Rust code. x0 and x1 hold the function arguments provided to _start_rust().
+ // Jump to Rust code. x0, x1 and x2 hold the function arguments provided to _start_rust().
b _start_rust
@ -731,7 +743,7 @@ diff -uNr 15_virtual_mem_part3_precomputed_tables/kernel/src/bsp/raspberrypi/mem
diff -uNr 15_virtual_mem_part3_precomputed_tables/kernel/src/lib.rs 16_virtual_mem_part4_higher_half_kernel/kernel/src/lib.rs
--- 15_virtual_mem_part3_precomputed_tables/kernel/src/lib.rs
+++ 16_virtual_mem_part4_higher_half_kernel/kernel/src/lib.rs
@@ -154,11 +154,6 @@
@@ -157,11 +157,6 @@
)
}

@ -84,6 +84,14 @@ _start:
ADR_REL x4, __boot_core_stack_end_exclusive
mov sp, x4
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x5, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x6, CNTFRQ_EL0
cmp x6, xzr
b.eq .L_parking_loop
str w6, [x5]
// Jump to Rust code. x0, x1 and x2 hold the function arguments provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -144,7 +144,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -111,15 +111,18 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(generic_const_exprs)]
#![feature(int_roundings)]
#![feature(is_sorted)]
#![feature(linkage)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(step_trait)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_std]
// Testing
#![cfg_attr(test, no_main)]

@ -59,8 +59,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
exception::asynchronous::local_irq_mask();
// Protect against panic infinite loops if any of the following code panics itself.

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, memory, time, time::interface::TimeManager};
use libkernel::{bsp, cpu, driver, exception, memory, time};
use test_macros::kernel_test;
#[no_mangle]
@ -38,6 +38,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient.
#[kernel_test]
fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100)
}

@ -326,7 +326,7 @@ diff -uNr 16_virtual_mem_part4_higher_half_kernel/kernel/src/bsp/raspberrypi/mem
diff -uNr 16_virtual_mem_part4_higher_half_kernel/kernel/src/lib.rs 17_kernel_symbols/kernel/src/lib.rs
--- 16_virtual_mem_part4_higher_half_kernel/kernel/src/lib.rs
+++ 17_kernel_symbols/kernel/src/lib.rs
@@ -139,6 +139,7 @@
@@ -142,6 +142,7 @@
pub mod memory;
pub mod print;
pub mod state;

@ -84,6 +84,14 @@ _start:
ADR_REL x4, __boot_core_stack_end_exclusive
mov sp, x4
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x5, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x6, CNTFRQ_EL0
cmp x6, xzr
b.eq .L_parking_loop
str w6, [x5]
// Jump to Rust code. x0, x1 and x2 hold the function arguments provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -144,7 +144,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -111,15 +111,18 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(generic_const_exprs)]
#![feature(int_roundings)]
#![feature(is_sorted)]
#![feature(linkage)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(step_trait)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_std]
// Testing
#![cfg_attr(test, no_main)]

@ -59,8 +59,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
exception::asynchronous::local_irq_mask();
// Protect against panic infinite loops if any of the following code panics itself.

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"]
mod arch_time;
use core::time::Duration;
//--------------------------------------------------------------------------------------------------
// Architectural Public Reexports
// Public Definitions
//--------------------------------------------------------------------------------------------------
pub use arch_time::time_manager;
/// Provides time management functions.
pub struct TimeManager;
//--------------------------------------------------------------------------------------------------
// Public Definitions
// Global instances
//--------------------------------------------------------------------------------------------------
/// Timekeeping interfaces.
pub mod interface {
use core::time::Duration;
static TIME_MANAGER: TimeManager = TimeManager::new();
/// Time management functions.
pub trait TimeManager {
/// The timer's resolution.
fn resolution(&self) -> Duration;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
fn uptime(&self) -> Duration;
/// Return a reference to the global TimeManager.
pub fn time_manager() -> &'static TimeManager {
&TIME_MANAGER
}
impl TimeManager {
/// Create an instance.
pub const fn new() -> Self {
Self
}
/// The timer's resolution.
pub fn resolution(&self) -> Duration {
arch_time::resolution()
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime(&self) -> Duration {
arch_time::uptime()
}
/// Spin for a given duration.
fn spin_for(&self, duration: Duration);
/// Spin for a given duration.
pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
}
}

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)]
use core::time::Duration;
use libkernel::{bsp, cpu, driver, exception, memory, time, time::interface::TimeManager};
use libkernel::{bsp, cpu, driver, exception, memory, time};
use test_macros::kernel_test;
#[no_mangle]
@ -38,6 +38,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient.
#[kernel_test]
fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100)
}

@ -909,7 +909,7 @@ diff -uNr 17_kernel_symbols/kernel/src/bsp/raspberrypi/memory/mmu.rs 18_backtrac
diff -uNr 17_kernel_symbols/kernel/src/lib.rs 18_backtrace/kernel/src/lib.rs
--- 17_kernel_symbols/kernel/src/lib.rs
+++ 18_backtrace/kernel/src/lib.rs
@@ -130,6 +130,7 @@
@@ -133,6 +133,7 @@
mod panic_wait;
mod synchronization;
@ -972,7 +972,7 @@ diff -uNr 17_kernel_symbols/kernel/src/panic_wait.rs 18_backtrace/kernel/src/pan
use core::panic::PanicInfo;
//--------------------------------------------------------------------------------------------------
@@ -75,6 +75,7 @@
@@ -73,6 +73,7 @@
println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\n\n\
@ -980,7 +980,7 @@ diff -uNr 17_kernel_symbols/kernel/src/panic_wait.rs 18_backtrace/kernel/src/pan
{}",
timestamp.as_secs(),
timestamp.subsec_micros(),
@@ -82,6 +83,7 @@
@@ -80,6 +81,7 @@
line,
column,
info.message().unwrap_or(&format_args!("")),

@ -84,6 +84,14 @@ _start:
ADR_REL x4, __boot_core_stack_end_exclusive
mov sp, x4
// Read the CPU's timer counter frequency and store it in ARCH_TIMER_COUNTER_FREQUENCY.
// Abort if the frequency read back as 0.
ADR_REL x5, ARCH_TIMER_COUNTER_FREQUENCY // provided by aarch64/time.rs
mrs x6, CNTFRQ_EL0
cmp x6, xzr
b.eq .L_parking_loop
str w6, [x5]
// Jump to Rust code. x0, x1 and x2 hold the function arguments provided to _start_rust().
b _start_rust

@ -11,111 +11,152 @@
//!
//! crate::time::arch_time
use crate::{time, warn};
use core::time::Duration;
use crate::warn;
use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
use tock_registers::interfaces::Readable;
//--------------------------------------------------------------------------------------------------
// Private Definitions
//--------------------------------------------------------------------------------------------------
const NS_PER_S: u64 = 1_000_000_000;
const NANOSEC_PER_SEC: NonZeroU64 = NonZeroU64::new(1_000_000_000).unwrap();
/// ARMv8 Generic Timer.
struct GenericTimer;
#[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimerCounterValue(u64);
//--------------------------------------------------------------------------------------------------
// Global instances
//--------------------------------------------------------------------------------------------------
static TIME_MANAGER: GenericTimer = GenericTimer;
/// Boot assembly code overwrites this value with the value of CNTFRQ_EL0 before any Rust code is
/// executed. This given value here is just a (safe) dummy.
#[no_mangle]
static ARCH_TIMER_COUNTER_FREQUENCY: NonZeroU32 = NonZeroU32::MIN;
//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------
impl GenericTimer {
#[inline(always)]
fn read_cntpct(&self) -> u64 {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
CNTPCT_EL0.get()
}
impl GenericTimerCounterValue {
pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl Add for GenericTimerCounterValue {
type Output = Self;
/// Return a reference to the time manager.
pub fn time_manager() -> &'static impl time::interface::TimeManager {
&TIME_MANAGER
fn add(self, other: Self) -> Self {
GenericTimerCounterValue(self.0.wrapping_add(other.0))
}
}
//------------------------------------------------------------------------------
// OS Interface Code
//------------------------------------------------------------------------------
impl From<GenericTimerCounterValue> for Duration {
fn from(counter_value: GenericTimerCounterValue) -> Self {
if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer {
fn resolution(&self) -> Duration {
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64))
// Read volatile is needed here to prevent the compiler from optimizing
// ARCH_TIMER_COUNTER_FREQUENCY away.
//
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: NonZeroU64 =
unsafe { core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY) }.into();
// Div<NonZeroU64> implementation for u64 cannot panic.
let secs = counter_value.0.div(frequency);
// This is safe, because frequency can never be greater than u32::MAX, which means the
// largest theoretical value for sub_second_counter_value is (u32::MAX - 1). Therefore,
// (sub_second_counter_value * NANOSEC_PER_SEC) cannot overflow an u64.
//
// The subsequent division ensures the result fits into u32, since the max result is smaller
// than NANOSEC_PER_SEC. Therefore, just cast it to u32 using `as`.
let sub_second_counter_value = counter_value.0 % frequency;
let nanos = unsafe { sub_second_counter_value.unchecked_mul(u64::from(NANOSEC_PER_SEC)) }
.div(frequency) as u32;
Duration::new(secs, nanos)
}
}
fn uptime(&self) -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S;
let frq: u64 = CNTFRQ_EL0.get() as u64;
fn max_duration() -> Duration {
Duration::from(GenericTimerCounterValue::MAX)
}
Duration::from_nanos(current_count / frq)
}
impl TryFrom<Duration> for GenericTimerCounterValue {
type Error = &'static str;
fn spin_for(&self, duration: Duration) {
// Instantly return on zero.
if duration.as_nanos() == 0 {
return;
fn try_from(duration: Duration) -> Result<Self, Self::Error> {
if duration < resolution() {
return Ok(GenericTimerCounterValue(0));
}
// Calculate the register compare value.
let frq = CNTFRQ_EL0.get();
let x = match frq.checked_mul(duration.as_nanos() as u64) {
#[allow(unused_imports)]
None => {
warn!("Spin duration too long, skipping");
return;
}
Some(val) => val,
};
let tval = x / NS_PER_S;
// Check if it is within supported bounds.
let warn: Option<&str> = if tval == 0 {
Some("smaller")
// The upper 32 bits of CNTP_TVAL_EL0 are reserved.
} else if tval > u32::max_value().into() {
Some("bigger")
} else {
None
};
#[allow(unused_imports)]
if let Some(w) = warn {
warn!(
"Spin duration {} than architecturally supported, skipping",
w
);
return;
if duration > max_duration() {
return Err("Conversion error. Duration too big");
}
// Set the compare value register.
CNTP_TVAL_EL0.set(tval);
// This is safe, because all the safety requirements as stated in read_volatile()'s
// documentation are fulfilled.
let frequency: u128 =
unsafe { u32::from(core::ptr::read_volatile(&ARCH_TIMER_COUNTER_FREQUENCY)) as u128 };
let duration: u128 = duration.as_nanos();
// Kick off the counting. // Disable timer interrupt.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
// This is safe, because frequency can never be greater than u32::MAX, and
// (Duration::MAX.as_nanos() * u32::MAX) < u128::MAX.
let counter_value =
unsafe { duration.unchecked_mul(frequency) }.div(NonZeroU128::from(NANOSEC_PER_SEC));
// ISTATUS will be '1' when cval ticks have passed. Busy-check it.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {}
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
// Since we checked above that we are <= max_duration(), just cast to u64.
Ok(GenericTimerCounterValue(counter_value as u64))
}
}
#[inline(always)]
fn read_cntpct() -> GenericTimerCounterValue {
// Prevent that the counter is read ahead of time due to out-of-order execution.
unsafe { barrier::isb(barrier::SY) };
let cnt = CNTPCT_EL0.get();
GenericTimerCounterValue(cnt)
}
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
/// The timer's resolution.
pub fn resolution() -> Duration {
Duration::from(GenericTimerCounterValue(1))
}
/// The uptime since power-on of the device.
///
/// This includes time consumed by firmware and bootloaders.
pub fn uptime() -> Duration {
read_cntpct().into()
}
/// Spin for a given duration.
pub fn spin_for(duration: Duration) {
let curr_counter_value = read_cntpct();
let counter_value_delta: GenericTimerCounterValue = match duration.try_into() {
Err(msg) => {
warn!("spin_for: {}. Skipping", msg);
return;
}
Ok(val) => val,
};
let counter_value_target = curr_counter_value + counter_value_delta;
// Busy wait.
//
// Read CNTPCT_EL0 directly to avoid the ISB that is part of [`read_cntpct`].
while GenericTimerCounterValue(CNTPCT_EL0.get()) < counter_value_target {}
}

@ -144,7 +144,7 @@ impl GPIOInner {
/// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager};
use crate::time;
use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps.

@ -111,15 +111,18 @@
#![allow(clippy::upper_case_acronyms)]
#![allow(incomplete_features)]
#![feature(asm_const)]
#![feature(const_option)]
#![feature(core_intrinsics)]
#![feature(format_args_nl)]
#![feature(generic_const_exprs)]
#![feature(int_roundings)]
#![feature(is_sorted)]
#![feature(linkage)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)]
#![feature(step_trait)]
#![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_std]
// Testing
#![cfg_attr(test, no_main)]

@ -59,8 +59,6 @@ fn panic_prevent_reenter() {
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
exception::asynchronous::local_irq_mask();
// Protect against panic infinite loops if any of the following code panics itself.

@ -39,8 +39,6 @@ macro_rules! println {
#[macro_export]
macro_rules! info {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -50,8 +48,6 @@ macro_rules! info {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -67,8 +63,6 @@ macro_rules! info {
#[macro_export]
macro_rules! warn {
($string:expr) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(
@ -78,8 +72,6 @@ macro_rules! warn {
));
});
($format_string:expr, $($arg:tt)*) => ({
use $crate::time::interface::TimeManager;
let timestamp = $crate::time::time_manager().uptime();
$crate::print::_print(format_args_nl!(

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save