The first little patch (already swallowed by qemu-devel) corrects the
cpuid_level exposed for various cpu models, and lets us change it.

The main attached patch is the userspace part of cpuid support. Please
comment if you find something that is not to your liking.

Regards,
    Dan.
diff --git a/qemu/target-i386/helper2.c b/qemu/target-i386/helper2.c
index 1874b73..da7a54c 100644
--- a/qemu/target-i386/helper2.c
+++ b/qemu/target-i386/helper2.c
@@ -125,6 +125,7 @@ CPUX86State *cpu_x86_init(const char *cpu_model)
 
 typedef struct x86_def_t {
     const char *name;
+    uint32_t level;
     uint32_t vendor1, vendor2, vendor3;
     int family;
     int model;
@@ -141,6 +142,7 @@ static x86_def_t x86_defs[] = {
 #ifdef TARGET_X86_64
     {
         .name = "qemu64",
+        .level = 2,
         .vendor1 = 0x68747541, /* "Auth" */
         .vendor2 = 0x69746e65, /* "enti" */
         .vendor3 = 0x444d4163, /* "cAMD" */
@@ -161,6 +163,7 @@ static x86_def_t x86_defs[] = {
 #endif
     {
         .name = "qemu32",
+        .level = 2,
         .family = 6,
         .model = 3,
         .stepping = 3,
@@ -170,6 +173,7 @@ static x86_def_t x86_defs[] = {
     },
     {
         .name = "486",
+        .level = 0,
         .family = 4,
         .model = 0,
         .stepping = 0,
@@ -178,6 +182,7 @@ static x86_def_t x86_defs[] = {
     },
     {
         .name = "pentium",
+        .level = 1,
         .family = 5,
         .model = 4,
         .stepping = 3,
@@ -186,6 +191,7 @@ static x86_def_t x86_defs[] = {
     },
     {
         .name = "pentium2",
+        .level = 2,
         .family = 6,
         .model = 5,
         .stepping = 2,
@@ -194,6 +200,7 @@ static x86_def_t x86_defs[] = {
     },
     {
         .name = "pentium3",
+        .level = 2,
         .family = 6,
         .model = 7,
         .stepping = 3,
@@ -312,7 +319,7 @@ static int cpu_x86_register (CPUX86State *env, const char 
*cpu_model)
         env->cpuid_vendor2 = 0x49656e69; /* "ineI" */
         env->cpuid_vendor3 = 0x6c65746e; /* "ntel" */
     }
-    env->cpuid_level = 2;
+    env->cpuid_level = def->level;
     env->cpuid_version = (def->family << 8) | (def->model << 4) | 
def->stepping;
     env->cpuid_features = def->features;
     env->pat = 0x0007040600070406ULL;
commit cd7d63ca03061e8c0dd7d71606779ab1d7b6c4cd
Author: Dan Kenigsberg <[EMAIL PROTECTED]>
Date:   Thu Dec 20 19:34:18 2007 +0200

    Add -cpu host option. Expose to guest the CPUID that is supported by the
    host (or specifically requested in the command line).
    Note that the default cpu is now `host' instead of `qemu64'
    
    Signed-off-by: Dan Kenigsberg <[EMAIL PROTECTED]>

diff --git a/libkvm/libkvm-x86.c b/libkvm/libkvm-x86.c
index ca56adb..e4f5936 100644
--- a/libkvm/libkvm-x86.c
+++ b/libkvm/libkvm-x86.c
@@ -452,10 +452,40 @@ __u64 kvm_get_cr8(kvm_context_t kvm, int vcpu)
 }
 
 int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent,
-                   struct kvm_cpuid_entry *entries)
+                   struct kvm_cpuid_entry2 *entries)
 {
-       struct kvm_cpuid *cpuid;
-       int r;
+       int r = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+       r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+       if (r <= 0) 
+#endif
+       { /* kernel has no KVM_SET_CPUID2 */
+               int i;
+               struct kvm_cpuid *cpuid;
+
+               cpuid = malloc(sizeof(*cpuid) +
+                               nent * sizeof(struct kvm_cpuid_entry));
+               if (!cpuid)
+                       return -ENOMEM;
+
+               cpuid->nent = nent;
+               for (i = 0; i < nent; ++i) {
+                       cpuid->entries[i].function = entries[i].function;
+                       cpuid->entries[i].eax = entries[i].eax;
+                       cpuid->entries[i].ebx = entries[i].ebx;
+                       cpuid->entries[i].ecx = entries[i].ecx;
+                       cpuid->entries[i].edx = entries[i].edx;
+               }
+               r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID, cpuid);
+               if (r)
+                       r = -errno;
+
+               free(cpuid);
+       }
+#ifdef KVM_CAP_EXT_CPUID
+       else {
+               struct kvm_cpuid2 *cpuid;
 
        cpuid = malloc(sizeof(*cpuid) + nent * sizeof(*entries));
        if (!cpuid)
@@ -463,9 +493,69 @@ int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent,
 
        cpuid->nent = nent;
        memcpy(cpuid->entries, entries, nent * sizeof(*entries));
-       r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID, cpuid);
+               r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID2, cpuid);
+               if (r)
+                       r = -errno;
+
+               free(cpuid);
+       }
+#endif
+       return r;
+}
+
+int kvm_get_supported_cpuid(kvm_context_t kvm, int *nent,
+               struct kvm_cpuid_entry2 *entries)
+{
+       struct kvm_cpuid2 *cpuid;
+       int r = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+       r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+       if (r <= 0)
+               return -ENOSYS;
+
+       cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+       if (!cpuid)
+               return -ENOMEM;
+       cpuid->nent = *nent;
 
+       r = ioctl(kvm->vm_fd, KVM_GET_SUPPORTED_CPUID, cpuid);
+       if (r)
+               r = -errno;
+       else {
+               memcpy(entries, cpuid->entries, *nent * sizeof(*entries));
+               *nent = cpuid->nent;
+       }
+       free(cpuid);
+#endif
+       return r;
+}
+
+int kvm_get_cpuid(kvm_context_t kvm, int vcpu, int *nent,
+                   struct kvm_cpuid_entry2 *entries)
+{
+       struct kvm_cpuid2 *cpuid;
+       int r = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+       r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+       if (r <= 0)
+               return -ENOSYS;
+
+       cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+       if (!cpuid)
+               return -ENOMEM;
+       cpuid->nent = *nent;
+
+       r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_CPUID2, cpuid);
+       if (r)
+               r = -errno;
+       else {
+               memcpy(entries, cpuid->entries, *nent * sizeof(*entries));
+               *nent = cpuid->nent;
+       }
        free(cpuid);
+#endif
        return r;
 }
 
diff --git a/libkvm/libkvm.h b/libkvm/libkvm.h
index 110912a..21cb508 100644
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -310,6 +310,19 @@ int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned 
irq);
 int kvm_guest_debug(kvm_context_t, int vcpu, struct kvm_debug_guest *dbg);
 
 #if defined(__i386__) || defined(__x86_64__)
