From 3ae117b81291443a110e87919dd644cfc16d2f3d Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Sun, 30 Mar 2025 15:28:25 +0200 Subject: [PATCH] Outsource x86 RTC handling to rtcd. --- src/arch/x86_shared/device/mod.rs | 3 - src/arch/x86_shared/device/rtc.rs | 150 ------------------------------ src/scheme/sys/mod.rs | 138 ++++++++++++++++++--------- src/time.rs | 8 ++ 4 files changed, 102 insertions(+), 197 deletions(-) diff --git a/src/arch/x86_shared/device/mod.rs b/src/arch/x86_shared/device/mod.rs index 8ec164f22d..5617db38c1 100644 --- a/src/arch/x86_shared/device/mod.rs +++ b/src/arch/x86_shared/device/mod.rs @@ -7,7 +7,6 @@ pub mod ioapic; pub mod local_apic; pub mod pic; pub mod pit; -pub mod rtc; pub mod serial; #[cfg(feature = "system76_ec_debug")] pub mod system76_ec; @@ -62,8 +61,6 @@ pub unsafe fn init_noncore() { log::info!("PIT used as system timer"); } - log::info!("Initializing RTC"); - rtc::init(); log::info!("Initializing serial"); serial::init(); log::info!("Finished initializing devices"); diff --git a/src/arch/x86_shared/device/rtc.rs b/src/arch/x86_shared/device/rtc.rs index 0b7bba9e7a..e69de29bb2 100644 --- a/src/arch/x86_shared/device/rtc.rs +++ b/src/arch/x86_shared/device/rtc.rs @@ -1,150 +0,0 @@ -use crate::{ - syscall::io::{Io, Pio}, - time, -}; - -pub fn init() { - let mut rtc = Rtc::new(); - *time::START.lock() = (rtc.time() as u128) * time::NANOS_PER_SEC; -} - -fn cvt_bcd(value: usize) -> usize { - (value & 0xF) + ((value / 16) * 10) -} - -/// RTC -pub struct Rtc { - addr: Pio, - data: Pio, - nmi: bool, -} - -impl Rtc { - /// Create new empty RTC - pub fn new() -> Self { - Rtc { - addr: Pio::::new(0x70), - data: Pio::::new(0x71), - nmi: false, - } - } - - /// Read - unsafe fn read(&mut self, reg: u8) -> u8 { - if self.nmi { - self.addr.write(reg & 0x7F); - } else { - self.addr.write(reg | 0x80); - } - self.data.read() - } - - /// Write - #[allow(dead_code)] - unsafe fn write(&mut self, reg: u8, value: u8) { - if self.nmi { - self.addr.write(reg & 0x7F); - } else { - self.addr.write(reg | 0x80); - } - self.data.write(value); - } - - /// Wait for an update, can take one second if full is specified! - unsafe fn wait(&mut self, full: bool) { - if full { - while self.read(0xA) & 0x80 != 0x80 {} - } - while self.read(0xA) & 0x80 == 0x80 {} - } - - /// Get time without waiting - pub unsafe fn time_no_wait(&mut self) -> u64 { - /*let century_register = if let Some(ref fadt) = acpi::ACPI_TABLE.lock().fadt { - Some(fadt.century) - } else { - None - };*/ - - let mut second = self.read(0) as usize; - let mut minute = self.read(2) as usize; - let mut hour = self.read(4) as usize; - let mut day = self.read(7) as usize; - let mut month = self.read(8) as usize; - let mut year = self.read(9) as usize; - let mut century = /* TODO: Fix invalid value from VirtualBox - if let Some(century_reg) = century_register { - self.read(century_reg) as usize - } else */ { - 20 - }; - let register_b = self.read(0xB); - - if register_b & 4 != 4 { - second = cvt_bcd(second); - minute = cvt_bcd(minute); - hour = cvt_bcd(hour & 0x7F) | (hour & 0x80); - day = cvt_bcd(day); - month = cvt_bcd(month); - year = cvt_bcd(year); - century = /* TODO: Fix invalid value from VirtualBox - if century_register.is_some() { - cvt_bcd(century) - } else */ { - century - }; - } - - if register_b & 2 != 2 || hour & 0x80 == 0x80 { - hour = ((hour & 0x7F) + 12) % 24; - } - - year += century * 100; - - // Unix time from clock - let mut secs: u64 = (year as u64 - 1970) * 31_536_000; - - let mut leap_days = (year as u64 - 1972) / 4 + 1; - if year % 4 == 0 && month <= 2 { - leap_days -= 1; - } - secs += leap_days * 86_400; - - match month { - 2 => secs += 2_678_400, - 3 => secs += 5_097_600, - 4 => secs += 7_776_000, - 5 => secs += 10_368_000, - 6 => secs += 13_046_400, - 7 => secs += 15_638_400, - 8 => secs += 18_316_800, - 9 => secs += 20_995_200, - 10 => secs += 23_587_200, - 11 => secs += 26_265_600, - 12 => secs += 28_857_600, - _ => (), - } - - secs += (day as u64 - 1) * 86_400; - secs += hour as u64 * 3600; - secs += minute as u64 * 60; - secs += second as u64; - - secs - } - - /// Get time - pub fn time(&mut self) -> u64 { - loop { - unsafe { - self.wait(false); - let time = self.time_no_wait(); - self.wait(false); - let next_time = self.time_no_wait(); - if time == next_time { - return time; - } - } - } - } -} diff --git a/src/scheme/sys/mod.rs b/src/scheme/sys/mod.rs index 413e65ac79..842dfc67f9 100644 --- a/src/scheme/sys/mod.rs +++ b/src/scheme/sys/mod.rs @@ -1,6 +1,11 @@ +// TODO: This scheme can be simplified significantly, and through it, several other APIs where it's +// dubious whether they require dedicated schemes (like irq, dtb, acpi). In particular, the kernel +// could abandon the filesystem-like APIs here in favor of SYS_CALL, and instead let userspace wrap +// those to say shell-accessible fs-like APIs. + use ::syscall::{ dirent::{DirEntry, DirentBuf, DirentKind}, - EIO, EISDIR, ENOTDIR, + EBADFD, EIO, EISDIR, ENOTDIR, }; use alloc::{collections::BTreeMap, vec::Vec}; use core::{ @@ -17,7 +22,7 @@ use crate::{ data::Stat, error::{Error, Result, EBADF, ENOENT}, flag::{MODE_DIR, MODE_FILE}, - usercopy::UserSliceWo, + usercopy::{UserSliceRo, UserSliceWo}, }, }; @@ -44,10 +49,17 @@ mod stat; enum Handle { TopLevel, - Resource { path: &'static str, data: Vec }, + Resource { + path: &'static str, + data: Option>, + }, } -type SysFn = fn() -> Result>; +enum Kind { + Rd(fn() -> Result>), + Wr(fn(&[u8]) -> Result), +} +use Kind::*; /// System information scheme pub struct SysScheme; @@ -55,32 +67,36 @@ static NEXT_ID: AtomicUsize = AtomicUsize::new(1); // Using BTreeMap as hashbrown doesn't have a const constructor. static HANDLES: RwLock> = RwLock::new(BTreeMap::new()); -const FILES: &[(&'static str, SysFn)] = &[ - ("block", block::resource), - ("context", context::resource), - ("cpu", cpu::resource), +const FILES: &[(&'static str, Kind)] = &[ + ("block", Rd(block::resource)), + ("context", Rd(context::resource)), + ("cpu", Rd(cpu::resource)), #[cfg(feature = "sys_fdstat")] - ("fdstat", fdstat::resource), - ("exe", exe::resource), - ("iostat", iostat::resource), - ("irq", irq::resource), - ("log", log::resource), - ("scheme", scheme::resource), - ("scheme_num", scheme_num::resource), - ("syscall", syscall::resource), - ("uname", uname::resource), - ("env", || Ok(Vec::from(crate::init_env()))), + ("fdstat", Rd(fdstat::resource)), + ("exe", Rd(exe::resource)), + ("iostat", Rd(iostat::resource)), + ("irq", Rd(irq::resource)), + ("log", Rd(log::resource)), + ("scheme", Rd(scheme::resource)), + ("scheme_num", Rd(scheme_num::resource)), + ("syscall", Rd(syscall::resource)), + ("uname", Rd(uname::resource)), + ("env", Rd(|| Ok(Vec::from(crate::init_env())))), #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - ("spurious_irq", interrupt::irq::spurious_irq_resource), + ("spurious_irq", Rd(interrupt::irq::spurious_irq_resource)), #[cfg(feature = "sys_stat")] - ("stat", stat::resource), + ("stat", Rd(stat::resource)), // Disabled because the debugger is inherently unsafe and probably will break the system. /* - ("trigger_debugger", || unsafe { + ("trigger_debugger", Rd(|| unsafe { crate::debugger::debugger(None); Ok(Vec::new()) - }), + })), */ + ( + "update_time_offset", + Wr(crate::time::sys_update_time_offset), + ), ]; impl KernelScheme for SysScheme { @@ -91,32 +107,35 @@ impl KernelScheme for SysScheme { let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); HANDLES.write().insert(id, Handle::TopLevel); - return Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)); + + Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)) } else { //Have to iterate to get the path without allocation - for entry in FILES.iter() { - if &entry.0 == &path { - let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); - let data = entry.1()?; - HANDLES.write().insert( - id, - Handle::Resource { - path: entry.0, - data, - }, - ); - return Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)); - } - } - } + let entry = FILES + .iter() + .find(|(entry_path, _)| *entry_path == path) + .ok_or(Error::new(ENOENT))?; - Err(Error::new(ENOENT)) + let id = NEXT_ID.fetch_add(1, Ordering::Relaxed); + let data = match entry.1 { + Rd(r) => Some(r()?), + Wr(_) => None, + }; + HANDLES.write().insert( + id, + Handle::Resource { + path: entry.0, + data, + }, + ); + Ok(OpenResult::SchemeLocal(id, InternalFlags::POSITIONED)) + } } fn fsize(&self, id: usize) -> Result { match HANDLES.read().get(&id).ok_or(Error::new(EBADF))? { Handle::TopLevel => Ok(0), - Handle::Resource { data, .. } => Ok(data.len() as u64), + Handle::Resource { data, .. } => Ok(data.as_ref().map_or(0, |d| d.len() as u64)), } } @@ -153,14 +172,45 @@ impl KernelScheme for SysScheme { }; match HANDLES.read().get(&id).ok_or(Error::new(EBADF))? { - Handle::TopLevel => return Err(Error::new(EISDIR)), - Handle::Resource { data, .. } => { + Handle::TopLevel | Handle::Resource { data: None, .. } => { + return Err(Error::new(EISDIR)) + } + Handle::Resource { + data: Some(ref data), + .. + } => { let avail_buf = data.get(pos..).unwrap_or(&[]); buffer.copy_common_bytes_from_slice(avail_buf) } } } + fn kwriteoff( + &self, + id: usize, + buffer: UserSliceRo, + _pos: u64, + _flags: u32, + _stored_flags: u32, + ) -> Result { + match HANDLES.read().get(&id).ok_or(Error::new(EBADF))? { + Handle::TopLevel | Handle::Resource { data: Some(_), .. } => { + return Err(Error::new(EISDIR)) + } + Handle::Resource { data: None, path } => { + let mut intermediate = [0_u8; 256]; + let len = buffer.copy_common_bytes_to_slice(&mut intermediate)?; + let (_, Wr(handler)) = FILES + .iter() + .find(|(entry_path, _)| entry_path == path) + .ok_or(Error::new(EBADFD))? + else { + return Err(Error::new(EBADFD))?; + }; + handler(&intermediate[..len]) + } + } + } fn getdents( &self, id: usize, @@ -191,10 +241,10 @@ impl KernelScheme for SysScheme { fn kfstat(&self, id: usize, buf: UserSliceWo) -> Result<()> { let stat = match HANDLES.read().get(&id).ok_or(Error::new(EBADF))? { Handle::Resource { data, .. } => Stat { - st_mode: 0o444 | MODE_FILE, + st_mode: 0o666 | MODE_FILE, st_uid: 0, st_gid: 0, - st_size: data.len() as u64, + st_size: data.as_ref().map_or(0, |d| d.len() as u64), ..Default::default() }, Handle::TopLevel => Stat { diff --git a/src/time.rs b/src/time.rs index 5a233967a7..2de43b4183 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,5 +1,7 @@ use spin::Mutex; +use crate::syscall::error::{Error, Result, EINVAL}; + pub const NANOS_PER_SEC: u128 = 1_000_000_000; // TODO: seqlock? @@ -15,3 +17,9 @@ pub fn monotonic() -> u128 { pub fn realtime() -> u128 { *START.lock() + monotonic() } + +pub fn sys_update_time_offset(buf: &[u8]) -> Result { + let start = <[u8; 16]>::try_from(buf).map_err(|_| Error::new(EINVAL))?; + *START.lock() = u128::from_ne_bytes(start); + Ok(16) +}