diff --git a/kernel/arch/aarch64/src/lib.rs b/kernel/arch/aarch64/src/lib.rs index df29356f..d8d29aba 100644 --- a/kernel/arch/aarch64/src/lib.rs +++ b/kernel/arch/aarch64/src/lib.rs @@ -43,6 +43,7 @@ extern "C" fn idle_task(_: usize) -> ! { } impl ArchitectureImpl { + #[inline] pub fn local_cpu_data() -> Option<&'static mut PerCpuData> { unsafe { (Self::local_cpu() as *mut PerCpuData).as_mut() } } diff --git a/kernel/libk/src/task/sched.rs b/kernel/libk/src/task/sched.rs index 57a547ab..4ee9b838 100644 --- a/kernel/libk/src/task/sched.rs +++ b/kernel/libk/src/task/sched.rs @@ -179,8 +179,9 @@ impl Scheduler for CpuQueue { let t = monotonic_time(); if let Some(t0) = self.last_stats_measure.get() { - let dt = t.checked_sub(&t0).unwrap(); - self.update_stats(dt, current.as_ref()); + if let Some(dt) = t.checked_sub(&t0) { + self.update_stats(dt, current.as_ref()); + } } self.last_stats_measure.set(Some(t)); diff --git a/kernel/libk/src/time.rs b/kernel/libk/src/time.rs index df69a97a..b348b33f 100644 --- a/kernel/libk/src/time.rs +++ b/kernel/libk/src/time.rs @@ -4,42 +4,72 @@ use yggdrasil_abi::time::SystemTime; // Millisecond resolution const NANOSECONDS_IN_SECOND: u64 = 1_000_000_000; -const TIME_RESOLUTION_NS: u64 = 1_000_000; -pub const TICKS_PER_SEC: u64 = NANOSECONDS_IN_SECOND / TIME_RESOLUTION_NS; +// Monotonic and real times are running in nanoseconds static MONOTONIC_TIME: AtomicU64 = AtomicU64::new(0); static REAL_TIME_OFFSET: AtomicU64 = AtomicU64::new(0); + +// Real time is running in milliseconds static REAL_TIME: AtomicU64 = AtomicU64::new(0); #[inline] fn make_time(ticks: u64) -> SystemTime { SystemTime { - seconds: ticks / TICKS_PER_SEC, - nanoseconds: (ticks % TICKS_PER_SEC) * TIME_RESOLUTION_NS, + seconds: ticks / NANOSECONDS_IN_SECOND, + nanoseconds: ticks % NANOSECONDS_IN_SECOND, } } +#[inline] pub fn real_time() -> SystemTime { - make_time(REAL_TIME.load(Ordering::Acquire) + REAL_TIME_OFFSET.load(Ordering::Relaxed)) + let real_ms = REAL_TIME.load(Ordering::Acquire); + let off_ns = REAL_TIME_OFFSET.load(Ordering::Relaxed); + + let mut nanoseconds = off_ns % NANOSECONDS_IN_SECOND + (real_ms % 1000) * 1000000; + let mut seconds = off_ns / NANOSECONDS_IN_SECOND + real_ms / 1000; + if nanoseconds >= NANOSECONDS_IN_SECOND { + seconds = seconds.saturating_add(1); + nanoseconds -= NANOSECONDS_IN_SECOND; + debug_assert!(nanoseconds < NANOSECONDS_IN_SECOND); + } + + SystemTime { + seconds, + nanoseconds, + } } +#[inline] pub fn monotonic_time() -> SystemTime { make_time(MONOTONIC_TIME.load(Ordering::Acquire)) } -pub fn add_ticks(ticks: u64) { - MONOTONIC_TIME.fetch_add(ticks, Ordering::Release); - REAL_TIME_OFFSET.fetch_add(ticks, Ordering::Release); -} - -pub fn set_real_time(ticks: u64, reset: bool) { - REAL_TIME.store(ticks, Ordering::Release); +#[inline] +pub fn set_real_seconds(s: u64, reset: bool) { + REAL_TIME.store(s * 1000, Ordering::Release); if reset { REAL_TIME_OFFSET.store(0, Ordering::Release); } } -pub fn set_ticks(ticks: u64) { - MONOTONIC_TIME.store(ticks, Ordering::Release); - REAL_TIME_OFFSET.store(ticks, Ordering::Release); +#[inline] +pub fn add_nanoseconds(ns: u64) { + MONOTONIC_TIME.fetch_add(ns, Ordering::Release); + REAL_TIME_OFFSET.fetch_add(ns, Ordering::Release); +} + +#[inline] +pub fn add_milliseconds(ms: u64) { + add_nanoseconds(ms * 1000000); +} + +#[inline] +pub fn set_nanoseconds(ns: u64) { + MONOTONIC_TIME.store(ns, Ordering::Release); + REAL_TIME_OFFSET.store(ns, Ordering::Release); +} + +#[inline] +pub fn set_milliseconds(ms: u64) { + set_nanoseconds(ms * 1000000); } diff --git a/kernel/src/arch/aarch64/mod.rs b/kernel/src/arch/aarch64/mod.rs index f435c7cf..b6195189 100644 --- a/kernel/src/arch/aarch64/mod.rs +++ b/kernel/src/arch/aarch64/mod.rs @@ -2,7 +2,7 @@ use core::sync::atomic::Ordering; -use aarch64_cpu::registers::{CNTP_CTL_EL0, CNTP_TVAL_EL0}; +use aarch64_cpu::registers::{CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0}; use abi::error::Error; use alloc::sync::Arc; use device_api::{ @@ -31,7 +31,7 @@ use libk_mm::{ table::EntryLevelExt, }; use libk_util::OneTimeInit; -use tock_registers::interfaces::Writeable; +use tock_registers::interfaces::{Readable, Writeable}; use ygg_driver_pci::PciBusManager; use crate::{ diff --git a/kernel/src/arch/aarch64/timer.rs b/kernel/src/arch/aarch64/timer.rs index 8908aa44..0534f4e2 100644 --- a/kernel/src/arch/aarch64/timer.rs +++ b/kernel/src/arch/aarch64/timer.rs @@ -1,14 +1,16 @@ //! AArch64 Generic Timer +use core::sync::atomic::{AtomicU64, Ordering}; + use aarch64_cpu::registers::{CNTFRQ_EL0, CNTPCT_EL0, CNTP_CTL_EL0, CNTP_TVAL_EL0}; -use abi::error::Error; +use abi::{error::Error, time::NANOSECONDS_IN_SECOND}; use alloc::sync::Arc; use device_api::{ device::Device, interrupt::{InterruptHandler, Irq}, }; use device_tree::device_tree_driver; -use kernel_arch::task::Scheduler; +use kernel_arch::{task::Scheduler, Architecture, ArchitectureImpl}; use libk::{arch::Cpu, device::external_interrupt_controller, task::runtime, time}; use tock_registers::interfaces::{ReadWriteable, Readable, Writeable}; @@ -20,14 +22,24 @@ pub struct ArmTimer { /// ARM timer tick interval (in some time units?) pub const TICK_INTERVAL: u64 = 250000; +static LAST_TICKS: AtomicU64 = AtomicU64::new(0); + impl InterruptHandler for ArmTimer { fn handle_irq(self: Arc, _vector: Option) -> bool { - let count = CNTPCT_EL0.get() * 1_000; - let freq = CNTFRQ_EL0.get(); + let count = CNTPCT_EL0.get(); CNTP_TVAL_EL0.set(TICK_INTERVAL); - time::set_ticks(count / freq); + if Cpu::local().id() == 0 { + let last = LAST_TICKS.swap(count, Ordering::Relaxed); + let freq = CNTFRQ_EL0.get(); + let delta = count.wrapping_sub(last); + + // Only update time from local CPU + let dt = delta * NANOSECONDS_IN_SECOND / freq; + time::add_nanoseconds(dt); + } + runtime::tick(); unsafe { diff --git a/kernel/src/arch/x86/mod.rs b/kernel/src/arch/x86/mod.rs index 1ce4ea5f..7da4cfc9 100644 --- a/kernel/src/arch/x86/mod.rs +++ b/kernel/src/arch/x86/mod.rs @@ -3,7 +3,7 @@ #![allow(missing_docs)] use abi::error::Error; -use alloc::sync::Arc; +use alloc::{sync::Arc, vec::Vec}; use device_api::{device::Device, interrupt::Irq}; use libk::{debug, devfs, task::runtime}; use libk_mm::{ @@ -28,14 +28,28 @@ pub mod intrinsics; mod pci; pub mod peripherals; +pub type ProbeClockSourceFn = fn() -> Result, Error>; + pub trait InitrdSource { fn start(&self) -> PhysicalAddress; fn end(&self) -> PhysicalAddress; } +struct ProbeClockSource { + probe: ProbeClockSourceFn, + name: &'static str, +} + pub struct EarlyPlatformDevices { pub com1_3: Arc, pub i8259: Arc, + + clock_sources: Vec, +} + +pub enum SelectedClockSource { + Proper(Arc), + Fallback(Arc), } // TODO move this to some sort of .init_array-style implicit thing @@ -106,7 +120,11 @@ pub fn init_platform_devices_early() -> Result { i8259.disable(); } - Ok(EarlyPlatformDevices { i8259, com1_3 }) + Ok(EarlyPlatformDevices { + i8259, + com1_3, + clock_sources: Vec::new(), + }) } pub fn add_legacy_pci() { @@ -116,13 +134,19 @@ pub fn add_legacy_pci() { } pub fn init_platform_devices(early: EarlyPlatformDevices) { - // TODO clocksource can be selected here - match I8253::setup() { - Ok(i8253) => { + match init_clock_source(&early.clock_sources) { + Ok(SelectedClockSource::Proper(source)) => { + log::info!( + "Selected {:?} as the primary clock source", + source.display_name() + ); + } + Ok(SelectedClockSource::Fallback(i8253)) => { + log::info!("Selected i8253 as the primary clock source"); early.i8259.set_i8253(i8253); } Err(error) => { - log::error!("Couldn't setup i8253: {error:?}"); + log::error!("Could not select a clock source: {error:?}"); } } if let Err(error) = PS2Controller::setup() { @@ -140,6 +164,27 @@ pub fn init_platform_devices(early: EarlyPlatformDevices) { } } +fn init_clock_source(sources: &[ProbeClockSource]) -> Result { + for source in sources { + match (source.probe)() { + Ok(source) => { + return Ok(SelectedClockSource::Proper(source)); + } + Err(error) => { + log::error!( + "Could not pick {:?} as the primary clock source: {error:?}", + source.name + ); + } + } + } + + // Fall back to i8253 (or use it as the default one in i686) + let i8253 = I8253::setup()?; + + Ok(SelectedClockSource::Fallback(i8253)) +} + pub fn set_initrd(initrd: &impl InitrdSource, reserve: bool) { let initrd_start = initrd.start(); let initrd_end = initrd.end(); @@ -178,3 +223,9 @@ pub fn set_initrd(initrd: &impl InitrdSource, reserve: bool) { INITRD_DATA.init(initrd); } + +impl EarlyPlatformDevices { + pub fn add_preferred_clock_source(&mut self, name: &'static str, probe: ProbeClockSourceFn) { + self.clock_sources.push(ProbeClockSource { probe, name }); + } +} diff --git a/kernel/src/arch/x86/peripherals/hpet.rs b/kernel/src/arch/x86/peripherals/hpet.rs new file mode 100644 index 00000000..482cfa1c --- /dev/null +++ b/kernel/src/arch/x86/peripherals/hpet.rs @@ -0,0 +1,395 @@ +use core::sync::atomic::{AtomicU64, Ordering}; + +use abi::error::Error; +use acpi::HpetInfo; +use alloc::{collections::btree_map::BTreeMap, format, string::String, sync::Arc}; +use device_api::{ + device::Device, + interrupt::{InterruptHandler, Irq, IrqOptions, IrqTrigger}, +}; +use libk::{device::external_interrupt_controller, task::runtime, time}; +use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo}; +use libk_util::sync::spin_rwlock::IrqSafeRwLock; +use tock_registers::{ + interfaces::{ReadWriteable, Readable, Writeable}, + register_bitfields, register_structs, + registers::{ReadOnly, ReadWrite}, +}; + +register_bitfields! { + u64, + GENERAL_CAPABILITIES [ + COUNTER_CLK_PERIOD OFFSET(32) NUMBITS(32) [], + VENDOR_ID OFFSET(16) NUMBITS(16) [], + COUNT_SIZE_CAP OFFSET(13) NUMBITS(1) [], + NUM_TIM_CAP OFFSET(8) NUMBITS(4) [], + REV_ID OFFSET(0) NUMBITS(8) [], + ], + GENERAL_CONFIG [ + LEG_RT_CNF OFFSET(1) NUMBITS(1) [], + ENABLE_CNF OFFSET(0) NUMBITS(1) [], + ], + Tn_CONFIG [ + Tn_INT_ROUTE_CAP OFFSET(32) NUMBITS(32) [], + Tn_FSB_INT_DEL_CAP OFFSET(15) NUMBITS(1) [], + Tn_FSB_EN_CNF OFFSET(14) NUMBITS(1) [], + Tn_INT_ROUTE_CNF OFFSET(9) NUMBITS(5) [], + Tn_32MODE_CNF OFFSET(8) NUMBITS(1) [], + Tn_VAL_SET_CNF OFFSET(6) NUMBITS(1) [], + Tn_SIZE_CAP OFFSET(5) NUMBITS(1) [], + Tn_PER_INT_CAP OFFSET(4) NUMBITS(1) [], + Tn_TYPE_CNF OFFSET(3) NUMBITS(1) [ + NonPeriodic = 0, + Periodic = 1, + ], + Tn_INT_ENB_CNF OFFSET(2) NUMBITS(1) [], + Tn_INT_TYPE_CNF OFFSET(1) NUMBITS(1) [ + Edge = 0, + Level = 1, + ], + ] +} + +register_structs! { + #[allow(non_snake_case)] + GeneralRegs { + (0x000 => GENERAL_CAPABILITIES: ReadOnly), + (0x008 => _0), + (0x010 => GENERAL_CONFIG: ReadWrite), + (0x018 => _1), + (0x020 => GENERAL_ISR: ReadWrite), + (0x028 => _2), + (0x0F0 => MAIN_COUNTER: ReadWrite), + (0x0F8 => _3), + (0x100 => @END), + } +} + +register_structs! { + #[allow(non_snake_case)] + TimerRegs { + (0x00 => Tn_CONFIG: ReadWrite), + (0x08 => Tn_COMPARATOR: ReadWrite), + (0x10 => Tn_FSB_ROUTE: ReadWrite), + (0x18 => _0), + (0x20 => @END), + } +} + +pub struct HpetTimer { + name: String, + parent: Arc, + regs: IrqSafeRwLock>, + system_clock_source: bool, + period: u64, + + last_ticks: AtomicU64, + + periodic: bool, + bits64: bool, +} + +pub struct Hpet { + base: PhysicalAddress, + regs: IrqSafeRwLock>, + timers: IrqSafeRwLock>>, + + base_frequency: u64, + base_period: u64, +} + +const FS_IN_S: u64 = 10u64.pow(15); +const FS_IN_NS: u64 = 10u64.pow(6); + +impl InterruptHandler for HpetTimer { + fn handle_irq(self: Arc, _vector: Option) -> bool { + let now = self.parent.read_counter(); + let last = self.last_ticks.swap(now, Ordering::Relaxed); + let delta = now.wrapping_sub(last); + + if !self.periodic { + self.regs.write().rearm(now.wrapping_add(self.period)); + } + + if self.system_clock_source { + let dt = delta * (self.parent.base_period / FS_IN_NS); + time::add_nanoseconds(dt); + runtime::tick(); + } + + true + } + + fn display_name(&self) -> &str { + self.name.as_str() + } +} + +impl Device for HpetTimer { + unsafe fn init(self: Arc) -> Result<(), Error> { + // TODO support arbitrary configurations + assert!(self.bits64); + + let intc = external_interrupt_controller()?; + + let regs = self.regs.write(); + + if regs.is_fsb_capable() { + log::warn!("TODO: HPET supports FSB interrupt delivery, but the kernel doesn't yet"); + } + + let irq = regs.select_irq().ok_or(Error::InvalidArgument)?; + + log::info!( + "Set up {:?}, period={}t, periodic={}, irq={:?}", + self.name, + self.period, + self.periodic, + irq + ); + + intc.register_irq( + irq, + IrqOptions { + trigger: IrqTrigger::Edge, + ..Default::default() + }, + self.clone(), + )?; + intc.enable_irq(irq)?; + let irq = IrqDestination::Irq(irq); + + let now = self.parent.read_counter(); + + // Pause the timer + regs.stop(); + if self.periodic { + regs.configure_periodic(now.wrapping_add(self.period), self.period, irq); + } else { + regs.configure_oneshot(now.wrapping_add(self.period), irq); + } + + Ok(()) + } + + unsafe fn init_irq(self: Arc) -> Result<(), Error> { + todo!() + } + + fn display_name(&self) -> &str { + self.name.as_str() + } +} + +impl Device for Hpet { + unsafe fn init(self: Arc) -> Result<(), Error> { + let regs = self.regs.write(); + + // Reset the device until at least one timer is created + regs.stop(); + regs.GENERAL_CONFIG + .modify(GENERAL_CONFIG::LEG_RT_CNF::CLEAR); + regs.MAIN_COUNTER.set(0); + + Ok(()) + } + + fn display_name(&self) -> &str { + "HPET" + } +} + +impl HpetTimer { + fn create( + hpet: Arc, + index: usize, + frequency: u64, + system_clock_source: bool, + ) -> Result, Error> { + let base = hpet.base.add(0x100 + 0x20 * index); + let regs = unsafe { DeviceMemoryIo::::map(base, Default::default()) }?; + + let period = hpet.base_frequency / frequency; + + let periodic = regs.is_periodic(); + let bits64 = regs.is_64_bit(); + + let timer = Arc::new(HpetTimer { + name: format!("hpet{index}"), + regs: IrqSafeRwLock::new(regs), + parent: hpet, + last_ticks: AtomicU64::new(0), + system_clock_source, + periodic, + period, + bits64, + }); + + Ok(timer) + } +} + +impl Hpet { + fn new(base: PhysicalAddress) -> Result { + let regs = unsafe { DeviceMemoryIo::::map(base, Default::default()) }?; + let base_period = regs.timer_period(); + let base_frequency = FS_IN_S / base_period; + Ok(Self { + base, + base_period, + base_frequency, + regs: IrqSafeRwLock::new(regs), + timers: IrqSafeRwLock::new(BTreeMap::new()), + }) + } + + pub fn setup_from_acpi(acpi: &HpetInfo) -> Result, Error> { + let base = PhysicalAddress::from_usize(acpi.base_address); + let hpet = Arc::new(Self::new(base)?); + unsafe { hpet.clone().init() }?; + Ok(hpet) + } + + pub fn setup_timer( + self: Arc, + index: usize, + frequency: u64, + system_clock_source: bool, + ) -> Result, Error> { + let regs = self.regs.write(); + let mut timers = self.timers.write(); + let timer_count = regs.timer_count(); + + if index >= timer_count { + log::warn!("HPET #{index} does not exist"); + return Err(Error::DoesNotExist); + } + + if timers.contains_key(&index) { + log::warn!("HPET #{index} already configured"); + return Err(Error::AlreadyExists); + } + + // Pause the timer + regs.stop(); + + drop(regs); + + let timer = HpetTimer::create(self.clone(), index, frequency, system_clock_source); + let timer = if let Ok(timer) = timer { + timers.insert(index, timer.clone()); + + match unsafe { timer.clone().init() } { + Ok(()) => Ok(timer), + Err(error) => Err(error), + } + } else { + timer + }; + + let regs = self.regs.write(); + + regs.start(); + + timer + } + + fn read_counter(&self) -> u64 { + self.regs.read().counter() + } +} + +impl TimerRegs { + fn is_periodic(&self) -> bool { + self.Tn_CONFIG.matches_all(Tn_CONFIG::Tn_PER_INT_CAP::SET) + } + + fn is_64_bit(&self) -> bool { + self.Tn_CONFIG.matches_all(Tn_CONFIG::Tn_SIZE_CAP::SET) + } + + fn is_fsb_capable(&self) -> bool { + self.Tn_CONFIG + .matches_all(Tn_CONFIG::Tn_FSB_INT_DEL_CAP::SET) + } + + fn select_irq(&self) -> Option { + let mask = self.Tn_CONFIG.read(Tn_CONFIG::Tn_INT_ROUTE_CAP); + for i in 9..32 { + if mask & (1 << i) != 0 { + return Some(Irq::External(i)); + } + } + None + } + + fn stop(&self) { + self.Tn_CONFIG.modify(Tn_CONFIG::Tn_INT_ENB_CNF::CLEAR); + } + + fn enable(&self, irq: IrqDestination) { + let config = match irq { + IrqDestination::Irq(Irq::External(irq)) => { + Tn_CONFIG::Tn_INT_ROUTE_CNF.val(irq as u64) + Tn_CONFIG::Tn_INT_TYPE_CNF::Edge + } + _ => unreachable!(), + }; + + self.Tn_CONFIG.modify(config); + self.Tn_CONFIG.modify(Tn_CONFIG::Tn_INT_ENB_CNF::SET); + } + + fn configure_oneshot(&self, compare_value: u64, irq: IrqDestination) { + self.Tn_CONFIG + .modify(Tn_CONFIG::Tn_32MODE_CNF::CLEAR + Tn_CONFIG::Tn_TYPE_CNF::NonPeriodic); + + self.Tn_COMPARATOR.set(compare_value); + + self.enable(irq); + } + + fn configure_periodic(&self, compare_value: u64, period: u64, irq: IrqDestination) { + self.Tn_CONFIG.modify( + Tn_CONFIG::Tn_VAL_SET_CNF::SET + + Tn_CONFIG::Tn_32MODE_CNF::CLEAR + + Tn_CONFIG::Tn_TYPE_CNF::Periodic, + ); + self.Tn_COMPARATOR.set(compare_value); + self.Tn_COMPARATOR.set(period); + + self.enable(irq); + } + + fn rearm(&self, compare_value: u64) { + self.Tn_COMPARATOR.set(compare_value); + } +} + +impl GeneralRegs { + fn stop(&self) { + self.GENERAL_CONFIG + .modify(GENERAL_CONFIG::ENABLE_CNF::CLEAR); + } + fn start(&self) { + self.GENERAL_CONFIG.modify(GENERAL_CONFIG::ENABLE_CNF::SET); + } + + fn timer_count(&self) -> usize { + self.GENERAL_CAPABILITIES + .read(GENERAL_CAPABILITIES::NUM_TIM_CAP) as usize + + 1 + } + + fn timer_period(&self) -> u64 { + self.GENERAL_CAPABILITIES + .read(GENERAL_CAPABILITIES::COUNTER_CLK_PERIOD) + } + + fn counter(&self) -> u64 { + self.MAIN_COUNTER.get() + } +} + +enum IrqDestination { + Irq(Irq), +} diff --git a/kernel/src/arch/x86/peripherals/i8253.rs b/kernel/src/arch/x86/peripherals/i8253.rs index 0929879f..2d620a96 100644 --- a/kernel/src/arch/x86/peripherals/i8253.rs +++ b/kernel/src/arch/x86/peripherals/i8253.rs @@ -85,7 +85,7 @@ impl I8253 { } pub fn irq_handler_fastpath(&self) { - time::add_ticks(1); + time::add_milliseconds(1); runtime::tick(); #[cfg(any(target_arch = "x86", rust_analyzer))] diff --git a/kernel/src/arch/x86/peripherals/mod.rs b/kernel/src/arch/x86/peripherals/mod.rs index eb14817c..67797734 100644 --- a/kernel/src/arch/x86/peripherals/mod.rs +++ b/kernel/src/arch/x86/peripherals/mod.rs @@ -3,3 +3,6 @@ pub mod i8259; pub mod ps2; pub mod rtc; pub mod serial; + +#[cfg(any(target_arch = "x86_64", rust_analyzer))] +pub mod hpet; diff --git a/kernel/src/arch/x86/peripherals/rtc.rs b/kernel/src/arch/x86/peripherals/rtc.rs index 27d5b442..9c73403b 100644 --- a/kernel/src/arch/x86/peripherals/rtc.rs +++ b/kernel/src/arch/x86/peripherals/rtc.rs @@ -131,7 +131,7 @@ impl InterruptHandler for Rtc { let time = inner.read_date_time(); inner.last_timestamp = Some(time); - time::set_real_time(time.to_ticks(), true); + time::set_real_seconds(time.to_seconds(), true); } inner.read_reg(CMOS_REG_STATUS_C); @@ -182,7 +182,7 @@ impl Rtc { } impl DateTime { - pub fn to_ticks(self) -> u64 { + pub fn to_seconds(self) -> u64 { let date = chrono::NaiveDate::from_ymd_opt( 2000 + self.year as i32, self.month as u32, @@ -199,6 +199,6 @@ impl DateTime { .signed_duration_since(chrono::NaiveDateTime::UNIX_EPOCH) .num_seconds() as u64; - seconds * time::TICKS_PER_SEC + seconds } } diff --git a/kernel/src/arch/x86_64/apic/mod.rs b/kernel/src/arch/x86_64/apic/mod.rs index f0eeb637..d4baf409 100644 --- a/kernel/src/arch/x86_64/apic/mod.rs +++ b/kernel/src/arch/x86_64/apic/mod.rs @@ -4,7 +4,11 @@ use core::arch::global_asm; use kernel_arch::task::Scheduler; use kernel_arch_x86_64::context::IrqFrame; -use libk::{arch::Cpu, device::external_interrupt_controller, task::thread::Thread}; +use libk::{ + arch::Cpu, + device::external_interrupt_controller, + task::{runtime, thread::Thread}, +}; use static_assertions::{const_assert, const_assert_eq}; use super::exception; @@ -90,6 +94,9 @@ unsafe extern "C" fn msi_handler(vector: usize, frame: *mut IrqFrame) { unsafe extern "C" fn local_timer_irq_handler(frame: *mut IrqFrame) { let frame = &mut *frame; + + runtime::tick(); + let cpu = Cpu::local(); // Clear interrupt before switching, because otherwise we won't receive the next one cpu.local_apic().clear_interrupt(); diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index 535b2885..d9b571f5 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -1,11 +1,12 @@ //! x86-64 architecture implementation use core::{mem::size_of, ops::DerefMut, ptr::null_mut, sync::atomic::Ordering}; -use ::acpi::{mcfg::Mcfg, AcpiTables, InterruptModel}; +use ::acpi::{mcfg::Mcfg, AcpiTables, HpetInfo, InterruptModel}; use abi::error::Error; use acpi::{AcpiAllocator, AcpiHandlerImpl}; use alloc::{boxed::Box, sync::Arc}; use apic::{ioapic::IoApic, local::LocalApic}; +use device_api::device::Device; use kernel_arch_x86::{ cpuid::{self, CpuFeatures, EcxFeatures, EdxFeatures}, gdt, @@ -47,7 +48,10 @@ mod exception; mod smp; mod syscall; -use crate::{arch::x86, device::display::linear_fb::LinearFramebuffer}; +use crate::{ + arch::x86::{self, peripherals::hpet::Hpet}, + device::display::linear_fb::LinearFramebuffer, +}; use self::boot::BootData; @@ -255,7 +259,7 @@ impl X86_64 { self.setup_from_boot_data()?; - let early = x86::init_platform_devices_early()?; + let mut early = x86::init_platform_devices_early()?; if let Err(error) = self.init_framebuffer() { log::error!("Could not initialize boot framebuffer: {error:?}"); @@ -265,12 +269,22 @@ impl X86_64 { self.init_platform_from_acpi(acpi)?; } + early.add_preferred_clock_source("hpet", Self::setup_hpet); + x86::init_platform_devices(early); } Ok(()) } + fn setup_hpet() -> Result, Error> { + let acpi = PLATFORM.acpi.try_get().ok_or(Error::DoesNotExist)?; + let hpet = HpetInfo::new(acpi).map_err(|_| Error::DoesNotExist)?; + let hpet = Hpet::setup_from_acpi(&hpet)?; + let timer = hpet.setup_timer(0, 1000, true)?; + Ok(timer) + } + unsafe fn init_local_cpu(&self, cpu_id: usize) { let local_apic = Box::new(LocalApic::new()); let tss_address = gdt::init(); @@ -424,31 +438,6 @@ impl X86_64 { Ok(()) } - - // fn init_initrd(initrd_start: PhysicalAddress, initrd_end: PhysicalAddress) { - // if initrd_start.is_zero() || initrd_end <= initrd_start { - // log::info!("No initrd loaded"); - // return; - // } - - // let start_aligned = initrd_start.page_align_down::(); - // let end_aligned = initrd_start.page_align_up::(); - - // let data = unsafe { - // core::slice::from_raw_parts( - // start_aligned.virtualize() as *const u8, - // initrd_end - initrd_start, - // ) - // }; - - // let initrd = Initrd { - // phys_page_start: start_aligned, - // phys_page_len: end_aligned - start_aligned, - // data, - // }; - - // INITRD_DATA.init(initrd); - // } } impl InitrdSource for LoadProtocolV1 { diff --git a/userspace/sysutils/Cargo.toml b/userspace/sysutils/Cargo.toml index eb8a35aa..6b11cee7 100644 --- a/userspace/sysutils/Cargo.toml +++ b/userspace/sysutils/Cargo.toml @@ -104,6 +104,10 @@ path = "src/date.rs" name = "sync" path = "src/sync.rs" +[[bin]] +name = "sleep" +path = "src/sleep.rs" + [[bin]] name = "tst" path = "src/tst.rs" diff --git a/userspace/sysutils/src/sleep.rs b/userspace/sysutils/src/sleep.rs new file mode 100644 index 00000000..f471d8df --- /dev/null +++ b/userspace/sysutils/src/sleep.rs @@ -0,0 +1,25 @@ +use std::{env, process::ExitCode, thread::sleep, time::Duration}; + +fn parse_args(args: &[String]) -> Option { + if args.len() != 2 { + return None; + } + + let ms: u64 = args[1].parse().ok()?; + Some(Duration::from_millis(ms)) +} + +fn main() -> ExitCode { + let args = env::args().collect::>(); + let t = match parse_args(&args) { + Some(t) => t, + None => { + eprintln!("Usage: {} ", args[0]); + return ExitCode::FAILURE; + } + }; + + sleep(t); + + ExitCode::SUCCESS +} diff --git a/xtask/src/build/userspace.rs b/xtask/src/build/userspace.rs index 57238bd5..2e9d1084 100644 --- a/xtask/src/build/userspace.rs +++ b/xtask/src/build/userspace.rs @@ -42,6 +42,7 @@ const PROGRAMS: &[(&str, &str)] = &[ ("sysmon", "bin/sysmon"), ("date", "bin/date"), ("sync", "bin/sync"), + ("sleep", "bin/sleep"), ("tst", "bin/tst"), // netutils ("netconf", "sbin/netconf"),