diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index d35a878e6..09577b436 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -819,6 +819,7 @@ pub fn configure_vcpu( cpu_vendor: CpuVendor, topology: (u16, u16, u16, u16), nested: bool, + setup_registers: bool, ) -> super::Result<()> { let x2apic_id = get_x2apic_id(id, Some(topology)); @@ -892,17 +893,19 @@ pub fn configure_vcpu( regs::setup_msrs(vcpu).map_err(Error::MsrsConfiguration)?; if let Some((kernel_entry_point, guest_memory)) = boot_setup { - regs::setup_regs(vcpu, kernel_entry_point).map_err(Error::RegsConfiguration)?; - regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?; + if setup_registers { + regs::setup_regs(vcpu, kernel_entry_point).map_err(Error::RegsConfiguration)?; - // CPUs are required (by Intel sdm spec) to boot in x2apic mode if any - // of the apic IDs is larger than 255. Experimentally, the Linux kernel - // does not recognize the last vCPU if x2apic is not enabled when - // there are 256 vCPUs in a flat hierarchy (i.e. max x2apic ID is 255), - // so we need to enable x2apic in this case as well. - let enable_x2_apic_mode = get_max_x2apic_id(topology) > MAX_SUPPORTED_CPUS_LEGACY; - regs::setup_sregs(&guest_memory.memory(), vcpu, enable_x2_apic_mode) - .map_err(Error::SregsConfiguration)?; + // CPUs are required (by Intel sdm spec) to boot in x2apic mode if any + // of the apic IDs is larger than 255. Experimentally, the Linux kernel + // does not recognize the last vCPU if x2apic is not enabled when + // there are 256 vCPUs in a flat hierarchy (i.e. max x2apic ID is 255), + // so we need to enable x2apic in this case as well. + let enable_x2_apic_mode = get_max_x2apic_id(topology) > MAX_SUPPORTED_CPUS_LEGACY; + regs::setup_sregs(&guest_memory.memory(), vcpu, enable_x2_apic_mode) + .map_err(Error::SregsConfiguration)?; + } + regs::setup_fpu(vcpu).map_err(Error::FpuConfiguration)?; } interrupts::set_lint(vcpu).map_err(|e| Error::LocalIntConfiguration(e.into()))?; Ok(()) diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 787cfbf1f..8357e63b7 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -64,7 +64,7 @@ pub use vm::{ pub use crate::hypervisor::{Hypervisor, HypervisorError}; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum HypervisorType { #[cfg(feature = "kvm")] Kvm, diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 1450e0a8e..261f4c1c7 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -546,6 +546,7 @@ impl Vcpu { #[cfg(target_arch = "x86_64")] kvm_hyperv: bool, #[cfg(target_arch = "x86_64")] topology: (u16, u16, u16, u16), #[cfg(target_arch = "x86_64")] nested: bool, + #[cfg(feature = "igvm")] igvm_enabled: bool, ) -> Result<()> { #[cfg(target_arch = "aarch64")] { @@ -558,17 +559,32 @@ impl Vcpu { .map_err(Error::VcpuConfiguration)?; info!("Configuring vCPU: cpu_id = {}", self.id); #[cfg(target_arch = "x86_64")] - arch::configure_vcpu( - self.vcpu.as_ref(), - self.id, - boot_setup, - cpuid, - kvm_hyperv, - self.vendor, - topology, - nested, - ) - .map_err(Error::VcpuConfiguration)?; + { + // When IGVM is enabled, skip standard register setup here — the IGVM + // loader populates vCPU registers from the VMSA via set_sev_control_register + // (currently KVM-specific; MSHV handles this through its own import path). + // igvm_enabled is kept as an explicit flag rather than derived from sev_snp + // state because IGVM could theoretically be used independently of SEV-SNP. + cfg_if::cfg_if! { + if #[cfg(feature = "igvm")] { + let setup_registers = !igvm_enabled; + } else { + let setup_registers = true; + } + } + arch::configure_vcpu( + self.vcpu.as_ref(), + self.id, + boot_setup, + cpuid, + kvm_hyperv, + self.vendor, + topology, + nested, + setup_registers, + ) + .map_err(Error::VcpuConfiguration)?; + } Ok(()) } @@ -697,6 +713,8 @@ pub struct CpuManager { sev_snp_enabled: bool, // State of the core scheduling group leader election (VM mode). core_scheduling_group_leader: Arc, + #[cfg(feature = "igvm")] + igvm_enabled: bool, } /// State of the core scheduling group leader election for VM-wide cookie @@ -826,6 +844,7 @@ impl CpuManager { #[cfg(feature = "tdx")] tdx_enabled: bool, numa_nodes: &NumaNodes, #[cfg(feature = "sev_snp")] sev_snp_enabled: bool, + #[cfg(feature = "igvm")] igvm_enabled: bool, ) -> Result>> { if config.max_vcpus > hypervisor.get_max_vcpus() { return Err(Error::MaximumVcpusExceeded( @@ -902,6 +921,8 @@ impl CpuManager { core_scheduling_group_leader: Arc::new(AtomicI32::new( CoreSchedulingLeader::Initial as i32, )), + #[cfg(feature = "igvm")] + igvm_enabled, }))) } @@ -980,8 +1001,10 @@ impl CpuManager { vcpu: &mut Vcpu, boot_setup: Option<(EntryPoint, &GuestMemoryAtomic)>, ) -> Result<()> { - #[cfg(feature = "sev_snp")] - if self.sev_snp_enabled { + #[cfg(all(feature = "sev_snp", feature = "mshv"))] + if self.sev_snp_enabled + && self.hypervisor.hypervisor_type() == hypervisor::HypervisorType::Mshv + { if let Some((kernel_entry_point, _)) = boot_setup { vcpu.set_sev_control_register( kernel_entry_point.entry_addr.0 / crate::igvm::HV_PAGE_SIZE, @@ -1022,6 +1045,8 @@ impl CpuManager { self.config.kvm_hyperv, topology, self.config.nested, + #[cfg(feature = "igvm")] + self.igvm_enabled, )?; #[cfg(target_arch = "aarch64")] diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index ffe7b8624..f67d5bc10 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -738,6 +738,14 @@ impl Vm { let tdx_enabled = config.lock().unwrap().is_tdx_enabled(); #[cfg(feature = "sev_snp")] let sev_snp_enabled = config.lock().unwrap().is_sev_snp_enabled(); + #[cfg(feature = "igvm")] + let igvm_enabled = config + .lock() + .unwrap() + .payload + .as_ref() + .and_then(|p| p.igvm.as_ref()) + .is_some(); let cpus_config = config.lock().unwrap().cpus.clone(); let cpu_manager = cpu::CpuManager::new( @@ -755,6 +763,8 @@ impl Vm { numa_nodes, #[cfg(feature = "sev_snp")] sev_snp_enabled, + #[cfg(feature = "igvm")] + igvm_enabled, ) .map_err(Error::CpuManager)?;