diff mbox

target-i386: Allow changing of Hypervisor CPUIDs.

Message ID 1346354435-21685-1-git-send-email-Don@CloudSwitch.com
State New
Headers show

Commit Message

Don Slutz Aug. 30, 2012, 7:20 p.m. UTC
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@CloudSwitch.com>
---
 target-i386/cpu.c |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 target-i386/cpu.h |    9 +++
 target-i386/kvm.c |   33 ++++++++--
 3 files changed, 214 insertions(+), 6 deletions(-)

Comments

Marcelo Tosatti Sept. 5, 2012, 4:48 p.m. UTC | #1
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@CloudSwitch.com>

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@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andreas Färber Sept. 6, 2012, 10:28 a.m. UTC | #2
Am 30.08.2012 21:20, schrieb Don Slutz:
> 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@CloudSwitch.com>
> ---
>  target-i386/cpu.c |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>  target-i386/cpu.h |    9 +++
>  target-i386/kvm.c |   33 ++++++++--
>  3 files changed, 214 insertions(+), 6 deletions(-)

Please don't add new CPU command line options without matching QOM
properties. There are patch series in the works that convert the CPU
definitions to QOM subclasses, the parameters then need to be set on the
X86CPU object instance.

Note that the convention for QOM properties is
descriptive-name-with-dashes rather than shortened_abbrev_with_underscore.

Regards,
Andreas
Eduardo Habkost Sept. 6, 2012, 6:40 p.m. UTC | #3
On Thu, Sep 06, 2012 at 12:28:05PM +0200, Andreas Färber wrote:
> Am 30.08.2012 21:20, schrieb Don Slutz:
> > 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@CloudSwitch.com>
> > ---
> >  target-i386/cpu.c |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  target-i386/cpu.h |    9 +++
> >  target-i386/kvm.c |   33 ++++++++--
> >  3 files changed, 214 insertions(+), 6 deletions(-)
> 
> Please don't add new CPU command line options without matching QOM
> properties. There are patch series in the works that convert the CPU
> definitions to QOM subclasses, the parameters then need to be set on the
> X86CPU object instance.

It seems to be a good idea to wait for the CPU properties series from
Igor to be applied, before implementing this. It should make the new
code much simpler.

> 
> Note that the convention for QOM properties is
> descriptive-name-with-dashes rather than shortened_abbrev_with_underscore.
>
Don Slutz Sept. 6, 2012, 7 p.m. UTC | #4
On 09/06/12 14:40, Eduardo Habkost wrote:
> On Thu, Sep 06, 2012 at 12:28:05PM +0200, Andreas Färber wrote:
>> Am 30.08.2012 21:20, schrieb Don Slutz:
>>> 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@CloudSwitch.com>
>>> ---
>>>   target-i386/cpu.c |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++++
>>>   target-i386/cpu.h |    9 +++
>>>   target-i386/kvm.c |   33 ++++++++--
>>>   3 files changed, 214 insertions(+), 6 deletions(-)
>> Please don't add new CPU command line options without matching QOM
>> properties. There are patch series in the works that convert the CPU
>> definitions to QOM subclasses, the parameters then need to be set on the
>> X86CPU object instance.
I found:
http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg00587.html

Is this the right set?
> It seems to be a good idea to wait for the CPU properties series from
> Igor to be applied, before implementing this. It should make the new
> code much simpler.
So Igor has a patch set that does a similar change like above.  I so far 
have not found it on QEMU-DEVEL.  Please provide a pointer to this patch 
set.
>
>> Note that the convention for QOM properties is
>> descriptive-name-with-dashes rather than shortened_abbrev_with_underscore.
>>
I will be re-working this change, and wait (for v2) until master has 
changed to the new way.
    -Don
