Skip to content

Commit 2366242

Browse files
committed
refactor(time): 重构计时器校准逻辑并提取通用 PIT 校准工具
- 在 libs/time 中实现 calibrate_with_pit 通用校准函数 - 将 APIC 计时器校准逻辑从中断模块迁移至 libs/time/apic - 使用通用工具简化 TSC 初始化流程 - 修正 scripts/install.sh 权限并包含新模块文件
1 parent 3004553 commit 2366242

File tree

5 files changed

+89
-81
lines changed

5 files changed

+89
-81
lines changed

kernel/src/interrupts/apic/mod.rs

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::interrupts::pic;
33
use crate::libs::msr;
44
use crate::memory::protection;
55
use crate::{get_hhdm_offset, libs::acpi::ACPI_INFO};
6-
use log::{debug, info};
6+
use log::debug;
77
use raw_cpuid::CpuId;
88
use spin::Mutex;
99
use x86_64::registers::model_specific::Msr;
@@ -18,10 +18,6 @@ const APIC_BASE_X2APIC_ENABLE: u64 = 1 << 10;
1818
// MMIO Offsets for xAPIC
1919
const XAPIC_EOI_OFFSET: u32 = 0x0B0;
2020
const XAPIC_SIVR_OFFSET: u32 = 0x0F0;
21-
const XAPIC_LVT_TIMER_OFFSET: u32 = 0x320;
22-
const XAPIC_TIMER_INIT_COUNT_OFFSET: u32 = 0x380;
23-
const XAPIC_TIMER_CUR_COUNT_OFFSET: u32 = 0x390;
24-
const XAPIC_TIMER_DIV_CONF_OFFSET: u32 = 0x3E0;
2521

2622
pub const TIMER_VECTOR: u8 = 0x30; // APIC Timer interrupt vector
2723

@@ -146,37 +142,12 @@ impl LocalApic {
146142
debug!("Local APIC initialized in {:?} mode", self.mode);
147143
}
148144

149-
// TODO: move to libs/time
150145
pub unsafe fn calibrate_timer(&self) {
151-
// Stop timer
152-
self.write_reg(XAPIC_TIMER_INIT_COUNT_OFFSET, 0);
153-
// Set divider to 16
154-
self.write_reg(XAPIC_TIMER_DIV_CONF_OFFSET, 0x3);
155-
156-
let mut pit = crate::libs::time::pit::PIT.lock();
157-
158-
// Start PIT one-shot for 10ms (10000 us)
159-
// PIT freq is 1.193182 MHz. 10ms = 11932 ticks
160-
let pit_ticks = 11932;
161-
pit.start_one_shot(pit_ticks);
162-
163-
// Set APIC timer to max
164-
self.write_reg(XAPIC_TIMER_INIT_COUNT_OFFSET, 0xFFFFFFFF);
165-
166-
// Wait for PIT
167-
while (x86_64::instructions::port::Port::<u8>::new(0x61).read() & 0x20) == 0 {
168-
core::hint::spin_loop();
169-
}
170-
171-
// Stop APIC timer
172-
let current_count = self.read_reg(XAPIC_TIMER_CUR_COUNT_OFFSET);
173-
let ticks_per_10ms = 0xFFFFFFFF - current_count;
174-
175-
info!("APIC Timer calibrated: {} ticks per 10ms", ticks_per_10ms);
176-
177-
// Set timer for periodic interrupt at 100Hz (10ms)
178-
self.write_reg(XAPIC_LVT_TIMER_OFFSET, 0x20000 | TIMER_VECTOR as u32); // Periodic mode
179-
self.write_reg(XAPIC_TIMER_INIT_COUNT_OFFSET, ticks_per_10ms);
146+
crate::libs::time::apic::calibrate_timer(
147+
|reg| self.read_reg(reg),
148+
|reg, val| self.write_reg(reg, val),
149+
TIMER_VECTOR,
150+
);
180151
}
181152
}
182153

kernel/src/libs/time/apic.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use log::info;
2+
3+
pub const XAPIC_LVT_TIMER_OFFSET: u32 = 0x320;
4+
pub const XAPIC_TIMER_INIT_COUNT_OFFSET: u32 = 0x380;
5+
pub const XAPIC_TIMER_CUR_COUNT_OFFSET: u32 = 0x390;
6+
pub const XAPIC_TIMER_DIV_CONF_OFFSET: u32 = 0x3E0;
7+
8+
/// Calibrate the APIC timer using PIT.
9+
///
10+
/// # Arguments
11+
/// * `read_reg` - A closure to read an APIC register.
12+
/// * `write_reg` - A closure to write an APIC register.
13+
/// * `timer_vector` - The interrupt vector to use for the timer.
14+
///
15+
/// # Returns
16+
/// The number of ticks per 10ms.
17+
pub unsafe fn calibrate_timer<F1, F2>(read_reg: F1, write_reg: F2, timer_vector: u8) -> u64
18+
where
19+
F1: Fn(u32) -> u32,
20+
F2: Fn(u32, u32),
21+
{
22+
// Stop timer
23+
write_reg(XAPIC_TIMER_INIT_COUNT_OFFSET, 0);
24+
// Set divider to 16
25+
write_reg(XAPIC_TIMER_DIV_CONF_OFFSET, 0x3);
26+
27+
let (ticks_per_10ms, _) = super::calibrate_with_pit(10000, || {
28+
// Set APIC timer to max
29+
write_reg(XAPIC_TIMER_INIT_COUNT_OFFSET, 0xFFFFFFFF);
30+
move || {
31+
let current_count = read_reg(XAPIC_TIMER_CUR_COUNT_OFFSET);
32+
0xFFFFFFFF - current_count as u64
33+
}
34+
});
35+
36+
info!("APIC Timer calibrated: {} ticks per 10ms", ticks_per_10ms);
37+
38+
// Set timer for periodic interrupt at 100Hz (10ms)
39+
write_reg(XAPIC_LVT_TIMER_OFFSET, 0x20000 | timer_vector as u32); // Periodic mode
40+
write_reg(XAPIC_TIMER_INIT_COUNT_OFFSET, ticks_per_10ms as u32);
41+
42+
ticks_per_10ms
43+
}

