diff --git a/kernel/src/arch/x86/peripherals/hpet.rs b/kernel/src/arch/x86/peripherals/hpet.rs index 902f51fb..a7b35313 100644 --- a/kernel/src/arch/x86/peripherals/hpet.rs +++ b/kernel/src/arch/x86/peripherals/hpet.rs @@ -5,7 +5,9 @@ use acpi::HpetInfo; use alloc::{collections::btree_map::BTreeMap, format, string::String, sync::Arc}; use device_api::{ device::{Device, DeviceInitContext}, - interrupt::{InterruptHandler, Irq, IrqOptions, IrqTrigger, IrqVector}, + interrupt::{ + InterruptAffinity, InterruptHandler, Irq, IrqOptions, IrqTrigger, IrqVector, MsiInfo, + }, }; use libk::{device::external_interrupt_controller, task::runtime, time}; use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo}; @@ -16,7 +18,7 @@ use tock_registers::{ registers::{ReadOnly, ReadWrite}, }; -use crate::arch::x86::dummy_init_context; +use crate::arch::{x86::dummy_init_context, x86_64::X86_64}; register_bitfields! { u64, @@ -49,7 +51,11 @@ register_bitfields! { Edge = 0, Level = 1, ], - ] + ], + Tn_FSB_ROUTE [ + Tn_FSB_INT_VAL OFFSET(0) NUMBITS(32) [], + Tn_FSB_INT_ADDR OFFSET(32) NUMBITS(32) [], + ], } register_structs! { @@ -72,7 +78,7 @@ register_structs! { TimerRegs { (0x00 => Tn_CONFIG: ReadWrite), (0x08 => Tn_COMPARATOR: ReadWrite), - (0x10 => Tn_FSB_ROUTE: ReadWrite), + (0x10 => Tn_FSB_ROUTE: ReadWrite), (0x18 => _0), (0x20 => @END), } @@ -136,30 +142,40 @@ impl Device for HpetTimer { let regs = self.regs.write(); - if regs.is_fsb_capable() { - log::warn!("TODO: HPET supports FSB interrupt delivery, but the kernel doesn't yet"); - } + #[cfg(target_arch = "x86_64")] + let irq = if regs.is_fsb_capable() { + let lapic = X86_64::cpu_local_apic(); + let msi_info = lapic.register_msi(InterruptAffinity::Any, self.clone())?; + log::info!( + "Set up {:?}, period={}t, periodic={}, msi mode", + self.name, + self.period, + self.periodic + ); - let irq = regs.select_irq().ok_or(Error::InvalidArgument)?; + IrqDestination::Fsb(msi_info) + } else { + let irq = regs.select_irq().ok_or(Error::InvalidArgument)?; + log::info!( + "Set up {:?}, period={}t, periodic={}, irq={:?}", + self.name, + self.period, + self.periodic, + irq + ); - 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)?; - intc.register_irq( - irq, - IrqOptions { - trigger: IrqTrigger::Edge, - ..Default::default() - }, - self.clone(), - )?; - intc.enable_irq(irq)?; - let irq = IrqDestination::Irq(irq); + IrqDestination::Irq(irq) + }; let now = self.parent.read_counter(); @@ -216,6 +232,8 @@ impl HpetTimer { let periodic = regs.is_periodic(); let bits64 = regs.is_64_bit(); + log::info!("hpet{index}: periodic={periodic}, bits64={bits64}, period={period}"); + let timer = Arc::new(HpetTimer { name: format!("hpet{index}"), regs: IrqSafeRwLock::new(regs), @@ -235,14 +253,42 @@ impl Hpet { fn new(base: PhysicalAddress) -> Result { let regs = unsafe { DeviceMemoryIo::::map(base, Default::default()) }?; let base_period = regs.timer_period(); + // qemu: 100000000 + // t430: 14318179 let base_frequency = FS_IN_S / base_period; - Ok(Self { + + let hpet = Self { base, base_period, base_frequency, regs: IrqSafeRwLock::new(regs), timers: IrqSafeRwLock::new(BTreeMap::new()), - }) + }; + + hpet.test_timer_counts()?; + + Ok(hpet) + } + + fn test_timer_counts(&self) -> Result<(), Error> { + let regs = self.regs.write(); + regs.stop(); + for _ in 0..10000 { + core::hint::spin_loop(); + } + let start = regs.counter(); + regs.start(); + for _ in 0..100000 { + core::hint::spin_loop(); + } + regs.stop(); + let now = regs.counter(); + + if now > start { + Ok(()) + } else { + Err(Error::InvalidOperation) + } } pub fn setup_from_acpi(acpi: &HpetInfo) -> Result, Error> { @@ -332,14 +378,28 @@ impl TimerRegs { } fn enable(&self, irq: IrqDestination) { - let config = match irq { + match irq { IrqDestination::Irq(Irq::External(irq)) => { - Tn_CONFIG::Tn_INT_ROUTE_CNF.val(irq as u64) + Tn_CONFIG::Tn_INT_TYPE_CNF::Edge + self.Tn_CONFIG.modify( + Tn_CONFIG::Tn_INT_ROUTE_CNF.val(irq as u64) + + Tn_CONFIG::Tn_INT_TYPE_CNF::Edge + + Tn_CONFIG::Tn_FSB_EN_CNF::CLEAR, + ); + } + IrqDestination::Fsb(MsiInfo { address, value, .. }) => { + let address: u32 = address + .try_into() + .expect("FIXME: non-32-bit LAPIC MSI address"); + self.Tn_CONFIG + .modify(Tn_CONFIG::Tn_INT_TYPE_CNF::Edge + Tn_CONFIG::Tn_FSB_EN_CNF::SET); + self.Tn_FSB_ROUTE.write( + Tn_FSB_ROUTE::Tn_FSB_INT_VAL.val(value as u64) + + Tn_FSB_ROUTE::Tn_FSB_INT_ADDR.val(address as u64), + ); } _ => unreachable!(), - }; + } - self.Tn_CONFIG.modify(config); self.Tn_CONFIG.modify(Tn_CONFIG::Tn_INT_ENB_CNF::SET); } @@ -396,4 +456,5 @@ impl GeneralRegs { enum IrqDestination { Irq(Irq), + Fsb(MsiInfo), } diff --git a/kernel/src/arch/x86_64/mod.rs b/kernel/src/arch/x86_64/mod.rs index b494b8b4..9e432344 100644 --- a/kernel/src/arch/x86_64/mod.rs +++ b/kernel/src/arch/x86_64/mod.rs @@ -16,7 +16,7 @@ use kernel_arch_x86_64::{ table::{PageAttributes, PageEntry, PageTable, L1, L2, L3}, EarlyMapping, MEMORY_LIMIT, RAM_MAPPING_L1, RAM_MAPPING_OFFSET, }, - PerCpuData, + LocalApicInterface, PerCpuData, }; use libk::{ arch::Cpu, @@ -471,6 +471,10 @@ impl X86_64 { Ok(()) } + + pub(crate) fn cpu_local_apic() -> Arc { + Cpu::local().local_apic.clone() + } } impl InitrdSource for LoadProtocolV1 {