// Copyright 2022 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34// TODO(b/213149158): Remove after uses are added.5#![allow(dead_code)]67use std::arch::x86_64::CpuidResult;89/// Function to retrieve the given CPUID leaf and sub-leaf.10pub type CpuidCountFn = unsafe fn(u32, u32) -> CpuidResult;1112/// Gets the TSC frequency for cpuid leaf 0x15 from the existing leaves 0x15 and 0x16.13///14/// # Arguments15/// * `cpuid_count`: function that returns the CPUID information for the given leaf/subleaf16/// combination. `std::arch::x86_64::__cpuid_count` may be used to provide the CPUID information17/// from the host.18pub fn tsc_frequency_cpuid(cpuid_count: CpuidCountFn) -> Option<hypervisor::CpuIdEntry> {19// SAFETY:20// Safe because we pass 0 and 0 for this call and the host supports the `cpuid` instruction.21let result = unsafe { cpuid_count(0, 0) };22if result.eax < 0x15 {23return None;24}2526let mut tsc_freq = hypervisor::CpuIdEntry {27// 0x15 is the TSC frequency leaf.28function: 0x15,29index: 0,30flags: 0,31cpuid: CpuidResult {32eax: 0,33ebx: 0,34ecx: 0,35edx: 0,36},37};38// SAFETY:39// Safe because we pass 0 and 0 for this call and the host supports the `cpuid` instruction.40tsc_freq.cpuid = unsafe { cpuid_count(tsc_freq.function, tsc_freq.index) };4142if tsc_freq.cpuid.ecx != 0 {43Some(tsc_freq)44} else {45// The core crystal frequency is missing. Old kernels (<5.3) don't try to derive it from the46// CPU base clock speed. Here, we essentially implement47// https://lore.kernel.org/patchwork/patch/1064690/ so that old kernels can calibrate TSC.48// SAFETY:49// Safe because the host supports `cpuid` instruction.50let cpu_clock = unsafe {51// 0x16 is the base clock frequency leaf.52cpuid_count(0x16, 0)53};54if cpu_clock.eax > 0 {55// Here, we assume the CPU base clock is the core crystal clock, as is done in the patch56// that exists in 5.3+ kernels. We further assume that the core crystal clock is exactly57// the TSC frequency. As such, we expose the base clock scaled by the _inverse_ of the58// "tsc freq" / "core crystal clock freq" ratio. That way when the kernel extracts59// the frequency & multiplies by the ratio, it obtains the TSC frequency.60//61// base_mhz = cpu_clock.eax62// tsc_to_base_ratio = tsc_freq.eax / tsc_freq.ebx63// crystal_hz = base_mhz * tsc_base_to_clock_ratio * 10^664tsc_freq.cpuid.ecx = (cpu_clock.eax as f64 * tsc_freq.cpuid.eax as f64 * 1_000_000_f6465/ tsc_freq.cpuid.ebx as f64)66.round() as u32;67Some(tsc_freq)68} else {69None70}71}72}7374/// Given the tsc frequency in Hz and the bus frequency in Hz, return a fake version of75/// cpuid leaf 0x15.76pub fn fake_tsc_frequency_cpuid(tsc_hz: u64, bus_hz: u32) -> CpuidResult {77// We use 1000 for the crystal clock ratio denominator so we can preserve precision in case78// tsc_hz is not neatly divisible by bus_hz79let crystal_clock_ratio_denominator: u32 = 1000;80let crystal_clock_ratio_numerator: u32 =81(tsc_hz * crystal_clock_ratio_denominator as u64 / bus_hz as u64) as u32;8283CpuidResult {84eax: crystal_clock_ratio_denominator,85ebx: crystal_clock_ratio_numerator,86ecx: bus_hz,87edx: 0,88}89}9091/// Returns the Bus frequency in Hz, based on reading Intel-specific cpuids, or None92/// if the frequency can't be determined from cpuids.93pub fn bus_freq_hz(cpuid_count: CpuidCountFn) -> Option<u32> {94tsc_frequency_cpuid(cpuid_count).map(|cpuid| cpuid.cpuid.ecx)95}9697/// Returns the TSC frequency in Hz, based on reading Intel-specific cpuids, or None98/// if the frequency can't be determined from cpuids.99pub fn tsc_freq_hz(cpuid_count: CpuidCountFn) -> Option<u32> {100tsc_frequency_cpuid(cpuid_count).map(|cpuid| {101(cpuid.cpuid.ecx as u64 * cpuid.cpuid.ebx as u64 / cpuid.cpuid.eax as u64) as u32102})103}104105#[cfg(test)]106mod tests {107use super::*;108109#[test]110// It seems that most Intel CPUs don't have any TSC frequency information in CPUID.15H.ECX. The111// linux kernel only treats the TSC frequency as a "known" frequency if it comes from112// CPUID.15H.ECX, and we want our TSC frequency to be "known" to prevent clock watchdogs from113// invalidating the TSC clocksource. So we derive CPUID.15H.ECX from the values in CPUID.16H.114// This test verifies that that derivation is working correctly.115fn test_leaf15_derivation() {116const CRYSTAL_CLOCK_RATIO: u32 = 88;117const TSC_FREQUENCY_HZ: u32 = 2100000000u32;118119let fake_cpuid = |function: u32, index: u32| {120match (function, index) {121(0, 0) => {122CpuidResult {123eax: 0x16, // highest available leaf is 0x16124ebx: 0,125ecx: 0,126edx: 0,127}128}129(0x15, 0) => {130CpuidResult {131eax: 2, // eax usually contains 2, and ebx/eax is the crystal clock ratio132ebx: CRYSTAL_CLOCK_RATIO * 2,133ecx: 0,134edx: 0,135}136}137(0x16, 0) => {138CpuidResult {139eax: TSC_FREQUENCY_HZ / 1_000_000_u32, // MHz frequency140ebx: 0,141ecx: 0,142edx: 0,143}144}145_ => CpuidResult {146eax: 0,147ebx: 0,148ecx: 0,149edx: 0,150},151}152};153154// We compare the frequencies divided by the CRYSTAL_CLOCK_RATIO because that's the155// resolution that the tsc frequency is stored at in CPUID.15H.ECX.156assert_eq!(157tsc_freq_hz(fake_cpuid).unwrap() / CRYSTAL_CLOCK_RATIO,158TSC_FREQUENCY_HZ / CRYSTAL_CLOCK_RATIO159);160}161}162163164