diff mbox

[3/5] target-i386: Slim conversion to X86CPU subclasses

Message ID 1360082364-12475-4-git-send-email-imammedo@redhat.com
State New
Headers show

Commit Message

Igor Mammedov Feb. 5, 2013, 4:39 p.m. UTC
From: Andreas Färber <afaerber@suse.de>

Move x86_def_t definition to header and embed into X86CPUClass.
Register types per built-in model definition.

Move version initialization from x86_cpudef_setup() to class_init.

Inline cpu_x86_register() into the X86CPU initfn.
Since instance_init cannot reports errors, drop error handling.

Replace cpu_x86_find_by_name() with x86_cpu_class_by_name().
Move handling of KVM host vendor override from cpu_x86_find_by_name()
to the kvm_arch_init() and class_init(). Use TYPE_X86_CPU class to
communicate kvm specific defaults to other sub-classes.

Register host-{i386,x86_64}-cpu type from KVM code to avoid #ifdefs
and only when KVM is enabled to avoid hacks in CPU code.
Make kvm_cpu_fill_host() into a host specific class_init and inline
cpu_x86_fill_model_id().

Let kvm_check_features_against_host() obtain host-{i386,86_64}-cpu for
comparison.

Signed-off-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Igor Mammedov <imammedo@redhat.com>
---
v4:
  * set error if cpu model is not found and goto out;
  * copy vendor override from 'host' CPU class in sub-class'es
    class_init() if 'host' CPU class is available.
  * register type TYPE_HOST_X86_CPU in kvm_arch_init(), this type
    should be available only in KVM mode and we haven't printed it in
    -cpu ? output so far, so we can continue doing so. It's not
    really confusing to show 'host' cpu (even if we do it) when KVM
    is not enabled.
  * remove special case for 'host' CPU check in x86_cpu_class_by_name(),
    due to 'host' CPU will not find anything if not in KVM mode or
    return 'host' CPU class in KVM mode, i.e. treat it as regular CPUs.
---
 target-i386/cpu-qom.h |   24 ++++
 target-i386/cpu.c     |  331 ++++++++++++++++++-------------------------------
 target-i386/cpu.h     |    5 +-
 target-i386/kvm.c     |   72 +++++++++++
 4 files changed, 217 insertions(+), 215 deletions(-)

Comments

Eduardo Habkost Feb. 7, 2013, 3:08 p.m. UTC | #1
On Tue, Feb 05, 2013 at 05:39:22PM +0100, Igor Mammedov wrote:
> From: Andreas Färber <afaerber@suse.de>
> 
> Move x86_def_t definition to header and embed into X86CPUClass.
> Register types per built-in model definition.
> 
> Move version initialization from x86_cpudef_setup() to class_init.
> 
> Inline cpu_x86_register() into the X86CPU initfn.
> Since instance_init cannot reports errors, drop error handling.
> 
> Replace cpu_x86_find_by_name() with x86_cpu_class_by_name().
> Move handling of KVM host vendor override from cpu_x86_find_by_name()
> to the kvm_arch_init() and class_init(). Use TYPE_X86_CPU class to
> communicate kvm specific defaults to other sub-classes.
> 
> Register host-{i386,x86_64}-cpu type from KVM code to avoid #ifdefs
> and only when KVM is enabled to avoid hacks in CPU code.
> Make kvm_cpu_fill_host() into a host specific class_init and inline
> cpu_x86_fill_model_id().
> 
> Let kvm_check_features_against_host() obtain host-{i386,86_64}-cpu for
> comparison.
> 
> Signed-off-by: Andreas Färber <afaerber@suse.de>
> Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> ---
> v4:
>   * set error if cpu model is not found and goto out;
>   * copy vendor override from 'host' CPU class in sub-class'es
>     class_init() if 'host' CPU class is available.
>   * register type TYPE_HOST_X86_CPU in kvm_arch_init(), this type
>     should be available only in KVM mode and we haven't printed it in
>     -cpu ? output so far, so we can continue doing so. It's not
>     really confusing to show 'host' cpu (even if we do it) when KVM
>     is not enabled.
>   * remove special case for 'host' CPU check in x86_cpu_class_by_name(),
>     due to 'host' CPU will not find anything if not in KVM mode or
>     return 'host' CPU class in KVM mode, i.e. treat it as regular CPUs.
> ---
>  target-i386/cpu-qom.h |   24 ++++
>  target-i386/cpu.c     |  331 ++++++++++++++++++-------------------------------
>  target-i386/cpu.h     |    5 +-
>  target-i386/kvm.c     |   72 +++++++++++
>  4 files changed, 217 insertions(+), 215 deletions(-)
> 
> diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
> index 48e6b54..80bf72d 100644
> --- a/target-i386/cpu-qom.h
> +++ b/target-i386/cpu-qom.h
> @@ -30,6 +30,27 @@
>  #define TYPE_X86_CPU "i386-cpu"
>  #endif
>  
> +#define TYPE_HOST_X86_CPU "host-" TYPE_X86_CPU

Can we introduce a X86_CPU_CLASS_NAME() macro to help us make the rules
to generate CPU class names clearer?

e.g.:

#define X86_CPU_CLASS_NAME(s) s "-" TYPE_X86_CPU
[...]
#define TYPE_HOST_X86_CPU X86_CPU_CLASS_NAME("host")
[...]
  /* (at the class lookup code) */
  typename = g_strdup_printf(X86_CPU_CLASS_NAME("%s"), name);