Eduardo Habkost Sept. 6, 2012, 7:36 p.m. UTC | #5
On Thu, Sep 06, 2012 at 03:00:47PM -0400, Don Slutz wrote:
> On 09/06/12 14:40, Eduardo Habkost wrote:
> >On Thu, Sep 06, 2012 at 12:28:05PM +0200, Andreas Färber wrote:
> >>Am 30.08.2012 21:20, schrieb Don Slutz:
> >>>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@CloudSwitch.com>
> >>>---
> >>>  target-i386/cpu.c |  178 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> >>>  target-i386/cpu.h |    9 +++
> >>>  target-i386/kvm.c |   33 ++++++++--
> >>>  3 files changed, 214 insertions(+), 6 deletions(-)
> >>Please don't add new CPU command line options without matching QOM
> >>properties. There are patch series in the works that convert the CPU
> >>definitions to QOM subclasses, the parameters then need to be set on the
> >>X86CPU object instance.
> I found:
> http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg00587.html
> 
> Is this the right set?

Yes. But note that there are lots of work in progress in the list (and
work that we haven't submitted to the list yet). You don't necessarily
need to wait for the above to be applied, probably you can just rebase
on top of the "CPU properties" work (that affects your patch more
directly). See below.


> >It seems to be a good idea to wait for the CPU properties series from
> >Igor to be applied, before implementing this. It should make the new
> >code much simpler.
> So Igor has a patch set that does a similar change like above.  I so
> far have not found it on QEMU-DEVEL.  Please provide a pointer to
> this patch set.

I was talking specifically about the "CPU properties" series, at:
http://article.gmane.org/gmane.comp.emulators.qemu/165728

The "CPU model classes" series (URL you sent above) is an additional
series to be applied on top of the "CPU properties" series. But the "CPU
model classes" work probably don't impact your patch, so you can simply
be ready to rebase/resend your patch after Igor sends a new version of
the CPU properties series.

Note that the URLs above are old versions of the work in progress. You
may find newer versions at my github tree[1] or at Igor's tree[2].

Basically once we introduce CPU properties, you'll just need to define
the new properties on X86CPU, without the need to change the CPU model
string parsing code. It should make your patch simpler.


[1] An experimental rebase of Igor's CPU properties series (Igor's
    latest version may look different):
    https://github.com/ehabkost/qemu-hacks/tree/work/cpu-properties-igor-rebase-v4.3-2012-08-31
    CPU model classes:
    (may look different when I submit to qemu-devel again):
    https://github.com/ehabkost/qemu-hacks/tree/work/cpu-model-classes-v2.8-2012-08-31
    Lots of work in progress, including multiple branches/series:
    (may look very different when I submit to qemu-devel):
    https://github.com/ehabkost/qemu-hacks/tree/work/cpuid-refactor-v0.22-2012-08-31
[2] Igor's work in progress branch:
    https://github.com/imammedo/qemu/tree/x86-cpu-properties.WIP


> >
> >>Note that the convention for QOM properties is
> >>descriptive-name-with-dashes rather than shortened_abbrev_with_underscore.
> >>
> I will be re-working this change, and wait (for v2) until master has
> changed to the new way.
>    -Don
Don Slutz Sept. 11, 2012, 1:57 p.m. UTC | #6
On 09/05/12 12:48, Marcelo Tosatti wrote:
> 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@CloudSwitch.com>
> For what purpose?
To be able to run bits copied from a ESX(i) server with limited changes 
to the bits.
>
> Is the VMWare interface documented somewhere?
Not that I know of.  All of this change is taken from the Linux source 
(2.6.18-194.32.1.el5 or later) that checks for running on a VMware 
hypervisor.
>
>> ---
>>   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@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
v2 is on it way now that QEMU 1.3 is open.
     -Don
Don Slutz Sept. 11, 2012, 2:07 p.m. UTC | #7
This depends on:

http://lists.gnu.org/archive/html/qemu-devel/2012-09/msg01400.html

As far as I know it is #4. It depends on (1) and (2) and (3).

Changes from v1 to v2:

1) Added 1/4 from http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg05153.html

   Because Fred is changing jobs and so will not be pushing to get
   this in. It needed to be rebased, And I needed it to complete the
   testing of this change.

2) Added 2/4 because of the re-work I needed a way to clear all KVM bits,

3) The rework of v1.  Make it fit into the object model re-work of cpu.c for x86.

