Outsource x86 RTC handling to rtcd.

This commit is contained in:
4lDO2
2025-03-30 15:28:25 +02:00
parent ce60a7939e
commit 3ae117b812
4 changed files with 102 additions and 197 deletions
-3
View File
@@ -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");
-150
View File
@@ -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
View File
@@ -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 {
+8
View File
@@ -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)
}