These patches expose host CPU features (that are known to work under
KVM) to guests. It makes a couple of benchmarks run faster, and
generally gives kvm's user better info on its host.

The kernel-space patch adds KVM_GET_SUPPORTED_CPUID ioctl to obtain the
table of cpuid functions supported by the host. The user-space patch
allows fine-tuning this table from the command-line.

I had to define struct kvm_cpuid2, KVM_SET_CPUID2 etc., because cpuid
functions are a little more complex than just function-value pairs.

Dan.
commit e9775d0a16097cfb71779cb2fb985fb3e5040dc8
Author: Dan Kenigsberg <[EMAIL PROTECTED]>
Date:   Sun Nov 18 13:55:26 2007 +0200

    Support -cpu host option. Negotiate cpuid table with userspace.
    
    Signed-off-by: Dan Kenigsberg <[EMAIL PROTECTED]>

diff --git a/drivers/kvm/x86.c b/drivers/kvm/x86.c
index b42c9d4..5240e5f 100644
--- a/drivers/kvm/x86.c
+++ b/drivers/kvm/x86.c
@@ -626,6 +626,7 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_MMU_SHADOW_CACHE_CONTROL:
        case KVM_CAP_USER_MEMORY:
        case KVM_CAP_SET_TSS_ADDR:
+       case KVM_CAP_EXT_CPUID:
                r = 1;
                break;
        default:
@@ -688,13 +689,17 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
        kvm_put_guest_fpu(vcpu);
 }
 
+static int is_efer_nx(void) {
+       u64 efer;
+       rdmsrl(MSR_EFER, efer);
+       return efer & EFER_NX;
+}
+
 static void cpuid_fix_nx_cap(struct kvm_vcpu *vcpu)
 {
-       u64 efer;
        int i;
-       struct kvm_cpuid_entry *e, *entry;
+       struct kvm_cpuid_entry2 *e, *entry;
 
-       rdmsrl(MSR_EFER, efer);
        entry = NULL;
        for (i = 0; i < vcpu->cpuid_nent; ++i) {
                e = &vcpu->cpuid_entries[i];
@@ -703,16 +708,56 @@ static void cpuid_fix_nx_cap(struct kvm_vcpu *vcpu)
                        break;
                }
        }
-       if (entry && (entry->edx & (1 << 20)) && !(efer & EFER_NX)) {
+       if (entry && (entry->edx & (1 << 20)) && !is_efer_nx()) {
                entry->edx &= ~(1 << 20);
                printk(KERN_INFO "kvm: guest NX capability removed\n");
        }
 }
 
+/* when an old userspace process fills a new kernel module */
 static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu,
                                    struct kvm_cpuid *cpuid,
                                    struct kvm_cpuid_entry __user *entries)
 {
+       int r, i;
+       struct kvm_cpuid_entry *cpuid_entries;
+
+       r = -E2BIG;
+       if (cpuid->nent > KVM_MAX_CPUID_ENTRIES)
+               goto out;
+       r = -ENOMEM;
+       cpuid_entries = vmalloc(sizeof(struct kvm_cpuid_entry) * cpuid->nent);
+       if (!cpuid_entries)
+               goto out;
+       r = -EFAULT;
+       if (copy_from_user(cpuid_entries, entries,
+                          cpuid->nent * sizeof(struct kvm_cpuid_entry)))
+               goto out_free;
+       for (i = 0; i < cpuid->nent; i++) {
+               vcpu->cpuid_entries[i].function = cpuid_entries[i].function;
+               vcpu->cpuid_entries[i].eax = cpuid_entries[i].eax;
+               vcpu->cpuid_entries[i].ebx = cpuid_entries[i].ebx;
+               vcpu->cpuid_entries[i].ecx = cpuid_entries[i].ecx;
+               vcpu->cpuid_entries[i].edx = cpuid_entries[i].edx;
+               vcpu->cpuid_entries[i].index = 0;
+               vcpu->cpuid_entries[i].flags = 0;
+               vcpu->cpuid_entries[i].padding[0] = 0;
+               vcpu->cpuid_entries[i].padding[1] = 0;
+               vcpu->cpuid_entries[i].padding[2] = 0;
+       }
+       vcpu->cpuid_nent = cpuid->nent;
+       r = 0;
+
+out_free:
+       vfree(cpuid_entries);
+out:
+       return r;
+}
+
+static int kvm_vcpu_ioctl_set_cpuid2(struct kvm_vcpu *vcpu,
+                                   struct kvm_cpuid2 *cpuid,
+                                   struct kvm_cpuid_entry2 __user *entries)
+{
        int r;
 
        r = -E2BIG;
@@ -720,7 +765,7 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu,
                goto out;
        r = -EFAULT;
        if (copy_from_user(&vcpu->cpuid_entries, entries,
-                          cpuid->nent * sizeof(struct kvm_cpuid_entry)))
+                          cpuid->nent * sizeof(struct kvm_cpuid_entry2)))
                goto out;
        vcpu->cpuid_nent = cpuid->nent;
        cpuid_fix_nx_cap(vcpu);
@@ -730,6 +775,192 @@ out:
        return r;
 }
 
