timer: rework timers, implement hpet for x86_64
This commit is contained in:
parent
433094837d
commit
718aad8a7a
@ -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() }
|
||||
}
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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::{
|
||||
|
@ -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<Self>, _vector: Option<usize>) -> 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 {
|
||||
|
@ -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<Arc<dyn Device>, 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<ComPort>,
|
||||
pub i8259: Arc<I8259>,
|
||||
|
||||
clock_sources: Vec<ProbeClockSource>,
|
||||
}
|
||||
|
||||
pub enum SelectedClockSource {
|
||||
Proper(Arc<dyn Device>),
|
||||
Fallback(Arc<I8253>),
|
||||
}
|
||||
|
||||
// TODO move this to some sort of .init_array-style implicit thing
|
||||
@ -106,7 +120,11 @@ pub fn init_platform_devices_early() -> Result<EarlyPlatformDevices, Error> {
|
||||
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<SelectedClockSource, Error> {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
395
kernel/src/arch/x86/peripherals/hpet.rs
Normal file
395
kernel/src/arch/x86/peripherals/hpet.rs
Normal file
@ -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<u64, GENERAL_CAPABILITIES::Register>),
|
||||
(0x008 => _0),
|
||||
(0x010 => GENERAL_CONFIG: ReadWrite<u64, GENERAL_CONFIG::Register>),
|
||||
(0x018 => _1),
|
||||
(0x020 => GENERAL_ISR: ReadWrite<u64>),
|
||||
(0x028 => _2),
|
||||
(0x0F0 => MAIN_COUNTER: ReadWrite<u64>),
|
||||
(0x0F8 => _3),
|
||||
(0x100 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
TimerRegs {
|
||||
(0x00 => Tn_CONFIG: ReadWrite<u64, Tn_CONFIG::Register>),
|
||||
(0x08 => Tn_COMPARATOR: ReadWrite<u64>),
|
||||
(0x10 => Tn_FSB_ROUTE: ReadWrite<u64>),
|
||||
(0x18 => _0),
|
||||
(0x20 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
pub struct HpetTimer {
|
||||
name: String,
|
||||
parent: Arc<Hpet>,
|
||||
regs: IrqSafeRwLock<DeviceMemoryIo<'static, TimerRegs>>,
|
||||
system_clock_source: bool,
|
||||
period: u64,
|
||||
|
||||
last_ticks: AtomicU64,
|
||||
|
||||
periodic: bool,
|
||||
bits64: bool,
|
||||
}
|
||||
|
||||
pub struct Hpet {
|
||||
base: PhysicalAddress,
|
||||
regs: IrqSafeRwLock<DeviceMemoryIo<'static, GeneralRegs>>,
|
||||
timers: IrqSafeRwLock<BTreeMap<usize, Arc<HpetTimer>>>,
|
||||
|
||||
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<Self>, _vector: Option<usize>) -> 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<Self>) -> 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<Self>) -> Result<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Hpet {
|
||||
unsafe fn init(self: Arc<Self>) -> 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<Hpet>,
|
||||
index: usize,
|
||||
frequency: u64,
|
||||
system_clock_source: bool,
|
||||
) -> Result<Arc<Self>, Error> {
|
||||
let base = hpet.base.add(0x100 + 0x20 * index);
|
||||
let regs = unsafe { DeviceMemoryIo::<TimerRegs>::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<Self, Error> {
|
||||
let regs = unsafe { DeviceMemoryIo::<GeneralRegs>::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<Arc<Self>, 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<Self>,
|
||||
index: usize,
|
||||
frequency: u64,
|
||||
system_clock_source: bool,
|
||||
) -> Result<Arc<HpetTimer>, 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<Irq> {
|
||||
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),
|
||||
}
|
@ -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))]
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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<Arc<dyn Device>, 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::<L3>();
|
||||
// let end_aligned = initrd_start.page_align_up::<L3>();
|
||||
|
||||
// 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 {
|
||||
|
@ -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"
|
||||
|
25
userspace/sysutils/src/sleep.rs
Normal file
25
userspace/sysutils/src/sleep.rs
Normal file
@ -0,0 +1,25 @@
|
||||
use std::{env, process::ExitCode, thread::sleep, time::Duration};
|
||||
|
||||
fn parse_args(args: &[String]) -> Option<Duration> {
|
||||
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::<Vec<_>>();
|
||||
let t = match parse_args(&args) {
|
||||
Some(t) => t,
|
||||
None => {
|
||||
eprintln!("Usage: {} <ms>", args[0]);
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
};
|
||||
|
||||
sleep(t);
|
||||
|
||||
ExitCode::SUCCESS
|
||||
}
|
@ -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"),
|
||||
|
Loading…
x
Reference in New Issue
Block a user