From 27c28fa3b092b9a4fc0c1015d6abfa6566d5bc18 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Tue, 15 Sep 2020 16:26:34 +0100 Subject: [PATCH] vmm, arch: Enable KVM HyperV support Inject CPUID leaves for advertising KVM HyperV support when the "kvm_hyperv" toggle is enabled. Currently we only enable a selection of features required to boot. Signed-off-by: Rob Bradford --- arch/src/x86_64/mod.rs | 61 ++++++++++++++++++++++++++++++++++++++++++ vmm/src/cpu.rs | 19 ++++++++++--- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/arch/src/x86_64/mod.rs b/arch/src/x86_64/mod.rs index e283f417d..168420e35 100644 --- a/arch/src/x86_64/mod.rs +++ b/arch/src/x86_64/mod.rs @@ -174,6 +174,9 @@ pub enum Error { /// Missing SGX_LC CPU feature MissingSgxLaunchControlFeature, + + // Error populating Cpuid + PopulatingCpuid, } impl From for super::Error { @@ -333,13 +336,71 @@ pub fn configure_vcpu( kernel_entry_point: Option, vm_memory: &GuestMemoryAtomic, cpuid: CpuId, + kvm_hyperv: bool, ) -> super::Result<()> { let mut cpuid = cpuid; CpuidPatch::set_cpuid_reg(&mut cpuid, 0xb, None, CpuidReg::EDX, u32::from(id)); CpuidPatch::set_cpuid_reg(&mut cpuid, 0x1f, None, CpuidReg::EDX, u32::from(id)); + + if kvm_hyperv { + // Remove conflicting entries + cpuid.retain(|c| c.function != 0x4000_0000); + cpuid.retain(|c| c.function != 0x4000_0001); + + // See "Hypervisor Top Level Functional Specification" for details + // Compliance with "Hv#1" requires leaves up to 0x4000_000a + cpuid + .push(CpuIdEntry { + function: 0x40000000, + eax: 0x4000000a, // Maximum cpuid leaf + ebx: 0x756e694c, // "Linu" + ecx: 0x564b2078, // "x KV" + edx: 0x7648204d, // "M Hv" + ..Default::default() + }) + .map_err(|_| Error::PopulatingCpuid)?; + cpuid + .push(CpuIdEntry { + function: 0x40000001, + eax: 0x31237648, // "Hv#1" + ..Default::default() + }) + .map_err(|_| Error::PopulatingCpuid)?; + cpuid + .push(CpuIdEntry { + function: 0x40000002, + eax: 0x3839, // "Build number" + ebx: 0xa0000, // "Version" + ..Default::default() + }) + .map_err(|_| Error::PopulatingCpuid)?; + cpuid + .push(CpuIdEntry { + function: 0x4000_0003, + eax: 1 << 1 // AccessPartitionReferenceCounter + | 1 << 2 // AccessSynicRegs + | 1 << 3 // AccessSyntheticTimerRegs + | 1 << 9, // AccessPartitionReferenceTsc + ..Default::default() + }) + .map_err(|_| Error::PopulatingCpuid)?; + for i in 0x4000_0004..=0x4000_000a { + cpuid + .push(CpuIdEntry { + function: i, + ..Default::default() + }) + .map_err(|_| Error::PopulatingCpuid)?; + } + } + fd.set_cpuid2(&cpuid) .map_err(|e| Error::SetSupportedCpusFailed(e.into()))?; + if kvm_hyperv { + fd.enable_hyperv_synic().unwrap(); + } + regs::setup_msrs(fd).map_err(Error::MSRSConfiguration)?; if let Some(kernel_entry_point) = kernel_entry_point { // Safe to unwrap because this method is called after the VM is configured diff --git a/vmm/src/cpu.rs b/vmm/src/cpu.rs index 9c693f699..ff70094c5 100644 --- a/vmm/src/cpu.rs +++ b/vmm/src/cpu.rs @@ -299,6 +299,7 @@ impl Vcpu { kernel_entry_point: Option, vm_memory: &GuestMemoryAtomic, #[cfg(target_arch = "x86_64")] cpuid: CpuId, + #[cfg(target_arch = "x86_64")] kvm_hyperv: bool, ) -> Result<()> { #[cfg(target_arch = "aarch64")] { @@ -308,8 +309,15 @@ impl Vcpu { } #[cfg(target_arch = "x86_64")] - arch::configure_vcpu(&self.vcpu, self.id, kernel_entry_point, vm_memory, cpuid) - .map_err(Error::VcpuConfiguration)?; + arch::configure_vcpu( + &self.vcpu, + self.id, + kernel_entry_point, + vm_memory, + cpuid, + kvm_hyperv, + ) + .map_err(Error::VcpuConfiguration)?; Ok(()) } @@ -770,7 +778,12 @@ impl CpuManager { #[cfg(target_arch = "x86_64")] vcpu.lock() .unwrap() - .configure(entry_point, &vm_memory, self.cpuid.clone()) + .configure( + entry_point, + &vm_memory, + self.cpuid.clone(), + self.config.kvm_hyperv, + ) .expect("Failed to configure vCPU"); #[cfg(target_arch = "aarch64")]