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();