Skip to content

Commit cff75e3

Browse files
committed
kernel: migrate CPUID feature detection to cpufeature crate
Replace the hand-rolled CpuFeat/CpuidReg machinery with descriptors from the external cpufeature crate and route CPUID queries through CpuidBackend on each platform. - Add cpufeature as a workspace dependency - Simplify features.rs to map Feature variants to leaf descriptors - Remove SvsmPlatform::cpuid; implement CpuidBackend for Native, SNP, and TDP instead - Add platform::cpuid and platform::cpuid_value dispatchers - Use XSAVE_SZ descriptor in xsave_area_size() Signed-off-by: tanish111 <tanishdesai37@gmail.com>
1 parent 4b05d69 commit cff75e3

10 files changed

Lines changed: 127 additions & 199 deletions

File tree

Cargo.lock

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers.git", rev =
103103
zerocopy = { version = "0.8.2", features = ["derive"] }
104104
zeroize = { version = "1.8.2", default-features = false }
105105

106+
#Github repos
107+
cpufeature = { git = "https://github.com/tanish111/cpufeature.git", rev = "4f86e2d062ab8e456566d00db64913caa2f53f33" }
108+
106109
# Verus repos
107110
verus_builtin = { version = "=0.0.0-2026-04-12-0118", default-features = false }
108111
verus_builtin_macros = { version = "=0.0.0-2026-04-12-0118", features = ["vpanic"], default-features = false }

kernel/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ doctest = true
1616
bootdefs.workspace = true
1717
bootimg.workspace = true
1818
cpuarch.workspace = true
19+
cpufeature.workspace = true
1920
libaproxy = { workspace = true, optional = true }
2021
elf.workspace = true
2122
syscall.workspace = true

kernel/src/cpu/features.rs

