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