Skip to content

Commit 9954a15

Browse files
pmjbonzini
authored andcommitted
x86-KVM: Supply TSC and APIC clock rates to guest like VMWare
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 <[email protected]> Message-Id: <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
1 parent c4c41a0 commit 9954a15

File tree

3 files changed

+35
-6
lines changed

3 files changed

+35
-6
lines changed

target/i386/cpu.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3658,6 +3658,7 @@ static Property x86_cpu_properties[] = {
36583658
DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true),
36593659
DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false),
36603660
DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true),
3661+
DEFINE_PROP_BOOL("vmware-cpuid-freq", X86CPU, vmware_cpuid_freq, false),
36613662
DEFINE_PROP_END_OF_LIST()
36623663
};
36633664

target/i386/cpu.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,10 @@ struct X86CPU {
12141214
bool host_features;
12151215
uint32_t apic_id;
12161216

1217+
/* Enables publishing of TSC increment and Local APIC bus frequencies to
1218+
* the guest OS in CPUID page 0x40000010, the same way that VMWare does. */
1219+
bool vmware_cpuid_freq;
1220+
12171221
/* if true the CPUID code directly forward host cache leaves to the guest */
12181222
bool cache_info_passthrough;
12191223

target/i386/kvm.c

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -982,12 +982,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
982982
}
983983
}
984984

985-
cpuid_data.cpuid.padding = 0;
986-
r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);
987-
if (r) {
988-
goto fail;
989-
}
990-
991985
r = kvm_arch_set_tsc_khz(cs);
992986
if (r < 0) {
993987
goto fail;
@@ -1007,6 +1001,36 @@ int kvm_arch_init_vcpu(CPUState *cs)
10071001
}
10081002
}
10091003

1004+
if (cpu->vmware_cpuid_freq
1005+
/* Guests depend on 0x40000000 to detect this feature, so only expose
1006+
* it if KVM exposes leaf 0x40000000. (Conflicts with Hyper-V) */
1007+
&& cpu->expose_kvm
1008+
&& kvm_base == KVM_CPUID_SIGNATURE
1009+
/* TSC clock must be stable and known for this feature. */
1010+
&& ((env->features[FEAT_8000_0007_EDX] & CPUID_APM_INVTSC)
1011+
|| env->user_tsc_khz != 0)
1012+
&& env->tsc_khz != 0) {
1013+
1014+
c = &cpuid_data.entries[cpuid_i++];
1015+
c->function = KVM_CPUID_SIGNATURE | 0x10;
1016+
c->eax = env->tsc_khz;
1017+
/* LAPIC resolution of 1ns (freq: 1GHz) is hardcoded in KVM's
1018+
* APIC_BUS_CYCLE_NS */
1019+
c->ebx = 1000000;
1020+
c->ecx = c->edx = 0;
1021+
1022+
c = cpuid_find_entry(&cpuid_data.cpuid, kvm_base, 0);
1023+
c->eax = MAX(c->eax, KVM_CPUID_SIGNATURE | 0x10);
1024+
}
1025+
1026+
cpuid_data.cpuid.nent = cpuid_i;
1027+
1028+
cpuid_data.cpuid.padding = 0;
1029+
r = kvm_vcpu_ioctl(cs, KVM_SET_CPUID2, &cpuid_data);
1030+
if (r) {
1031+
goto fail;
1032+
}
1033+
10101034
if (has_xsave) {
10111035
env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
10121036
}

0 commit comments

Comments
 (0)