From 4475a0e96208480cc69cae477c5bad2476f6fc90 Mon Sep 17 00:00:00 2001 From: Sunil Muthuswamy Date: Mon, 27 Apr 2026 16:33:52 +0000 Subject: [PATCH 1/3] SNP: Allow lower VTL to get VMPCK --- openhcl/hcl/src/ioctl.rs | 12 +++++ .../virt_mshv_vtl/src/processor/snp/mod.rs | 44 ++++++++++++++++++- vm/hv1/hv1_hypercall/src/imp.rs | 16 +++++++ vm/hv1/hvdef/src/lib.rs | 10 +++++ vm/whp/src/abi.rs | 4 ++ vm/x86/x86defs/src/snp.rs | 9 ++++ 6 files changed, 94 insertions(+), 1 deletion(-) diff --git a/openhcl/hcl/src/ioctl.rs b/openhcl/hcl/src/ioctl.rs index 8e56b9269c..f64cd80f74 100755 --- a/openhcl/hcl/src/ioctl.rs +++ b/openhcl/hcl/src/ioctl.rs @@ -567,6 +567,7 @@ pub(crate) mod ioctls { pub const HCL_CAP_VTL_RETURN_ACTION: u32 = 2; pub const HCL_CAP_DR6_SHARED: u32 = 3; pub const HCL_CAP_LOWER_VTL_TIMER_VIRT: u32 = 4; + pub const HCL_CAP_LOWER_VTL_SNP_GUEST_REQUEST: u32 = 5; ioctl_write_ptr!( /// Check for the presence of an extension capability. @@ -1343,6 +1344,7 @@ pub struct Hcl { supports_register_page: bool, dr6_shared: bool, supports_lower_vtl_timer_virt: bool, + supports_lower_vtl_snp_guest_request: bool, isolation: IsolationType, snp_register_bitmap: [u8; 64], sidecar: Option, @@ -1383,6 +1385,12 @@ impl Hcl { pub fn supports_lower_vtl_timer_virt(&self) -> bool { self.supports_lower_vtl_timer_virt } + + /// Returns true if SNP guest requests from lower VTLs are intercepted and + /// forwarded to VTL2. + pub fn supports_lower_vtl_snp_guest_request(&self) -> bool { + self.supports_lower_vtl_snp_guest_request + } } #[derive(Debug)] @@ -1784,10 +1792,13 @@ impl Hcl { let dr6_shared = mshv_fd.check_extension(HCL_CAP_DR6_SHARED)?; let supports_lower_vtl_timer_virt = mshv_fd.check_extension(HCL_CAP_LOWER_VTL_TIMER_VIRT)?; + let supports_lower_vtl_snp_guest_request = + mshv_fd.check_extension(HCL_CAP_LOWER_VTL_SNP_GUEST_REQUEST)?; tracing::debug!( supports_vtl_ret_action, supports_register_page, supports_lower_vtl_timer_virt, + supports_lower_vtl_snp_guest_request, "HCL capabilities", ); @@ -1812,6 +1823,7 @@ impl Hcl { supports_register_page, dr6_shared, supports_lower_vtl_timer_virt, + supports_lower_vtl_snp_guest_request, isolation, snp_register_bitmap, sidecar, diff --git a/openhcl/virt_mshv_vtl/src/processor/snp/mod.rs b/openhcl/virt_mshv_vtl/src/processor/snp/mod.rs index 58c2ea54a3..d90ac0590f 100755 --- a/openhcl/virt_mshv_vtl/src/processor/snp/mod.rs +++ b/openhcl/virt_mshv_vtl/src/processor/snp/mod.rs @@ -73,6 +73,9 @@ use vmcore::vmtime::VmTimeAccess; use x86defs::RFlags; use x86defs::apic::X2APIC_MSR_BASE; use x86defs::cpuid::CpuidFunction; +use x86defs::snp::SNP_NUM_VMPCKS; +use x86defs::snp::SNP_SECRETS_VMPCK0_OFFSET; +use x86defs::snp::SNP_VMPCK_KEY_SIZE; use x86defs::snp::SevAvicIncompleteIpiInfo1; use x86defs::snp::SevAvicIncompleteIpiInfo2; use x86defs::snp::SevAvicNoAccelInfo; @@ -427,11 +430,18 @@ pub struct SnpBackedShared { #[inspect(skip)] guest_timer: hardware_cvm::VmTimeGuestTimer, secure_avic: bool, + /// VMPCK keys extracted from the SNP secrets page at partition initialization. + /// Indexed by VMPCK index (0-3), where each key is [`SNP_VMPCK_KEY_SIZE`] bytes. + #[inspect(skip)] + vmpck_keys: [[u8; SNP_VMPCK_KEY_SIZE]; SNP_NUM_VMPCKS], + /// Whether the hypervisor supports intercepting SNP guest requests from lower + /// VTLs and forwarding them to VTL2. + supports_lower_vtl_snp_guest_request: bool, } impl SnpBackedShared { pub(crate) fn new( - _partition_params: &UhPartitionNewParams<'_>, + partition_params: &UhPartitionNewParams<'_>, params: BackingSharedParams<'_>, ) -> Result { let cvm = params.cvm_state.unwrap(); @@ -460,6 +470,18 @@ impl SnpBackedShared { // Configure timer interface for lower VTLs. let guest_timer = hardware_cvm::VmTimeGuestTimer; + // Extract the four VMPCK keys from the SNP secrets page. + let vmpck_keys = if let Some(secrets) = partition_params.snp_secrets { + let mut keys = [[0u8; SNP_VMPCK_KEY_SIZE]; SNP_NUM_VMPCKS]; + for (i, key) in keys.iter_mut().enumerate() { + let offset = SNP_SECRETS_VMPCK0_OFFSET + i * SNP_VMPCK_KEY_SIZE; + key.copy_from_slice(&secrets[offset..offset + SNP_VMPCK_KEY_SIZE]); + } + keys + } else { + [[0u8; SNP_VMPCK_KEY_SIZE]; SNP_NUM_VMPCKS] + }; + Ok(Self { sev_status, invlpgb_count_max, @@ -467,6 +489,8 @@ impl SnpBackedShared { secure_avic, cvm, guest_timer, + vmpck_keys, + supports_lower_vtl_snp_guest_request: params.hcl.supports_lower_vtl_snp_guest_request(), }) } } @@ -954,6 +978,7 @@ impl UhHypercallHandler<'_, '_, SnpBacked> { hv1_hypercall::HvSendSyntheticClusterIpiEx, hv1_hypercall::HvInstallIntercept, hv1_hypercall::HvAssertVirtualInterrupt, + hv1_hypercall::HvGetSnpVmpck, ], ); @@ -3123,6 +3148,23 @@ impl TlbFlushLockAccess for SnpTlbLockFlushAccess<'_> { } } +impl hv1_hypercall::GetSnpVmpck for UhHypercallHandler<'_, '_, SnpBacked> { + fn get_snp_vmpck(&mut self) -> hvdef::HvResult { + if !self.vp.shared.supports_lower_vtl_snp_guest_request { + return Err(HvError::AccessDenied); + } + + // The VMPCK index corresponds to the VMPL of the calling VTL: + // VTL0 runs at VMPL2, VTL1 runs at VMPL1. + let index = match self.intercepted_vtl { + GuestVtl::Vtl0 => 2, + GuestVtl::Vtl1 => 1, + }; + let vmpck_key = self.vp.shared.vmpck_keys[index]; + Ok(hvdef::hypercall::GetSnpVmpckOutput { vmpck_key }) + } +} + mod save_restore { use super::SnpBacked; use super::UhProcessor; diff --git a/vm/hv1/hv1_hypercall/src/imp.rs b/vm/hv1/hv1_hypercall/src/imp.rs index 7a8a61c001..3701e1cdfb 100644 --- a/vm/hv1/hv1_hypercall/src/imp.rs +++ b/vm/hv1/hv1_hypercall/src/imp.rs @@ -389,6 +389,22 @@ impl HypercallDispatch; + +/// Implements the `HvGetSnpVmpck` hypercall. +pub trait GetSnpVmpck { + /// Returns the VMPCK (VM Platform Communication Key) for the calling VTL. + fn get_snp_vmpck(&mut self) -> HvResult; +} + +impl HypercallDispatch for T { + fn dispatch(&mut self, params: HypercallParameters<'_>) -> HypercallOutput { + HvGetSnpVmpck::run(params, |()| self.get_snp_vmpck()) + } +} + /// Defines the `HvGetVpRegisters` hypercall. pub type HvGetVpRegisters = RepHypercall< defs::GetSetVpRegisters, diff --git a/vm/hv1/hvdef/src/lib.rs b/vm/hv1/hvdef/src/lib.rs index 8e3f1ab21b..c927c03569 100755 --- a/vm/hv1/hvdef/src/lib.rs +++ b/vm/hv1/hvdef/src/lib.rs @@ -745,6 +745,7 @@ open_enum! { HvCallPinGpaPageRanges = 0x0112, HvCallUnpinGpaPageRanges = 0x0113, HvCallQuerySparseGpaPageHostVisibility = 0x011C, + HvCallGetSnpVmpck = 0x0134, // Extended hypercalls. HvExtCallQueryCapabilities = 0x8001, @@ -2487,6 +2488,15 @@ pub mod hypercall { pub reference_time_in_100_ns: u64, pub tsc: u64, } + + /// The size of a VMPCK key in bytes. + pub const SNP_VMPCK_KEY_SIZE: usize = 0x20; + + #[repr(C)] + #[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + pub struct GetSnpVmpckOutput { + pub vmpck_key: [u8; SNP_VMPCK_KEY_SIZE], + } } macro_rules! registers { diff --git a/vm/whp/src/abi.rs b/vm/whp/src/abi.rs index 55129dba9d..b3d48dab2d 100644 --- a/vm/whp/src/abi.rs +++ b/vm/whp/src/abi.rs @@ -567,6 +567,10 @@ impl WHV_SYNTHETIC_PROCESSOR_FEATURES { /// SPIs are advertised to VTL2. #[cfg(target_arch = "aarch64")] pub const ManagementVtlSpiSupport: Self = Self(1 << 44); + + // Hypervisor supports for lower VTLs to make guest requests. + #[cfg(target_arch = "x86_64")] + pub const LowerVtlGuestRequestSupport: Self = Self(1 << 46); } #[repr(C)] diff --git a/vm/x86/x86defs/src/snp.rs b/vm/x86/x86defs/src/snp.rs index f389541dfb..f91f06c2ba 100644 --- a/vm/x86/x86defs/src/snp.rs +++ b/vm/x86/x86defs/src/snp.rs @@ -21,6 +21,15 @@ pub const SEV_INTR_TYPE_SW: u32 = 4; pub const REG_TWEAK_BITMAP_OFFSET: usize = 0x100; pub const REG_TWEAK_BITMAP_SIZE: usize = 0x40; +// VMPCK key offsets within the SNP secrets page. +// See AMD SEV-SNP Firmware ABI Specification, Table 11 (Guest-Secrets Page Layout). +pub const SNP_NUM_VMPCKS: usize = 4; +pub const SNP_SECRETS_VMPCK0_OFFSET: usize = 0x20; +pub const SNP_SECRETS_VMPCK1_OFFSET: usize = 0x40; +pub const SNP_SECRETS_VMPCK2_OFFSET: usize = 0x60; +pub const SNP_SECRETS_VMPCK3_OFFSET: usize = 0x80; +pub const SNP_VMPCK_KEY_SIZE: usize = 0x20; + /// Value for the `msg_version` member in [`SNP_GUEST_REQ_MSG_VERSION`]. /// Use 1 for now. pub const SNP_GUEST_REQ_MSG_VERSION: u32 = 1; From 5440cfdff97cc04f800e3d23f2ea5d4c7a2a7326 Mon Sep 17 00:00:00 2001 From: Sunil Muthuswamy Date: Wed, 29 Apr 2026 00:16:31 +0000 Subject: [PATCH 2/3] Refactor cpuid into a separate crate --- Cargo.lock | 14 ++++- Cargo.toml | 1 + openhcl/hcl/Cargo.toml | 1 + openhcl/hcl/src/ioctl.rs | 24 ++++--- openhcl/openhcl_cpuid_features/Cargo.toml | 15 +++++ openhcl/openhcl_cpuid_features/src/lib.rs | 62 +++++++++++++++++++ openhcl/underhill_core/Cargo.toml | 3 +- openhcl/underhill_core/src/worker.rs | 37 ++++------- openhcl/virt_mshv_vtl/Cargo.toml | 1 + openhcl/virt_mshv_vtl/src/lib.rs | 6 ++ .../virt_mshv_vtl/src/processor/snp/mod.rs | 12 ++-- vm/hv1/hvdef/src/lib.rs | 13 +++- vm/whp/src/abi.rs | 4 -- 13 files changed, 144 insertions(+), 49 deletions(-) create mode 100644 openhcl/openhcl_cpuid_features/Cargo.toml create mode 100644 openhcl/openhcl_cpuid_features/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 427cc83813..495bcd3840 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3058,6 +3058,7 @@ dependencies = [ "memory_range", "nix 0.30.1", "open_enum", + "openhcl_cpuid_features", "pal", "parking_lot", "safe_intrinsics", @@ -5095,6 +5096,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "openhcl_cpuid_features" +version = "0.0.0" +dependencies = [ + "hvdef", + "safe_intrinsics", + "x86defs", +] + [[package]] name = "openhcl_dma_manager" version = "0.0.0" @@ -8388,13 +8398,13 @@ dependencies = [ "nvme_resources", "nvme_spec", "openhcl_attestation_protocol", + "openhcl_cpuid_features", "openhcl_dma_manager", "pal", "pal_async", "pal_uring", "parking_lot", "profiler_worker", - "safe_intrinsics", "scsi_buffers", "scsi_core", "scsidisk", @@ -8461,7 +8471,6 @@ dependencies = [ "vpci_relay", "watchdog_core", "watchdog_vmgs_format", - "x86defs", "zerocopy", ] @@ -9019,6 +9028,7 @@ dependencies = [ "memory_range", "mesh", "minircu", + "openhcl_cpuid_features", "pal", "pal_async", "pal_uring", diff --git a/Cargo.toml b/Cargo.toml index 2fc5b515c9..dbafc0ff01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -195,6 +195,7 @@ minimal_rt_build = { path = "openhcl/minimal_rt_build" } minimal_rt_reloc = { path = "openhcl/minimal_rt_reloc" } mem_profile_tracing = { path = "openhcl/mem_profile_tracing" } openhcl_dma_manager = { path = "openhcl/openhcl_dma_manager" } +openhcl_cpuid_features = { path = "openhcl/openhcl_cpuid_features" } sidecar_client = { path = "openhcl/sidecar_client" } sidecar_defs = { path = "openhcl/sidecar_defs" } tee_call = { path = "openhcl/tee_call" } diff --git a/openhcl/hcl/Cargo.toml b/openhcl/hcl/Cargo.toml index 83c09df290..188779816e 100644 --- a/openhcl/hcl/Cargo.toml +++ b/openhcl/hcl/Cargo.toml @@ -12,6 +12,7 @@ hv1_structs.workspace = true hvdef.workspace = true pal.workspace = true memory_range.workspace = true +openhcl_cpuid_features.workspace = true sidecar_client.workspace = true tdcall = { workspace = true, features = ["tracing"] } x86defs.workspace = true diff --git a/openhcl/hcl/src/ioctl.rs b/openhcl/hcl/src/ioctl.rs index f64cd80f74..26bd9d4667 100755 --- a/openhcl/hcl/src/ioctl.rs +++ b/openhcl/hcl/src/ioctl.rs @@ -567,7 +567,6 @@ pub(crate) mod ioctls { pub const HCL_CAP_VTL_RETURN_ACTION: u32 = 2; pub const HCL_CAP_DR6_SHARED: u32 = 3; pub const HCL_CAP_LOWER_VTL_TIMER_VIRT: u32 = 4; - pub const HCL_CAP_LOWER_VTL_SNP_GUEST_REQUEST: u32 = 5; ioctl_write_ptr!( /// Check for the presence of an extension capability. @@ -1344,10 +1343,10 @@ pub struct Hcl { supports_register_page: bool, dr6_shared: bool, supports_lower_vtl_timer_virt: bool, - supports_lower_vtl_snp_guest_request: bool, isolation: IsolationType, snp_register_bitmap: [u8; 64], sidecar: Option, + cpuid_features: openhcl_cpuid_features::CpuidFeatures, } /// The isolation type for a partition. @@ -1386,10 +1385,9 @@ impl Hcl { self.supports_lower_vtl_timer_virt } - /// Returns true if SNP guest requests from lower VTLs are intercepted and - /// forwarded to VTL2. - pub fn supports_lower_vtl_snp_guest_request(&self) -> bool { - self.supports_lower_vtl_snp_guest_request + /// Returns cached host CPUID feature information. + pub fn cpuid_features(&self) -> &openhcl_cpuid_features::CpuidFeatures { + &self.cpuid_features } } @@ -1735,7 +1733,12 @@ impl<'a, T: Backing<'a>> ProcessorRunner<'a, T> { impl Hcl { /// Returns a new HCL instance. - pub fn new(isolation: IsolationType, sidecar: Option) -> Result { + #[cfg(guest_arch = "x86_64")] // xtask-fmt allow-target-arch cpu-intrinsic + pub fn new( + isolation: IsolationType, + sidecar: Option, + cpuid_features: openhcl_cpuid_features::CpuidFeatures, + ) -> Result { static SIGNAL_HANDLER_INIT: Once = Once::new(); // SAFETY: The signal handler does not perform any actions that are forbidden // for signal handlers to perform, as it performs nothing. @@ -1792,8 +1795,11 @@ impl Hcl { let dr6_shared = mshv_fd.check_extension(HCL_CAP_DR6_SHARED)?; let supports_lower_vtl_timer_virt = mshv_fd.check_extension(HCL_CAP_LOWER_VTL_TIMER_VIRT)?; + + // Use CPUID features from caller let supports_lower_vtl_snp_guest_request = - mshv_fd.check_extension(HCL_CAP_LOWER_VTL_SNP_GUEST_REQUEST)?; + cpuid_features.supports_lower_vtl_guest_request(); + tracing::debug!( supports_vtl_ret_action, supports_register_page, @@ -1823,10 +1829,10 @@ impl Hcl { supports_register_page, dr6_shared, supports_lower_vtl_timer_virt, - supports_lower_vtl_snp_guest_request, isolation, snp_register_bitmap, sidecar, + cpuid_features, }) } diff --git a/openhcl/openhcl_cpuid_features/Cargo.toml b/openhcl/openhcl_cpuid_features/Cargo.toml new file mode 100644 index 0000000000..5a38be119a --- /dev/null +++ b/openhcl/openhcl_cpuid_features/Cargo.toml @@ -0,0 +1,15 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +[package] +name = "openhcl_cpuid_features" +edition.workspace = true +rust-version.workspace = true + +[target.'cfg(target_arch = "x86_64")'.dependencies] +hvdef.workspace = true +x86defs.workspace = true +safe_intrinsics.workspace = true + +[lints] +workspace = true diff --git a/openhcl/openhcl_cpuid_features/src/lib.rs b/openhcl/openhcl_cpuid_features/src/lib.rs new file mode 100644 index 0000000000..998351de0c --- /dev/null +++ b/openhcl/openhcl_cpuid_features/src/lib.rs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +//! Shared CPUID feature detection for OpenHCL components. + +// xtask-fmt allow-target-arch cpu-intrinsic +#![cfg(target_arch = "x86_64")] + +use hvdef::HvEnlightenmentInformation; +use x86defs::cpuid::VersionAndFeaturesEcx; + +/// Cached CPUID feature detection results. +#[derive(Clone, Copy, Debug)] +pub struct CpuidFeatures { + enlightenment_info: HvEnlightenmentInformation, + version_features: VersionAndFeaturesEcx, +} + +impl CpuidFeatures { + /// Queries host CPUID and builds a snapshot of relevant feature leaves. + pub fn new() -> Self { + let result = + safe_intrinsics::cpuid(hvdef::HV_CPUID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION, 0); + let enlightenment_info = HvEnlightenmentInformation::from_cpuid([ + result.eax, result.ebx, result.ecx, result.edx, + ]); + + let result = safe_intrinsics::cpuid(x86defs::cpuid::CpuidFunction::VersionAndFeatures.0, 0); + let version_features = VersionAndFeaturesEcx::from(result.ecx); + + Self { + enlightenment_info, + version_features, + } + } + + /// Returns whether Hyper-V recommends MMIO access hypercalls. + pub fn use_hypercall_for_mmio_access(&self) -> bool { + self.enlightenment_info.use_hypercall_for_mmio_access() + } + + /// Returns whether x2APIC is supported by the host CPU. + pub fn x2_apic_supported(&self) -> bool { + self.version_features.x2_apic() + } + + /// Returns whether lower VTL guest request interception is supported. + pub fn supports_lower_vtl_guest_request(&self) -> bool { + self.enlightenment_info.lower_vtl_guest_request_support() + } + + /// Returns whether restore partition time on resume is supported. + pub fn supports_restore_partition_time(&self) -> bool { + self.enlightenment_info.restore_time_on_resume() + } +} + +impl Default for CpuidFeatures { + fn default() -> Self { + Self::new() + } +} diff --git a/openhcl/underhill_core/Cargo.toml b/openhcl/underhill_core/Cargo.toml index bfb96bd36f..a41d2c2e49 100644 --- a/openhcl/underhill_core/Cargo.toml +++ b/openhcl/underhill_core/Cargo.toml @@ -78,6 +78,7 @@ netvsp.workspace = true nvme_driver.workspace = true nvme_resources.workspace = true openhcl_dma_manager.workspace = true +openhcl_cpuid_features.workspace = true scsi_core.workspace = true scsidisk.workspace = true scsidisk_resources.workspace = true @@ -133,9 +134,7 @@ vmgs = { workspace = true, features = ["encryption", "save_restore"] } vmgs_broker = { workspace = true, features = ["encryption"] } vmgs_format.workspace = true vmgs_resources.workspace = true -x86defs.workspace = true -safe_intrinsics.workspace = true debug_ptr.workspace = true guid.workspace = true crypto = { workspace = true, features = ["vendored", "openssl"] } diff --git a/openhcl/underhill_core/src/worker.rs b/openhcl/underhill_core/src/worker.rs index df511c89e5..6d27761359 100644 --- a/openhcl/underhill_core/src/worker.rs +++ b/openhcl/underhill_core/src/worker.rs @@ -1591,21 +1591,15 @@ async fn new_underhill_vm( let uevent_listener = Arc::new(UeventListener::new(tp.driver(0)).context("failed to start uevent listener")?); - let use_mmio_hypercalls = dps.general.always_relay_host_mmio; - // TODO: Centralize cpuid based feature determination. + // Initialize CPUID features once at worker startup. + #[cfg(guest_arch = "x86_64")] // xtask-fmt allow-target-arch cpu-intrinsic + let cpuid_features = openhcl_cpuid_features::CpuidFeatures::new(); + #[cfg(guest_arch = "x86_64")] - let use_mmio_hypercalls = use_mmio_hypercalls - || hardware_isolated && { - let result = - safe_intrinsics::cpuid(hvdef::HV_CPUID_FUNCTION_MS_HV_ENLIGHTENMENT_INFORMATION, 0); - hvdef::HvEnlightenmentInformation::from( - result.eax as u128 - | (result.ebx as u128) << 32 - | (result.ecx as u128) << 64 - | (result.edx as u128) << 96, - ) - .use_hypercall_for_mmio_access() - }; + let use_mmio_hypercalls = dps.general.always_relay_host_mmio + || (hardware_isolated && cpuid_features.use_hypercall_for_mmio_access()); + #[cfg(not(guest_arch = "x86_64"))] + let use_mmio_hypercalls = dps.general.always_relay_host_mmio; let boot_info = runtime_params.parsed_openhcl_boot(); @@ -1713,21 +1707,14 @@ async fn new_underhill_vm( // boot rather than allowing the host to make that decision. This would // just require Underhill setting the apicbase register on the VPs // before start. - // - // TODO: centralize cpuid querying logic. #[cfg(guest_arch = "x86_64")] let x2apic = if isolation.is_hardware_isolated() && !hide_isolation { // For hardware CVMs, always enable x2apic support at boot. vm_topology::processor::x86::X2ApicState::Enabled + } else if cpuid_features.x2_apic_supported() { + vm_topology::processor::x86::X2ApicState::Supported } else { - let features = x86defs::cpuid::VersionAndFeaturesEcx::from( - safe_intrinsics::cpuid(x86defs::cpuid::CpuidFunction::VersionAndFeatures.0, 0).ecx, - ); - if features.x2_apic() { - vm_topology::processor::x86::X2ApicState::Supported - } else { - vm_topology::processor::x86::X2ApicState::Unsupported - } + vm_topology::processor::x86::X2ApicState::Unsupported }; #[cfg(guest_arch = "x86_64")] @@ -1871,6 +1858,8 @@ async fn new_underhill_vm( hide_isolation, disable_proxy_redirect: env_cfg.disable_proxy_redirect, disable_lower_vtl_timer_virt: env_cfg.disable_lower_vtl_timer_virt, + #[cfg(guest_arch = "x86_64")] // xtask-fmt allow-target-arch cpu-intrinsic + cpuid_features, }; let proto_partition = UhProtoPartition::new(params, |cpu| tp.driver(cpu).clone()) diff --git a/openhcl/virt_mshv_vtl/Cargo.toml b/openhcl/virt_mshv_vtl/Cargo.toml index 0be8fcf3af..b44c92c7a2 100644 --- a/openhcl/virt_mshv_vtl/Cargo.toml +++ b/openhcl/virt_mshv_vtl/Cargo.toml @@ -11,6 +11,7 @@ gdb = [] [target.'cfg(target_os = "linux")'.dependencies] hcl.workspace = true +openhcl_cpuid_features.workspace = true virt.workspace = true virt_support_apic.workspace = true virt_support_x86emu.workspace = true diff --git a/openhcl/virt_mshv_vtl/src/lib.rs b/openhcl/virt_mshv_vtl/src/lib.rs index 60ffabb807..e1144bbfe0 100755 --- a/openhcl/virt_mshv_vtl/src/lib.rs +++ b/openhcl/virt_mshv_vtl/src/lib.rs @@ -1440,6 +1440,9 @@ pub struct UhPartitionNewParams<'a> { pub disable_proxy_redirect: bool, /// Disable lower VTL timer virtualization. pub disable_lower_vtl_timer_virt: bool, + /// Cached CPUID features queried once at worker startup. + #[cfg(guest_arch = "x86_64")] // xtask-fmt allow-target-arch cpu-intrinsic + pub cpuid_features: openhcl_cpuid_features::CpuidFeatures, } /// Parameters to [`UhProtoPartition::build`]. @@ -1610,6 +1613,9 @@ impl<'a> UhProtoPartition<'a> { // Try to open the sidecar device, if it is present. let sidecar = sidecar_client::SidecarClient::new(driver).map_err(Error::Sidecar)?; + #[cfg(guest_arch = "x86_64")] // xtask-fmt allow-target-arch cpu-intrinsic + let hcl = Hcl::new(hcl_isolation, sidecar, params.cpuid_features).map_err(Error::Hcl)?; + #[cfg(not(guest_arch = "x86_64"))] // xtask-fmt allow-target-arch cpu-intrinsic let hcl = Hcl::new(hcl_isolation, sidecar).map_err(Error::Hcl)?; // Set the hypercalls that this process will use. diff --git a/openhcl/virt_mshv_vtl/src/processor/snp/mod.rs b/openhcl/virt_mshv_vtl/src/processor/snp/mod.rs index d90ac0590f..35ae89eb94 100755 --- a/openhcl/virt_mshv_vtl/src/processor/snp/mod.rs +++ b/openhcl/virt_mshv_vtl/src/processor/snp/mod.rs @@ -434,9 +434,6 @@ pub struct SnpBackedShared { /// Indexed by VMPCK index (0-3), where each key is [`SNP_VMPCK_KEY_SIZE`] bytes. #[inspect(skip)] vmpck_keys: [[u8; SNP_VMPCK_KEY_SIZE]; SNP_NUM_VMPCKS], - /// Whether the hypervisor supports intercepting SNP guest requests from lower - /// VTLs and forwarding them to VTL2. - supports_lower_vtl_snp_guest_request: bool, } impl SnpBackedShared { @@ -490,7 +487,6 @@ impl SnpBackedShared { cvm, guest_timer, vmpck_keys, - supports_lower_vtl_snp_guest_request: params.hcl.supports_lower_vtl_snp_guest_request(), }) } } @@ -3150,7 +3146,13 @@ impl TlbFlushLockAccess for SnpTlbLockFlushAccess<'_> { impl hv1_hypercall::GetSnpVmpck for UhHypercallHandler<'_, '_, SnpBacked> { fn get_snp_vmpck(&mut self) -> hvdef::HvResult { - if !self.vp.shared.supports_lower_vtl_snp_guest_request { + if !self + .vp + .partition + .hcl + .cpuid_features() + .supports_lower_vtl_guest_request() + { return Err(HvError::AccessDenied); } diff --git a/vm/hv1/hvdef/src/lib.rs b/vm/hv1/hvdef/src/lib.rs index c927c03569..7fe8808d8b 100755 --- a/vm/hv1/hvdef/src/lib.rs +++ b/vm/hv1/hvdef/src/lib.rs @@ -621,13 +621,20 @@ pub struct HvEnlightenmentInformation { pub use_hypercall_for_mmio_access: bool, pub use_gpa_pinning_hypercall: bool, pub wake_vps: bool, - _reserved: u8, + pub proxy_interrupt_doorbell_support: bool, + pub memory_type_locking_support: bool, + pub map_partition_event_log_buffer: bool, + pub lower_vtl_guest_request_support: bool, + pub heat_hint_beneficial_support: bool, + pub ring_buffer_message_port_support: bool, + _reserved1: bool, + _reserved2: bool, pub long_spin_wait_count: u32, #[bits(7)] pub implemented_physical_address_bits: u32, #[bits(25)] - _reserved1: u32, - _reserved2: u32, + _reserved3: u32, + _reserved4: u32, } impl HvEnlightenmentInformation { diff --git a/vm/whp/src/abi.rs b/vm/whp/src/abi.rs index b3d48dab2d..55129dba9d 100644 --- a/vm/whp/src/abi.rs +++ b/vm/whp/src/abi.rs @@ -567,10 +567,6 @@ impl WHV_SYNTHETIC_PROCESSOR_FEATURES { /// SPIs are advertised to VTL2. #[cfg(target_arch = "aarch64")] pub const ManagementVtlSpiSupport: Self = Self(1 << 44); - - // Hypervisor supports for lower VTLs to make guest requests. - #[cfg(target_arch = "x86_64")] - pub const LowerVtlGuestRequestSupport: Self = Self(1 << 46); } #[repr(C)] From 5592161e6df875f9c9ad5bafb1c0301b069c64af Mon Sep 17 00:00:00 2001 From: Sunil Muthuswamy Date: Thu, 30 Apr 2026 01:37:46 +0000 Subject: [PATCH 3/3] No debug for VMPCK --- vm/hv1/hvdef/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vm/hv1/hvdef/src/lib.rs b/vm/hv1/hvdef/src/lib.rs index 7fe8808d8b..543f164614 100755 --- a/vm/hv1/hvdef/src/lib.rs +++ b/vm/hv1/hvdef/src/lib.rs @@ -2500,7 +2500,7 @@ pub mod hypercall { pub const SNP_VMPCK_KEY_SIZE: usize = 0x20; #[repr(C)] - #[derive(Copy, Clone, Debug, IntoBytes, Immutable, KnownLayout, FromBytes)] + #[derive(Copy, Clone, IntoBytes, Immutable, KnownLayout, FromBytes)] pub struct GetSnpVmpckOutput { pub vmpck_key: [u8; SNP_VMPCK_KEY_SIZE], }