On Mon, Jan 14, 2008 at 02:49:31PM +0100, Alexander Graf wrote:
> Hi,
>
> Currently CPUID function 4 is broken. This function's values rely on the
> value of ECX.
> To solve the issue cleanly, there is already a new API for cpuid
> settings, which is not used yet.
> Using the current interface, the function 4 can be easily passed
> through, by giving multiple function 4 outputs and increasing the
> index-identifier on the fly. This does not break compatibility.
>
> This fix is really important for Mac OS X, as it requires cache
> information. Please also see my previous patches for Mac OS X (or rather
> core duo target) compatibility.
>
> Regards,
>
> Alex
> diff --git a/kernel/x86.c b/kernel/x86.c
> index b55c177..73312e9 100644
> --- a/kernel/x86.c
> +++ b/kernel/x86.c
> @@ -783,7 +783,7 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu *vcpu,
> struct kvm_cpuid *cpuid,
> struct kvm_cpuid_entry __user *entries)
> {
> - int r, i;
> + int r, i, n = 0;
> struct kvm_cpuid_entry *cpuid_entries;
>
> r = -E2BIG;
> @@ -803,8 +803,17 @@ static int kvm_vcpu_ioctl_set_cpuid(struct kvm_vcpu
> *vcpu,
> vcpu->arch.cpuid_entries[i].ebx = cpuid_entries[i].ebx;
> vcpu->arch.cpuid_entries[i].ecx = cpuid_entries[i].ecx;
> vcpu->arch.cpuid_entries[i].edx = cpuid_entries[i].edx;
> - vcpu->arch.cpuid_entries[i].index = 0;
> - vcpu->arch.cpuid_entries[i].flags = 0;
> + switch(vcpu->arch.cpuid_entries[i].function) {
> + case 4:
> + vcpu->arch.cpuid_entries[i].index = n;
> + vcpu->arch.cpuid_entries[i].flags =
> KVM_CPUID_FLAG_SIGNIFCANT_INDEX;
> + n++;
> + break;
> + default:
> + vcpu->arch.cpuid_entries[i].index = 0;
> + vcpu->arch.cpuid_entries[i].flags = 0;
> + break;
> + }
I will not mention the whitespace damage here :-). Instead, I'd ask you
to review, comment, and even try, the patch that I posted here not long
ago, exposing all safe host cpuid functions to guests.
Thanks,
Dan.
diff --git a/libkvm/libkvm-x86.c b/libkvm/libkvm-x86.c
index 2fa8146..e9a8113 100644
--- a/libkvm/libkvm-x86.c
+++ b/libkvm/libkvm-x86.c
@@ -485,20 +485,109 @@ __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)
return -ENOMEM;
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 2574abe..920c4b1 100644
--- a/libkvm/libkvm.h
+++ b/libkvm/libkvm.h
@@ -305,6 +305,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
*
@@ -317,7 +330,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 3972ab4..228b3ad 100644
--- a/qemu/hw/pc.c
+++ b/qemu/hw/pc.c
@@ -783,6 +783,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 c79ca36..ea08c4e 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 ac663aa..f637d82 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;
}
-------------------------------------------------------------------------
Check out the new SourceForge.net Marketplace.
It's the best place to buy or sell services for
just about anything Open Source.
http://ad.doubleclick.net/clk;164216239;13503038;w?http://sf.net/marketplace
_______________________________________________
kvm-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/kvm-devel