diff --git a/hypervisor/src/arch/x86/mod.rs b/hypervisor/src/arch/x86/mod.rs index 8ff396121..dc7244c44 100644 --- a/hypervisor/src/arch/x86/mod.rs +++ b/hypervisor/src/arch/x86/mod.rs @@ -340,6 +340,12 @@ pub struct MsrEntry { pub data: u64, } +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct VcpuMsrConfigUpdate { + pub feature_msrs: Vec, + pub snapshottable_msr_indices: Vec, +} + #[serde_with::serde_as] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct XsaveState { diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index fe393d5d8..70b4423c2 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -95,7 +95,7 @@ use crate::ClockData; #[cfg(target_arch = "x86_64")] use crate::arch::x86::{ CpuIdEntry, FpuState, LapicState, MTRR_MSR_INDICES, MsrEntry, NUM_IOAPIC_PINS, - SpecialRegisters, XsaveState, + SpecialRegisters, VcpuMsrConfigUpdate, XsaveState, }; #[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))] use crate::{ClockRestoreMode, ClockState}; @@ -886,6 +886,7 @@ impl vm::Vm for KvmVm { &self, id: u32, vm_ops: Option>, + #[cfg(target_arch = "x86_64")] msr_config_update: Option, ) -> vm::Result> { let fd = self .fd @@ -911,6 +912,21 @@ impl vm::Vm for KvmVm { })?; } + #[cfg(target_arch = "x86_64")] + let (feature_msrs, msrs) = msr_config_update.map_or_else( + || (Vec::new(), self.msrs.clone()), + |update| { + ( + update.feature_msrs, + update + .snapshottable_msr_indices + .into_iter() + .map(|index| MsrEntry { index, data: 0 }) + .collect(), + ) + }, + ); + #[cfg(target_arch = "x86_64")] // Safety: `xsave_size` will not change after vcpu creation because: // 1. `xsave_size` depends on cpuid @@ -924,7 +940,9 @@ impl vm::Vm for KvmVm { let vcpu = KvmVcpu { fd, #[cfg(target_arch = "x86_64")] - msrs: self.msrs.clone(), + msrs, + #[cfg(target_arch = "x86_64")] + feature_msrs, vm_ops, #[cfg(target_arch = "x86_64")] hyperv_synic: AtomicBool::new(false), @@ -1921,6 +1939,8 @@ pub struct KvmVcpu { fd: VcpuFd, #[cfg(target_arch = "x86_64")] msrs: Vec, + #[cfg(target_arch = "x86_64")] + feature_msrs: Vec, vm_ops: Option>, #[cfg(target_arch = "x86_64")] hyperv_synic: AtomicBool, @@ -2011,7 +2031,7 @@ impl KvmVcpu { /// let kvm = KvmHypervisor::new().unwrap(); /// let hypervisor = Arc::new(kvm); /// let vm = hypervisor.create_vm(HypervisorVmConfig::default()).expect("new VM fd creation failed"); -/// let vcpu = vm.create_vcpu(0, None).unwrap(); +/// let vcpu = vm.create_vcpu(0, None, #[cfg(target_arch = "x86_64")] Default::default()).unwrap(); /// ``` impl cpu::Vcpu for KvmVcpu { /// @@ -3068,7 +3088,7 @@ impl cpu::Vcpu for KvmVcpu { /// let hv = Arc::new(kvm); /// let vm = hv.create_vm(HypervisorVmConfig::default()).expect("new VM fd creation failed"); /// vm.enable_split_irq().unwrap(); - /// let vcpu = vm.create_vcpu(0, None).unwrap(); + /// let vcpu = vm.create_vcpu(0, None, Default::default()).unwrap(); /// let state = vcpu.state().unwrap(); /// ``` fn state(&self) -> cpu::Result { @@ -3333,7 +3353,7 @@ impl cpu::Vcpu for KvmVcpu { /// let hv = Arc::new(kvm); /// let vm = hv.create_vm(HypervisorVmConfig::default()).expect("new VM fd creation failed"); /// vm.enable_split_irq().unwrap(); - /// let vcpu = vm.create_vcpu(0, None).unwrap(); + /// let vcpu = vm.create_vcpu(0, None, Default::default()).unwrap(); /// let state = vcpu.state().unwrap(); /// vcpu.set_state(&state).unwrap(); /// ``` diff --git a/hypervisor/src/mshv/mod.rs b/hypervisor/src/mshv/mod.rs index d1b8e4c7c..c861af14f 100644 --- a/hypervisor/src/mshv/mod.rs +++ b/hypervisor/src/mshv/mod.rs @@ -42,7 +42,7 @@ use crate::arch::emulator::PlatformEmulator; #[cfg(target_arch = "x86_64")] use crate::arch::x86::emulator::Emulator; #[cfg(target_arch = "x86_64")] -use crate::arch::x86::{LapicState, SpecialRegisters}; +use crate::arch::x86::{LapicState, SpecialRegisters, VcpuMsrConfigUpdate}; #[cfg(target_arch = "aarch64")] use crate::mshv::aarch64::emulator; use crate::mshv::emulator::MshvEmulatorContext; @@ -481,7 +481,7 @@ pub struct MshvVcpu { /// let mshv = MshvHypervisor::new().unwrap(); /// let hypervisor = Arc::new(mshv); /// let vm = hypervisor.create_vm(HypervisorVmConfig::default()).expect("new VM fd creation failed"); -/// let vcpu = vm.create_vcpu(0, None).unwrap(); +/// let vcpu = vm.create_vcpu(0, None, #[cfg(target_arch = "x86_64")] Default::default()).unwrap(); /// ``` impl cpu::Vcpu for MshvVcpu { /// @@ -1896,6 +1896,7 @@ impl vm::Vm for MshvVm { &self, id: u32, vm_ops: Option>, + #[cfg(target_arch = "x86_64")] _msr_config_update: Option, ) -> vm::Result> { let id: u8 = id.try_into().unwrap(); let vcpu_fd = self diff --git a/hypervisor/src/vm.rs b/hypervisor/src/vm.rs index 9c78c3b00..980ceddd5 100644 --- a/hypervisor/src/vm.rs +++ b/hypervisor/src/vm.rs @@ -33,6 +33,8 @@ use crate::arch::aarch64::gic::{Vgic, VgicConfig}; use crate::arch::riscv64::aia::{Vaia, VaiaConfig}; #[cfg(feature = "tdx")] use crate::arch::x86::CpuIdEntry; +#[cfg(target_arch = "x86_64")] +use crate::arch::x86::VcpuMsrConfigUpdate; use crate::cpu::Vcpu; use crate::{ClockRestoreMode, ClockState, IoEventAddress, IrqRoutingEntry}; @@ -347,7 +349,12 @@ pub trait Vm: Send + Sync + Any { /// Unregister an event that will, when signaled, trigger the `gsi` IRQ. fn unregister_irqfd(&self, fd: &EventFd, gsi: u32) -> Result<()>; /// Creates a new KVM vCPU file descriptor and maps the memory corresponding - fn create_vcpu(&self, id: u32, vm_ops: Option>) -> Result>; + fn create_vcpu( + &self, + id: u32, + vm_ops: Option>, + #[cfg(target_arch = "x86_64")] msr_config_update: Option, + ) -> Result>; #[cfg(target_arch = "aarch64")] fn create_vgic(&self, config: &VgicConfig) -> Result>>; #[cfg(target_arch = "riscv64")] diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 98f4e9325..21301e9fe 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -45,14 +45,14 @@ use hypervisor::StandardRegisters; use hypervisor::arch::aarch64::gic::Vgic; #[cfg(all(target_arch = "aarch64", feature = "guest_debug"))] use hypervisor::arch::aarch64::regs::{ID_AA64MMFR0_EL1, TCR_EL1, TTBR1_EL1}; -#[cfg(target_arch = "x86_64")] -use hypervisor::arch::x86::CpuIdEntry; #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] use hypervisor::arch::x86::MsrEntry; #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] use hypervisor::arch::x86::SpecialRegisters; #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] use hypervisor::arch::x86::msr_index; +#[cfg(target_arch = "x86_64")] +use hypervisor::arch::x86::{CpuIdEntry, VcpuMsrConfigUpdate}; #[cfg(feature = "tdx")] use hypervisor::kvm::{TdxExitDetails, TdxExitStatus}; #[cfg(feature = "mshv")] @@ -504,15 +504,22 @@ impl Vcpu { /// * `vm` - The virtual machine this vcpu will get attached to. /// * `vm_ops` - Optional object for exit handling. /// * `cpu_vendor` - CPU vendor as reported by __cpuid(0x0) + /// * `msr_config_update` - An optional update to MSR configuration. pub fn new( id: u32, apic_id: u32, vm: &dyn hypervisor::Vm, vm_ops: Option>, #[cfg(target_arch = "x86_64")] cpu_vendor: CpuVendor, + #[cfg(target_arch = "x86_64")] msr_config_update: Option, ) -> Result { let vcpu = vm - .create_vcpu(apic_id, vm_ops) + .create_vcpu( + apic_id, + vm_ops, + #[cfg(target_arch = "x86_64")] + msr_config_update, + ) .map_err(|e| Error::VcpuCreate(e.into()))?; // Initially the cpuid per vCPU is the one supported by this VM. Ok(Vcpu { @@ -978,6 +985,8 @@ impl CpuManager { Some(self.vm_ops.clone()), #[cfg(target_arch = "x86_64")] self.hypervisor.get_cpu_vendor(), + #[cfg(target_arch = "x86_64")] + Default::default(), )?; if let Some(snapshot) = snapshot { @@ -3439,7 +3448,7 @@ mod unit_tests { hv.check_required_extensions().unwrap(); // Calling get_lapic will fail if there is no irqchip before hand. vm.create_irq_chip().unwrap(); - let vcpu = vm.create_vcpu(0, None).unwrap(); + let vcpu = vm.create_vcpu(0, None, Default::default()).unwrap(); let klapic_before: LapicState = vcpu.get_lapic().unwrap(); // Compute the value that is expected to represent LVT0 and LVT1. @@ -3464,7 +3473,7 @@ mod unit_tests { let vm = hv .create_vm(HypervisorVmConfig::default()) .expect("new VM fd creation failed"); - let vcpu = vm.create_vcpu(0, None).unwrap(); + let vcpu = vm.create_vcpu(0, None, Default::default()).unwrap(); setup_fpu(vcpu.as_ref()).unwrap(); let expected_fpu: FpuState = FpuState { @@ -3490,7 +3499,7 @@ mod unit_tests { let vm = hv .create_vm(HypervisorVmConfig::default()) .expect("new VM fd creation failed"); - let vcpu = vm.create_vcpu(0, None).unwrap(); + let vcpu = vm.create_vcpu(0, None, Default::default()).unwrap(); setup_msrs(vcpu.as_ref()).unwrap(); // This test will check against the last MSR entry configured (the tenth one). @@ -3518,7 +3527,7 @@ mod unit_tests { let vm = hv .create_vm(HypervisorVmConfig::default()) .expect("new VM fd creation failed"); - let vcpu = vm.create_vcpu(0, None).unwrap(); + let vcpu = vm.create_vcpu(0, None, Default::default()).unwrap(); let mut expected_regs: StandardRegisters = vcpu.create_standard_regs(); expected_regs.set_rflags(0x0000000000000002u64); @@ -3544,7 +3553,7 @@ mod unit_tests { let vm = hv .create_vm(HypervisorVmConfig::default()) .expect("new VM fd creation failed"); - let vcpu = vm.create_vcpu(0, None).unwrap(); + let vcpu = vm.create_vcpu(0, None, Default::default()).unwrap(); let mut expected_regs: StandardRegisters = vcpu.create_standard_regs(); expected_regs.set_rflags(0x0000000000000002u64); diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index 63a62d8a1..12a9fe0ad 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -3935,7 +3935,9 @@ mod unit_tests { mem.write_slice(&code, load_addr) .expect("Writing code to memory failed"); - let mut vcpu = vm.create_vcpu(0, None).expect("new Vcpu failed"); + let mut vcpu = vm + .create_vcpu(0, None, Default::default()) + .expect("new Vcpu failed"); let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed"); vcpu_sregs.cs.base = 0; @@ -4073,7 +4075,9 @@ pub fn test_vm() { mem.write_slice(&code, load_addr) .expect("Writing code to memory failed"); - let mut vcpu = vm.create_vcpu(0, None).expect("new Vcpu failed"); + let mut vcpu = vm + .create_vcpu(0, None, Default::default()) + .expect("new Vcpu failed"); let mut vcpu_sregs = vcpu.get_sregs().expect("get sregs failed"); vcpu_sregs.cs.base = 0;