+#ifndef KVM_CAP_EXT_CPUID
+/* for compilation against old kernels */
+struct kvm_cpuid_entry2 {
+       __u32 function;
+       __u32 index;
+       __u32 flags;
+       __u32 eax;
+       __u32 ebx;
+       __u32 ecx;
+       __u32 edx;
+       __u32 padding[3];
+};
+#endif
 /*!
  * \brief Setup a vcpu's cpuid instruction emulation
  *
@@ -322,7 +335,36 @@ int kvm_guest_debug(kvm_context_t, int vcpu, struct 
kvm_debug_guest *dbg);
  * \return 0 on success, or -errno on error
  */
 int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent,
-                   struct kvm_cpuid_entry *entries);
+                   struct kvm_cpuid_entry2 *entries);
+
+/*!
+ * \brief Obtain vcpu's cpuid table
+ *
+ * Get the currently-held table of cpuid functions and their states.\n
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be initialized
+ * \param nent Pointer to the number of entries to be obtained.
+ *             On return, the number that was actually obtained.
+ * \param entries cpuid function entries table
+ * \return 0 on success, or -errno on error
+ */
+int kvm_get_cpuid(kvm_context_t kvm, int vcpu, int *nent,
+                   struct kvm_cpuid_entry2 *entries);
+
+/*!
+ * \brief Obtain cpuid supported by kvm
+ *
+ * Obtain a table of cpuid function supported by kvm.\n
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param nent Pointer to the number of entries to be obtained.
+ *             On return, the number that was actually obtained.
+ * \param entries cpuid function entries table
+ * \return 0 on success, or -errno on error
+ */
+int kvm_get_supported_cpuid(kvm_context_t kvm, int *nent,
+       struct kvm_cpuid_entry2 *entries);
 
 /*!
  * \brief Setting the number of shadow pages to be allocated to the vm
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index dde40c3..b653c46 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -751,6 +751,10 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
 #else
         cpu_model = "qemu32";
 #endif
+#ifdef USE_KVM
+        if (kvm_allowed)
+            cpu_model = "host";
+#endif
     }
     
     for(i = 0; i < smp_cpus; i++) {
diff --git a/qemu/qemu-kvm-x86.c b/qemu/qemu-kvm-x86.c
index d86fec3..5e3b5f7 100644
--- a/qemu/qemu-kvm-x86.c
+++ b/qemu/qemu-kvm-x86.c
@@ -461,8 +461,8 @@ static void host_cpuid(uint32_t function, uint32_t *eax, 
uint32_t *ebx,
 }
 
 
-static void do_cpuid_ent(struct kvm_cpuid_entry *e, uint32_t function,
-                        CPUState *env)
+static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function,
+                        CPUState *env, int keep_index_flags)
 {
     env->regs[R_EAX] = function;
     qemu_kvm_cpuid_on_env(env);
@@ -471,6 +471,8 @@ static void do_cpuid_ent(struct kvm_cpuid_entry *e, 
uint32_t function,
     e->ebx = env->regs[R_EBX];
     e->ecx = env->regs[R_ECX];
     e->edx = env->regs[R_EDX];
+    if (!keep_index_flags)
+        e->index = e->flags = 0;
     if (function == 0x80000001) {
        uint32_t h_eax, h_edx;
        struct utsname utsname;
@@ -506,11 +508,28 @@ static void do_cpuid_ent(struct kvm_cpuid_entry *e, 
uint32_t function,
     }
 }
 
+static struct kvm_cpuid_entry2 *cpuid_entry_lookup(struct kvm_cpuid_entry2 
*entries,
+       int nent, uint32_t function, uint32_t index)
+{
+    int i;
+
+    for (i = 0; i < nent; ++i) {
+        struct kvm_cpuid_entry2 *e = &entries[i];
+        if (e->function == function &&
+            (!(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX)
+               || e->index == index)) {
+                        return e;
+        }
+    }
+    fprintf(stderr, "failed to find cpuid func %x index %x\n", function, 
index);
+    return 0;
+}
+
 int kvm_arch_qemu_init_env(CPUState *cenv)
 {
-    struct kvm_cpuid_entry cpuid_ent[100];
+    struct kvm_cpuid_entry2 cpuid_ent[100];
 #ifdef KVM_CPUID_SIGNATURE
-    struct kvm_cpuid_entry *pv_ent;
+    struct kvm_cpuid_entry2 *pv_ent;
     uint32_t signature[3];
 #endif
     int cpuid_nent = 0;
@@ -519,6 +538,72 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
 
     copy = *cenv;
 
+    if (cenv->cpuid_hostlike) {
+        struct kvm_cpuid_entry2 *e;
+        int r;
+        extern struct hostlike_user_disable {
+            uint32_t features;
+            uint32_t ext_features;
+            uint32_t ext2_features;
+            uint32_t ext3_features;
+        } hostlike_user_disable;
+
+        cpuid_nent = sizeof(cpuid_ent) / sizeof(struct kvm_cpuid_entry2);
+        r = kvm_get_supported_cpuid(kvm_context, &cpuid_nent, cpuid_ent);
+        if (r) {
+            printf("failed to obtain supported cpuid from kvm module (%d)\n", 
r);
+            exit(1);
+        }
+
+        e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0, 0);
+        copy.cpuid_level = cenv->cpuid_level = e->eax;
+        e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0x80000000, 0);
+        copy.cpuid_xlevel = cenv->cpuid_xlevel = e->eax;
+
+        /* override some of the advertized data with qemu's */
+        /* family-model-stepping */
+        e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 1, 0);
+        if (cenv->cpuid_version >> 8 == 0)
+            cenv->cpuid_version |= e->eax & 0xFF00;
+        if ((cenv->cpuid_version >> 4 & 0xF) == 0)
+            cenv->cpuid_version |= e->eax & 0xF0;
+        if ((cenv->cpuid_version & 0xF) == 0)
+            cenv->cpuid_version |= e->eax & 0xF;
+        e->eax = cenv->cpuid_version;
+        e->ecx = (e->ecx | cenv->cpuid_ext_features) & 
~hostlike_user_disable.ext_features;
+        e->edx = (e->edx | cenv->cpuid_features) & 
~hostlike_user_disable.features;
+
+        /* extended features */
+        e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0x80000001, 0);
+        e->eax = (e->eax | cenv->cpuid_features) & 
~hostlike_user_disable.features;
+        e->ecx = (e->ecx | cenv->cpuid_ext3_features) & 
~hostlike_user_disable.ext3_features;
+        e->edx = (e->edx | cenv->cpuid_ext2_features) & 
~hostlike_user_disable.ext2_features;
+
+        /* cpuid_model */
+        {
+            extern const char *cpu_vendor_string;
+            if (cpu_vendor_string)
+                for (i = 0x80000002; i <= 0x80000004; ++i) {
+                    e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, i, 0);
+                    do_cpuid_ent(e, i, &copy, 1);
+                }
+        }
+    } else {
+        copy.regs[R_EAX] = 0;
+        qemu_kvm_cpuid_on_env(&copy);
+        limit = copy.regs[R_EAX];
+
+        for (i = 0; i <= limit; ++i)
+            do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, &copy, 0);
+
+        copy.regs[R_EAX] = 0x80000000;
+        qemu_kvm_cpuid_on_env(&copy);
+        limit = copy.regs[R_EAX];
+
+        for (i = 0x80000000; i <= limit; ++i)
+            do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, &copy, 0);
+    }
+
 #ifdef KVM_CPUID_SIGNATURE
     /* Paravirtualization CPUIDs */
     memcpy(signature, "KVMKVMKVM", 12);
