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