Skip to content

kernel: migrate CPUID feature detection to cpufeature crate#1137

Open
tanish111 wants to merge 1 commit into
coconut-svsm:mainfrom
tanish111:cpufeature_impl
Open

kernel: migrate CPUID feature detection to cpufeature crate#1137
tanish111 wants to merge 1 commit into
coconut-svsm:mainfrom
tanish111:cpufeature_impl

Conversation

@tanish111

@tanish111 tanish111 commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

The kernel maintained its own CPUID feature layer: a Feature enum,
define_cpu_feats! macro, per-feature CpuFeat cache cells, and
scattered leaf/register/bit constants at call sites. Every new
feature meant updating the macro table, duplicating CPUID layout
knowledge, and keeping that in sync with how each platform
executes CPUID (native insn, SNP CPUID table, TDP VM exit).

Adopt the shared cpufeature crate, generated from the X86-CPUID
database, as the single source of truth for standard feature
descriptors. Call sites pass cpufeature::leaves constants directly
to cpu_has_feat() and cpu_get_feat() instead of a local Feature
enum, so leaf numbers, registers, and bit positions are no longer
hand-maintained in the kernel.

Platform CPUID goes through CpuidBackend: native and TDP use the
default backend; SNP routes architectural leaves through the CPUID
table and hypervisor leaves through GHCB. platform::cpuid() takes a
CpuidFeature descriptor, so feature checks and platform dispatch
share one path. features.rs keeps only leaves outside the database
(HYPERV_INTERFACE, PHYS_ADDR_SIZES).

This removes about 110 lines of custom detection code, eliminates
the macro-generatedd lookup table, and simplifies future feature
additions by relying on external cpufeature crate instead
of extending another local table.

cpufeature crate:- https://github.com/coconut-svsm/cpufeature

@tanish111

Copy link
Copy Markdown
Contributor Author

@luigix25 a test PR.

Comment thread kernel/src/platform/native.rs Outdated

@luigix25 luigix25 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should we drop cpuid from the SvsmPlatform trait entirely.
We still have code that is calling the "old" cpuid.

Please update the commit message as we are not doing anything related to the shadowstack.

Comment thread kernel/src/platform/native.rs

@luigix25 luigix25 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once again avoid unrelated changes, and split the PR into multiple commits as we said during the call.

Ideally one commit per feature.

One the last commit should drop the cpuid function from SvsmPlatform so that every commit compiles.

@tanish111

tanish111 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@luigix25 it's giving clippy errors with this code

// Authors: Joerg Roedel <jroedel@suse.de>
//          Carlos López <clopez@suse.de>

Can you check it once.

@stefano-garzarella

Copy link
Copy Markdown
Member

@luigix25 it's giving clippy errors with this code

// Authors: Joerg Roedel <jroedel@suse.de>
//          Carlos López <clopez@suse.de>

Can you check it once.

CI runs make clippy on PRs and I didn't see any error about that. What change triggered it?

Anyway, also if it's the case, it's unrelated so it should be in a separate commit/PR.

@tanish111

tanish111 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

