mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-15 16:26:48 +00:00
Support TSC-Deadline mode APIC timer
This commit is contained in:
parent
d1c0090d3a
commit
f0f498e46a
@ -6,3 +6,4 @@
|
||||
pub mod acpi;
|
||||
pub mod apic;
|
||||
pub mod pic;
|
||||
pub mod tsc;
|
||||
|
40
framework/jinux-frame/src/arch/x86/kernel/tsc.rs
Normal file
40
framework/jinux-frame/src/arch/x86/kernel/tsc.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use x86::cpuid::cpuid;
|
||||
|
||||
/// Determine TSC frequency via CPUID. If the CPU does not support calculating TSC frequency by
|
||||
/// CPUID, the function will return None. The unit of the return value is KHz.
|
||||
///
|
||||
/// Ref: function `native_calibrate_tsc` in linux `arch/x86/kernel/tsc.c`
|
||||
///
|
||||
pub fn tsc_freq() -> Option<u32> {
|
||||
// Check the max cpuid supported
|
||||
let cpuid = cpuid!(0);
|
||||
let max_cpuid = cpuid.eax;
|
||||
if max_cpuid <= 0x15 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// TSC frequecny = ecx * ebx / eax
|
||||
// CPUID 0x15: Time Stamp Counter and Nominal Core Crystal Clock Information Leaf
|
||||
let mut cpuid = cpuid!(0x15);
|
||||
if cpuid.eax == 0 || cpuid.ebx == 0 {
|
||||
return None;
|
||||
}
|
||||
let eax_denominator = cpuid.eax;
|
||||
let ebx_numerator = cpuid.ebx;
|
||||
let mut crystal_khz = cpuid.ecx / 1000;
|
||||
|
||||
// Some Intel SoCs like Skylake and Kabylake don't report the crystal
|
||||
// clock, but we can easily calculate it to a high degree of accuracy
|
||||
// by considering the crystal ratio and the CPU speed.
|
||||
if crystal_khz == 0 && max_cpuid >= 0x16 {
|
||||
cpuid = cpuid!(0x16);
|
||||
let base_mhz = cpuid.eax;
|
||||
crystal_khz = base_mhz * 1000 * eax_denominator / ebx_numerator;
|
||||
}
|
||||
|
||||
if crystal_khz == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(crystal_khz * ebx_numerator / eax_denominator)
|
||||
}
|
||||
}
|
@ -1,14 +1,67 @@
|
||||
use core::arch::x86_64::_rdtsc;
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use log::info;
|
||||
use trapframe::TrapFrame;
|
||||
|
||||
use crate::arch::x86::kernel::{
|
||||
apic::{DivideConfig, APIC_INSTANCE},
|
||||
pic,
|
||||
use alloc::boxed::Box;
|
||||
use alloc::sync::Arc;
|
||||
use log::info;
|
||||
use spin::Once;
|
||||
use trapframe::TrapFrame;
|
||||
use x86::cpuid::cpuid;
|
||||
use x86::msr::{wrmsr, IA32_TSC_DEADLINE};
|
||||
|
||||
use crate::{
|
||||
arch::x86::kernel::{
|
||||
apic::{DivideConfig, APIC_INSTANCE},
|
||||
pic,
|
||||
tsc::tsc_freq,
|
||||
},
|
||||
config::TIMER_FREQ,
|
||||
};
|
||||
use crate::config;
|
||||
|
||||
pub fn init() {
|
||||
if tsc_mode_support() {
|
||||
info!("APIC Timer: Enable TSC deadline mode.");
|
||||
tsc_mode_init();
|
||||
} else {
|
||||
info!("APIC Timer: Enable periodic mode.");
|
||||
periodic_mode_init();
|
||||
}
|
||||
}
|
||||
|
||||
fn tsc_mode_support() -> bool {
|
||||
let tsc_rate = tsc_freq();
|
||||
if tsc_rate.is_none() {
|
||||
return false;
|
||||
}
|
||||
let cpuid = cpuid!(0x1);
|
||||
// bit 24
|
||||
cpuid.ecx & 0x100_0000 != 0
|
||||
}
|
||||
|
||||
pub(super) static APIC_TIMER_CALLBACK: Once<Arc<dyn Fn() + Sync + Send>> = Once::new();
|
||||
|
||||
fn tsc_mode_init() {
|
||||
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
|
||||
// Enable tsc deadline mode.
|
||||
apic_lock.set_lvt_timer(super::TIMER_IRQ_NUM as u64 | (1 << 18));
|
||||
|
||||
let tsc_step = {
|
||||
let tsc_rate = tsc_freq().unwrap() as u64;
|
||||
info!("TSC frequency:{:?} Hz", tsc_rate * 1000);
|
||||
tsc_rate * 1000 / TIMER_FREQ
|
||||
};
|
||||
|
||||
let callback = move || unsafe {
|
||||
let tsc_value = _rdtsc();
|
||||
let next_tsc_value = tsc_step + tsc_value;
|
||||
wrmsr(IA32_TSC_DEADLINE, next_tsc_value);
|
||||
};
|
||||
|
||||
callback.call(());
|
||||
APIC_TIMER_CALLBACK.call_once(|| Arc::new(callback));
|
||||
}
|
||||
|
||||
fn periodic_mode_init() {
|
||||
let mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
|
||||
let handle = unsafe { crate::trap::IrqLine::acquire(super::TIMER_IRQ_NUM) };
|
||||
let a = handle.on_active(init_function);
|
||||
@ -58,9 +111,7 @@ pub fn init() {
|
||||
|
||||
info!(
|
||||
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
|
||||
ticks,
|
||||
remain_ticks,
|
||||
config::TIMER_FREQ
|
||||
ticks, remain_ticks, TIMER_FREQ
|
||||
);
|
||||
IS_FINISH.store(true, Ordering::Release);
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ use crate::arch::x86::kernel;
|
||||
use crate::config::TIMER_FREQ;
|
||||
use crate::trap::IrqAllocateHandle;
|
||||
|
||||
use self::apic::APIC_TIMER_CALLBACK;
|
||||
|
||||
pub const TIMER_IRQ_NUM: u8 = 32;
|
||||
pub static TICK: AtomicU64 = AtomicU64::new(0);
|
||||
|
||||
@ -46,6 +48,9 @@ fn timer_callback(trap_frame: &TrapFrame) {
|
||||
for callback in callbacks {
|
||||
callback.callback.call((&callback,));
|
||||
}
|
||||
if APIC_TIMER_CALLBACK.is_completed() {
|
||||
APIC_TIMER_CALLBACK.get().unwrap().call(());
|
||||
}
|
||||
}
|
||||
|
||||
static TIMEOUT_LIST: Once<Mutex<BinaryHeap<Arc<TimerCallback>>>> = Once::new();
|
||||
|
Loading…
x
Reference in New Issue
Block a user