ソースを参照

feat(rtc): implement RTC for riscv64

greatbridf 7 ヶ月 前
コミット
5fdaaef28e

+ 15 - 9
crates/posix_types/src/stat.rs

@@ -1,9 +1,8 @@
 #[repr(C)]
 #[derive(Debug, Default, Copy, Clone)]
 pub struct StatXTimestamp {
-    pub tv_sec: i64,
+    pub tv_sec: u64,
     pub tv_nsec: u32,
-    pub __reserved: i32,
 }
 
 #[repr(C)]
@@ -37,7 +36,14 @@ pub struct StatX {
 #[derive(Debug, Default, Copy, Clone)]
 pub struct TimeSpec {
     pub tv_sec: u64,
-    pub tv_nsec: u64,
+    pub tv_nsec: u32,
+}
+
+#[repr(C)]
+#[derive(Debug, Default, Copy, Clone)]
+pub struct TimeVal {
+    pub tv_sec: u64,
+    pub tv_usec: u32,
 }
 
 #[repr(C)]
@@ -80,16 +86,16 @@ impl From<StatX> for Stat {
 
             st_blocks: statx.stx_blocks,
             st_atime: TimeSpec {
-                tv_sec: statx.stx_atime.tv_sec as u64,
-                tv_nsec: statx.stx_atime.tv_nsec as u64,
+                tv_sec: statx.stx_atime.tv_sec,
+                tv_nsec: statx.stx_atime.tv_nsec,
             },
             st_mtime: TimeSpec {
-                tv_sec: statx.stx_mtime.tv_sec as u64,
-                tv_nsec: statx.stx_mtime.tv_nsec as u64,
+                tv_sec: statx.stx_mtime.tv_sec,
+                tv_nsec: statx.stx_mtime.tv_nsec,
             },
             st_ctime: TimeSpec {
-                tv_sec: statx.stx_ctime.tv_sec as u64,
-                tv_nsec: statx.stx_ctime.tv_nsec as u64,
+                tv_sec: statx.stx_ctime.tv_sec,
+                tv_nsec: statx.stx_ctime.tv_nsec,
             },
         }
     }

+ 3 - 0
src/driver.rs

@@ -7,3 +7,6 @@ pub mod virtio;
 
 #[cfg(target_arch = "riscv64")]
 pub mod sbi_console;
+
+#[cfg(target_arch = "riscv64")]
+pub mod goldfish_rtc;

+ 53 - 0
src/driver/goldfish_rtc.rs

@@ -0,0 +1,53 @@
+use crate::kernel::{
+    rtc::{register_rtc, RealTimeClock},
+    timer::Instant,
+};
+use core::ptr::NonNull;
+use eonix_hal::{arch_exported::fdt::FDT, mm::ArchPhysAccess};
+use eonix_log::println_warn;
+use eonix_mm::address::{PAddr, PhysAccess};
+
+#[cfg(not(target_arch = "riscv64"))]
+compile_error!("Goldfish RTC driver is only supported on RISC-V architecture");
+
+struct GoldfishRtc {
+    time_low: NonNull<u32>,
+    time_high: NonNull<u32>,
+}
+
+unsafe impl Send for GoldfishRtc {}
+unsafe impl Sync for GoldfishRtc {}
+
+impl RealTimeClock for GoldfishRtc {
+    fn now(&self) -> Instant {
+        // SAFETY: The pointer is guaranteed to be valid as long as the RTC is registered.
+        let time_high = unsafe { self.time_high.read_volatile() };
+        let time_low = unsafe { self.time_low.read_volatile() };
+
+        let nsecs = ((time_high as u64) << 32) | (time_low as u64);
+        let secs_since_epoch = nsecs / 1_000_000_000;
+        let nsecs_within = nsecs % 1_000_000_000;
+
+        Instant::new(secs_since_epoch as u64, nsecs_within as u32)
+    }
+}
+
+pub fn probe() {
+    let Some(rtc) = FDT.find_compatible(&["google,goldfish-rtc"]) else {
+        println_warn!("Goldfish RTC not found in FDT");
+        return;
+    };
+
+    let mut regs = rtc.reg().expect("Goldfish RTC reg not found");
+    let base = regs
+        .next()
+        .map(|r| PAddr::from(r.starting_address as usize))
+        .expect("Goldfish RTC base address not found");
+
+    let goldfish_rtc = GoldfishRtc {
+        time_low: unsafe { ArchPhysAccess::as_ptr(base) },
+        time_high: unsafe { ArchPhysAccess::as_ptr(base + 4) },
+    };
+
+    register_rtc(goldfish_rtc);
+}

