Re: [PATCH] target-i386: Allow changing of Hypervisor CPUIDs.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 <Don@xxxxxxxxxxxxxxx>

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 majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [KVM ARM]     [KVM ia64]     [KVM ppc]     [Virtualization Tools]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Questions]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux