From dac11ce566467ebc00d14f00030b6325dfb363d5 Mon Sep 17 00:00:00 2001 From: tanish111 Date: Wed, 8 Jul 2026 16:03:04 +0000 Subject: [PATCH] kernel: use cpufeature crate for CPUID feature detection 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 --- Cargo.lock | 6 + Cargo.toml | 3 + kernel/Cargo.toml | 1 + kernel/src/cpu/control_regs.rs | 15 ++- kernel/src/cpu/features.rs | 201 +++++++-------------------------- kernel/src/cpu/percpu.rs | 11 +- kernel/src/cpu/sse.rs | 24 ++-- kernel/src/hyperv/hv.rs | 4 +- kernel/src/platform/mod.rs | 17 +-- kernel/src/platform/native.rs | 26 ++--- kernel/src/platform/snp.rs | 39 ++++--- kernel/src/platform/tdp.rs | 9 +- kernel/src/sev/tlb.rs | 5 +- kernel/src/svsm.rs | 5 +- kernel/src/task/tasks.rs | 5 +- 15 files changed, 137 insertions(+), 234 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d13aaa0a8..dca4826a8e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -573,6 +573,11 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" +[[package]] +name = "cpufeature" +version = "0.1.0" +source = "git+https://github.com/coconut-svsm/cpufeature.git?rev=0182d61954372552fe2a40477340e949471a49ee#0182d61954372552fe2a40477340e949471a49ee" + [[package]] name = "cpufeatures" version = "0.2.17" @@ -2082,6 +2087,7 @@ dependencies = [ "cocoon-tpm-utils-common", "concat-kdf", "cpuarch", + "cpufeature", "elf", "gdbstub", "gdbstub_arch", diff --git a/Cargo.toml b/Cargo.toml index 2e8749012c..44098cf717 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -103,6 +103,9 @@ virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers.git", rev = zerocopy = { version = "0.8.2", features = ["derive"] } zeroize = { version = "1.8.2", default-features = false } +#Github repos +cpufeature = { git = "https://github.com/coconut-svsm/cpufeature.git", rev = "0182d61954372552fe2a40477340e949471a49ee" } + # Verus repos verus_builtin = { version = "=0.0.0-2026-04-12-0118", default-features = false } verus_builtin_macros = { version = "=0.0.0-2026-04-12-0118", features = ["vpanic"], default-features = false } diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index e9e17bcf9f..6a701dbdeb 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -16,6 +16,7 @@ doctest = true bootdefs.workspace = true bootimg.workspace = true cpuarch.workspace = true +cpufeature.workspace = true libaproxy = { workspace = true, optional = true } elf.workspace = true syscall.workspace = true diff --git a/kernel/src/cpu/control_regs.rs b/kernel/src/cpu/control_regs.rs index 9ec65c6a35..5a2417c670 100644 --- a/kernel/src/cpu/control_regs.rs +++ b/kernel/src/cpu/control_regs.rs @@ -5,10 +5,13 @@ // Author: Joerg Roedel use crate::address::{Address, PhysAddr}; -use crate::cpu::features::{Feature, cpu_has_feat}; +use crate::cpu::features::cpu_has_feat; use core::arch::asm; use cpuarch::x86::CR0Flags; use cpuarch::x86::CR4Flags; +use cpufeature::leaves::{ + CET_SS, X86_FEATURE_PGE, X86_FEATURE_SMAP, X86_FEATURE_SMEP, X86_FEATURE_UMIP, +}; #[inline] pub fn cr0_init() { @@ -33,25 +36,25 @@ pub fn cr4_init() { // All processors that are capable of virtualization will support global // page table entries, so there is no reason to support any processor that // does not enumerate PGE capability. - assert!(cpu_has_feat(Feature::Pge), "CPU does not support PGE"); + assert!(cpu_has_feat(&X86_FEATURE_PGE), "CPU does not support PGE"); cr4.insert(CR4Flags::PGE); // Enable Global Pages if !cfg!(feature = "nosmep") { - assert!(cpu_has_feat(Feature::Smep), "CPU does not support SMEP"); + assert!(cpu_has_feat(&X86_FEATURE_SMEP), "CPU does not support SMEP"); cr4.insert(CR4Flags::SMEP); } if !cfg!(feature = "nosmap") { - assert!(cpu_has_feat(Feature::Smap), "CPU does not support SMAP"); + assert!(cpu_has_feat(&X86_FEATURE_SMAP), "CPU does not support SMAP"); cr4.insert(CR4Flags::SMAP); } - if cpu_has_feat(Feature::Umip) { + if cpu_has_feat(&X86_FEATURE_UMIP) { cr4.insert(CR4Flags::UMIP); } - if cpu_has_feat(Feature::CetSS) { + if cpu_has_feat(&CET_SS) { cr4.insert(CR4Flags::CET); } diff --git a/kernel/src/cpu/features.rs b/kernel/src/cpu/features.rs index 6b2736210e..9364147999 100644 --- a/kernel/src/cpu/features.rs +++ b/kernel/src/cpu/features.rs @@ -5,170 +5,51 @@ // Authors: Joerg Roedel // Carlos López -use core::arch::x86_64::CpuidResult; - -use crate::{platform::cpuid, utils::immut_after_init::ImmutAfterInitCell}; - -/// The CPUID output register for a particular feature -#[derive(Clone, Copy, Debug)] -enum CpuidReg { - Eax, - Ebx, - Ecx, - Edx, -} - -/// A discoverable CPU feature. -/// -/// At the moment this type is used for CPUID detection, but it can -/// be expanded to use other sources of information. -#[derive(Debug)] -struct CpuFeat { - /// Raw value - val: ImmutAfterInitCell, - /// CPUID leaf - leaf: u32, - /// CPUID subleaf - subleaf: u32, - /// CPUID output register - reg: CpuidReg, - /// Bitshift to apply to raw value - shift: u8, - /// Bitmask to apply to raw value - bitsize: u8, - /// Expected value after shift + mask - expected: u32, -} - -impl CpuFeat { - /// Create a new feature, indicated by a single bit in the specified - /// register in the given CPUID leaf. - const fn new_bit(leaf: u32, reg: CpuidReg, bit: u8) -> Self { - Self::new(leaf, reg, bit, 1, 1) - } - - /// Create a new feature, indicated by the full value of a register - /// in the given CPUID leaf. - const fn new_u32(leaf: u32, reg: CpuidReg, expected: u32) -> Self { - Self::new(leaf, reg, 0, u32::BITS as u8, expected) - } - - /// Create a new CPU feature, detected by querying the given CPUID leaf, and applying - /// a bitshift and bitmask on the specified output register to compare it to the given - /// expected value. - const fn new(leaf: u32, reg: CpuidReg, shift: u8, bitsize: u8, expected: u32) -> Self { - // This method is only called from const context, so these assertions have no - // runtime effect. - assert!((shift as u32) < u32::BITS); - assert!((bitsize as u32) <= u32::BITS); - Self { - val: ImmutAfterInitCell::uninit(), - leaf, - subleaf: 0, - reg, - shift, - bitsize, - expected, - } - } - - /// Create a copy of the given CPU feature, but with the specified - /// subleaf. - const fn with_subfn(mut self, subleaf: u32) -> Self { - self.subleaf = subleaf; - self - } - - /// Get the CPUID register that corresponds to this feature - const fn get_reg(&self, cpuid: &CpuidResult) -> u32 { - match self.reg { - CpuidReg::Eax => cpuid.eax, - CpuidReg::Ebx => cpuid.ebx, - CpuidReg::Ecx => cpuid.ecx, - CpuidReg::Edx => cpuid.edx, - } - } - - const fn mask(&self) -> u32 { - ((1u64 << self.bitsize) - 1) as u32 - } - - fn get_or_init(&self) -> u32 { - if let Ok(val) = self.val.try_get_inner() { - return *val; - } - let val = cpuid(self.leaf, self.subleaf).map_or(0, |c| self.get_reg(&c)); - // If init() fails it means the cell got initialized by someone else - // concurrently, which is always benign. - let _ = self.val.init(val); - val - } - - /// Gets the raw value of this feature, lazily querying CPUID if - /// the value is not cached from a previous query - fn get(&self) -> u32 { - (self.get_or_init() >> self.shift) & self.mask() - } - - /// Checks whether this feature is available by comparing it to - /// its expected value, lazily querying CPUID if the value is not - /// cached from a previous query - fn enabled(&self) -> bool { - self.get() == self.expected - } -} - -/// Macro to generate a CPU feature lookup table -macro_rules! define_cpu_feats { - ( - $( - $variant:ident => $feat_expr:expr - ),* $(,)? - ) => { - - // Feature enum to use as index - #[repr(usize)] - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub enum Feature { - $( $variant, )* - } - - /// The number of defined features - const NUM_FEATS: usize = [ $( stringify!($variant) ),* ].len(); - - static CPU_FEATS: [CpuFeat; NUM_FEATS] = [ - $( $feat_expr, )* - ]; - }; +use crate::platform::cpuid; +use cpufeature::CpuidFeature; +use cpufeature::CpuidRegister; + +/// Raw EAX from CPUID leaf 0x8000_0008 — processor capacity parameters. +pub const PHYS_ADDR_SIZES: CpuidFeature = CpuidFeature { + leaf: 0x8000_0008, + subleaf: 0, + register: CpuidRegister::Eax, + shift: 0, + width: 32, +}; + +/// CPUID.4000_0001:EAX — Hyper-V interface signature ("Hv#1"). +pub const HYPERV_INTERFACE: CpuidFeature = CpuidFeature { + leaf: 0x4000_0001, + subleaf: 0, + register: CpuidRegister::Eax, + shift: 0, + width: 32, +}; + +const HYPERV_INTERFACE_SIGNATURE: u32 = 0x3123_7648; + +fn cpuid_field(feature: &CpuidFeature) -> Option { + cpuid(feature).map(|regs| { + let raw = feature + .register + .select(regs.eax, regs.ebx, regs.ecx, regs.edx); + let mask = ((1u64 << feature.width) as u32).wrapping_sub(1); + (raw >> feature.shift) & mask + }) } /// Check if the given feature is enabled by comparing it to its expected /// value. -pub fn cpu_has_feat(feat: Feature) -> bool { - // Because of #[repr(usize)], this cast matches the array index perfectly. - CPU_FEATS[feat as usize].enabled() -} - -/// Gets the raw value of the given feature -pub fn cpu_get_feat(feat: Feature) -> u32 { - CPU_FEATS[feat as usize].get() +pub fn cpu_has_feat(feature: &CpuidFeature) -> bool { + if core::ptr::eq(feature, &HYPERV_INTERFACE) { + cpuid_field(feature) == Some(HYPERV_INTERFACE_SIGNATURE) + } else { + cpuid_field(feature) == Some(1) + } } -define_cpu_feats! { - X2Apic => CpuFeat::new_bit(0x0000_0001, CpuidReg::Ecx, 21), - Xsave => CpuFeat::new_bit(0x0000_0001, CpuidReg::Ecx, 26), - Pge => CpuFeat::new_bit(0x0000_0001, CpuidReg::Edx, 13), - Sse1 => CpuFeat::new_bit(0x0000_0001, CpuidReg::Edx, 25), - Smep => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ebx, 7), - Smap => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ebx, 20), - Umip => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ecx, 2), - CetSS => CpuFeat::new_bit(0x0000_0007, CpuidReg::Ecx, 7), - Xcr0X87 => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 0), - Xcr0Sse => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 1), - Xcr0Avx => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 2), - XsaveOpt => CpuFeat::new_bit(0x0000_000d, CpuidReg::Eax, 0).with_subfn(1), - HyperV => CpuFeat::new_u32(0x40000001, CpuidReg::Eax, 0x31237648), - PhysAddrSizes => CpuFeat::new_u32(0x80000008, CpuidReg::Eax, 0), - InvlpgbMax => CpuFeat::new(0x80000008, CpuidReg::Edx, 0, u16::BITS as u8, 0), - Cbit => CpuFeat::new(0x8000001f, CpuidReg::Ebx, 0, 6, 0), +/// Gets the raw value of the given feature. +pub fn cpu_get_feat(feature: &CpuidFeature) -> u32 { + cpuid_field(feature).unwrap_or(0) } diff --git a/kernel/src/cpu/percpu.rs b/kernel/src/cpu/percpu.rs index 3f7a009832..86e4ac8893 100644 --- a/kernel/src/cpu/percpu.rs +++ b/kernel/src/cpu/percpu.rs @@ -6,7 +6,6 @@ extern crate alloc; -use super::features::{Feature, cpu_has_feat}; use super::gdt::GDT; use super::ipi::IpiState; use super::isst::Isst; @@ -16,6 +15,7 @@ use super::tss::{IST_DF, X86Tss}; use crate::address::{Address, PhysAddr, VirtAddr}; use crate::cpu::control_regs::{read_cr0, read_cr4}; use crate::cpu::efer::read_efer; +use crate::cpu::features::{HYPERV_INTERFACE, cpu_has_feat}; use crate::cpu::idt::common::INT_INJ_VECTOR; use crate::cpu::tss::TSS_LIMIT; use crate::cpu::vmsa::{init_guest_vmsa, init_svsm_vmsa}; @@ -75,6 +75,7 @@ use core::sync::atomic::AtomicU64; use core::sync::atomic::AtomicUsize; use core::sync::atomic::Ordering; use cpuarch::vmsa::VMSA; +use cpufeature::leaves::CET_SS; // PERCPU areas virtual addresses into shared memory pub static PERCPU_AREAS: PerCpuAreas = PerCpuAreas::new(); @@ -837,14 +838,14 @@ impl PerCpu { // Reserve ranges and initialize allocator for temporary mappings self.initialize_vm_ranges()?; - if cpu_has_feat(Feature::CetSS) { + if cpu_has_feat(&CET_SS) { self.allocate_init_shadow_stack()?; } // Allocate per-cpu context switch stack self.allocate_context_switch_stack()?; - if cpu_has_feat(Feature::CetSS) { + if cpu_has_feat(&CET_SS) { self.allocate_context_switch_shadow_stack()?; } @@ -854,7 +855,7 @@ impl PerCpu { // Setup TSS self.setup_tss(); - if cpu_has_feat(Feature::CetSS) { + if cpu_has_feat(&CET_SS) { // Allocate ISST shadow stacks self.allocate_isst_shadow_stacks()?; @@ -869,7 +870,7 @@ impl PerCpu { // Allocate hypercall pages if running on Hyper-V, unless this is the // BSP (where they will be allocated later). - if self.shared.cpu_index() != 0 && cpu_has_feat(Feature::HyperV) { + if self.shared.cpu_index() != 0 && cpu_has_feat(&HYPERV_INTERFACE) { self.allocate_hypercall_pages()?; } diff --git a/kernel/src/cpu/sse.rs b/kernel/src/cpu/sse.rs index 0778a5a219..494e7d51f3 100644 --- a/kernel/src/cpu/sse.rs +++ b/kernel/src/cpu/sse.rs @@ -5,13 +5,16 @@ // Author: Vasant Karasulli use crate::cpu::control_regs::{cr0_sse_enable, cr4_osfxsr_enable, cr4_xsave_enable}; +use crate::cpu::features::cpu_has_feat; use crate::platform::cpuid; use core::arch::asm; use core::arch::x86_64::{_xgetbv, _xsetbv}; use core::sync::atomic::{AtomicU64, Ordering}; - -use super::features::{Feature, cpu_has_feat}; - +use cpufeature::CpuidFeature; +use cpufeature::leaves::XSAVE_SZ; +use cpufeature::leaves::{ + X86_FEATURE_XMM, X86_FEATURE_XSAVE, X86_FEATURE_XSAVEOPT, XCR0_AVX, XCR0_SSE, XCR0_X87, +}; const XCR0_X87_ENABLE: u64 = 0x1; const XCR0_SSE_ENABLE: u64 = 0x2; const XCR0_YMM_ENABLE: u64 = 0x4; @@ -22,7 +25,7 @@ pub const XSAVE_LEGACY_SIZE: u32 = 0x240; static SVSM_XCR0: AtomicU64 = AtomicU64::new(XCR0_X87_ENABLE | XCR0_SSE_ENABLE); fn legacy_sse_enable() { - if cpu_has_feat(Feature::Sse1) { + if cpu_has_feat(&X86_FEATURE_XMM) { cr4_osfxsr_enable(); cr0_sse_enable(); } else { @@ -40,11 +43,8 @@ fn xcr0_set() { } fn xsave_enable() { - if cpu_has_feat(Feature::Xsave) && cpu_has_feat(Feature::XsaveOpt) { - if cpu_has_feat(Feature::Xcr0X87) - && cpu_has_feat(Feature::Xcr0Sse) - && cpu_has_feat(Feature::Xcr0Avx) - { + if cpu_has_feat(&X86_FEATURE_XSAVE) && cpu_has_feat(&X86_FEATURE_XSAVEOPT) { + if cpu_has_feat(&XCR0_X87) && cpu_has_feat(&XCR0_SSE) && cpu_has_feat(&XCR0_AVX) { SVSM_XCR0.fetch_or(XCR0_YMM_ENABLE, Ordering::Relaxed); } cr4_xsave_enable(); @@ -75,7 +75,11 @@ pub fn xsave_area_size() -> u32 { if xcr0 & (1u64 << bit) == 0 { continue; } - let Some(result) = cpuid(0xd, bit) else { + let feature = CpuidFeature { + subleaf: bit, + ..XSAVE_SZ + }; + let Some(result) = cpuid(&feature) else { log::warn!("FP feature {bit:#x} enabled but not present in CPUID"); continue; }; diff --git a/kernel/src/hyperv/hv.rs b/kernel/src/hyperv/hv.rs index 11211389cb..3c53812292 100644 --- a/kernel/src/hyperv/hv.rs +++ b/kernel/src/hyperv/hv.rs @@ -5,7 +5,7 @@ // Author: Jon Lange (jlange@microsoft.com) use crate::address::{PhysAddr, VirtAddr}; -use crate::cpu::features::{Feature, cpu_has_feat}; +use crate::cpu::features::{HYPERV_INTERFACE, cpu_has_feat}; use crate::cpu::mem::unsafe_copy_bytes; use crate::cpu::msr::write_msr; use crate::cpu::percpu::{PerCpu, this_cpu}; @@ -333,7 +333,7 @@ fn hyperv_setup_hypercalls() -> Result<(), SvsmError> { pub fn hyperv_setup() -> Result<(), SvsmError> { // First, determine if this is a Hyper-V system. - if cpu_has_feat(Feature::HyperV) { + if cpu_has_feat(&HYPERV_INTERFACE) { // If this is the BSP, then configure hypercall pages. this_cpu().allocate_hypercall_pages()?; diff --git a/kernel/src/platform/mod.rs b/kernel/src/platform/mod.rs index 465788004d..6d6e25899e 100644 --- a/kernel/src/platform/mod.rs +++ b/kernel/src/platform/mod.rs @@ -19,9 +19,11 @@ use snp::SnpPlatform; use tdp::TdpPlatform; use core::arch::asm; -use core::arch::x86_64::{__cpuid_count, CpuidResult}; +use core::arch::x86_64::CpuidResult; use core::fmt::Debug; use core::mem::MaybeUninit; +use cpufeature::CpuidFeature; +use cpufeature::backend::CpuidBackend; use crate::address::{PhysAddr, VirtAddr}; use crate::boot_params::BootParams; @@ -181,15 +183,6 @@ pub trait SvsmPlatform: Sync { hypercall_pages: &hyperv::HypercallPagesGuard<'_>, ) -> hyperv::HvHypercallOutput; - /// Obtain CPUID using platform-specific tables. - fn cpuid(eax: u32, ecx: u32) -> Option - where - Self: Sized, - { - // SAFETY: CPUID is always safe - unsafe { Some(__cpuid_count(eax, ecx)) } - } - /// Write a host-owned MSR. /// # Safety /// The caller must ensure that the requested MSR modification does mot @@ -368,8 +361,8 @@ macro_rules! platform_method { } #[inline] -pub fn cpuid(leaf: u32, subleaf: u32) -> Option { - platform_method!(cpuid, leaf, subleaf) +pub fn cpuid(feature: &CpuidFeature) -> Option { + platform_method!(cpuid, feature) } pub fn halt() { diff --git a/kernel/src/platform/native.rs b/kernel/src/platform/native.rs index 2daab58bd3..62adca6694 100644 --- a/kernel/src/platform/native.rs +++ b/kernel/src/platform/native.rs @@ -12,11 +12,10 @@ use super::PageStateChangeOp; use super::PageValidateOp; use super::SvsmPlatform; use super::capabilities::Caps; -use super::cpuid; use crate::address::{PhysAddr, VirtAddr}; use crate::console::init_svsm_console; use crate::cpu::IrqGuard; -use crate::cpu::features::{Feature, cpu_get_feat, cpu_has_feat}; +use crate::cpu::features::{HYPERV_INTERFACE, PHYS_ADDR_SIZES, cpu_get_feat, cpu_has_feat}; use crate::cpu::irq_state::raw_irqs_disable; use crate::cpu::msr::write_msr; use crate::cpu::percpu::PerCpu; @@ -34,10 +33,13 @@ use crate::types::PageSize; use crate::utils::MemoryRegion; use cpuarch::x86apic::ApicIcr; use cpuarch::x86apic::IcrMessageType; +use cpufeature::leaves::X86_FEATURE_X2APIC; use bootdefs::kernel_launch::BLDR_BASE; use core::arch::asm; use core::mem::MaybeUninit; +use cpufeature::backend::CpuidBackend; +use cpufeature::leaves::MAX_STD_LEAF; use syscall::GlobalFeatureFlags; #[cfg(debug_assertions)] @@ -52,13 +54,15 @@ pub struct NativePlatform {} impl NativePlatform { pub fn new(_suppress_svsm_interrupts: bool) -> Self { // Execution is not possible unless X2APIC is supported. - if !cpu_has_feat(Feature::X2Apic) { + if !cpu_has_feat(&X86_FEATURE_X2APIC) { panic!("X2APIC is not supported"); } Self {} } } +impl CpuidBackend for NativePlatform {} + impl SvsmPlatform for NativePlatform { #[cfg(test)] fn platform_type(&self) -> SvsmPlatformType { @@ -87,14 +91,10 @@ impl SvsmPlatform for NativePlatform { } fn get_cpu_vendor(&self) -> CpuVendor { - if let Some(r) = cpuid(0, 0) { - match r.edx { - 0x69746e65 => CpuVendor::AMD, - 0x49656e69 => CpuVendor::Intel, - _ => CpuVendor::Unknown, - } - } else { - CpuVendor::Unknown + match Self::cpuid(&MAX_STD_LEAF).map(|r| r.edx) { + Some(0x6974_6e65) => CpuVendor::AMD, + Some(0x4965_6e69) => CpuVendor::Intel, + _ => CpuVendor::Unknown, } } @@ -111,7 +111,7 @@ impl SvsmPlatform for NativePlatform { fn get_page_encryption_masks(&self) -> PageEncryptionMasks { // Find physical address size. - let phys_addr_sizes = cpu_get_feat(Feature::PhysAddrSizes); + let phys_addr_sizes = cpu_get_feat(&PHYS_ADDR_SIZES); PageEncryptionMasks { private_pte_mask: 0, shared_pte_mask: 0, @@ -224,7 +224,7 @@ impl SvsmPlatform for NativePlatform { ap_start_context_ref: Option<&ApStartContextRef>, ) -> Result<(), SvsmError> { let context = cpu.get_initial_context(start_rip); - if cpu_has_feat(Feature::HyperV) { + if cpu_has_feat(&HYPERV_INTERFACE) { return hyperv_start_cpu(cpu, &context); } diff --git a/kernel/src/platform/snp.rs b/kernel/src/platform/snp.rs index 2d443bbeed..bb2b9c3744 100644 --- a/kernel/src/platform/snp.rs +++ b/kernel/src/platform/snp.rs @@ -22,7 +22,7 @@ use crate::boot_params::BootParams; use crate::console::init_svsm_console; use crate::cpu::cpuid::cpuid_table; use crate::cpu::cpuid::init_cpuid_table; -use crate::cpu::features::{Feature, cpu_get_feat}; +use crate::cpu::features::{PHYS_ADDR_SIZES, cpu_get_feat}; use crate::cpu::irq_state::raw_irqs_disable; use crate::cpu::percpu::{PerCpu, current_ghcb, this_cpu}; use crate::cpu::smp::ApStartContextRef; @@ -58,6 +58,9 @@ use core::arch::x86_64::CpuidResult; use core::mem::MaybeUninit; use core::ptr; use core::sync::atomic::{AtomicU32, Ordering}; +use cpufeature::CpuidFeature; +use cpufeature::backend::CpuidBackend; +use cpufeature::leaves::PTE_CBIT_POS; use syscall::GlobalFeatureFlags; static GHCB_IO_DRIVER: GHCBIOPort = GHCBIOPort::new(); @@ -249,7 +252,7 @@ impl SvsmPlatform for SnpPlatform { fn get_page_encryption_masks(&self) -> PageEncryptionMasks { // Find physical address size. - let phys_addr_sizes = cpu_get_feat(Feature::PhysAddrSizes); + let phys_addr_sizes = cpu_get_feat(&PHYS_ADDR_SIZES); if vtom_enabled() { let vtom = *VTOM; PageEncryptionMasks { @@ -260,7 +263,7 @@ impl SvsmPlatform for SnpPlatform { } } else { // Find C-bit position. - let c_bit = cpu_get_feat(Feature::Cbit); + let c_bit = cpu_get_feat(&PTE_CBIT_POS); if c_bit == 0 { panic!("Cannot get C-Bit position from CPUID"); } @@ -296,20 +299,6 @@ impl SvsmPlatform for SnpPlatform { }) } - fn cpuid(eax: u32, ecx: u32) -> Option - where - Self: Sized, - { - // If this is an architectural CPUID leaf, then extract the result - // from the CPUID table. Otherwise, request the value from the - // hypervisor. - if (eax >> 28) == 4 { - current_ghcb().cpuid(eax, ecx).ok() - } else { - cpuid_table(eax, ecx) - } - } - unsafe fn write_host_msr(&self, msr: u32, value: u64) { current_ghcb() .wrmsr(msr, value) @@ -480,6 +469,22 @@ impl SvsmPlatform for SnpPlatform { } } +impl CpuidBackend for SnpPlatform { + fn cpuid(feature: &CpuidFeature) -> Option + where + Self: Sized, + { + // If this is an architectural CPUID leaf, then extract the result + // from the CPUID table. Otherwise, request the value from the + // hypervisor. + if (feature.leaf >> 28) == 4 { + current_ghcb().cpuid(feature.leaf, feature.subleaf).ok() + } else { + cpuid_table(feature.leaf, feature.subleaf) + } + } +} + #[derive(Clone, Copy, Debug, Default)] pub struct GHCBIOPort {} diff --git a/kernel/src/platform/tdp.rs b/kernel/src/platform/tdp.rs index 267c9eacb9..8bf409d9d3 100644 --- a/kernel/src/platform/tdp.rs +++ b/kernel/src/platform/tdp.rs @@ -12,7 +12,7 @@ use super::SvsmPlatform; use super::capabilities::Caps; use crate::address::{Address, PhysAddr, VirtAddr}; use crate::console::init_svsm_console; -use crate::cpu::features::{Feature, cpu_get_feat, cpu_has_feat}; +use crate::cpu::features::{HYPERV_INTERFACE, PHYS_ADDR_SIZES, cpu_get_feat, cpu_has_feat}; use crate::cpu::irq_state::raw_irqs_disable; use crate::cpu::percpu::PerCpu; use crate::cpu::smp::ApStartContextRef; @@ -37,6 +37,7 @@ use crate::utils::{MemoryRegion, is_aligned}; use bootdefs::tdp_start::TdpStartContext; use core::mem::MaybeUninit; use core::sync::atomic::Ordering; +use cpufeature::backend::CpuidBackend; use syscall::GlobalFeatureFlags; #[cfg(test)] @@ -54,6 +55,8 @@ impl TdpPlatform { } } +impl CpuidBackend for TdpPlatform {} + impl SvsmPlatform for TdpPlatform { #[cfg(test)] fn platform_type(&self) -> SvsmPlatformType { @@ -101,7 +104,7 @@ impl SvsmPlatform for TdpPlatform { fn get_page_encryption_masks(&self) -> PageEncryptionMasks { // Find physical address size. - let phys_addr_sizes = cpu_get_feat(Feature::PhysAddrSizes); + let phys_addr_sizes = cpu_get_feat(&PHYS_ADDR_SIZES); let vtom = *VTOM; PageEncryptionMasks { private_pte_mask: 0, @@ -261,7 +264,7 @@ impl SvsmPlatform for TdpPlatform { // When running under Hyper-V, the target vCPU does not begin running // until a start hypercall is issued, so make that hypercall now. - if cpu_has_feat(Feature::HyperV) { + if cpu_has_feat(&HYPERV_INTERFACE) { // Do not expose the actual CPU context via the hypercall since it // is not needed. Use a default context instead. let ctx = hyperv::HvInitialVpContext::default(); diff --git a/kernel/src/sev/tlb.rs b/kernel/src/sev/tlb.rs index 206efa83c7..ac5e6e854e 100644 --- a/kernel/src/sev/tlb.rs +++ b/kernel/src/sev/tlb.rs @@ -8,10 +8,11 @@ use bitfield_struct::bitfield; use crate::address::{Address, VirtAddr}; use crate::cpu::TlbFlushRange; -use crate::cpu::features::{Feature, cpu_get_feat}; +use crate::cpu::features::cpu_get_feat; use crate::cpu::tlb::TlbFlushScope; use crate::types::PageSize; use crate::utils::MemoryRegion; +use cpufeature::leaves::INVLPGB_MAX_PAGES; use core::arch::asm; @@ -71,7 +72,7 @@ fn flush_tlb_sync(global: bool) { fn flush_tlb_sync_range(global: bool, region: MemoryRegion, pgsize: PageSize) { // A value of 0 indicates a single page, so add 1 - let max_count = cpu_get_feat(Feature::InvlpgbMax) as usize + 1; + let max_count = cpu_get_feat(&INVLPGB_MAX_PAGES) as usize + 1; for start in region.iter_pages(pgsize).step_by(max_count) { // Take up to `max_count` pages diff --git a/kernel/src/svsm.rs b/kernel/src/svsm.rs index 195584e628..be5332cffb 100755 --- a/kernel/src/svsm.rs +++ b/kernel/src/svsm.rs @@ -14,6 +14,7 @@ use bootdefs::platform::SvsmPlatformType; use core::arch::global_asm; use core::panic::PanicInfo; use core::ptr::NonNull; +use cpufeature::leaves::CET_SS; use svsm::address::{Address, PhysAddr, VirtAddr}; #[cfg(feature = "attest")] use svsm::attest::AttestationDriver; @@ -22,7 +23,7 @@ use svsm::boot_params::BootParams; use svsm::console::install_console_logger; use svsm::cpu::control_regs::{cr0_init, cr4_init}; use svsm::cpu::cpuid::dump_cpuid_table; -use svsm::cpu::features::{Feature, cpu_has_feat}; +use svsm::cpu::features::cpu_has_feat; use svsm::cpu::gdt::GLOBAL_GDT; use svsm::cpu::idt::svsm::{early_idt_init, idt_init}; use svsm::cpu::idt::{EARLY_IDT_ENTRIES, IDT, IdtEntry}; @@ -440,7 +441,7 @@ unsafe extern "C" fn svsm_entry(li: *mut KernelLaunchInfo, platform_type: SvsmPl // Shadow stacks must be enabled once no further function returns are // possible. - if cpu_has_feat(Feature::CetSS) { + if cpu_has_feat(&CET_SS) { set_cet_ss_enabled(); let ssp_token_addr = ssp_token.unwrap(); enable_shadow_stacks!(ssp_token_addr); diff --git a/kernel/src/task/tasks.rs b/kernel/src/task/tasks.rs index 01a2443a9a..35515689bf 100644 --- a/kernel/src/task/tasks.rs +++ b/kernel/src/task/tasks.rs @@ -22,7 +22,7 @@ use crate::address::{Address, VirtAddr}; use crate::cpu::IrqGuard; use crate::cpu::ShadowStackInit; use crate::cpu::X86ExceptionContext; -use crate::cpu::features::{Feature, cpu_has_feat}; +use crate::cpu::features::cpu_has_feat; use crate::cpu::idt::svsm::return_new_task; use crate::cpu::irq_state::EFLAGS_IF; use crate::cpu::irqs_enable; @@ -48,6 +48,7 @@ use crate::platform::SVSM_PLATFORM; use crate::syscall::{Obj, ObjError, ObjHandle}; use crate::types::{SVSM_USER_CS, SVSM_USER_DS}; use crate::utils::{MemoryRegion, is_aligned}; +use cpufeature::leaves::CET_SS; use intrusive_collections::{LinkedListAtomicLink, intrusive_adapter}; use super::WaitQueue; @@ -421,7 +422,7 @@ impl Task { let mut shadow_stack_offset = VirtAddr::null(); let mut shadow_stack_base = VirtAddr::null(); - let shadow_stack_mapping = if cpu_has_feat(Feature::CetSS) { + let shadow_stack_mapping = if cpu_has_feat(&CET_SS) { // Allocate shadow stack and safe top_of_stack offset let shadow_stack = VMKernelStack::new_shadow()?; let offset = shadow_stack.top_of_stack();