+ 1 - 0
src/kernel.rs

@@ -4,6 +4,7 @@ pub mod constants;
 pub mod interrupt;
 pub mod mem;
 pub mod pcie;
+pub mod rtc;
 pub mod syscall;
 pub mod task;
 pub mod timer;

+ 33 - 0
src/kernel/rtc/mod.rs

@@ -0,0 +1,33 @@
+use core::time::Duration;
+
+use super::timer::{Instant, Ticks};
+use alloc::sync::Arc;
+use eonix_log::println_warn;
+use eonix_sync::Spin;
+
+static RTC: Spin<Option<Arc<dyn RealTimeClock>>> = Spin::new(None);
+
+pub trait RealTimeClock: Send + Sync {
+    fn now(&self) -> Instant;
+}
+
+impl Instant {
+    pub fn now() -> Instant {
+        RTC.lock().as_ref().map(|rtc| rtc.now()).unwrap_or_else(|| {
+            let since_boot = Ticks::since_boot();
+            let pseudo_now = Duration::from_secs((55 * 365 + 30) * 24 * 3600) + since_boot;
+
+            Instant::new(pseudo_now.as_secs(), pseudo_now.subsec_nanos())
+        })
+    }
+}
+
+pub fn register_rtc(rtc: impl RealTimeClock + 'static) {
+    let mut rtc_lock = RTC.lock();
+    if rtc_lock.is_some() {
+        println_warn!("RTC is already registered, ignoring new registration");
+        return;
+    }
+
+    *rtc_lock = Some(Arc::new(rtc));
+}

+ 2 - 3
src/kernel/syscall/procops.rs

@@ -1,6 +1,3 @@
-use core::time::Duration;
-
-use super::sysinfo::TimeVal;
 use super::SyscallNoReturn;
 use crate::io::Buffer;
 use crate::kernel::constants::{EINVAL, ENOENT, ENOTDIR, ERANGE, ESRCH};
@@ -23,6 +20,7 @@ use alloc::borrow::ToOwned;
 use alloc::ffi::CString;
 use bitflags::bitflags;
 use core::ptr::NonNull;
+use core::time::Duration;
 use eonix_hal::processor::UserTLS;
 use eonix_hal::traits::trap::RawTrapContext;
 use eonix_mm::address::Addr as _;
@@ -31,6 +29,7 @@ use eonix_sync::AsProof as _;
 use posix_types::constants::{P_ALL, P_PID};
 use posix_types::ctypes::PtrT;
 use posix_types::signal::{SigAction, SigInfo, SigSet, Signal};
+use posix_types::stat::TimeVal;
 use posix_types::{syscall_no::*, SIGNAL_NOW};
 
 #[repr(C)]

+ 16 - 23
src/kernel/syscall/sysinfo.rs

@@ -2,12 +2,15 @@ use crate::{
     kernel::{
         constants::{CLOCK_MONOTONIC, CLOCK_REALTIME, EINVAL},
         task::Thread,
-        timer::ticks,
+        timer::{Instant, Ticks},
         user::UserPointerMut,
     },
     prelude::*,
 };
