[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH] target-i386: Allow changing of Hypervisor CPUID
From: |
Marcelo Tosatti |
Subject: |
Re: [Qemu-devel] [PATCH] target-i386: Allow changing of Hypervisor CPUIDs. |
Date: |
Wed, 5 Sep 2012 13:48:52 -0300 |
User-agent: |
Mutt/1.5.21 (2010-09-15) |
On Thu, Aug 30, 2012 at 03:20:35PM -0400, Don Slutz wrote:
> This is primarily done so that the guest will think it is running
> under vmware when hypervisor=vmware is specified as a property of a
> cpu.
>
> Also allow this to work in accel=tcg mode.
>
> The new cpu properties hyper_level, hyper_extra, hyper_extra_a, and
> hyper_extra_b can be used to further adjust what the guest sees.
>
> Signed-off-by: Don Slutz <address@hidden>
For what purpose?
Is the VMWare interface documented somewhere?
> ---
> target-i386/cpu.c | 178
> +++++++++++++++++++++++++++++++++++++++++++++++++++++
> target-i386/cpu.h | 9 +++
> target-i386/kvm.c | 33 ++++++++--
> 3 files changed, 214 insertions(+), 6 deletions(-)
>
> diff --git a/target-i386/cpu.c b/target-i386/cpu.c
> index f3cac49..a444b95 100644
> --- a/target-i386/cpu.c
> +++ b/target-i386/cpu.c
> @@ -26,6 +26,7 @@
>
> #include "qemu-option.h"
> #include "qemu-config.h"
> +#include "qemu-timer.h"
>
> #include "qapi/qapi-visit-core.h"
> #include "arch_init.h"
> @@ -244,6 +245,15 @@ typedef struct x86_def_t {
> uint32_t xlevel2;
> /* The feature bits on CPUID[EAX=7,ECX=0].EBX */
> uint32_t cpuid_7_0_ebx_features;
> + /* Hypervisor CPUIDs */
> + uint32_t cpuid_hv_level;
> + uint32_t cpuid_hv_vendor1;
> + uint32_t cpuid_hv_vendor2;
> + uint32_t cpuid_hv_vendor3;
> + /* VMware extra data */
> + uint32_t cpuid_hv_extra;
> + uint32_t cpuid_hv_extra_a;
> + uint32_t cpuid_hv_extra_b;
> } x86_def_t;
>
> #define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE)
> @@ -860,6 +870,18 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor
> *v, void *opaque,
> cpu->env.tsc_khz = value / 1000;
> }
>
> +static void x86_cpuid_set_hv(x86_def_t *x86_cpu_def, uint32_t level,
> + const char *who)
> +{
> + uint32_t signature[3];
> +
> + memcpy(signature, who, 12);
> + x86_cpu_def->cpuid_hv_level = level;
> + x86_cpu_def->cpuid_hv_vendor1 = signature[0];
> + x86_cpu_def->cpuid_hv_vendor2 = signature[1];
> + x86_cpu_def->cpuid_hv_vendor3 = signature[2];
> +}
> +
> static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char
> *cpu_model)
> {
> unsigned int i;
> @@ -867,6 +889,10 @@ static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def,
> const char *cpu_model)
>
> char *s = g_strdup(cpu_model);
> char *featurestr, *name = strtok(s, ",");
> + bool hyperv_enabled = false;
> + bool hv_enabled = false;
> + long hyper_level = -1;
> + long hyper_extra = -1;
> /* Features to be added*/
> uint32_t plus_features = 0, plus_ext_features = 0;
> uint32_t plus_ext2_features = 0, plus_ext3_features = 0;
> @@ -993,12 +1019,84 @@ static int cpu_x86_find_by_name(x86_def_t
> *x86_cpu_def, const char *cpu_model)
> x86_cpu_def->tsc_khz = tsc_freq / 1000;
> } else if (!strcmp(featurestr, "hv_spinlocks")) {
> char *err;
> +
> + if (hv_enabled) {
> + fprintf(stderr,
> + "Only one of hypervisor= or hv_* can be used at
> one time.\n");
> + goto error;
> + }
> numvalue = strtoul(val, &err, 0);
> if (!*val || *err) {
> fprintf(stderr, "bad numerical value %s\n", val);
> goto error;
> }
> + hyperv_enabled = true;
> hyperv_set_spinlock_retries(numvalue);
> + } else if (!strcmp(featurestr, "hyper_level")) {
> + char *err;
> + long longvalue = strtol(val, &err, 0);
> +
> + if (!*val || *err) {
> + fprintf(stderr, "bad numerical value for
> hyper_level=%s\n",
> + val);
> + goto error;
> + }
> + hyper_level = longvalue;
> + } else if (!strcmp(featurestr, "hyper_extra")) {
> + char *err;
> + long longvalue = strtol(val, &err, 0);
> +
> + if (!*val || *err) {
> + fprintf(stderr, "bad numerical value for
> hyper_extra=%s\n",
> + val);
> + goto error;
> + }
> + hyper_extra = longvalue;
> + } else if (!strcmp(featurestr, "hyper_extra_a")) {
> + char *err;
> +
> + numvalue = strtoul(val, &err, 0);
> + if (!*val || *err) {
> + fprintf(stderr,
> + "bad numerical value for hyper_extra_a=%s\n",
> + val);
> + goto error;
> + }
> + x86_cpu_def->cpuid_hv_extra_a = (uint32_t)numvalue;
> + } else if (!strcmp(featurestr, "hyper_extra_b")) {
> + char *err;
> +
> + numvalue = strtoul(val, &err, 0);
> + if (!*val || *err) {
> + fprintf(stderr,
> + "bad numerical value for hyper_extra_b=%s\n",
> + val);
> + goto error;
> + }
> + x86_cpu_def->cpuid_hv_extra_b = (uint32_t)numvalue;
> + } else if (!strcmp(featurestr, "hv") ||
> + !strcmp(featurestr, "hypervisor")) {
> + if (hyperv_enabled) {
> + fprintf(stderr,
> + "Only one of hypervisor= or hv_* can be used at
> one time.\n");
> + goto error;
> + }
> + hv_enabled = true;
> + if (!strcmp(val, "vmware")) {
> + x86_cpuid_set_hv(x86_cpu_def, 0x40000010,
> "VMwareVMware");
> + minus_kvm_features = ~0; /* Expected to be zero... */
> + } else if (!strcmp(val, "vmware3")) {
> + x86_cpuid_set_hv(x86_cpu_def, 0x40000002,
> "VMwareVMware");
> + minus_kvm_features = ~0; /* Expected to be zero... */
> + } else if (!strcmp(val, "xen")) {
> + x86_cpuid_set_hv(x86_cpu_def, 0x40000002,
> "XenVMMXenVMM");
> + } else if (!strcmp(val, "kvm")) {
> + x86_cpuid_set_hv(x86_cpu_def, 0, "KVMKVMKVM\0\0\0");
> + } else {
> + fprintf(stderr, "unknown hypervisor %s\n",
> + val);
> + goto error;
> + }
> } else {
> fprintf(stderr, "unrecognized feature %s\n", featurestr);
> goto error;
> @@ -1008,8 +1106,20 @@ static int cpu_x86_find_by_name(x86_def_t
> *x86_cpu_def, const char *cpu_model)
> } else if (!strcmp(featurestr, "enforce")) {
> check_cpuid = enforce_cpuid = 1;
> } else if (!strcmp(featurestr, "hv_relaxed")) {
> + if (hv_enabled) {
> + fprintf(stderr,
> + "Only one of hypervisor= or hv_* can be used at one
> time.\n");
> + goto error;
> + }
> + hyperv_enabled = true;
> hyperv_enable_relaxed_timing(true);
> } else if (!strcmp(featurestr, "hv_vapic")) {
> + if (hv_enabled) {
> + fprintf(stderr,
> + "Only one of hypervisor= or hv_* can be used at one
> time.\n");
> + goto error;
> + }
> + hyperv_enabled = true;
> hyperv_enable_vapic_recommended(true);
> } else {
> fprintf(stderr, "feature string `%s' not in format
> (+feature|-feature|feature=xyz)\n", featurestr);
> @@ -1017,6 +1127,34 @@ static int cpu_x86_find_by_name(x86_def_t
> *x86_cpu_def, const char *cpu_model)
> }
> featurestr = strtok(NULL, ",");
> }
> +#ifdef CONFIG_KVM
> + if (hyperv_enabled) {
> + x86_cpuid_set_hv(x86_cpu_def, HYPERV_CPUID_MIN, "Microsoft Hv");
> + }
> +#endif
> + if (hyper_extra >= 0) {
> + x86_cpu_def->cpuid_hv_extra = 0x40000000 + hyper_extra;
> + } else if (hv_enabled && x86_cpu_def->tsc_khz) {
> + /*
> + * From http://article.gmane.org/gmane.comp.emulators.kvm.devel/22643
> + *
> + * Leaf 0x40000010, Timing Information.
> + *
> + * VMware has defined the first generic leaf to provide timing
> + * information. This leaf returns the current TSC frequency and
> + * current Bus frequency in kHz.
> + *
> + * # EAX: (Virtual) TSC frequency in kHz.
> + * # EBX: (Virtual) Bus (local apic timer) frequency in kHz.
> + * # ECX, EDX: RESERVED (Per above, reserved fields are set to
> zero).
> + */
> + x86_cpu_def->cpuid_hv_extra = 0x40000010;
> + x86_cpu_def->cpuid_hv_extra_a = (uint32_t)x86_cpu_def->tsc_khz;
> + x86_cpu_def->cpuid_hv_extra_b = (uint32_t)(get_ticks_per_sec() /
> 1000);
> + }
> + if (hyper_level >= 0) {
> + x86_cpu_def->cpuid_hv_level = 0x40000000 + hyper_level;
> + }
> x86_cpu_def->features |= plus_features;
> x86_cpu_def->ext_features |= plus_ext_features;
> x86_cpu_def->ext2_features |= plus_ext2_features;
> @@ -1192,6 +1330,13 @@ int cpu_x86_register(X86CPU *cpu, const char
> *cpu_model)
> env->cpuid_ext4_features = def->ext4_features;
> env->cpuid_7_0_ebx = def->cpuid_7_0_ebx_features;
> env->cpuid_xlevel2 = def->xlevel2;
> + env->cpuid_hv_level = def->cpuid_hv_level;
> + env->cpuid_hv_vendor1 = def->cpuid_hv_vendor1;
> + env->cpuid_hv_vendor2 = def->cpuid_hv_vendor2;
> + env->cpuid_hv_vendor3 = def->cpuid_hv_vendor3;
> + env->cpuid_hv_extra = def->cpuid_hv_extra;
> + env->cpuid_hv_extra_a = def->cpuid_hv_extra_a;
> + env->cpuid_hv_extra_b = def->cpuid_hv_extra_b;
> object_property_set_int(OBJECT(cpu), (int64_t)def->tsc_khz * 1000,
> "tsc-frequency", &error);
> if (!kvm_enabled()) {
> @@ -1390,6 +1535,16 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index,
> uint32_t count,
> index = env->cpuid_xlevel;
> }
> }
> + } else if (index & 0x40000000) {
> + if (env->cpuid_hv_level > 0) {
> + /* Handle Paravirtualization CPUIDs */
> + if (index > env->cpuid_hv_level) {
> + index = env->cpuid_hv_level;
> + }
> + } else {
> + if (index > env->cpuid_level)
> + index = env->cpuid_level;
> + }
> } else {
> if (index > env->cpuid_level)
> index = env->cpuid_level;
> @@ -1528,6 +1683,29 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index,
> uint32_t count,
> *edx = 0;
> }
> break;
> + case 0x40000000:
> + *eax = env->cpuid_hv_level;
> + *ebx = env->cpuid_hv_vendor1;
> + *ecx = env->cpuid_hv_vendor2;
> + *edx = env->cpuid_hv_vendor3;
> + break;
> + case 0x40000001:
> + *eax = env->cpuid_kvm_features;
> + *ebx = 0;
> + *ecx = 0;
> + *edx = 0;
> + break;
> + case 0x40000002 ... 0x400000FF:
> + if (index == env->cpuid_hv_extra) {
> + *eax = env->cpuid_hv_extra_a;
> + *ebx = env->cpuid_hv_extra_b;
> + } else {
> + *eax = 0;
> + *ebx = 0;
> + }
> + *ecx = 0;
> + *edx = 0;
> + break;
> case 0x80000000:
> *eax = env->cpuid_xlevel;
> *ebx = env->cpuid_vendor1;
> diff --git a/target-i386/cpu.h b/target-i386/cpu.h
> index 0677502..dc2039a 100644
> --- a/target-i386/cpu.h
> +++ b/target-i386/cpu.h
> @@ -746,6 +746,15 @@ typedef struct CPUX86State {
> uint32_t cpuid_ext4_features;
> /* Flags from CPUID[EAX=7,ECX=0].EBX */
> uint32_t cpuid_7_0_ebx;
> + /* Paravirtualization CPUIDs */
> + uint32_t cpuid_hv_level;
> + uint32_t cpuid_hv_vendor1;
> + uint32_t cpuid_hv_vendor2;
> + uint32_t cpuid_hv_vendor3;
> + /* VMware extra data */
> + uint32_t cpuid_hv_extra;
> + uint32_t cpuid_hv_extra_a;
> + uint32_t cpuid_hv_extra_b;
>
> /* MTRRs */
> uint64_t mtrr_fixed[11];
> diff --git a/target-i386/kvm.c b/target-i386/kvm.c
> index ffc294e..d01a5f8 100644
> --- a/target-i386/kvm.c
> +++ b/target-i386/kvm.c
> @@ -389,16 +389,18 @@ int kvm_arch_init_vcpu(CPUX86State *env)
> c = &cpuid_data.entries[cpuid_i++];
> memset(c, 0, sizeof(*c));
> c->function = KVM_CPUID_SIGNATURE;
> - if (!hyperv_enabled()) {
> + if (env->cpuid_hv_level == 0) {
> memcpy(signature, "KVMKVMKVM\0\0\0", 12);
> c->eax = 0;
> + c->ebx = signature[0];
> + c->ecx = signature[1];
> + c->edx = signature[2];
> } else {
> - memcpy(signature, "Microsoft Hv", 12);
> - c->eax = HYPERV_CPUID_MIN;
> + c->eax = env->cpuid_hv_level;
> + c->ebx = env->cpuid_hv_vendor1;
> + c->ecx = env->cpuid_hv_vendor2;
> + c->edx = env->cpuid_hv_vendor3;
> }
> - c->ebx = signature[0];
> - c->ecx = signature[1];
> - c->edx = signature[2];
>
> c = &cpuid_data.entries[cpuid_i++];
> memset(c, 0, sizeof(*c));
> @@ -452,6 +454,25 @@ int kvm_arch_init_vcpu(CPUX86State *env)
> c->ebx = signature[0];
> c->ecx = signature[1];
> c->edx = signature[2];
> + } else if (env->cpuid_hv_level > 0) {
> + for (i = KVM_CPUID_FEATURES + 1; i <= env->cpuid_hv_level; i++) {
> + c = &cpuid_data.entries[cpuid_i++];
> + memset(c, 0, sizeof(*c));
> + c->function = i;
> + if (i == env->cpuid_hv_extra) {
> + c->eax = env->cpuid_hv_extra_a;
> + c->ebx = env->cpuid_hv_extra_b;
> + }
> + }
> +
> + c = &cpuid_data.entries[cpuid_i++];
> + memset(c, 0, sizeof(*c));
> + c->function = KVM_CPUID_SIGNATURE_NEXT;
> + memcpy(signature, "KVMKVMKVM\0\0\0", 12);
> + c->eax = 0;
> + c->ebx = signature[0];
> + c->ecx = signature[1];
> + c->edx = signature[2];
> }
>
> has_msr_async_pf_en = c->eax & (1 << KVM_FEATURE_ASYNC_PF);
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to address@hidden
> More majordomo info at http://vger.kernel.org/majordomo-info.html
- Re: [Qemu-devel] [PATCH] target-i386: Allow changing of Hypervisor CPUIDs.,
Marcelo Tosatti <=