4) Added 3/4 -- The split out of the code that is not needed for accel=kvm.


Don Slutz (4):
  target-i386: Allow tsc-frequency to be larger then 2.147G
  target-i386: Add missing kvm bits.
  target-i386: Allow changing of Hypervisor CPUIDs.
  target-i386: Add Hypervisor CPUIDs in accel=tcg mode.

 target-i386/cpu.c |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 target-i386/cpu.h |   21 +++++
 target-i386/kvm.c |   33 ++++++--
 3 files changed, 304 insertions(+), 11 deletions(-)
Don Slutz Sept. 17, 2012, 1:39 p.m. UTC | #8
Also known as Paravirtualization CPUIDs.

This is primarily done so that the guest will think it is running
under vmware when hypervisor-vendor=vmware is specified as a
property of a cpu.


This depends on:

http://lists.gnu.org/archive/html/qemu-devel/2012-09/msg01400.html

As far as I know it is #4. It depends on (1) and (2) and (3).

This change is based on:

Microsoft Hypervisor CPUID Leaves:
  http://msdn.microsoft.com/en-us/library/windows/hardware/ff542428%28v=vs.85%29.aspx

Linux kernel change starts with:
  http://fixunix.com/kernel/538707-use-cpuid-communicate-hypervisor.html
Also:
  http://lkml.indiana.edu/hypermail/linux/kernel/1205.0/00100.html

VMware documention on CPUIDs (Mechanisms to determine if software is
running in a VMware virtual machine):
  http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458

Changes from v1 to v2:

1) Added 1/4 from http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg05153.html

   Because Fred is changing jobs and so will not be pushing to get
   this in. It needed to be rebased, And I needed it to complete the
   testing of this change.

2) Added 2/4 because of the re-work I needed a way to clear all KVM bits,

3) The rework of v1.  Make it fit into the object model re-work of cpu.c for x86.

4) Added 3/4 -- The split out of the code that is not needed for accel=kvm.

Changes from v2 to v3:

Marcelo Tosatti:
  Its one big patch, better split in logically correlated patches
  (with better changelog). This would help reviewers.

So split 3 and 4 into 3 to 17.  More info in change log.
No code change.

Don Slutz (17):
  target-i386: Allow tsc-frequency to be larger then 2.147G
  target-i386: Add missing kvm bits.
  target-i386: Add Hypervisor level.
  target-i386: Add cpu object access routines for Hypervisor level.
  target-i386: Add x86_set_hyperv.
  target-i386: Use Hypervisor level in -machine pc,accel=kvm.
  target-i386: Use Hypervisor level in -machine pc,accel=tcg.
  target-i386: Add Hypervisor vendor.
  target-i386: Add cpu object access routines for Hypervisor vendor.
  target-i386: Use Hypervisor vendor in -machine pc,accel=kvm.
  target-i386: Use Hypervisor vendor in -machine pc,accel=tcg.
  target-i386: Add some known names to Hypervisor vendor.
  target-i386: Add optional Hypervisor leaf extra.
  target-i386: Add cpu object access routines for Hypervisor leaf
    extra.
  target-i386: Add setting of Hypervisor leaf extra for known vmare4.
  target-i386: Use Hypervisor leaf extra in -machine pc,accel=kvm.
  target-i386: Use Hypervisor leaf extra in -machine pc,accel=tcg.

 target-i386/cpu.c |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 target-i386/cpu.h |   21 +++++
 target-i386/kvm.c |   33 ++++++--
 3 files changed, 304 insertions(+), 11 deletions(-)
Don Slutz Sept. 17, 2012, 1:39 p.m. UTC | #9
*** BLURB HERE ***