+static int kvm_vcpu_ioctl_get_cpuid2(struct kvm_vcpu *vcpu,
+                                   struct kvm_cpuid2 *cpuid,
+                                   struct kvm_cpuid_entry2 __user *entries)
+{
+       int r;
+
+       r = -E2BIG;
+       if (cpuid->nent < vcpu->cpuid_nent)
+               goto out;
+       r = -EFAULT;
+       if (copy_to_user(entries, &vcpu->cpuid_entries,
+                          vcpu->cpuid_nent * sizeof(struct kvm_cpuid_entry2)))
+               goto out;
+       return 0;
+
+out:
+        cpuid->nent = vcpu->cpuid_nent;
+       return r;
+}
+
+static inline u32 bit(int bitno)
+{
+       return 1 << (bitno & 31);
+}
+
+static void do_cpuid_1_ent(struct kvm_cpuid_entry2 *entry, u32 function,
+                         u32 index)
+{
+       entry->function = function;
+       entry->index = index;
+       cpuid_count(entry->function, entry->index,
+               &entry->eax, &entry->ebx, &entry->ecx, &entry->edx);
+       entry->flags = 0;
+}
+
+static void do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
+                        u32 index, int *nent, int maxnent)
+{
+       const __u32 kvm_supported_word0_x86_features = bit(X86_FEATURE_FPU) |
+               bit(X86_FEATURE_VME) | bit(X86_FEATURE_DE) |
+               bit(X86_FEATURE_PSE) | bit(X86_FEATURE_TSC) |
+               bit(X86_FEATURE_MSR) | bit(X86_FEATURE_PAE) |
+               bit(X86_FEATURE_CX8) | bit(X86_FEATURE_APIC) |
+               bit(X86_FEATURE_SEP) | bit(X86_FEATURE_PGE) |
+               bit(X86_FEATURE_CMOV) | bit(X86_FEATURE_PSE36) |
+               bit(X86_FEATURE_CLFLSH) | bit(X86_FEATURE_MMX) |
+               bit(X86_FEATURE_FXSR) | bit(X86_FEATURE_XMM) |
+               bit(X86_FEATURE_XMM2) | bit(X86_FEATURE_SELFSNOOP);
+       const __u32 kvm_supported_word1_x86_features = bit(X86_FEATURE_FPU) |
+               bit(X86_FEATURE_VME) | bit(X86_FEATURE_DE) |
+               bit(X86_FEATURE_PSE) | bit(X86_FEATURE_TSC) |
+               bit(X86_FEATURE_MSR) | bit(X86_FEATURE_PAE) |
+               bit(X86_FEATURE_CX8) | bit(X86_FEATURE_APIC) |
+               bit(X86_FEATURE_PGE) |
+               bit(X86_FEATURE_CMOV) | bit(X86_FEATURE_PSE36) |
+               bit(X86_FEATURE_MMX) | bit(X86_FEATURE_FXSR) |
+               bit(X86_FEATURE_SYSCALL) |
+               (bit(X86_FEATURE_NX) && is_efer_nx()) |
+#ifdef CONFIG_X86_64
+               bit(X86_FEATURE_LM) |
+#endif
+               /* TODO: make sure the following features are
+                * safe for SVM guests */
+               bit(X86_FEATURE_MMXEXT) |
+               bit(X86_FEATURE_RDTSCP) | bit(X86_FEATURE_3DNOWEXT) |
+               bit(X86_FEATURE_3DNOW);
+       const __u32 kvm_supported_word3_x86_features =
+               bit(X86_FEATURE_XMM3) | bit(X86_FEATURE_CX16);
+       const __u32 kvm_supported_word6_x86_features =
+               bit(X86_FEATURE_LAHF_LM) | bit(X86_FEATURE_CMP_LEGACY);
+
+       /* all func 2 cpuid_count() should be called on the same cpu */
+       if (function==2)
+               get_cpu();
+       do_cpuid_1_ent(entry, function, index);
+       ++*nent;
+
+       switch (function) {
+       case 0:
+               entry->eax = min(entry->eax, (u32)0xb);
+               break;
+       case 1:
+               entry->edx &= kvm_supported_word0_x86_features;
+               entry->ecx &= kvm_supported_word3_x86_features;
+               break;
+       /* function 2 entries are STATEFUL. That is, repeated cpuid commands
+        * may return different values. This forces us to get_cpu() before
+        * issuing the first command, and also to emulate this annoying behavior
+        * in kvm_emulate_cpuid() using KVM_CPUID_FLAG_STATE_READ_NEXT */
+       case 2: {
+                       int t, times = entry->eax & 0xff;
+
+                       entry->flags |= KVM_CPUID_FLAG_STATEFUL_FUNC;
+                       for (t = 1; t < times && *nent < maxnent; ++t) {
+                               do_cpuid_1_ent(&entry[t], function, 0);
+                               entry[t].flags |= KVM_CPUID_FLAG_STATEFUL_FUNC;
+                               ++*nent;
+                       }
+                       break;
+               }
+       /* function 4 and 0xb have additional index. */
+       case 4: {
+                       int index, cache_type;
+
+                       entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+                       /* read more entries until cache_type is zero */
+                       for (index = 1; *nent < maxnent; ++index) {
+                               cache_type = entry[index - 1].eax & 0x1f;
+                               if (!cache_type)
+                                       break;
+                               do_cpuid_1_ent(&entry[index], function, index);
+                               entry[index].flags |=
+                                      KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+                               ++*nent;
+                       }
+                       break;
+               }
+       case 0xb: {
+                       int index, level_type;
+
+                       entry->flags |= KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+                       /* read more entries until level_type is zero */
+                       for (index = 1; *nent < maxnent; ++index) {
+                               level_type = entry[index - 1].ecx & 0xff;
+                               if (!level_type)
+                                       break;
+                               do_cpuid_1_ent(&entry[index], function, index);
+                               entry[index].flags |=
+                                      KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
+                               ++*nent;
+                       }
+                       break;
+               }
+       case 0x80000000:
+               entry->eax = min(entry->eax, 0x8000001a);
+                break;
+       case 0x80000001:
+               entry->edx &= kvm_supported_word1_x86_features;
+               entry->ecx &= kvm_supported_word6_x86_features;
+               break;
+       }
+       if (function==2)
+               put_cpu();
+}
+
+static int kvm_vm_ioctl_get_supported_cpuid(struct kvm *kvm,
+                                   struct kvm_cpuid2 *cpuid,
+                                   struct kvm_cpuid_entry2 __user *entries)
+{
+       struct kvm_cpuid_entry2 *cpuid_entries;
+       int limit, nent = 0, r = -E2BIG;
+        u32 func;
+
+       if (cpuid->nent < 1)
+               goto out;
+       r = -ENOMEM;
+       cpuid_entries = vmalloc(sizeof(struct kvm_cpuid_entry2) * cpuid->nent);
+       if (!cpuid_entries)
+               goto out;
+
+       do_cpuid_ent(&cpuid_entries[0], 0, 0, &nent, cpuid->nent);
+       limit = cpuid_entries[0].eax;
+       for (func = 1; func <= limit && nent < cpuid->nent; ++func)
+               do_cpuid_ent(&cpuid_entries[nent], func, 0,
+                               &nent, cpuid->nent);
+
+       if (nent >= cpuid->nent)
+               goto out_free;
+       do_cpuid_ent(&cpuid_entries[nent], 0x80000000, 0, &nent, cpuid->nent);
+       limit = cpuid_entries[nent - 1].eax;
+       for (func = 0x80000001; func <= limit && nent < cpuid->nent; ++func)
+               do_cpuid_ent(&cpuid_entries[nent], func, 0,
+                              &nent, cpuid->nent);
+       r = -EFAULT;
+       if (copy_to_user(entries, cpuid_entries,
+                       nent * sizeof(struct kvm_cpuid_entry2)))
+               goto out_free;
+       cpuid->nent = nent;
+       r = 0;
+
+out_free:
+       vfree(cpuid_entries);
+out:
+       return r;
+}
+
 static int kvm_vcpu_ioctl_get_lapic(struct kvm_vcpu *vcpu,
                                    struct kvm_lapic_state *s)
 {
@@ -796,6 +1027,36 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
                        goto out;
                break;
        }
