openhcl: relay NVIDIA GPUs to CVM guests behind a host config flag - #4207
Conversation
…rtschaedler3/azurelocal-cgpu
Relaying NVIDIA GPUs and NVLink/NVSwitch fabric devices into a confidential guest was gated behind the `nvidia_vpci_relay_allowed` cargo feature, which required a separate build. That build has a different launch measurement, so customers had to re-pin attestation policies to a new MRDT, and it depended on sideloading remaining permitted. Replace the build-time feature with a per-VM host configuration flag, `NvidiaVpciRelayAllowed`, delivered over the existing Device Platform Settings channel. It defaults to false, so hosts that do not set it are unaffected and the default relay allow-list is unchanged. This collapses the two builds back into one: no divergent measurement, no MRDT churn, and no sideloading dependency. Report the resulting posture to attestation as a new runtime claim so a relying party can tell whether a given CVM had the relay widened. The claim is `Option<bool>` and is skipped during serialization when unset. This matters beyond wire size: the serialized `AttestationVmConfig` is also an input to the hardware-derived key KDF, so emitting the field unconditionally would change the derived sealing keys for every existing guest and break stateful CVMs across the upgrade. Omitting it when unset keeps the runtime claims byte-for-byte identical for guests that do not use the feature, following the precedent already set by `current_time` and `vmgs_provisioner`. A regression test covers this. The flag is unmeasured host input. That is acceptable because admission is not privilege: a relayed non-TDISP device DMAs only into shared memory and cannot reach guest-private memory or lower a VTL, and the guest independently attests any device it chooses to trust. The set of admissible devices remains compiled in; the host can only enable or disable that fixed set, never widen it. Also drop the now-redundant `x64-cvm-nvidia-vpci-relay` IGVM recipe, which was identical to `x64-cvm` apart from the removed feature. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
…list is empty Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
There was a problem hiding this comment.
Pull request overview
This PR introduces an opt-in host configuration flag to allow relaying NVIDIA datacenter GPUs (and NVLink/NVSwitch fabric devices) into confidential guests via the VPCI relay, and surfaces that posture in the guest’s attestation runtime claims so relying parties can detect the widened device filter.
Changes:
- Make VPCI relay device filtering deny-by-default (empty allow-list admits nothing) and rate-limit denial warnings.
- Add NVIDIA GPU + fabric allow-list rules gated by a new
nvidia_vpci_relay_allowedhost setting, and emit an optionalnvidia-vpci-relay-allowedruntime claim only when actually used. - Extend GET/DPS transport structs to carry the new host setting end-to-end, plus update attestation protocol/tests accordingly.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| vm/devices/pci/vpci_relay/src/lib.rs | Deny-by-default allow-list evaluation, ratelimited warnings, and unit tests for allow-list semantics. |
| vm/devices/pci/pci_core/src/spec.rs | Adds DISPLAY_CONTROLLER_3D PCI subclass constant needed for NVIDIA GPU filtering. |
| vm/devices/get/guest_emulation_transport/src/client.rs | Plumbs nvidia_vpci_relay_allowed from DPS JSON into platform settings. |
| vm/devices/get/guest_emulation_transport/src/api.rs | Adds platform setting field and documentation for NVIDIA VPCI relay gating. |
| vm/devices/get/guest_emulation_device/src/lib.rs | Initializes the new platform setting to a safe default (false). |
| vm/devices/get/get_protocol/src/dps_json.rs | Extends DPS JSON schema with nvidia_vpci_relay_allowed (default false). |
| openhcl/underhill_core/src/worker.rs | Gates NVIDIA allow-list entries and attestation claim emission on the new host flag. |
| openhcl/underhill_attestation/src/lib.rs | Updates attestation test helpers for the new optional claim field. |
| openhcl/underhill_attestation/src/igvm_attest/mod.rs | Updates IGVM attestation tests for the new optional claim field. |
| openhcl/underhill_attestation/src/hardware_key_sealing.rs | Adds regression test ensuring claim omission preserves KDF input when unset. |
| openhcl/openhcl_attestation_protocol/src/igvm_attest/get.rs | Adds the optional nvidia-vpci-relay-allowed runtime claim field with skip_serializing_if. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| let vpci_relay_active = | ||
| with_vmbus_relay && dps.general.vpci_boot_enabled && isolation.is_isolated(); |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
…ates Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
…//github.com/robertschaedler3/openvmm into robertschaedler3/cgpu-vpci-relay-host-config
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Suppressed comments (3)
vm/devices/pci/vpci_relay/src/lib.rs:210
AllowedDeviceusesOption<...>fields whereNoneacts as the wildcard; the doc currently says a hardware ID of!0is treated as a wildcard, which doesn't match this API.
/// Adds an allowed device to the list. If one of the hardware ID is `!0`
/// then it is treated as a wildcard.
openhcl/openhcl_attestation_protocol/src/igvm_attest/get.rs:496
- This claim is used to report whether the device filter was widened (per PR description), not whether at least one NVIDIA device was actually present and relayed. The wording “were relayed” is easy to misread as the latter; consider phrasing it as “were allowed to be relayed”.
/// Whether NVIDIA GPUs and NVLink/NVSwitch fabric devices were relayed
/// into this guest.
openhcl/underhill_core/src/worker.rs:2134
- This comment says “devices are actually relayed”, but at this point the code is only deciding whether the feature/claim is enabled (it can be true even if no matching devices are present). Rewording avoids overstating the semantics.
// Reported only when the devices are actually relayed, and omitted
// entirely otherwise so that the runtime claims (and hence the sealing
// key derivation) are unchanged for guests not using this feature.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated no new comments.
Suppressed comments (3)
openhcl/openhcl_attestation_protocol/src/igvm_attest/get.rs:499
AttestationVmConfigderivesMeshPayload, which assigns field numbers by struct field order unless explicitly overridden (seesupport/mesh/mesh_derive/src/lib.rs:313-323). Insertingnvidia_vpci_relay_allowedhere shifts the implicit field numbers forvm_unique_id,vmgs_provisioner, andhardware_sealing_policy, breaking compatibility with existing binaries/protocol consumers. Add explicit#[mesh(N)]numbers to preserve the prior numbering (and give the new field a new number).
/// Whether NVIDIA GPUs and NVLink/NVSwitch fabric devices were relayed
/// into this guest.
///
/// `None` when the relay was not enabled for these devices, which is
/// the default. This field is skipped during serialization when `None`
vm/devices/pci/vpci_relay/src/lib.rs:213
- The
add_allowed_devicedoc comment still describes the old!0wildcard behavior, butAllowedDevicenow usesOptionfields whereNoneis the wildcard (seeAllowedDevice::allows). This can mislead callers when building allow-list rules.
/// Adds an allowed device to the list. If one of the hardware ID is `!0`
/// then it is treated as a wildcard.
///
/// Devices are denied by default: a relay with no allowed devices admits
/// nothing.
openhcl/underhill_core/src/worker.rs:2139
- This comment says the claim is emitted only when NVIDIA devices are "actually relayed", but
nvidia_vpci_devices_relayedis derived fromenable_vpci_relay && dps.general.nvidia_vpci_relay_allowed(a configuration/allow-list widening), not from observing whether any NVIDIA devices were present. Consider rewording to reflect what the value actually represents.
// Reported only when the devices are actually relayed, and omitted
// entirely otherwise so that the runtime claims (and hence the sealing
// key derivation) are unchanged for guests not using this feature.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
…mesh field numbering Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (3)
vm/devices/pci/vpci_relay/src/lib.rs:213
- The
add_allowed_devicedoc comment still describes a!0wildcard, butAllowedDeviceusesOptionfields whereNoneis the wildcard. This mismatch can lead to incorrect callers and makes the deny-by-default semantics harder to understand.
/// Adds an allowed device to the list. If one of the hardware ID is `!0`
/// then it is treated as a wildcard.
///
/// Devices are denied by default: a relay with no allowed devices admits
/// nothing.
openhcl/underhill_core/src/worker.rs:2140
- This comment says the claim is reported only when the devices are "actually relayed", but
nvidia_vpci_devices_relayedis derived from configuration (enable_vpci_relay && ..._allowed) and does not reflect whether any NVIDIA devices were present. Clarifying the wording avoids overstating what the claim means.
// Reported only when the devices are actually relayed, and omitted
// entirely otherwise so that the runtime claims (and hence the sealing
// key derivation) are unchanged for guests not using this feature.
nvidia_vpci_relay_allowed: nvidia_vpci_devices_relayed.then_some(true),
openhcl/openhcl_attestation_protocol/src/igvm_attest/get.rs:507
- The runtime-claims field name (
nvidia-vpci-relay-allowed) and the PR description suggest this is about whether the host opted in / the device filter was widened, but the doc comment says the devices "were relayed". Align the wording with the intended posture signal to avoid implying that a GPU was necessarily present.
/// Whether NVIDIA GPUs and NVLink/NVSwitch fabric devices were relayed
/// into this guest.
///
/// `None` when the relay was not enabled for these devices, which is
/// the default. This field is skipped during serialization when `None`
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (4)
openhcl/underhill_core/src/worker.rs:2139
- This comment says the claim is reported only when the devices are "actually relayed", but
nvidia_vpci_devices_relayedis a configuration/enablement boolean and does not reflect whether any NVIDIA device was present/attached. Rewording this avoids implying stronger semantics than the code provides.
// Reported only when the devices are actually relayed, and omitted
// entirely otherwise so that the runtime claims (and hence the sealing
// key derivation) are unchanged for guests not using this feature.
vm/devices/pci/vpci_relay/src/lib.rs:213
- The doc comment for
add_allowed_devicesays a hardware ID value of!0is treated as a wildcard, butAllowedDeviceusesOption<...>fields whereNoneis the wildcard. This is misleading for callers and reviewers (especially now that deny-by-default semantics are emphasized).
/// Adds an allowed device to the list. If one of the hardware ID is `!0`
/// then it is treated as a wildcard.
///
/// Devices are denied by default: a relay with no allowed devices admits
/// nothing.
openhcl/openhcl_attestation_protocol/src/igvm_attest/get.rs:510
- The runtime-claims field name is
nvidia_vpci_relay_allowed, and the PR description describes it as reporting whether the device filter was widened. The doc comment currently says NVIDIA devices "were relayed" which implies device presence, not enablement/allowance. This can confuse relying parties and future maintainers about the claim’s semantics.
/// Whether NVIDIA GPUs and NVLink/NVSwitch fabric devices were relayed
/// into this guest.
///
/// `None` when the relay was not enabled for these devices, which is
/// the default. This field is skipped during serialization when `None`
openhcl/underhill_core/src/worker.rs:1730
nvidia_vpci_devices_relayedreads like it reflects device presence/attachment, but it’s actually an enablement gate (enable_vpci_relay && ...allowed). Renaming to something likenvidia_vpci_relay_allowed/enable_nvidia_vpci_relaywould better match the attestation field and avoid semantic confusion (it’s used both for the allow-list gate and the runtime claim).
This issue also appears on line 2137 of the same file.
let nvidia_vpci_devices_relayed = enable_vpci_relay && dps.general.nvidia_vpci_relay_allowed;
…esence Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.
Suppressed comments (1)
openhcl/underhill_core/src/worker.rs:1739
- This change introduces a new host-configurable gate (
nvidia_vpci_relay_allowed) and a new attestation runtime claim (nvidia-vpci-relay-allowed). The OpenHCL VMBus/VPCI Guide page documents VPCI relay and filtering but does not mention this new flag/claim, so readers won’t know how to enable it or how it impacts attestation/sealing behavior.
// Whether the NVIDIA GPU and NVLink/NVSwitch allow-list entries are
// installed: the host must opt in, and the VPCI relay must be running.
// This is an enablement gate -- it does not indicate that such a device is
// present or attached.
//
// This single value gates both the relay allow-list entries and the
// attestation claim that reports them. Do not re-derive either from a
// different set of conditions: if the two can disagree, the host can widen
// the filter while attestation reports that it did not.
let enable_nvidia_vpci_relay = enable_vpci_relay && dps.general.nvidia_vpci_relay_allowed;
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
|
One or more custom setup steps configured for this repository failed during this Copilot code review run: Setup steps run before each review. If the review above is missing context, or no review was posted at all, the failing step above may be the cause. See the workflow run for failure details, fix your setup steps configuration, and re-request a review. Note You can configure setup steps for Copilot code review separately from Copilot cloud agent with a |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 18 out of 18 changed files in this pull request and generated no new comments.
Suppressed comments (2)
vm/devices/pci/vpci_relay/Cargo.toml:31
vpci_relaynow has unit tests; if you switch them to usetest_with_tracing::test(as done in other PCI crates), the crate needs atest_with_tracingdev-dependency so it compiles.
tracelimit.workspace = true
tracing.workspace = true
vm/devices/pci/vpci_relay/src/lib.rs:468
- The new unit tests use the built-in
#[test], so tracing won’t be initialized for these tests. In this repo’s PCI-related crates, unit tests typically importtest_with_tracing::testso#[test]expands to the tracing-initializing attribute (e.g.vm/devices/pci/vpci_client/src/tests.rs:28,vm/devices/pci/vfio_assigned_device/src/lib.rs:1576).
Please add use test_with_tracing::test; to this module (and add the corresponding dev-dependency in Cargo.toml).
mod tests {
use super::AllowedDevice;
use super::device_allowed;
use pci_core::spec::hwid::ClassCode;
use pci_core::spec::hwid::HardwareIds;
use pci_core::spec::hwid::ProgrammingInterface;
use pci_core::spec::hwid::Subclass;
… setting Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.
Suppressed comments (2)
vm/devices/pci/vpci_relay/Cargo.toml:41
- If you adopt the repo’s common
test_with_tracing::testconvention for these new unit tests,vpci_relaywill also needtest_with_tracingas a dev-dependency. Adding it here avoids relying on transitive dev-deps from other crates.
sparse_mmap.workspace = true
[dev-dependencies]
vpci.workspace = true
mesh.workspace = true
vm/devices/pci/vpci_relay/src/lib.rs:467
- The new unit tests use
#[test]without importingtest_with_tracing::test, which means tracing may not be initialized consistently with the rest of the codebase’s unit tests. Importingtest_with_tracing::testin this module keeps test behavior consistent and ensures traces show up when tests fail.
use super::device_allowed;
use pci_core::spec::hwid::ClassCode;
use pci_core::spec::hwid::HardwareIds;
use pci_core::spec::hwid::ProgrammingInterface;
use pci_core::spec::hwid::Subclass;
…ridges Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 5621a3b2-62f0-42aa-914b-32d48349f3cc
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.
Suppressed comments (1)
openvmm/openvmm_entry/src/cli_args.rs:857
- This PR adds a new user-facing CLI flag (
--nvidia-vpci-relay-allowed), but there is no corresponding update to the OpenVMM Guide CLI reference. Please document the new flag (and its security/attestation implications) inGuide/src/reference/openvmm/management/cli.mdso users can discover it and understand when to use it.
/// allow NVIDIA GPUs and NVLink/NVSwitch fabric devices through the VTL 2
/// VPCI relay's device filter.
#[clap(long, requires("vtl2"))]
pub nvidia_vpci_relay_allowed: bool,
| pub vmbus_redirection: bool, | ||
| /// Allow NVIDIA GPUs and NVLink/NVSwitch fabric devices through the | ||
| /// VPCI relay's device filter. | ||
| pub nvidia_vpci_relay_allowed: bool, | ||
| /// Enable the TPM. |
Relays NVIDIA GPUs (and their NVLink fabric) through the VPCI relay to confidential VM guests, enabling confidential GPU workloads on Azure Local.
The resulting posture is reported in the attestation runtime claims as
nvidia-vpci-relay-allowed, so a relying party can tell whether a CVM had its device filter widened.Why vendor + class rather than an explicit device list?
Datacenter GPUs (H100/H200/HGX H100/H200, HGX B200/B300) are headless and enumerate as 3D controllers rather than VGA, and HGX clusters additionally expose NVSwitch devices that are PCI bridges so a single display-class filter would miss the fabric. Keying on vendor + the two relevant classes covers whole clusters while still rejecting all non-NVIDIA devices, and avoids a rebuild each time a new SKU ships.