diff --git a/kernel/driver/bsp/arm/src/lib.rs b/kernel/driver/bsp/arm/src/lib.rs index 250262b0..7f1103c8 100644 --- a/kernel/driver/bsp/arm/src/lib.rs +++ b/kernel/driver/bsp/arm/src/lib.rs @@ -4,4 +4,5 @@ extern crate alloc; mod pl011; +mod pl031; mod pl061; diff --git a/kernel/driver/bsp/arm/src/pl031.rs b/kernel/driver/bsp/arm/src/pl031.rs new file mode 100644 index 00000000..a7dbb731 --- /dev/null +++ b/kernel/driver/bsp/arm/src/pl031.rs @@ -0,0 +1,66 @@ +use alloc::sync::Arc; +use device_api::device::{Device, DeviceInitContext}; +use device_tree::driver::{device_tree_driver, Node, ProbeContext}; +use libk::{ + error::Error, + fs::sysfs::{self, nodes::SysfsRtcNode}, +}; +use libk_mm::device::DeviceMemoryIo; +use libk_util::sync::IrqSafeSpinlock; +use tock_registers::{ + interfaces::Readable, + register_structs, + registers::{ReadOnly, ReadWrite, WriteOnly}, +}; + +register_structs! { + #[allow(non_snake_case)] + Regs { + (0x00 => RTCDR: ReadOnly), + (0x04 => RTCMR: ReadWrite), + (0x08 => RTCLR: ReadWrite), + (0x0C => RTCCR: ReadWrite), + (0x10 => RTCIMSC: ReadWrite), + (0x14 => RTCRIS: ReadOnly), + (0x18 => RTCMIS: ReadOnly), + (0x1C => RTCICR: WriteOnly), + (0x20 => @END), + } +} + +struct Pl031 { + regs: IrqSafeSpinlock>, +} + +impl SysfsRtcNode for Pl031 { + fn read(&self) -> Result { + let regs = self.regs.lock(); + Ok(regs.RTCDR.get() as _) + } +} + +impl Device for Pl031 { + unsafe fn init(self: Arc, _cx: DeviceInitContext) -> Result<(), Error> { + sysfs::nodes::add_rtc_node(self.clone()); + Ok(()) + } + + fn display_name(&self) -> &str { + "ARM PL031 RTC" + } +} + +device_tree_driver! { + compatible: ["arm,pl031"], + driver: { + fn probe(&self, node: &Arc, context: &mut ProbeContext) -> Option> { + let base = node.map_base(context, 0)?; + + let regs = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?; + + let rtc = Arc::new(Pl031 { regs: IrqSafeSpinlock::new(regs) }); + + Some(rtc) + } + } +} diff --git a/kernel/libk/src/fs/sysfs/mod.rs b/kernel/libk/src/fs/sysfs/mod.rs index 2878be62..4af556e1 100644 --- a/kernel/libk/src/fs/sysfs/mod.rs +++ b/kernel/libk/src/fs/sysfs/mod.rs @@ -5,6 +5,7 @@ use object::KObject; use crate::vfs::NodeRef; pub mod attribute; +pub mod nodes; pub mod object; static ROOT: OneTimeInit = OneTimeInit::new(); diff --git a/kernel/libk/src/fs/sysfs/nodes.rs b/kernel/libk/src/fs/sysfs/nodes.rs new file mode 100644 index 00000000..6e7668d2 --- /dev/null +++ b/kernel/libk/src/fs/sysfs/nodes.rs @@ -0,0 +1,52 @@ +use core::{ + marker::PhantomData, + sync::atomic::{AtomicUsize, Ordering}, +}; + +use alloc::{format, string::String, sync::Arc}; +use libk_util::OneTimeInit; +use yggdrasil_abi::error::Error; + +use crate::fs::sysfs::{ + attribute::{StringAttribute, StringAttributeOps}, + device, + object::KObject, +}; + +pub trait SysfsRtcNode: Send + Sync + 'static { + fn read(&self) -> Result; +} + +static RTC_NODES: AtomicUsize = AtomicUsize::new(0); + +pub fn add_rtc_node(node: Arc) { + struct Time(PhantomData); + + impl StringAttributeOps for Time { + type Data = Arc; + const NAME: &'static str = "time"; + + fn read(state: &Self::Data) -> Result { + let time = state.read()?; + Ok(format!("{time}")) + } + } + + static RTC_OBJECT: OneTimeInit>> = OneTimeInit::new(); + let rtc_object = RTC_OBJECT.or_init_with(|| { + let device_object = device().unwrap(); + let rtc_object = KObject::new(()); + device_object.add_object("rtc", rtc_object.clone()).ok(); + rtc_object + }); + + let rtc_node = KObject::new(node); + + rtc_node + .add_attribute(StringAttribute::from(Time(PhantomData))) + .ok(); + + let index = RTC_NODES.fetch_add(1, Ordering::AcqRel); + let name = format!("rtc{index}"); + rtc_object.add_object(name, rtc_node).ok(); +} diff --git a/kernel/src/arch/x86/peripherals/rtc.rs b/kernel/src/arch/x86/peripherals/rtc.rs index 0393b7af..d09290ff 100644 --- a/kernel/src/arch/x86/peripherals/rtc.rs +++ b/kernel/src/arch/x86/peripherals/rtc.rs @@ -1,18 +1,11 @@ use abi::error::Error; use alloc::sync::Arc; -use device_api::{ - device::Device, - interrupt::{InterruptHandler, Irq, IrqVector}, -}; +use device_api::device::Device; use kernel_arch::{Architecture, ArchitectureImpl}; -use kernel_arch_x86::{ - intrinsics::{io_wait, IoPort, IoPortAccess}, - ISA_IRQ_OFFSET, -}; -use libk::{device::external_interrupt_controller, time}; +use kernel_arch_x86::intrinsics::{io_wait, IoPort, IoPortAccess}; +use libk::fs::sysfs::{self, nodes::SysfsRtcNode}; use libk_util::sync::IrqSafeSpinlock; -const NMI_DISABLE: u8 = 1 << 7; const CMOS_REG_SEC: u8 = 0x00; const CMOS_REG_MIN: u8 = 0x02; const CMOS_REG_HOUR: u8 = 0x04; @@ -20,13 +13,8 @@ const CMOS_REG_DAY: u8 = 0x07; const CMOS_REG_MON: u8 = 0x08; const CMOS_REG_YEAR: u8 = 0x09; const CMOS_REG_STATUS_A: u8 = 0x0A; -const CMOS_REG_STATUS_B: u8 = 0x0B; -const CMOS_REG_STATUS_C: u8 = 0x0C; const STATUS_A_UPDATE_IN_PROGRESS: u8 = 1 << 7; -// Refresh every 4 ticks (every 2s) -const REFRESH_INTERVAL: u32 = 4; - #[derive(Debug, PartialEq, Eq, Clone, Copy)] struct DateTime { seconds: u8, @@ -40,14 +28,18 @@ struct DateTime { struct Inner { command: IoPort, data: IoPort, - counter: u32, - last_timestamp: Option, } pub struct Rtc { inner: IrqSafeSpinlock, } +fn bcd_to_dec(x: u8) -> u8 { + let a = (x >> 4) & 0xF; + let b = x & 0xF; + a * 10 + b +} + impl Inner { fn read_date_time(&mut self) -> DateTime { self.wait_for_update(); @@ -71,12 +63,12 @@ impl Inner { } fn try_read_time(&mut self) -> DateTime { - let seconds = self.read_reg(CMOS_REG_SEC); - let minutes = self.read_reg(CMOS_REG_MIN); - let hours = self.read_reg(CMOS_REG_HOUR); - let day_of_month = self.read_reg(CMOS_REG_DAY); - let month = self.read_reg(CMOS_REG_MON); - let year = self.read_reg(CMOS_REG_YEAR); + let seconds = bcd_to_dec(self.read_reg(CMOS_REG_SEC)); + let minutes = bcd_to_dec(self.read_reg(CMOS_REG_MIN)); + let hours = bcd_to_dec(self.read_reg(CMOS_REG_HOUR)); + let day_of_month = bcd_to_dec(self.read_reg(CMOS_REG_DAY)); + let month = bcd_to_dec(self.read_reg(CMOS_REG_MON)); + let year = bcd_to_dec(self.read_reg(CMOS_REG_YEAR)); DateTime { seconds, minutes, @@ -99,59 +91,16 @@ impl Inner { } value } - - fn write_reg(&mut self, reg: u8, value: u8) { - assert!(ArchitectureImpl::interrupt_mask()); - self.command.write(reg); - for _ in 0..10 { - io_wait(); - } - self.data.write(value); - for _ in 0..10 { - io_wait(); - } - } - - fn setup_irq(&mut self) { - let rate = 15; // freq = 2Hz - let old_a = self.read_reg(CMOS_REG_STATUS_A | NMI_DISABLE); - let old_b = self.read_reg(CMOS_REG_STATUS_B | NMI_DISABLE); - let new_a = (old_a & 0xF0) | rate; - let new_b = old_b | (1 << 6) | (1 << 2) | (1 << 1); - self.write_reg(CMOS_REG_STATUS_A | NMI_DISABLE, new_a); - self.write_reg(CMOS_REG_STATUS_B | NMI_DISABLE, new_b); - } } -impl InterruptHandler for Rtc { - fn handle_irq(self: Arc, _vector: IrqVector) -> bool { - let mut inner = self.inner.lock(); - if inner.counter == 0 { - let time = inner.read_date_time(); - inner.last_timestamp = Some(time); - - time::set_real_seconds(time.to_seconds(), true); - } - inner.read_reg(CMOS_REG_STATUS_C); - - inner.counter += 1; - if inner.counter == REFRESH_INTERVAL { - inner.counter = 0; - } - true +impl SysfsRtcNode for Rtc { + fn read(&self) -> Result { + let time = self.inner.lock().read_date_time(); + Ok(time.to_seconds()) } } impl Device for Rtc { - unsafe fn init_irq(self: Arc) -> Result<(), Error> { - let irq = Irq::External(ISA_IRQ_OFFSET + 8); - let intc = external_interrupt_controller()?; - self.inner.lock().setup_irq(); - intc.register_irq(irq, Default::default(), self.clone())?; - intc.enable_irq(irq)?; - Ok(()) - } - fn display_name(&self) -> &str { "x86 RTC" } @@ -163,15 +112,13 @@ impl Rtc { inner: IrqSafeSpinlock::new(Inner { command: IoPort::new(0x70), data: IoPort::new(0x71), - counter: 0, - last_timestamp: None, }), } } pub fn setup() -> Result, Error> { let this = Arc::new(Self::new()); - unsafe { this.clone().init_irq() }?; + sysfs::nodes::add_rtc_node(this.clone()); Ok(this) } } diff --git a/userspace/etc/rc.d/20-timesync b/userspace/etc/rc.d/20-timesync index 33b4c754..5e110500 100755 --- a/userspace/etc/rc.d/20-timesync +++ b/userspace/etc/rc.d/20-timesync @@ -1,4 +1,5 @@ #!/bin/sh # Sync every 20 minutes +/bin/date set --from-rtc /sbin/service start -- /bin/ntpc -i 1200 time.google.com:123 diff --git a/userspace/sysutils/src/date.rs b/userspace/sysutils/src/date.rs index 53d3ea32..6021b811 100644 --- a/userspace/sysutils/src/date.rs +++ b/userspace/sysutils/src/date.rs @@ -1,25 +1,86 @@ -use std::process::ExitCode; +#![feature(rustc_private)] +use std::{fs, io, process::ExitCode}; use chrono::DateTime; -use yggdrasil_rt::time::get_real_time; +use clap::{Parser, Subcommand}; +use cross::time::set_real_time; +use runtime::rt::time::{get_real_time, SystemTime}; + +const DEFAULT_RTC_PATH: &str = "/sys/device/rtc/rtc0/time"; + +#[derive(Debug, Parser)] +struct Args { + #[clap(subcommand)] + action: Option, +} + +#[derive(Debug, Subcommand)] +enum Action { + Get { + #[clap(short, long)] + rtc: bool, + }, + Set { + #[clap(short = 'r', long)] + from_rtc: bool, + }, +} + +impl Default for Action { + fn default() -> Self { + Self::Get { rtc: false } + } +} #[derive(Debug, thiserror::Error)] enum Error { #[error("Could not get current time: {0:?}")] - GetTime(yggdrasil_rt::Error), + Io(#[from] io::Error), #[error("Time conversion error")] UtcTime, } -fn run() -> Result<(), Error> { - let now = get_real_time().map_err(Error::GetTime)?; - let now = DateTime::from_timestamp(now.seconds() as _, now.subsec_nanos() as _).ok_or(Error::UtcTime)?; +fn read_rtc_time() -> Result { + let rtc_timestamp: u64 = fs::read_to_string(DEFAULT_RTC_PATH)? + .trim() + .parse() + .map_err(|_| Error::UtcTime)?; + + Ok(SystemTime::new(rtc_timestamp, 0)) +} + +fn get_date(rtc: bool) -> Result<(), Error> { + let time = if rtc { + read_rtc_time()? + } else { + get_real_time().map_err(io::Error::from)? + }; + + let now = DateTime::from_timestamp(time.seconds() as _, time.subsec_nanos() as _) + .ok_or(Error::UtcTime)?; println!("{now}"); + Ok(()) } +fn set_date(from_rtc: bool) -> Result<(), Error> { + let time = if from_rtc { read_rtc_time()? } else { todo!() }; + + set_real_time(time.seconds(), 0)?; + Ok(()) +} + +fn run(args: Args) -> Result<(), Error> { + let action = args.action.unwrap_or_default(); + match action { + Action::Get { rtc } => get_date(rtc), + Action::Set { from_rtc } => set_date(from_rtc), + } +} + fn main() -> ExitCode { - match run() { + let args = Args::parse(); + match run(args) { Ok(()) => ExitCode::SUCCESS, Err(error) => { eprintln!("Error: {error}");