mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-20 13:06:33 +00:00
Support TSC-Deadline mode APIC timer
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
d1c0090d3a
commit
f0f498e46a
@ -6,3 +6,4 @@
|
|||||||
pub mod acpi;
|
pub mod acpi;
|
||||||
pub mod apic;
|
pub mod apic;
|
||||||
pub mod pic;
|
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 core::sync::atomic::{AtomicBool, Ordering};
|
||||||
use log::info;
|
|
||||||
use trapframe::TrapFrame;
|
|
||||||
|
|
||||||
use crate::arch::x86::kernel::{
|
use alloc::boxed::Box;
|
||||||
apic::{DivideConfig, APIC_INSTANCE},
|
use alloc::sync::Arc;
|
||||||
pic,
|
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() {
|
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 mut apic_lock = APIC_INSTANCE.get().unwrap().lock();
|
||||||
let handle = unsafe { crate::trap::IrqLine::acquire(super::TIMER_IRQ_NUM) };
|
let handle = unsafe { crate::trap::IrqLine::acquire(super::TIMER_IRQ_NUM) };
|
||||||
let a = handle.on_active(init_function);
|
let a = handle.on_active(init_function);
|
||||||
@ -58,9 +111,7 @@ pub fn init() {
|
|||||||
|
|
||||||
info!(
|
info!(
|
||||||
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
|
"APIC Timer ticks count:{:x}, remain ticks: {:x},Timer Freq:{} Hz",
|
||||||
ticks,
|
ticks, remain_ticks, TIMER_FREQ
|
||||||
remain_ticks,
|
|
||||||
config::TIMER_FREQ
|
|
||||||
);
|
);
|
||||||
IS_FINISH.store(true, Ordering::Release);
|
IS_FINISH.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,8 @@ use crate::arch::x86::kernel;
|
|||||||
use crate::config::TIMER_FREQ;
|
use crate::config::TIMER_FREQ;
|
||||||
use crate::trap::IrqAllocateHandle;
|
use crate::trap::IrqAllocateHandle;
|
||||||
|
|
||||||
|
use self::apic::APIC_TIMER_CALLBACK;
|
||||||
|
|
||||||
pub const TIMER_IRQ_NUM: u8 = 32;
|
pub const TIMER_IRQ_NUM: u8 = 32;
|
||||||
pub static TICK: AtomicU64 = AtomicU64::new(0);
|
pub static TICK: AtomicU64 = AtomicU64::new(0);
|
||||||
|
|
||||||
@ -46,6 +48,9 @@ fn timer_callback(trap_frame: &TrapFrame) {
|
|||||||
for callback in callbacks {
|
for callback in callbacks {
|
||||||
callback.callback.call((&callback,));
|
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();
|
static TIMEOUT_LIST: Once<Mutex<BinaryHeap<Arc<TimerCallback>>>> = Once::new();
|
||||||
|
Reference in New Issue
Block a user