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