timer: rework timers, implement hpet for x86_64

This commit is contained in:
Mark Poliakov 2024-12-10 20:37:47 +02:00
parent 433094837d
commit 718aad8a7a
15 changed files with 582 additions and 63 deletions

View File

@ -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() }
}

View File

@ -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));

View File

@ -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);
}

View File

@ -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::{

View File

@ -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 {

View File

@ -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 });
}
}

View 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),
}

View File

@ -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))]

View File

@ -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;

View File

@ -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
}
}

View File

@ -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();

View File

@ -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 {

View File

@ -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"

View 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
}

View File

@ -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"),