]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: x86: Update Xen TSC leaves during CPUID emulation
authorFred Griffoul <fgriffo@amazon.co.uk>
Fri, 24 Jan 2025 15:05:39 +0000 (15:05 +0000)
committerSean Christopherson <seanjc@google.com>
Tue, 25 Feb 2025 15:09:55 +0000 (07:09 -0800)
The Xen emulation in KVM modifies certain CPUID leaves to expose
TSC information to the guest.

Previously, these CPUID leaves were updated whenever guest time changed,
but this conflicts with KVM_SET_CPUID/KVM_SET_CPUID2 ioctls which reject
changes to CPUID entries on running vCPUs.

Fix this by updating the TSC information directly in the CPUID emulation
handler instead of modifying the vCPU's CPUID entries.

Signed-off-by: Fred Griffoul <fgriffo@amazon.co.uk>
Reviewed-by: Paul Durrant <paul@xen.org>
Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Link: https://lore.kernel.org/r/20250124150539.69975-1-fgriffo@amazon.co.uk
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/kvm/cpuid.c
arch/x86/kvm/x86.c
arch/x86/kvm/x86.h
arch/x86/kvm/xen.c
arch/x86/kvm/xen.h

index 8eb3a88707f213b139e529999337d8163c3b4941..0b238c537893634e9eb0d3c57cea738345526b91 100644 (file)
@@ -2006,6 +2006,22 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx,
                } else if (function == 0x80000007) {
                        if (kvm_hv_invtsc_suppressed(vcpu))
                                *edx &= ~feature_bit(CONSTANT_TSC);
+               } else if (IS_ENABLED(CONFIG_KVM_XEN) &&
+                          kvm_xen_is_tsc_leaf(vcpu, function)) {
+                       /*
+                        * Update guest TSC frequency information if necessary.
+                        * Ignore failures, there is no sane value that can be
+                        * provided if KVM can't get the TSC frequency.
+                        */
+                       if (kvm_check_request(KVM_REQ_CLOCK_UPDATE, vcpu))
+                               kvm_guest_time_update(vcpu);
+
+                       if (index == 1) {
+                               *ecx = vcpu->arch.hv_clock.tsc_to_system_mul;
+                               *edx = vcpu->arch.hv_clock.tsc_shift;
+                       } else if (index == 2) {
+                               *eax = vcpu->arch.hw_tsc_khz;
+                       }
                }
        } else {
                *eax = *ebx = *ecx = *edx = 0;
index f97d4d435e7f19c63aab053200666af2eaa33b15..c3007982c88c6b86f1cd5bb1721b0840bc658ca0 100644 (file)
@@ -3170,7 +3170,7 @@ static void kvm_setup_guest_pvclock(struct kvm_vcpu *v,
        trace_kvm_pvclock_update(v->vcpu_id, &vcpu->hv_clock);
 }
 
-static int kvm_guest_time_update(struct kvm_vcpu *v)
+int kvm_guest_time_update(struct kvm_vcpu *v)
 {
        unsigned long flags, tgt_tsc_khz;
        unsigned seq;
@@ -3253,7 +3253,6 @@ static int kvm_guest_time_update(struct kvm_vcpu *v)
                                   &vcpu->hv_clock.tsc_shift,
                                   &vcpu->hv_clock.tsc_to_system_mul);
                vcpu->hw_tsc_khz = tgt_tsc_khz;
-               kvm_xen_update_tsc_info(v);
        }
 
        vcpu->hv_clock.tsc_timestamp = tsc_timestamp;
index 91e50a513100edca6645327c6d5cfe1b3eb95f74..658c28632deb10ab546264da5e1aeead2adb384a 100644 (file)
@@ -362,6 +362,7 @@ void kvm_inject_realmode_interrupt(struct kvm_vcpu *vcpu, int irq, int inc_eip);
 u64 get_kvmclock_ns(struct kvm *kvm);
 uint64_t kvm_get_wall_clock_epoch(struct kvm *kvm);
 bool kvm_get_monotonic_and_clockread(s64 *kernel_ns, u64 *tsc_timestamp);
