All of lore.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.