Outsource x86 RTC handling to rtcd.
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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<u8>,
|
||||
data: Pio<u8>,
|
||||
nmi: bool,
|
||||
}
|
||||
|
||||
impl Rtc {
|
||||
/// Create new empty RTC
|
||||
pub fn new() -> Self {
|
||||
Rtc {
|
||||
addr: Pio::<u8>::new(0x70),
|
||||
data: Pio::<u8>::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+94
-44
@@ -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<u8> },
|
||||
Resource {
|
||||
path: &'static str,
|
||||
data: Option<Vec<u8>>,
|
||||
},
|
||||
}
|
||||
|
||||
type SysFn = fn() -> Result<Vec<u8>>;
|
||||
enum Kind {
|
||||
Rd(fn() -> Result<Vec<u8>>),
|
||||
Wr(fn(&[u8]) -> Result<usize>),
|
||||
}
|
||||
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<BTreeMap<usize, Handle>> = 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<u64> {
|
||||
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<usize> {
|
||||
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 {
|
||||
|
||||
@@ -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<usize> {
|
||||
let start = <[u8; 16]>::try_from(buf).map_err(|_| Error::new(EINVAL))?;
|
||||
*START.lock() = u128::from_ne_bytes(start);
|
||||
Ok(16)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user