From be8722e8702060975eea5185b78ad53c3ef2a18e Mon Sep 17 00:00:00 2001 From: Nikola Bojanic Date: Tue, 8 Sep 2026 15:02:05 -0700 Subject: [PATCH 1/7] vmservice: support SNP IGVM boot over TTRPC Add an IGVM boot option and an explicit isolation configuration to the VM-service API. Map SNP IGVM requests to the existing IGVM loader. --- openvmm/openvmm_entry/src/ttrpc/mod.rs | 50 +++++++++++++++++-- .../src/vmservice.proto | 17 +++++++ 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index 4737783a84..128574a2eb 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -58,6 +58,7 @@ use openvmm_defs::config::ArchTopologyConfig; use openvmm_defs::config::Config; use openvmm_defs::config::DeviceVtl; use openvmm_defs::config::HypervisorConfig; +use openvmm_defs::config::IsolationType; use openvmm_defs::config::LoadMode; use openvmm_defs::config::MemoryConfig; use openvmm_defs::config::NumaDistance; @@ -75,6 +76,7 @@ use openvmm_defs::config::VirtioBus; use openvmm_defs::config::VmbusConfig; use openvmm_defs::config::VpAssignment; use openvmm_defs::config::VpciDeviceConfig; +use openvmm_defs::config::Vtl2BaseAddressType; use openvmm_defs::rpc::VmRpc; use openvmm_defs::worker::VM_WORKER; use openvmm_defs::worker::VmWorkerParameters; @@ -88,6 +90,7 @@ use pal_async::task::Task; use scsidisk_resources::SimpleScsiDiskHandle; use std::fs::File; use std::future::Future; +use std::path::PathBuf; use std::sync::Arc; use std::time::Duration; use storvsp_resources::ScsiControllerHandle; @@ -778,14 +781,27 @@ impl VmService { // move it into whichever LoadMode is selected below. let smbios = Box::new(smbios_config_from_proto(req_config.smbios_config.take())?); + let isolation = match req_config + .isolation_config + .take() + .unwrap_or_default() + .isolation_type() + { + vmservice::isolation_config::Type::None => None, + vmservice::isolation_config::Type::Snp => Some(IsolationType::Snp), + }; + // The boot configuration also determines the base chipset, since the // firmware and the device model have to agree on the platform. - let (load_mode, base_chipset_type, uefi_config) = match req_config + let (load_mode, base_chipset_type, uefi_config, igvm_path, vmbus) = match req_config .boot_config .take() .context("missing boot configuration")? { vmservice::vm_config::BootConfig::DirectBoot(boot) => { + if isolation.is_some() { + bail!("VM-service SNP isolation currently supports only IGVM boot"); + } let kernel = File::open(boot.kernel_path).context("failed to open kernel")?; let initrd = if boot.initrd_path.is_empty() { None @@ -804,9 +820,34 @@ impl VmService { }, vm_manifest_builder::BaseChipsetType::HyperVGen2LinuxDirect, None, + None, + Some(VmbusConfig::default()), + ) + } + vmservice::vm_config::BootConfig::Igvm(boot) => { + if isolation != Some(IsolationType::Snp) { + bail!("VM-service IGVM boot currently supports only SNP isolation"); + } + let igvm_path = PathBuf::from(&boot.igvm_path); + let file = File::open(&igvm_path) + .with_context(|| format!("failed to open IGVM {}", igvm_path.display()))?; + ( + LoadMode::Igvm { + file: file.into(), + cmdline: String::new(), + vtl2_base_address: Vtl2BaseAddressType::File, + com_serial: None, + }, + vm_manifest_builder::BaseChipsetType::EnlightenedLinuxDirect, + None, + Some(igvm_path), + None, ) } vmservice::vm_config::BootConfig::Uefi(uefi) => { + if isolation.is_some() { + bail!("VM-service SNP isolation currently supports only IGVM boot"); + } let firmware = File::open(&uefi.firmware_path).with_context(|| { format!("failed to open uefi firmware {}", uefi.firmware_path) })?; @@ -872,6 +913,8 @@ impl VmService { }, vm_manifest_builder::BaseChipsetType::HypervGen2Uefi, Some((base_template, uefi.secure_boot_enabled)), + None, + Some(VmbusConfig::default()), ) } }; @@ -970,6 +1013,7 @@ impl VmService { }, hypervisor: HypervisorConfig { with_hv: true, + with_isolation: isolation, ..Default::default() }, #[cfg(windows)] @@ -979,7 +1023,7 @@ impl VmService { vga_firmware: None, vtl2_gfx: false, virtio_devices: vec![], - vmbus: Some(VmbusConfig::default()), + vmbus, vtl2_vmbus: None, vmbus_devices: vec![], #[cfg(windows)] @@ -1152,7 +1196,7 @@ impl VmService { ged_rpc: None, vm_rpc: send.clone(), paravisor_diag: None, - igvm_path: None, + igvm_path, memory_backing_file: None, memory, processors, diff --git a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto index c2e0571783..c51f026b68 100644 --- a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto +++ b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto @@ -73,6 +73,19 @@ message DirectBoot { string kernel_cmdline = 3; } +message IgvmBoot { + string igvm_path = 1; +} + +message IsolationConfig { + enum Type { + NONE = 0; + SNP = 1; + } + + Type isolation_type = 1; +} + message UEFI { // Variables used to initialize an empty UEFI variable store. These are // ignored when the store already contains any variables. @@ -566,6 +579,7 @@ message VMConfig { oneof BootConfig { DirectBoot direct_boot = 5; UEFI uefi = 6; + IgvmBoot igvm = 15; } WindowsOptions windows_options = 7; // Field 8 was previously map extra_data. @@ -588,6 +602,9 @@ message VMConfig { SMBIOSConfig smbios_config = 13; optional string crash_dump_path = 14; + + // Confidential-computing isolation mode. + IsolationConfig isolation_config = 17; } // WindowsOptions contains virtual machine configurations that are only present on a Windows host. From add775d5269a640bef31e331dc1e512189f71899 Mon Sep 17 00:00:00 2001 From: Nikola Bojanic Date: Thu, 10 Sep 2026 11:23:52 -0700 Subject: [PATCH 2/7] vmservice: make IGVM personality explicit Add an IGVM personality field, defaulting to Linux-direct. Reject UEFI personality for now. Reject SMBIOS overrides for IGVM boot rather than silently ignoring them. --- openvmm/openvmm_entry/src/ttrpc/mod.rs | 19 ++++++++++++++++--- .../src/vmservice.proto | 6 ++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index 128574a2eb..0685ef1acd 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -777,8 +777,8 @@ impl VmService { #[cfg(guest_arch = "x86_64")] let arch = vm_manifest_builder::MachineArch::X86_64; - // SMBIOS identity is applied regardless of boot type; build it once and - // move it into whichever LoadMode is selected below. + // Build SMBIOS identity for direct Linux or UEFI boot. + let smbios_requested = req_config.smbios_config.is_some(); let smbios = Box::new(smbios_config_from_proto(req_config.smbios_config.take())?); let isolation = match req_config @@ -825,9 +825,22 @@ impl VmService { ) } vmservice::vm_config::BootConfig::Igvm(boot) => { + if smbios_requested { + bail!("VM-service IGVM boot does not support SMBIOS overrides"); + } if isolation != Some(IsolationType::Snp) { bail!("VM-service IGVM boot currently supports only SNP isolation"); } + let base_chipset_type = + match vmservice::igvm_boot::Personality::from_i32(boot.personality) { + Some(vmservice::igvm_boot::Personality::LinuxDirect) => { + vm_manifest_builder::BaseChipsetType::EnlightenedLinuxDirect + } + Some(vmservice::igvm_boot::Personality::Uefi) => { + bail!("VM-service IGVM boot with UEFI personality is not yet supported"); + } + None => bail!("unsupported IGVM personality {}", boot.personality), + }; let igvm_path = PathBuf::from(&boot.igvm_path); let file = File::open(&igvm_path) .with_context(|| format!("failed to open IGVM {}", igvm_path.display()))?; @@ -838,7 +851,7 @@ impl VmService { vtl2_base_address: Vtl2BaseAddressType::File, com_serial: None, }, - vm_manifest_builder::BaseChipsetType::EnlightenedLinuxDirect, + base_chipset_type, None, Some(igvm_path), None, diff --git a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto index c51f026b68..fe423e5ddb 100644 --- a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto +++ b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto @@ -74,7 +74,13 @@ message DirectBoot { } message IgvmBoot { + enum Personality { + LINUX_DIRECT = 0; + UEFI = 1; + } + string igvm_path = 1; + Personality personality = 2; } message IsolationConfig { From a37cffaf1e8f911b1f96cda11b89852b21e3626e Mon Sep 17 00:00:00 2001 From: Nikola Bojanic Date: Fri, 11 Sep 2026 17:48:21 -0700 Subject: [PATCH 3/7] vmservice: make VMBus configuration explicit Add no_vmbus independently of boot, reject HVSocket without VMBus. Clarify that IGVM boot does not support SMBIOS overrides. --- openvmm/openvmm_entry/src/ttrpc/mod.rs | 20 +++++++++++-------- .../src/vmservice.proto | 4 +++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index 0685ef1acd..a3fd4c478d 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -793,7 +793,7 @@ impl VmService { // The boot configuration also determines the base chipset, since the // firmware and the device model have to agree on the platform. - let (load_mode, base_chipset_type, uefi_config, igvm_path, vmbus) = match req_config + let (load_mode, base_chipset_type, uefi_config, igvm_path) = match req_config .boot_config .take() .context("missing boot configuration")? @@ -821,7 +821,6 @@ impl VmService { vm_manifest_builder::BaseChipsetType::HyperVGen2LinuxDirect, None, None, - Some(VmbusConfig::default()), ) } vmservice::vm_config::BootConfig::Igvm(boot) => { @@ -854,7 +853,6 @@ impl VmService { base_chipset_type, None, Some(igvm_path), - None, ) } vmservice::vm_config::BootConfig::Uefi(uefi) => { @@ -905,7 +903,7 @@ impl VmService { // VM with no graphics adapter. uefi_console_mode: com1_configured.then_some(UefiConsoleMode::Com1), smbios, - enable_vmbus: true, + enable_vmbus: !req_config.no_vmbus, // Everything below is fixed for now. The proto has no // way to express these yet; fields will be added as // callers need them. @@ -927,13 +925,15 @@ impl VmService { vm_manifest_builder::BaseChipsetType::HypervGen2Uefi, Some((base_template, uefi.secure_boot_enabled)), None, - Some(VmbusConfig::default()), ) } }; let mut chipset_builder = VmManifestBuilder::new(base_chipset_type, arch).with_serial(ports); + if req_config.no_vmbus { + chipset_builder = chipset_builder.without_vmbus(); + } if let Some((base_template, secure_boot_enabled)) = uefi_config { // The UEFI helper device backs the firmware's variable store and // runtime services, so it is required for a UEFI boot. The store is @@ -1036,7 +1036,7 @@ impl VmService { vga_firmware: None, vtl2_gfx: false, virtio_devices: vec![], - vmbus, + vmbus: (!req_config.no_vmbus).then(VmbusConfig::default), vtl2_vmbus: None, vmbus_devices: vec![], #[cfg(windows)] @@ -1160,11 +1160,15 @@ impl VmService { } if let Some(hvsocket_config) = req_config.hvsocket_config { + let vmbus = config + .vmbus + .as_mut() + .context("HVSocket requires VMBus to be enabled")?; let listener = UnixListener::bind(&hvsocket_config.path).with_context(|| { format!("failed to bind hvsocket path: {}", hvsocket_config.path) })?; - config.vmbus.as_mut().unwrap().vsock_listener = Some(listener); - config.vmbus.as_mut().unwrap().vsock_path = Some(hvsocket_config.path); + vmbus.vsock_listener = Some(listener); + vmbus.vsock_path = Some(hvsocket_config.path); } let (send, recv) = mesh::channel(); diff --git a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto index fe423e5ddb..206e4a25ca 100644 --- a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto +++ b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto @@ -604,13 +604,15 @@ message VMConfig { // SMBIOS (DMI) identity overrides. Applied to whichever boot type is // selected; note that PCAT boot honors only the system UUID and serial - // number. + // number. IGVM boot does not support these overrides. SMBIOSConfig smbios_config = 13; optional string crash_dump_path = 14; // Confidential-computing isolation mode. IsolationConfig isolation_config = 17; + + bool no_vmbus = 16; } // WindowsOptions contains virtual machine configurations that are only present on a Windows host. From f8d11f5bb91c195bad26cde8dbd9ad4fe04d1739 Mon Sep 17 00:00:00 2001 From: Nikola Bojanic Date: Mon, 14 Sep 2026 10:35:37 -0700 Subject: [PATCH 4/7] vmservice: reject unknown isolation types Reject unsupported enum values instead of treating them as no isolation. --- openvmm/openvmm_entry/src/ttrpc/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index a3fd4c478d..3fdc66826c 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -781,14 +781,15 @@ impl VmService { let smbios_requested = req_config.smbios_config.is_some(); let smbios = Box::new(smbios_config_from_proto(req_config.smbios_config.take())?); - let isolation = match req_config + let isolation_type = req_config .isolation_config .take() .unwrap_or_default() - .isolation_type() - { - vmservice::isolation_config::Type::None => None, - vmservice::isolation_config::Type::Snp => Some(IsolationType::Snp), + .isolation_type; + let isolation = match vmservice::isolation_config::Type::from_i32(isolation_type) { + Some(vmservice::isolation_config::Type::None) => None, + Some(vmservice::isolation_config::Type::Snp) => Some(IsolationType::Snp), + None => bail!("unsupported isolation type {isolation_type}"), }; // The boot configuration also determines the base chipset, since the From b8af788a3104b1a7d199631d88ce3872d7ba5657 Mon Sep 17 00:00:00 2001 From: Nikola Bojanic Date: Mon, 14 Sep 2026 10:55:20 -0700 Subject: [PATCH 5/7] vmservice: address formatting and clippy feedback --- openvmm/openvmm_entry/src/ttrpc/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index 3fdc66826c..b25cd6d008 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -837,7 +837,9 @@ impl VmService { vm_manifest_builder::BaseChipsetType::EnlightenedLinuxDirect } Some(vmservice::igvm_boot::Personality::Uefi) => { - bail!("VM-service IGVM boot with UEFI personality is not yet supported"); + bail!( + "VM-service IGVM boot with UEFI personality is not yet supported" + ); } None => bail!("unsupported IGVM personality {}", boot.personality), }; @@ -846,7 +848,7 @@ impl VmService { .with_context(|| format!("failed to open IGVM {}", igvm_path.display()))?; ( LoadMode::Igvm { - file: file.into(), + file, cmdline: String::new(), vtl2_base_address: Vtl2BaseAddressType::File, com_serial: None, From e91f1d2a9da2c2f503c051fb9426152ab5ac848e Mon Sep 17 00:00:00 2001 From: Nikola Bojanic Date: Mon, 14 Sep 2026 16:11:08 -0700 Subject: [PATCH 6/7] vmservice: order config fields by tag number --- openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto index 206e4a25ca..26c759a8ea 100644 --- a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto +++ b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto @@ -609,10 +609,10 @@ message VMConfig { optional string crash_dump_path = 14; + bool no_vmbus = 16; + // Confidential-computing isolation mode. IsolationConfig isolation_config = 17; - - bool no_vmbus = 16; } // WindowsOptions contains virtual machine configurations that are only present on a Windows host. From cc79dcb7e9427bc86d53090315ed770a027feffe Mon Sep 17 00:00:00 2001 From: Nikola Bojanic Date: Wed, 16 Sep 2026 14:56:58 -0700 Subject: [PATCH 7/7] vmservice: rename disable_vmbus and document fields --- openvmm/openvmm_entry/src/ttrpc/mod.rs | 6 +++--- openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto | 12 +++++++++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/openvmm/openvmm_entry/src/ttrpc/mod.rs b/openvmm/openvmm_entry/src/ttrpc/mod.rs index b25cd6d008..8beb62281b 100644 --- a/openvmm/openvmm_entry/src/ttrpc/mod.rs +++ b/openvmm/openvmm_entry/src/ttrpc/mod.rs @@ -906,7 +906,7 @@ impl VmService { // VM with no graphics adapter. uefi_console_mode: com1_configured.then_some(UefiConsoleMode::Com1), smbios, - enable_vmbus: !req_config.no_vmbus, + enable_vmbus: !req_config.disable_vmbus, // Everything below is fixed for now. The proto has no // way to express these yet; fields will be added as // callers need them. @@ -934,7 +934,7 @@ impl VmService { let mut chipset_builder = VmManifestBuilder::new(base_chipset_type, arch).with_serial(ports); - if req_config.no_vmbus { + if req_config.disable_vmbus { chipset_builder = chipset_builder.without_vmbus(); } if let Some((base_template, secure_boot_enabled)) = uefi_config { @@ -1039,7 +1039,7 @@ impl VmService { vga_firmware: None, vtl2_gfx: false, virtio_devices: vec![], - vmbus: (!req_config.no_vmbus).then(VmbusConfig::default), + vmbus: (!req_config.disable_vmbus).then(VmbusConfig::default), vtl2_vmbus: None, vmbus_devices: vec![], #[cfg(windows)] diff --git a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto index 26c759a8ea..cd1ec66310 100644 --- a/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto +++ b/openvmm/openvmm_ttrpc_vmservice/src/vmservice.proto @@ -73,22 +73,31 @@ message DirectBoot { string kernel_cmdline = 3; } +// Boot from an IGVM file. message IgvmBoot { enum Personality { + // Linux direct boot with Hyper-V enlightenments. LINUX_DIRECT = 0; + // Reserved for UEFI IGVM boot. UEFI = 1; } + // Path to the IGVM file on the host. string igvm_path = 1; + // Guest boot environment. Personality personality = 2; } +// Confidential-computing isolation configuration. message IsolationConfig { enum Type { + // No confidential-computing isolation. NONE = 0; + // AMD SEV-SNP isolation. SNP = 1; } + // Isolation mode. Type isolation_type = 1; } @@ -609,7 +618,8 @@ message VMConfig { optional string crash_dump_path = 14; - bool no_vmbus = 16; + // Run without VMBus. + bool disable_vmbus = 16; // Confidential-computing isolation mode. IsolationConfig isolation_config = 17;