diff --git a/libkvm/libkvm-x86.c b/libkvm/libkvm-x86.c index a8cca15..7aafa20 100644 --- a/libkvm/libkvm-x86.c +++ b/libkvm/libkvm-x86.c @@ -379,6 +379,34 @@ 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 != E2BIG) + 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, @@ -458,9 +486,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)); @@ -469,7 +497,7 @@ 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; diff --git a/libkvm/libkvm.h b/libkvm/libkvm.h index 423ce31..f84d524 100644 --- a/libkvm/libkvm.h +++ b/libkvm/libkvm.h @@ -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 /*! @@ -374,7 +377,7 @@ 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 Setting the number of shadow pages to be allocated to the vm diff --git a/qemu/qemu-kvm-x86.c b/qemu/qemu-kvm-x86.c index bf62e18..b776ebf 100644 --- a/qemu/qemu-kvm-x86.c +++ b/qemu/qemu-kvm-x86.c @@ -21,6 +21,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; extern kvm_context_t kvm_context; static int kvm_has_msr_star; @@ -52,11 +53,17 @@ int kvm_arch_qemu_create_context(void) kvm_msr_list = kvm_get_msr_list(kvm_context); if (!kvm_msr_list) - return -1; + return -1; + for (i = 0; i < kvm_msr_list->nmsrs; ++i) - if (kvm_msr_list->indices[i] == MSR_STAR) - kvm_has_msr_star = 1; - return 0; + if (kvm_msr_list->indices[i] == MSR_STAR) + kvm_has_msr_star = 1; + + 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, @@ -476,13 +483,61 @@ static void host_cpuid(uint32_t function, uint32_t *eax, uint32_t *ebx, *edx = vec[3]; } +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; inent; 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_entry *e, uint32_t function, - CPUState *env) +static void do_cpuid_ent(struct kvm_cpuid_entry2 *e, uint32_t function, + 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] = 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]; @@ -521,6 +576,11 @@ static void do_cpuid_ent(struct kvm_cpuid_entry *e, uint32_t function, if (function == 1) e->ecx |= (1u << 31); + if ((function == 4) || (function == 0xb)) + e->flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX; + else + e->flags = 0; + // 3dnow isn't properly emulated yet if (function == 0x80000001) e->edx &= ~0xc0000000; @@ -559,17 +619,30 @@ static int get_para_features(kvm_context_t kvm_context) int kvm_arch_qemu_init_env(CPUState *cenv) { - struct kvm_cpuid_entry cpuid_ent[100]; + struct kvm_cpuid_entry2 *cpuid_ent, entry, *e; + int cpuid_nent = 0, malloc_size = 0; + CPUState copy; + uint32_t i, limit; #ifdef KVM_CPUID_SIGNATURE - struct kvm_cpuid_entry *pv_ent; + struct kvm_cpuid_entry2 *pv_ent; uint32_t signature[3]; + + malloc_size += 2; #endif - int cpuid_nent = 0; - CPUState copy; - uint32_t i, limit; copy = *cenv; + if (copy.cpuid_host_cpu) { + if (!kvm_host_cpuid_entries) + return -EINVAL; + malloc_size += kvm_host_cpuid_entries->nent; + } else + malloc_size += 100; + + cpuid_ent = malloc(malloc_size * sizeof (struct kvm_cpuid_entry2)); + if (!cpuid_ent) + return -ENOMEM; + #ifdef KVM_CPUID_SIGNATURE /* Paravirtualization CPUIDs */ memcpy(signature, "KVMKVMKVM", 12); @@ -587,21 +660,48 @@ 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<2) && (i= 2) { /* get the multiple stateful leaf values */ + do_cpuid_ent(&entry, 2, 0, ©); + cpuid_ent[cpuid_nent++] = entry; + for (i = 1; i<(entry.eax & 0xff); i++) { + e = &cpuid_ent[cpuid_nent++]; + do_cpuid_ent(e, 2, 0, ©); + } + } - for (i = 0; i <= limit; ++i) - do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©); + for (i = 3; i <= limit; i++) { + e = &cpuid_ent[cpuid_nent++]; + do_cpuid_ent(e, i, 0, ©); + } - copy.regs[R_EAX] = 0x80000000; - qemu_kvm_cpuid_on_env(©); - limit = copy.regs[R_EAX]; + if (limit >= 4) { /* get the per index values */ + int i = 1; + do { + e = &cpuid_ent[cpuid_nent++]; + do_cpuid_ent(e, 4, i++, ©); + } while(e->eax & 0x1f); /* until the last index */ + } + + if (limit >= 0xb) { /* get the per index values */ + int i = 1; + do { + e = &cpuid_ent[cpuid_nent++]; + do_cpuid_ent(e, 0xb, i++, ©); + } while(e->ecx & 0xff00); /* until the last index */ + } + limit = copy.cpuid_xlevel; for (i = 0x80000000; i <= limit; ++i) - do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, ©); + do_cpuid_ent(&cpuid_ent[cpuid_nent++], i, 0, ©); kvm_setup_cpuid(kvm_context, cenv->cpu_index, cpuid_nent, cpuid_ent); + free(cpuid_ent); return 0; } diff --git a/qemu/target-i386/cpu.h b/qemu/target-i386/cpu.h index 11bc2c1..42d646a 100644 --- a/qemu/target-i386/cpu.h +++ b/qemu/target-i386/cpu.h @@ -612,6 +612,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; diff --git a/qemu/target-i386/helper.c b/qemu/target-i386/helper.c index 68efd4d..c23e16e 100644 --- a/qemu/target-i386/helper.c +++ b/qemu/target-i386/helper.c @@ -152,6 +152,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, @@ -405,10 +408,59 @@ void x86_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) (*cpu_fprintf)(f, "x86 %16s\n", x86_defs[i].name); } +int fill_x86_defs_for_host(CPUX86State *env, x86_def_t * def) +{ + struct kvm_cpuid_entry2 e; + + 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 (strcmp(cpu_model, "host") == 0) { + env->cpuid_host_cpu = 1; + fill_x86_defs_for_host(env, def); + return 0; + } /* else follow through */ + env->cpuid_host_cpu = 0; if (cpu_x86_find_by_name(def, cpu_model) < 0) return -1; if (def->vendor1) {