From patchwork Fri Jan 30 01:54:08 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nitin A Kamble X-Patchwork-Id: 4692 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n0U1sEas030300 for ; Fri, 30 Jan 2009 01:54:14 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754224AbZA3ByM (ORCPT ); Thu, 29 Jan 2009 20:54:12 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1754757AbZA3ByM (ORCPT ); Thu, 29 Jan 2009 20:54:12 -0500 Received: from mga14.intel.com ([143.182.124.37]:36957 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754224AbZA3ByK (ORCPT ); Thu, 29 Jan 2009 20:54:10 -0500 Received: from azsmga001.ch.intel.com ([10.2.17.19]) by azsmga102.ch.intel.com with ESMTP; 29 Jan 2009 17:54:09 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.37,347,1231142400"; d="diff'?scan'208";a="105253700" Received: from mukti.sc.intel.com (HELO [143.183.140.220]) ([143.183.140.220]) by azsmga001.ch.intel.com with ESMTP; 29 Jan 2009 17:54:09 -0800 Subject: [userspace patch] exposing host cpuids to guest From: Nitin A Kamble To: "avi@redhat.com" Cc: "kvm@vger.kernel.org" In-Reply-To: <1233277976.28605.9.camel@mukti.sc.intel.com> References: <1233277976.28605.9.camel@mukti.sc.intel.com> Date: Thu, 29 Jan 2009 17:54:08 -0800 Message-Id: <1233280448.28605.12.camel@mukti.sc.intel.com> Mime-Version: 1.0 X-Mailer: Evolution 2.24.2 (2.24.2-2.fc10) Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Avi, Attached is the patch for kvm-userspace.git tree. With this patch adding "-cpu host" to qemu commandline would expose the host cpuids to the guest. Some of the cpuid data is still filtered in the kernel kvm code. Please apply or give comments for the patch. Thanks & Regards, Nitin On Thu, 2009-01-29 at 17:12 -0800, Nitin A Kamble wrote: > Avi, > I reworked the earlier patch for exposing the host cpuid bits to > guests. Attached is the patch for your kvm.git tree. With this new code > in the kernel both the old and new qemu binaries are working. > > There are also changes for the kvm-userspace.git tree. I will be > sending out those changes too. > > Please apply or give feedback for this patch. > > Thanks & Regards, > Nitin diff --git a/libkvm/libkvm-x86.c b/libkvm/libkvm-x86.c index dcef548..10f6614 100644 --- a/libkvm/libkvm-x86.c +++ b/libkvm/libkvm-x86.c @@ -379,6 +379,37 @@ int kvm_set_msrs(kvm_context_t kvm, int vcpu, struct kvm_msr_entry *msrs, return r; } +/* + * Returns available host cpuid entries. User must free. + */ +struct kvm_cpuid2 *kvm_get_host_cpuid_entries(kvm_context_t kvm) +{ + struct kvm_cpuid2 sizer, *cpuids; + int r, e; + + sizer.nent = 0; + r = ioctl(kvm->fd, KVM_GET_SUPPORTED_CPUID, &sizer); + if (r != -1 && errno != EAGAIN) + return NULL; + + cpuids = malloc(sizeof *cpuids + sizer.nent * sizeof *cpuids->entries); + if (!cpuids) { + errno = ENOMEM; + return NULL; + } + + cpuids->nent = sizer.nent; + r = ioctl(kvm->fd, KVM_GET_SUPPORTED_CPUID, cpuids); + if (r == -1) { + e = errno; + free(cpuids); + errno = e; + return NULL; + } + return cpuids; +} + + static void print_seg(FILE *file, const char *name, struct kvm_segment *seg) { fprintf(stderr, diff --git a/libkvm/libkvm.h b/libkvm/libkvm.h index e79e4fd..5b7f063 100644 --- a/libkvm/libkvm.h +++ b/libkvm/libkvm.h @@ -27,6 +27,9 @@ typedef struct kvm_context *kvm_context_t; struct kvm_msr_list *kvm_get_msr_list(kvm_context_t); int kvm_get_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n); int kvm_set_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n); +struct kvm_cpuid2 *kvm_get_host_cpuid_entries(kvm_context_t); +void get_host_cpuid_entry(uint32_t function, uint32_t index, + struct kvm_cpuid_entry2 *e); #endif /*! diff --git a/qemu/qemu-kvm-x86.c b/qemu/qemu-kvm-x86.c index 01748ed..4c1c159 100644 --- a/qemu/qemu-kvm-x86.c +++ b/qemu/qemu-kvm-x86.c @@ -23,6 +23,7 @@ #define MSR_IA32_TSC 0x10 static struct kvm_msr_list *kvm_msr_list; +static struct kvm_cpuid2 *kvm_host_cpuid_entries; extern unsigned int kvm_shadow_memory; static int kvm_has_msr_star; @@ -57,7 +58,12 @@ int kvm_arch_qemu_create_context(void) for (i = 0; i < kvm_msr_list->nmsrs; ++i) if (kvm_msr_list->indices[i] == MSR_STAR) kvm_has_msr_star = 1; - return 0; + + kvm_host_cpuid_entries = kvm_get_host_cpuid_entries(kvm_context); + if (!kvm_host_cpuid_entries) + return -1; + + return 0; } static void set_msr_entry(struct kvm_msr_entry *entry, uint32_t index, @@ -461,13 +467,61 @@ void kvm_arch_save_regs(CPUState *env) } } +void get_host_cpuid_entry(uint32_t function, uint32_t index, + struct kvm_cpuid_entry2 *e) +{ + int i; + struct kvm_cpuid_entry2 *entries; + + memset(e, 0, (sizeof *e)); + e->function = function; + e->index = index; + + if (!kvm_host_cpuid_entries) + return; + + entries = kvm_host_cpuid_entries->entries; + + for (i=0; inent; i++) { + struct kvm_cpuid_entry2 *ent = &entries[i]; + if (ent->function != function) + continue; + if ((ent->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX) && + (ent->index != index)) + continue; + if ((ent->flags & KVM_CPUID_FLAG_STATEFUL_FUNC) && + !(ent->flags & KVM_CPUID_FLAG_STATE_READ_NEXT)) + continue; + + memcpy(e, ent, sizeof (*e)); + + if (ent->flags & KVM_CPUID_FLAG_STATEFUL_FUNC) { + int j; + ent->flags &= ~KVM_CPUID_FLAG_STATE_READ_NEXT; + for (j=i+1; ;j=(j+1)%(kvm_host_cpuid_entries->nent)) { + struct kvm_cpuid_entry2 *entj = &entries[j]; + if (entj->function == ent->function) { + entj->flags |= KVM_CPUID_FLAG_STATE_READ_NEXT; + break; + } + } + } + break; + } +} + static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function, - uint32_t count, CPUState *env) + uint32_t index, CPUState *env) { + if (env->cpuid_host_cpu) { + get_host_cpuid_entry(function, index, e); + return; + } + e->function = function; + e->index = index; env->regs[R_EAX] = function; - env->regs[R_ECX] = count; + env->regs[R_ECX] = index; qemu_kvm_cpuid_on_env(env); - e->function = function; e->eax = env->regs[R_EAX]; e->ebx = env->regs[R_EBX]; e->ecx = env->regs[R_ECX]; @@ -518,6 +572,10 @@ int kvm_arch_qemu_init_env(CPUState *cenv) copy = *cenv; + if (copy.cpuid_host_cpu) + if (!kvm_host_cpuid_entries) + return -EINVAL; + #ifdef KVM_CPUID_SIGNATURE /* Paravirtualization CPUIDs */ memcpy(signature, "KVMKVMKVM\0\0\0", 12); @@ -535,34 +593,32 @@ int kvm_arch_qemu_init_env(CPUState *cenv) pv_ent->eax = get_para_features(kvm_context); #endif - copy.regs[R_EAX] = 0; - qemu_kvm_cpuid_on_env(©); - limit = copy.regs[R_EAX]; + limit = copy.cpuid_level; for (i = 0; i <= limit; ++i) { - if (i == 4 || i == 0xb || i == 0xd) { + if ( i == 2 || i == 4 || i == 0xb || i == 0xd) { for (j = 0; ; ++j) { do_cpuid_ent(&cpuid_ent[cpuid_nent], i, j, ©); cpuid_ent[cpuid_nent].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; cpuid_ent[cpuid_nent].index = j; - cpuid_nent++; - if (i == 4 && copy.regs[R_EAX] == 0) + if (i == 2 && (cpuid_ent[cpuid_nent-1].eax & 0xff) == (j+1)) { + break; + } + if (i == 4 && (cpuid_ent[cpuid_nent-1].eax & 0x1f) == 0) break; - if (i == 0xb && !(copy.regs[R_ECX] & 0xff00)) + if (i == 0xb && !(cpuid_ent[cpuid_nent-1].ecx & 0xff00)) break; - if (i == 0xd && copy.regs[R_EAX] == 0) + if (i == 0xd && cpuid_ent[cpuid_nent-1].eax == 0) break; } } else do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©); } - copy.regs[R_EAX] = 0x80000000; - qemu_kvm_cpuid_on_env(©); - limit = copy.regs[R_EAX]; + limit = copy.cpuid_xlevel; for (i = 0x80000000; i <= limit; ++i) do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©); diff --git a/qemu/target-i386/cpu.h b/qemu/target-i386/cpu.h index 944e386..0a0fccf 100644 --- a/qemu/target-i386/cpu.h +++ b/qemu/target-i386/cpu.h @@ -628,6 +628,7 @@ typedef struct CPUX86State { uint32_t cpuid_ext2_features; uint32_t cpuid_ext3_features; uint32_t cpuid_apic_id; + uint32_t cpuid_host_cpu; #ifdef USE_KQEMU int kqemu_enabled; diff --git a/qemu/target-i386/helper.c b/qemu/target-i386/helper.c index cda0390..43de18f 100644 --- a/qemu/target-i386/helper.c +++ b/qemu/target-i386/helper.c @@ -119,6 +119,9 @@ typedef struct x86_def_t { static x86_def_t x86_defs[] = { #ifdef TARGET_X86_64 { + .name = "host", + }, + { .name = "qemu64", .level = 2, .vendor1 = CPUID_VENDOR_AMD_1, @@ -372,10 +375,57 @@ void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) (*cpu_fprintf)(f, "x86 %16s\n", x86_defs[i].name); } +static int cpu_host_x86_register (CPUX86State *env) +{ + struct kvm_cpuid_entry2 e; + env->cpuid_host_cpu = 1; + + get_host_cpuid_entry(0, 0, &e); + env->cpuid_level = e.eax; + env->cpuid_vendor1 = e.ebx; + env->cpuid_vendor2 = e.ecx; + env->cpuid_vendor3 = e.edx; + + get_host_cpuid_entry(1, 0, &e); + env->cpuid_version = e.eax; + env->cpuid_features = e.edx; + env->cpuid_ext_features = e.ecx; + + get_host_cpuid_entry(0x80000000, 0, &e); + env->cpuid_xlevel = e.eax; + + get_host_cpuid_entry(0x80000001, 0, &e); + env->cpuid_ext3_features = e.ecx; + env->cpuid_ext2_features = e.edx; + + get_host_cpuid_entry(0x80000002, 0, &e); + env->cpuid_model[0] = e.eax; + env->cpuid_model[1] = e.ebx; + env->cpuid_model[2] = e.ecx; + env->cpuid_model[3] = e.edx; + + get_host_cpuid_entry(0x80000003, 0, &e); + env->cpuid_model[4] = e.eax; + env->cpuid_model[5] = e.ebx; + env->cpuid_model[6] = e.ecx; + env->cpuid_model[7] = e.edx; + + get_host_cpuid_entry(0x80000004, 0, &e); + env->cpuid_model[8] = e.eax; + env->cpuid_model[9] = e.ebx; + env->cpuid_model[10] = e.ecx; + env->cpuid_model[11] = e.edx; + + return 0; +} + static int cpu_x86_register (CPUX86State *env, const char *cpu_model) { x86_def_t def1, *def = &def1; + if (0 == strcmp(cpu_model, "host")) + return cpu_host_x86_register(env); + if (cpu_x86_find_by_name(def, cpu_model) < 0) return -1; if (def->vendor1) {