@@ -536,20 +621,6 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
     pv_ent->eax = 0;
 #endif
 
-    copy.regs[R_EAX] = 0;
-    qemu_kvm_cpuid_on_env(&copy);
-    limit = copy.regs[R_EAX];
-
-    for (i = 0; i <= limit; ++i)
-       do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, &copy);
-
-    copy.regs[R_EAX] = 0x80000000;
-    qemu_kvm_cpuid_on_env(&copy);
-    limit = copy.regs[R_EAX];
-
-    for (i = 0x80000000; i <= limit; ++i)
-       do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, &copy);
-
     kvm_setup_cpuid(kvm_context, cenv->cpu_index, cpuid_nent, cpuid_ent);
     return 0;
 }
diff --git a/qemu/target-i386/cpu.h b/qemu/target-i386/cpu.h
index 7143ab3..2034381 100644
--- a/qemu/target-i386/cpu.h
+++ b/qemu/target-i386/cpu.h
@@ -588,6 +588,9 @@ typedef struct CPUX86State {
     uint32_t cpuid_ext2_features;
     uint32_t cpuid_ext3_features;
     uint32_t cpuid_apic_id;
+#ifdef USE_KVM
+    int cpuid_hostlike;
+#endif
 
 #ifdef USE_KQEMU
     int kqemu_enabled;
diff --git a/qemu/target-i386/helper2.c b/qemu/target-i386/helper2.c
index da7a54c..78c5cf9 100644
--- a/qemu/target-i386/helper2.c
+++ b/qemu/target-i386/helper2.c
@@ -123,6 +123,15 @@ CPUX86State *cpu_x86_init(const char *cpu_model)
     return env;
 }
 
