1 use core::{ 2 cmp::{max, min}, 3 intrinsics::unlikely, 4 }; 5 6 use crate::{ 7 arch::{io::PortIOArch, CurrentIrqArch, CurrentPortIOArch, CurrentTimeArch}, 8 driver::acpi::pmtmr::{ACPI_PM_OVERRUN, PMTMR_TICKS_PER_SEC}, 9 exception::InterruptArch, 10 kdebug, kerror, kinfo, kwarn, 11 syscall::SystemError, 12 time::TimeArch, 13 }; 14 15 use super::hpet::hpet_instance; 16 17 /// The clock frequency of the i8253/i8254 PIT 18 const PIT_TICK_RATE: u64 = 1193182; 19 20 #[derive(Debug)] 21 pub struct TSCManager; 22 23 static mut TSC_KHZ: u64 = 0; 24 static mut CPU_KHZ: u64 = 0; 25 26 impl TSCManager { 27 const DEFAULT_THRESHOLD: u64 = 0x20000; 28 29 /// 初始化TSC 30 /// 31 /// 目前由于未支持acpi pm timer, 因此调用该函数时,HPET应当完成初始化,否则将无法校准TSC 32 /// 33 /// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#1511 34 pub fn init() -> Result<(), SystemError> { 35 let cpuid = x86::cpuid::CpuId::new(); 36 let feat = cpuid.get_feature_info().ok_or(SystemError::ENODEV)?; 37 if !feat.has_tsc() { 38 kerror!("TSC is not available"); 39 return Err(SystemError::ENODEV); 40 } 41 42 if unsafe { TSC_KHZ == 0 } { 43 if let Err(e) = Self::determine_cpu_tsc_frequency(false) { 44 kerror!("Failed to determine CPU TSC frequency: {:?}", e); 45 // todo: mark TSC as unstable clock source 46 return Err(e); 47 } 48 } 49 50 // todo: register TSC as clock source and deal with unstable clock source 51 52 return Ok(()); 53 } 54 55 /// 获取TSC和CPU总线的频率 56 /// 57 /// ## 参数 58 /// 59 /// - `early`:是否在早期初始化 60 /// 61 /// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#1438 62 fn determine_cpu_tsc_frequency(early: bool) -> Result<(), SystemError> { 63 if unlikely(Self::cpu_khz() != 0 || Self::tsc_khz() != 0) { 64 kwarn!("TSC and CPU frequency already determined"); 65 } 66 67 if early { 68 // todo: 先根据cpuid或者读取msr或者pit来测量TSC和CPU总线的频率 69 todo!("detect TSC and CPU frequency by cpuid or msr or pit"); 70 } else { 71 // 使用pit来测量TSC和CPU总线的频率 72 Self::set_cpu_khz(Self::calibrate_cpu_by_pit_hpet_ptimer()?); 73 } 74 75 // 认为非0的TSC频率是可靠的,并且使用它来检查CPU总线的频率 76 if Self::tsc_khz() == 0 { 77 Self::set_tsc_khz(Self::cpu_khz()); 78 } else if (Self::cpu_khz() as i64 - Self::tsc_khz() as i64).abs() * 10 79 > Self::cpu_khz() as i64 80 { 81 // 如果TSC和CPU总线的频率相差太大,那么认为CPU总线的频率是不可靠的,使用TSC的频率 82 Self::set_cpu_khz(Self::tsc_khz()); 83 } 84 85 if Self::cpu_khz() == 0 { 86 kerror!("Failed to determine CPU frequency"); 87 return Err(SystemError::ENODEV); 88 } 89 90 kinfo!( 91 "Detected {}.{} MHz processor", 92 Self::cpu_khz() / 1000, 93 Self::cpu_khz() % 1000 94 ); 95 kinfo!( 96 "Detected {}.{} MHz TSC", 97 Self::tsc_khz() / 1000, 98 Self::tsc_khz() % 1000 99 ); 100 101 return Ok(()); 102 } 103 104 /// 测量CPU总线的频率 105 /// 106 /// 使用pit、hpet、ptimer来测量CPU总线的频率 107 fn calibrate_cpu_by_pit_hpet_ptimer() -> Result<u64, SystemError> { 108 let hpet = hpet_instance().enabled(); 109 kdebug!( 110 "Calibrating TSC with {}", 111 if hpet { "HPET" } else { "PMTIMER" } 112 ); 113 114 let mut tsc_pit_min = u64::MAX; 115 let mut tsc_ref_min = u64::MAX; 116 117 // 默认的校准参数 118 let cal_ms = 10; 119 let cal_latch = PIT_TICK_RATE / (1000 / cal_ms); 120 let cal_pit_loops = 1000; 121 122 // 如果第一轮校准失败,那么使用这些参数(因为虚拟化平台的问题,第一轮校准可能失败) 123 let cal2_ms = 50; 124 let cal2_latch = PIT_TICK_RATE / (1000 / cal2_ms); 125 let cal2_pit_loops = 5000; 126 127 let mut latch = cal_latch; 128 let mut loopmin = cal_pit_loops; 129 let mut ms = cal_ms; 130 131 let mut global_ref1 = 0; 132 let mut global_ref2 = 0; 133 134 for i in 0..3 { 135 let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() }; 136 137 let (tsc1, ref1) = Self::read_refs(hpet); 138 let tsc_pit_khz = Self::pit_calibrate_tsc(latch, ms, loopmin).unwrap_or(u64::MAX); 139 let (tsc2, ref2) = Self::read_refs(hpet); 140 drop(irq_guard); 141 142 global_ref1 = ref1; 143 global_ref2 = ref2; 144 145 // 选用最小的tsc_pit_khz 146 tsc_pit_min = min(tsc_pit_min, tsc_pit_khz); 147 148 // HPET或者PTIMER可能是不可用的 149 if ref1 == ref2 { 150 kdebug!("HPET/PMTIMER not available"); 151 continue; 152 } 153 154 // 检查采样是否被打断 155 if tsc1 == u64::MAX || tsc2 == u64::MAX { 156 continue; 157 } 158 159 let mut tsc2 = (tsc2 - tsc1) * 1000000; 160 161 if hpet { 162 tsc2 = Self::calc_hpet_ref(tsc2, ref1, ref2); 163 } else { 164 tsc2 = Self::calc_pmtimer_ref(tsc2, ref1, ref2); 165 } 166 167 tsc_ref_min = min(tsc_ref_min, tsc2); 168 169 // 检查与参考值的误差 170 let mut delta = tsc_pit_min * 100; 171 delta /= tsc_ref_min; 172 173 // 如果误差在10%以内,那么认为测量成功 174 // 返回参考值,因为它是更精确的 175 if delta >= 90 && delta <= 110 { 176 kinfo!( 177 "PIT calibration matches {}. {} loops", 178 if hpet { "HPET" } else { "PMTIMER" }, 179 i + 1 180 ); 181 return Ok(tsc_ref_min); 182 } 183 184 if i == 1 && tsc_pit_min == u64::MAX { 185 latch = cal2_latch; 186 ms = cal2_ms; 187 loopmin = cal2_pit_loops; 188 } 189 } 190 191 if tsc_pit_min == u64::MAX { 192 kwarn!("Unable to calibrate against PIT"); 193 194 // 如果没有参考值,那么禁用tsc 195 if (!hpet) && (global_ref1 == 0) && (global_ref2 == 0) { 196 kwarn!("No reference (HPET/PMTIMER) available"); 197 return Err(SystemError::ENODEV); 198 } 199 200 if tsc_ref_min == u64::MAX { 201 kwarn!("Unable to calibrate against HPET/PMTIMER"); 202 return Err(SystemError::ENODEV); 203 } 204 205 kinfo!( 206 "Using {} reference calibration", 207 if hpet { "HPET" } else { "PMTIMER" } 208 ); 209 return Ok(tsc_ref_min); 210 } 211 212 // We don't have an alternative source, use the PIT calibration value 213 if (!hpet) && (global_ref1 == 0) && (global_ref2 == 0) { 214 kinfo!("Using PIT calibration value"); 215 return Ok(tsc_pit_min); 216 } 217 218 // The alternative source failed, use the PIT calibration value 219 if tsc_ref_min == u64::MAX { 220 kwarn!("Unable to calibrate against HPET/PMTIMER, using PIT calibration value"); 221 return Ok(tsc_pit_min); 222 } 223 224 // The calibration values differ too much. In doubt, we use 225 // the PIT value as we know that there are PMTIMERs around 226 // running at double speed. At least we let the user know: 227 kwarn!( 228 "PIT calibration deviates from {}: tsc_pit_min={}, tsc_ref_min={}", 229 if hpet { "HPET" } else { "PMTIMER" }, 230 tsc_pit_min, 231 tsc_ref_min 232 ); 233 234 kinfo!("Using PIT calibration value"); 235 return Ok(tsc_pit_min); 236 } 237 238 /// 尝试使用PIT来校准tsc时间,并且返回tsc的频率(khz)。 239 /// 如果失败,那么返回None 240 /// 241 /// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#389 242 fn pit_calibrate_tsc(latch: u64, ms: u64, loopmin: u64) -> Option<u64> { 243 unsafe { 244 // Set the Gate high, disable speaker 245 let d = (CurrentPortIOArch::in8(0x61) & (!0x02)) | 0x01; 246 CurrentPortIOArch::out8(0x61, d); 247 248 // Setup CTC channel 2* for mode 0, (interrupt on terminal 249 // count mode), binary count. Set the latch register to 50ms 250 // (LSB then MSB) to begin countdown. 251 CurrentPortIOArch::out8(0x43, 0xb0); 252 CurrentPortIOArch::out8(0x42, (latch & 0xff) as u8); 253 CurrentPortIOArch::out8(0x42, ((latch >> 8) & 0xff) as u8); 254 } 255 256 let mut tsc = CurrentTimeArch::get_cycles() as u64; 257 let t1 = tsc; 258 let mut t2 = tsc; 259 let mut pitcnt = 0u64; 260 let mut tscmax = 0u64; 261 let mut tscmin = u64::MAX; 262 while unsafe { (CurrentPortIOArch::in8(0x61) & 0x20) == 0 } { 263 t2 = CurrentTimeArch::get_cycles() as u64; 264 let delta = t2 - tsc; 265 tsc = t2; 266 267 tscmin = min(tscmin, delta); 268 tscmax = max(tscmax, delta); 269 270 pitcnt += 1; 271 } 272 273 // Sanity checks: 274 // 275 // If we were not able to read the PIT more than loopmin 276 // times, then we have been hit by a massive SMI 277 // 278 // If the maximum is 10 times larger than the minimum, 279 // then we got hit by an SMI as well. 280 if pitcnt < loopmin || tscmax > 10 * tscmin { 281 return None; 282 } 283 284 let mut delta = t2 - t1; 285 delta /= ms; 286 287 return Some(delta); 288 } 289 290 /// 读取tsc和参考值 291 /// 292 /// ## 参数 293 /// 294 /// - `hpet_enabled`:是否启用hpet 295 /// 296 /// ## 返回 297 /// 298 /// - `Ok((tsc, ref))`:tsc和参考值 299 /// 300 /// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#317 301 fn read_refs(hpet_enabled: bool) -> (u64, u64) { 302 let thresh = if Self::tsc_khz() == 0 { 303 Self::DEFAULT_THRESHOLD 304 } else { 305 Self::tsc_khz() >> 5 306 }; 307 308 let mut ref_ret = 0; 309 for _ in 0..5 { 310 let t1 = CurrentTimeArch::get_cycles() as u64; 311 if hpet_enabled { 312 ref_ret = hpet_instance().main_counter_value(); 313 } else { 314 todo!("read pmtimer") 315 } 316 let t2 = CurrentTimeArch::get_cycles() as u64; 317 if (t2 - t1) < thresh { 318 return (t2, ref_ret); 319 } 320 } 321 322 kwarn!("TSCManager: Failed to read reference value, tsc delta too high"); 323 return (u64::MAX, ref_ret); 324 } 325 326 /// 根据HPET的参考值计算tsc的频率 327 /// 328 /// https://opengrok.ringotek.cn/xref/linux-6.1.9/arch/x86/kernel/tsc.c#339 329 fn calc_hpet_ref(mut deltatsc: u64, ref1: u64, mut ref2: u64) -> u64 { 330 if ref2 <= ref1 { 331 ref2 += 0x100000000; 332 } 333 334 ref2 -= ref1; 335 let mut tmp = ref2 * hpet_instance().period(); 336 337 tmp /= 1000000; 338 339 deltatsc /= tmp; 340 341 return deltatsc; 342 } 343 344 /// 根据PMtimer的参考值计算tsc的频率 345 fn calc_pmtimer_ref(mut deltatsc: u64, ref1: u64, mut ref2: u64) -> u64 { 346 if unlikely(ref1 == 0 && ref2 == 0) { 347 return u64::MAX; 348 } 349 350 if ref2 < ref1 { 351 ref2 += ACPI_PM_OVERRUN; 352 } 353 354 ref2 -= ref1; 355 356 let mut tmp = ref2 * 1000000000; 357 358 tmp /= PMTMR_TICKS_PER_SEC; 359 360 deltatsc /= tmp; 361 362 return deltatsc; 363 } 364 365 pub fn tsc_khz() -> u64 { 366 unsafe { TSC_KHZ } 367 } 368 369 pub fn cpu_khz() -> u64 { 370 unsafe { CPU_KHZ } 371 } 372 373 fn set_cpu_khz(khz: u64) { 374 unsafe { 375 CPU_KHZ = khz; 376 } 377 } 378 379 fn set_tsc_khz(khz: u64) { 380 unsafe { 381 TSC_KHZ = khz; 382 } 383 } 384 } 385