> +
> +typedef struct x86_def_t {
> +    const char *name;
> +    uint32_t level;
> +    /* vendor is zero-terminated, 12 character ASCII string */
> +    char vendor[CPUID_VENDOR_SZ + 1];
> +    int family;
> +    int model;
> +    int stepping;
> +    uint32_t features, ext_features, ext2_features, ext3_features;
> +    uint32_t kvm_features, svm_features;
> +    uint32_t xlevel;
> +    char model_id[48];
> +    /* Store the results of Centaur's CPUID instructions */
> +    uint32_t ext4_features;
> +    uint32_t xlevel2;
> +    /* The feature bits on CPUID[EAX=7,ECX=0].EBX */
> +    uint32_t cpuid_7_0_ebx_features;
> +} x86_def_t;
> +
>  #define X86_CPU_CLASS(klass) \
>      OBJECT_CLASS_CHECK(X86CPUClass, (klass), TYPE_X86_CPU)
>  #define X86_CPU(obj) \
> @@ -41,6 +62,7 @@
>   * X86CPUClass:
>   * @parent_realize: The parent class' realize handler.
>   * @parent_reset: The parent class' reset handler.
> + * @info: Model-specific data.
>   *
>   * An x86 CPU model or family.
>   */
> @@ -51,6 +73,8 @@ typedef struct X86CPUClass {
>  
>      DeviceRealize parent_realize;
>      void (*parent_reset)(CPUState *cpu);
> +
> +    x86_def_t info;

I thought you had suggesting making it a pointer. If you made it a
pointer, you wouldn't need to move the x86_def_t declaration to
cpu-qom.h.

>  } X86CPUClass;
>  
>  /**
> diff --git a/target-i386/cpu.c b/target-i386/cpu.c
> index 1aee097..62fdc84 100644
> --- a/target-i386/cpu.c
> +++ b/target-i386/cpu.c
> @@ -47,8 +47,8 @@
>  #include "hw/apic_internal.h"
>  #endif
>  
> -static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
> -                                     uint32_t vendor2, uint32_t vendor3)
> +void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
> +                              uint32_t vendor2, uint32_t vendor3)
>  {
>      int i;
>      for (i = 0; i < 4; i++) {
> @@ -346,25 +346,6 @@ static void add_flagname_to_bitmaps(const char *flagname,
>      }
>  }
>  
> -typedef struct x86_def_t {
> -    const char *name;
> -    uint32_t level;
> -    /* vendor is zero-terminated, 12 character ASCII string */
> -    char vendor[CPUID_VENDOR_SZ + 1];
> -    int family;
> -    int model;
> -    int stepping;
> -    uint32_t features, ext_features, ext2_features, ext3_features;
> -    uint32_t kvm_features, svm_features;
> -    uint32_t xlevel;
> -    char model_id[48];
> -    /* Store the results of Centaur's CPUID instructions */
> -    uint32_t ext4_features;
> -    uint32_t xlevel2;
> -    /* The feature bits on CPUID[EAX=7,ECX=0].EBX */
> -    uint32_t cpuid_7_0_ebx_features;
> -} x86_def_t;
> -
>  #define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE)
>  #define PENTIUM_FEATURES (I486_FEATURES | CPUID_DE | CPUID_TSC | \
>            CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_MMX | CPUID_APIC)
> @@ -868,86 +849,6 @@ static x86_def_t builtin_x86_defs[] = {
>      },
>  };
>  
> -#ifdef CONFIG_KVM
> -static int cpu_x86_fill_model_id(char *str)
> -{
> -    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
> -    int i;
> -
> -    for (i = 0; i < 3; i++) {
> -        host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx);
> -        memcpy(str + i * 16 +  0, &eax, 4);
> -        memcpy(str + i * 16 +  4, &ebx, 4);
> -        memcpy(str + i * 16 +  8, &ecx, 4);
> -        memcpy(str + i * 16 + 12, &edx, 4);
> -    }
> -    return 0;
> -}
> -#endif
> -
> -/* Fill a x86_def_t struct with information about the host CPU, and
> - * the CPU features supported by the host hardware + host kernel
> - *
> - * This function may be called only if KVM is enabled.
> - */
> -static void kvm_cpu_fill_host(x86_def_t *x86_cpu_def)
> -{
> -#ifdef CONFIG_KVM
> -    KVMState *s = kvm_state;
> -    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
> -
> -    assert(kvm_enabled());
> -
> -    x86_cpu_def->name = "host";
> -    host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
> -    x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx);
> -
> -    host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
> -    x86_cpu_def->family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF);
> -    x86_cpu_def->model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12);
> -    x86_cpu_def->stepping = eax & 0x0F;
> -
> -    x86_cpu_def->level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX);
> -    x86_cpu_def->features = kvm_arch_get_supported_cpuid(s, 0x1, 0, R_EDX);
> -    x86_cpu_def->ext_features = kvm_arch_get_supported_cpuid(s, 0x1, 0, R_ECX);
> -
> -    if (x86_cpu_def->level >= 7) {
> -        x86_cpu_def->cpuid_7_0_ebx_features =
> -                    kvm_arch_get_supported_cpuid(s, 0x7, 0, R_EBX);
> -    } else {
> -        x86_cpu_def->cpuid_7_0_ebx_features = 0;
> -    }
> -
> -    x86_cpu_def->xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX);
> -    x86_cpu_def->ext2_features =
> -                kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX);
> -    x86_cpu_def->ext3_features =
> -                kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX);
> -
> -    cpu_x86_fill_model_id(x86_cpu_def->model_id);
> -
> -    /* Call Centaur's CPUID instruction. */
> -    if (!strcmp(x86_cpu_def->vendor, CPUID_VENDOR_VIA)) {
> -        host_cpuid(0xC0000000, 0, &eax, &ebx, &ecx, &edx);
> -        eax = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX);
> -        if (eax >= 0xC0000001) {
> -            /* Support VIA max extended level */
> -            x86_cpu_def->xlevel2 = eax;
> -            host_cpuid(0xC0000001, 0, &eax, &ebx, &ecx, &edx);
> -            x86_cpu_def->ext4_features =
> -                    kvm_arch_get_supported_cpuid(s, 0xC0000001, 0, R_EDX);
> -        }
> -    }
> -
> -    /* Other KVM-specific feature fields: */
> -    x86_cpu_def->svm_features =
> -        kvm_arch_get_supported_cpuid(s, 0x8000000A, 0, R_EDX);
> -    x86_cpu_def->kvm_features =
> -        kvm_arch_get_supported_cpuid(s, KVM_CPUID_FEATURES, 0, R_EAX);
> -
> -#endif /* CONFIG_KVM */
> -}
> -
>  static int unavailable_host_feature(FeatureWordInfo *f, uint32_t mask)
>  {
>      int i;
> @@ -975,31 +876,31 @@ static int unavailable_host_feature(FeatureWordInfo *f, uint32_t mask)
>  static int kvm_check_features_against_host(X86CPU *cpu)
>  {
>      CPUX86State *env = &cpu->env;
> -    x86_def_t host_def;
> +    ObjectClass *host_oc = object_class_by_name(TYPE_HOST_X86_CPU);
> +    X86CPUClass *host_xcc = X86_CPU_CLASS(host_oc);
>      uint32_t mask;
>      int rv, i;
>      struct model_features_t ft[] = {
> -        {&env->cpuid_features, &host_def.features,
> +        {&env->cpuid_features, &host_xcc->info.features,
>              FEAT_1_EDX },
> -        {&env->cpuid_ext_features, &host_def.ext_features,
> +        {&env->cpuid_ext_features, &host_xcc->info.ext_features,
>              FEAT_1_ECX },
> -        {&env->cpuid_ext2_features, &host_def.ext2_features,
> +        {&env->cpuid_ext2_features, &host_xcc->info.ext2_features,
>              FEAT_8000_0001_EDX },
> -        {&env->cpuid_ext3_features, &host_def.ext3_features,
> +        {&env->cpuid_ext3_features, &host_xcc->info.ext3_features,
>              FEAT_8000_0001_ECX },
> -        {&env->cpuid_ext4_features, &host_def.ext4_features,
> +        {&env->cpuid_ext4_features, &host_xcc->info.ext4_features,
>              FEAT_C000_0001_EDX },
> -        {&env->cpuid_7_0_ebx_features, &host_def.cpuid_7_0_ebx_features,
> +        {&env->cpuid_7_0_ebx_features, &host_xcc->info.cpuid_7_0_ebx_features,
>              FEAT_7_0_EBX },
> -        {&env->cpuid_svm_features, &host_def.svm_features,
> +        {&env->cpuid_svm_features, &host_xcc->info.svm_features,
>              FEAT_SVM },
> -        {&env->cpuid_kvm_features, &host_def.kvm_features,
> +        {&env->cpuid_kvm_features, &host_xcc->info.kvm_features,
>              FEAT_KVM },
>      };
>  
>      assert(kvm_enabled());
>  
> -    kvm_cpu_fill_host(&host_def);
>      for (rv = 0, i = 0; i < ARRAY_SIZE(ft); ++i) {
>          FeatureWord w = ft[i].feat_word;
>          FeatureWordInfo *wi = &feature_word_info[w];
> @@ -1261,40 +1162,30 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque,
>      cpu->env.tsc_khz = value / 1000;
>  }
>  
> -static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *name)
> +static ObjectClass *x86_cpu_class_by_name(const char *name)
>  {
> -    x86_def_t *def;
> -    int i;
> +    ObjectClass *oc;
> +    char *typename;
>  
>      if (name == NULL) {
> -        return -1;
> -    }
> -    if (kvm_enabled() && strcmp(name, "host") == 0) {
> -        kvm_cpu_fill_host(x86_cpu_def);
> -        return 0;
> +        return NULL;
>      }
>  
> -    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
> -        def = &builtin_x86_defs[i];
> -        if (strcmp(name, def->name) == 0) {
> -            memcpy(x86_cpu_def, def, sizeof(*def));
> -            /* sysenter isn't supported in compatibility mode on AMD,
> -             * syscall isn't supported in compatibility mode on Intel.
> -             * Normally we advertise the actual CPU vendor, but you can
> -             * override this using the 'vendor' property if you want to use
> -             * KVM's sysenter/syscall emulation in compatibility mode and
> -             * when doing cross vendor migration
> -             */
> -            if (kvm_enabled()) {
> -                uint32_t  ebx = 0, ecx = 0, edx = 0;
> -                host_cpuid(0, 0, NULL, &ebx, &ecx, &edx);
> -                x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx);
> -            }
> -            return 0;
> +    if (strcmp(name, "host") == 0) {
> +        if (kvm_enabled()) {
> +            return object_class_by_name(TYPE_HOST_X86_CPU);
>          }
> +        return NULL;
>      }
>  
> -    return -1;
> +    typename = g_strdup_printf("%s-" TYPE_X86_CPU, name);
> +    oc = object_class_by_name(typename);
> +    g_free(typename);
> +    if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_X86_CPU) ||
> +                       object_class_is_abstract(oc))) {
> +        oc = NULL;
> +    }
> +    return oc;
>  }
>  
>  /* Parse "+feature,-feature,feature=foo" CPU feature string
> @@ -1516,57 +1407,13 @@ static void filter_features_for_kvm(X86CPU *cpu)
>  }
>  #endif
>  
> -static int cpu_x86_register(X86CPU *cpu, const char *name)
> -{
> -    CPUX86State *env = &cpu->env;
> -    x86_def_t def1, *def = &def1;
> -    Error *error = NULL;
> -
> -    memset(def, 0, sizeof(*def));
> -
> -    if (cpu_x86_find_by_name(def, name) < 0) {
> -        error_setg(&error, "Unable to find CPU definition: %s", name);
> -        goto out;
> -    }
> -
> -    if (kvm_enabled()) {
> -        def->kvm_features |= kvm_default_features;
> -    }
> -    def->ext_features |= CPUID_EXT_HYPERVISOR;
> -
> -    object_property_set_str(OBJECT(cpu), def->vendor, "vendor", &error);
> -    object_property_set_int(OBJECT(cpu), def->level, "level", &error);
> -    object_property_set_int(OBJECT(cpu), def->family, "family", &error);
> -    object_property_set_int(OBJECT(cpu), def->model, "model", &error);
> -    object_property_set_int(OBJECT(cpu), def->stepping, "stepping", &error);
> -    env->cpuid_features = def->features;
> -    env->cpuid_ext_features = def->ext_features;
> -    env->cpuid_ext2_features = def->ext2_features;
> -    env->cpuid_ext3_features = def->ext3_features;
> -    object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", &error);
> -    env->cpuid_kvm_features = def->kvm_features;
> -    env->cpuid_svm_features = def->svm_features;
> -    env->cpuid_ext4_features = def->ext4_features;
> -    env->cpuid_7_0_ebx_features = def->cpuid_7_0_ebx_features;
> -    env->cpuid_xlevel2 = def->xlevel2;
> -
> -    object_property_set_str(OBJECT(cpu), def->model_id, "model-id", &error);
> -
> -out:
> -    if (error) {
> -        fprintf(stderr, "%s\n", error_get_pretty(error));
> -        error_free(error);
> -        return -1;
> -    }
> -    return 0;
> -}
> -
>  X86CPU *cpu_x86_init(const char *cpu_model)
>  {
>      X86CPU *cpu = NULL;
>      CPUX86State *env;
>      gchar **model_pieces;
>      char *name, *features;
> +    ObjectClass *oc;
>      Error *error = NULL;
>  
>      model_pieces = g_strsplit(cpu_model, ",", 2);
> @@ -1577,14 +1424,14 @@ X86CPU *cpu_x86_init(const char *cpu_model)
>      name = model_pieces[0];
>      features = model_pieces[1];
>  
> -    cpu = X86_CPU(object_new(TYPE_X86_CPU));
> -    env = &cpu->env;
> -    env->cpu_model_str = cpu_model;
> -
> -    if (cpu_x86_register(cpu, name) < 0) {
> -        error_setg(&error, "Unable to register CPU: %s", name);
> +    oc = x86_cpu_class_by_name(name);
> +    if (oc == NULL) {
> +        error_setg(&error, "Unable to find CPU model: %s", name);
>          goto out;
>      }
> +    cpu = X86_CPU(object_new(object_class_get_name(oc)));
> +    env = &cpu->env;
> +    env->cpu_model_str = cpu_model;
>  
>      cpu_x86_parse_featurestr(cpu, features, &error);
>      if (error) {
> @@ -1618,30 +1465,6 @@ void cpu_clear_apic_feature(CPUX86State *env)
>  
>  #endif /* !CONFIG_USER_ONLY */
>  
> -/* Initialize list of CPU models, filling some non-static fields if necessary
> - */
> -void x86_cpudef_setup(void)
> -{
> -    int i, j;
> -    static const char *model_with_versions[] = { "qemu32", "qemu64", "athlon" };
> -
> -    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); ++i) {
> -        x86_def_t *def = &builtin_x86_defs[i];
> -
> -        /* Look for specific "cpudef" models that */
> -        /* have the QEMU version in .model_id */
> -        for (j = 0; j < ARRAY_SIZE(model_with_versions); j++) {
> -            if (strcmp(model_with_versions[j], def->name) == 0) {
> -                pstrcpy(def->model_id, sizeof(def->model_id),
> -                        "QEMU Virtual CPU version ");
> -                pstrcat(def->model_id, sizeof(def->model_id),
> -                        qemu_get_version());
> -                break;
> -            }
> -        }
> -    }
> -}
> -
>  static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx,
>                               uint32_t *ecx, uint32_t *edx)
>  {
> @@ -2195,6 +2018,8 @@ static void x86_cpu_initfn(Object *obj)
>      CPUState *cs = CPU(obj);
>      X86CPU *cpu = X86_CPU(obj);
>      CPUX86State *env = &cpu->env;
> +    X86CPUClass *xcc = X86_CPU_GET_CLASS(obj);
> +    const x86_def_t *def = &xcc->info;
>      static int inited;
>  
>      cpu_exec_init(env);
> @@ -2224,6 +2049,28 @@ static void x86_cpu_initfn(Object *obj)
>                          x86_cpuid_get_tsc_freq,
>                          x86_cpuid_set_tsc_freq, NULL, NULL, NULL);
>  
> +    object_property_set_str(OBJECT(cpu), def->vendor, "vendor", NULL);
> +    object_property_set_int(OBJECT(cpu), def->level, "level", NULL);
> +    object_property_set_int(OBJECT(cpu), def->family, "family", NULL);
> +    object_property_set_int(OBJECT(cpu), def->model, "model", NULL);
> +    object_property_set_int(OBJECT(cpu), def->stepping, "stepping", NULL);
> +    env->cpuid_features = def->features;
> +    env->cpuid_ext_features = def->ext_features;
> +    env->cpuid_ext_features |= CPUID_EXT_HYPERVISOR;
> +    env->cpuid_ext2_features = def->ext2_features;
> +    env->cpuid_ext3_features = def->ext3_features;
> +    object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", NULL);
> +    env->cpuid_kvm_features = def->kvm_features;
> +    if (kvm_enabled()) {
> +        env->cpuid_kvm_features |= kvm_default_features;
> +    }

"-cpu host,enforce" is supposed to never fail. What if the host doesn't
support some of the features present in kvm_default_features? We need to
use kvm_default_features only if the CPU model is not "host".

But this is an existing bug in the code, you are not introducing it with
this patch.


> +    env->cpuid_svm_features = def->svm_features;
> +    env->cpuid_ext4_features = def->ext4_features;
> +    env->cpuid_7_0_ebx_features = def->cpuid_7_0_ebx_features;
> +    env->cpuid_xlevel2 = def->xlevel2;
> +
> +    object_property_set_str(OBJECT(cpu), def->model_id, "model-id", NULL);
> +
>      env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index);
>  
>      /* init various static tables used in TCG mode */
> @@ -2236,6 +2083,44 @@ static void x86_cpu_initfn(Object *obj)
>      }
>  }
>  
> +static void x86_cpu_def_class_init(ObjectClass *oc, void *data)
> +{
> +    X86CPUClass *xcc = X86_CPU_CLASS(oc);
> +    ObjectClass *hoc = object_class_by_name(TYPE_HOST_X86_CPU);
> +    X86CPUClass *hostcc;
> +    x86_def_t *def = data;
> +    int i;
> +    static const char *versioned_models[] = { "qemu32", "qemu64", "athlon" };
> +
> +    memcpy(&xcc->info, def, sizeof(x86_def_t));
> +
> +    /* host cpu class is available if KVM is enabled,
> +     * get kvm overrides from it */
> +    if (hoc) {
> +        hostcc = X86_CPU_CLASS(hoc);
> +        /* sysenter isn't supported in compatibility mode on AMD,
> +         * syscall isn't supported in compatibility mode on Intel.
> +         * Normally we advertise the actual CPU vendor, but you can
> +         * override this using the 'vendor' property if you want to use
> +         * KVM's sysenter/syscall emulation in compatibility mode and
> +         * when doing cross vendor migration
> +         */
> +        memcpy(xcc->info.vendor, hostcc->info.vendor,
> +               sizeof(xcc->info.vendor));
> +    }

Again, we have the same problem we had before, but now in the non-host
classes. What if class_init is called before KVM is initialized? I
believe we will be forced to move this hack to the instance init
function.

If we still want the default vendor to be a static property in the
class, we can do that if we set the default in a "tcg-vendor" property
instead of a "vendor" property (that would be empty/unset by default),
and x86_cpu_initfn() could do this:

    vendor = object_property_get_str(cpu, "vendor");
    tcg_vendor = object_property_get_str(cpu, "tcg-vendor");
    if (vendor && vendor[0]) {
      cpu->cpuid_vendor = vendor;
    } else if (kvm_enabled()) {
      cpu->cpuid_vendor = get_host_vendor();
    } else {
      cpu->cpuid_vendor = tcg_vendor;
    }


> +
> +    /* Look for specific models that have the QEMU version in .model_id */
> +    for (i = 0; i < ARRAY_SIZE(versioned_models); i++) {
> +        if (strcmp(versioned_models[i], def->name) == 0) {
> +            pstrcpy(xcc->info.model_id, sizeof(xcc->info.model_id),
> +                    "QEMU Virtual CPU version ");
> +            pstrcat(xcc->info.model_id, sizeof(xcc->info.model_id),
> +                    qemu_get_version());
> +            break;
> +        }
> +    }
> +}
> +
>  static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
>  {
>      X86CPUClass *xcc = X86_CPU_CLASS(oc);
> @@ -2247,6 +2132,21 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
>  
>      xcc->parent_reset = cc->reset;
>      cc->reset = x86_cpu_reset;
> +
> +    cc->class_by_name = x86_cpu_class_by_name;
> +}
> +
> +static void x86_register_cpu_type(const x86_def_t *def)
> +{
> +    TypeInfo type_info = {
> +        .parent = TYPE_X86_CPU,
> +        .class_init = x86_cpu_def_class_init,
> +        .class_data = (void *)def,
> +    };
> +
> +    type_info.name = g_strdup_printf("%s-" TYPE_X86_CPU, def->name);
> +    type_register(&type_info);
> +    g_free((void *)type_info.name);
>  }
>  
>  static const TypeInfo x86_cpu_type_info = {
> @@ -2254,14 +2154,19 @@ static const TypeInfo x86_cpu_type_info = {
>      .parent = TYPE_CPU,
>      .instance_size = sizeof(X86CPU),
>      .instance_init = x86_cpu_initfn,
> -    .abstract = false,
> +    .abstract = true,
>      .class_size = sizeof(X86CPUClass),
>      .class_init = x86_cpu_common_class_init,
>  };
>  
>  static void x86_cpu_register_types(void)
>  {
> +    int i;
> +
>      type_register_static(&x86_cpu_type_info);
> +    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
> +        x86_register_cpu_type(&builtin_x86_defs[i]);
> +    }
>  }
>  
>  type_init(x86_cpu_register_types)
> diff --git a/target-i386/cpu.h b/target-i386/cpu.h
> index 7577e4f..11ef942 100644
> --- a/target-i386/cpu.h
> +++ b/target-i386/cpu.h
> @@ -887,7 +887,6 @@ typedef struct CPUX86State {
>  X86CPU *cpu_x86_init(const char *cpu_model);
>  int cpu_x86_exec(CPUX86State *s);
>  void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf);
> -void x86_cpudef_setup(void);
>  int cpu_x86_support_mca_broadcast(CPUX86State *env);
>  
>  int cpu_get_pic_interrupt(CPUX86State *s);
> @@ -1079,7 +1078,6 @@ static inline CPUX86State *cpu_init(const char *cpu_model)
>  #define cpu_gen_code cpu_x86_gen_code
>  #define cpu_signal_handler cpu_x86_signal_handler
>  #define cpu_list x86_cpu_list
> -#define cpudef_setup	x86_cpudef_setup
>  
>  #define CPU_SAVE_VERSION 12
>  
> @@ -1256,4 +1254,7 @@ const char *get_register_name_32(unsigned int reg);
>  uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index);
>  void enable_compat_apic_id_mode(void);
>  
> +void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
> +                              uint32_t vendor2, uint32_t vendor3);
> +
>  #endif /* CPU_I386_H */
> diff --git a/target-i386/kvm.c b/target-i386/kvm.c
> index 9ebf181..dcaae76 100644
> --- a/target-i386/kvm.c
> +++ b/target-i386/kvm.c
> @@ -735,6 +735,69 @@ static int kvm_get_supported_msrs(KVMState *s)
>      return ret;
>  }
>  
> +
> +static void kvm_host_cpu_class_init(ObjectClass *oc, void *data)
> +{
> +    X86CPUClass *xcc = X86_CPU_CLASS(oc);
> +    x86_def_t *x86_cpu_def = &xcc->info;
> +    KVMState *s = kvm_state;
> +    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
> +    int i;
> +
> +    xcc->info.name = "host";
> +    host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
> +    x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx);
> +
> +    host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
> +    x86_cpu_def->family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF);
> +    x86_cpu_def->model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12);
> +    x86_cpu_def->stepping = eax & 0x0F;
> +
> +    x86_cpu_def->level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX);
> +    x86_cpu_def->features = kvm_arch_get_supported_cpuid(s, 0x1, 0, R_EDX);
> +    x86_cpu_def->ext_features = kvm_arch_get_supported_cpuid(s, 0x1, 0, R_ECX);
> +
> +    if (x86_cpu_def->level >= 7) {
> +        x86_cpu_def->cpuid_7_0_ebx_features =
> +                    kvm_arch_get_supported_cpuid(s, 0x7, 0, R_EBX);
> +    } else {
> +        x86_cpu_def->cpuid_7_0_ebx_features = 0;
> +    }
> +
> +    x86_cpu_def->xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX);
> +    x86_cpu_def->ext2_features =
> +                kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX);
> +    x86_cpu_def->ext3_features =
> +                kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX);
> +
> +    for (i = 0; i < 3; i++) {
> +        host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx);
> +        memcpy(xcc->info.model_id + i * 16 +  0, &eax, 4);
> +        memcpy(xcc->info.model_id + i * 16 +  4, &ebx, 4);
> +        memcpy(xcc->info.model_id + i * 16 +  8, &ecx, 4);
> +        memcpy(xcc->info.model_id + i * 16 + 12, &edx, 4);
> +    }
> +
> +    /* Call Centaur's CPUID instruction. */
> +    if (!strcmp(x86_cpu_def->vendor, CPUID_VENDOR_VIA)) {
> +        host_cpuid(0xC0000000, 0, &eax, &ebx, &ecx, &edx);
> +        eax = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX);
> +        if (eax >= 0xC0000001) {
> +            /* Support VIA max extended level */
> +            x86_cpu_def->xlevel2 = eax;
> +            host_cpuid(0xC0000001, 0, &eax, &ebx, &ecx, &edx);
> +            x86_cpu_def->ext4_features =
> +                    kvm_arch_get_supported_cpuid(s, 0xC0000001, 0, R_EDX);
> +        }
> +    }
> +
> +    /* Other KVM-specific feature fields: */
> +    x86_cpu_def->svm_features =
> +        kvm_arch_get_supported_cpuid(s, 0x8000000A, 0, R_EDX);
> +    x86_cpu_def->kvm_features =
> +        kvm_arch_get_supported_cpuid(s, KVM_CPUID_FEATURES, 0, R_EAX);
> +}
> +
>  int kvm_arch_init(KVMState *s)
>  {
>      QemuOptsList *list = qemu_find_opts("machine");
> @@ -743,6 +806,12 @@ int kvm_arch_init(KVMState *s)
>      int ret;
>      struct utsname utsname;
>  
> +    static const TypeInfo host_x86_cpu_type_info = {
> +        .name = TYPE_HOST_X86_CPU,
> +        .parent = TYPE_X86_CPU,
> +        .class_init = kvm_host_cpu_class_init,
> +    };
> +
>      ret = kvm_get_supported_msrs(s);
>      if (ret < 0) {
>          return ret;
> @@ -797,6 +866,9 @@ int kvm_arch_init(KVMState *s)
>              }
>          }
>      }
> +
> +    type_register(&host_x86_cpu_type_info);

