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 // Public Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -48,35 +37,23 @@ @@ -48,35 +37,31 @@
// If execution reaches here, it is the boot core. // If execution reaches here, it is the boot core.
// Initialize DRAM. // 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. - // Jump to the relocated Rust code.
- ADR_ABS x1, _start_rust - ADR_ABS x1, _start_rust
- br x1 - 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. + // Jump to Rust code.
+ b _start_rust + 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 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 --- 06_uart_chainloader/src/_arch/aarch64/time.rs
+++ 07_timestamps/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 +// SPDX-License-Identifier: MIT OR Apache-2.0
+// +//
+// Copyright (c) 2018-2022 Andre Richter <andre.o.richter@gmail.com> +// 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 +//! crate::time::arch_time
+ +
+use crate::{time, warn}; +use crate::warn;
+use core::time::Duration; +use core::{
+ num::{NonZeroU128, NonZeroU32, NonZeroU64},
+ ops::{Add, Div},
+ time::Duration,
+};
+use cortex_a::{asm::barrier, registers::*}; +use cortex_a::{asm::barrier, registers::*};
+use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; +use tock_registers::interfaces::Readable;
+ +
+//-------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------
+// Private Definitions +// 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. +#[derive(Copy, Clone, PartialOrd, PartialEq)]
+struct GenericTimer; +struct GenericTimerCounterValue(u64);
+ +
+//-------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------
+// Global instances +// 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 +// Private Code
+//-------------------------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------------------------
+ +
+impl GenericTimer { +impl GenericTimerCounterValue {
+ #[inline(always)] + pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
+ 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 Add for GenericTimerCounterValue {
+// Public Code + type Output = Self;
+//--------------------------------------------------------------------------------------------------
+ +
+/// Return a reference to the time manager. + fn add(self, other: Self) -> Self {
+pub fn time_manager() -> &'static impl time::interface::TimeManager { + GenericTimerCounterValue(self.0.wrapping_add(other.0))
+ &TIME_MANAGER + }
+} +}
+ +
+//------------------------------------------------------------------------------ +impl From<GenericTimerCounterValue> for Duration {
+// OS Interface Code + fn from(counter_value: GenericTimerCounterValue) -> Self {
+//------------------------------------------------------------------------------ + if counter_value.0 == 0 {
+ return Duration::ZERO;
+ }
+ +
+impl time::interface::TimeManager for GenericTimer { + // Read volatile is needed here to prevent the compiler from optimizing
+ fn resolution(&self) -> Duration { + // ARCH_TIMER_COUNTER_FREQUENCY away.
+ Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) + //
+ // 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 { +fn max_duration() -> Duration {
+ let current_count: u64 = self.read_cntpct() * NS_PER_S; + Duration::from(GenericTimerCounterValue::MAX)
+ let frq: u64 = CNTFRQ_EL0.get() as u64; +}
+ +
+ Duration::from_nanos(current_count / frq) +impl TryFrom<Duration> for GenericTimerCounterValue {
+ } + type Error = &'static str;
+ +
+ fn spin_for(&self, duration: Duration) { + fn try_from(duration: Duration) -> Result<Self, Self::Error> {
+ // Instantly return on zero. + if duration < resolution() {
+ if duration.as_nanos() == 0 { + return Ok(GenericTimerCounterValue(0));
+ return;
+ } + }
+ +
+ // Calculate the register compare value. + if duration > max_duration() {
+ let frq = CNTFRQ_EL0.get(); + return Err("Conversion error. Duration too big");
+ 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;
+ } + }
+ +
+ // Set the compare value register. + // This is safe, because all the safety requirements as stated in read_volatile()'s
+ CNTP_TVAL_EL0.set(tval); + // 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. + // This is safe, because frequency can never be greater than u32::MAX, and
+ CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); + // (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. + // Since we checked above that we are <= max_duration(), just cast to u64.
+ while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} + Ok(GenericTimerCounterValue(counter_value as u64))
+
+ // Disable counting again.
+ CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
+ } + }
+} +}
+
+#[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 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 --- 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")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
- use crate::cpu; - use crate::cpu;
+ use crate::{time, time::interface::TimeManager}; + use crate::time;
+ use core::time::Duration; + use core::time::Duration;
- // Make an educated guess for a good delay value (Sequence described in the BCM2837 - // 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 diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
--- 06_uart_chainloader/src/main.rs --- 06_uart_chainloader/src/main.rs
+++ 07_timestamps/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 panic_wait;
mod print; mod print;
mod synchronization; mod synchronization;
@ -542,7 +604,7 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
/// Early init code. /// Early init code.
/// ///
@@ -143,55 +144,38 @@ @@ -143,55 +147,37 @@
kernel_main() kernel_main()
} }
@ -558,7 +620,6 @@ diff -uNr 06_uart_chainloader/src/main.rs 07_timestamps/src/main.rs
- use console::console; - use console::console;
+ use core::time::Duration; + use core::time::Duration;
+ use driver::interface::DriverManager; + use driver::interface::DriverManager;
+ use time::interface::TimeManager;
- println!("{}", MINILOAD_LOGO); - println!("{}", MINILOAD_LOGO);
- println!("{:^37}", bsp::board_name()); - 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 diff -uNr 06_uart_chainloader/src/panic_wait.rs 07_timestamps/src/panic_wait.rs
--- 06_uart_chainloader/src/panic_wait.rs --- 06_uart_chainloader/src/panic_wait.rs
+++ 07_timestamps/src/panic_wait.rs +++ 07_timestamps/src/panic_wait.rs
@@ -42,18 +42,23 @@ @@ -45,15 +45,18 @@
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
+ use crate::time::interface::TimeManager;
+
// Protect against panic infinite loops if any of the following code panics itself. // Protect against panic infinite loops if any of the following code panics itself.
panic_prevent_reenter(); 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 diff -uNr 06_uart_chainloader/src/print.rs 07_timestamps/src/print.rs
--- 06_uart_chainloader/src/print.rs --- 06_uart_chainloader/src/print.rs
+++ 07_timestamps/src/print.rs +++ 07_timestamps/src/print.rs
@@ -34,3 +34,59 @@ @@ -34,3 +34,51 @@
$crate::print::_print(format_args_nl!($($arg)*)); $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_export]
+macro_rules! info { +macro_rules! info {
+ ($string:expr) => ({ + ($string:expr) => ({
+ use $crate::time::interface::TimeManager;
+
+ let timestamp = $crate::time::time_manager().uptime(); + let timestamp = $crate::time::time_manager().uptime();
+ +
+ $crate::print::_print(format_args_nl!( + $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)*) => ({ + ($format_string:expr, $($arg:tt)*) => ({
+ use $crate::time::interface::TimeManager;
+
+ let timestamp = $crate::time::time_manager().uptime(); + let timestamp = $crate::time::time_manager().uptime();
+ +
+ $crate::print::_print(format_args_nl!( + $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_export]
+macro_rules! warn { +macro_rules! warn {
+ ($string:expr) => ({ + ($string:expr) => ({
+ use $crate::time::interface::TimeManager;
+
+ let timestamp = $crate::time::time_manager().uptime(); + let timestamp = $crate::time::time_manager().uptime();
+ +
+ $crate::print::_print(format_args_nl!( + $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)*) => ({ + ($format_string:expr, $($arg:tt)*) => ({
+ use $crate::time::interface::TimeManager;
+
+ let timestamp = $crate::time::time_manager().uptime(); + let timestamp = $crate::time::time_manager().uptime();
+ +
+ $crate::print::_print(format_args_nl!( + $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 diff -uNr 06_uart_chainloader/src/time.rs 07_timestamps/src/time.rs
--- 06_uart_chainloader/src/time.rs --- 06_uart_chainloader/src/time.rs
+++ 07_timestamps/src/time.rs +++ 07_timestamps/src/time.rs
@@ -0,0 +1,37 @@ @@ -0,0 +1,57 @@
+// SPDX-License-Identifier: MIT OR Apache-2.0 +// SPDX-License-Identifier: MIT OR Apache-2.0
+// +//
+// Copyright (c) 2020-2022 Andre Richter <andre.o.richter@gmail.com> +// 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"] +#[path = "_arch/aarch64/time.rs"]
+mod arch_time; +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. +static TIME_MANAGER: TimeManager = TimeManager::new();
+pub mod interface {
+ use core::time::Duration;
+ +
+ /// Time management functions. +//--------------------------------------------------------------------------------------------------
+ pub trait TimeManager { +// Public Code
+ /// The timer's resolution. +//--------------------------------------------------------------------------------------------------
+ fn resolution(&self) -> Duration;
+ +
+ /// The uptime since power-on of the device. +/// Return a reference to the global TimeManager.
+ /// +pub fn time_manager() -> &'static TimeManager {
+ /// This includes time consumed by firmware and bootloaders. + &TIME_MANAGER
+ fn uptime(&self) -> Duration; +}
+
+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. + /// Spin for a given duration.
+ fn spin_for(&self, duration: 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 ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0 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. // Jump to Rust code.
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: 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 ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0 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. // Jump to Rust code.
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: 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. // Only proceed on the boot core. Park it otherwise.
mrs x1, MPIDR_EL1 mrs x1, MPIDR_EL1
and x1, x1, {CONST_CORE_ID_MASK} and x1, x1, {CONST_CORE_ID_MASK}
@@ -48,11 +53,11 @@ @@ -48,7 +53,7 @@
// Prepare the jump to Rust code. // Prepare the jump to Rust code.
.L_prepare_rust: .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 ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0 mov sp, x0
@@ -60,7 +65,7 @@
b.eq .L_parking_loop
str w2, [x1]
- // Jump to Rust code. - // Jump to Rust code.
+ // Jump to Rust code. x0 holds the function argument provided to _start_rust(). + // Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _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 diff -uNr 08_hw_debug_JTAG/src/main.rs 09_privilege_level/src/main.rs
--- 08_hw_debug_JTAG/src/main.rs --- 08_hw_debug_JTAG/src/main.rs
+++ 09_privilege_level/src/main.rs +++ 09_privilege_level/src/main.rs
@@ -118,6 +118,7 @@ @@ -121,6 +121,7 @@
mod console; mod console;
mod cpu; mod cpu;
mod driver; 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 panic_wait;
mod print; mod print;
mod synchronization; mod synchronization;
@@ -146,6 +147,7 @@ @@ -149,6 +150,7 @@
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { fn kernel_main() -> ! {
+ use console::console; + use console::console;
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager; use driver::interface::DriverManager;
use time::interface::TimeManager;
@@ -157,6 +159,12 @@ @@ -159,6 +161,12 @@
); );
info!("Booting on: {}", bsp::board_name()); 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!( info!(
"Architectural timer resolution: {} ns", "Architectural timer resolution: {} ns",
time::time_manager().resolution().as_nanos() time::time_manager().resolution().as_nanos()
@@ -171,11 +179,15 @@ @@ -173,11 +181,15 @@
info!(" {}. {}", i + 1, driver.compatible()); info!(" {}. {}", i + 1, driver.compatible());
} }

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0 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(). // Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: 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 diff -uNr 09_privilege_level/src/main.rs 10_virtual_mem_part1_identity_mapping/src/main.rs
--- 09_privilege_level/src/main.rs --- 09_privilege_level/src/main.rs
+++ 10_virtual_mem_part1_identity_mapping/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()`. //! 2. Once finished with architectural setup, the arch code calls `kernel_init()`.
#![allow(clippy::upper_case_acronyms)] #![allow(clippy::upper_case_acronyms)]
+#![allow(incomplete_features)] +#![allow(incomplete_features)]
#![feature(asm_const)] #![feature(asm_const)]
#![feature(const_option)]
+#![feature(core_intrinsics)] +#![feature(core_intrinsics)]
#![feature(format_args_nl)] #![feature(format_args_nl)]
+#![feature(int_roundings)] +#![feature(int_roundings)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)] #![feature(panic_info_message)]
#![feature(trait_alias)] #![feature(trait_alias)]
#![no_main] @@ -118,10 +121,12 @@
#![no_std] #![no_std]
mod bsp; 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 panic_wait;
mod print; mod print;
mod synchronization; mod synchronization;
@@ -129,9 +134,17 @@ @@ -132,9 +137,17 @@
/// # Safety /// # Safety
/// ///
/// - Only a single core must be active and running this function. /// - 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() { for i in bsp::driver::driver_manager().all_device_drivers().iter() {
if let Err(x) = i.init() { if let Err(x) = i.init() {
@@ -147,7 +160,7 @@ @@ -150,7 +163,7 @@
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { 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 console::{console, interface::Write};
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager; use driver::interface::DriverManager;
use time::interface::TimeManager;
@@ -159,6 +172,9 @@ @@ -161,6 +174,9 @@
); );
info!("Booting on: {}", bsp::board_name()); 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(); let (_, privilege_level) = exception::current_privilege_level();
info!("Current privilege level: {}", privilege_level); info!("Current privilege level: {}", privilege_level);
@@ -182,6 +198,13 @@ @@ -184,6 +200,13 @@
info!("Timer test, spinning for 1 second"); info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1)); time::time_manager().spin_for(Duration::from_secs(1));

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0 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(). // Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: 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 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 --- 10_virtual_mem_part1_identity_mapping/src/main.rs
+++ 11_exceptions_part1_groundwork/src/main.rs +++ 11_exceptions_part1_groundwork/src/main.rs
@@ -142,6 +142,8 @@ @@ -145,6 +145,8 @@
use driver::interface::DriverManager; use driver::interface::DriverManager;
use memory::mmu::interface::MMU; 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() { if let Err(string) = memory::mmu::mmu().enable_mmu_and_caching() {
panic!("MMU: {}", string); panic!("MMU: {}", string);
} }
@@ -160,7 +162,7 @@ @@ -163,7 +165,7 @@
/// The main function running after the early init. /// The main function running after the early init.
fn kernel_main() -> ! { 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 console::console;
use core::time::Duration; use core::time::Duration;
use driver::interface::DriverManager; use driver::interface::DriverManager;
use time::interface::TimeManager;
@@ -198,13 +200,28 @@ @@ -200,13 +202,28 @@
info!("Timer test, spinning for 1 second"); info!("Timer test, spinning for 1 second");
time::time_manager().spin_for(Duration::from_secs(1)); time::time_manager().spin_for(Duration::from_secs(1));

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0 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(). // Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: 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)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; 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; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
@ -643,6 +643,7 @@ unsafe fn kernel_init() -> ! {
#[kernel_test] #[kernel_test]
fn timer_is_counting() { fn timer_is_counting() {
assert!(time::time_manager().uptime().as_nanos() > 0) assert!(time::time_manager().uptime().as_nanos() > 0)
assert!(time::time_manager().resolution().as_nanos() < 100)
} }
/// Timer resolution must be sufficient. /// Timer resolution must be sufficient.

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0 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(). // Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: Duration); pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
} }
} }

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; 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; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
@ -37,6 +37,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient. /// Timer resolution must be sufficient.
#[kernel_test] #[kernel_test]
fn timer_resolution_is_sufficient() { fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100) 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 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 --- 12_integrated_testing/kernel/src/lib.rs
+++ 13_exceptions_part2_peripheral_IRQs/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 exception;
pub mod memory; pub mod memory;
pub mod print; 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; use core::panic::PanicInfo;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -61,6 +61,8 @@ @@ -59,6 +59,8 @@
fn panic(info: &PanicInfo) -> ! {
use crate::time::interface::TimeManager;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
+ exception::asynchronous::local_irq_mask(); + exception::asynchronous::local_irq_mask();
+ +
// Protect against panic infinite loops if any of the following code panics itself. // 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 ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0 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(). // Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: Duration); pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
} }
} }

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; 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; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
@ -37,6 +37,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient. /// Timer resolution must be sufficient.
#[kernel_test] #[kernel_test]
fn timer_resolution_is_sufficient() { fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100) 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 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 --- 13_exceptions_part2_peripheral_IRQs/kernel/src/lib.rs
+++ 14_virtual_mem_part2_mmio_remap/kernel/src/lib.rs +++ 14_virtual_mem_part2_mmio_remap/kernel/src/lib.rs
@@ -113,9 +113,12 @@ @@ -114,10 +114,13 @@
#![feature(asm_const)] #![feature(const_option)]
#![feature(core_intrinsics)] #![feature(core_intrinsics)]
#![feature(format_args_nl)] #![feature(format_args_nl)]
+#![feature(generic_const_exprs)] +#![feature(generic_const_exprs)]
#![feature(int_roundings)] #![feature(int_roundings)]
+#![feature(is_sorted)] +#![feature(is_sorted)]
#![feature(linkage)] #![feature(linkage)]
#![feature(nonzero_min_max)]
#![feature(panic_info_message)] #![feature(panic_info_message)]
+#![feature(step_trait)] +#![feature(step_trait)]
#![feature(trait_alias)] #![feature(trait_alias)]
#![feature(unchecked_math)]
#![no_std] #![no_std]
// Testing @@ -186,6 +189,17 @@
@@ -183,6 +186,17 @@
use driver::interface::DriverManager; use driver::interface::DriverManager;
exception::handling_init(); 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)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; use core::time::Duration;
-use libkernel::{bsp, cpu, driver, exception, time, time::interface::TimeManager}; -use libkernel::{bsp, cpu, driver, exception, time};
+use libkernel::{bsp, cpu, driver, exception, memory, time, time::interface::TimeManager}; +use libkernel::{bsp, cpu, driver, exception, memory, time};
use test_macros::kernel_test; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]

@ -57,6 +57,14 @@ _start:
ADR_REL x0, __boot_core_stack_end_exclusive ADR_REL x0, __boot_core_stack_end_exclusive
mov sp, x0 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(). // Jump to Rust code. x0 holds the function argument provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: Duration); pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
} }
} }

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; 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; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
@ -48,6 +48,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient. /// Timer resolution must be sufficient.
#[kernel_test] #[kernel_test]
fn timer_resolution_is_sufficient() { fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100) 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 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 --- 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 +++ 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. // Prepare the jump to Rust code.
.L_prepare_rust: .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 + ADR_REL x1, __boot_core_stack_end_exclusive
+ mov sp, x1 + 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 holds the function argument provided to _start_rust().
+ // Jump to Rust code. x0 and x1 hold the function arguments provided to _start_rust(). + // Jump to Rust code. x0 and x1 hold the function arguments provided to _start_rust().
b _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 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 --- 14_virtual_mem_part2_mmio_remap/kernel/src/lib.rs
+++ 15_virtual_mem_part3_precomputed_tables/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; use driver::interface::DriverManager;
exception::handling_init(); exception::handling_init();

@ -60,6 +60,14 @@ _start:
ADR_REL x1, __boot_core_stack_end_exclusive ADR_REL x1, __boot_core_stack_end_exclusive
mov sp, x1 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(). // Jump to Rust code. x0 and x1 hold the function arguments provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: Duration); pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
} }
} }

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; 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; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
@ -38,6 +38,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient. /// Timer resolution must be sufficient.
#[kernel_test] #[kernel_test]
fn timer_resolution_is_sufficient() { fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100) 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 // Public Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -56,11 +68,23 @@ @@ -56,19 +68,31 @@
// Load the base address of the kernel's translation tables. // 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 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 + ADR_REL x4, __boot_core_stack_end_exclusive
+ mov sp, x4 + 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 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(). + // Jump to Rust code. x0, x1 and x2 hold the function arguments provided to _start_rust().
b _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 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 --- 15_virtual_mem_part3_precomputed_tables/kernel/src/lib.rs
+++ 16_virtual_mem_part4_higher_half_kernel/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 ADR_REL x4, __boot_core_stack_end_exclusive
mov sp, x4 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(). // Jump to Rust code. x0, x1 and x2 hold the function arguments provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: Duration); pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
} }
} }

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; 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; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
@ -38,6 +38,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient. /// Timer resolution must be sufficient.
#[kernel_test] #[kernel_test]
fn timer_resolution_is_sufficient() { fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100) 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 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 --- 16_virtual_mem_part4_higher_half_kernel/kernel/src/lib.rs
+++ 17_kernel_symbols/kernel/src/lib.rs +++ 17_kernel_symbols/kernel/src/lib.rs
@@ -139,6 +139,7 @@ @@ -142,6 +142,7 @@
pub mod memory; pub mod memory;
pub mod print; pub mod print;
pub mod state; pub mod state;

@ -84,6 +84,14 @@ _start:
ADR_REL x4, __boot_core_stack_end_exclusive ADR_REL x4, __boot_core_stack_end_exclusive
mov sp, x4 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(). // Jump to Rust code. x0, x1 and x2 hold the function arguments provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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

@ -8,30 +8,50 @@
#[path = "_arch/aarch64/time.rs"] #[path = "_arch/aarch64/time.rs"]
mod arch_time; 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. static TIME_MANAGER: TimeManager = TimeManager::new();
pub mod interface {
use core::time::Duration;
/// Time management functions. //--------------------------------------------------------------------------------------------------
pub trait TimeManager { // Public Code
/// The timer's resolution. //--------------------------------------------------------------------------------------------------
fn resolution(&self) -> Duration;
/// The uptime since power-on of the device. /// Return a reference to the global TimeManager.
/// pub fn time_manager() -> &'static TimeManager {
/// This includes time consumed by firmware and bootloaders. &TIME_MANAGER
fn uptime(&self) -> Duration; }
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. /// Spin for a given duration.
fn spin_for(&self, duration: Duration); pub fn spin_for(&self, duration: Duration) {
arch_time::spin_for(duration)
} }
} }

@ -11,7 +11,7 @@
#![test_runner(libkernel::test_runner)] #![test_runner(libkernel::test_runner)]
use core::time::Duration; 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; use test_macros::kernel_test;
#[no_mangle] #[no_mangle]
@ -38,6 +38,7 @@ fn timer_is_counting() {
/// Timer resolution must be sufficient. /// Timer resolution must be sufficient.
#[kernel_test] #[kernel_test]
fn timer_resolution_is_sufficient() { fn timer_resolution_is_sufficient() {
assert!(time::time_manager().resolution().as_nanos() > 0);
assert!(time::time_manager().resolution().as_nanos() < 100) 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 diff -uNr 17_kernel_symbols/kernel/src/lib.rs 18_backtrace/kernel/src/lib.rs
--- 17_kernel_symbols/kernel/src/lib.rs --- 17_kernel_symbols/kernel/src/lib.rs
+++ 18_backtrace/kernel/src/lib.rs +++ 18_backtrace/kernel/src/lib.rs
@@ -130,6 +130,7 @@ @@ -133,6 +133,7 @@
mod panic_wait; mod panic_wait;
mod synchronization; 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; use core::panic::PanicInfo;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
@@ -75,6 +75,7 @@ @@ -73,6 +73,7 @@
println!( println!(
"[ {:>3}.{:06}] Kernel panic!\n\n\ "[ {:>3}.{:06}] Kernel panic!\n\n\
Panic location:\n File '{}', line {}, column {}\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.as_secs(),
timestamp.subsec_micros(), timestamp.subsec_micros(),
@@ -82,6 +83,7 @@ @@ -80,6 +81,7 @@
line, line,
column, column,
info.message().unwrap_or(&format_args!("")), info.message().unwrap_or(&format_args!("")),

@ -84,6 +84,14 @@ _start:
ADR_REL x4, __boot_core_stack_end_exclusive ADR_REL x4, __boot_core_stack_end_exclusive
mov sp, x4 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(). // Jump to Rust code. x0, x1 and x2 hold the function arguments provided to _start_rust().
b _start_rust b _start_rust

@ -11,111 +11,152 @@
//! //!
//! crate::time::arch_time //! crate::time::arch_time
use crate::{time, warn}; use crate::warn;
use core::time::Duration; use core::{
num::{NonZeroU128, NonZeroU32, NonZeroU64},
ops::{Add, Div},
time::Duration,
};
use cortex_a::{asm::barrier, registers::*}; use cortex_a::{asm::barrier, registers::*};
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; use tock_registers::interfaces::Readable;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Private Definitions // 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. #[derive(Copy, Clone, PartialOrd, PartialEq)]
struct GenericTimer; struct GenericTimerCounterValue(u64);
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// Global instances // 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 // Private Code
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
impl GenericTimer { impl GenericTimerCounterValue {
#[inline(always)] pub const MAX: Self = GenericTimerCounterValue(u64::MAX);
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 Add for GenericTimerCounterValue {
// Public Code type Output = Self;
//--------------------------------------------------------------------------------------------------
/// Return a reference to the time manager. fn add(self, other: Self) -> Self {
pub fn time_manager() -> &'static impl time::interface::TimeManager { GenericTimerCounterValue(self.0.wrapping_add(other.0))
&TIME_MANAGER }
} }
//------------------------------------------------------------------------------ impl From<GenericTimerCounterValue> for Duration {
// OS Interface Code fn from(counter_value: GenericTimerCounterValue) -> Self {
//------------------------------------------------------------------------------ if counter_value.0 == 0 {
return Duration::ZERO;
}
impl time::interface::TimeManager for GenericTimer { // Read volatile is needed here to prevent the compiler from optimizing
fn resolution(&self) -> Duration { // ARCH_TIMER_COUNTER_FREQUENCY away.
Duration::from_nanos(NS_PER_S / (CNTFRQ_EL0.get() as u64)) //
// 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 { fn max_duration() -> Duration {
let current_count: u64 = self.read_cntpct() * NS_PER_S; Duration::from(GenericTimerCounterValue::MAX)
let frq: u64 = CNTFRQ_EL0.get() as u64; }
Duration::from_nanos(current_count / frq) impl TryFrom<Duration> for GenericTimerCounterValue {
} type Error = &'static str;
fn spin_for(&self, duration: Duration) { fn try_from(duration: Duration) -> Result<Self, Self::Error> {
// Instantly return on zero. if duration < resolution() {
if duration.as_nanos() == 0 { return Ok(GenericTimerCounterValue(0));
return;
} }
// Calculate the register compare value. if duration > max_duration() {
let frq = CNTFRQ_EL0.get(); return Err("Conversion error. Duration too big");
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;
} }
// Set the compare value register. // This is safe, because all the safety requirements as stated in read_volatile()'s
CNTP_TVAL_EL0.set(tval); // 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. // This is safe, because frequency can never be greater than u32::MAX, and
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET); // (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. // Since we checked above that we are <= max_duration(), just cast to u64.
while !CNTP_CTL_EL0.matches_all(CNTP_CTL_EL0::ISTATUS::SET) {} Ok(GenericTimerCounterValue(counter_value as u64))
// Disable counting again.
CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::CLEAR);
} }
} }
#[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. /// Disable pull-up/down on pins 14 and 15.
#[cfg(feature = "bsp_rpi3")] #[cfg(feature = "bsp_rpi3")]
fn disable_pud_14_15_bcm2837(&mut self) { fn disable_pud_14_15_bcm2837(&mut self) {
use crate::{time, time::interface::TimeManager}; use crate::time;
use core::time::Duration; use core::time::Duration;
// The Linux 2837 GPIO driver waits 1 µs between the steps. // The Linux 2837 GPIO driver waits 1 µs between the steps.

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

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

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