+       case KVM_SET_CPUID2: {
+               struct kvm_cpuid2 __user *cpuid_arg = argp;
+               struct kvm_cpuid2 cpuid;
+
+               r = -EFAULT;
+               if (copy_from_user(&cpuid, cpuid_arg, sizeof cpuid))
+                       goto out;
+               r = kvm_vcpu_ioctl_set_cpuid2(vcpu, &cpuid,
+                               cpuid_arg->entries);
+               if (r)
+                       goto out;
+               break;
+       }
+       case KVM_GET_CPUID2: {
+               struct kvm_cpuid2 __user *cpuid_arg = argp;
+               struct kvm_cpuid2 cpuid;
+
+               r = -EFAULT;
+               if (copy_from_user(&cpuid, cpuid_arg, sizeof cpuid))
+                       goto out;
+               r = kvm_vcpu_ioctl_get_cpuid2(vcpu, &cpuid,
+                               cpuid_arg->entries);
+               if (r)
+                       goto out;
+               r = -EFAULT;
+               if (copy_to_user(cpuid_arg, &cpuid, sizeof cpuid))
+                       goto out;
+               r = 0;
+               break;
+       }
        case KVM_GET_MSRS:
                r = msr_io(vcpu, argp, kvm_get_msr, 1);
                break;
@@ -1091,6 +1352,24 @@ long kvm_arch_vm_ioctl(struct file *filp,
                r = 0;
                break;
        }
+       case KVM_GET_SUPPORTED_CPUID: {
+               struct kvm_cpuid2 __user *cpuid_arg = argp;
+               struct kvm_cpuid2 cpuid;
+
+               r = -EFAULT;
+               if (copy_from_user(&cpuid, cpuid_arg, sizeof cpuid))
+                       goto out;
+               r = kvm_vm_ioctl_get_supported_cpuid(kvm, &cpuid,
+                       cpuid_arg->entries);
+               if (r)
+                       goto out;
+
+               r = -EFAULT;
+               if (copy_to_user(cpuid_arg, &cpuid, sizeof cpuid))
+                       goto out;
+               r = 0;
+               break;
+       }
        default:
                ;
        }
@@ -1887,14 +2166,33 @@ void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, 
unsigned long val,
        }
 }
 