Are we really allowed to register QOM classes that late?

If QOM design allows us to register the class very late (I would like to
confirm that), this approach sounds really clean and sane to me.
Pre-KVM-init class introspection of the "host" class would be completely
useless anyway (because all data in the "host" class depend on data
available only post-KVM-init anyway).

> +
>      return 0;
>  }
>  
> -- 
> 1.7.1
>
Igor Mammedov Feb. 8, 2013, 9:03 a.m. UTC | #2
On Thu, 7 Feb 2013 13:08:19 -0200
Eduardo Habkost <ehabkost@redhat.com> wrote:

> On Tue, Feb 05, 2013 at 05:39:22PM +0100, Igor Mammedov wrote:
> > From: Andreas Färber <afaerber@suse.de>
> > 
> > Move x86_def_t definition to header and embed into X86CPUClass.
> > Register types per built-in model definition.
> > 
> > Move version initialization from x86_cpudef_setup() to class_init.
> > 
> > Inline cpu_x86_register() into the X86CPU initfn.
> > Since instance_init cannot reports errors, drop error handling.
> > 
> > Replace cpu_x86_find_by_name() with x86_cpu_class_by_name().
> > Move handling of KVM host vendor override from cpu_x86_find_by_name()
> > to the kvm_arch_init() and class_init(). Use TYPE_X86_CPU class to
> > communicate kvm specific defaults to other sub-classes.
> > 
> > Register host-{i386,x86_64}-cpu type from KVM code to avoid #ifdefs
> > and only when KVM is enabled to avoid hacks in CPU code.
> > Make kvm_cpu_fill_host() into a host specific class_init and inline
> > cpu_x86_fill_model_id().
> > 
> > Let kvm_check_features_against_host() obtain host-{i386,86_64}-cpu for
> > comparison.
> > 
> > Signed-off-by: Andreas Färber <afaerber@suse.de>
> > Signed-off-by: Igor Mammedov <imammedo@redhat.com>
> > ---
> > v4:
> >   * set error if cpu model is not found and goto out;
> >   * copy vendor override from 'host' CPU class in sub-class'es
> >     class_init() if 'host' CPU class is available.
> >   * register type TYPE_HOST_X86_CPU in kvm_arch_init(), this type
> >     should be available only in KVM mode and we haven't printed it in
> >     -cpu ? output so far, so we can continue doing so. It's not
> >     really confusing to show 'host' cpu (even if we do it) when KVM
> >     is not enabled.
> >   * remove special case for 'host' CPU check in x86_cpu_class_by_name(),
> >     due to 'host' CPU will not find anything if not in KVM mode or
> >     return 'host' CPU class in KVM mode, i.e. treat it as regular CPUs.
> > ---
> >  target-i386/cpu-qom.h |   24 ++++
> >  target-i386/cpu.c     |  331 ++++++++++++++++++-------------------------------
> >  target-i386/cpu.h     |    5 +-
> >  target-i386/kvm.c     |   72 +++++++++++
> >  4 files changed, 217 insertions(+), 215 deletions(-)
> > 
> > diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
> > index 48e6b54..80bf72d 100644
> > --- a/target-i386/cpu-qom.h
> > +++ b/target-i386/cpu-qom.h
> > @@ -30,6 +30,27 @@
> >  #define TYPE_X86_CPU "i386-cpu"
> >  #endif
> >  
> > +#define TYPE_HOST_X86_CPU "host-" TYPE_X86_CPU
> 
> Can we introduce a X86_CPU_CLASS_NAME() macro to help us make the rules
> to generate CPU class names clearer?
> 
> e.g.:
> 
> #define X86_CPU_CLASS_NAME(s) s "-" TYPE_X86_CPU
> [...]
> #define TYPE_HOST_X86_CPU X86_CPU_CLASS_NAME("host")
> [...]
>   /* (at the class lookup code) */
>   typename = g_strdup_printf(X86_CPU_CLASS_NAME("%s"), name);
I kind of like Andreas' variant not hiding format string in macro, for
one doesn't have look-up what macro does to see how name will look.
Especially since it's called in only 2 places.

> 
> 
> 
> > +
> > +typedef struct x86_def_t {
> > +    const char *name;
> > +    uint32_t level;
> > +    /* vendor is zero-terminated, 12 character ASCII string */
> > +    char vendor[CPUID_VENDOR_SZ + 1];
> > +    int family;
> > +    int model;
> > +    int stepping;
> > +    uint32_t features, ext_features, ext2_features, ext3_features;
> > +    uint32_t kvm_features, svm_features;
> > +    uint32_t xlevel;
> > +    char model_id[48];
> > +    /* Store the results of Centaur's CPUID instructions */
> > +    uint32_t ext4_features;
> > +    uint32_t xlevel2;
> > +    /* The feature bits on CPUID[EAX=7,ECX=0].EBX */
> > +    uint32_t cpuid_7_0_ebx_features;
> > +} x86_def_t;
> > +
> >  #define X86_CPU_CLASS(klass) \
> >      OBJECT_CLASS_CHECK(X86CPUClass, (klass), TYPE_X86_CPU)
> >  #define X86_CPU(obj) \
> > @@ -41,6 +62,7 @@
> >   * X86CPUClass:
> >   * @parent_realize: The parent class' realize handler.
> >   * @parent_reset: The parent class' reset handler.
> > + * @info: Model-specific data.
> >   *
> >   * An x86 CPU model or family.
> >   */
> > @@ -51,6 +73,8 @@ typedef struct X86CPUClass {
> >  
> >      DeviceRealize parent_realize;
> >      void (*parent_reset)(CPUState *cpu);
> > +
> > +    x86_def_t info;
> 
> I thought you had suggesting making it a pointer. If you made it a
> pointer, you wouldn't need to move the x86_def_t declaration to
> cpu-qom.h.

x86_def_t is needed in kvm.c for host class. So there is no much point
in changing info into pointer, considering it's temporary solution.


> 
> >  } X86CPUClass;
> >  
> >  /**
> > diff --git a/target-i386/cpu.c b/target-i386/cpu.c
> > index 1aee097..62fdc84 100644
> > --- a/target-i386/cpu.c
> > +++ b/target-i386/cpu.c
> > @@ -47,8 +47,8 @@
[...]

> > @@ -2195,6 +2018,8 @@ static void x86_cpu_initfn(Object *obj)
> >      CPUState *cs = CPU(obj);
> >      X86CPU *cpu = X86_CPU(obj);
> >      CPUX86State *env = &cpu->env;
> > +    X86CPUClass *xcc = X86_CPU_GET_CLASS(obj);
> > +    const x86_def_t *def = &xcc->info;
> >      static int inited;
> >  
> >      cpu_exec_init(env);
> > @@ -2224,6 +2049,28 @@ static void x86_cpu_initfn(Object *obj)
> >                          x86_cpuid_get_tsc_freq,
> >                          x86_cpuid_set_tsc_freq, NULL, NULL, NULL);
> >  
> > +    object_property_set_str(OBJECT(cpu), def->vendor, "vendor", NULL);
> > +    object_property_set_int(OBJECT(cpu), def->level, "level", NULL);
> > +    object_property_set_int(OBJECT(cpu), def->family, "family", NULL);
> > +    object_property_set_int(OBJECT(cpu), def->model, "model", NULL);
> > +    object_property_set_int(OBJECT(cpu), def->stepping, "stepping", NULL);
> > +    env->cpuid_features = def->features;
> > +    env->cpuid_ext_features = def->ext_features;
> > +    env->cpuid_ext_features |= CPUID_EXT_HYPERVISOR;
> > +    env->cpuid_ext2_features = def->ext2_features;
> > +    env->cpuid_ext3_features = def->ext3_features;
> > +    object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", NULL);
> > +    env->cpuid_kvm_features = def->kvm_features;
> > +    if (kvm_enabled()) {
> > +        env->cpuid_kvm_features |= kvm_default_features;
> > +    }
> 
> "-cpu host,enforce" is supposed to never fail. What if the host doesn't
> support some of the features present in kvm_default_features? We need to
> use kvm_default_features only if the CPU model is not "host".
> 
> But this is an existing bug in the code, you are not introducing it with
> this patch.
> 
whould be moving it in x86_cpu_def_class_init() suitable solution?

> 
> > +    env->cpuid_svm_features = def->svm_features;
> > +    env->cpuid_ext4_features = def->ext4_features;
> > +    env->cpuid_7_0_ebx_features = def->cpuid_7_0_ebx_features;
> > +    env->cpuid_xlevel2 = def->xlevel2;
> > +
> > +    object_property_set_str(OBJECT(cpu), def->model_id, "model-id", NULL);
> > +
> >      env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index);
> >  
> >      /* init various static tables used in TCG mode */
> > @@ -2236,6 +2083,44 @@ static void x86_cpu_initfn(Object *obj)
> >      }
> >  }
> >  
> > +static void x86_cpu_def_class_init(ObjectClass *oc, void *data)
> > +{
> > +    X86CPUClass *xcc = X86_CPU_CLASS(oc);
> > +    ObjectClass *hoc = object_class_by_name(TYPE_HOST_X86_CPU);
> > +    X86CPUClass *hostcc;
> > +    x86_def_t *def = data;
> > +    int i;
> > +    static const char *versioned_models[] = { "qemu32", "qemu64", "athlon" };
> > +
> > +    memcpy(&xcc->info, def, sizeof(x86_def_t));
> > +
> > +    /* host cpu class is available if KVM is enabled,
> > +     * get kvm overrides from it */
> > +    if (hoc) {
> > +        hostcc = X86_CPU_CLASS(hoc);
> > +        /* sysenter isn't supported in compatibility mode on AMD,
> > +         * syscall isn't supported in compatibility mode on Intel.
> > +         * Normally we advertise the actual CPU vendor, but you can
> > +         * override this using the 'vendor' property if you want to use
> > +         * KVM's sysenter/syscall emulation in compatibility mode and
> > +         * when doing cross vendor migration
> > +         */
> > +        memcpy(xcc->info.vendor, hostcc->info.vendor,
> > +               sizeof(xcc->info.vendor));
> > +    }
> 
> Again, we have the same problem we had before, but now in the non-host
> classes. What if class_init is called before KVM is initialized? I
> believe we will be forced to move this hack to the instance init
> function.
I believe, the in the case where non-host CPU classes might be initialized
before KVM "-cpu ?" we do not care what their defaults are, since we only
would use class names there and then exit.

For case where classes could be inspected over QMP, OQM, KVM would be already
initialized if enabled and we would get proper initialization order without
hack.

Instead of adding hack, I'd rather enforce valid initialization order and
abort in initfn() if type was initialized without KVM present and KVM is
enabled at initfn() time. Something along the lines:

static x86_cpu_builtin_class_initialized_without_kvm;

x86_cpu_def_class_init() {
    ...
    if (!kvm_enabled() && !x86_cpu_builtin_class_initialized_without_kvm) {
        x86_cpu_builtin_class_initialized_without_kvm = true;
    }
    ...
}

initfn() {
    ...
    if (kvm_enabled() && x86_cpu_builtin_class_initialized_without_kvm) {
        abort();
    }
    ...
}

> If we still want the default vendor to be a static property in the
> class, we can do that if we set the default in a "tcg-vendor" property
> instead of a "vendor" property (that would be empty/unset by default),
> and x86_cpu_initfn() could do this:
> 
>     vendor = object_property_get_str(cpu, "vendor");
>     tcg_vendor = object_property_get_str(cpu, "tcg-vendor");
>     if (vendor && vendor[0]) {
>       cpu->cpuid_vendor = vendor;
>     } else if (kvm_enabled()) {
>       cpu->cpuid_vendor = get_host_vendor();
>     } else {
>       cpu->cpuid_vendor = tcg_vendor;
>     }
> 
> > +
> > +    /* Look for specific models that have the QEMU version in .model_id */
> > +    for (i = 0; i < ARRAY_SIZE(versioned_models); i++) {
> > +        if (strcmp(versioned_models[i], def->name) == 0) {
> > +            pstrcpy(xcc->info.model_id, sizeof(xcc->info.model_id),
> > +                    "QEMU Virtual CPU version ");
> > +            pstrcat(xcc->info.model_id, sizeof(xcc->info.model_id),
> > +                    qemu_get_version());
> > +            break;
> > +        }
> > +    }
> > +}
> > +
[...]

