diff --git a/alioth/src/device/clock.rs b/alioth/src/device/clock.rs index 0e84d720..deceac88 100644 --- a/alioth/src/device/clock.rs +++ b/alioth/src/device/clock.rs @@ -13,19 +13,20 @@ // limitations under the License. use std::fmt::Debug; - -use chrono::{DateTime, Utc}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; pub trait Clock: Debug + Send + Sync + 'static { - fn now(&self) -> DateTime; + fn now(&self) -> Duration; } #[derive(Debug)] pub struct SystemClock; impl Clock for SystemClock { - fn now(&self) -> DateTime { - Utc::now() + fn now(&self) -> Duration { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() } } diff --git a/alioth/src/device/clock_test.rs b/alioth/src/device/clock_test.rs index 57ae635b..521f5257 100644 --- a/alioth/src/device/clock_test.rs +++ b/alioth/src/device/clock_test.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + use chrono::{DateTime, Utc}; use crate::device::clock::{Clock, SystemClock}; @@ -22,15 +24,22 @@ pub struct TestClock { } impl Clock for TestClock { - fn now(&self) -> DateTime { - self.now + fn now(&self) -> Duration { + let nanos = (self.now - DateTime::UNIX_EPOCH).num_nanoseconds().unwrap(); + Duration::from_nanos(nanos as u64) + } +} + +impl TestClock { + pub fn tick(&mut self) { + self.now += Duration::from_secs(1); } } #[test] fn test_system_clock() { let now = SystemClock.now(); - let utc_now = Utc::now(); - let diff = utc_now - now; - assert!(diff.num_seconds() < 1); + let sys_now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let diff = sys_now - now; + assert!(diff.as_secs() < 1); } diff --git a/alioth/src/device/cmos.rs b/alioth/src/device/cmos.rs index 15b18fa0..4a3b8834 100644 --- a/alioth/src/device/cmos.rs +++ b/alioth/src/device/cmos.rs @@ -15,7 +15,7 @@ use std::sync::atomic::{AtomicU8, Ordering}; use bitfield::bitfield; -use chrono::{Datelike, Timelike}; +use chrono::{DateTime, Datelike, Timelike}; use crate::device::clock::Clock; use crate::device::{MmioDev, Pause, Result}; @@ -96,7 +96,8 @@ impl Mmio for Cmos { if offset == 0 { return Ok(reg as u64); } - let now = self.clock.now(); + let nanos = self.clock.now().as_nanos(); + let now = DateTime::from_timestamp_nanos(nanos as i64); let ret = match CmosReg(reg).reg() { 0x00 => now.second(), 0x02 => now.minute(), diff --git a/alioth/src/device/pl031.rs b/alioth/src/device/pl031.rs index c328294f..3ca2b416 100644 --- a/alioth/src/device/pl031.rs +++ b/alioth/src/device/pl031.rs @@ -15,11 +15,10 @@ //! Emulated PL031 Real Time Clock (RTC) device. //! See: https://developer.arm.com/documentation/ddi0224/c -use std::time::{SystemTime, UNIX_EPOCH}; - use parking_lot::Mutex; -use crate::device::{self, MmioDev, Pause}; +use crate::device::clock::Clock; +use crate::device::{MmioDev, Pause, Result}; use crate::mem::emulated::{Action, Mmio}; use crate::{bitflags, mem}; @@ -59,31 +58,29 @@ struct Pl031Reg { } #[derive(Debug)] -pub struct Pl031 { +pub struct Pl031 { name: Box, reg: Mutex, + clock: C, } -impl Pl031 { - pub fn new(base_addr: u64) -> Self { +impl Pl031 { + pub fn new(base_addr: u64, clock: C) -> Self { Self { name: Box::from(format!("pl031@{base_addr:x}")), reg: Mutex::new(Pl031Reg::default()), + clock, } } +} - fn now() -> u32 { - match SystemTime::now().duration_since(UNIX_EPOCH) { - Ok(duration) => duration.as_secs() as u32, - Err(_) => { - log::error!("System clock is before UNIX_EPOCH. PL031 won't work!"); - 0 - } - } +impl Pl031 { + fn now(&self) -> u32 { + self.clock.now().as_secs() as u32 } } -impl Mmio for Pl031 { +impl Mmio for Pl031 { fn size(&self) -> u64 { 0x1000 } @@ -91,7 +88,7 @@ impl Mmio for Pl031 { fn read(&self, offset: u64, _size: u8) -> mem::Result { let reg = self.reg.lock(); let val = match offset { - RTC_DR => reg.offset.wrapping_add(Self::now()), + RTC_DR => reg.offset.wrapping_add(self.now()), RTC_MR => reg.mr, RTC_LR => reg.lr, RTC_CR => 1, // RTC is always enabled @@ -119,7 +116,7 @@ impl Mmio for Pl031 { match offset { RTC_MR => reg.mr = val, RTC_LR => { - reg.offset = val.wrapping_sub(Self::now()); + reg.offset = val.wrapping_sub(self.now()); reg.lr = val; } RTC_CR => {} // RTC is always enabled @@ -142,14 +139,18 @@ impl Mmio for Pl031 { } } -impl Pause for Pl031 { - fn pause(&self) -> device::Result<()> { +impl Pause for Pl031 { + fn pause(&self) -> Result<()> { Ok(()) } - fn resume(&self) -> device::Result<()> { + fn resume(&self) -> Result<()> { Ok(()) } } -impl MmioDev for Pl031 {} +impl MmioDev for Pl031 {} + +#[cfg(test)] +#[path = "pl031_test.rs"] +mod tests; diff --git a/alioth/src/device/pl031_test.rs b/alioth/src/device/pl031_test.rs new file mode 100644 index 00000000..d0c3dcaf --- /dev/null +++ b/alioth/src/device/pl031_test.rs @@ -0,0 +1,67 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use assert_matches::assert_matches; +use chrono::DateTime; + +use crate::arch::layout::PL031_START; +use crate::device::clock::tests::TestClock; +use crate::device::pl031::{ + Pl031, RTC_CR, RTC_DR, RTC_ICR, RTC_IMSC, RTC_LR, RTC_MIS, RTC_MR, RTC_PCELL_ID0, + RTC_PCELL_ID1, RTC_PCELL_ID2, RTC_PCELL_ID3, RTC_PERIPH_ID0, RTC_PERIPH_ID1, RTC_PERIPH_ID2, + RTC_PERIPH_ID3, RTC_RIS, +}; +use crate::mem::emulated::Mmio; + +#[test] +fn test_pl031() { + // Nov 21, 2025 at 15:16:59 GMT-08:00 + let now = DateTime::from_timestamp_nanos(1763767019000_000000); + let mut pl031 = Pl031::new(PL031_START, TestClock { now }); + + assert_eq!(pl031.size(), 0x1000); + + assert_matches!(pl031.write(RTC_DR, 4, 33), Ok(_)); // ignored + assert_matches!(pl031.read(RTC_DR, 4), Ok(1763767019)); + + assert_matches!(pl031.write(RTC_LR, 4, 1763770619), Ok(_)); + pl031.clock.tick(); + assert_matches!(pl031.read(RTC_LR, 4), Ok(1763770619)); + assert_matches!(pl031.read(RTC_DR, 4), Ok(1763770620)); + + assert_matches!(pl031.write(RTC_MR, 4, 750), Ok(_)); + assert_matches!(pl031.read(RTC_MR, 4), Ok(750)); + + assert_matches!(pl031.write(RTC_CR, 4, 0), Ok(_)); // Write to CR is ignored + assert_matches!(pl031.read(RTC_CR, 4), Ok(1)); + + assert_matches!(pl031.write(RTC_IMSC, 4, 1), Ok(_)); // Unmask interrupt + assert_matches!(pl031.read(RTC_IMSC, 4), Ok(0)); // Ignored + + assert_matches!(pl031.write(RTC_ICR, 4, 1), Ok(_)); + assert_matches!(pl031.read(RTC_ICR, 4), Ok(0)); + + assert_matches!(pl031.read(RTC_RIS, 4), Ok(0)); + assert_matches!(pl031.read(RTC_MIS, 4), Ok(0)); + + assert_matches!(pl031.read(RTC_PERIPH_ID0, 4), Ok(0x31)); + assert_matches!(pl031.read(RTC_PERIPH_ID1, 4), Ok(0x10)); + assert_matches!(pl031.read(RTC_PERIPH_ID2, 4), Ok(0x04)); + assert_matches!(pl031.read(RTC_PERIPH_ID3, 4), Ok(0x00)); + + assert_matches!(pl031.read(RTC_PCELL_ID0, 4), Ok(0x0d)); + assert_matches!(pl031.read(RTC_PCELL_ID1, 4), Ok(0xf0)); + assert_matches!(pl031.read(RTC_PCELL_ID2, 4), Ok(0x05)); + assert_matches!(pl031.read(RTC_PCELL_ID3, 4), Ok(0xb1)); +} diff --git a/alioth/src/vm/vm.rs b/alioth/src/vm/vm.rs index 961788db..386a854a 100644 --- a/alioth/src/vm/vm.rs +++ b/alioth/src/vm/vm.rs @@ -28,6 +28,8 @@ use crate::arch::layout::{PL011_START, PL031_START}; #[cfg(target_arch = "x86_64")] use crate::arch::layout::{PORT_COM1, PORT_FW_CFG_SELECTOR}; use crate::board::{Board, BoardConfig}; +#[cfg(target_arch = "aarch64")] +use crate::device::clock::SystemClock; #[cfg(target_arch = "x86_64")] use crate::device::fw_cfg::{FwCfg, FwCfgItemParam}; #[cfg(target_arch = "aarch64")] @@ -169,7 +171,7 @@ where #[cfg(target_arch = "aarch64")] pub fn add_pl031(&self) { - let pl031_dev = Pl031::new(PL031_START); + let pl031_dev = Pl031::new(PL031_START, SystemClock); let mut mmio_devs = self.board.mmio_devs.write(); mmio_devs.push((PL031_START, Arc::new(pl031_dev))); }