support for ARM PL031 RTC

This commit is contained in:
Ashwin Naren
2026-03-06 19:42:58 -08:00
parent 9fea27a427
commit 54265fdae0
10 changed files with 133 additions and 6 deletions

7
Cargo.lock generated
View File

@@ -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",

View File

@@ -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 }

View File

@@ -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,

View File

@@ -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<TimeSpec>) -> Result<usize> {
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

View File

@@ -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<i32> for ClockId {
fn try_from(value: i32) -> Result<Self, Self::Error> {
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),

View File

@@ -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<Option<(Duration, Instant)>> = SpinLock::new(None);
#[cfg(test)]

View File

@@ -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;

29
src/drivers/rtc/mod.rs Normal file
View File

@@ -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<Duration>;
/// 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<Arc<dyn Rtc>> = OnceLock::new();
pub fn get_rtc() -> Option<&'static Arc<dyn Rtc>> {
RTC_DRIVER.get()
}
fn set_rtc_driver(driver: Arc<dyn Rtc>) -> bool {
RTC_DRIVER.set(driver).is_ok()
}

81
src/drivers/rtc/pl031.rs Normal file
View File

@@ -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<Duration> {
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<Arc<dyn Driver>> {
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);

View File

@@ -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"));