+static int move_to_next_stateful_cpuid_entry(struct kvm_vcpu *vcpu, int i)
+{
+       struct kvm_cpuid_entry2 *e = &vcpu->cpuid_entries[i];
+       int j, nent = vcpu->cpuid_nent;
+
+       e->flags &= ~KVM_CPUID_FLAG_STATE_READ_NEXT;
+       /* when no next entry is found, the current entry[i] is reselected */
+       for (j = i + 1; j == i; j = (j + 1) % nent) {
+               struct kvm_cpuid_entry2 *ej =
+                       &vcpu->cpuid_entries[j];
+               if (ej->function == e->function) {
+                       ej->flags |= KVM_CPUID_FLAG_STATE_READ_NEXT;
+                       return j;
+               }
+       }
+       return 0; /* silence gcc, even though control never reaches here */
+}
+
 void kvm_emulate_cpuid(struct kvm_vcpu *vcpu)
 {
        int i;
-       u32 function;
-       struct kvm_cpuid_entry *e, *best;
+       u32 function, index;
+       struct kvm_cpuid_entry2 *e, *best;
 
        kvm_x86_ops->cache_regs(vcpu);
        function = vcpu->regs[VCPU_REGS_RAX];
+       index = vcpu->regs[VCPU_REGS_RCX];
        vcpu->regs[VCPU_REGS_RAX] = 0;
        vcpu->regs[VCPU_REGS_RBX] = 0;
        vcpu->regs[VCPU_REGS_RCX] = 0;
@@ -1902,7 +2200,15 @@ void kvm_emulate_cpuid(struct kvm_vcpu *vcpu)
        best = NULL;
        for (i = 0; i < vcpu->cpuid_nent; ++i) {
                e = &vcpu->cpuid_entries[i];
-               if (e->function == function) {
+               /* find an entry with matching function, matching index (if
+                * needed), and that should be read next (if it's stateful) */
+               if (e->function == function &&
+                   (!(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX)
+                     || e->index == index) &&
+                   (!(e->flags & KVM_CPUID_FLAG_STATEFUL_FUNC)
+                     || (e->flags & KVM_CPUID_FLAG_STATE_READ_NEXT) )) {
+                       if (e->flags & KVM_CPUID_FLAG_STATEFUL_FUNC)
+                               move_to_next_stateful_cpuid_entry(vcpu, i);
                        best = e;
                        break;
                }
diff --git a/drivers/kvm/x86.h b/drivers/kvm/x86.h
index b1528c9..78ab1e1 100644
--- a/drivers/kvm/x86.h
+++ b/drivers/kvm/x86.h
@@ -149,7 +149,7 @@ struct kvm_vcpu {
        int halt_request; /* real mode on Intel only */
 
        int cpuid_nent;
-       struct kvm_cpuid_entry cpuid_entries[KVM_MAX_CPUID_ENTRIES];
+       struct kvm_cpuid_entry2 cpuid_entries[KVM_MAX_CPUID_ENTRIES];
 
        /* emulate context */
 
diff --git a/include/asm-x86/kvm.h b/include/asm-x86/kvm.h
index 4837d75..17afa81 100644
--- a/include/asm-x86/kvm.h
+++ b/include/asm-x86/kvm.h
@@ -151,5 +151,26 @@ struct kvm_cpuid {
        struct kvm_cpuid_entry entries[0];
 };
 
+struct kvm_cpuid_entry2 {
+       __u32 function;
+       __u32 index;
+       __u32 flags;
+       __u32 eax;
+       __u32 ebx;
+       __u32 ecx;
+       __u32 edx;
+       __u32 padding[3];
+};
+
+#define KVM_CPUID_FLAG_SIGNIFCANT_INDEX 1
+#define KVM_CPUID_FLAG_STATEFUL_FUNC    2
+#define KVM_CPUID_FLAG_STATE_READ_NEXT  4
+
+/* for KVM_SET_CPUID2 */
+struct kvm_cpuid2 {
+       __u32 nent;
+       __u32 padding;
+       struct kvm_cpuid_entry2 entries[0];
+};
 
 #endif
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index fd4f900..b751552 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -224,6 +224,7 @@ struct kvm_signal_mask {
 #define KVM_CAP_MMU_SHADOW_CACHE_CONTROL 2
 #define KVM_CAP_USER_MEMORY 3
 #define KVM_CAP_SET_TSS_ADDR 4
+#define KVM_CAP_EXT_CPUID 5
 
 /*
  * ioctls for VM fds
@@ -241,6 +242,7 @@ struct kvm_signal_mask {
 #define KVM_CREATE_VCPU           _IO(KVMIO,  0x41)
 #define KVM_GET_DIRTY_LOG         _IOW(KVMIO, 0x42, struct kvm_dirty_log)
 #define KVM_SET_MEMORY_ALIAS      _IOW(KVMIO, 0x43, struct kvm_memory_alias)
+#define KVM_GET_SUPPORTED_CPUID   _IOWR(KVMIO, 0x48, struct kvm_cpuid2)
 /* Device model IOC */
 #define KVM_CREATE_IRQCHIP       _IO(KVMIO,  0x60)
 #define KVM_IRQ_LINE             _IOW(KVMIO, 0x61, struct kvm_irq_level)
@@ -266,5 +268,7 @@ struct kvm_signal_mask {
 #define KVM_SET_FPU               _IOW(KVMIO,  0x8d, struct kvm_fpu)
 #define KVM_GET_LAPIC             _IOR(KVMIO,  0x8e, struct kvm_lapic_state)
 #define KVM_SET_LAPIC             _IOW(KVMIO,  0x8f, struct kvm_lapic_state)
+#define KVM_SET_CPUID2            _IOW(KVMIO,  0x90, struct kvm_cpuid2)
+#define KVM_GET_CPUID2            _IOWR(KVMIO, 0x91, struct kvm_cpuid2)
 
 #endif
commit e5a590065ab37fb5eb0481e176d455fba98310dd
Author: Dan Kenigsberg <[EMAIL PROTECTED]>
Date:   Sun Nov 18 13:36:41 2007 +0200

    Add -cpu host option. Negotiate cpuid table with kernel.
    
    Signed-off-by: Dan Kenigsberg <[EMAIL PROTECTED]>

diff --git a/kernel/external-module-compat.h b/kernel/external-module-compat.h
index e3f81fe..ef5d70b 100644
--- a/kernel/external-module-compat.h
+++ b/kernel/external-module-compat.h
@@ -530,14 +530,6 @@ out:
 #define dest_ExtINT       7
 #endif
 
-/* empty_zero_page isn't exported in all kernels */
-#include <asm/pgtable.h>
-
-#define empty_zero_page kvm_empty_zero_page
-
-static char empty_zero_page[PAGE_SIZE];
-
-static inline void blahblah(void)
-{
-       (void)empty_zero_page[0];
-}
+#ifndef X86_FEATURE_NX
+#define X86_FEATURE_NX (1*32+20)
+#endif
diff --git a/libkvm/libkvm-x86.c b/libkvm/libkvm-x86.c
index 6dabfd8..0b087ef 100644
--- a/libkvm/libkvm-x86.c
+++ b/libkvm/libkvm-x86.c
@@ -641,9 +641,9 @@ __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;
+       struct kvm_cpuid2 *cpuid;
        int r;
 
        cpuid = malloc(sizeof(*cpuid) + nent * sizeof(*entries));
@@ -652,9 +652,57 @@ 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);
 
        free(cpuid);
        return r;
 }
 
+int kvm_get_supported_cpuid(kvm_context_t kvm, int *nent,
+               struct kvm_cpuid_entry2 *entries)
+{
+       struct kvm_cpuid2 *cpuid;
+       int r = -1;
+#ifdef KVM_CAP_EXT_CPUID
+       r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+       if (!r)
+               return -1;
+
+       cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+       if (!cpuid)
+               return -ENOMEM;
+       cpuid->nent = *nent;
+
+       r = ioctl(kvm->vm_fd, KVM_GET_SUPPORTED_CPUID, cpuid);
+
+       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 = -1;
+#ifdef KVM_CAP_EXT_CPUID
+       r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+       if (!r)
+               return -1;
+
+       cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+       if (!cpuid)
+               return -ENOMEM;
+       cpuid->nent = *nent;
+
+       r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_CPUID2, cpuid);
+
+       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 2094dfd..d23b52b 100644
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -360,7 +360,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 Setup a vcpu's cpuid instruction emulation
+ *
+ * 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 Set a vcpu's signal mask for guest mode
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index 6c71b09..588c3ba 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -683,7 +683,7 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size, 
int boot_device,
                      DisplayState *ds, const char **fd_filename, int snapshot,
                      const char *kernel_filename, const char *kernel_cmdline,
                      const char *initrd_filename,
-                     int pci_enabled)
+                     int pci_enabled, const char *cpu_model)
 {
     char buf[1024];
     int ret, linux_boot, i;
@@ -705,6 +705,13 @@ static void pc_init1(ram_addr_t ram_size, int 
vga_ram_size, int boot_device,
     linux_boot = (kernel_filename != NULL);
 
     /* init CPUs */
+    if (cpu_model == NULL)
+        cpu_model = "basic";
+
+    if (x86_find_cpu_by_name(cpu_model)) {
+        fprintf(stderr, "Unable to find x86 CPU definition\n");
+        exit(1);
+    }
     for(i = 0; i < smp_cpus; i++) {
         env = cpu_init();
         if (i != 0)
@@ -1038,7 +1045,7 @@ static void pc_init_pci(ram_addr_t ram_size, int 
vga_ram_size, int boot_device,
     pc_init1(ram_size, vga_ram_size, boot_device,
              ds, fd_filename, snapshot,
              kernel_filename, kernel_cmdline,
-             initrd_filename, 1);
+             initrd_filename, 1, cpu_model);
 }
 
 static void pc_init_isa(ram_addr_t ram_size, int vga_ram_size, int boot_device,
@@ -1052,7 +1059,7 @@ static void pc_init_isa(ram_addr_t ram_size, int 
vga_ram_size, int boot_device,
     pc_init1(ram_size, vga_ram_size, boot_device,
              ds, fd_filename, snapshot,
              kernel_filename, kernel_cmdline,
-             initrd_filename, 0);
+             initrd_filename, 0, cpu_model);
 }
 
 QEMUMachine pc_machine = {
diff --git a/qemu/qemu-kvm.c b/qemu/qemu-kvm.c
index 716b8fb..ea96099 100644
--- a/qemu/qemu-kvm.c
+++ b/qemu/qemu-kvm.c
@@ -1125,7 +1125,7 @@ static void host_cpuid(uint32_t function, uint32_t *eax, 
uint32_t *ebx,
        *edx = vec[3];
 }
 
-static void do_cpuid_ent(struct kvm_cpuid_entry *e, uint32_t function,
+static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function,
                         CPUState *env)
 {
     env->regs[R_EAX] = function;
@@ -1170,11 +1170,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;
+        }
+    }
+    return 0;
+}
+
+
 int kvm_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;
@@ -1183,6 +1200,66 @@ int kvm_qemu_init_env(CPUState *cenv)
 
     copy = *cenv;
 
+    if (cenv->hostlike) {
+        struct kvm_cpuid_entry2 *e;
+        int r;
+
+        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) & 
~cenv->cpuid_disable_ext_features;
+        e->edx = (e->edx | cenv->cpuid_features) & 
~cenv->cpuid_disable_features;
+
+        /* extended features */
+        e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0x80000001, 0);
+        e->eax = (e->eax | cenv->cpuid_features) & 
~cenv->cpuid_disable_features;
+        e->ecx = (e->ecx | cenv->cpuid_ext3_features) & 
~cenv->cpuid_disable_ext3_features;
+        e->edx = (e->edx | cenv->cpuid_ext2_features) & 
~cenv->cpuid_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);
+                }
+        }
+    } 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);
+
+        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);
+    }
+
 #ifdef KVM_CPUID_SIGNATURE
     /* Paravirtualization CPUIDs */
     memcpy(signature, "KVMKVMKVM", 12);
