From 6d9fef1a02e6efd51e9ebd0130651ca61f75839b Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Mon, 7 Dec 2009 11:58:02 +0100 Subject: [PATCH] cpuid: Fix multicore setup on Intel The multicore CPUID code detects whether the guest is an Intel or an AMD CPU, because the Linux kernel is picky about the CmpLegacy bit. KVM by default passes through the host's vendor, which was not catched by the code. So fork out the vendor determining bits into a separate function to be used from both places and always get the real vendor. This fixes KVM's multicore setup on Intel CPUs. Signed-off-by: Andre Przywara Reported-by: Dietmar Maurer Signed-off-by: Anthony Liguori --- target-i386/helper.c | 46 +++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/target-i386/helper.c b/target-i386/helper.c index 957b3fc1f2..08e6d6c396 100644 --- a/target-i386/helper.c +++ b/target-i386/helper.c @@ -1638,6 +1638,24 @@ static void host_cpuid(uint32_t function, uint32_t count, #endif } +static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + *ebx = env->cpuid_vendor1; + *edx = env->cpuid_vendor2; + *ecx = env->cpuid_vendor3; + + /* sysenter isn't supported on compatibility mode on AMD, syscall + * isn't supported in compatibility mode on Intel. + * Normally we advertise the actual cpu vendor, but you can override + * this if you want to use KVM's sysenter/syscall emulation + * in compatibility mode and when doing cross vendor migration + */ + if (kvm_enabled() && env->cpuid_vendor_override) { + host_cpuid(0, 0, NULL, ebx, ecx, edx); + } +} + void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) @@ -1654,16 +1672,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch(index) { case 0: *eax = env->cpuid_level; - *ebx = env->cpuid_vendor1; - *edx = env->cpuid_vendor2; - *ecx = env->cpuid_vendor3; - - /* sysenter isn't supported on compatibility mode on AMD. and syscall - * isn't supported in compatibility mode on Intel. so advertise the - * actuall cpu, and say goodbye to migration between different vendors - * is you use compatibility mode. */ - if (kvm_enabled() && !env->cpuid_vendor_override) - host_cpuid(0, 0, NULL, ebx, ecx, edx); + get_cpuid_vendor(env, ebx, ecx, edx); break; case 1: *eax = env->cpuid_version; @@ -1759,11 +1768,18 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = env->cpuid_ext3_features; *edx = env->cpuid_ext2_features; - if (env->nr_cores * env->nr_threads > 1 && - env->cpuid_vendor1 == CPUID_VENDOR_AMD_1 && - env->cpuid_vendor2 == CPUID_VENDOR_AMD_2 && - env->cpuid_vendor3 == CPUID_VENDOR_AMD_3) { - *ecx |= 1 << 1; /* CmpLegacy bit */ + /* The Linux kernel checks for the CMPLegacy bit and + * discards multiple thread information if it is set. + * So dont set it here for Intel to make Linux guests happy. + */ + if (env->nr_cores * env->nr_threads > 1) { + uint32_t tebx, tecx, tedx; + get_cpuid_vendor(env, &tebx, &tecx, &tedx); + if (tebx != CPUID_VENDOR_INTEL_1 || + tedx != CPUID_VENDOR_INTEL_2 || + tecx != CPUID_VENDOR_INTEL_3) { + *ecx |= 1 << 1; /* CmpLegacy bit */ + } } if (kvm_enabled()) {