Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
03939e4
underhill_core: allow NVIDIA GPUs through the VPCI relay
robertschaedler3 Jul 8, 2026
86cdce6
flowey: add azurelocal OpenHCL IGVM build recipe
robertschaedler3 Jul 8, 2026
8d8f25b
underhill_core: rename GPU relay feature to nvidia_vpci_relay_allowed
robertschaedler3 Jul 9, 2026
b18d471
Merge branch 'main' of https://github.com/microsoft/openvmm into robe…
robertschaedler3 Jul 10, 2026
2bb0462
flowey: fix recipe name
robertschaedler3 Jul 10, 2026
202faa3
Merge branch 'main' into robertschaedler3/azurelocal-cgpu
robertschaedler3 Jul 27, 2026
2ae6d10
build_openhcl_igvm: Update uses_dev_kernel to include X64CvmNvidiaVpc…
robertschaedler3 Jul 31, 2026
1ab5b90
openhcl: gate NVIDIA VPCI relay on a host config flag
robertschaedler3 Aug 6, 2026
ed6dd8c
openhcl: derive the NVIDIA relay claim from the relay guard itself
robertschaedler3 Aug 10, 2026
089db8d
vpci_relay: ratelimit the host-triggerable device rejection warning
robertschaedler3 Aug 10, 2026
3de9c4b
vpci_relay: deny devices by default instead of allowing all when the …
robertschaedler3 Aug 10, 2026
365844a
Potential fix for pull request finding
robertschaedler3 Aug 10, 2026
e1a988b
openhcl: name the legacy vpci claim variable after the claim it popul…
robertschaedler3 Aug 10, 2026
8bf9e4f
Merge branch 'robertschaedler3/cgpu-vpci-relay-host-config' of https:…
robertschaedler3 Aug 10, 2026
25a002c
guest_emulation_transport: restore the nvidia_vpci_relay_allowed field
robertschaedler3 Aug 10, 2026
d33b88d
vpci_relay: make tracelimit an unconditional dependency
robertschaedler3 Aug 10, 2026
bcebb70
openhcl_attestation_protocol: declare the new claim last to preserve …
robertschaedler3 Aug 10, 2026
1b4a05f
openhcl: describe the NVIDIA relay claim as enablement, not device pr…
robertschaedler3 Aug 10, 2026
c5e3f0f
openvmm: add --nvidia-vpci-relay-allowed to set the host config locally
robertschaedler3 Aug 12, 2026
ae7ecf4
openhcl: allow OPENHCL_NVIDIA_VPCI_RELAY_ALLOWED to override the host…
robertschaedler3 Aug 12, 2026
da9dbdf
vpci_relay: test that the NVIDIA entries admit only NVIDIA GPUs and b…
robertschaedler3 Aug 12, 2026
7b453ea
openhcl: accept the NVIDIA relay opt-in via a DevicePolicy VTL2 setti…
robertschaedler3 Aug 13, 2026
b5b15e0
openhcl: warn when a runtime VTL2 settings update carries a device po…
robertschaedler3 Aug 13, 2026
8d615e4
openhcl: tolerate a UTF-8 BOM in the device policy payload
robertschaedler3 Aug 14, 2026
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
19 changes: 19 additions & 0 deletions openhcl/openhcl_attestation_protocol/src/igvm_attest/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,24 @@ pub mod runtime_claims {
pub vmgs_provisioner: Option<VmgsProvisioner>,
/// Hardware sealing policy
pub hardware_sealing_policy: HardwareSealingPolicy,
/// Whether NVIDIA GPUs and NVLink/NVSwitch fabric devices are permitted
/// through the device filter for this guest.
///
/// This reports that the filter was widened to admit those device
/// classes, not that such a device is present or attached. Per-device
/// trust comes from the guest's own device attestation.
///
/// `None` when the relay was not enabled for these devices, which is
/// the default. This field is skipped during serialization when `None`
/// so that the runtime claims — and therefore the hardware-derived key
/// KDF input (see `underhill_attestation::hardware_key_sealing`) — are
/// byte-for-byte unchanged for guests that do not use this feature.
///
/// Declared last on purpose: `MeshPayload` numbers fields by
/// declaration order, so inserting a field mid-struct would renumber
/// the following ones and break wire compatibility.
#[serde(skip_serializing_if = "Option::is_none")]
pub nvidia_vpci_relay_allowed: Option<bool>,
}

