ostd/arch/x86/cpu/cpuid.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
// SPDX-License-Identifier: MPL-2.0
//! CPU information from the CPUID instruction.
use core::arch::x86_64::CpuidResult;
use spin::Once;
static MAX_LEAF: Once<u32> = Once::new();
static MAX_EXTENDED_LEAF: Once<u32> = Once::new();
#[repr(u32)]
enum Leaf {
Base = 0x00,
Xstate = 0x0d,
Tsc = 0x15,
ExtBase = 0x80000000,
}
/// Executes the CPUID instruction for the given leaf and subleaf.
///
/// This method will return `None` if the leaf is not supported.
pub fn cpuid(leaf: u32, subleaf: u32) -> Option<CpuidResult> {
fn raw_cpuid(leaf: u32, subleaf: u32) -> CpuidResult {
// SAFETY: It is safe to execute the CPUID instruction.
unsafe { core::arch::x86_64::__cpuid_count(leaf, subleaf) }
}
let max_leaf = if leaf < Leaf::ExtBase as u32 {
*MAX_LEAF.call_once(|| raw_cpuid(Leaf::Base as u32, 0).eax)
} else {
*MAX_EXTENDED_LEAF.call_once(|| raw_cpuid(Leaf::ExtBase as u32, 0).eax)
};
if leaf > max_leaf {
None
} else {
Some(raw_cpuid(leaf, subleaf))
}
}
/// Queries the frequency in Hz of the Time Stamp Counter (TSC).
///
/// This is based on the information given by the CPUID instruction in the Time Stamp Counter and
/// Nominal Core Crystal Clock Information Leaf.
///
/// Note that the CPUID leaf is currently only supported by new Intel CPUs. This method will return
/// `None` if it is not supported.
pub(in crate::arch) fn query_tsc_freq() -> Option<u64> {
let CpuidResult {
eax: denominator,
ebx: numerator,
ecx: crystal_freq,
..
} = cpuid(Leaf::Tsc as u32, 0)?;
if denominator == 0 || numerator == 0 {
return None;
}
// If the nominal core crystal clock frequency is not enumerated, we can either obtain that
// information from a hardcoded table or rely on the processor base frequency. The Intel
// documentation recommends the first approach [1], but Linux uses the second approach because
// the first approach is difficult to implement correctly for all corner cases [2]. However,
// the second approach does not provide 100% accurate frequencies, so Linux must adjust them at
// runtime [2]. For now, we avoid these headaches by faithfully reporting that the TSC
// frequency is unavailable.
//
// [1]: Intel(R) 64 and IA-32 Architectures Software Developer’s Manual,
// Section 20.7.3, Determining the Processor Base Frequency
// [2]: https://github.com/torvalds/linux/commit/604dc9170f2435d27da5039a3efd757dceadc684
if crystal_freq == 0 {
return None;
}
Some((crystal_freq as u64) * (numerator as u64) / (denominator as u64))
}
/// Queries the supported XSTATE features, i.e., the supported bits of `XCR0` and `IA32_XSS`.
pub(in crate::arch) fn query_xstate_max_features() -> Option<u64> {
let res0 = cpuid(Leaf::Xstate as u32, 0)?;
let res1 = cpuid(Leaf::Xstate as u32, 1)?;
// Supported bits in `XCR0`.
let xcr_bits = (res0.eax as u64) | ((res0.edx as u64) << 32);
// Supported bits in `IA32_XSS`.
let xss_bits = (res1.ecx as u64) | ((res1.edx as u64) << 32);
Some(xcr_bits | xss_bits)
}
/// Queries the size in bytes of the XSAVE area containing states enabled by `XCRO` and `IA32_XSS`.
pub(in crate::arch) fn query_xsave_area_size() -> Option<u32> {
cpuid(Leaf::Xstate as u32, 1).map(|res| res.ebx)
}