@@ -1200,20 +1277,6 @@ int kvm_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 de2669e..9a34f4d 100644
--- a/qemu/target-i386/cpu.h
+++ b/qemu/target-i386/cpu.h
@@ -280,23 +280,56 @@
 #define CPUID_CMOV (1 << 15)
 #define CPUID_PAT  (1 << 16)
 #define CPUID_PSE36   (1 << 17)
+#define CPUID_PN   (1 << 18)
 #define CPUID_CLFLUSH (1 << 19)
-/* ... */
+#define CPUID_DTS (1 << 21)
+#define CPUID_ACPI (1 << 22)
 #define CPUID_MMX  (1 << 23)
 #define CPUID_FXSR (1 << 24)
 #define CPUID_SSE  (1 << 25)
 #define CPUID_SSE2 (1 << 26)
+#define CPUID_SS (1 << 27)
+#define CPUID_HT (1 << 28)
+#define CPUID_TM (1 << 29)
+#define CPUID_IA64 (1 << 30)
+#define CPUID_PBE (1 << 31)
 
 #define CPUID_EXT_SSE3     (1 << 0)
 #define CPUID_EXT_MONITOR  (1 << 3)
+#define CPUID_EXT_DSCPL    (1 << 4)
+#define CPUID_EXT_VMX      (1 << 5)
+#define CPUID_EXT_SMX      (1 << 6)
+#define CPUID_EXT_EST      (1 << 7)
+#define CPUID_EXT_TM2      (1 << 8)
+#define CPUID_EXT_SSSE3    (1 << 9)
+#define CPUID_EXT_CID      (1 << 10)
 #define CPUID_EXT_CX16     (1 << 13)
+#define CPUID_EXT_XTPR     (1 << 14)
+#define CPUID_EXT_DCA      (1 << 17)
+#define CPUID_EXT_POPCNT   (1 << 22)
 
 #define CPUID_EXT2_SYSCALL (1 << 11)
+#define CPUID_EXT2_MP      (1 << 19)
 #define CPUID_EXT2_NX      (1 << 20)
+#define CPUID_EXT2_MMXEXT  (1 << 22)
 #define CPUID_EXT2_FFXSR   (1 << 25)
+#define CPUID_EXT2_PDPE1GB (1 << 26)
+#define CPUID_EXT2_RDTSCP  (1 << 27)
 #define CPUID_EXT2_LM      (1 << 29)
+#define CPUID_EXT2_3DNOWEXT (1 << 30)
+#define CPUID_EXT2_3DNOW   (1 << 31)
 
+#define CPUID_EXT3_LAHF_LM (1 << 0)
+#define CPUID_EXT3_CMP_LEG (1 << 1)
 #define CPUID_EXT3_SVM     (1 << 2)
+#define CPUID_EXT3_EXTAPIC (1 << 3)
+#define CPUID_EXT3_CR8LEG  (1 << 4)
+#define CPUID_EXT3_ABM     (1 << 5)
+#define CPUID_EXT3_SSE4A   (1 << 6)
+#define CPUID_EXT3_MISALIGNSSE (1 << 7)
+#define CPUID_EXT3_3DNOWPREFETCH (1 << 8)
+#define CPUID_EXT3_OSVW    (1 << 9)
+#define CPUID_EXT3_IBS     (1 << 10)
 
 #define EXCP00_DIVZ    0
 #define EXCP01_SSTP    1
