[userspace patch] exposing host cpuids to guest

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

 



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; 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) {

[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux