qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PULL 18/41] x86-KVM: Supply TSC and APIC clock rates to gu


From: Paolo Bonzini
Subject: [Qemu-devel] [PULL 18/41] x86-KVM: Supply TSC and APIC clock rates to guest like VMWare
Date: Fri, 27 Jan 2017 20:35:04 +0100

From: Phil Dennis-Jordan <address@hidden>

This fixes timekeeping of x86-64 Darwin/OS X/macOS guests when using KVM.

Darwin/OS X/macOS for x86-64 uses the TSC for timekeeping; it normally 
calibrates this by querying various clock frequency scaling MSRs. Details 
depend on the exact CPU model detected. The local APIC timer frequency is 
extracted from (EFI) firmware.

This is problematic in the presence of virtualisation, as the MSRs in question 
are typically not handled by the hypervisor. VMWare (Fusion) advertises TSC and 
APIC frequency via a custom 0x40000010 CPUID leaf, in the eax and ebx registers 
respectively. This is documented at https://lwn.net/Articles/301888/ among 
other places.

Darwin/OS X/macOS looks for the generic 0x40000000 hypervisor leaf, and if this 
indicates via eax that leaf 0x40000010 might be available, that is in turn 
queried for the two frequencies.

This adds a CPU option "vmware-cpuid-freq" to enable the same behaviour when 
running Qemu with KVM acceleration, if the KVM TSC frequency can be determined, 
and it is stable. (invtsc or user-specified) The virtualised APIC bus cycle is 
hardcoded to 1GHz in KVM, so ebx of the CPUID leaf is also hardcoded to this 
value.

Signed-off-by: Phil Dennis-Jordan <address@hidden>
Message-Id: <address@hidden>
Signed-off-by: Paolo Bonzini <address@hidden>
---
 target/i386/cpu.c |  1 +
 target/i386/cpu.h |  4 ++++
 target/i386/kvm.c | 36 ++++++++++++++++++++++++++++++------
 3 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index cff23e1..e41375e 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -3658,6 +3658,7 @@ static Property x86_cpu_properties[] = {
     DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true),
     DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false),
     DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true),
+    DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, false),
     DEFINE_PROP_END_OF_LIST()
 };
 
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 10c5a35..4d788d5 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1214,6 +1214,10 @@ struct X86CPU {
     bool host_features;
     uint32_t apic_id;
 
+    /* Enables publishing of TSC increment and Local APIC bus frequencies to
+     * the guest OS in CPUID page 0x40000010, the same way that VMWare does. */
+    bool vmware_cpuid_freq;
+
     /* if true the CPUID code directly forward host cache leaves to the guest 
*/
     bool cache_info_passthrough;
 
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index 8e130cc..27fd050 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -982,12 +982,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
         }
     }
 
-    cpuid_data.cpuid.padding = 0;
-    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);
-    if (r) {
-        goto fail;
-    }
-
     r = kvm_arch_set_tsc_khz(cs);
     if (r < 0) {
         goto fail;
@@ -1007,6 +1001,36 @@ int kvm_arch_init_vcpu(CPUState *cs)
         }
     }
 
+    if (cpu->vmware_cpuid_freq
+        /* Guests depend on 0x40000000 to detect this feature, so only expose
+         * it if KVM exposes leaf 0x40000000. (Conflicts with Hyper-V) */
+        && cpu->expose_kvm
+        && kvm_base == KVM_CPUID_SIGNATURE
+        /* TSC clock must be stable and known for this feature. */
+        && ((env->features[FEAT_8000_0007_EDX] & CPUID_APM_INVTSC)
+            || env->user_tsc_khz != 0)
+        && env->tsc_khz != 0) {
+
+        c = &cpuid_data.entries[cpuid_i++];
+        c->function = KVM_CPUID_SIGNATURE | 0x10;
+        c->eax = env->tsc_khz;
+        /* LAPIC resolution of 1ns (freq: 1GHz) is hardcoded in KVM's
+         * APIC_BUS_CYCLE_NS */
+        c->ebx = 1000000;
+        c->ecx = c->edx = 0;
+
+        c = cpuid_find_entry(&cpuid_data.cpuid, kvm_base, 0);
+        c->eax = MAX(c->eax, KVM_CPUID_SIGNATURE | 0x10);
+    }
+
+    cpuid_data.cpuid.nent = cpuid_i;
+
+    cpuid_data.cpuid.padding = 0;
+    r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);
+    if (r) {
+        goto fail;
+    }
+
     if (has_xsave) {
         env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
     }
-- 
2.9.3



reply via email to

[Prev in Thread] Current Thread [Next in Thread]