From 54265fdae069b0d4eec603538c739b977b8aa4ae Mon Sep 17 00:00:00 2001 From: Ashwin Naren Date: Fri, 6 Mar 2026 19:42:58 -0800 Subject: [PATCH] support for ARM PL031 RTC --- Cargo.lock | 7 ++++ Cargo.toml | 1 + scripts/qemu_runner.py | 1 + src/clock/gettime.rs | 2 +- src/clock/mod.rs | 8 ++-- src/clock/realtime.rs | 2 +- src/drivers/mod.rs | 1 + src/drivers/rtc/mod.rs | 29 ++++++++++++++ src/drivers/rtc/pl031.rs | 81 ++++++++++++++++++++++++++++++++++++++++ src/main.rs | 7 ++++ 10 files changed, 133 insertions(+), 6 deletions(-) create mode 100644 src/drivers/rtc/mod.rs create mode 100644 src/drivers/rtc/pl031.rs diff --git a/Cargo.lock b/Cargo.lock index a8699e6..a43bf28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "arm_pl031" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13696b1c2b59992f4223e0ae5bb173c81c63039367ca90eee845346ad2a13421" + [[package]] name = "async-trait" version = "0.1.89" @@ -539,6 +545,7 @@ version = "0.1.0" dependencies = [ "aarch64-cpu", "arm-pl011-uart", + "arm_pl031", "async-trait", "bitflags 2.11.0", "blake2", diff --git a/Cargo.toml b/Cargo.toml index ece85b5..e3d1780 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ moss-macros = { path = "moss-macros" } aarch64-cpu = "11.1.0" arm-pl011-uart = { version = "0.5.0", default-features = false } +arm_pl031 = { version = "0.2.1", default-features = false } async-trait = { workspace = true } bitflags = { workspace = true } blake2 = { version = "0.10.6", default-features = false } diff --git a/scripts/qemu_runner.py b/scripts/qemu_runner.py index d67a62f..e519f12 100755 --- a/scripts/qemu_runner.py +++ b/scripts/qemu_runner.py @@ -38,6 +38,7 @@ default_args = { "-cpu": args.cpu, "-m": args.memory, "-smp": str(args.smp), + "-rtc": "base=utc,clock=host", "-nographic": None, "-s": None, "-kernel": bin_executable_location, diff --git a/src/clock/gettime.rs b/src/clock/gettime.rs index 4ccdc90..45b39b4 100644 --- a/src/clock/gettime.rs +++ b/src/clock/gettime.rs @@ -12,8 +12,8 @@ use crate::{drivers::timer::uptime, memory::uaccess::copy_to_user}; pub async fn sys_clock_gettime(clockid: i32, time_spec: TUA) -> Result { let time = match ClockId::try_from(clockid).map_err(|_| KernelError::InvalidValue)? { - ClockId::Monotonic => uptime(), ClockId::Realtime => date(), + ClockId::Monotonic => uptime(), ClockId::ProcessCpuTimeId => { let task = current_task_shared(); let total_time = task.process.stime.load(Ordering::Relaxed) as u64 diff --git a/src/clock/mod.rs b/src/clock/mod.rs index d73959c..3c92dc9 100644 --- a/src/clock/mod.rs +++ b/src/clock/mod.rs @@ -5,8 +5,8 @@ pub mod timeofday; pub mod timespec; pub enum ClockId { - Monotonic = 0, - Realtime = 1, + Realtime = 0, + Monotonic = 1, ProcessCpuTimeId = 2, ThreadCpuTimeId = 3, MonotonicRaw = 4, @@ -23,8 +23,8 @@ impl TryFrom for ClockId { fn try_from(value: i32) -> Result { match value { - 0 => Ok(ClockId::Monotonic), - 1 => Ok(ClockId::Realtime), + 0 => Ok(ClockId::Realtime), + 1 => Ok(ClockId::Monotonic), 2 => Ok(ClockId::ProcessCpuTimeId), 3 => Ok(ClockId::ThreadCpuTimeId), 4 => Ok(ClockId::MonotonicRaw), diff --git a/src/clock/realtime.rs b/src/clock/realtime.rs index b0efa58..d30ac6c 100644 --- a/src/clock/realtime.rs +++ b/src/clock/realtime.rs @@ -25,7 +25,7 @@ pub fn set_date(duration: Duration) { } } -// Represents a known duration since the epoch at the assoicated instant. +// Represents a known duration since the epoch at the associated instant. static EPOCH_DURATION: SpinLock> = SpinLock::new(None); #[cfg(test)] diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs index ee9c726..a3a0579 100644 --- a/src/drivers/mod.rs +++ b/src/drivers/mod.rs @@ -28,6 +28,7 @@ pub mod init; pub mod interrupts; pub mod probe; pub mod rng; +pub mod rtc; pub mod timer; pub mod uart; mod virtio_hal; diff --git a/src/drivers/rtc/mod.rs b/src/drivers/rtc/mod.rs new file mode 100644 index 0000000..d615666 --- /dev/null +++ b/src/drivers/rtc/mod.rs @@ -0,0 +1,29 @@ +//! Real-time clock (RTC) drivers. +//! +//! RTCs often differ in how they represent time, so the idea is to return a [`Duration`] since the Unix epoch, +//! with each driver responsible for converting/handling hardware bugs. + +pub mod pl031; + +use crate::sync::OnceLock; +use alloc::sync::Arc; +use core::time::Duration; + +pub trait Rtc: Send + Sync { + /// Gets the current RTC time as a `Duration` since the Unix epoch. + fn time(&self) -> Option; + + /// Sets the RTC time. The provided `Duration` should represent the time since the Unix epoch. + #[expect(unused)] + fn set_time(&mut self, time: Duration) -> libkernel::error::Result<()>; +} + +pub static RTC_DRIVER: OnceLock> = OnceLock::new(); + +pub fn get_rtc() -> Option<&'static Arc> { + RTC_DRIVER.get() +} + +fn set_rtc_driver(driver: Arc) -> bool { + RTC_DRIVER.set(driver).is_ok() +} diff --git a/src/drivers/rtc/pl031.rs b/src/drivers/rtc/pl031.rs new file mode 100644 index 0000000..df40fa2 --- /dev/null +++ b/src/drivers/rtc/pl031.rs @@ -0,0 +1,81 @@ +use crate::arch::ArchImpl; +use crate::drivers::init::PlatformBus; +use crate::drivers::probe::{DeviceDescriptor, DeviceMatchType}; +use crate::drivers::rtc::{Rtc, set_rtc_driver}; +use crate::drivers::{Driver, DriverManager}; +use crate::kernel_driver; +use alloc::boxed::Box; +use alloc::sync::Arc; +use core::time::Duration; +use libkernel::error::{ProbeError, Result}; +use libkernel::memory::address::{PA, VA}; +use libkernel::memory::region::PhysMemoryRegion; +use libkernel::{KernAddressSpace, VirtualMemory}; + +/// Driver for a PL031 real-time clock. +pub struct PL031 { + inner: arm_pl031::Rtc, +} + +impl PL031 { + /// Constructs a new instance of the RTC driver for a PL031 device with the + /// given base address. + pub fn new(base_addr: VA) -> Self { + let rtc = unsafe { arm_pl031::Rtc::new(base_addr.as_ptr_mut() as _) }; + Self { inner: rtc } + } +} + +impl Rtc for PL031 { + fn time(&self) -> Option { + Some(Duration::new(self.inner.get_unix_timestamp() as u64, 0)) + } + + fn set_time(&mut self, time: Duration) -> libkernel::error::Result<()> { + self.inner.set_unix_timestamp(time.as_secs() as _); + Ok(()) + } +} + +impl Driver for PL031 { + fn name(&self) -> &'static str { + "ARM PrimeCell Real Time Clock" + } +} + +pub fn pl031_probe(_dm: &mut DriverManager, d: DeviceDescriptor) -> Result> { + match d { + DeviceDescriptor::Fdt(fdt_node, _flags) => { + let region = fdt_node + .reg() + .ok_or(ProbeError::NoReg)? + .next() + .ok_or(ProbeError::NoReg)?; + + let size = region.size.ok_or(ProbeError::NoRegSize)?; + + let mem = + ArchImpl::kern_address_space() + .lock_save_irq() + .map_mmio(PhysMemoryRegion::new( + PA::from_value(region.address as usize), + size, + ))?; + + let dev = Arc::new(PL031::new(mem)); + set_rtc_driver(dev.clone()); + Ok(dev) + } + } +} + +pub fn pl031_init(bus: &mut PlatformBus, _dm: &mut DriverManager) -> Result<()> { + bus.register_platform_driver( + DeviceMatchType::FdtCompatible("arm,pl031"), + Box::new(pl031_probe), + ); + + Ok(()) +} + +kernel_driver!(pl031_init); diff --git a/src/main.rs b/src/main.rs index 9d75f16..dd5f05b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -109,6 +109,13 @@ async fn launch_init(mut opts: KOptions) { None }; + // Set time to rtc time if possible + if let Some(rtc) = drivers::rtc::get_rtc() + && let Some(time) = rtc.time() + { + clock::realtime::set_date(time); + } + let root_fs = opts .root_fs .unwrap_or_else(|| panic!("No root FS driver specified in kernel command line"));