Skip to content

uhyve: using bootinfo to determine the boot time #1703

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
May 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ memory_addresses = { version = "0.2.2", default-features = false, features = [
] }

[target.'cfg(target_arch = "aarch64")'.dependencies]
aarch64 = { version = "0.0.13", default-features = false }
aarch64 = { version = "0.0.14", default-features = false }
arm-gic = { version = "0.3" }
hermit-dtb = { version = "0.1" }
semihosting = { version = "0.1", optional = true }
Expand Down
78 changes: 29 additions & 49 deletions src/arch/aarch64/kernel/processor.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
use core::arch::asm;
use core::{fmt, str};

use aarch64::regs::{CNTFRQ_EL0, Readable};
use aarch64::regs::*;
use hermit_dtb::Dtb;
use hermit_sync::{Lazy, without_interrupts};
use hermit_sync::{Lazy, OnceCell, without_interrupts};

use crate::env;

// System counter frequency in Hz
// System counter frequency in KHz
static CPU_FREQUENCY: Lazy<CpuFrequency> = Lazy::new(|| {
let mut cpu_frequency = CpuFrequency::new();
unsafe {
cpu_frequency.detect();
}
cpu_frequency
});
// Value of CNTPCT_EL0 at boot time
static BOOT_COUNTER: OnceCell<u64> = OnceCell::new();