Don Slutz (17):
  target-i386: Allow tsc-frequency to be larger then 2.147G
  target-i386: Add missing kvm bits.
  target-i386: Add Hypervisor level.
  target-i386: Add cpu object access routines for Hypervisor level.
  target-i386: Add x86_set_hyperv.
  target-i386: Use Hypervisor level in -machine pc,accel=kvm.
  target-i386: Use Hypervisor level in -machine pc,accel=tcg.
  target-i386: Add Hypervisor vendor.
  target-i386: Add cpu object access routines for Hypervisor vendor.
  target-i386: Use Hypervisor vendor in -machine pc,accel=kvm.
  target-i386: Use Hypervisor vendor in -machine pc,accel=tcg.
  target-i386: Add some known names to Hypervisor vendor.
  target-i386: Add optional Hypervisor leaf extra.
  target-i386: Add cpu object access routines for Hypervisor leaf
    extra.
  target-i386: Add setting of Hypervisor leaf extra for known vmare4.
  target-i386: Use Hypervisor leaf extra in -machine pc,accel=kvm.
  target-i386: Use Hypervisor leaf extra in -machine pc,accel=tcg.

 target-i386/cpu.c |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 target-i386/cpu.h |   21 +++++
 target-i386/kvm.c |   33 ++++++--
 3 files changed, 304 insertions(+), 11 deletions(-)
Don Slutz Sept. 17, 2012, 1:39 p.m. UTC | #10
Also known as Paravirtualization CPUIDs.

This is primarily done so that the guest will think it is running
under vmware when hypervisor-vendor=vmware is specified as a
property of a cpu.


This depends on:

http://lists.gnu.org/archive/html/qemu-devel/2012-09/msg01400.html

As far as I know it is #4. It depends on (1) and (2) and (3).

This change is based on:

Microsoft Hypervisor CPUID Leaves:
  http://msdn.microsoft.com/en-us/library/windows/hardware/ff542428%28v=vs.85%29.aspx

Linux kernel change starts with:
  http://fixunix.com/kernel/538707-use-cpuid-communicate-hypervisor.html
Also:
  http://lkml.indiana.edu/hypermail/linux/kernel/1205.0/00100.html

VMware documention on CPUIDs (Mechanisms to determine if software is
running in a VMware virtual machine):
  http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458

Changes from v1 to v2:

1) Added 1/4 from http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg05153.html

   Because Fred is changing jobs and so will not be pushing to get
   this in. It needed to be rebased, And I needed it to complete the
   testing of this change.

2) Added 2/4 because of the re-work I needed a way to clear all KVM bits,

3) The rework of v1.  Make it fit into the object model re-work of cpu.c for x86.

4) Added 3/4 -- The split out of the code that is not needed for accel=kvm.

Changes from v2 to v3:

Marcelo Tosatti:
  Its one big patch, better split in logically correlated patches
  (with better changelog). This would help reviewers.

So split 3 and 4 into 3 to 17.  More info in change log.
No code change.

Don Slutz (17):
  target-i386: Allow tsc-frequency to be larger then 2.147G
  target-i386: Add missing kvm bits.
  target-i386: Add Hypervisor level.
  target-i386: Add cpu object access routines for Hypervisor level.
  target-i386: Add x86_set_hyperv.
  target-i386: Use Hypervisor level in -machine pc,accel=kvm.
  target-i386: Use Hypervisor level in -machine pc,accel=tcg.
  target-i386: Add Hypervisor vendor.
  target-i386: Add cpu object access routines for Hypervisor vendor.
  target-i386: Use Hypervisor vendor in -machine pc,accel=kvm.
  target-i386: Use Hypervisor vendor in -machine pc,accel=tcg.
  target-i386: Add some known names to Hypervisor vendor.
  target-i386: Add optional Hypervisor leaf extra.
  target-i386: Add cpu object access routines for Hypervisor leaf
    extra.
  target-i386: Add setting of Hypervisor leaf extra for known vmare4.
  target-i386: Use Hypervisor leaf extra in -machine pc,accel=kvm.
  target-i386: Use Hypervisor leaf extra in -machine pc,accel=tcg.

 target-i386/cpu.c |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 target-i386/cpu.h |   21 +++++
 target-i386/kvm.c |   33 ++++++--
 3 files changed, 304 insertions(+), 11 deletions(-)