> > --- a/target-i386/kvm.c
> > +++ b/target-i386/kvm.c
> > @@ -735,6 +735,69 @@ static int kvm_get_supported_msrs(KVMState *s)
> >      return ret;
> >  }
> >  
[...]

> >  int kvm_arch_init(KVMState *s)
> >  {
> >      QemuOptsList *list = qemu_find_opts("machine");
> > @@ -743,6 +806,12 @@ int kvm_arch_init(KVMState *s)
> >      int ret;
> >      struct utsname utsname;
> >  
> > +    static const TypeInfo host_x86_cpu_type_info = {
> > +        .name = TYPE_HOST_X86_CPU,
> > +        .parent = TYPE_X86_CPU,
> > +        .class_init = kvm_host_cpu_class_init,
> > +    };
> > +
> >      ret = kvm_get_supported_msrs(s);
> >      if (ret < 0) {
> >          return ret;
> > @@ -797,6 +866,9 @@ int kvm_arch_init(KVMState *s)
> >              }
> >          }
> >      }
> > +
> > +    type_register(&host_x86_cpu_type_info);
> 
> Are we really allowed to register QOM classes that late?
> 
> If QOM design allows us to register the class very late (I would like to
> confirm that), this approach sounds really clean and sane to me.
> Pre-KVM-init class introspection of the "host" class would be completely
> useless anyway (because all data in the "host" class depend on data
> available only post-KVM-init anyway).