enum CpuFrequencySources {
Invalid,
Expand All @@ -35,27 +37,27 @@ impl fmt::Display for CpuFrequencySources {
}

struct CpuFrequency {
hz: u32,
khz: u32,
source: CpuFrequencySources,
}

impl CpuFrequency {
const fn new() -> Self {
CpuFrequency {
hz: 0,
khz: 0,
source: CpuFrequencySources::Invalid,
}
}

fn set_detected_cpu_frequency(
&mut self,
hz: u32,
khz: u32,
source: CpuFrequencySources,
) -> Result<(), ()> {
//The clock frequency must never be set to zero, otherwise a division by zero will
//occur during runtime
if hz > 0 {
self.hz = hz;
if khz > 0 {
self.khz = khz;
self.source = source;
Ok(())
} else {
Expand All @@ -65,15 +67,12 @@ impl CpuFrequency {

unsafe fn detect_from_cmdline(&mut self) -> Result<(), ()> {
let mhz = env::freq().ok_or(())?;
self.set_detected_cpu_frequency(
u32::from(mhz) * 1_000_000,
CpuFrequencySources::CommandLine,
)
self.set_detected_cpu_frequency(u32::from(mhz) * 1000, CpuFrequencySources::CommandLine)
}

unsafe fn detect_from_register(&mut self) -> Result<(), ()> {
let hz = CNTFRQ_EL0.get() & 0xffff_ffff;
self.set_detected_cpu_frequency(hz.try_into().unwrap(), CpuFrequencySources::Register)
let khz = (CNTFRQ_EL0.get() & 0xffff_ffff) / 1000;
self.set_detected_cpu_frequency(khz.try_into().unwrap(), CpuFrequencySources::Register)
}

unsafe fn detect(&mut self) {
Expand All @@ -85,13 +84,13 @@ impl CpuFrequency {
}

fn get(&self) -> u32 {
self.hz
self.khz
}
}

impl fmt::Display for CpuFrequency {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} Hz (from {})", self.hz, self.source)
write!(f, "{} KHz (from {})", self.khz, self.source)
}
}

Expand Down Expand Up @@ -136,29 +135,21 @@ pub fn shutdown(error_code: i32) -> ! {
#[inline]
pub fn get_timer_ticks() -> u64 {
// We simulate a timer with a 1 microsecond resolution by taking the CPU timestamp
// and dividing it by the CPU frequency in MHz.
let ticks = 1_000_000 * u128::from(get_timestamp()) / u128::from(CPU_FREQUENCY.get());
u64::try_from(ticks).unwrap()
// and dividing it by the CPU frequency (in KHz).

let freq: u64 = CPU_FREQUENCY.get().into(); // frequency in KHz
1000 * get_timestamp() / freq
}

/// Returns the timer frequency in MHz
#[inline]
pub fn get_frequency() -> u16 {
(CPU_FREQUENCY.get() / 1_000_000).try_into().unwrap()
(CPU_FREQUENCY.get() / 1_000).try_into().unwrap()
}

#[inline]
pub fn get_timestamp() -> u64 {
let value: u64;

unsafe {
asm!(
"mrs {value}, cntpct_el0",
value = out(reg) value,
options(nostack),
);
}

value
CNTPCT_EL0.get() - BOOT_COUNTER.get().unwrap()
}

#[inline]
Expand Down Expand Up @@ -214,34 +205,23 @@ pub fn configure() {
}

pub fn detect_frequency() {
BOOT_COUNTER.set(CNTPCT_EL0.get()).unwrap();
Lazy::force(&CPU_FREQUENCY);
}

#[inline]
fn __set_oneshot_timer(wakeup_time: Option<u64>) {
if let Some(wt) = wakeup_time {
// wt is the absolute wakeup time in microseconds based on processor::get_timer_ticks.
let deadline = u128::from(wt) * u128::from(CPU_FREQUENCY.get()) / 1_000_000;
let deadline = u64::try_from(deadline).unwrap();
let freq: u64 = CPU_FREQUENCY.get().into(); // frequency in KHz
let deadline = (wt / 1000) * freq;

unsafe {
asm!(
"msr cntp_cval_el0, {value}",
"msr cntp_ctl_el0, {enable}",
value = in(reg) deadline,
enable = in(reg) 1u64,
options(nostack, nomem),
);
}
CNTP_CVAL_EL0.set(deadline);
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::SET);
} else {
// disable timer
unsafe {
asm!(
"msr cntp_cval_el0, xzr",
"msr cntp_ctl_el0, xzr",
options(nostack, nomem),
);
}
CNTP_CVAL_EL0.set(0);
CNTP_CTL_EL0.write(CNTP_CTL_EL0::ENABLE::CLEAR);
}
}

Expand Down
106 changes: 59 additions & 47 deletions src/arch/aarch64/kernel/systemtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use core::arch::asm;
use core::str;

use hermit_dtb::Dtb;
use hermit_entry::boot_info::PlatformInfo;
use hermit_sync::OnceCell;
use memory_addresses::arch::aarch64::{PhysAddr, VirtAddr};
use time::OffsetDateTime;
Expand Down Expand Up @@ -46,57 +47,68 @@ fn rtc_read(off: usize) -> u32 {
}

pub fn init() {
let dtb = unsafe {
Dtb::from_raw(core::ptr::with_exposed_provenance(
env::boot_info().hardware_info.device_tree.unwrap().get() as usize,
))
.expect(".dtb file has invalid header")
};
match env::boot_info().platform_info {
PlatformInfo::Uhyve { boot_time, .. } => {
PL031_ADDRESS.set(VirtAddr::zero()).unwrap();
BOOT_TIME.set(u64::try_from(boot_time.unix_timestamp_nanos() / 1000).unwrap());
info!("Hermit booted on {boot_time}");

for node in dtb.enum_subnodes("/") {
let parts: Vec<_> = node.split('@').collect();

if let Some(compatible) = dtb.get_property(parts.first().unwrap(), "compatible") {
if str::from_utf8(compatible).unwrap().contains("pl031") {
let reg = dtb.get_property(parts.first().unwrap(), "reg").unwrap();
let (slice, residual_slice) = reg.split_at(core::mem::size_of::<u64>());
let addr = PhysAddr::new(u64::from_be_bytes(slice.try_into().unwrap()));
let (slice, _residual_slice) = residual_slice.split_at(core::mem::size_of::<u64>());
let size = u64::from_be_bytes(slice.try_into().unwrap());

debug!("Found RTC at {addr:p} (size {size:#X})");

let pl031_address = virtualmem::allocate_aligned(
size.try_into().unwrap(),
BasePageSize::SIZE.try_into().unwrap(),
)
.unwrap();
PL031_ADDRESS.set(pl031_address).unwrap();
debug!("Mapping RTC to virtual address {pl031_address:p}",);

let mut flags = PageTableEntryFlags::empty();
flags.device().writable().execute_disable();
paging::map::<BasePageSize>(
pl031_address,
addr,
(size / BasePageSize::SIZE).try_into().unwrap(),
flags,
);

let boot_time =
OffsetDateTime::from_unix_timestamp(rtc_read(RTC_DR).into()).unwrap();
info!("Hermit booted on {boot_time}");

let micros = u64::try_from(boot_time.unix_timestamp_nanos() / 1000).unwrap();
let current_ticks = super::processor::get_timer_ticks();
BOOT_TIME.set(micros - current_ticks).unwrap();

return;
return;
}
_ => {
let dtb = unsafe {
Dtb::from_raw(core::ptr::with_exposed_provenance(
env::boot_info().hardware_info.device_tree.unwrap().get() as usize,
))
.expect(".dtb file has invalid header")
};

for node in dtb.enum_subnodes("/") {
let parts: Vec<_> = node.split('@').collect();

if let Some(compatible) = dtb.get_property(parts.first().unwrap(), "compatible") {
if str::from_utf8(compatible).unwrap().contains("pl031") {
let reg = dtb.get_property(parts.first().unwrap(), "reg").unwrap();
let (slice, residual_slice) = reg.split_at(core::mem::size_of::<u64>());
let addr = PhysAddr::new(u64::from_be_bytes(slice.try_into().unwrap()));
let (slice, _residual_slice) =
residual_slice.split_at(core::mem::size_of::<u64>());
let size = u64::from_be_bytes(slice.try_into().unwrap());

debug!("Found RTC at {addr:p} (size {size:#X})");

let pl031_address = virtualmem::allocate_aligned(
size.try_into().unwrap(),
BasePageSize::SIZE.try_into().unwrap(),
)
.unwrap();
PL031_ADDRESS.set(pl031_address).unwrap();
debug!("Mapping RTC to virtual address {pl031_address:p}",);

let mut flags = PageTableEntryFlags::empty();
flags.device().writable().execute_disable();
paging::map::<BasePageSize>(
pl031_address,
addr,
(size / BasePageSize::SIZE).try_into().unwrap(),
flags,
);

let boot_time =
OffsetDateTime::from_unix_timestamp(rtc_read(RTC_DR).into()).unwrap();
info!("Hermit booted on {boot_time}");

BOOT_TIME
.set(u64::try_from(boot_time.unix_timestamp_nanos() / 1000).unwrap())
.unwrap();

return;
}
}
}
}
}
};

PL031_ADDRESS.set(VirtAddr::zero()).unwrap();
BOOT_TIME.set(0).unwrap();
}

Expand Down
1 change: 1 addition & 0 deletions src/arch/riscv64/kernel/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub fn get_timer_ticks() -> u64 {
get_timestamp() / u64::from(get_frequency())
}

/// Returns the timer frequency in MHz
pub fn get_frequency() -> u16 {
(get_timebase_freq() / 1_000_000).try_into().unwrap()
}
Expand Down
1 change: 1 addition & 0 deletions src/arch/x86_64/kernel/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1113,6 +1113,7 @@ pub fn get_timer_ticks() -> u64 {
get_timestamp() / u64::from(get_frequency())
}

/// Returns the timer frequency in MHz
pub fn get_frequency() -> u16 {
CPU_FREQUENCY.get()
}
Expand Down