Don Slutz Sept. 17, 2012, 1:49 p.m. UTC | #11
forgot to delete the backup versions. :(
     -Don
On 09/17/12 09:39, Don Slutz wrote:
> *** BLURB HERE ***
>
> Don Slutz (17):
>    target-i386: Allow tsc-frequency to be larger then 2.147G
>    target-i386: Add missing kvm bits.
>    target-i386: Add Hypervisor level.
>    target-i386: Add cpu object access routines for Hypervisor level.
>    target-i386: Add x86_set_hyperv.
>    target-i386: Use Hypervisor level in -machine pc,accel=kvm.
>    target-i386: Use Hypervisor level in -machine pc,accel=tcg.
>    target-i386: Add Hypervisor vendor.
>    target-i386: Add cpu object access routines for Hypervisor vendor.
>    target-i386: Use Hypervisor vendor in -machine pc,accel=kvm.
>    target-i386: Use Hypervisor vendor in -machine pc,accel=tcg.
>    target-i386: Add some known names to Hypervisor vendor.
>    target-i386: Add optional Hypervisor leaf extra.
>    target-i386: Add cpu object access routines for Hypervisor leaf
>      extra.
>    target-i386: Add setting of Hypervisor leaf extra for known vmare4.
>    target-i386: Use Hypervisor leaf extra in -machine pc,accel=kvm.
>    target-i386: Use Hypervisor leaf extra in -machine pc,accel=tcg.
>
>   target-i386/cpu.c |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
>   target-i386/cpu.h |   21 +++++
>   target-i386/kvm.c |   33 ++++++--
>   3 files changed, 304 insertions(+), 11 deletions(-)
>
Don Slutz Sept. 17, 2012, 1:51 p.m. UTC | #12
On 09/17/12 09:49, Don Slutz wrote:
> forgot to delete the backup versions. :(
>     -Don
> On 09/17/12 09:39, Don Slutz wrote:
Here is the planned cover letter:

 From 7c0a80d8e870da981786b7235d3a968024c89abb Mon Sep 17 00:00:00 2001
In-Reply-To: <1346354435-21685-1-git-send-email-Don@CloudSwitch.com>
References: <1346354435-21685-1-git-send-email-Don@CloudSwitch.com>
From: Don Slutz <Don@CloudSwitch.com>
Date: Mon, 17 Sep 2012 09:23:29 -0400
Subject: [PATCH v3 00/17] Allow changing of Hypervisor CPUIDs.

Also known as Paravirtualization CPUIDs.

This is primarily done so that the guest will think it is running
under vmware when hypervisor-vendor=vmware is specified as a
property of a cpu.


This depends on:

http://lists.gnu.org/archive/html/qemu-devel/2012-09/msg01400.html

As far as I know it is #4. It depends on (1) and (2) and (3).

This change is based on:

Microsoft Hypervisor CPUID Leaves:
http://msdn.microsoft.com/en-us/library/windows/hardware/ff542428%28v=vs.85%29.aspx

Linux kernel change starts with:
http://fixunix.com/kernel/538707-use-cpuid-communicate-hypervisor.html
Also:
   http://lkml.indiana.edu/hypermail/linux/kernel/1205.0/00100.html

VMware documention on CPUIDs (Mechanisms to determine if software is
running in a VMware virtual machine):
http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458

Changes from v1 to v2:

1) Added 1/4 from 
http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg05153.html

    Because Fred is changing jobs and so will not be pushing to get
    this in. It needed to be rebased, And I needed it to complete the
    testing of this change.

2) Added 2/4 because of the re-work I needed a way to clear all KVM bits,

3) The rework of v1.  Make it fit into the object model re-work of cpu.c 
for x86.

4) Added 3/4 -- The split out of the code that is not needed for accel=kvm.

Changes from v2 to v3:

Marcelo Tosatti:
   Its one big patch, better split in logically correlated patches
   (with better changelog). This would help reviewers.

So split 3 and 4 into 3 to 17.  More info in change log.
No code change.

