@@ -379,6 +379,37 @@ int kvm_set_msrs(kvm_context_t kvm, int vcpu, struct kvm_msr_entry *msrs,
return r;
}
+/*
+ * Returns available host cpuid entries. User must free.
+ */
+struct kvm_cpuid2 *kvm_get_host_cpuid_entries(kvm_context_t kvm)
+{
+ struct kvm_cpuid2 sizer, *cpuids;
+ int r, e;
+
+ sizer.nent = 0;
+ r = ioctl(kvm->fd, KVM_GET_SUPPORTED_CPUID, &sizer);
+ if (r != -1 && errno != EAGAIN)
+ return NULL;
+
+ cpuids = malloc(sizeof *cpuids + sizer.nent * sizeof *cpuids->entries);
+ if (!cpuids) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ cpuids->nent = sizer.nent;
+ r = ioctl(kvm->fd, KVM_GET_SUPPORTED_CPUID, cpuids);
+ if (r == -1) {
+ e = errno;
+ free(cpuids);
+ errno = e;
+ return NULL;
+ }
+ return cpuids;
+}
+
+
static void print_seg(FILE *file, const char *name, struct kvm_segment *seg)
{
fprintf(stderr,
@@ -27,6 +27,9 @@ typedef struct kvm_context *kvm_context_t;
struct kvm_msr_list *kvm_get_msr_list(kvm_context_t);
int kvm_get_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n);
int kvm_set_msrs(kvm_context_t, int vcpu, struct kvm_msr_entry *msrs, int n);
+struct kvm_cpuid2 *kvm_get_host_cpuid_entries(kvm_context_t);
+void get_host_cpuid_entry(uint32_t function, uint32_t index,
+ struct kvm_cpuid_entry2 *e);
#endif
/*!
@@ -23,6 +23,7 @@
#define MSR_IA32_TSC 0x10
static struct kvm_msr_list *kvm_msr_list;
+static struct kvm_cpuid2 *kvm_host_cpuid_entries;
extern unsigned int kvm_shadow_memory;
static int kvm_has_msr_star;
@@ -57,7 +58,12 @@ int kvm_arch_qemu_create_context(void)
for (i = 0; i < kvm_msr_list->nmsrs; ++i)
if (kvm_msr_list->indices[i] == MSR_STAR)
kvm_has_msr_star = 1;
- return 0;
+
+ kvm_host_cpuid_entries = kvm_get_host_cpuid_entries(kvm_context);
+ if (!kvm_host_cpuid_entries)
+ return -1;
+
+ return 0;
}
static void set_msr_entry(struct kvm_msr_entry *entry, uint32_t index,
@@ -461,13 +467,61 @@ void kvm_arch_save_regs(CPUState *env)
}
}
+void get_host_cpuid_entry(uint32_t function, uint32_t index,
+ struct kvm_cpuid_entry2 *e)
+{
+ int i;
+ struct kvm_cpuid_entry2 *entries;
+
+ memset(e, 0, (sizeof *e));
+ e->function = function;
+ e->index = index;
+
+ if (!kvm_host_cpuid_entries)
+ return;
+
+ entries = kvm_host_cpuid_entries->entries;
+
+ for (i=0; i<kvm_host_cpuid_entries->nent; i++) {
+ struct kvm_cpuid_entry2 *ent = &entries[i];
+ if (ent->function != function)
+ continue;
+ if ((ent->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX) &&
+ (ent->index != index))
+ continue;
+ if ((ent->flags & KVM_CPUID_FLAG_STATEFUL_FUNC) &&
+ !(ent->flags & KVM_CPUID_FLAG_STATE_READ_NEXT))
+ continue;
+
+ memcpy(e, ent, sizeof (*e));
+
+ if (ent->flags & KVM_CPUID_FLAG_STATEFUL_FUNC) {
+ int j;
+ ent->flags &= ~KVM_CPUID_FLAG_STATE_READ_NEXT;
+ for (j=i+1; ;j=(j+1)%(kvm_host_cpuid_entries->nent)) {
+ struct kvm_cpuid_entry2 *entj = &entries[j];
+ if (entj->function == ent->function) {
+ entj->flags |= KVM_CPUID_FLAG_STATE_READ_NEXT;
+ break;
+ }
+ }
+ }
+ break;
+ }
+}
+
static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function,
- uint32_t count, CPUState *env)
+ uint32_t index, CPUState *env)
{
+ if (env->cpuid_host_cpu) {
+ get_host_cpuid_entry(function, index, e);
+ return;
+ }
+ e->function = function;
+ e->index = index;
env->regs[R_EAX] = function;
- env->regs[R_ECX] = count;
+ env->regs[R_ECX] = index;
qemu_kvm_cpuid_on_env(env);
- e->function = function;
e->eax = env->regs[R_EAX];
e->ebx = env->regs[R_EBX];
e->ecx = env->regs[R_ECX];
@@ -518,6 +572,10 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
copy = *cenv;
+ if (copy.cpuid_host_cpu)
+ if (!kvm_host_cpuid_entries)
+ return -EINVAL;
+
#ifdef KVM_CPUID_SIGNATURE
/* Paravirtualization CPUIDs */
memcpy(signature, "KVMKVMKVM\0\0\0", 12);
@@ -535,34 +593,32 @@ int kvm_arch_qemu_init_env(CPUState *cenv)
pv_ent->eax = get_para_features(kvm_context);
#endif
- copy.regs[R_EAX] = 0;
- qemu_kvm_cpuid_on_env(©);
- limit = copy.regs[R_EAX];
+ limit = copy.cpuid_level;
for (i = 0; i <= limit; ++i) {
- if (i == 4 || i == 0xb || i == 0xd) {
+ if ( i == 2 || i == 4 || i == 0xb || i == 0xd) {
for (j = 0; ; ++j) {
do_cpuid_ent(&cpuid_ent[cpuid_nent], i, j, ©);
cpuid_ent[cpuid_nent].flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
cpuid_ent[cpuid_nent].index = j;
-
cpuid_nent++;
- if (i == 4 && copy.regs[R_EAX] == 0)
+ if (i == 2 && (cpuid_ent[cpuid_nent-1].eax & 0xff) == (j+1)) {
+ break;
+ }
+ if (i == 4 && (cpuid_ent[cpuid_nent-1].eax & 0x1f) == 0)
break;
- if (i == 0xb && !(copy.regs[R_ECX] & 0xff00))
+ if (i == 0xb && !(cpuid_ent[cpuid_nent-1].ecx & 0xff00))
break;
- if (i == 0xd && copy.regs[R_EAX] == 0)
+ if (i == 0xd && cpuid_ent[cpuid_nent-1].eax == 0)
break;
}
} else
do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©);
}
- copy.regs[R_EAX] = 0x80000000;
- qemu_kvm_cpuid_on_env(©);
- limit = copy.regs[R_EAX];
+ limit = copy.cpuid_xlevel;
for (i = 0x80000000; i <= limit; ++i)
do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©);
@@ -628,6 +628,7 @@ typedef struct CPUX86State {
uint32_t cpuid_ext2_features;
uint32_t cpuid_ext3_features;
uint32_t cpuid_apic_id;
+ uint32_t cpuid_host_cpu;
#ifdef USE_KQEMU
int kqemu_enabled;
@@ -119,6 +119,9 @@ typedef struct x86_def_t {
static x86_def_t x86_defs[] = {
#ifdef TARGET_X86_64
{
+ .name = "host",
+ },
+ {
.name = "qemu64",
.level = 2,
.vendor1 = CPUID_VENDOR_AMD_1,
@@ -372,10 +375,57 @@ void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
(*cpu_fprintf)(f, "x86 %16s\n", x86_defs[i].name);
}
+static int cpu_host_x86_register (CPUX86State *env)
+{
+ struct kvm_cpuid_entry2 e;
+ env->cpuid_host_cpu = 1;
+
+ get_host_cpuid_entry(0, 0, &e);
+ env->cpuid_level = e.eax;
+ env->cpuid_vendor1 = e.ebx;
+ env->cpuid_vendor2 = e.ecx;
+ env->cpuid_vendor3 = e.edx;
+
+ get_host_cpuid_entry(1, 0, &e);
+ env->cpuid_version = e.eax;
+ env->cpuid_features = e.edx;
+ env->cpuid_ext_features = e.ecx;
+
+ get_host_cpuid_entry(0x80000000, 0, &e);
+ env->cpuid_xlevel = e.eax;
+
+ get_host_cpuid_entry(0x80000001, 0, &e);
+ env->cpuid_ext3_features = e.ecx;
+ env->cpuid_ext2_features = e.edx;
+
+ get_host_cpuid_entry(0x80000002, 0, &e);
+ env->cpuid_model[0] = e.eax;
+ env->cpuid_model[1] = e.ebx;
+ env->cpuid_model[2] = e.ecx;
+ env->cpuid_model[3] = e.edx;
+
+ get_host_cpuid_entry(0x80000003, 0, &e);
+ env->cpuid_model[4] = e.eax;
+ env->cpuid_model[5] = e.ebx;
+ env->cpuid_model[6] = e.ecx;
+ env->cpuid_model[7] = e.edx;
+
+ get_host_cpuid_entry(0x80000004, 0, &e);
+ env->cpuid_model[8] = e.eax;
+ env->cpuid_model[9] = e.ebx;
+ env->cpuid_model[10] = e.ecx;
+ env->cpuid_model[11] = e.edx;
+
+ return 0;
+}
+
static int cpu_x86_register (CPUX86State *env, const char *cpu_model)
{
x86_def_t def1, *def = &def1;
+ if (0 == strcmp(cpu_model, "host"))
+ return cpu_host_x86_register(env);
+
if (cpu_x86_find_by_name(def, cpu_model) < 0)
return -1;
if (def->vendor1) {