@@ -561,6 +594,14 @@ typedef struct CPUX86State {
     uint32_t cpuid_ext2_features;
     uint32_t cpuid_ext3_features;
     uint32_t cpuid_apic_id;
+#ifdef USE_KVM
+    uint32_t cpuid_disable_features;
+    uint32_t cpuid_disable_ext_features;
+    uint32_t cpuid_disable_ext2_features;
+    uint32_t cpuid_disable_ext3_features;
+    uint32_t cpuid_disable_apic_id;
+    int hostlike;
+#endif
 
 #ifdef USE_KQEMU
     int kqemu_enabled;
@@ -581,6 +622,9 @@ typedef struct CPUX86State {
 CPUX86State *cpu_x86_init(void);
 int cpu_x86_exec(CPUX86State *s);
 void cpu_x86_close(CPUX86State *s);
+int x86_find_cpu_by_name (const unsigned char *name);
+void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt,
+                                                 ...));
 int cpu_get_pic_interrupt(CPUX86State *s);
 /* MSDOS compatibility mode FPU exception support */
 void cpu_set_ferr(CPUX86State *s);
diff --git a/qemu/target-i386/helper2.c b/qemu/target-i386/helper2.c
index b0e9692..89bed41 100644
--- a/qemu/target-i386/helper2.c
+++ b/qemu/target-i386/helper2.c
@@ -52,6 +52,63 @@ int modify_ldt(int func, void *ptr, unsigned long bytecount)
 
 extern const char *cpu_vendor_string;
 
+static struct x86_def_t *x86_cpu_def;
+typedef struct x86_def_t x86_def_t;
+static int cpu_x86_register (CPUX86State *env, const x86_def_t *def);
+
+static void add_flagname_to_bitmaps(char *flagname, uint32_t *features, 
uint32_t *ext_features, uint32_t *ext2_features, uint32_t *ext3_features)
+{
+    int i;
+    /* feature flags taken from "Intel Processor Identification and the CPUID
+     * Instruction" and AMD's "CPUID Specification". In cases of disagreement 
+     * about feature names, the Linux name is used. */
+    const char *feature_name[] = {
+        "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
+        "cx8", "apic", NULL, "sep", "mtrr", "pge", "mca", "cmov",
+        "pat", "pse36", "pn" /* Intel psn */, "clflush" /* Intel clfsh */, 
NULL, "ds" /* Intel dts */, "acpi", "mmx",
+        "fxsr", "sse", "sse2", "ss", "ht" /* Intel htt */, "tm", "ia64", "pbe",
+    };
+    const char *ext_feature_name[] = {
+       "pni" /* Intel,AMD sse3 */, NULL, NULL, "monitor", "ds_cpl", "vmx", 
NULL /* Linux smx */, "est",
+       "tm2", "ssse3", "cid", NULL, NULL, "cx16", "xtpr", NULL,
+       NULL, NULL, "dca", NULL, NULL, NULL, NULL, "popcnt",
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    };
+    const char *ext2_feature_name[] = {
+       "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce",
+       "cx8" /* AMD CMPXCHG8B */, "apic", NULL, "syscall", "mttr", "pge", 
"mca", "cmov",
+       "pat", "pse36", NULL, NULL /* Linux mp */, "nx" /* Intel xd */, NULL, 
"mmxext", "mmx",
+       "fxsr", "fxsr_opt" /* AMD ffxsr */, "pdpe1gb" /* AMD Page1GB */, 
"rdtscp", NULL, "lm" /* Intel 64 */, "3dnowext", "3dnow",
+    };
+    const char *ext3_feature_name[] = {
+       "lahf_lm" /* AMD LahfSahf */, "cmp_legacy", "svm", "extapic" /* AMD 
ExtApicSpace */, "cr8legacy" /* AMD AltMovCr8 */, "abm", "sse4a", "misalignsse",
+       "3dnowprefetch", "osvw", NULL /* Linux ibs */, NULL, "skinit", "wdt", 
NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+       NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+    };
+    for ( i = 0 ; i < 32 ; i++ ) 
+        if (feature_name[i] && !strcmp (flagname, feature_name[i])) {
+            *features |= 1 << i;
+            return;
+        }
+    for ( i = 0 ; i < 32 ; i++ ) 
+        if (ext_feature_name[i] && !strcmp (flagname, ext_feature_name[i])) {
+            *ext_features |= 1 << i;
+            return;
+        }
+    for ( i = 0 ; i < 32 ; i++ ) 
+        if (ext2_feature_name[i] && !strcmp (flagname, ext2_feature_name[i])) {
+            *ext2_features |= 1 << i;
+            return;
+        }
+    for ( i = 0 ; i < 32 ; i++ ) 
+        if (ext3_feature_name[i] && !strcmp (flagname, ext3_feature_name[i])) {
+            *ext3_features |= 1 << i;
+            return;
+        }
+    fprintf(stderr, "CPU feature %s not found\n", flagname);
+}
+
 CPUX86State *cpu_x86_init(void)
 {
     CPUX86State *env;
@@ -86,70 +143,7 @@ CPUX86State *cpu_x86_init(void)
         asm volatile ("movl %0, %%fs" : : "r" ((1 << 3) | 7));
     }
 #endif