kernel/src/libs/time/mod.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,37 @@
1+
pub mod apic;
12
pub mod pit;
23
pub mod tsc;
34

45
pub use tsc::{init, sleep_us, time_since_boot};
6+
7+
/// Calibrate a timer using the PIT (Programmable Interval Timer).
8+
///
9+
/// # Arguments
10+
/// * `us` - The duration to wait for in microseconds.
11+
/// * `f` - A closure that starts the timer and returns another closure to stop it.
12+
///
13+
/// # Returns
14+
/// A tuple containing (measured_value, actual_pit_ticks).
15+
pub fn calibrate_with_pit<F, R>(us: u64, f: F) -> (u64, u64)
16+
where
17+
F: FnOnce() -> R,
18+
R: FnOnce() -> u64,
19+
{
20+
let mut pit = pit::PIT.lock();
21+
// PIT freq is 1.193182 MHz
22+
let ticks = (us * 1_193_182) / 1_000_000;
23+
24+
x86_64::instructions::interrupts::without_interrupts(|| {
25+
pit.start_one_shot(ticks as u16);
26+
let stop_fn = f();
27+
28+
// Wait for PIT to finish (Port 0x61, Bit 5 goes HIGH)
29+
unsafe {
30+
while (x86_64::instructions::port::Port::<u8>::new(0x61).read() & 0x20) == 0 {
31+
core::hint::spin_loop();
32+
}
33+
}
34+
35+
(stop_fn(), ticks as u64)
36+
})
37+
}

kernel/src/libs/time/tsc.rs

Lines changed: 7 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,60 +6,21 @@ static TSC_FREQUENCY: AtomicU64 = AtomicU64::new(0);
66

77
/// Initialize TSC by calibrating it against the PIT
88
pub fn init() {
9-
let mut pit = PIT.lock();
10-
119
// Check if TSC is supported? (Assuming yes for x86_64)
1210

13-
// We will wait for approximately 50ms worth of PIT ticks
14-
// 1193182 Hz / 1000 * 50 ~= 59659 ticks
15-
// This fits in u16 (65535)
16-
const PIT_FREQ: u64 = 1_193_182;
1711
const CAL_MS: u64 = 50;
18-
const TARGET_TICKS: u16 = (PIT_FREQ * CAL_MS / 1000) as u16;
19-
20-
// Run calibration with interrupts disabled to minimize jitter
21-
let freq = x86_64::instructions::interrupts::without_interrupts(|| {
22-
// Setup PIT Ch2 in Mode 0 with max count
23-
// We start from 0xFFFF (65535) and count down
24-
pit.start_one_shot(0xFFFF);
12+
const PIT_FREQ: u64 = 1_193_182;
2513

26-
// Wait for a short while to ensure counting started and stable
27-
// (Just reading it once is usually enough to settle?)
28-
let start_pit = pit.read_count();
14+
let (tsc_delta, pit_ticks) = super::calibrate_with_pit(CAL_MS * 1000, || {
2915
let start_tsc = unsafe { _rdtsc() };
30-
31-
let mut end_pit;
32-
let mut end_tsc;
33-
34-
loop {
35-
end_pit = pit.read_count();
36-
end_tsc = unsafe { _rdtsc() };
37-
38-
// Check delta (handling potential wrap if any, though Mode 0 shouldn't)
39-
// Since we count down: delta = start - end
40-
if start_pit >= end_pit {
41-
let delta = start_pit - end_pit;
42-
if delta >= TARGET_TICKS {
43-
break;
44-
}
45-
} else {
46-
// If end > start, something weird happened (reload? or wrap?)
47-
// Just break to avoid infinite loop
48-
break;
49-
}
16+
move || {
17+
let end_tsc = unsafe { _rdtsc() };
18+
end_tsc - start_tsc
5019
}
51-
52-
let pit_delta = (start_pit - end_pit) as u64;
53-
let tsc_delta = end_tsc - start_tsc;
54-
55-
// Calculate Frequency
56-
// freq = tsc_delta / time
57-
// time = pit_delta / PIT_FREQ
58-
// freq = tsc_delta * PIT_FREQ / pit_delta
59-
60-
(tsc_delta * PIT_FREQ).checked_div(pit_delta).unwrap_or(0)
6120
});
6221

22+
let freq = (tsc_delta * PIT_FREQ).checked_div(pit_ticks).unwrap_or(0);
23+
6324
TSC_FREQUENCY.store(freq, Ordering::Relaxed);
6425
}
6526

scripts/install.sh

100644100755
File mode changed.

0 commit comments

Comments
 (0)