impl Default for AttestationVmConfig {
Expand All @@ -516,6 +534,7 @@ pub mod runtime_claims {
vm_unique_id: String::new(),
vmgs_provisioner: None,
hardware_sealing_policy: HardwareSealingPolicy::None,
nvidia_vpci_relay_allowed: None,
}
}
}
Expand Down
24 changes: 24 additions & 0 deletions openhcl/underhill_attestation/src/hardware_key_sealing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,35 @@ mod tests {
tpm_persisted: false,
hardware_sealing_policy,
filtered_vpci_devices_allowed: true,
nvidia_vpci_relay_allowed: None,
vm_unique_id: "".to_string(),
vmgs_provisioner: None,
}
}

/// The serialized `AttestationVmConfig` is an input to the hardware-derived
/// key KDF, so a guest that does not opt into the NVIDIA VPCI relay must
/// serialize exactly as it did before the field was introduced. Otherwise
/// every existing stateful CVM would derive different sealing keys and fail
/// to unseal its state after an upgrade.
#[test]
fn nvidia_vpci_relay_claim_omitted_when_unset() {
let vm_config = create_test_vm_config(HardwareSealingPolicy::Hash);
let json = serde_json::to_string(&vm_config).unwrap();
assert!(
!json.contains("nvidia-vpci-relay-allowed"),
"claim must be omitted when unset, got {json}"
);

let enabled = AttestationVmConfig {
nvidia_vpci_relay_allowed: Some(true),
..create_test_vm_config(HardwareSealingPolicy::Hash)
};
let enabled_json = serde_json::to_string(&enabled).unwrap();
assert!(enabled_json.contains(r#""nvidia-vpci-relay-allowed":true"#));
assert_ne!(json, enabled_json);
}

#[test]
fn hardware_derived_keys_hash_policy() {
let vm_config = create_test_vm_config(HardwareSealingPolicy::Hash);
Expand Down
2 changes: 2 additions & 0 deletions openhcl/underhill_attestation/src/igvm_attest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ mod tests {
tpm_persisted: false,
hardware_sealing_policy: HardwareSealingPolicy::Signer,
filtered_vpci_devices_allowed: true,
nvidia_vpci_relay_allowed: None,
vm_unique_id: String::new(),
vmgs_provisioner: None,
};
Expand All @@ -535,6 +536,7 @@ mod tests {
tpm_persisted: false,
hardware_sealing_policy: HardwareSealingPolicy::Hash,
filtered_vpci_devices_allowed: true,
nvidia_vpci_relay_allowed: None,
vm_unique_id: String::new(),
vmgs_provisioner: None,
};
Expand Down
5 changes: 5 additions & 0 deletions openhcl/underhill_attestation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2118,6 +2118,7 @@ mod tests {
tpm_persisted: true,
hardware_sealing_policy: HardwareSealingPolicy::None,
filtered_vpci_devices_allowed: false,
nvidia_vpci_relay_allowed: None,
vm_unique_id: String::new(),
vmgs_provisioner: None,
}
Expand Down Expand Up @@ -2692,6 +2693,7 @@ mod tests {
tpm_persisted: false,
hardware_sealing_policy: HardwareSealingPolicy::Hash,
filtered_vpci_devices_allowed: true,
nvidia_vpci_relay_allowed: None,
vm_unique_id: String::new(),
vmgs_provisioner: None,
},
Expand Down Expand Up @@ -2769,6 +2771,7 @@ mod tests {
tpm_persisted: false,
hardware_sealing_policy: HardwareSealingPolicy::Hash,
filtered_vpci_devices_allowed: true,
nvidia_vpci_relay_allowed: None,
vm_unique_id: String::new(),
vmgs_provisioner: None,
},
Expand Down Expand Up @@ -2811,6 +2814,7 @@ mod tests {
tpm_persisted: false,
hardware_sealing_policy: HardwareSealingPolicy::Hash,
filtered_vpci_devices_allowed: true,
nvidia_vpci_relay_allowed: None,
vm_unique_id: String::new(),
vmgs_provisioner: None,
},
Expand Down Expand Up @@ -2879,6 +2883,7 @@ mod tests {
tpm_persisted: false,
hardware_sealing_policy: HardwareSealingPolicy::Hash,
filtered_vpci_devices_allowed: true,
nvidia_vpci_relay_allowed: None,
vm_unique_id: String::new(),
vmgs_provisioner: None,
},
Expand Down
14 changes: 14 additions & 0 deletions openhcl/underhill_core/src/dispatch/vtl2_settings_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ impl Vtl2SettingsWorker {
let old_settings = Vtl2Settings {
fixed: Default::default(),
dynamic: self.old_settings.clone(),
device_policy: None,
};
let vtl2_settings = Vtl2Settings::read_from(buf, old_settings).map_err(|err| match err {
underhill_config::schema::ParseError::Json(err) => {
Expand Down Expand Up @@ -314,6 +315,19 @@ impl Vtl2SettingsWorker {

let vtl2_settings = vtl2_settings?;

// The device policy is consumed once, during VM startup, to build the
// VPCI relay's allow-list. Applying it later would let the reported
// device posture drift from the attestation claim, which is computed at
// the same time, so a runtime update is ignored. Say so rather than
// silently dropping it.
if vtl2_settings.device_policy.is_some() {
tracelimit::warn_ratelimited!(
CVM_ALLOWED,
"ignoring DevicePolicy in a runtime VTL2 settings update; \
it is only honored at VM startup"
);
}

let new_settings = vtl2_settings.dynamic;

tracing::info!(CVM_ALLOWED, ?new_settings, "Received VTL2 settings");
Expand Down
1 change: 1 addition & 0 deletions openhcl/underhill_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ async fn launch_workers(
strict_encryption_policy: opt.strict_encryption_policy,
attempt_ak_cert_callback: opt.attempt_ak_cert_callback,
enable_vpci_relay: opt.enable_vpci_relay,
nvidia_vpci_relay_allowed: opt.nvidia_vpci_relay_allowed,
disable_proxy_redirect: opt.disable_proxy_redirect,
disable_lower_vtl_timer_virt: opt.disable_lower_vtl_timer_virt,
config_timeout_in_seconds: opt.config_timeout_in_seconds,
Expand Down
10 changes: 10 additions & 0 deletions openhcl/underhill_core/src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,14 @@ pub struct Options {
/// (OPENHCL_ENABLE_VPCI_RELAY=1) Enable the VPCI relay.
pub enable_vpci_relay: Option<bool>,

/// (OPENHCL_NVIDIA_VPCI_RELAY_ALLOWED=1) Allow NVIDIA GPUs and
/// NVLink/NVSwitch fabric devices through the VPCI relay's device filter.
///
/// Overrides the host's `NvidiaVpciRelayAllowed` Device Platform Settings
/// value, for hosts that cannot yet set it. The attestation claim follows
/// this override, so the reported posture always matches the filter.
pub nvidia_vpci_relay_allowed: Option<bool>,

/// (OPENHCL_DISABLE_PROXY_REDIRECT=1) Disable proxy interrupt redirection.
pub disable_proxy_redirect: bool,

Expand Down Expand Up @@ -540,6 +548,7 @@ impl Options {
let strict_encryption_policy = parse_env_bool_opt("HCL_STRICT_ENCRYPTION_POLICY");
let attempt_ak_cert_callback = parse_env_bool_opt("HCL_ATTEMPT_AK_CERT_CALLBACK");
let enable_vpci_relay = parse_env_bool_opt("OPENHCL_ENABLE_VPCI_RELAY");
let nvidia_vpci_relay_allowed = parse_env_bool_opt("OPENHCL_NVIDIA_VPCI_RELAY_ALLOWED");
let disable_proxy_redirect = parse_env_bool("OPENHCL_DISABLE_PROXY_REDIRECT");
let disable_lower_vtl_timer_virt = parse_env_bool("OPENHCL_DISABLE_LOWER_VTL_TIMER_VIRT");
let config_timeout_in_seconds =
Expand Down Expand Up @@ -611,6 +620,7 @@ impl Options {
strict_encryption_policy,
attempt_ak_cert_callback,
enable_vpci_relay,
nvidia_vpci_relay_allowed,
disable_proxy_redirect,
disable_lower_vtl_timer_virt,
config_timeout_in_seconds,
Expand Down
Loading
Loading