public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
* [RFC] Expose host cpuid to guest
@ 2007-11-21 11:06 Dan Kenigsberg
       [not found] ` <20071121110631.GA14292-iWbx9bcAnq+Hk9JtIoIkgNBPR1lH4CV8@public.gmane.org>
  0 siblings, 1 reply; 12+ messages in thread
From: Dan Kenigsberg @ 2007-11-21 11:06 UTC (permalink / raw)
  To: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f

[-- Attachment #1: Type: text/plain, Size: 525 bytes --]

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.

[-- Attachment #2: cpuid.kernel --]
[-- Type: text/plain, Size: 14354 bytes --]

commit e9775d0a16097cfb71779cb2fb985fb3e5040dc8
Author: Dan Kenigsberg <danken-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
Date:   Sun Nov 18 13:55:26 2007 +0200

    Support -cpu host option. Negotiate cpuid table with userspace.
    
    Signed-off-by: Dan Kenigsberg <danken-atKUWr5tajBWk0Htik3J/w@public.gmane.org>

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

[-- Attachment #3: cpuid.user --]
[-- Type: text/plain, Size: 28342 bytes --]

commit e5a590065ab37fb5eb0481e176d455fba98310dd
Author: Dan Kenigsberg <danken-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
Date:   Sun Nov 18 13:36:41 2007 +0200

    Add -cpu host option. Negotiate cpuid table with kernel.
    
    Signed-off-by: Dan Kenigsberg <danken-atKUWr5tajBWk0Htik3J/w@public.gmane.org>

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 {

[-- Attachment #4: Type: text/plain, Size: 228 bytes --]

-------------------------------------------------------------------------
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/

[-- Attachment #5: Type: text/plain, Size: 186 bytes --]

_______________________________________________
kvm-devel mailing list
kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/kvm-devel

^ permalink raw reply related	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2007-12-25 15:56 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-11-21 11:06 [RFC] Expose host cpuid to guest Dan Kenigsberg
     [not found] ` <20071121110631.GA14292-iWbx9bcAnq+Hk9JtIoIkgNBPR1lH4CV8@public.gmane.org>
2007-11-21 11:21   ` Amit Shah
2007-11-21 11:52   ` Avi Kivity
     [not found]     ` <47441BEF.4070207-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
2007-11-21 14:41       ` Dan Kenigsberg
     [not found]         ` <20071121144128.GA26036-iWbx9bcAnq+Hk9JtIoIkgNBPR1lH4CV8@public.gmane.org>
2007-11-21 15:11           ` Avi Kivity
2007-11-21 23:42   ` Mike Lampard
     [not found]     ` <200711221012.05384.mike-/+b+wjvSUqIxsqv6Oivclw@public.gmane.org>
2007-11-22 13:01       ` Dan Kenigsberg
     [not found]         ` <20071122130118.GA14295-iWbx9bcAnq+Hk9JtIoIkgNBPR1lH4CV8@public.gmane.org>
2007-11-22 13:20           ` Mike Lampard
     [not found]             ` <200711222350.14746.mike-/+b+wjvSUqIxsqv6Oivclw@public.gmane.org>
2007-11-22 13:36               ` Dan Kenigsberg
2007-11-22 13:30       ` Avi Kivity
     [not found]         ` <47458458.3030104-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
2007-11-22 13:50           ` Mike Lampard
2007-12-25 15:56   ` [RFC] Expose host cpuid to guest: userspace patch Dan Kenigsberg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox