diff --git a/arch/x86_64/src/device/mod.rs b/arch/x86_64/src/device/mod.rs index ff8f7c7..7a50f6e 100644 --- a/arch/x86_64/src/device/mod.rs +++ b/arch/x86_64/src/device/mod.rs @@ -1,5 +1,7 @@ +pub mod rtc; pub mod serial; pub unsafe fn init(){ + rtc::init(); serial::init(); } diff --git a/arch/x86_64/src/device/rtc.rs b/arch/x86_64/src/device/rtc.rs new file mode 100644 index 0000000..ef6de05 --- /dev/null +++ b/arch/x86_64/src/device/rtc.rs @@ -0,0 +1,109 @@ +use io::{Io, Pio}; +use time; + +pub fn init() { + let mut rtc = Rtc::new(); + time::START.lock().0 = rtc.time(); +} + +fn cvt_bcd(value: usize) -> usize { + (value & 0xF) + ((value / 16) * 10) +} + +/// RTC +pub struct Rtc { + addr: Pio, + data: Pio, +} + +impl Rtc { + /// Create new empty RTC + pub fn new() -> Self { + return Rtc { + addr: Pio::::new(0x70), + data: Pio::::new(0x71), + }; + } + + /// Read + unsafe fn read(&mut self, reg: u8) -> u8 { + self.addr.write(reg); + return self.data.read(); + } + + /// Wait + unsafe fn wait(&mut self) { + while self.read(0xA) & 0x80 != 0x80 {} + while self.read(0xA) & 0x80 == 0x80 {} + } + + /// Get time + pub fn time(&mut self) -> u64 { + let mut second; + let mut minute; + let mut hour; + let mut day; + let mut month; + let mut year; + let register_b; + unsafe { + self.wait(); + second = self.read(0) as usize; + minute = self.read(2) as usize; + hour = self.read(4) as usize; + day = self.read(7) as usize; + month = self.read(8) as usize; + year = self.read(9) as usize; + 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); + } + + if register_b & 2 != 2 || hour & 0x80 == 0x80 { + hour = ((hour & 0x7F) + 12) % 24; + } + + // TODO: Century Register + year += 2000; + + // Unix time from clock + let mut secs: u64 = (year as u64 - 1970) * 31536000; + + let mut leap_days = (year as u64 - 1972) / 4 + 1; + if year % 4 == 0 { + if month <= 2 { + leap_days -= 1; + } + } + secs += leap_days * 86400; + + match month { + 2 => secs += 2678400, + 3 => secs += 5097600, + 4 => secs += 7776000, + 5 => secs += 10368000, + 6 => secs += 13046400, + 7 => secs += 15638400, + 8 => secs += 18316800, + 9 => secs += 20995200, + 10 => secs += 23587200, + 11 => secs += 26265600, + 12 => secs += 28857600, + _ => (), + } + + secs += (day as u64 - 1) * 86400; + secs += hour as u64 * 3600; + secs += minute as u64 * 60; + secs += second as u64; + + secs + } +} diff --git a/arch/x86_64/src/interrupt/irq.rs b/arch/x86_64/src/interrupt/irq.rs index 091a2e2..18cd3c7 100644 --- a/arch/x86_64/src/interrupt/irq.rs +++ b/arch/x86_64/src/interrupt/irq.rs @@ -2,6 +2,7 @@ use spin::Mutex; use x86::io; use device::serial::{COM1, COM2}; +use time; pub static ACKS: Mutex<[usize; 16]> = Mutex::new([0; 16]); pub static COUNTS: Mutex<[usize; 16]> = Mutex::new([0; 16]); @@ -27,6 +28,16 @@ pub unsafe fn acknowledge(irq: usize) { interrupt!(pit, { COUNTS.lock()[0] += 1; + + { + const PIT_RATE: u64 = 46500044; + + let mut offset = time::OFFSET.lock(); + let sum = offset.1 + PIT_RATE; + offset.1 = sum % 1000000000; + offset.0 += sum / 1000000000; + } + master_ack(); }); diff --git a/arch/x86_64/src/lib.rs b/arch/x86_64/src/lib.rs index 427fe62..300cb1e 100644 --- a/arch/x86_64/src/lib.rs +++ b/arch/x86_64/src/lib.rs @@ -218,3 +218,6 @@ pub mod panic; /// Initialization and start function pub mod start; + +/// Time +pub mod time; diff --git a/arch/x86_64/src/time.rs b/arch/x86_64/src/time.rs new file mode 100644 index 0000000..7af3ef7 --- /dev/null +++ b/arch/x86_64/src/time.rs @@ -0,0 +1,15 @@ +use spin::Mutex; + +pub static START: Mutex<(u64, u64)> = Mutex::new((0, 0)); +pub static OFFSET: Mutex<(u64, u64)> = Mutex::new((0, 0)); + +pub fn monotonic() -> (u64, u64) { + *OFFSET.lock() +} + +pub fn realtime() -> (u64, u64) { + let offset = monotonic(); + let start = *START.lock(); + let sum = start.1 + offset.1; + (start.0 + offset.0 + sum / 1000000000, sum % 1000000000) +} diff --git a/bootloader/x86_64/initialize.asm b/bootloader/x86_64/initialize.asm index 20f059b..1d468a1 100644 --- a/bootloader/x86_64/initialize.asm +++ b/bootloader/x86_64/initialize.asm @@ -38,7 +38,7 @@ initialize: .pit: ;initialize the PIT - mov ax, 65535 ;5370 ;this is the divider for the PIT + mov ax, 55483 ;5370 ;this is the divider for the PIT out 0x40, al rol ax, 8 out 0x40, al diff --git a/kernel/scheme/pipe.rs b/kernel/scheme/pipe.rs index eee3d4f..f9e680d 100644 --- a/kernel/scheme/pipe.rs +++ b/kernel/scheme/pipe.rs @@ -3,7 +3,6 @@ use collections::{BTreeMap, VecDeque}; use core::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; use spin::{Mutex, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; -use context; use syscall::error::{Error, Result, EBADF, EPIPE}; use syscall::scheme::Scheme; diff --git a/kernel/syscall/mod.rs b/kernel/syscall/mod.rs index 9599a44..9de8b92 100644 --- a/kernel/syscall/mod.rs +++ b/kernel/syscall/mod.rs @@ -6,8 +6,10 @@ pub use self::syscall::{data, error, flag, number, scheme}; pub use self::fs::*; pub use self::process::*; +pub use self::time::*; pub use self::validate::*; +use self::data::TimeSpec; use self::error::{Error, Result, ENOSYS}; use self::number::*; @@ -17,6 +19,9 @@ pub mod fs; /// Process syscalls pub mod process; +/// Time syscalls +pub mod time; + /// Validate input pub mod validate; @@ -52,6 +57,7 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize SYS_IOPL => iopl(b), SYS_CLONE => clone(b, stack), SYS_YIELD => sched_yield(), + SYS_NANOSLEEP => nanosleep(validate_slice(b as *const TimeSpec, 1).map(|req| &req[0])?, validate_slice_mut(c as *mut TimeSpec, 1).ok().map(|rem| &mut rem[0])), SYS_GETCWD => getcwd(validate_slice_mut(b as *mut u8, c)?), SYS_GETUID => getuid(), SYS_GETGID => getgid(), @@ -59,6 +65,7 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize SYS_GETEGID => getegid(), SYS_SETUID => setuid(b as u32), SYS_SETGID => setgid(b as u32), + SYS_CLOCK_GETTIME => clock_gettime(b, validate_slice_mut(c as *mut TimeSpec, 1).map(|time| &mut time[0])?), SYS_PIPE2 => pipe2(validate_slice_mut(b as *mut usize, 2)?, c), SYS_PHYSALLOC => physalloc(b), SYS_PHYSFREE => physfree(b, c), diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs index 61d0b03..cdd768f 100644 --- a/kernel/syscall/process.rs +++ b/kernel/syscall/process.rs @@ -770,11 +770,6 @@ pub fn physunmap(virtual_address: usize) -> Result { } } -pub fn sched_yield() -> Result { - unsafe { context::switch(); } - Ok(0) -} - pub fn setgid(gid: u32) -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; diff --git a/kernel/syscall/time.rs b/kernel/syscall/time.rs new file mode 100644 index 0000000..ca05773 --- /dev/null +++ b/kernel/syscall/time.rs @@ -0,0 +1,50 @@ +use arch; +use context; +use syscall::data::TimeSpec; +use syscall::error::*; +use syscall::flag::{CLOCK_REALTIME, CLOCK_MONOTONIC}; + +pub fn clock_gettime(clock: usize, time: &mut TimeSpec) -> Result { + match clock { + CLOCK_REALTIME => { + let arch_time = arch::time::realtime(); + time.tv_sec = arch_time.0 as i64; + time.tv_nsec = arch_time.1 as i32; + Ok(0) + }, + CLOCK_MONOTONIC => { + let arch_time = arch::time::monotonic(); + time.tv_sec = arch_time.0 as i64; + time.tv_nsec = arch_time.1 as i32; + Ok(0) + }, + _ => Err(Error::new(EINVAL)) + } +} + +pub fn nanosleep(req: &TimeSpec, rem_opt: Option<&mut TimeSpec>) -> Result { + let start = arch::time::monotonic(); + let sum = start.1 + req.tv_nsec as u64; + let end = (start.0 + req.tv_sec as u64 + sum / 1000000000, sum % 1000000000); + + loop { + unsafe { context::switch(); } + + let current = arch::time::monotonic(); + if current.0 > end.0 || (current.0 == end.0 && current.1 >= end.1) { + break; + } + } + + if let Some(mut rem) = rem_opt { + rem.tv_sec = 0; + rem.tv_nsec = 0; + } + + Ok(0) +} + +pub fn sched_yield() -> Result { + unsafe { context::switch(); } + Ok(0) +} diff --git a/syscall/src/flag.rs b/syscall/src/flag.rs index c3e7d91..679fe5e 100644 --- a/syscall/src/flag.rs +++ b/syscall/src/flag.rs @@ -11,6 +11,7 @@ pub const CLONE_VFORK: usize = 0x4000; /// This is an important security measure, since otherwise the process would be able to fork it /// self right after starting, making supervising it impossible. pub const CLONE_SUPERVISE: usize = 0x400000; + pub const CLOCK_REALTIME: usize = 1; pub const CLOCK_MONOTONIC: usize = 4;