Looking at type_register() impl. it seems safe to do so + relying on QBL for
type_table_add() safety. So it's really design question for QOM experts.

Antnony, Paolo, Andreas
 what do you think?

> 
> > +
> >      return 0;
> >  }
> >  
> > -- 
> > 1.7.1
> > 
> 
> -- 
> Eduardo
diff mbox

Patch

diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h
index 48e6b54..80bf72d 100644
--- a/target-i386/cpu-qom.h
+++ b/target-i386/cpu-qom.h
@@ -30,6 +30,27 @@ 
 #define TYPE_X86_CPU "i386-cpu"
 #endif
 
+#define TYPE_HOST_X86_CPU "host-" TYPE_X86_CPU
+
+typedef struct x86_def_t {
+    const char *name;
+    uint32_t level;
+    /* vendor is zero-terminated, 12 character ASCII string */
+    char vendor[CPUID_VENDOR_SZ + 1];
+    int family;
+    int model;
+    int stepping;
+    uint32_t features, ext_features, ext2_features, ext3_features;
+    uint32_t kvm_features, svm_features;
+    uint32_t xlevel;
+    char model_id[48];
+    /* Store the results of Centaur's CPUID instructions */
+    uint32_t ext4_features;
+    uint32_t xlevel2;
+    /* The feature bits on CPUID[EAX=7,ECX=0].EBX */
+    uint32_t cpuid_7_0_ebx_features;
+} x86_def_t;
+
 #define X86_CPU_CLASS(klass) \
     OBJECT_CLASS_CHECK(X86CPUClass, (klass), TYPE_X86_CPU)
 #define X86_CPU(obj) \
@@ -41,6 +62,7 @@ 
  * X86CPUClass:
  * @parent_realize: The parent class' realize handler.
  * @parent_reset: The parent class' reset handler.
+ * @info: Model-specific data.
  *
  * An x86 CPU model or family.
  */
@@ -51,6 +73,8 @@  typedef struct X86CPUClass {
 
     DeviceRealize parent_realize;
     void (*parent_reset)(CPUState *cpu);
+
+    x86_def_t info;
 } X86CPUClass;
 
 /**
diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index 1aee097..62fdc84 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -47,8 +47,8 @@ 
 #include "hw/apic_internal.h"
 #endif
 
-static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
-                                     uint32_t vendor2, uint32_t vendor3)
+void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
+                              uint32_t vendor2, uint32_t vendor3)
 {
     int i;
     for (i = 0; i < 4; i++) {
@@ -346,25 +346,6 @@  static void add_flagname_to_bitmaps(const char *flagname,
     }
 }
 
-typedef struct x86_def_t {
-    const char *name;
-    uint32_t level;
-    /* vendor is zero-terminated, 12 character ASCII string */
-    char vendor[CPUID_VENDOR_SZ + 1];
-    int family;
-    int model;
-    int stepping;
-    uint32_t features, ext_features, ext2_features, ext3_features;
-    uint32_t kvm_features, svm_features;
-    uint32_t xlevel;
-    char model_id[48];
-    /* Store the results of Centaur's CPUID instructions */
-    uint32_t ext4_features;
-    uint32_t xlevel2;
-    /* The feature bits on CPUID[EAX=7,ECX=0].EBX */
-    uint32_t cpuid_7_0_ebx_features;
-} x86_def_t;
-
 #define I486_FEATURES (CPUID_FP87 | CPUID_VME | CPUID_PSE)
 #define PENTIUM_FEATURES (I486_FEATURES | CPUID_DE | CPUID_TSC | \
           CPUID_MSR | CPUID_MCE | CPUID_CX8 | CPUID_MMX | CPUID_APIC)
@@ -868,86 +849,6 @@  static x86_def_t builtin_x86_defs[] = {
     },
 };
 
