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, ©);
+ }
+ }
+ } else {
+ copy.regs[R_EAX] = 0;
+ qemu_kvm_cpuid_on_env(©);
+ limit = copy.regs[R_EAX];
+
+ for (i = 0; i <= limit; ++i)
+ do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
+
+ copy.regs[R_EAX] = 0x80000000;
+ qemu_kvm_cpuid_on_env(©);
+ limit = copy.regs[R_EAX];
+
+ for (i = 0x80000000; i <= limit; ++i)
+ do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
+ }
+
#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(©);
- limit = copy.regs[R_EAX];
-
- for (i = 0; i <= limit; ++i)
- do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
-
- copy.regs[R_EAX] = 0x80000000;
- qemu_kvm_cpuid_on_env(©);
- limit = copy.regs[R_EAX];
-
- for (i = 0x80000000; i <= limit; ++i)
- do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©);
-
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