diff mbox

[userspace] exposing host cpuids to guest

Message ID 1233280448.28605.12.camel@mukti.sc.intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Nitin A Kamble Jan. 30, 2009, 1:54 a.m. UTC
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

Comments

Amit Shah Jan. 30, 2009, 10:57 a.m. UTC | #1
On (Thu) Jan 29 2009 [17:54:08], Nitin A Kamble wrote:
> 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.

Please split them into multiple patches; they do a lot of things here.
It's easier to review once all the logical changes are separated out.

Also, split out the qemu-specific (qemu/target-i386/*) and kvm-specific
parts so that the qemu-specific ones can be sent upstream.

Thanks,
Amit
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Nitin A Kamble Jan. 30, 2009, 7:40 p.m. UTC | #2
On Fri, 2009-01-30 at 02:57 -0800, Amit Shah wrote:
> Please split them into multiple patches; they do a lot of things here.
> It's easier to review once all the logical changes are separated out.
> 
> Also, split out the qemu-specific (qemu/target-i386/*) and kvm-specific
> parts so that the qemu-specific ones can be sent upstream.

ok, I will try to split all the patches like that. My Concern is, The
code is inter-dependent, if all splits are not in then it may break the
tree's functionality. I will try to group the split patches for that
reason.

Thanks & Regards,
Nitin


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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; i<kvm_host_cpuid_entries->nent; 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(&copy);
-    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, &copy);
 
                 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);
     }
 
-    copy.regs[R_EAX] = 0x80000000;
-    qemu_kvm_cpuid_on_env(&copy);
-    limit = copy.regs[R_EAX];
+    limit = copy.cpuid_xlevel;
 
     for (i = 0x80000000; i <= limit; ++i)
 	do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, &copy);
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) {