-use posix_types::syscall_no::*;
+use posix_types::{
+    stat::{TimeSpec, TimeVal},
+    syscall_no::*,
+};
 
 #[derive(Clone, Copy)]
 struct NewUTSName {
@@ -54,20 +57,6 @@ fn newuname(buffer: *mut NewUTSName) -> KResult<()> {
     buffer.write(uname)
 }
 
-#[allow(dead_code)]
-#[derive(Default, Clone, Copy)]
-pub struct TimeVal {
-    sec: u64,
-    usec: u64,
-}
-
-#[allow(dead_code)]
-#[derive(Clone, Copy)]
-pub struct TimeSpec {
-    sec: u64,
-    nsec: u64,
-}
-
 #[eonix_macros::define_syscall(SYS_GETTIMEOFDAY)]
 fn gettimeofday(timeval: *mut TimeVal, timezone: *mut ()) -> KResult<()> {
     if !timezone.is_null() {
@@ -76,10 +65,12 @@ fn gettimeofday(timeval: *mut TimeVal, timezone: *mut ()) -> KResult<()> {
 
     if !timeval.is_null() {
         let timeval = UserPointerMut::new(timeval)?;
-        let ticks = ticks();
+        let now = Instant::now();
+        let since_epoch = now.since_epoch();
+
         timeval.write(TimeVal {
-            sec: ticks.in_secs() as u64,
-            usec: ticks.in_usecs() as u64 % 1_000_000,
+            tv_sec: since_epoch.as_secs(),
+            tv_usec: since_epoch.subsec_micros(),
         })?;
     }
 
@@ -92,10 +83,12 @@ fn do_clock_gettime64(_thread: &Thread, clock_id: u32, timespec: *mut TimeSpec)
     }
 
     let timespec = UserPointerMut::new(timespec)?;
-    let ticks = ticks();
+    let now = Instant::now();
+    let since_epoch = now.since_epoch();
+
     timespec.write(TimeSpec {
-        sec: ticks.in_secs() as u64,
-        nsec: ticks.in_nsecs() as u64 % 1_000_000_000,
+        tv_sec: since_epoch.as_secs(),
+        tv_nsec: since_epoch.subsec_nanos(),
     })
 }
 
@@ -133,7 +126,7 @@ struct Sysinfo {
 fn sysinfo(info: *mut Sysinfo) -> KResult<()> {
     let info = UserPointerMut::new(info)?;
     info.write(Sysinfo {
-        uptime: ticks().in_secs() as u32,
+        uptime: Ticks::since_boot().as_secs() as u32,
         loads: [0; 3],
         totalram: 100,
         freeram: 50,

+ 34 - 21
src/kernel/timer.rs

@@ -17,7 +17,10 @@ static SLEEPERS_LIST: Spin<BinaryHeap<Reverse<Sleepers>>> = Spin::new(BinaryHeap
 #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
 pub struct Ticks(usize);
 
-pub struct Instant(Ticks);
+pub struct Instant {
+    secs_since_epoch: u64,
+    nsecs_within: u32,
+}
 
 struct Sleepers {
     wakeup_tick: Ticks,
@@ -71,24 +74,31 @@ impl Ticks {
 }
 
 impl Instant {
-    pub fn now() -> Self {
-        Instant(Ticks::now())
+    pub fn new(secs_since_epoch: u64, nsecs_within: u32) -> Self {
+        Instant {
+            secs_since_epoch,
+            nsecs_within,
+        }
     }
 
     pub fn elapsed(&self) -> Duration {
-        Duration::from_nanos((Ticks::now().in_nsecs() - self.0.in_nsecs()) as u64)
-    }
-}
-
-impl From<Ticks> for Instant {
-    fn from(ticks: Ticks) -> Self {
-        Instant(ticks)
+        let now = Instant::now();
+        if now.nsecs_within < self.nsecs_within {
+            // We have wrapped around the nanoseconds.
+            Duration::new(
+                now.secs_since_epoch - self.secs_since_epoch - 1,
+                1_000_000_000 + now.nsecs_within - self.nsecs_within,
+            )
+        } else {
+            Duration::new(
+                now.secs_since_epoch - self.secs_since_epoch,
+                now.nsecs_within - self.nsecs_within,
+            )
+        }
     }
-}
 
-impl From<Instant> for Ticks {
-    fn from(instant: Instant) -> Self {
-        instant.0
+    pub fn since_epoch(&self) -> Duration {
+        Duration::new(self.secs_since_epoch, self.nsecs_within)
     }
 }
 
@@ -104,7 +114,15 @@ impl Add<Duration> for Instant {
     type Output = Instant;
 
     fn add(self, duration: Duration) -> Self::Output {
-        Instant(self.0 + Ticks(duration.as_millis() as usize))
+        let nsecs = self.nsecs_within + duration.subsec_nanos();
+        let nsecs_within = nsecs % 1_000_000_000;
+        let secs_since_epoch =
+            self.secs_since_epoch + duration.as_secs() + (nsecs / 1_000_000_000) as u64;
+
+        Instant {
+            secs_since_epoch,
+            nsecs_within: nsecs_within,
+        }
     }
 }
 
@@ -156,13 +174,8 @@ pub fn should_reschedule() -> bool {
     }
 }
 
-pub fn ticks() -> Ticks {
-    Ticks::now()
-}
-
 pub async fn sleep(duration: Duration) {
-    let wakeup_time = Instant::now() + duration;
-    let wakeup_tick = Ticks::from(wakeup_time);
+    let wakeup_tick = Ticks::now() + Ticks(duration.as_millis() as usize);
 
     core::future::poll_fn(|ctx| {
         if Ticks::now() >= wakeup_tick {

+ 1 - 0
src/lib.rs

@@ -139,6 +139,7 @@ async fn init_process(early_kstack: PRange) {
         driver::virtio::init_virtio_devices();
         driver::e1000e::register_e1000e_driver();
         driver::ahci::register_ahci_driver();
+        driver::goldfish_rtc::probe();
     }
 
     fs::tmpfs::init();