-#ifdef CONFIG_KVM
-static int cpu_x86_fill_model_id(char *str)
-{
-    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
-    int i;
-
-    for (i = 0; i < 3; i++) {
-        host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx);
-        memcpy(str + i * 16 +  0, &eax, 4);
-        memcpy(str + i * 16 +  4, &ebx, 4);
-        memcpy(str + i * 16 +  8, &ecx, 4);
-        memcpy(str + i * 16 + 12, &edx, 4);
-    }
-    return 0;
-}
-#endif
-
-/* Fill a x86_def_t struct with information about the host CPU, and
- * the CPU features supported by the host hardware + host kernel
- *
- * This function may be called only if KVM is enabled.
- */
-static void kvm_cpu_fill_host(x86_def_t *x86_cpu_def)
-{
-#ifdef CONFIG_KVM
-    KVMState *s = kvm_state;
-    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
-
-    assert(kvm_enabled());
-
-    x86_cpu_def->name = "host";
-    host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
-    x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx);
-
-    host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
-    x86_cpu_def->family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF);
-    x86_cpu_def->model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12);
-    x86_cpu_def->stepping = eax & 0x0F;
-
-    x86_cpu_def->level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX);
-    x86_cpu_def->features = kvm_arch_get_supported_cpuid(s, 0x1, 0, R_EDX);
-    x86_cpu_def->ext_features = kvm_arch_get_supported_cpuid(s, 0x1, 0, R_ECX);
-
-    if (x86_cpu_def->level >= 7) {
-        x86_cpu_def->cpuid_7_0_ebx_features =
-                    kvm_arch_get_supported_cpuid(s, 0x7, 0, R_EBX);
-    } else {
-        x86_cpu_def->cpuid_7_0_ebx_features = 0;
-    }
-
-    x86_cpu_def->xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX);
-    x86_cpu_def->ext2_features =
-                kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX);
-    x86_cpu_def->ext3_features =
-                kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX);
-
-    cpu_x86_fill_model_id(x86_cpu_def->model_id);
-
-    /* Call Centaur's CPUID instruction. */
-    if (!strcmp(x86_cpu_def->vendor, CPUID_VENDOR_VIA)) {
-        host_cpuid(0xC0000000, 0, &eax, &ebx, &ecx, &edx);
-        eax = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX);
-        if (eax >= 0xC0000001) {
-            /* Support VIA max extended level */
-            x86_cpu_def->xlevel2 = eax;
-            host_cpuid(0xC0000001, 0, &eax, &ebx, &ecx, &edx);
-            x86_cpu_def->ext4_features =
-                    kvm_arch_get_supported_cpuid(s, 0xC0000001, 0, R_EDX);
-        }
-    }
-
-    /* Other KVM-specific feature fields: */
-    x86_cpu_def->svm_features =
-        kvm_arch_get_supported_cpuid(s, 0x8000000A, 0, R_EDX);
-    x86_cpu_def->kvm_features =
-        kvm_arch_get_supported_cpuid(s, KVM_CPUID_FEATURES, 0, R_EAX);
-
-#endif /* CONFIG_KVM */
-}
-
 static int unavailable_host_feature(FeatureWordInfo *f, uint32_t mask)
 {
     int i;
@@ -975,31 +876,31 @@  static int unavailable_host_feature(FeatureWordInfo *f, uint32_t mask)
 static int kvm_check_features_against_host(X86CPU *cpu)
 {
     CPUX86State *env = &cpu->env;
-    x86_def_t host_def;
+    ObjectClass *host_oc = object_class_by_name(TYPE_HOST_X86_CPU);
+    X86CPUClass *host_xcc = X86_CPU_CLASS(host_oc);
     uint32_t mask;
     int rv, i;
     struct model_features_t ft[] = {
-        {&env->cpuid_features, &host_def.features,
+        {&env->cpuid_features, &host_xcc->info.features,
             FEAT_1_EDX },
-        {&env->cpuid_ext_features, &host_def.ext_features,
+        {&env->cpuid_ext_features, &host_xcc->info.ext_features,
             FEAT_1_ECX },
-        {&env->cpuid_ext2_features, &host_def.ext2_features,
+        {&env->cpuid_ext2_features, &host_xcc->info.ext2_features,
             FEAT_8000_0001_EDX },
-        {&env->cpuid_ext3_features, &host_def.ext3_features,
+        {&env->cpuid_ext3_features, &host_xcc->info.ext3_features,
             FEAT_8000_0001_ECX },
-        {&env->cpuid_ext4_features, &host_def.ext4_features,
+        {&env->cpuid_ext4_features, &host_xcc->info.ext4_features,
             FEAT_C000_0001_EDX },
-        {&env->cpuid_7_0_ebx_features, &host_def.cpuid_7_0_ebx_features,
+        {&env->cpuid_7_0_ebx_features, &host_xcc->info.cpuid_7_0_ebx_features,
             FEAT_7_0_EBX },
-        {&env->cpuid_svm_features, &host_def.svm_features,
+        {&env->cpuid_svm_features, &host_xcc->info.svm_features,
             FEAT_SVM },
-        {&env->cpuid_kvm_features, &host_def.kvm_features,
+        {&env->cpuid_kvm_features, &host_xcc->info.kvm_features,
             FEAT_KVM },
     };
 
     assert(kvm_enabled());
 
-    kvm_cpu_fill_host(&host_def);
     for (rv = 0, i = 0; i < ARRAY_SIZE(ft); ++i) {
         FeatureWord w = ft[i].feat_word;
         FeatureWordInfo *wi = &feature_word_info[w];
@@ -1261,40 +1162,30 @@  static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque,
     cpu->env.tsc_khz = value / 1000;
 }
 
-static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *name)
+static ObjectClass *x86_cpu_class_by_name(const char *name)
 {
-    x86_def_t *def;
-    int i;
+    ObjectClass *oc;
+    char *typename;
 
     if (name == NULL) {
-        return -1;
-    }
-    if (kvm_enabled() && strcmp(name, "host") == 0) {
-        kvm_cpu_fill_host(x86_cpu_def);
-        return 0;
+        return NULL;
     }
 
-    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
-        def = &builtin_x86_defs[i];
-        if (strcmp(name, def->name) == 0) {
-            memcpy(x86_cpu_def, def, sizeof(*def));
-            /* sysenter isn't supported in compatibility mode on AMD,
-             * syscall isn't supported in compatibility mode on Intel.
-             * Normally we advertise the actual CPU vendor, but you can
-             * override this using the 'vendor' property if you want to use
-             * KVM's sysenter/syscall emulation in compatibility mode and
-             * when doing cross vendor migration
-             */
-            if (kvm_enabled()) {
-                uint32_t  ebx = 0, ecx = 0, edx = 0;
-                host_cpuid(0, 0, NULL, &ebx, &ecx, &edx);
-                x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx);
-            }
-            return 0;
+    if (strcmp(name, "host") == 0) {
+        if (kvm_enabled()) {
+            return object_class_by_name(TYPE_HOST_X86_CPU);
         }
+        return NULL;
     }
 
-    return -1;
+    typename = g_strdup_printf("%s-" TYPE_X86_CPU, name);
+    oc = object_class_by_name(typename);
+    g_free(typename);
+    if (oc != NULL && (!object_class_dynamic_cast(oc, TYPE_X86_CPU) ||
+                       object_class_is_abstract(oc))) {
+        oc = NULL;
+    }
+    return oc;
 }
 
 /* Parse "+feature,-feature,feature=foo" CPU feature string
@@ -1516,57 +1407,13 @@  static void filter_features_for_kvm(X86CPU *cpu)
 }
 #endif
 
-static int cpu_x86_register(X86CPU *cpu, const char *name)
-{
-    CPUX86State *env = &cpu->env;
-    x86_def_t def1, *def = &def1;
-    Error *error = NULL;
-
-    memset(def, 0, sizeof(*def));
-
-    if (cpu_x86_find_by_name(def, name) < 0) {
-        error_setg(&error, "Unable to find CPU definition: %s", name);
-        goto out;
-    }
-
-    if (kvm_enabled()) {
-        def->kvm_features |= kvm_default_features;
-    }
-    def->ext_features |= CPUID_EXT_HYPERVISOR;
-
-    object_property_set_str(OBJECT(cpu), def->vendor, "vendor", &error);
-    object_property_set_int(OBJECT(cpu), def->level, "level", &error);
-    object_property_set_int(OBJECT(cpu), def->family, "family", &error);
-    object_property_set_int(OBJECT(cpu), def->model, "model", &error);
-    object_property_set_int(OBJECT(cpu), def->stepping, "stepping", &error);
-    env->cpuid_features = def->features;
-    env->cpuid_ext_features = def->ext_features;
-    env->cpuid_ext2_features = def->ext2_features;
-    env->cpuid_ext3_features = def->ext3_features;
-    object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", &error);
-    env->cpuid_kvm_features = def->kvm_features;
-    env->cpuid_svm_features = def->svm_features;
-    env->cpuid_ext4_features = def->ext4_features;
-    env->cpuid_7_0_ebx_features = def->cpuid_7_0_ebx_features;
-    env->cpuid_xlevel2 = def->xlevel2;
-
-    object_property_set_str(OBJECT(cpu), def->model_id, "model-id", &error);
-
-out:
-    if (error) {
-        fprintf(stderr, "%s\n", error_get_pretty(error));
-        error_free(error);
-        return -1;
-    }
-    return 0;
-}
-
 X86CPU *cpu_x86_init(const char *cpu_model)
 {
     X86CPU *cpu = NULL;
     CPUX86State *env;
     gchar **model_pieces;
     char *name, *features;
+    ObjectClass *oc;
     Error *error = NULL;
 
     model_pieces = g_strsplit(cpu_model, ",", 2);
@@ -1577,14 +1424,14 @@  X86CPU *cpu_x86_init(const char *cpu_model)
     name = model_pieces[0];
     features = model_pieces[1];
 
-    cpu = X86_CPU(object_new(TYPE_X86_CPU));
-    env = &cpu->env;
-    env->cpu_model_str = cpu_model;
-
-    if (cpu_x86_register(cpu, name) < 0) {
-        error_setg(&error, "Unable to register CPU: %s", name);
+    oc = x86_cpu_class_by_name(name);
+    if (oc == NULL) {
+        error_setg(&error, "Unable to find CPU model: %s", name);
         goto out;
     }
+    cpu = X86_CPU(object_new(object_class_get_name(oc)));
+    env = &cpu->env;
+    env->cpu_model_str = cpu_model;
 
     cpu_x86_parse_featurestr(cpu, features, &error);
     if (error) {
@@ -1618,30 +1465,6 @@  void cpu_clear_apic_feature(CPUX86State *env)
 
 #endif /* !CONFIG_USER_ONLY */
 
-/* Initialize list of CPU models, filling some non-static fields if necessary
- */
-void x86_cpudef_setup(void)
-{
-    int i, j;
-    static const char *model_with_versions[] = { "qemu32", "qemu64", "athlon" };
-
-    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); ++i) {
-        x86_def_t *def = &builtin_x86_defs[i];
-
-        /* Look for specific "cpudef" models that */
-        /* have the QEMU version in .model_id */
-        for (j = 0; j < ARRAY_SIZE(model_with_versions); j++) {
-            if (strcmp(model_with_versions[j], def->name) == 0) {
-                pstrcpy(def->model_id, sizeof(def->model_id),
-                        "QEMU Virtual CPU version ");
-                pstrcat(def->model_id, sizeof(def->model_id),
-                        qemu_get_version());
-                break;
-            }
-        }
-    }
-}
-
 static void get_cpuid_vendor(CPUX86State *env, uint32_t *ebx,
                              uint32_t *ecx, uint32_t *edx)
 {
@@ -2195,6 +2018,8 @@  static void x86_cpu_initfn(Object *obj)
     CPUState *cs = CPU(obj);
     X86CPU *cpu = X86_CPU(obj);
     CPUX86State *env = &cpu->env;
+    X86CPUClass *xcc = X86_CPU_GET_CLASS(obj);
+    const x86_def_t *def = &xcc->info;
     static int inited;
 
     cpu_exec_init(env);
@@ -2224,6 +2049,28 @@  static void x86_cpu_initfn(Object *obj)
                         x86_cpuid_get_tsc_freq,
                         x86_cpuid_set_tsc_freq, NULL, NULL, NULL);
 
+    object_property_set_str(OBJECT(cpu), def->vendor, "vendor", NULL);
+    object_property_set_int(OBJECT(cpu), def->level, "level", NULL);
+    object_property_set_int(OBJECT(cpu), def->family, "family", NULL);
+    object_property_set_int(OBJECT(cpu), def->model, "model", NULL);
+    object_property_set_int(OBJECT(cpu), def->stepping, "stepping", NULL);
+    env->cpuid_features = def->features;
+    env->cpuid_ext_features = def->ext_features;
+    env->cpuid_ext_features |= CPUID_EXT_HYPERVISOR;
+    env->cpuid_ext2_features = def->ext2_features;
+    env->cpuid_ext3_features = def->ext3_features;
+    object_property_set_int(OBJECT(cpu), def->xlevel, "xlevel", NULL);
+    env->cpuid_kvm_features = def->kvm_features;
+    if (kvm_enabled()) {
+        env->cpuid_kvm_features |= kvm_default_features;
+    }
+    env->cpuid_svm_features = def->svm_features;
+    env->cpuid_ext4_features = def->ext4_features;
+    env->cpuid_7_0_ebx_features = def->cpuid_7_0_ebx_features;
+    env->cpuid_xlevel2 = def->xlevel2;
+
+    object_property_set_str(OBJECT(cpu), def->model_id, "model-id", NULL);
+
     env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index);
 
     /* init various static tables used in TCG mode */
@@ -2236,6 +2083,44 @@  static void x86_cpu_initfn(Object *obj)
     }
 }
 
+static void x86_cpu_def_class_init(ObjectClass *oc, void *data)
+{
+    X86CPUClass *xcc = X86_CPU_CLASS(oc);
+    ObjectClass *hoc = object_class_by_name(TYPE_HOST_X86_CPU);
+    X86CPUClass *hostcc;
+    x86_def_t *def = data;
+    int i;
+    static const char *versioned_models[] = { "qemu32", "qemu64", "athlon" };
+
+    memcpy(&xcc->info, def, sizeof(x86_def_t));
+
+    /* host cpu class is available if KVM is enabled,
+     * get kvm overrides from it */
+    if (hoc) {
+        hostcc = X86_CPU_CLASS(hoc);
+        /* sysenter isn't supported in compatibility mode on AMD,
+         * syscall isn't supported in compatibility mode on Intel.
+         * Normally we advertise the actual CPU vendor, but you can
+         * override this using the 'vendor' property if you want to use
+         * KVM's sysenter/syscall emulation in compatibility mode and
+         * when doing cross vendor migration
+         */
+        memcpy(xcc->info.vendor, hostcc->info.vendor,
+               sizeof(xcc->info.vendor));
+    }
+
+    /* Look for specific models that have the QEMU version in .model_id */
+    for (i = 0; i < ARRAY_SIZE(versioned_models); i++) {
+        if (strcmp(versioned_models[i], def->name) == 0) {
+            pstrcpy(xcc->info.model_id, sizeof(xcc->info.model_id),
+                    "QEMU Virtual CPU version ");
+            pstrcat(xcc->info.model_id, sizeof(xcc->info.model_id),
+                    qemu_get_version());
+            break;
+        }
+    }
+}
+
 static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
 {
     X86CPUClass *xcc = X86_CPU_CLASS(oc);
@@ -2247,6 +2132,21 @@  static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
 
     xcc->parent_reset = cc->reset;
     cc->reset = x86_cpu_reset;
+
+    cc->class_by_name = x86_cpu_class_by_name;
+}
+
+static void x86_register_cpu_type(const x86_def_t *def)
+{
+    TypeInfo type_info = {
+        .parent = TYPE_X86_CPU,
+        .class_init = x86_cpu_def_class_init,
+        .class_data = (void *)def,
+    };
+
+    type_info.name = g_strdup_printf("%s-" TYPE_X86_CPU, def->name);
+    type_register(&type_info);
+    g_free((void *)type_info.name);
 }
 
 static const TypeInfo x86_cpu_type_info = {
@@ -2254,14 +2154,19 @@  static const TypeInfo x86_cpu_type_info = {
     .parent = TYPE_CPU,
     .instance_size = sizeof(X86CPU),
     .instance_init = x86_cpu_initfn,
-    .abstract = false,
+    .abstract = true,
     .class_size = sizeof(X86CPUClass),
     .class_init = x86_cpu_common_class_init,
 };
 
 static void x86_cpu_register_types(void)
 {
+    int i;
+
     type_register_static(&x86_cpu_type_info);
+    for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
+        x86_register_cpu_type(&builtin_x86_defs[i]);
+    }
 }
 
 type_init(x86_cpu_register_types)
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index 7577e4f..11ef942 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -887,7 +887,6 @@  typedef struct CPUX86State {
 X86CPU *cpu_x86_init(const char *cpu_model);
 int cpu_x86_exec(CPUX86State *s);
 void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf);
-void x86_cpudef_setup(void);
 int cpu_x86_support_mca_broadcast(CPUX86State *env);
 
 int cpu_get_pic_interrupt(CPUX86State *s);
@@ -1079,7 +1078,6 @@  static inline CPUX86State *cpu_init(const char *cpu_model)
 #define cpu_gen_code cpu_x86_gen_code
 #define cpu_signal_handler cpu_x86_signal_handler
 #define cpu_list x86_cpu_list
-#define cpudef_setup	x86_cpudef_setup
 
 #define CPU_SAVE_VERSION 12
 
@@ -1256,4 +1254,7 @@  const char *get_register_name_32(unsigned int reg);
 uint32_t x86_cpu_apic_id_from_index(unsigned int cpu_index);
 void enable_compat_apic_id_mode(void);
 
+void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1,
+                              uint32_t vendor2, uint32_t vendor3);
+
 #endif /* CPU_I386_H */
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 9ebf181..dcaae76 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -735,6 +735,69 @@  static int kvm_get_supported_msrs(KVMState *s)
     return ret;
 }
 
+
+static void kvm_host_cpu_class_init(ObjectClass *oc, void *data)
+{
+    X86CPUClass *xcc = X86_CPU_CLASS(oc);
+    x86_def_t *x86_cpu_def = &xcc->info;
+    KVMState *s = kvm_state;
+    uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
+    int i;
+
+    xcc->info.name = "host";
+    host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx);
+    x86_cpu_vendor_words2str(x86_cpu_def->vendor, ebx, edx, ecx);
+
+    host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx);
+    x86_cpu_def->family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF);
+    x86_cpu_def->model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12);
+    x86_cpu_def->stepping = eax & 0x0F;
+
+    x86_cpu_def->level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX);
+    x86_cpu_def->features = kvm_arch_get_supported_cpuid(s, 0x1, 0, R_EDX);
+    x86_cpu_def->ext_features = kvm_arch_get_supported_cpuid(s, 0x1, 0, R_ECX);
+
+    if (x86_cpu_def->level >= 7) {
+        x86_cpu_def->cpuid_7_0_ebx_features =
+                    kvm_arch_get_supported_cpuid(s, 0x7, 0, R_EBX);
+    } else {
+        x86_cpu_def->cpuid_7_0_ebx_features = 0;
+    }
+
+    x86_cpu_def->xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX);
+    x86_cpu_def->ext2_features =
+                kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX);
+    x86_cpu_def->ext3_features =
+                kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX);
+
+    for (i = 0; i < 3; i++) {
+        host_cpuid(0x80000002 + i, 0, &eax, &ebx, &ecx, &edx);
+        memcpy(xcc->info.model_id + i * 16 +  0, &eax, 4);
+        memcpy(xcc->info.model_id + i * 16 +  4, &ebx, 4);
+        memcpy(xcc->info.model_id + i * 16 +  8, &ecx, 4);
+        memcpy(xcc->info.model_id + i * 16 + 12, &edx, 4);
+    }
+
+    /* Call Centaur's CPUID instruction. */
+    if (!strcmp(x86_cpu_def->vendor, CPUID_VENDOR_VIA)) {
+        host_cpuid(0xC0000000, 0, &eax, &ebx, &ecx, &edx);
+        eax = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX);
+        if (eax >= 0xC0000001) {
+            /* Support VIA max extended level */
+            x86_cpu_def->xlevel2 = eax;
+            host_cpuid(0xC0000001, 0, &eax, &ebx, &ecx, &edx);
+            x86_cpu_def->ext4_features =
+                    kvm_arch_get_supported_cpuid(s, 0xC0000001, 0, R_EDX);
+        }
+    }
+
+    /* Other KVM-specific feature fields: */
+    x86_cpu_def->svm_features =
+        kvm_arch_get_supported_cpuid(s, 0x8000000A, 0, R_EDX);
+    x86_cpu_def->kvm_features =
+        kvm_arch_get_supported_cpuid(s, KVM_CPUID_FEATURES, 0, R_EAX);
+}
+
 int kvm_arch_init(KVMState *s)
 {
     QemuOptsList *list = qemu_find_opts("machine");
@@ -743,6 +806,12 @@  int kvm_arch_init(KVMState *s)
     int ret;
     struct utsname utsname;
 
+    static const TypeInfo host_x86_cpu_type_info = {
+        .name = TYPE_HOST_X86_CPU,
+        .parent = TYPE_X86_CPU,
+        .class_init = kvm_host_cpu_class_init,
+    };
+
     ret = kvm_get_supported_msrs(s);
     if (ret < 0) {
         return ret;
@@ -797,6 +866,9 @@  int kvm_arch_init(KVMState *s)
             }
         }
     }
+
+    type_register(&host_x86_cpu_type_info);
+
     return 0;
 }