Lines changed: 69 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -2,173 +2,83 @@
22
//
33
// Copyright (c) 2022-2023, 2026 SUSE LLC
44
//
5-
// Authors: Joerg Roedel <jroedel@suse.de>
6-
// Carlos López <clopez@suse.de>
7-
8-
use core::arch::x86_64::CpuidResult;
9-
10-
use crate::{platform::cpuid, utils::immut_after_init::ImmutAfterInitCell};
11-
12-
/// The CPUID output register for a particular feature
13-
#[derive(Clone, Copy, Debug)]
14-
enum CpuidReg {
15-
Eax,
16-
Ebx,
17-
Ecx,
18-
Edx,
19-
}
20-
21-
/// A discoverable CPU feature.
22-
///
23-
/// At the moment this type is used for CPUID detection, but it can
24-
/// be expanded to use other sources of information.
25-
#[derive(Debug)]
26-
struct CpuFeat {
27-
/// Raw value
28-
val: ImmutAfterInitCell<u32>,
29-
/// CPUID leaf
30-
leaf: u32,
31-
/// CPUID subleaf
32-
subleaf: u32,
33-
/// CPUID output register
34-
reg: CpuidReg,
35-
/// Bitshift to apply to raw value
36-
shift: u8,
37-
/// Bitmask to apply to raw value
38-
bitsize: u8,
39-
/// Expected value after shift + mask
40-
expected: u32,
5+
// Author: Joerg Roedel <jroedel@suse.de>
6+
// Author: Carlos López <clopez@suse.de>
7+
8+
use cpufeature::CpuidFeature;
9+
use cpufeature::CpuidRegister;
10+
use cpufeature::leaves::{
11+
CET_SS, INVLPGB_MAX_PAGES, PHYS_ADDR_BITS, PTE_CBIT_POS, X86_FEATURE_PGE, X86_FEATURE_SMAP,
12+
X86_FEATURE_SMEP, X86_FEATURE_UMIP, X86_FEATURE_X2APIC, X86_FEATURE_XMM, X86_FEATURE_XSAVE,
13+
X86_FEATURE_XSAVEOPT, XCR0_AVX, XCR0_SSE, XCR0_X87,
14+
};
15+
16+
use crate::platform::cpuid_value;
17+
18+
/// Hyper-V CPUID interface signature (`"Hv#1"`) on leaf `0x40000001`.
19+
const HYPERV_INTERFACE: CpuidFeature = CpuidFeature {
20+
leaf: 0x4000_0001,
21+
subleaf: 0,
22+
register: CpuidRegister::Eax,
23+
shift: 0,
24+
width: 32,
25+
};
26+
27+
/// Discoverable CPU features backed by [`cpufeature`] leaf descriptors.
28+
#[repr(usize)]
29+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30+
pub enum Feature {
31+
X2Apic,
32+
Xsave,
33+
Pge,
34+
Sse1,
35+
Smep,
36+
Smap,
37+
Umip,
38+
CetSS,
39+
Xcr0X87,
40+
Xcr0Sse,
41+
Xcr0Avx,
42+
XsaveOpt,
43+
HyperV,
44+
PhysAddrSizes,
45+
InvlpgbMax,
46+
Cbit,
4147
}
4248

43-
impl CpuFeat {
44-
/// Create a new feature, indicated by a single bit in the specified
45-
/// register in the given CPUID leaf.
46-
const fn new_bit(leaf: u32, reg: CpuidReg, bit: u8) -> Self {
47-
Self::new(leaf, reg, bit, 1, 1)
48-
}
49-
50-
/// Create a new feature, indicated by the full value of a register
51-
/// in the given CPUID leaf.
52-
const fn new_u32(leaf: u32, reg: CpuidReg, expected: u32) -> Self {
53-
Self::new(leaf, reg, 0, u32::BITS as u8, expected)
54-
}
55-
56-
/// Create a new CPU feature, detected by querying the given CPUID leaf, and applying
57-
/// a bitshift and bitmask on the specified output register to compare it to the given
58-
/// expected value.
59-
const fn new(leaf: u32, reg: CpuidReg, shift: u8, bitsize: u8, expected: u32) -> Self {
60-
// This method is only called from const context, so these assertions have no
61-
// runtime effect.
62-
assert!((shift as u32) < u32::BITS);
63-
assert!((bitsize as u32) <= u32::BITS);
64-
Self {
65-
val: ImmutAfterInitCell::uninit(),
66-
leaf,
67-
subleaf: 0,
68-
reg,
69-
shift,
70-
bitsize,
71-
expected,
72-
}
73-
}
74-
75-
/// Create a copy of the given CPU feature, but with the specified
76-
/// subleaf.
77-
const fn with_subfn(mut self, subleaf: u32) -> Self {
78-
self.subleaf = subleaf;
79-
self
80-
}
81-
82-
/// Get the CPUID register that corresponds to this feature
83-
const fn get_reg(&self, cpuid: &CpuidResult) -> u32 {
84-
match self.reg {
85-
CpuidReg::Eax => cpuid.eax,
86-
CpuidReg::Ebx => cpuid.ebx,
87-
CpuidReg::Ecx => cpuid.ecx,
88-
CpuidReg::Edx => cpuid.edx,
89-
}
90-
}
91-
92-
const fn mask(&self) -> u32 {
93-
((1u64 << self.bitsize) - 1) as u32
94-
}
95-
96-
fn get_or_init(&self) -> u32 {
97-
if let Ok(val) = self.val.try_get_inner() {
98-
return *val;
49+
impl Feature {
50+
const fn descriptor(self) -> &'static CpuidFeature {
51+
match self {
52+
Self::X2Apic => &X86_FEATURE_X2APIC,
53+
Self::Xsave => &X86_FEATURE_XSAVE,
54+
Self::Pge => &X86_FEATURE_PGE,
55+
Self::Sse1 => &X86_FEATURE_XMM,
56+
Self::Smep => &X86_FEATURE_SMEP,
57+
Self::Smap => &X86_FEATURE_SMAP,
58+
Self::Umip => &X86_FEATURE_UMIP,
59+
Self::CetSS => &CET_SS,
60+
Self::Xcr0X87 => &XCR0_X87,
61+
Self::Xcr0Sse => &XCR0_SSE,
62+
Self::Xcr0Avx => &XCR0_AVX,
63+
Self::XsaveOpt => &X86_FEATURE_XSAVEOPT,
64+
Self::HyperV => &HYPERV_INTERFACE,
65+
Self::PhysAddrSizes => &PHYS_ADDR_BITS,
66+
Self::InvlpgbMax => &INVLPGB_MAX_PAGES,
67+
Self::Cbit => &PTE_CBIT_POS,
9968
}
100-
let val = cpuid(self.leaf, self.subleaf).map_or(0, |c| self.get_reg(&c));
101-
// If init() fails it means the cell got initialized by someone else
102-
// concurrently, which is always benign.
103-
let _ = self.val.init(val);
104-
val
105-
}
106-
107-
/// Gets the raw value of this feature, lazily querying CPUID if
108-
/// the value is not cached from a previous query
109-
fn get(&self) -> u32 {
110-
(self.get_or_init() >> self.shift) & self.mask()
111-
}
112-
113-
/// Checks whether this feature is available by comparing it to
114-
/// its expected value, lazily querying CPUID if the value is not
115-
/// cached from a previous query
116-
fn enabled(&self) -> bool {
117-
self.get() == self.expected
11869
}
11970
}
12071

121-
/// Macro to generate a CPU feature lookup table
122-
macro_rules! define_cpu_feats {
123-
(
124-
$(
125-
$variant:ident => $feat_expr:expr
126-
),* $(,)?
127-
) => {
128-
129-
// Feature enum to use as index
130-
#[repr(usize)]
131-
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
132-
pub enum Feature {
133-
$( $variant, )*
134-
}
135-
136-
/// The number of defined features
137-
const NUM_FEATS: usize = [ $( stringify!($variant) ),* ].len();
138-
139-
static CPU_FEATS: [CpuFeat; NUM_FEATS] = [
140-
$( $feat_expr, )*
141-
];
142-
};
143-
}
144-
145-
/// Check if the given feature is enabled by comparing it to its expected
146-
/// value.
72+
/// Check if the given feature is enabled.
14773
pub fn cpu_has_feat(feat: Feature) -> bool {
148-
// Because of #[repr(usize)], this cast matches the array index perfectly.
149-
CPU_FEATS[feat as usize].enabled()
74+
match feat {
75+
Feature::HyperV => cpuid_value(feat.descriptor()) == Some(0x3123_7648),
76+
Feature::PhysAddrSizes | Feature::InvlpgbMax | Feature::Cbit => false,
77+
_ => cpuid_value(feat.descriptor()) == Some(1),
78+
}
15079
}
15180

152-
/// Gets the raw value of the given feature
81+
/// Get the raw CPUID field value for the given feature.
15382
pub fn cpu_get_feat(feat: Feature) -> u32 {
154-
CPU_FEATS[feat as usize].get()
155-
}
156-
157-
define_cpu_feats! {
158-
X2Apic => CpuFeat::new_bit(0x0000_0001, CpuidReg::Ecx, 21),
159-
Xsave => CpuFeat::new_bit(0x0000_0001, CpuidReg::Ecx, 26),
160-
Pge => CpuFeat::new_bit(0x0000_0001, CpuidReg::Edx, 13),
161-
Sse1 => CpuFeat::new_bit(0x0000_0001, CpuidReg::Edx, 25),
162-
Smep => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ebx, 7),
163-
Smap => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ebx, 20),
164-
Umip => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ecx, 2),
165-
CetSS => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ecx, 7),
166-
Xcr0X87 => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 0),
167-
Xcr0Sse => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 1),
168-
Xcr0Avx => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 2),
169-
XsaveOpt => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 0).with_subfn(1),
170-
HyperV => CpuFeat::new_u32(0x40000001, CpuidReg::Eax, 0x31237648),
171-
PhysAddrSizes => CpuFeat::new_u32(0x80000008, CpuidReg::Eax, 0),
172-
InvlpgbMax => CpuFeat::new(0x80000008, CpuidReg::Edx, 0, u16::BITS as u8, 0),
173-
Cbit => CpuFeat::new(0x8000001f, CpuidReg::Ebx, 0, 6, 0),
83+
cpuid_value(feat.descriptor()).unwrap_or(0)
17484
}