@stefano-garzarella sorry clippy was not the culprit it's pre-commit hooks

	if  ! [[ "${arr[4]}" =~ ^//[[:space:]]Author: ]] ; then
		echo "${header_warning}"
		return 1
	fi

thanks @luigix25 for quick debug.
(for now would remove pre-commit hook locally and would work on long term fix)

@tanish111 tanish111 force-pushed the cpufeature_impl branch 2 times, most recently from f1fa0fe to ce95c00 Compare June 29, 2026 10:40
@tanish111 tanish111 changed the title kernel: use cpufeature crate for CET shadow stack detection kernel: migrate CPUID feature detection to cpufeature crate Jun 29, 2026
@stefano-garzarella

Copy link
Copy Markdown
Member

@tanish111 please could you explain more clearly in the commit and also in the PR description why we're doing this? (It's not at all clear to me, though I admit I haven't seen the changes; but since I don't understand the purpose of these changes, it's hard for me to follow)

@tanish111

Copy link
Copy Markdown
Contributor Author

@stefano-garzarella The motivation is to stop maintaining hand-written CPUID checks in SVSM. Right now, each cpu feature is detected with custom leaf/register/bit logic, which is repetitive and easy to get wrong as we add more features.
Instead, we're using a shared cpufeature crate that is auto-generated from the X86-CPUID database. That gives us a single, consistent way to enumerate CPU features, rather than scattering open-coded CPUID logic across the kernel. Also this is part of my GSoC project.

@stefano-garzarella

Copy link
Copy Markdown
Member

@tanish111 Thanks, I see now!

This is exactly the information that the PR and commit descriptions should include. This makes it easier for reviewers or anyone who looks at the commit in the future to understand the reasoning behind a change.

@tanish111 tanish111 force-pushed the cpufeature_impl branch 2 times, most recently from 7f8446b to 350772c Compare June 30, 2026 04:52
@tanish111

Copy link
Copy Markdown
Contributor Author

CPUID leaf 0x4000_0001 (Hyper-V interface signature "Hv#1" in EAX) is defined by the Hyper-V TLFS but is not in x86-cpuid-db only 0x4000_0000 (vendor ID) is. So cpufeature has no generated descriptor for it, and we define it locally in hyperv until it can be added upstream.

@tanish111 tanish111 force-pushed the cpufeature_impl branch 2 times, most recently from 03386f9 to 53974cd Compare June 30, 2026 10:10
@tanish111

Copy link
Copy Markdown
Contributor Author

@luigix25 it's ready to review

@osteffenrh

Copy link
Copy Markdown
Collaborator

Instead, we're using a shared cpufeature crate that is auto-generated from the X86-CPUID database.

I missed all the initial discussion, maybe it got discussed already:
Why reinvent a cpu feature detection crate instead of using https://crates.io/crates/cpufeatures ?

@tanish111

Copy link
Copy Markdown
Contributor Author

@osteffenrh crate you mentioned only covers ~20 hardcoded crypto/SIMD features as booleans. It still lacks support for arbitrary fields like multi-bit PCID/address-width values and many more.

Also it hardcodes raw CPUID execution, which doesn’t work for SNP guests that need to validate/source CPUID via the CPUID page rather than trusting the instruction directly, our CpuidBackend trait makes that pluggable.

@00xc 00xc left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand why we need to remove features.rs, since it already contains some of the functions you're introducing with a new name (e.g. cpu_has_feat() -> has_cpuid_feature()). I'd rater have the existing functions updated to use the new backend you're introducing (with the proper CPUID leaf definitions instead of manually inserted ones).

@00xc

00xc commented Jul 3, 2026

Copy link
Copy Markdown
Member

Also it hardcodes raw CPUID execution, which doesn’t work for SNP guests that need to validate/source CPUID via the CPUID page rather than trusting the instruction directly, our CpuidBackend trait makes that pluggable.

Technically it does work since we handle CPUID in the #VC exception handler, but it's definitely undesirable to take an exception for no good reason.

@tanish111

tanish111 commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

@00xc is there a specific reason we want to keep features.rs?
I agree that removing it means we lose the CPUID caching it had, and that’s a real downside. But in exchange we don’t have to register every feature in define_cpu_feats! before we can use it anywhere in the tree. Also CpuidFeature (which crate provides) and CpuFeat are identical when you compare the fields (leaf, subleaf, reg, shift, width), so keeping both felt redundant. The cpufeature crate already covers most of what features.rs did it had the registry (removing define_cpu_feats!) and the per-feature struct. That’s why I dropped the file and moved those helper functions to the platform layer instead.
Here is the PR for cpufeature crate for implementation details:- coconut-svsm/cpufeature#1

@tanish111

Copy link
Copy Markdown
Contributor Author

Also open to ideas on how we can implement caching without having a central registry that need to be updated every time we want to use a new CPUID. One possible solution is to add cache at query time with a global lookup table. (For the first time it fills look-up table and on next call onwards use it to return cpuid)

@00xc

00xc commented Jul 7, 2026

Copy link
Copy Markdown
Member

@00xc is there a specific reason we want to keep features.rs?

To minimize the overall diff size. The API you're replacing already exists.

I agree that removing it means we lose the CPUID caching it had, and that’s a real downside.

I am fine with removing the caching. What I was suggesting is that we can keep the existing public API and only change what's behind that API.

But in exchange we don’t have to register every feature in define_cpu_feats! before we can use it anywhere in the tree. Also CpuidFeature (which crate provides) and CpuFeat are identical when you compare the fields (leaf, subleaf, reg, shift, width), so keeping both felt redundant. The cpufeature crate already covers most of what features.rs did it had the registry (removing define_cpu_feats!) and the per-feature struct. That’s why I dropped the file and moved those helper functions to the platform layer instead. Here is the PR for cpufeature crate for implementation details:- coconut-svsm/cpufeature#1

That is all fine, we can rewrite the guts of features.rs and replace it with your crate (and even add caching back in later). That is what I meant by "I'd rater have the existing functions updated to use the new backend you're introducing (with the proper CPUID leaf definitions instead of manually inserted ones)".

@00xc

00xc commented Jul 7, 2026

Copy link
Copy Markdown
Member

Also open to ideas on how we can implement caching without having a central registry that need to be updated every time we want to use a new CPUID. One possible solution is to add cache at query time with a global lookup table. (For the first time it fills look-up table and on next call onwards use it to return cpuid)

That is what is what the existing code does.

For this to work efficiently you need to find a way to generate the lookup table at compile time (that is what define_cpu_feats!() does), since you need it to be const for the lookup to be cheap. Also IIRC const variables are inlined into each user (at least in release builds), as opposed to static, which generates a variable in e.g. the .bss section, and increases binary size.

@tanish111 tanish111 force-pushed the cpufeature_impl branch 2 times, most recently from 3ad3108 to 83e117d Compare July 8, 2026 18:36
The kernel maintained its own CPUID feature layer: a Feature enum,
define_cpu_feats! macro, per-feature CpuFeat cache cells, and
scattered leaf/register/bit constants at call sites. Every new
feature meant updating the macro table, duplicating CPUID layout
knowledge, and keeping that in sync with how each platform
executes CPUID (native insn, SNP CPUID table, TDP VM exit).

Adopt the shared cpufeature crate, generated from the X86-CPUID
database, as the single source of truth for standard feature
descriptors. Call sites pass cpufeature::leaves constants directly
to cpu_has_feat() and cpu_get_feat() instead of a local Feature
enum, so leaf numbers, registers, and bit positions are no longer
hand-maintained in the kernel.

Platform CPUID goes through CpuidBackend: native and TDP use the
default backend; SNP routes architectural leaves through the CPUID
table and hypervisor leaves through GHCB. platform::cpuid() takes a
CpuidFeature descriptor, so feature checks and platform dispatch
share one path. features.rs keeps only leaves outside the database
(HYPERV_INTERFACE, PHYS_ADDR_SIZES).

This removes about 110 lines of custom detection code, eliminates
the macro-generatedd lookup table, and simplifies future feature
additions by relying on external cpufeature crate instead
of extending another local table.

Signed-off-by: tanish111 <tanishdesai37@gmail.com>
@tanish111 tanish111 marked this pull request as ready for review July 8, 2026 18:48
@tanish111

Copy link
Copy Markdown
Contributor Author

@00xc I've updated the PR to keep the existing public API (cpu_has_feat / cpu_get_feat), but callers still need to change because the signature now takes &CpuidFeature constants from the crate instead of Feature. (The caller updates are mostly import + Feature::Foo → &X86_FEATURE_FOO; the behavioral logic stays the same.)

An alternative would be to keep define_cpu_feats! and the Feature enum, with each variant mapping to a cpufeature constant internally that would avoid touching call sites. I don't think that's worth it: we'd be bookkeeping the same CPUID leaf/register/bit data twice, and readers would have to mentally map two names (Feature::Xsave vs X86_FEATURE_XSAVE) for the same thing. Passing the crate constants directly keeps a single source of truth. wdyt?

Also PR is ready for review can you ptal?

if core::ptr::eq(feature, &HYPERV_INTERFACE) {
cpuid_field(feature) == Some(HYPERV_INTERFACE_SIGNATURE)
} else {
cpuid_field(feature) == Some(1)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or maybe we can consider the feature present if it is Some and not zero.

cpuid_field(feature).is_some_and(|f| f != 0)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this works as well

Comment thread kernel/src/cpu/sse.rs
Comment on lines +14 to +17
use cpufeature::leaves::XSAVE_SZ;
use cpufeature::leaves::{
X86_FEATURE_XMM, X86_FEATURE_XSAVE, X86_FEATURE_XSAVEOPT, XCR0_AVX, XCR0_SSE, XCR0_X87,
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These imports can be merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants