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