+#ifdef USE_KVM
+struct hostlike_user_disable {
+    uint32_t features;
+    uint32_t ext_features;
+    uint32_t ext2_features;
+    uint32_t ext3_features;
+} hostlike_user_disable;
+#endif
+
 typedef struct x86_def_t {
     const char *name;
     uint32_t level;
@@ -131,6 +140,9 @@ typedef struct x86_def_t {
     int model;
     int stepping;
     uint32_t features, ext_features, ext2_features, ext3_features;
+#ifdef USE_KVM
+    int hostlike;
+#endif
     uint32_t xlevel;
 } x86_def_t;
 
@@ -207,6 +219,12 @@ static x86_def_t x86_defs[] = {
         .features = 0x0383F9FF,
         .xlevel = 0,
     },
+#ifdef USE_KVM
+    {
+        .name = "host",
+        .hostlike = 1,
+    },
+#endif
 };
 
 static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model)
@@ -229,6 +247,12 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, 
const char *cpu_model)
     }
     if (!def)
         goto error;
+#ifdef USE_KVM
+    if (!kvm_allowed && def->hostlike) {
+        fprintf(stderr, "hostlike cpu requires KVM.\n");
+        goto error;
+    }
+#endif
     memcpy(x86_cpu_def, def, sizeof(*def));
 
     featurestr = strtok(NULL, ",");
@@ -288,6 +312,14 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, 
const char *cpu_model)
     x86_cpu_def->ext_features &= ~minus_ext_features;
     x86_cpu_def->ext2_features &= ~minus_ext2_features;
     x86_cpu_def->ext3_features &= ~minus_ext3_features;
+#ifdef USE_KVM
+    if (kvm_allowed) {
+        hostlike_user_disable.features = minus_features;
+        hostlike_user_disable.ext_features = minus_ext_features;
+        hostlike_user_disable.ext2_features = minus_ext2_features;
+        hostlike_user_disable.ext3_features = minus_ext3_features;
+    }
+#endif
     free(s);
     return 0;
 
@@ -341,6 +373,9 @@ static int cpu_x86_register (CPUX86State *env, const char 
*cpu_model)
             env->cpuid_model[i >> 2] |= c << (8 * (i & 3));
         }
     }
+#ifdef USE_KVM
+    env->cpuid_hostlike = def->hostlike;
+#endif
     return 0;
 }
 
-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2005.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/
_______________________________________________
kvm-devel mailing list
kvm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kvm-devel

Reply via email to