-    {
-        int family, model, stepping;
-#ifdef TARGET_X86_64
-        env->cpuid_vendor1 = 0x68747541; /* "Auth" */
-        env->cpuid_vendor2 = 0x69746e65; /* "enti" */
-        env->cpuid_vendor3 = 0x444d4163; /* "cAMD" */
-        family = 6;
-        model = 2;
-        stepping = 3;
-#else
-        env->cpuid_vendor1 = 0x756e6547; /* "Genu" */
-        env->cpuid_vendor2 = 0x49656e69; /* "ineI" */
-        env->cpuid_vendor3 = 0x6c65746e; /* "ntel" */
-#if 0
-        /* pentium 75-200 */
-        family = 5;
-        model = 2;
-        stepping = 11;
-#else
-        /* pentium pro */
-        family = 6;
-        model = 3;
-        stepping = 3;
-#endif
-#endif
-        env->cpuid_level = 2;
-        env->cpuid_version = (family << 8) | (model << 4) | stepping;
-        env->cpuid_features = (CPUID_FP87 | CPUID_DE | CPUID_PSE |
-                               CPUID_TSC | CPUID_MSR | CPUID_MCE |
-                               CPUID_CX8 | CPUID_PGE | CPUID_CMOV |
-                               CPUID_PAT);
-        env->pat = 0x0007040600070406ULL;
-        env->cpuid_ext3_features = CPUID_EXT3_SVM;
-        env->cpuid_ext_features = CPUID_EXT_SSE3;
-        env->cpuid_features |= CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 
| CPUID_PAE | CPUID_SEP;
-        env->cpuid_features |= CPUID_APIC;
-        env->cpuid_xlevel = 0x8000000e;
-        {
-            const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION;
-            int c, len, i;
-
-            if (cpu_vendor_string != NULL)
-               model_id = cpu_vendor_string;
-
-            len = strlen(model_id);
-            for(i = 0; i < 48; i++) {
-                if (i >= len)
-                    c = '\0';
-                else
-                    c = model_id[i];
-                env->cpuid_model[i >> 2] |= c << (8 * (i & 3));
-            }
-        }
-#ifdef TARGET_X86_64
-        /* currently not enabled for std i386 because not fully tested */
-        env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF);
-        env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | 
CPUID_EXT2_NX;
-
-        /* these features are needed for Win64 and aren't fully implemented */
-        env->cpuid_features |= CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA;
-        /* this feature is needed for Solaris and isn't fully implemented */
-        env->cpuid_features |= CPUID_PSE36;
-#endif
-    }
+    cpu_x86_register(env, x86_cpu_def);
     cpu_reset(env);
 #ifdef USE_KQEMU
     kqemu_init(env);
@@ -166,6 +160,252 @@ CPUX86State *cpu_x86_init(void)
     return env;
 }
 
