From patchwork Wed Feb 13 15:33:33 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Igor Mammedov X-Patchwork-Id: 220170 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 301052C0294 for ; Thu, 14 Feb 2013 02:37:59 +1100 (EST) Received: from localhost ([::1]:36028 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U5ePF-0003ue-4w for incoming@patchwork.ozlabs.org; Wed, 13 Feb 2013 10:37:57 -0500 Received: from eggs.gnu.org ([208.118.235.92]:33410) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U5eOv-0003sE-Uv for qemu-devel@nongnu.org; Wed, 13 Feb 2013 10:37:47 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1U5eOm-0003rD-5a for qemu-devel@nongnu.org; Wed, 13 Feb 2013 10:37:37 -0500 Received: from mx1.redhat.com ([209.132.183.28]:27800) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1U5eOl-0003qy-SI for qemu-devel@nongnu.org; Wed, 13 Feb 2013 10:37:28 -0500 Received: from int-mx10.intmail.prod.int.phx2.redhat.com (int-mx10.intmail.prod.int.phx2.redhat.com [10.5.11.23]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id r1DFbPKL009745 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 13 Feb 2013 10:37:25 -0500 Received: from dell-pet610-01.lab.eng.brq.redhat.com (dell-pet610-01.lab.eng.brq.redhat.com [10.34.42.20]) by int-mx10.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id r1DFbMmQ018748; Wed, 13 Feb 2013 10:37:23 -0500 From: Igor Mammedov To: qemu-devel@nongnu.org Date: Wed, 13 Feb 2013 16:33:33 +0100 Message-Id: <1360769613-22220-1-git-send-email-imammedo@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.68 on 10.5.11.23 X-MIME-Autoconverted: from 8bit to quoted-printable by mx1.redhat.com id r1DFbPKL009745 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 209.132.183.28 Cc: ehabkost@redhat.com, gleb@redhat.com, blauwirbel@gmail.com, anthony@codemonkey.ws, afaerber@suse.de, rth@twiddle.net Subject: [Qemu-devel] [RFC v5] target-i386: Slim conversion to X86CPU subclasses + KVM subclasses X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Andreas Färber Depends on http://lists.gnu.org/archive/html/qemu-devel/2013-02/msg00677.html 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(). Move default setting of CPUID_EXT_HYPERVISOR to class_init(). Move KVM specific built-in CPU defaults overrides in a kvm specific x86_cpu_kvm_def_class_init(). And select TCG vs KVM class of CPU to create at runtime in x86_cpu_class_by_name() when kvm_enable() is available. Inline cpu_x86_register() into the X86CPU initfn. Since instance_init cannot reports errors, die there if some of default values are incorrect, instead of ignoring errors. 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-kvm-{i386,x86_64}-cpu type from KVM code to avoid #ifdefs and only when KVM is enabled to avoid workarounds in name to class lookup code in x86_cpu_class_by_name(). 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-kvm-{i386,86_64}-cpu for comparison. Signed-off-by: Andreas Färber Signed-off-by: Igor Mammedov --- v5: * 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. * register KVM specific subclasses for built-in CPU models. * abort() in instance_init() if property setter fails to set default value. 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. --- target-i386/cpu-qom.h | 24 ++++ target-i386/cpu.c | 348 +++++++++++++++++++------------------------------ target-i386/cpu.h | 5 +- target-i386/kvm.c | 72 ++++++++++ 4 files changed, 232 insertions(+), 217 deletions(-) diff --git a/target-i386/cpu-qom.h b/target-i386/cpu-qom.h index 48e6b54..c8f320d 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-kvm-" 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..b786a57 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,27 @@ 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 (kvm_enabled()) { + typename = g_strdup_printf("%s-kvm-" TYPE_X86_CPU, name); + } else { + typename = g_strdup_printf("%s-" TYPE_X86_CPU, name); } - - return -1; + 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 +1404,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 +1421,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 +1462,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,7 +2015,10 @@ 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; + Error *error = NULL; cpu_exec_init(env); @@ -2224,6 +2047,24 @@ 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", &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); + env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index); /* init various static tables used in TCG mode */ @@ -2234,7 +2075,57 @@ static void x86_cpu_initfn(Object *obj) cpu_set_debug_excp_handler(breakpoint_handler); #endif } + + if (error) { + fprintf(stderr, "%s\n", error_get_pretty(error)); + error_free(error); + abort(); + } + +} + +static void x86_cpu_def_class_init(ObjectClass *oc, void *data) +{ + X86CPUClass *xcc = X86_CPU_CLASS(oc); + x86_def_t *def = data; + int i; + static const char *versioned_models[] = { "qemu32", "qemu64", "athlon" }; + + memcpy(&xcc->info, def, sizeof(x86_def_t)); + xcc->info.ext_features |= CPUID_EXT_HYPERVISOR; + + /* 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; + } + } +} + +#ifdef CONFIG_KVM +static void x86_cpu_kvm_def_class_init(ObjectClass *oc, void *data) +{ + uint32_t eax, ebx, ecx, edx; + X86CPUClass *xcc = X86_CPU_CLASS(oc); + + x86_cpu_def_class_init(oc, data); + /* 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 + */ + host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); + x86_cpu_vendor_words2str(xcc->info.vendor, ebx, edx, ecx); + + xcc->info.kvm_features |= kvm_default_features; } +#endif static void x86_cpu_common_class_init(ObjectClass *oc, void *data) { @@ -2247,6 +2138,28 @@ 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); + +#ifdef CONFIG_KVM + type_info.name = g_strdup_printf("%s-kvm-" TYPE_X86_CPU, def->name); + type_info.class_init = x86_cpu_kvm_def_class_init; + type_register(&type_info); + g_free((void *)type_info.name); +#endif } static const TypeInfo x86_cpu_type_info = { @@ -2254,14 +2167,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; }