From 0d0d19e223c05f105ecafe088670fa7d7afb37be Mon Sep 17 00:00:00 2001 From: Sebastien Boeuf Date: Fri, 7 Jun 2019 14:30:20 -0700 Subject: [PATCH] vmm: Enable TSC_DEADLINE_TIMER allows for PIT emulation removal As mentioned in the KVM documentation, TSC_DEADLINE_TIMER feature needs some special checks to validate that it is supported as the cpuid will always report it as disabled. We need to use the KVM_CHECK_EXTENSION ioctl to request the value of KVM_CAP_TSC_DEADLINE_TIMER. In case it is supported through the local APIC emulation provided by the CREATE_IRQCHIP in KVM, we have to set manually this feature by patching the cpuid. Here quoted from the KVM documentation: ``` The TSC deadline timer feature (CPUID leaf 1, ecx[24]) is always returned as false, since the feature depends on KVM_CREATE_IRQCHIP for local APIC support. Instead it is reported via ioctl(KVM_CHECK_EXTENSION, KVM_CAP_TSC_DEADLINE_TIMER) if that returns true and you use KVM_CREATE_IRQCHIP, or if you emulate the feature in userspace, then you can enable the feature for KVM_SET_CPUID2. ``` This patch implements the behavior described above, and this allows the VMM to remove the emulated Programmable Interval Timer (PIT) when the TSC_DEADLINE_TIMER feature can be enabled. Signed-off-by: Sebastien Boeuf --- vmm/src/vm.rs | 77 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 64 insertions(+), 13 deletions(-) diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index d8a7cac8b..a43607e5e 100755 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -52,7 +52,8 @@ const X86_64_IRQ_BASE: u32 = 5; const DEFAULT_MSIX_VEC_NUM: u16 = 2; // CPUID feature bits -const ECX_HYPERVISOR_SHIFT: u32 = 31; // Hypervisor bit. +const TSC_DEADLINE_TIMER_ECX_BIT: u8 = 24; // tsc deadline timer ecx bit. +const HYPERVISOR_ECX_BIT: u8 = 31; // Hypervisor ecx bit. /// Errors associated with VM management #[derive(Debug)] @@ -583,6 +584,16 @@ impl AsRawFd for EpollContext { } } +struct CpuidPatch { + function: u32, + index: u32, + flags_bit: Option, + eax_bit: Option, + ebx_bit: Option, + ecx_bit: Option, + edx_bit: Option, +} + pub struct Vm<'a> { fd: Arc, kernel: File, @@ -627,18 +638,44 @@ impl<'a> Vm<'a> { // Create IRQ chip fd.create_irq_chip().map_err(Error::VmSetup)?; - // Creates an in-kernel device model for the PIT. - let mut pit_config = kvm_pit_config::default(); - // We need to enable the emulation of a dummy speaker port stub so that writing to port 0x61 - // (i.e. KVM_SPEAKER_BASE_ADDRESS) does not trigger an exit to user space. - pit_config.flags = KVM_PIT_SPEAKER_DUMMY; - fd.create_pit2(pit_config).map_err(Error::VmSetup)?; - // Supported CPUID let mut cpuid = kvm .get_supported_cpuid(MAX_KVM_CPUID_ENTRIES) .map_err(Error::VmSetup)?; - Vm::patch_cpuid(&mut cpuid); + + let mut cpuid_patches = Vec::new(); + if kvm.check_extension(Cap::TscDeadlineTimer) { + // Patch tsc deadline timer bit + cpuid_patches.push(CpuidPatch { + function: 1, + index: 0, + flags_bit: None, + eax_bit: None, + ebx_bit: None, + ecx_bit: Some(TSC_DEADLINE_TIMER_ECX_BIT), + edx_bit: None, + }); + } else { + // Creates an in-kernel device model for the PIT. + let mut pit_config = kvm_pit_config::default(); + // We need to enable the emulation of a dummy speaker port stub so that writing to port 0x61 + // (i.e. KVM_SPEAKER_BASE_ADDRESS) does not trigger an exit to user space. + pit_config.flags = KVM_PIT_SPEAKER_DUMMY; + fd.create_pit2(pit_config).map_err(Error::VmSetup)?; + } + + // Patch hypervisor bit + cpuid_patches.push(CpuidPatch { + function: 1, + index: 0, + flags_bit: None, + eax_bit: None, + ebx_bit: None, + ecx_bit: Some(HYPERVISOR_ECX_BIT), + edx_bit: None, + }); + + Vm::patch_cpuid(&mut cpuid, cpuid_patches); // Let's allocate 64 GiB of addressable MMIO space, starting at 0. let mut allocator = SystemAllocator::new( @@ -838,13 +875,27 @@ impl<'a> Vm<'a> { &self.memory } - fn patch_cpuid(cpuid: &mut CpuId) { + fn patch_cpuid(cpuid: &mut CpuId, patches: Vec) { let entries = cpuid.mut_entries_slice(); for entry in entries.iter_mut() { - if let 1 = entry.function { - if entry.index == 0 { - entry.ecx |= 1 << ECX_HYPERVISOR_SHIFT; + for patch in patches.iter() { + if entry.function == patch.function && entry.index == patch.index { + if let Some(flags_bit) = patch.flags_bit { + entry.flags |= 1 << flags_bit; + } + if let Some(eax_bit) = patch.eax_bit { + entry.eax |= 1 << eax_bit; + } + if let Some(ebx_bit) = patch.ebx_bit { + entry.ebx |= 1 << ebx_bit; + } + if let Some(ecx_bit) = patch.ecx_bit { + entry.ecx |= 1 << ecx_bit; + } + if let Some(edx_bit) = patch.edx_bit { + entry.edx |= 1 << edx_bit; + } } } }