Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3058,6 +3058,7 @@ dependencies = [
"memory_range",
"nix 0.30.1",
"open_enum",
"openhcl_cpuid_features",
"pal",
"parking_lot",
"safe_intrinsics",
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -8461,7 +8471,6 @@ dependencies = [
"vpci_relay",
"watchdog_core",
"watchdog_vmgs_format",
"x86defs",
"zerocopy",
]

Expand Down Expand Up @@ -9019,6 +9028,7 @@ dependencies = [
"memory_range",
"mesh",
"minircu",
"openhcl_cpuid_features",
"pal",
"pal_async",
"pal_uring",
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
1 change: 1 addition & 0 deletions openhcl/hcl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment on lines 13 to 16
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

openhcl_cpuid_features is an x86_64-only crate (cfg(target_arch = "x86_64")). Adding it as an unconditional Linux dependency for hcl can cause non-x86_64 Linux builds to fail unless the code is fully cfg-gated or the crate provides non-x86 stubs. Consider moving this dependency under a cfg(target_arch = "x86_64") target section (or otherwise ensuring non-x86 builds don’t pull in/use this crate).

Copilot uses AI. Check for mistakes.
tdcall = { workspace = true, features = ["tracing"] }
x86defs.workspace = true
Expand Down
20 changes: 19 additions & 1 deletion openhcl/hcl/src/ioctl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1346,6 +1346,7 @@ pub struct Hcl {
isolation: IsolationType,
snp_register_bitmap: [u8; 64],
sidecar: Option<SidecarClient>,
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

Hcl now stores cpuid_features: openhcl_cpuid_features::CpuidFeatures, but openhcl_cpuid_features is cfg(target_arch = "x86_64") only. As written, this will fail to compile hcl on non-x86_64 targets (or any build where that type isn’t available). Gate this field + accessor behind an appropriate cfg, or provide a non-x86_64 stub type/API in openhcl_cpuid_features so Hcl remains portable.

Suggested change
sidecar: Option<SidecarClient>,
sidecar: Option<SidecarClient>,
#[cfg(target_arch = "x86_64")]

Copilot uses AI. Check for mistakes.
cpuid_features: openhcl_cpuid_features::CpuidFeatures,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we need to store this? It looks like we're only checking it once in the constructor?

}

/// The isolation type for a partition.
Expand Down Expand Up @@ -1383,6 +1384,11 @@ impl Hcl {
pub fn supports_lower_vtl_timer_virt(&self) -> bool {
self.supports_lower_vtl_timer_virt
}

/// Returns cached host CPUID feature information.
pub fn cpuid_features(&self) -> &openhcl_cpuid_features::CpuidFeatures {
&self.cpuid_features
}
}

#[derive(Debug)]
Expand Down Expand Up @@ -1727,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<SidecarClient>) -> Result<Hcl, Error> {
#[cfg(guest_arch = "x86_64")] // xtask-fmt allow-target-arch cpu-intrinsic
pub fn new(
isolation: IsolationType,
sidecar: Option<SidecarClient>,
Comment on lines +1736 to +1739
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

Hcl::new is now only defined under #[cfg(guest_arch = "x86_64")], but there are call sites (e.g. the #[cfg(not(guest_arch = "x86_64"))] path in virt_mshv_vtl) that still expect an Hcl::new(isolation, sidecar) constructor. Add a complementary #[cfg(not(guest_arch = "x86_64"))] constructor (or refactor to a single signature) so non-x86_64 guest builds continue to compile.

Suggested change
#[cfg(guest_arch = "x86_64")] // xtask-fmt allow-target-arch cpu-intrinsic
pub fn new(
isolation: IsolationType,
sidecar: Option<SidecarClient>,
pub fn new(
isolation: IsolationType,
sidecar: Option<SidecarClient>,
#[cfg(guest_arch = "x86_64")] // xtask-fmt allow-target-arch cpu-intrinsic

Copilot uses AI. Check for mistakes.
cpuid_features: openhcl_cpuid_features::CpuidFeatures,
) -> Result<Hcl, Error> {
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.
Expand Down Expand Up @@ -1784,10 +1795,16 @@ 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 =
cpuid_features.supports_lower_vtl_guest_request();

tracing::debug!(
supports_vtl_ret_action,
supports_register_page,
supports_lower_vtl_timer_virt,
supports_lower_vtl_snp_guest_request,
"HCL capabilities",
);

Expand Down Expand Up @@ -1815,6 +1832,7 @@ impl Hcl {
isolation,
snp_register_bitmap,
sidecar,
cpuid_features,
})
}

Expand Down
15 changes: 15 additions & 0 deletions openhcl/openhcl_cpuid_features/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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
62 changes: 62 additions & 0 deletions openhcl/openhcl_cpuid_features/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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()
}
}
3 changes: 1 addition & 2 deletions openhcl/underhill_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"] }
Expand Down
37 changes: 13 additions & 24 deletions openhcl/underhill_core/src/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This PR took the liberty to centralize the cpuid querying logic (as the PR also needed to make a cpuid call). If the general direction is right, I can try to converge other HCL cpuid calls into the cpuid crate in this PR or a subsequent PR.

#[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")]
Expand Down Expand Up @@ -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())
Expand Down
1 change: 1 addition & 0 deletions openhcl/virt_mshv_vtl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions openhcl/virt_mshv_vtl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`].
Expand Down Expand Up @@ -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)?;
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

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

The #[cfg(not(guest_arch = "x86_64"))] branch still calls Hcl::new(hcl_isolation, sidecar), but Hcl::new appears to be gated to guest_arch = "x86_64" only now. This will break non-x86_64 guest builds unless a non-x86_64 constructor is provided (or this call is updated to match the available API).

Suggested change
let hcl = Hcl::new(hcl_isolation, sidecar).map_err(Error::Hcl)?;
let hcl = Hcl::new(hcl_isolation, sidecar, params.cpuid_features).map_err(Error::Hcl)?;

Copilot uses AI. Check for mistakes.

// Set the hypercalls that this process will use.
Expand Down
Loading
Loading