Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/tsc/cpuid.rs
5394 views
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.
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.
77
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.
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.
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.
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
164