From ffb40974364c74ee3adaffdf8a7063b575233807 Mon Sep 17 00:00:00 2001 From: Zejun Zhao Date: Mon, 28 Apr 2025 11:04:29 +0800 Subject: [PATCH] Add RISC-V timer support --- kernel/comps/time/src/rtc/goldfish.rs | 30 ++++-- ostd/src/arch/riscv/cpu/context.rs | 10 +- ostd/src/arch/riscv/mod.rs | 9 +- ostd/src/arch/riscv/timer/mod.rs | 147 +++++++++++++++++++------- ostd/src/arch/riscv/trap/mod.rs | 16 ++- 5 files changed, 159 insertions(+), 53 deletions(-) diff --git a/kernel/comps/time/src/rtc/goldfish.rs b/kernel/comps/time/src/rtc/goldfish.rs index 189240b6..3cbd6d3d 100644 --- a/kernel/comps/time/src/rtc/goldfish.rs +++ b/kernel/comps/time/src/rtc/goldfish.rs @@ -1,28 +1,40 @@ // SPDX-License-Identifier: MPL-2.0 -use ostd::{arch::timer::GOLDFISH_IO_MEM, mm::VmIoOnce}; +use ostd::{arch::boot::DEVICE_TREE, io::IoMem, mm::VmIoOnce}; use chrono::{DateTime, Datelike, Timelike}; use crate::{SystemTime, rtc::Driver}; -pub struct RtcGoldfish; +pub struct RtcGoldfish { + io_mem: IoMem, +} impl Driver for RtcGoldfish { fn try_new() -> Option { - GOLDFISH_IO_MEM.get()?; - Some(RtcGoldfish) + let chosen = DEVICE_TREE.get().unwrap().find_node("/soc/rtc").unwrap(); + if let Some(compatible) = chosen.compatible() + && compatible.all().any(|c| c == "google,goldfish-rtc") + { + let region = chosen.reg().unwrap().next().unwrap(); + let io_mem = IoMem::acquire( + region.starting_address as usize + ..region.starting_address as usize + region.size.unwrap(), + ) + .unwrap(); + Some(RtcGoldfish { io_mem }) + } else { + None + } } fn read_rtc(&self) -> SystemTime { const TIME_LOW: usize = 0; const TIME_HIGH: usize = 4; - let io_mem = GOLDFISH_IO_MEM.get().unwrap(); - - let mut last_time_high = io_mem.read_once(TIME_HIGH).unwrap(); + let mut last_time_high = self.io_mem.read_once(TIME_HIGH).unwrap(); let timestamp = loop { - let time_low: u32 = io_mem.read_once(TIME_LOW).unwrap(); - let time_high: u32 = io_mem.read_once(TIME_HIGH).unwrap(); + let time_low: u32 = self.io_mem.read_once(TIME_LOW).unwrap(); + let time_high: u32 = self.io_mem.read_once(TIME_HIGH).unwrap(); if last_time_high == time_high { break ((time_high as u64) << 32) | time_low as u64; } diff --git a/ostd/src/arch/riscv/cpu/context.rs b/ostd/src/arch/riscv/cpu/context.rs index 771f8be8..2555b73f 100644 --- a/ostd/src/arch/riscv/cpu/context.rs +++ b/ostd/src/arch/riscv/cpu/context.rs @@ -4,11 +4,14 @@ use core::fmt::Debug; -use riscv::register::scause::{Exception, Trap}; +use riscv::register::scause::{Exception, Interrupt, Trap}; pub use crate::arch::trap::GeneralRegs as RawGeneralRegs; use crate::{ - arch::trap::{TrapFrame, UserContext as RawUserContext}, + arch::{ + timer::handle_timer_interrupt, + trap::{TrapFrame, UserContext as RawUserContext}, + }, user::{ReturnReason, UserContextApi, UserContextApiInternal}, }; @@ -111,6 +114,9 @@ impl UserContextApiInternal for UserContext { let ret = loop { self.user_context.run(); match riscv::register::scause::read().cause() { + Trap::Interrupt(Interrupt::SupervisorTimer) => { + handle_timer_interrupt(); + } Trap::Interrupt(_) => todo!(), Trap::Exception(Exception::UserEnvCall) => { self.user_context.sepc += 4; diff --git a/ostd/src/arch/riscv/mod.rs b/ostd/src/arch/riscv/mod.rs index 7d39b4b5..99108c21 100644 --- a/ostd/src/arch/riscv/mod.rs +++ b/ostd/src/arch/riscv/mod.rs @@ -15,8 +15,6 @@ pub mod task; pub mod timer; pub mod trap; -use core::sync::atomic::Ordering; - #[cfg(feature = "cvm_guest")] pub(crate) fn init_cvm_guest() { // Unimplemented, no-op @@ -29,7 +27,10 @@ pub(crate) unsafe fn late_init_on_bsp() { // SAFETY: We're on the BSP and we're ready to boot all APs. unsafe { crate::boot::smp::boot_all_aps() }; - timer::init(); + // SAFETY: This function is called once and at most once at a proper timing + // in the boot context of the BSP, with no timer-related operations having + // been performed. + unsafe { timer::init() }; let _ = pci::init(); } @@ -43,7 +44,7 @@ pub(crate) fn interrupts_ack(irq_number: usize) { /// Return the frequency of TSC. The unit is Hz. pub fn tsc_freq() -> u64 { - timer::TIMEBASE_FREQ.load(Ordering::Relaxed) + timer::get_timebase_freq() } /// Reads the current value of the processor’s time-stamp counter (TSC). diff --git a/ostd/src/arch/riscv/timer/mod.rs b/ostd/src/arch/riscv/timer/mod.rs index 00ac7ec0..1bbcfe7d 100644 --- a/ostd/src/arch/riscv/timer/mod.rs +++ b/ostd/src/arch/riscv/timer/mod.rs @@ -2,48 +2,123 @@ //! The timer support. -use core::sync::atomic::{AtomicU64, Ordering}; +use core::{ + arch::asm, + sync::atomic::{AtomicU64, Ordering}, +}; -use spin::Once; +use crate::{ + arch::boot::DEVICE_TREE, + cpu::{CpuId, PinCurrentCpu}, + timer::INTERRUPT_CALLBACKS, + trap, +}; -use crate::{arch::boot::DEVICE_TREE, io::IoMem}; - -/// The timer frequency (Hz). Here we choose 1000Hz since 1000Hz is easier for unit conversion and -/// convenient for timer. What's more, the frequency cannot be set too high or too low, 1000Hz is -/// a modest choice. +/// The timer frequency (Hz). Here we choose 1000Hz since 1000Hz is easier for +/// unit conversion and convenient for timer. What's more, the frequency cannot +/// be set too high or too low, 1000Hz is a modest choice. /// -/// For system performance reasons, this rate cannot be set too high, otherwise most of the time -/// is spent executing timer code. +/// For system performance reasons, this rate cannot be set too high, otherwise +/// most of the time is spent executing timer code. pub const TIMER_FREQ: u64 = 1000; -pub(crate) static TIMEBASE_FREQ: AtomicU64 = AtomicU64::new(1); +static TIMEBASE_FREQ: AtomicU64 = AtomicU64::new(0); +static TIMER_INTERVAL: AtomicU64 = AtomicU64::new(0); -/// [`IoMem`] of goldfish RTC, which will be used by `aster-time`. -pub static GOLDFISH_IO_MEM: Once = Once::new(); +/// Initializes the timer module. +/// +/// # Safety +/// +/// This function is safe to call on the following conditions: +/// 1. It is called once and at most once at a proper timing in the boot context. +/// 2. It is called before any other public functions of this module is called. +pub(super) unsafe fn init() { + TIMEBASE_FREQ.store( + DEVICE_TREE + .get() + .unwrap() + .cpus() + .next() + .unwrap() + .timebase_frequency() as u64, + Ordering::Relaxed, + ); + TIMER_INTERVAL.store( + TIMEBASE_FREQ.load(Ordering::Relaxed) / TIMER_FREQ, + Ordering::Relaxed, + ); -pub(super) fn init() { - let timer_freq = DEVICE_TREE - .get() - .unwrap() - .cpus() - .next() - .unwrap() - .timebase_frequency() as u64; - TIMEBASE_FREQ.store(timer_freq, Ordering::Relaxed); - - let chosen = DEVICE_TREE.get().unwrap().find_node("/soc/rtc").unwrap(); - if let Some(compatible) = chosen.compatible() - && compatible.all().any(|c| c == "google,goldfish-rtc") - { - let region = chosen.reg().unwrap().next().unwrap(); - let io_mem = unsafe { - IoMem::new( - (region.starting_address as usize) - ..(region.starting_address as usize) + region.size.unwrap(), - crate::mm::page_prop::PageFlags::RW, - crate::mm::page_prop::CachePolicy::Uncacheable, - ) - }; - GOLDFISH_IO_MEM.call_once(|| io_mem); + if is_sstc_enabled() { + // SAFETY: Mutating the static variable `SET_NEXT_TIMER_FN` is safe here + // because we ensure that it is only modified during the initialization + // phase of the timer. + unsafe { + SET_NEXT_TIMER_FN = set_next_timer_sstc; + } + } + set_next_timer(); + // SAFETY: Accessing the `sie` CSR to enable the timer interrupt is safe + // here because this function is only called during timer initialization, + // and we ensure that only the timer interrupt bit is set without affecting + // other interrupt sources. + unsafe { + riscv::register::sie::set_stimer(); } } + +pub(super) fn handle_timer_interrupt() { + let irq_guard = trap::disable_local(); + if irq_guard.current_cpu() == CpuId::bsp() { + crate::timer::jiffies::ELAPSED.fetch_add(1, Ordering::Relaxed); + } + + let callbacks_guard = INTERRUPT_CALLBACKS.get_with(&irq_guard); + for callback in callbacks_guard.borrow().iter() { + (callback)(); + } + drop(callbacks_guard); + + set_next_timer(); +} + +fn set_next_timer() { + // SAFETY: Calling the `SET_NEXT_TIMER_FN` function pointer is safe here + // because we ensure that it is set to a valid function during the timer + // initialization, and we never modify it after that. + unsafe { + SET_NEXT_TIMER_FN(); + } +} + +static mut SET_NEXT_TIMER_FN: fn() = set_next_timer_sbi; + +fn set_next_timer_sbi() { + sbi_rt::set_timer(TIMER_INTERVAL.load(Ordering::Relaxed)); +} + +fn set_next_timer_sstc() { + // SAFETY: Setting the next timer using the `stimecmp` CSR is safe here + // because we are using the `stimecmp` CSR to set the next timer interrupt + // only when we're handling a timer interrupt, which is a standard operation + // specified by RISC-V SSTC extension. + unsafe { + asm!("csrrw {}, stimecmp, {}", out(reg) _, in(reg) get_next_when()); + } +} + +fn is_sstc_enabled() -> bool { + let Some(misa) = riscv::register::misa::read() else { + return false; + }; + misa.has_extension('S') +} + +fn get_next_when() -> u64 { + let current = riscv::register::time::read64(); + let interval = TIMER_INTERVAL.load(Ordering::Relaxed); + current + interval +} + +pub(crate) fn get_timebase_freq() -> u64 { + TIMEBASE_FREQ.load(Ordering::Relaxed) +} diff --git a/ostd/src/arch/riscv/trap/mod.rs b/ostd/src/arch/riscv/trap/mod.rs index 9bec90ca..0cf4b519 100644 --- a/ostd/src/arch/riscv/trap/mod.rs +++ b/ostd/src/arch/riscv/trap/mod.rs @@ -4,6 +4,7 @@ mod trap; +use riscv::register::scause::Interrupt; use spin::Once; pub use trap::{GeneralRegs, TrapFrame, UserContext}; @@ -32,9 +33,20 @@ extern "C" fn trap_handler(f: &mut TrapFrame) { use riscv::register::scause::Trap; match riscv::register::scause::read().cause() { - Trap::Interrupt(_) => { + Trap::Interrupt(interrupt) => { IS_KERNEL_INTERRUPTED.store(true); - todo!(); + match interrupt { + Interrupt::SupervisorTimer => { + crate::arch::timer::handle_timer_interrupt(); + } + Interrupt::SupervisorExternal => todo!(), + Interrupt::SupervisorSoft => todo!(), + _ => { + panic!( + "cannot handle unknown supervisor interrupt: {interrupt:?}. trapframe: {f:#x?}.", + ); + } + } IS_KERNEL_INTERRUPTED.store(false); } Trap::Exception(e) => {