xref: /aosp_15_r20/external/crosvm/devices/src/tsc/cpuid.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // TODO(b/213149158): Remove after uses are added.
6 #![allow(dead_code)]
7 
8 use std::arch::x86_64::CpuidResult;
9 
10 /// Function to retrieve the given CPUID leaf and sub-leaf.
11 pub type CpuidCountFn = unsafe fn(u32, u32) -> CpuidResult;
12 
13 /// Gets the TSC frequency for cpuid leaf 0x15 from the existing leaves 0x15 and 0x16.
14 ///
15 /// # Arguments
16 /// * `cpuid_count`: function that returns the CPUID information for the given leaf/subleaf
17 ///   combination. `std::arch::x86_64::__cpuid_count` may be used to provide the CPUID information
18 ///   from the host.
tsc_frequency_cpuid(cpuid_count: CpuidCountFn) -> Option<hypervisor::CpuIdEntry>19 pub fn tsc_frequency_cpuid(cpuid_count: CpuidCountFn) -> Option<hypervisor::CpuIdEntry> {
20     // SAFETY:
21     // Safe because we pass 0 and 0 for this call and the host supports the `cpuid` instruction.
22     let result = unsafe { cpuid_count(0, 0) };
23     if result.eax < 0x15 {
24         return None;
25     }
26 
27     let mut tsc_freq = hypervisor::CpuIdEntry {
28         // 0x15 is the TSC frequency leaf.
29         function: 0x15,
30         index: 0,
31         flags: 0,
32         cpuid: CpuidResult {
33             eax: 0,
34             ebx: 0,
35             ecx: 0,
36             edx: 0,
37         },
38     };
39     // SAFETY:
40     // Safe because we pass 0 and 0 for this call and the host supports the `cpuid` instruction.
41     tsc_freq.cpuid = unsafe { cpuid_count(tsc_freq.function, tsc_freq.index) };
42 
43     if tsc_freq.cpuid.ecx != 0 {
44         Some(tsc_freq)
45     } else {
46         // The core crystal frequency is missing. Old kernels (<5.3) don't try to derive it from the
47         // CPU base clock speed. Here, we essentially implement
48         // https://lore.kernel.org/patchwork/patch/1064690/ so that old kernels can calibrate TSC.
49         // SAFETY:
50         // Safe because the host supports `cpuid` instruction.
51         let cpu_clock = unsafe {
52             // 0x16 is the base clock frequency leaf.
53             cpuid_count(0x16, 0)
54         };
55         if cpu_clock.eax > 0 {
56             // Here, we assume the CPU base clock is the core crystal clock, as is done in the patch
57             // that exists in 5.3+ kernels. We further assume that the core crystal clock is exactly
58             // the TSC frequency. As such, we expose the base clock scaled by the _inverse_ of the
59             // "tsc freq" / "core crystal clock freq" ratio. That way when the kernel extracts
60             // the frequency & multiplies by the ratio, it obtains the TSC frequency.
61             //
62             // base_mhz = cpu_clock.eax
63             // tsc_to_base_ratio = tsc_freq.eax / tsc_freq.ebx
64             // crystal_hz = base_mhz * tsc_base_to_clock_ratio * 10^6
65             tsc_freq.cpuid.ecx = (cpu_clock.eax as f64 * tsc_freq.cpuid.eax as f64 * 1_000_000_f64
66                 / tsc_freq.cpuid.ebx as f64)
67                 .round() as u32;
68             Some(tsc_freq)
69         } else {
70             None
71         }
72     }
73 }
74 
75 /// Given the tsc frequency in Hz and the bus frequency in Hz, return a fake version of
76 /// cpuid leaf 0x15.
fake_tsc_frequency_cpuid(tsc_hz: u64, bus_hz: u32) -> CpuidResult77 pub fn fake_tsc_frequency_cpuid(tsc_hz: u64, bus_hz: u32) -> CpuidResult {
78     // We use 1000 for the crystal clock ratio denominator so we can preserve precision in case
79     // tsc_hz is not neatly divisible by bus_hz
80     let crystal_clock_ratio_denominator: u32 = 1000;
81     let crystal_clock_ratio_numerator: u32 =
82         (tsc_hz * crystal_clock_ratio_denominator as u64 / bus_hz as u64) as u32;
83 
84     CpuidResult {
85         eax: crystal_clock_ratio_denominator,
86         ebx: crystal_clock_ratio_numerator,
87         ecx: bus_hz,
88         edx: 0,
89     }
90 }
91 
92 /// Returns the Bus frequency in Hz, based on reading Intel-specific cpuids, or None
93 /// if the frequency can't be determined from cpuids.
bus_freq_hz(cpuid_count: CpuidCountFn) -> Option<u32>94 pub fn bus_freq_hz(cpuid_count: CpuidCountFn) -> Option<u32> {
95     tsc_frequency_cpuid(cpuid_count).map(|cpuid| cpuid.cpuid.ecx)
96 }
97 
98 /// Returns the TSC frequency in Hz, based on reading Intel-specific cpuids, or None
99 /// if the frequency can't be determined from cpuids.
tsc_freq_hz(cpuid_count: CpuidCountFn) -> Option<u32>100 pub fn tsc_freq_hz(cpuid_count: CpuidCountFn) -> Option<u32> {
101     tsc_frequency_cpuid(cpuid_count).map(|cpuid| {
102         (cpuid.cpuid.ecx as u64 * cpuid.cpuid.ebx as u64 / cpuid.cpuid.eax as u64) as u32
103     })
104 }
105 
106 #[cfg(test)]
107 mod tests {
108     use super::*;
109 
110     #[test]
111     // It seems that most Intel CPUs don't have any TSC frequency information in CPUID.15H.ECX. The
112     // linux kernel only treats the TSC frequency as a "known" frequency if it comes from
113     // CPUID.15H.ECX, and we want our TSC frequency to be "known" to prevent clock watchdogs from
114     // invalidating the TSC clocksource. So we derive CPUID.15H.ECX from the values in CPUID.16H.
115     // This test verifies that that derivation is working correctly.
test_leaf15_derivation()116     fn test_leaf15_derivation() {
117         const CRYSTAL_CLOCK_RATIO: u32 = 88;
118         const TSC_FREQUENCY_HZ: u32 = 2100000000u32;
119 
120         let fake_cpuid = |function: u32, index: u32| {
121             match (function, index) {
122                 (0, 0) => {
123                     CpuidResult {
124                         eax: 0x16, // highest available leaf is 0x16
125                         ebx: 0,
126                         ecx: 0,
127                         edx: 0,
128                     }
129                 }
130                 (0x15, 0) => {
131                     CpuidResult {
132                         eax: 2, // eax usually contains 2, and ebx/eax is the crystal clock ratio
133                         ebx: CRYSTAL_CLOCK_RATIO * 2,
134                         ecx: 0,
135                         edx: 0,
136                     }
137                 }
138                 (0x16, 0) => {
139                     CpuidResult {
140                         eax: TSC_FREQUENCY_HZ / 1_000_000_u32, // MHz frequency
141                         ebx: 0,
142                         ecx: 0,
143                         edx: 0,
144                     }
145                 }
146                 _ => CpuidResult {
147                     eax: 0,
148                     ebx: 0,
149                     ecx: 0,
150                     edx: 0,
151                 },
152             }
153         };
154 
155         // We compare the frequencies divided by the CRYSTAL_CLOCK_RATIO because that's the
156         // resolution that the tsc frequency is stored at in CPUID.15H.ECX.
157         assert_eq!(
158             tsc_freq_hz(fake_cpuid).unwrap() / CRYSTAL_CLOCK_RATIO,
159             TSC_FREQUENCY_HZ / CRYSTAL_CLOCK_RATIO
160         );
161     }
162 }
163