mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-08 12:56:48 +00:00
Add RISC-V timer support
This commit is contained in:
parent
63daf69e17
commit
ffb4097436
@ -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<RtcGoldfish> {
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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).
|
||||
|
@ -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<IoMem> = Once::new();
|
||||
|
||||
pub(super) fn init() {
|
||||
let timer_freq = DEVICE_TREE
|
||||
/// 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;
|
||||
TIMEBASE_FREQ.store(timer_freq, Ordering::Relaxed);
|
||||
.timebase_frequency() as u64,
|
||||
Ordering::Relaxed,
|
||||
);
|
||||
TIMER_INTERVAL.store(
|
||||
TIMEBASE_FREQ.load(Ordering::Relaxed) / 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)
|
||||
}
|
||||
|
@ -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) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user