kernel/src/cpu/sse.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use crate::platform::cpuid;
99
use core::arch::asm;
1010
use core::arch::x86_64::{_xgetbv, _xsetbv};
1111
use core::sync::atomic::{AtomicU64, Ordering};
12+
use cpufeature::CpuidFeature;
13+
use cpufeature::leaves::XSAVE_SZ;
1214

1315
use super::features::{Feature, cpu_has_feat};
1416

@@ -75,7 +77,11 @@ pub fn xsave_area_size() -> u32 {
7577
if xcr0 & (1u64 << bit) == 0 {
7678
continue;
7779
}
78-
let Some(result) = cpuid(0xd, bit) else {
80+
let feature = CpuidFeature {
81+
subleaf: bit,
82+
..XSAVE_SZ
83+
};
84+
let Some(result) = cpuid(&feature) else {
7985
log::warn!("FP feature {bit:#x} enabled but not present in CPUID");
8086
continue;
8187
};

kernel/src/cpu/vc.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,7 @@ fn handle_cpuid(ctx: &mut X86ExceptionContext) -> Result<(), SvsmError> {
156156
}
157157

158158
fn snp_cpuid(ctx: &mut X86ExceptionContext) -> Result<(), SvsmError> {
159-
let cpuid_fn = ctx.regs.rax as u32;
160-
let cpuid_subfn = ctx.regs.rcx as u32;
161-
let Some(ret) = cpuid_table(cpuid_fn, cpuid_subfn) else {
159+
let Some(ret) = cpuid_table(ctx.regs.rax as u32, ctx.regs.rcx as u32) else {
162160
return Err(VcError::new(ctx, VcErrorType::UnknownCpuidLeaf).into());
163161
};
164162

kernel/src/platform/mod.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ use snp::SnpPlatform;
1919
use tdp::TdpPlatform;
2020

2121
use core::arch::asm;
22-
use core::arch::x86_64::{__cpuid_count, CpuidResult};
22+
use core::arch::x86_64::CpuidResult;
23+
2324
use core::fmt::Debug;
2425
use core::mem::MaybeUninit;
26+
use cpufeature::CpuidFeature;
27+
use cpufeature::backend::CpuidBackend;
2528

2629
use crate::address::{PhysAddr, VirtAddr};
2730
use crate::boot_params::BootParams;
@@ -181,15 +184,6 @@ pub trait SvsmPlatform: Sync {
181184
hypercall_pages: &hyperv::HypercallPagesGuard<'_>,
182185
) -> hyperv::HvHypercallOutput;
183186

184-
/// Obtain CPUID using platform-specific tables.
185-
fn cpuid(eax: u32, ecx: u32) -> Option<CpuidResult>
186-
where
187-
Self: Sized,
188-
{
189-
// SAFETY: CPUID is always safe
190-
unsafe { Some(__cpuid_count(eax, ecx)) }
191-
}
192-
193187
/// Write a host-owned MSR.
194188
/// # Safety
195189
/// The caller must ensure that the requested MSR modification does mot
@@ -368,8 +362,13 @@ macro_rules! platform_method {
368362
}
369363

370364
#[inline]
371-
pub fn cpuid(leaf: u32, subleaf: u32) -> Option<CpuidResult> {
372-
platform_method!(cpuid, leaf, subleaf)
365+
pub fn cpuid(feature: &CpuidFeature) -> Option<CpuidResult> {
366+
platform_method!(cpuid, feature)
367+
}
368+
369+
#[inline]
370+
pub fn cpuid_value(feature: &CpuidFeature) -> Option<u32> {
371+
platform_method!(cpuid_value, feature)
373372
}
374373

375374
pub fn halt() {

0 commit comments

Comments
 (0)