Don Slutz (17):
   target-i386: Allow tsc-frequency to be larger then 2.147G
   target-i386: Add missing kvm bits.
   target-i386: Add Hypervisor level.
   target-i386: Add cpu object access routines for Hypervisor level.
   target-i386: Add x86_set_hyperv.
   target-i386: Use Hypervisor level in -machine pc,accel=kvm.
   target-i386: Use Hypervisor level in -machine pc,accel=tcg.
   target-i386: Add Hypervisor vendor.
   target-i386: Add cpu object access routines for Hypervisor vendor.
   target-i386: Use Hypervisor vendor in -machine pc,accel=kvm.
   target-i386: Use Hypervisor vendor in -machine pc,accel=tcg.
   target-i386: Add some known names to Hypervisor vendor.
   target-i386: Add optional Hypervisor leaf extra.
   target-i386: Add cpu object access routines for Hypervisor leaf
     extra.
   target-i386: Add setting of Hypervisor leaf extra for known vmare4.
   target-i386: Use Hypervisor leaf extra in -machine pc,accel=kvm.
   target-i386: Use Hypervisor leaf extra in -machine pc,accel=tcg.

  target-i386/cpu.c |  261 
++++++++++++++++++++++++++++++++++++++++++++++++++++-
  target-i386/cpu.h |   21 +++++
  target-i386/kvm.c |   33 ++++++--
  3 files changed, 304 insertions(+), 11 deletions(-)
Don Slutz Sept. 17, 2012, 2 p.m. UTC | #13
Resend with new id so the backup files are not included.

Also known as Paravirtualization CPUIDs.

This is primarily done so that the guest will think it is running
under vmware when hypervisor-vendor=vmware is specified as a
property of a cpu.


This depends on:

http://lists.gnu.org/archive/html/qemu-devel/2012-09/msg01400.html

As far as I know it is #4. It depends on (1) and (2) and (3).

This change is based on:

Microsoft Hypervisor CPUID Leaves:
  http://msdn.microsoft.com/en-us/library/windows/hardware/ff542428%28v=vs.85%29.aspx

Linux kernel change starts with:
  http://fixunix.com/kernel/538707-use-cpuid-communicate-hypervisor.html
Also:
  http://lkml.indiana.edu/hypermail/linux/kernel/1205.0/00100.html

VMware documention on CPUIDs (Mechanisms to determine if software is
running in a VMware virtual machine):
  http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458

Changes from v1 to v2:

1) Added 1/4 from http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg05153.html

   Because Fred is changing jobs and so will not be pushing to get
   this in. It needed to be rebased, And I needed it to complete the
   testing of this change.

2) Added 2/4 because of the re-work I needed a way to clear all KVM bits,

3) The rework of v1.  Make it fit into the object model re-work of cpu.c for x86.

4) Added 3/4 -- The split out of the code that is not needed for accel=kvm.

Changes from v2 to v3:

Marcelo Tosatti:
  Its one big patch, better split in logically correlated patches
  (with better changelog). This would help reviewers.

So split 3 and 4 into 3 to 17.  More info in change log.
No code change.

Don Slutz (17):
  target-i386: Allow tsc-frequency to be larger then 2.147G
  target-i386: Add missing kvm bits.
  target-i386: Add Hypervisor level.
  target-i386: Add cpu object access routines for Hypervisor level.
  target-i386: Add x86_set_hyperv.
  target-i386: Use Hypervisor level in -machine pc,accel=kvm.
  target-i386: Use Hypervisor level in -machine pc,accel=tcg.
  target-i386: Add Hypervisor vendor.
  target-i386: Add cpu object access routines for Hypervisor vendor.
  target-i386: Use Hypervisor vendor in -machine pc,accel=kvm.
  target-i386: Use Hypervisor vendor in -machine pc,accel=tcg.
  target-i386: Add some known names to Hypervisor vendor.
  target-i386: Add optional Hypervisor leaf extra.
  target-i386: Add cpu object access routines for Hypervisor leaf
    extra.
  target-i386: Add setting of Hypervisor leaf extra for known vmare4.
  target-i386: Use Hypervisor leaf extra in -machine pc,accel=kvm.
  target-i386: Use Hypervisor leaf extra in -machine pc,accel=tcg.

 target-i386/cpu.c |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 target-i386/cpu.h |   21 +++++
 target-i386/kvm.c |   33 ++++++--
 3 files changed, 304 insertions(+), 11 deletions(-)
