arch, hypervisor, vmm: skip vcpu setup when using igvm and kvm

When we use igvm + kvm, we setup the regs and sregs using the cpuid
page. We still need to setup the fpu in configure_vcpu.

Co-authored-by: Keith Adler <kadler@cloudflare.com>
Signed-off-by: Keith Adler <kadler@cloudflare.com>
Co-authored-by: Alex Orozco <aorozco@google.com>
Signed-off-by: Alex Orozco <aorozco@google.com>
Signed-off-by: Ruben Hakobyan <hruben@meta.com>
This commit is contained in:
Ruben Hakobyan
2026-04-07 08:05:30 -07:00
committed by Rob Bradford
parent c31f5d4998
commit 8ee0a07ab1
4 changed files with 62 additions and 24 deletions

View File

@@ -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(())

View File

@@ -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,

View File

@@ -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<AtomicI32>,
#[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<Arc<Mutex<CpuManager>>> {
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<GuestMemoryMmap>)>,
) -> 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")]

View File

@@ -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)?;