+struct x86_def_t {
+    const char *name;
+    uint32_t vendor1, vendor2, vendor3;
+    uint32_t level;
+    int family;
+    int model;
+    int stepping;
+    uint32_t enable_features, enable_ext_features,
+             enable_ext2_features, enable_ext3_features;
+    uint32_t disable_features, disable_ext_features,
+             disable_ext2_features, disable_ext3_features;
+    uint32_t xlevel;
+#ifdef USE_KVM
+    int hostlike;
+#endif
+};
+
+#define PPRO_FEATURES (CPUID_FP87 | CPUID_DE | CPUID_PSE | CPUID_TSC | \
+          CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_PGE | CPUID_CMOV | \
+          CPUID_PAT | CPUID_FXSR | CPUID_MMX | CPUID_SSE | CPUID_SSE2 | \
+          CPUID_PAE | CPUID_SEP | CPUID_APIC)
+static x86_def_t x86_defs[] = {
+#ifdef TARGET_X86_64
+    {
+        .name = "basic",
+        .vendor1 = 0x68747541, /* "Auth" */
+        .vendor2 = 0x69746e65, /* "enti" */
+        .vendor3 = 0x444d4163, /* "cAMD" */
+        .level = 2,
+        .family = 6,
+        .model = 2,
+        .stepping = 3,
+        .enable_features = PPRO_FEATURES | 
+        /* these features are needed for Win64 and aren't fully implemented */
+            CPUID_MTRR | CPUID_CLFLUSH | CPUID_MCA |
+        /* this feature is needed for Solaris and isn't fully implemented */
+            CPUID_PSE36,
+        .enable_ext_features = CPUID_EXT_SSE3,
+        .enable_ext2_features = (PPRO_FEATURES & 0x0183F3FF) | 
+            CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX,
+        .xlevel = 0x80000008,
+    },
+#else
+    {
+        .name = "basic",
+        .level = 2,
+        .family = 6,
+        .model = 3,
+        .stepping = 3,
+        .enable_features = PPRO_FEATURES,
+        .enable_ext_features = CPUID_EXT_SSE3,
+        .xlevel = 0,
+    },
+#endif
+    {
+        .name = "486",
+        .level = 2,
+        .family = 4,
+        .model = 0,
+        .stepping = 0,
+        .enable_features = 0x0000000B,
+        .xlevel = 0,
+    },
+    {
+        .name = "pentium",
+        .level = 2,
+        .family = 5,
+        .model = 4,
+        .stepping = 3,
+        .enable_features = 0x008001BF,
+        .xlevel = 0,
+    },
+    {
+        .name = "pentium2",
+        .level = 2,
+        .family = 6,
+        .model = 5,
+        .stepping = 2,
+        .enable_features = 0x0183F9FF,
+        .xlevel = 0,
+    },
+    {
+        .name = "pentium3",
+        .level = 2,
+        .family = 6,
+        .model = 7,
+        .stepping = 3,
+        .enable_features = 0x0383F9FF,
+        .xlevel = 0,
+    },
+#ifdef USE_KVM
+    {
+        .name = "host",
+        .hostlike = 1,
+    },
+#endif
+};
+
+int x86_find_cpu_by_name(const unsigned char *cpu_model)
+{
+    int ret;
+    unsigned int i;
+
+    char *s = strdup(cpu_model);
+    char *featurestr, *name = strtok(s, ",");
+    uint32_t enable_features = 0, enable_ext_features = 0, 
enable_ext2_features = 0, enable_ext3_features = 0;
+    uint32_t disable_features = 0, disable_ext_features = 0, 
disable_ext2_features = 0, disable_ext3_features = 0;
+    int family = -1, model = -1, stepping = -1;
+
+    ret = -1;
+    x86_cpu_def = NULL;
+    for (i = 0; i < sizeof(x86_defs) / sizeof(x86_def_t); i++) {
+        if (strcasecmp(name, x86_defs[i].name) == 0) {
+            x86_cpu_def = &x86_defs[i];
+            break;
+        }
+    }
+    if (!x86_cpu_def)
+        goto error;
+ 
+#ifdef USE_KVM
+    {
+        extern int kvm_allowed;
+        if (!kvm_allowed && x86_cpu_def->hostlike) {
+            fprintf(stderr, "hostlike cpu requires KVM.\n");
+            goto error;
+        }
+    }
+#endif
+
+    ret = 0;
+    featurestr = strtok(NULL, ",");
+
+    while (featurestr) {
+        char *val;
+        if (featurestr[0] == '+') {
+            add_flagname_to_bitmaps(featurestr + 1, &enable_features, 
&enable_ext_features, &enable_ext2_features, &enable_ext3_features);
+        } else if (featurestr[0] == '-') {
+            add_flagname_to_bitmaps(featurestr + 1, &disable_features, 
&disable_ext_features, &disable_ext2_features, &disable_ext3_features);
+        } else if ((val = strchr(featurestr, '='))) {
+            *val = 0; val++;
+            if (!strcmp(featurestr, "family")) {
+                char *err;
+                family = strtol(val, &err, 10);
+                if (!*val || *err || family < 0) {
+                    fprintf(stderr, "bad numerical value %s\n", val);
+                    x86_cpu_def = 0;
+                    goto error;
+                }
+                x86_cpu_def->family = family;
+            } else if (!strcmp(featurestr, "model")) {
+                char *err;
+                model = strtol(val, &err, 10);
+                if (!*val || *err || model < 0 || model > 0xf) {
+                    fprintf(stderr, "bad numerical value %s\n", val);
+                    x86_cpu_def = 0;
+                    goto error;
+                }
+                x86_cpu_def->model = model;
+            } else if (!strcmp(featurestr, "stepping")) {
+                char *err;
+                stepping = strtol(val, &err, 10);
+                if (!*val || *err || stepping < 0 || stepping > 0xf) {
+                    fprintf(stderr, "bad numerical value %s\n", val);
+                    x86_cpu_def = 0;
+                    goto error;
+                }
+                x86_cpu_def->stepping = stepping;
+            } else {
+                fprintf(stderr, "unregnized feature %s\n", featurestr);
+                x86_cpu_def = 0;
+                goto error;
+            }
+        } else {
+            fprintf(stderr, "feature string `%s' not in format 
(+feature|-feature|feature=xyz)\n", featurestr);
+            x86_cpu_def = 0;
+            goto error;
+        }
+        featurestr = strtok(NULL, ",");
+    }
+    x86_cpu_def->enable_features |= enable_features;
+    x86_cpu_def->enable_ext_features |= enable_ext_features;
+    x86_cpu_def->enable_ext2_features |= enable_ext2_features;
+    x86_cpu_def->enable_ext3_features |= enable_ext3_features;
+    x86_cpu_def->disable_features = disable_features;
+    x86_cpu_def->disable_ext_features = disable_ext_features;
+    x86_cpu_def->disable_ext2_features = disable_ext2_features;
+    x86_cpu_def->disable_ext3_features = disable_ext3_features;
+
+error:
+    free(s);
+    return ret;
+}
+
+void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+    unsigned int i;
+
+    for (i = 0; i < sizeof(x86_defs) / sizeof(x86_def_t); i++)
+        (*cpu_fprintf)(f, "x86 %16s\n", x86_defs[i].name);
+}
+
+int cpu_x86_register (CPUX86State *env, const x86_def_t *def)
+{
+    if (def->vendor1) {
+        env->cpuid_vendor1 = def->vendor1;
+        env->cpuid_vendor2 = def->vendor2;
+        env->cpuid_vendor3 = def->vendor3;
+    } else {
+        env->cpuid_vendor1 = 0x756e6547; /* "Genu" */
+        env->cpuid_vendor2 = 0x49656e69; /* "ineI" */
+        env->cpuid_vendor3 = 0x6c65746e; /* "ntel" */
+    }
+    env->cpuid_level = def->level;
+    env->cpuid_version = (def->family << 8) | (def->model << 4) | 
def->stepping;
+    env->cpuid_features = def->enable_features;
+    env->pat = 0x0007040600070406ULL;
+    env->cpuid_ext_features = def->enable_ext_features;
+    env->cpuid_ext2_features = def->enable_ext2_features;
+    env->cpuid_xlevel = def->xlevel;
+    env->cpuid_ext3_features = def->enable_ext3_features;
+
+    env->cpuid_disable_features = def->disable_features;
+    env->cpuid_disable_ext_features = def->disable_ext_features;
+    env->cpuid_disable_ext2_features = def->disable_ext2_features;
+    env->cpuid_disable_ext3_features = def->disable_ext3_features;
+    {
+        const char *model_id = "QEMU Virtual CPU version " QEMU_VERSION;
+        int c, len, i;
+
+        if (cpu_vendor_string != NULL)
+            model_id = cpu_vendor_string;
+
+        len = strlen(model_id);
+        for(i = 0; i < 48; i++) {
+            if (i >= len)
+                c = '\0';
+            else
+                c = model_id[i];
+            env->cpuid_model[i >> 2] |= c << (8 * (i & 3));
+        }
+    }
+    env->hostlike = def->hostlike;
+    return 0;
+}
+
 /* NOTE: must be called outside the CPU execute loop */
 void cpu_reset(CPUX86State *env)
 {
@@ -203,7 +443,7 @@ void cpu_reset(CPUX86State *env)
     cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffff, 0);
 
     env->eip = 0xfff0;
-    env->regs[R_EDX] = 0x600; /* indicate P6 processor */
+    env->regs[R_EDX] = env->cpuid_version;
 
     env->eflags = 0x2;
 
diff --git a/qemu/vl.c b/qemu/vl.c
index 70d8c81..70558dc 100644
--- a/qemu/vl.c
+++ b/qemu/vl.c
@@ -8087,6 +8087,8 @@ int main(int argc, char **argv)
                     mips_cpu_list(stdout, &fprintf);
 #elif defined(TARGET_SPARC)
                     sparc_cpu_list(stdout, &fprintf);
+#elif defined(TARGET_I386)
+                    x86_cpu_list(stdout, &fprintf);
 #endif
                     exit(0);
                 } else {
-------------------------------------------------------------------------
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