Eduardo Habkost Sept. 17, 2012, 7:27 p.m. UTC | #14
On Mon, Sep 17, 2012 at 10:00:50AM -0400, Don Slutz wrote:
> Resend with new id so the backup files are not included.
> 
> Also known as Paravirtualization CPUIDs.
> 
> This is primarily done so that the guest will think it is running
> under vmware when hypervisor-vendor=vmware is specified as a
> property of a cpu.
> 
> 
> This depends on:
> 
> http://lists.gnu.org/archive/html/qemu-devel/2012-09/msg01400.html
> 
> As far as I know it is #4. It depends on (1) and (2) and (3).

Correct.

I have removed v2 and added this version to my cpu-queue[1] branch.

[1] https://github.com/ehabkost/qemu/commits/cpu-queue
    My branch is now based on Andreas's qom-cpu branch from
    https://github.com/afaerber/qemu-cpu/commits/qom-cpu


> 
> This change is based on:
> 
> Microsoft Hypervisor CPUID Leaves:
>   http://msdn.microsoft.com/en-us/library/windows/hardware/ff542428%28v=vs.85%29.aspx
> 
> Linux kernel change starts with:
>   http://fixunix.com/kernel/538707-use-cpuid-communicate-hypervisor.html
> Also:
>   http://lkml.indiana.edu/hypermail/linux/kernel/1205.0/00100.html
> 
> VMware documention on CPUIDs (Mechanisms to determine if software is
> running in a VMware virtual machine):
>   http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458
> 
> Changes from v1 to v2:
> 
> 1) Added 1/4 from http://lists.gnu.org/archive/html/qemu-devel/2012-08/msg05153.html
> 
>    Because Fred is changing jobs and so will not be pushing to get
>    this in. It needed to be rebased, And I needed it to complete the
>    testing of this change.
> 
> 2) Added 2/4 because of the re-work I needed a way to clear all KVM bits,
> 
> 3) The rework of v1.  Make it fit into the object model re-work of cpu.c for x86.
> 
> 4) Added 3/4 -- The split out of the code that is not needed for accel=kvm.
> 
> Changes from v2 to v3:
> 
> Marcelo Tosatti:
>   Its one big patch, better split in logically correlated patches
>   (with better changelog). This would help reviewers.
> 
> So split 3 and 4 into 3 to 17.  More info in change log.
> No code change.
> 
> Don Slutz (17):
>   target-i386: Allow tsc-frequency to be larger then 2.147G
>   target-i386: Add missing kvm bits.
>   target-i386: Add Hypervisor level.
>   target-i386: Add cpu object access routines for Hypervisor level.
>   target-i386: Add x86_set_hyperv.
>   target-i386: Use Hypervisor level in -machine pc,accel=kvm.
>   target-i386: Use Hypervisor level in -machine pc,accel=tcg.
>   target-i386: Add Hypervisor vendor.
>   target-i386: Add cpu object access routines for Hypervisor vendor.
>   target-i386: Use Hypervisor vendor in -machine pc,accel=kvm.
>   target-i386: Use Hypervisor vendor in -machine pc,accel=tcg.
>   target-i386: Add some known names to Hypervisor vendor.
>   target-i386: Add optional Hypervisor leaf extra.
>   target-i386: Add cpu object access routines for Hypervisor leaf
>     extra.
>   target-i386: Add setting of Hypervisor leaf extra for known vmare4.
>   target-i386: Use Hypervisor leaf extra in -machine pc,accel=kvm.
>   target-i386: Use Hypervisor leaf extra in -machine pc,accel=tcg.
> 
>  target-i386/cpu.c |  261 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  target-i386/cpu.h |   21 +++++
>  target-i386/kvm.c |   33 ++++++--
>  3 files changed, 304 insertions(+), 11 deletions(-)
> 
>
diff mbox

Patch

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);