* [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, ©);
+ }
+ }
+ } 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 {
[-- 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
* Re: [RFC] Expose host cpuid to guest
[not found] ` <20071121110631.GA14292-iWbx9bcAnq+Hk9JtIoIkgNBPR1lH4CV8@public.gmane.org>
@ 2007-11-21 11:21 ` Amit Shah
2007-11-21 11:52 ` Avi Kivity
` (2 subsequent siblings)
3 siblings, 0 replies; 12+ messages in thread
From: Amit Shah @ 2007-11-21 11:21 UTC (permalink / raw)
To: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
* Dan Kenigsberg wrote:
> 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.
Hey Dan, were those amd-specific things sorted out?
-------------------------------------------------------------------------
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/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC] Expose host cpuid to guest
[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 23:42 ` Mike Lampard
2007-12-25 15:56 ` [RFC] Expose host cpuid to guest: userspace patch Dan Kenigsberg
3 siblings, 1 reply; 12+ messages in thread
From: Avi Kivity @ 2007-11-21 11:52 UTC (permalink / raw)
To: Dan Kenigsberg; +Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Dan Kenigsberg wrote:
> 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.
>
> 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.
>
The kernel doesn't have a -cpu option. The description needs to be more
descriptive (motivation, special cases in cpuid).
>
>
> +static int is_efer_nx(void) {
> + u64 efer;
>
blank line
> + rdmsrl(MSR_EFER, efer);
> + return efer & EFER_NX;
> +}
> +
>
>
> +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;
>
whitespace damage here
> + return r;
> +
> +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);
>
u32, not __u32.
> + 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);
>
rdtscp isn't, I believe.
> + 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();
>
Avoid the special case, just to it unconditionally.
> + 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;
>
Indent this normally relative to other entries.
> +
> + 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
whitespace damage
> ;
> +
> + 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;
>
Should exit with an E2BIG here. ENOMEM means kernel's out of memory,
which isn't the case here.
> + 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];
>
80 column limit, not 40.
> + 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;
>
Please move this to a function with multiple if ()s and return statements.
> ------------------------------------------------------------------------
>
> 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];
> -}
>
Remove this removal.
>
> +int kvm_get_supported_cpuid(kvm_context_t kvm, int *nent,
> + struct kvm_cpuid_entry2 *entries)
> +{
> + struct kvm_cpuid2 *cpuid;
> + int r = -1;
>
blank line.
> +#ifdef KVM_CAP_EXT_CPUID
> + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
> + if (!r)
> + return -1;
>
return -errno on errors
> +
> + cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
> + if (!cpuid)
> + return -ENOMEM;
> + cpuid->nent = *nent;
> +
> + r = ioctl(kvm->vm_fd, KVM_GET_SUPPORTED_CPUID, cpuid);
> +
>
error check
> + 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;
>
blank line
> +#ifdef KVM_CAP_EXT_CPUID
> + r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
> + if (!r)
> + return -1;
>
-errno
> +
> + cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
> + if (!cpuid)
> + return -ENOMEM;
> + cpuid->nent = *nent;
> +
> + r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_CPUID2, cpuid);
> +
>
error check
> + memcpy(entries, cpuid->entries, *nent * sizeof(*entries));
> + *nent = cpuid->nent;
> + free(cpuid);
> +#endif
> + return r;
> +}
> +
>
--
error compiling committee.c: too many arguments to function
-------------------------------------------------------------------------
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/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC] Expose host cpuid to guest
[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>
0 siblings, 1 reply; 12+ messages in thread
From: Dan Kenigsberg @ 2007-11-21 14:41 UTC (permalink / raw)
To: Avi Kivity; +Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
[-- Attachment #1: Type: text/plain, Size: 942 bytes --]
On Wed, Nov 21, 2007 at 01:52:15PM +0200, Avi Kivity wrote:
> Dan Kenigsberg wrote:
>> 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.
>> 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.
I believe the attached two patches apply all your comments.
Dan.
[-- Attachment #2: cpuid.kernel2 --]
[-- Type: text/plain, Size: 14907 bytes --]
commit 53c12a2602642ed01488ae382d23d350e5464a71
Author: Dan Kenigsberg <danken-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
Date: Wed Nov 21 16:03:55 2007 +0200
Expose host CPU features to guests. a new KVM_GET_SUPPORTED_CPUID ioctl is used
to obtain the table of cpuid functions supported by the host & kvm. The
user-space may fine-tune this table and use the KVM_SET_CPUID2 ioctl to set the
table seen by guests.
This patch exposes CPU features that are hitherto hidden (e.g. SSE, cache
structure) and makes a couple of benchmarks faster.
The table of cpuid entries and kvm_emulate_cpuid() had to be changed because
for a couple of cpuid functions, edx - and not only eax - is significant.
Worse, function 2 is stateful; its value may change between consequtive calls.
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..c14967a 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,18 @@ 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 +709,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 +766,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 +776,189 @@ 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
+ bit(X86_FEATURE_MMXEXT) |
+ 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 */
+ 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;
+ }
+ 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);
+ r = -E2BIG;
+ 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 +1025,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 +1350,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 +2164,47 @@ 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 */
+}
+
+/* find an entry with matching function, matching index (if needed), and that
+ * should be read next (if it's stateful) */
+static int is_matching_cpuid_entry(struct kvm_cpuid_entry2 *e,
+ u32 function, u32 index)
+{
+ if (e->function != function)
+ return 0;
+ if ((e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX) && e->index != index)
+ return 0;
+ if ((e->flags & KVM_CPUID_FLAG_STATEFUL_FUNC) &&
+ !(e->flags & KVM_CPUID_FLAG_STATE_READ_NEXT))
+ return 0;
+ return 1;
+}
+
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 +2212,9 @@ 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) {
+ if (is_matching_cpuid_entry(e, function, index)) {
+ 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.user2 --]
[-- Type: text/plain, Size: 28360 bytes --]
commit c628e2861b63a7495f999b3f90afab2d7799e122
Author: Dan Kenigsberg <danken-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
Date: Wed Nov 21 16:37:07 2007 +0200
Add -cpu host option. Expose to guest the CPUID that is supported by the
host (or specifically requested in the command line)
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..dbb975a 100644
--- a/kernel/external-module-compat.h
+++ b/kernel/external-module-compat.h
@@ -541,3 +541,7 @@ 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..71cc0e1 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,63 @@ 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 = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+ if (!r)
+ return -ENOSYS;
+
+ cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+ if (!cpuid)
+ return -ENOMEM;
+ cpuid->nent = *nent;
+
+ r = ioctl(kvm->vm_fd, KVM_GET_SUPPORTED_CPUID, cpuid);
+ if (r)
+ return -errno;
+
+ memcpy(entries, cpuid->entries, *nent * sizeof(*entries));
+ *nent = cpuid->nent;
+ free(cpuid);
+#endif
+ return r;
+}
+
+int kvm_get_cpuid(kvm_context_t kvm, int vcpu, int *nent,
+ struct kvm_cpuid_entry2 *entries)
+{
+ struct kvm_cpuid2 *cpuid;
+ int r = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+ if (!r)
+ return -ENOSYS;
+
+ cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+ if (!cpuid)
+ return -ENOMEM;
+ cpuid->nent = *nent;
+
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_CPUID2, cpuid);
+ if (r)
+ return -errno;
+
+ 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..786d565 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,29 @@ static void do_cpuid_ent(struct kvm_cpuid_entry *e, uint32_t function,
}
}
+static struct kvm_cpuid_entry2 *cpuid_entry_lookup(struct kvm_cpuid_entry2 *entries,
+ int nent, uint32_t function, uint32_t index)
+{
+ int i;
+
+ for (i = 0; i < nent; ++i) {
+ struct kvm_cpuid_entry2 *e = &entries[i];
+ if (e->function == function &&
+ (!(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX)
+ || e->index == index)) {
+ return e;
+ }
+ }
+ fprintf(stderr, "failed to find cpuid func %x index %x\n", function, index);
+ return 0;
+}
+
+
int kvm_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 +1201,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 +1278,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 {
[-- 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
* Re: [RFC] Expose host cpuid to guest
[not found] ` <20071121144128.GA26036-iWbx9bcAnq+Hk9JtIoIkgNBPR1lH4CV8@public.gmane.org>
@ 2007-11-21 15:11 ` Avi Kivity
0 siblings, 0 replies; 12+ messages in thread
From: Avi Kivity @ 2007-11-21 15:11 UTC (permalink / raw)
To: Dan Kenigsberg; +Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Dan Kenigsberg wrote:
> On Wed, Nov 21, 2007 at 01:52:15PM +0200, Avi Kivity wrote:
>
>> Dan Kenigsberg wrote:
>>
>>> 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.
>>> 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.
>>>
>
> I believe the attached two patches apply all your comments.
>
>
Applied kernel patch. Will wait with userspace until next qemu merge
(which is pending qemu-cvs regressions).
--
error compiling committee.c: too many arguments to function
-------------------------------------------------------------------------
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/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC] Expose host cpuid to guest
[not found] ` <20071121110631.GA14292-iWbx9bcAnq+Hk9JtIoIkgNBPR1lH4CV8@public.gmane.org>
2007-11-21 11:21 ` Amit Shah
2007-11-21 11:52 ` Avi Kivity
@ 2007-11-21 23:42 ` Mike Lampard
[not found] ` <200711221012.05384.mike-/+b+wjvSUqIxsqv6Oivclw@public.gmane.org>
2007-12-25 15:56 ` [RFC] Expose host cpuid to guest: userspace patch Dan Kenigsberg
3 siblings, 1 reply; 12+ messages in thread
From: Mike Lampard @ 2007-11-21 23:42 UTC (permalink / raw)
To: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
On Wed, 21 Nov 2007 09:36:31 pm Dan Kenigsberg wrote:
> 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.
With current kvm-git (commit 51727a110220681f6f43b005d069e28c58f5d151)
(userspace is current to commit 6a385c9539f9746d7ff51ef34c064c3eba86448b) and
the userspace portion of this patch I cannot boot a 64 bit guest (Mandriva
2008 x64) on my AMD x2 without -cpu host. The guest kernel says "Your CPU
does not support long mode. Use a 32bit distribution.". Works fine without
the userspace portion of the patch.
With -cpu host, the 64bit kernel starts to boot then panics almost
immediately:
Code: 0f 30 e9 ed fc ff ff b8 66 00 00 00 f0 0f ab 45 08 e9 e2 fa
RIP [<ffffffff8020ff99> identify_cpu+0x609/0x700
RSP <ffffffff8057df58>
Kernel panic - not syncing: Attempted to kill the idle task!
In the host log I see: cpu0 unhandled wrmsr: 0xc0010015, which might not have
anything to do with this patch of course :)
Mike
-------------------------------------------------------------------------
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/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC] Expose host cpuid to guest
[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:30 ` Avi Kivity
1 sibling, 1 reply; 12+ messages in thread
From: Dan Kenigsberg @ 2007-11-22 13:01 UTC (permalink / raw)
To: Mike Lampard; +Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
On Thu, Nov 22, 2007 at 10:12:04AM +1030, Mike Lampard wrote:
> On Wed, 21 Nov 2007 09:36:31 pm Dan Kenigsberg wrote:
> > 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.
>
> With current kvm-git (commit 51727a110220681f6f43b005d069e28c58f5d151)
> (userspace is current to commit 6a385c9539f9746d7ff51ef34c064c3eba86448b) and
> the userspace portion of this patch I cannot boot a 64 bit guest (Mandriva
> 2008 x64) on my AMD x2 without -cpu host. The guest kernel says "Your CPU
> does not support long mode. Use a 32bit distribution.". Works fine without
> the userspace portion of the patch.
Thanks for your report. I'm probably not exposing one(?) of AMD's x86_64
required cpuid features. However, I fail to reproduce this on Dual-Core
AMD Opteron(tm) Processor 2210 running 2.6.18-8.1.15.el5. What is your
host?
Dan.
-------------------------------------------------------------------------
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/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC] Expose host cpuid to guest
[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>
0 siblings, 1 reply; 12+ messages in thread
From: Mike Lampard @ 2007-11-22 13:20 UTC (permalink / raw)
To: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
On Thu, 22 Nov 2007 11:31:18 pm Dan Kenigsberg wrote:
> On Thu, Nov 22, 2007 at 10:12:04AM +1030, Mike Lampard wrote:
> > On Wed, 21 Nov 2007 09:36:31 pm Dan Kenigsberg wrote:
> > > 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.
> >
> > With current kvm-git (commit 51727a110220681f6f43b005d069e28c58f5d151)
> > (userspace is current to commit 6a385c9539f9746d7ff51ef34c064c3eba86448b)
> > and the userspace portion of this patch I cannot boot a 64 bit guest
> > (Mandriva 2008 x64) on my AMD x2 without -cpu host. The guest kernel
> > says "Your CPU does not support long mode. Use a 32bit distribution.".
> > Works fine without the userspace portion of the patch.
>
> Thanks for your report. I'm probably not exposing one(?) of AMD's x86_64
> required cpuid features. However, I fail to reproduce this on Dual-Core
> AMD Opteron(tm) Processor 2210 running 2.6.18-8.1.15.el5. What is your
> host?
64bit 2.6.23.8 on an AMD Athlon X2 4400.
cpuid flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat
pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt rdtscp lm
3dnowext 3dnow rep_good pni cx16 lahf_lm cmp_legacy svm extapic cr8_legacy
misalignsse
If you need further info, just yell. I didnt want to clutter up the
mailinglist with the full output of cpuid, but will if it helps you.
Mike
-------------------------------------------------------------------------
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/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC] Expose host cpuid to guest
[not found] ` <200711221012.05384.mike-/+b+wjvSUqIxsqv6Oivclw@public.gmane.org>
2007-11-22 13:01 ` Dan Kenigsberg
@ 2007-11-22 13:30 ` Avi Kivity
[not found] ` <47458458.3030104-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
1 sibling, 1 reply; 12+ messages in thread
From: Avi Kivity @ 2007-11-22 13:30 UTC (permalink / raw)
To: Mike Lampard; +Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Mike Lampard wrote:
> With current kvm-git (commit 51727a110220681f6f43b005d069e28c58f5d151)
> (userspace is current to commit 6a385c9539f9746d7ff51ef34c064c3eba86448b) and
> the userspace portion of this patch I cannot boot a 64 bit guest (Mandriva
> 2008 x64) on my AMD x2 without -cpu host. The guest kernel says "Your CPU
> does not support long mode. Use a 32bit distribution.". Works fine without
> the userspace portion of the patch.
>
> With -cpu host, the 64bit kernel starts to boot then panics almost
> immediately:
> Code: 0f 30 e9 ed fc ff ff b8 66 00 00 00 f0 0f ab 45 08 e9 e2 fa
> RIP [<ffffffff8020ff99> identify_cpu+0x609/0x700
> RSP <ffffffff8057df58>
> Kernel panic - not syncing: Attempted to kill the idle task!
>
> In the host log I see: cpu0 unhandled wrmsr: 0xc0010015, which might not have
> anything to do with this patch of course :)
>
A listing/disassembly of the function will help show where the problem is.
--
error compiling committee.c: too many arguments to function
-------------------------------------------------------------------------
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/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC] Expose host cpuid to guest
[not found] ` <200711222350.14746.mike-/+b+wjvSUqIxsqv6Oivclw@public.gmane.org>
@ 2007-11-22 13:36 ` Dan Kenigsberg
0 siblings, 0 replies; 12+ messages in thread
From: Dan Kenigsberg @ 2007-11-22 13:36 UTC (permalink / raw)
To: Mike Lampard; +Cc: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
On Thu, Nov 22, 2007 at 11:50:14PM +1030, Mike Lampard wrote:
> On Thu, 22 Nov 2007 11:31:18 pm Dan Kenigsberg wrote:
> > On Thu, Nov 22, 2007 at 10:12:04AM +1030, Mike Lampard wrote:
> > > On Wed, 21 Nov 2007 09:36:31 pm Dan Kenigsberg wrote:
> > > > 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.
> > >
> > > With current kvm-git (commit 51727a110220681f6f43b005d069e28c58f5d151)
> > > (userspace is current to commit 6a385c9539f9746d7ff51ef34c064c3eba86448b)
> > > and the userspace portion of this patch I cannot boot a 64 bit guest
> > > (Mandriva 2008 x64) on my AMD x2 without -cpu host. The guest kernel
> > > says "Your CPU does not support long mode. Use a 32bit distribution.".
> > > Works fine without the userspace portion of the patch.
> >
> > Thanks for your report. I'm probably not exposing one(?) of AMD's x86_64
> > required cpuid features. However, I fail to reproduce this on Dual-Core
> > AMD Opteron(tm) Processor 2210 running 2.6.18-8.1.15.el5. What is your
> > host?
>
> 64bit 2.6.23.8 on an AMD Athlon X2 4400.
> cpuid flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat
> pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt rdtscp lm
> 3dnowext 3dnow rep_good pni cx16 lahf_lm cmp_legacy svm extapic cr8_legacy
> misalignsse
>
> If you need further info, just yell. I didnt want to clutter up the
> mailinglist with the full output of cpuid, but will if it helps you.
Since I'm currently out of ideas, would you try it with -no-kvm?
Since current Qemu cvs has a -cpu userspace patch, it might be
worthwhile to test that, too.
Dan.
-------------------------------------------------------------------------
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/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [RFC] Expose host cpuid to guest
[not found] ` <47458458.3030104-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
@ 2007-11-22 13:50 ` Mike Lampard
0 siblings, 0 replies; 12+ messages in thread
From: Mike Lampard @ 2007-11-22 13:50 UTC (permalink / raw)
To: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f; +Cc: Avi Kivity
On Fri, 23 Nov 2007 12:00:00 am Avi Kivity wrote:
> Mike Lampard wrote:
> > With current kvm-git (commit 51727a110220681f6f43b005d069e28c58f5d151)
> > (userspace is current to commit 6a385c9539f9746d7ff51ef34c064c3eba86448b)
> > and the userspace portion of this patch I cannot boot a 64 bit guest
> > (Mandriva 2008 x64) on my AMD x2 without -cpu host. The guest kernel
> > says "Your CPU does not support long mode. Use a 32bit distribution.".
> > Works fine without the userspace portion of the patch.
erm.. sorry, seems the patch mis-applied @ my end, I just re-applied the
userspace patch and the above problem is no longer, 1 down, 1 to go:
> >
> > With -cpu host, the 64bit kernel starts to boot then panics almost
> > immediately:
> > Code: 0f 30 e9 ed fc ff ff b8 66 00 00 00 f0 0f ab 45 08 e9 e2 fa
> > RIP [<ffffffff8020ff99> identify_cpu+0x609/0x700
> > RSP <ffffffff8057df58>
> > Kernel panic - not syncing: Attempted to kill the idle task!
> >
> > In the host log I see: cpu0 unhandled wrmsr: 0xc0010015, which might not
> > have anything to do with this patch of course :)
>
> A listing/disassembly of the function will help show where the problem is.
yes, of course.. I'll post it as soon as I can.
Thanks for the help
Mike
-------------------------------------------------------------------------
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/
^ permalink raw reply [flat|nested] 12+ messages in thread
* [RFC] Expose host cpuid to guest: userspace patch
[not found] ` <20071121110631.GA14292-iWbx9bcAnq+Hk9JtIoIkgNBPR1lH4CV8@public.gmane.org>
` (2 preceding siblings ...)
2007-11-21 23:42 ` Mike Lampard
@ 2007-12-25 15:56 ` Dan Kenigsberg
3 siblings, 0 replies; 12+ messages in thread
From: Dan Kenigsberg @ 2007-12-25 15:56 UTC (permalink / raw)
To: kvm-devel-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
[-- Attachment #1: Type: text/plain, Size: 286 bytes --]
The first little patch (already swallowed by qemu-devel) corrects the
cpuid_level exposed for various cpu models, and lets us change it.
The main attached patch is the userspace part of cpuid support. Please
comment if you find something that is not to your liking.
Regards,
Dan.
[-- Attachment #2: correct-cpuid-level.patch --]
[-- Type: text/plain, Size: 1991 bytes --]
diff --git a/qemu/target-i386/helper2.c b/qemu/target-i386/helper2.c
index 1874b73..da7a54c 100644
--- a/qemu/target-i386/helper2.c
+++ b/qemu/target-i386/helper2.c
@@ -125,6 +125,7 @@ CPUX86State *cpu_x86_init(const char *cpu_model)
typedef struct x86_def_t {
const char *name;
+ uint32_t level;
uint32_t vendor1, vendor2, vendor3;
int family;
int model;
@@ -141,6 +142,7 @@ static x86_def_t x86_defs[] = {
#ifdef TARGET_X86_64
{
.name = "qemu64",
+ .level = 2,
.vendor1 = 0x68747541, /* "Auth" */
.vendor2 = 0x69746e65, /* "enti" */
.vendor3 = 0x444d4163, /* "cAMD" */
@@ -161,6 +163,7 @@ static x86_def_t x86_defs[] = {
#endif
{
.name = "qemu32",
+ .level = 2,
.family = 6,
.model = 3,
.stepping = 3,
@@ -170,6 +173,7 @@ static x86_def_t x86_defs[] = {
},
{
.name = "486",
+ .level = 0,
.family = 4,
.model = 0,
.stepping = 0,
@@ -178,6 +182,7 @@ static x86_def_t x86_defs[] = {
},
{
.name = "pentium",
+ .level = 1,
.family = 5,
.model = 4,
.stepping = 3,
@@ -186,6 +191,7 @@ static x86_def_t x86_defs[] = {
},
{
.name = "pentium2",
+ .level = 2,
.family = 6,
.model = 5,
.stepping = 2,
@@ -194,6 +200,7 @@ static x86_def_t x86_defs[] = {
},
{
.name = "pentium3",
+ .level = 2,
.family = 6,
.model = 7,
.stepping = 3,
@@ -312,7 +319,7 @@ static int cpu_x86_register (CPUX86State *env, const char *cpu_model)
env->cpuid_vendor2 = 0x49656e69; /* "ineI" */
env->cpuid_vendor3 = 0x6c65746e; /* "ntel" */
}
- env->cpuid_level = 2;
+ env->cpuid_level = def->level;
env->cpuid_version = (def->family << 8) | (def->model << 4) | def->stepping;
env->cpuid_features = def->features;
env->pat = 0x0007040600070406ULL;
[-- Attachment #3: cpuid-user.patch --]
[-- Type: text/plain, Size: 13631 bytes --]
commit cd7d63ca03061e8c0dd7d71606779ab1d7b6c4cd
Author: Dan Kenigsberg <danken-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
Date: Thu Dec 20 19:34:18 2007 +0200
Add -cpu host option. Expose to guest the CPUID that is supported by the
host (or specifically requested in the command line).
Note that the default cpu is now `host' instead of `qemu64'
Signed-off-by: Dan Kenigsberg <danken-atKUWr5tajBWk0Htik3J/w@public.gmane.org>
diff --git a/libkvm/libkvm-x86.c b/libkvm/libkvm-x86.c
index ca56adb..e4f5936 100644
--- a/libkvm/libkvm-x86.c
+++ b/libkvm/libkvm-x86.c
@@ -452,10 +452,40 @@ __u64 kvm_get_cr8(kvm_context_t kvm, int vcpu)
}
int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent,
- struct kvm_cpuid_entry *entries)
+ struct kvm_cpuid_entry2 *entries)
{
- struct kvm_cpuid *cpuid;
- int r;
+ int r = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+ if (r <= 0)
+#endif
+ { /* kernel has no KVM_SET_CPUID2 */
+ int i;
+ struct kvm_cpuid *cpuid;
+
+ cpuid = malloc(sizeof(*cpuid) +
+ nent * sizeof(struct kvm_cpuid_entry));
+ if (!cpuid)
+ return -ENOMEM;
+
+ cpuid->nent = nent;
+ for (i = 0; i < nent; ++i) {
+ cpuid->entries[i].function = entries[i].function;
+ cpuid->entries[i].eax = entries[i].eax;
+ cpuid->entries[i].ebx = entries[i].ebx;
+ cpuid->entries[i].ecx = entries[i].ecx;
+ cpuid->entries[i].edx = entries[i].edx;
+ }
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID, cpuid);
+ if (r)
+ r = -errno;
+
+ free(cpuid);
+ }
+#ifdef KVM_CAP_EXT_CPUID
+ else {
+ struct kvm_cpuid2 *cpuid;
cpuid = malloc(sizeof(*cpuid) + nent * sizeof(*entries));
if (!cpuid)
@@ -463,9 +493,69 @@ int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent,
cpuid->nent = nent;
memcpy(cpuid->entries, entries, nent * sizeof(*entries));
- r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID, cpuid);
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_SET_CPUID2, cpuid);
+ if (r)
+ r = -errno;
+
+ free(cpuid);
+ }
+#endif
+ return r;
+}
+
+int kvm_get_supported_cpuid(kvm_context_t kvm, int *nent,
+ struct kvm_cpuid_entry2 *entries)
+{
+ struct kvm_cpuid2 *cpuid;
+ int r = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+ if (r <= 0)
+ return -ENOSYS;
+
+ cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+ if (!cpuid)
+ return -ENOMEM;
+ cpuid->nent = *nent;
+ r = ioctl(kvm->vm_fd, KVM_GET_SUPPORTED_CPUID, cpuid);
+ if (r)
+ r = -errno;
+ else {
+ memcpy(entries, cpuid->entries, *nent * sizeof(*entries));
+ *nent = cpuid->nent;
+ }
+ free(cpuid);
+#endif
+ return r;
+}
+
+int kvm_get_cpuid(kvm_context_t kvm, int vcpu, int *nent,
+ struct kvm_cpuid_entry2 *entries)
+{
+ struct kvm_cpuid2 *cpuid;
+ int r = -ENOSYS;
+
+#ifdef KVM_CAP_EXT_CPUID
+ r = ioctl(kvm->fd, KVM_CHECK_EXTENSION, KVM_CAP_EXT_CPUID);
+ if (r <= 0)
+ return -ENOSYS;
+
+ cpuid = malloc(sizeof(*cpuid) + *nent * sizeof(*entries));
+ if (!cpuid)
+ return -ENOMEM;
+ cpuid->nent = *nent;
+
+ r = ioctl(kvm->vcpu_fd[vcpu], KVM_GET_CPUID2, cpuid);
+ if (r)
+ r = -errno;
+ else {
+ memcpy(entries, cpuid->entries, *nent * sizeof(*entries));
+ *nent = cpuid->nent;
+ }
free(cpuid);
+#endif
return r;
}
diff --git a/libkvm/libkvm.h b/libkvm/libkvm.h
index 110912a..21cb508 100644
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -310,6 +310,19 @@ int kvm_inject_irq(kvm_context_t kvm, int vcpu, unsigned irq);
int kvm_guest_debug(kvm_context_t, int vcpu, struct kvm_debug_guest *dbg);
#if defined(__i386__) || defined(__x86_64__)
+#ifndef KVM_CAP_EXT_CPUID
+/* for compilation against old kernels */
+struct kvm_cpuid_entry2 {
+ __u32 function;
+ __u32 index;
+ __u32 flags;
+ __u32 eax;
+ __u32 ebx;
+ __u32 ecx;
+ __u32 edx;
+ __u32 padding[3];
+};
+#endif
/*!
* \brief Setup a vcpu's cpuid instruction emulation
*
@@ -322,7 +335,36 @@ int kvm_guest_debug(kvm_context_t, int vcpu, struct kvm_debug_guest *dbg);
* \return 0 on success, or -errno on error
*/
int kvm_setup_cpuid(kvm_context_t kvm, int vcpu, int nent,
- struct kvm_cpuid_entry *entries);
+ struct kvm_cpuid_entry2 *entries);
+
+/*!
+ * \brief Obtain vcpu's cpuid table
+ *
+ * Get the currently-held table of cpuid functions and their states.\n
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param vcpu Which virtual CPU should be initialized
+ * \param nent Pointer to the number of entries to be obtained.
+ * On return, the number that was actually obtained.
+ * \param entries cpuid function entries table
+ * \return 0 on success, or -errno on error
+ */
+int kvm_get_cpuid(kvm_context_t kvm, int vcpu, int *nent,
+ struct kvm_cpuid_entry2 *entries);
+
+/*!
+ * \brief Obtain cpuid supported by kvm
+ *
+ * Obtain a table of cpuid function supported by kvm.\n
+ *
+ * \param kvm Pointer to the current kvm_context
+ * \param nent Pointer to the number of entries to be obtained.
+ * On return, the number that was actually obtained.
+ * \param entries cpuid function entries table
+ * \return 0 on success, or -errno on error
+ */
+int kvm_get_supported_cpuid(kvm_context_t kvm, int *nent,
+ struct kvm_cpuid_entry2 *entries);
/*!
* \brief Setting the number of shadow pages to be allocated to the vm
diff --git a/qemu/hw/pc.c b/qemu/hw/pc.c
index dde40c3..b653c46 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -751,6 +751,10 @@ static void pc_init1(ram_addr_t ram_size, int vga_ram_size,
#else
cpu_model = "qemu32";
#endif
+#ifdef USE_KVM
+ if (kvm_allowed)
+ cpu_model = "host";
+#endif
}
for(i = 0; i < smp_cpus; i++) {
diff --git a/qemu/qemu-kvm-x86.c b/qemu/qemu-kvm-x86.c
index d86fec3..5e3b5f7 100644
--- a/qemu/qemu-kvm-x86.c
+++ b/qemu/qemu-kvm-x86.c
@@ -461,8 +461,8 @@ static void host_cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx,
}
-static void do_cpuid_ent(struct kvm_cpuid_entry *e, uint32_t function,
- CPUState *env)
+static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function,
+ CPUState *env, int keep_index_flags)
{
env->regs[R_EAX] = function;
qemu_kvm_cpuid_on_env(env);
@@ -471,6 +471,8 @@ static void do_cpuid_ent(struct kvm_cpuid_entry *e, uint32_t function,
e->ebx = env->regs[R_EBX];
e->ecx = env->regs[R_ECX];
e->edx = env->regs[R_EDX];
+ if (!keep_index_flags)
+ e->index = e->flags = 0;
if (function == 0x80000001) {
uint32_t h_eax, h_edx;
struct utsname utsname;
@@ -506,11 +508,28 @@ static void do_cpuid_ent(struct kvm_cpuid_entry *e, uint32_t function,
}
}
+static struct kvm_cpuid_entry2 *cpuid_entry_lookup(struct kvm_cpuid_entry2 *entries,
+ int nent, uint32_t function, uint32_t index)
+{
+ int i;
+
+ for (i = 0; i < nent; ++i) {
+ struct kvm_cpuid_entry2 *e = &entries[i];
+ if (e->function == function &&
+ (!(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX)
+ || e->index == index)) {
+ return e;
+ }
+ }
+ fprintf(stderr, "failed to find cpuid func %x index %x\n", function, index);
+ return 0;
+}
+
int kvm_arch_qemu_init_env(CPUState *cenv)
{
- struct kvm_cpuid_entry cpuid_ent[100];
+ struct kvm_cpuid_entry2 cpuid_ent[100];
#ifdef KVM_CPUID_SIGNATURE
- struct kvm_cpuid_entry *pv_ent;
+ struct kvm_cpuid_entry2 *pv_ent;
uint32_t signature[3];
#endif
int cpuid_nent = 0;
@@ -519,6 +538,72 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
copy = *cenv;
+ if (cenv->cpuid_hostlike) {
+ struct kvm_cpuid_entry2 *e;
+ int r;
+ extern struct hostlike_user_disable {
+ uint32_t features;
+ uint32_t ext_features;
+ uint32_t ext2_features;
+ uint32_t ext3_features;
+ } hostlike_user_disable;
+
+ cpuid_nent = sizeof(cpuid_ent) / sizeof(struct kvm_cpuid_entry2);
+ r = kvm_get_supported_cpuid(kvm_context, &cpuid_nent, cpuid_ent);
+ if (r) {
+ printf("failed to obtain supported cpuid from kvm module (%d)\n", r);
+ exit(1);
+ }
+
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0, 0);
+ copy.cpuid_level = cenv->cpuid_level = e->eax;
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0x80000000, 0);
+ copy.cpuid_xlevel = cenv->cpuid_xlevel = e->eax;
+
+ /* override some of the advertized data with qemu's */
+ /* family-model-stepping */
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 1, 0);
+ if (cenv->cpuid_version >> 8 == 0)
+ cenv->cpuid_version |= e->eax & 0xFF00;
+ if ((cenv->cpuid_version >> 4 & 0xF) == 0)
+ cenv->cpuid_version |= e->eax & 0xF0;
+ if ((cenv->cpuid_version & 0xF) == 0)
+ cenv->cpuid_version |= e->eax & 0xF;
+ e->eax = cenv->cpuid_version;
+ e->ecx = (e->ecx | cenv->cpuid_ext_features) & ~hostlike_user_disable.ext_features;
+ e->edx = (e->edx | cenv->cpuid_features) & ~hostlike_user_disable.features;
+
+ /* extended features */
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, 0x80000001, 0);
+ e->eax = (e->eax | cenv->cpuid_features) & ~hostlike_user_disable.features;
+ e->ecx = (e->ecx | cenv->cpuid_ext3_features) & ~hostlike_user_disable.ext3_features;
+ e->edx = (e->edx | cenv->cpuid_ext2_features) & ~hostlike_user_disable.ext2_features;
+
+ /* cpuid_model */
+ {
+ extern const char *cpu_vendor_string;
+ if (cpu_vendor_string)
+ for (i = 0x80000002; i <= 0x80000004; ++i) {
+ e = cpuid_entry_lookup(cpuid_ent, cpuid_nent, i, 0);
+ do_cpuid_ent(e, i, ©, 1);
+ }
+ }
+ } 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, ©, 0);
+
+ 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, ©, 0);
+ }
+
#ifdef KVM_CPUID_SIGNATURE
/* Paravirtualization CPUIDs */
memcpy(signature, "KVMKVMKVM", 12);
@@ -536,20 +621,6 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
pv_ent->eax = 0;
#endif
- copy.regs[R_EAX] = 0;
- qemu_kvm_cpuid_on_env(©);
- 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 7143ab3..2034381 100644
--- a/qemu/target-i386/cpu.h
+++ b/qemu/target-i386/cpu.h
@@ -588,6 +588,9 @@ typedef struct CPUX86State {
uint32_t cpuid_ext2_features;
uint32_t cpuid_ext3_features;
uint32_t cpuid_apic_id;
+#ifdef USE_KVM
+ int cpuid_hostlike;
+#endif
#ifdef USE_KQEMU
int kqemu_enabled;
diff --git a/qemu/target-i386/helper2.c b/qemu/target-i386/helper2.c
index da7a54c..78c5cf9 100644
--- a/qemu/target-i386/helper2.c
+++ b/qemu/target-i386/helper2.c
@@ -123,6 +123,15 @@ CPUX86State *cpu_x86_init(const char *cpu_model)
return env;
}
+#ifdef USE_KVM
+struct hostlike_user_disable {
+ uint32_t features;
+ uint32_t ext_features;
+ uint32_t ext2_features;
+ uint32_t ext3_features;
+} hostlike_user_disable;
+#endif
+
typedef struct x86_def_t {
const char *name;
uint32_t level;
@@ -131,6 +140,9 @@ typedef struct x86_def_t {
int model;
int stepping;
uint32_t features, ext_features, ext2_features, ext3_features;
+#ifdef USE_KVM
+ int hostlike;
+#endif
uint32_t xlevel;
} x86_def_t;
@@ -207,6 +219,12 @@ static x86_def_t x86_defs[] = {
.features = 0x0383F9FF,
.xlevel = 0,
},
+#ifdef USE_KVM
+ {
+ .name = "host",
+ .hostlike = 1,
+ },
+#endif
};
static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model)
@@ -229,6 +247,12 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model)
}
if (!def)
goto error;
+#ifdef USE_KVM
+ if (!kvm_allowed && def->hostlike) {
+ fprintf(stderr, "hostlike cpu requires KVM.\n");
+ goto error;
+ }
+#endif
memcpy(x86_cpu_def, def, sizeof(*def));
featurestr = strtok(NULL, ",");
@@ -288,6 +312,14 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *cpu_model)
x86_cpu_def->ext_features &= ~minus_ext_features;
x86_cpu_def->ext2_features &= ~minus_ext2_features;
x86_cpu_def->ext3_features &= ~minus_ext3_features;
+#ifdef USE_KVM
+ if (kvm_allowed) {
+ hostlike_user_disable.features = minus_features;
+ hostlike_user_disable.ext_features = minus_ext_features;
+ hostlike_user_disable.ext2_features = minus_ext2_features;
+ hostlike_user_disable.ext3_features = minus_ext3_features;
+ }
+#endif
free(s);
return 0;
@@ -341,6 +373,9 @@ static int cpu_x86_register (CPUX86State *env, const char *cpu_model)
env->cpuid_model[i >> 2] |= c << (8 * (i & 3));
}
}
+#ifdef USE_KVM
+ env->cpuid_hostlike = def->hostlike;
+#endif
return 0;
}
[-- 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