+int kvm_guest_time_update(struct kvm_vcpu *v);
 
 int kvm_read_guest_virt(struct kvm_vcpu *vcpu,
        gva_t addr, void *val, unsigned int bytes,
index 8aef7cd243490e232d23bcf7a2e39c540b95deb4..9174aa45597b7935ac99bddf76353402483d209d 100644 (file)
@@ -2256,29 +2256,6 @@ void kvm_xen_destroy_vcpu(struct kvm_vcpu *vcpu)
        del_timer_sync(&vcpu->arch.xen.poll_timer);
 }
 
-void kvm_xen_update_tsc_info(struct kvm_vcpu *vcpu)
-{
-       struct kvm_cpuid_entry2 *entry;
-       u32 function;
-
-       if (!vcpu->arch.xen.cpuid.base)
-               return;
-
-       function = vcpu->arch.xen.cpuid.base | XEN_CPUID_LEAF(3);
-       if (function > vcpu->arch.xen.cpuid.limit)
-               return;
-
-       entry = kvm_find_cpuid_entry_index(vcpu, function, 1);
-       if (entry) {
-               entry->ecx = vcpu->arch.hv_clock.tsc_to_system_mul;
-               entry->edx = vcpu->arch.hv_clock.tsc_shift;
-       }
-
-       entry = kvm_find_cpuid_entry_index(vcpu, function, 2);
-       if (entry)
-               entry->eax = vcpu->arch.hw_tsc_khz;
-}
-
 void kvm_xen_init_vm(struct kvm *kvm)
 {
        mutex_init(&kvm->arch.xen.xen_lock);
index d191103d8163442c725b01d7eba831879eba0afa..59e6128a7bd3c254975e82b4b2c1d76807d3d886 100644 (file)
@@ -9,6 +9,7 @@
 #ifndef __ARCH_X86_KVM_XEN_H__
 #define __ARCH_X86_KVM_XEN_H__
 
+#include <asm/xen/cpuid.h>
 #include <asm/xen/hypervisor.h>
 
 #ifdef CONFIG_KVM_XEN
@@ -35,7 +36,6 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe,
 int kvm_xen_setup_evtchn(struct kvm *kvm,
                         struct kvm_kernel_irq_routing_entry *e,
                         const struct kvm_irq_routing_entry *ue);
-void kvm_xen_update_tsc_info(struct kvm_vcpu *vcpu);
 
 static inline void kvm_xen_sw_enable_lapic(struct kvm_vcpu *vcpu)
 {
@@ -50,6 +50,14 @@ static inline void kvm_xen_sw_enable_lapic(struct kvm_vcpu *vcpu)
                kvm_xen_inject_vcpu_vector(vcpu);
 }
 
+static inline bool kvm_xen_is_tsc_leaf(struct kvm_vcpu *vcpu, u32 function)
+{
+       return static_branch_unlikely(&kvm_xen_enabled.key) &&
+              vcpu->arch.xen.cpuid.base &&
+              function <= vcpu->arch.xen.cpuid.limit &&
+              function == (vcpu->arch.xen.cpuid.base | XEN_CPUID_LEAF(3));
+}
+
 static inline bool kvm_xen_msr_enabled(struct kvm *kvm)
 {
        return static_branch_unlikely(&kvm_xen_enabled.key) &&
@@ -170,8 +178,9 @@ static inline bool kvm_xen_timer_enabled(struct kvm_vcpu *vcpu)
        return false;
 }
 
-static inline void kvm_xen_update_tsc_info(struct kvm_vcpu *vcpu)
+static inline bool kvm_xen_is_tsc_leaf(struct kvm_vcpu *vcpu, u32 function)
 {
+       return false;
 }
 #endif