diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 82c7c0a5fa427190686ba3bf1eed3f2a6d0a16bf..53bc968bd071e798381493d4f2d79cc5a9c9dcfe 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -432,13 +432,25 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_display = "std"; } -static void pc_i440fx_2_6_machine_options(MachineClass *m) +static void pc_i440fx_2_7_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } +DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", NULL, + pc_i440fx_2_7_machine_options); + + +static void pc_i440fx_2_6_machine_options(MachineClass *m) +{ + pc_i440fx_2_7_machine_options(m); + m->is_default = 0; + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_6); +} + DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL, pc_i440fx_2_6_machine_options); @@ -447,8 +459,6 @@ static void pc_i440fx_2_5_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_2_6_machine_options(m); - m->alias = NULL; - m->is_default = 0; pcmc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 31a6a5938393af65c7c048707deeacffd47525a9..e4b541f7b2ef3a98da43d6d518450a8ae87ebfd1 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -283,12 +283,22 @@ static void pc_q35_machine_options(MachineClass *m) m->no_floppy = 1; } -static void pc_q35_2_6_machine_options(MachineClass *m) +static void pc_q35_2_7_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } +DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL, + pc_q35_2_7_machine_options); + +static void pc_q35_2_6_machine_options(MachineClass *m) +{ + pc_q35_2_7_machine_options(m); + m->alias = NULL; + SET_MACHINE_COMPAT(m, PC_COMPAT_2_6); +} + DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL, pc_q35_2_6_machine_options); @@ -296,7 +306,6 @@ static void pc_q35_2_5_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_2_6_machine_options(m); - m->alias = NULL; pcmc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 9ca23098bdb0ff111bbfe9dfef5ff93566a14dde..49566c89d42820d060e98e436a8ca03b9519e5aa 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -356,7 +356,16 @@ int e820_add_entry(uint64_t, uint64_t, uint32_t); int e820_get_num_entries(void); bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *); +#define PC_COMPAT_2_6 \ + HW_COMPAT_2_6 \ + {\ + .driver = TYPE_X86_CPU,\ + .property = "cpuid-0xb",\ + .value = "off",\ + }, + #define PC_COMPAT_2_5 \ + PC_COMPAT_2_6 \ HW_COMPAT_2_5 /* Helper for setting model-id for CPU models that changed model-id diff --git a/target-i386/cpu.c b/target-i386/cpu.c index 895a386d3b7a94e363ca1bb98821d3251e70c0e0..3665fecb5f33694d6fefd5f9f1d7f124d41db2c9 100644 --- a/target-i386/cpu.c +++ b/target-i386/cpu.c @@ -41,6 +41,7 @@ #include "sysemu/sysemu.h" #include "hw/qdev-properties.h" +#include "hw/i386/topology.h" #ifndef CONFIG_USER_ONLY #include "exec/address-spaces.h" #include "hw/hw.h" @@ -677,6 +678,14 @@ static ObjectClass *x86_cpu_class_by_name(const char *cpu_model) return oc; } +static char *x86_cpu_class_get_model_name(X86CPUClass *cc) +{ + const char *class_name = object_class_get_name(OBJECT_CLASS(cc)); + assert(g_str_has_suffix(class_name, X86_CPU_TYPE_SUFFIX)); + return g_strndup(class_name, + strlen(class_name) - strlen(X86_CPU_TYPE_SUFFIX)); +} + struct X86CPUDefinition { const char *name; uint32_t level; @@ -1238,6 +1247,51 @@ static X86CPUDefinition builtin_x86_defs[] = { .xlevel = 0x80000008, .model_id = "Intel Core Processor (Broadwell)", }, + { + .name = "Skylake-Client", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 94, + .stepping = 3, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | + CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | + CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | + CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX, + /* Missing: XSAVES (not supported by some Linux versions, + * including v4.1 to v4.6). + * KVM doesn't yet expose any XSAVES state save component, + * and the only one defined in Skylake (processor tracing) + * probably will block migration anyway. + */ + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .xlevel = 0x80000008, + .model_id = "Intel Core Processor (Skylake)", + }, { .name = "Opteron_G1", .level = 5, @@ -1501,16 +1555,17 @@ static void host_x86_cpu_initfn(Object *obj) CPUX86State *env = &cpu->env; KVMState *s = kvm_state; - assert(kvm_enabled()); - /* We can't fill the features array here because we don't know yet if * "migratable" is true or false. */ cpu->host_features = true; - env->cpuid_level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); - env->cpuid_xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX); - env->cpuid_xlevel2 = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); + /* If KVM is disabled, x86_cpu_realizefn() will report an error later */ + if (kvm_enabled()) { + env->cpuid_level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); + env->cpuid_xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX); + env->cpuid_xlevel2 = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); + } object_property_set_bool(OBJECT(cpu), true, "pmu", &error_abort); } @@ -1894,6 +1949,14 @@ static inline void feat2prop(char *s) } } +/* Compatibily hack to maintain legacy +-feat semantic, + * where +-feat overwrites any feature set by + * feat=on|feat even if the later is parsed after +-feat + * (i.e. "-x2apic,x2apic=on" will result in x2apic disabled) + */ +static FeatureWordArray plus_features = { 0 }; +static FeatureWordArray minus_features = { 0 }; + /* Parse "+feature,-feature,feature=foo" CPU feature string */ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, @@ -1901,97 +1964,61 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, { X86CPU *cpu = X86_CPU(cs); char *featurestr; /* Single 'key=value" string being parsed */ - FeatureWord w; - /* Features to be added */ - FeatureWordArray plus_features = { 0 }; - /* Features to be removed */ - FeatureWordArray minus_features = { 0 }; - uint32_t numvalue; - CPUX86State *env = &cpu->env; Error *local_err = NULL; - featurestr = features ? strtok(features, ",") : NULL; + if (!features) { + return; + } - while (featurestr) { - char *val; + for (featurestr = strtok(features, ","); + featurestr && !local_err; + featurestr = strtok(NULL, ",")) { + const char *name; + const char *val = NULL; + char *eq = NULL; + + /* Compatibility syntax: */ if (featurestr[0] == '+') { add_flagname_to_bitmaps(featurestr + 1, plus_features, &local_err); + continue; } else if (featurestr[0] == '-') { add_flagname_to_bitmaps(featurestr + 1, minus_features, &local_err); - } else if ((val = strchr(featurestr, '='))) { - *val = 0; val++; - feat2prop(featurestr); - if (!strcmp(featurestr, "xlevel")) { - char *err; - char num[32]; - - numvalue = strtoul(val, &err, 0); - if (!*val || *err) { - error_setg(errp, "bad numerical value %s", val); - return; - } - if (numvalue < 0x80000000) { - error_report("xlevel value shall always be >= 0x80000000" - ", fixup will be removed in future versions"); - numvalue += 0x80000000; - } - snprintf(num, sizeof(num), "%" PRIu32, numvalue); - object_property_parse(OBJECT(cpu), num, featurestr, &local_err); - } else if (!strcmp(featurestr, "tsc-freq")) { - int64_t tsc_freq; - char *err; - char num[32]; - - tsc_freq = qemu_strtosz_suffix_unit(val, &err, - QEMU_STRTOSZ_DEFSUFFIX_B, 1000); - if (tsc_freq < 0 || *err) { - error_setg(errp, "bad numerical value %s", val); - return; - } - snprintf(num, sizeof(num), "%" PRId64, tsc_freq); - object_property_parse(OBJECT(cpu), num, "tsc-frequency", - &local_err); - } else if (!strcmp(featurestr, "hv-spinlocks")) { - char *err; - const int min = 0xFFF; - char num[32]; - numvalue = strtoul(val, &err, 0); - if (!*val || *err) { - error_setg(errp, "bad numerical value %s", val); - return; - } - if (numvalue < min) { - error_report("hv-spinlocks value shall always be >= 0x%x" - ", fixup will be removed in future versions", - min); - numvalue = min; - } - snprintf(num, sizeof(num), "%" PRId32, numvalue); - object_property_parse(OBJECT(cpu), num, featurestr, &local_err); - } else { - object_property_parse(OBJECT(cpu), val, featurestr, &local_err); - } - } else { - feat2prop(featurestr); - object_property_parse(OBJECT(cpu), "on", featurestr, &local_err); + continue; } - if (local_err) { - error_propagate(errp, local_err); - return; + + eq = strchr(featurestr, '='); + if (eq) { + *eq++ = 0; + val = eq; + } else { + val = "on"; } - featurestr = strtok(NULL, ","); - } - if (cpu->host_features) { - for (w = 0; w < FEATURE_WORDS; w++) { - env->features[w] = - x86_cpu_get_supported_feature_word(w, cpu->migratable); + feat2prop(featurestr); + name = featurestr; + + /* Special case: */ + if (!strcmp(name, "tsc-freq")) { + int64_t tsc_freq; + char *err; + char num[32]; + + tsc_freq = qemu_strtosz_suffix_unit(val, &err, + QEMU_STRTOSZ_DEFSUFFIX_B, 1000); + if (tsc_freq < 0 || *err) { + error_setg(errp, "bad numerical value %s", val); + return; + } + snprintf(num, sizeof(num), "%" PRId64, tsc_freq); + val = num; + name = "tsc-frequency"; } + + object_property_parse(OBJECT(cpu), val, name, &local_err); } - for (w = 0; w < FEATURE_WORDS; w++) { - env->features[w] |= plus_features[w]; - env->features[w] &= ~minus_features[w]; + if (local_err) { + error_propagate(errp, local_err); } } @@ -2175,7 +2202,6 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) X86CPU *cpu_x86_create(const char *cpu_model, Error **errp) { X86CPU *cpu = NULL; - X86CPUClass *xcc; ObjectClass *oc; gchar **model_pieces; char *name, *features; @@ -2194,12 +2220,6 @@ X86CPU *cpu_x86_create(const char *cpu_model, Error **errp) error_setg(&error, "Unable to find CPU definition: %s", name); goto out; } - xcc = X86_CPU_CLASS(oc); - - if (xcc->kvm_required && !kvm_enabled()) { - error_setg(&error, "CPU model '%s' requires KVM", name); - goto out; - } cpu = X86_CPU(object_new(object_class_get_name(oc))); @@ -2222,25 +2242,7 @@ out: X86CPU *cpu_x86_init(const char *cpu_model) { - Error *error = NULL; - X86CPU *cpu; - - cpu = cpu_x86_create(cpu_model, &error); - if (error) { - goto out; - } - - object_property_set_bool(OBJECT(cpu), true, "realized", &error); - -out: - if (error) { - error_report_err(error); - if (cpu != NULL) { - object_unref(OBJECT(cpu)); - cpu = NULL; - } - } - return cpu; + return X86_CPU(cpu_generic_init(TYPE_X86_CPU, cpu_model)); } static void x86_cpu_cpudef_class_init(ObjectClass *oc, void *data) @@ -2447,6 +2449,36 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = 0; } break; + case 0xB: + /* Extended Topology Enumeration Leaf */ + if (!cpu->enable_cpuid_0xb) { + *eax = *ebx = *ecx = *edx = 0; + break; + } + + *ecx = count & 0xff; + *edx = cpu->apic_id; + + switch (count) { + case 0: + *eax = apicid_core_offset(smp_cores, smp_threads); + *ebx = smp_threads; + *ecx |= CPUID_TOPOLOGY_LEVEL_SMT; + break; + case 1: + *eax = apicid_pkg_offset(smp_cores, smp_threads); + *ebx = smp_cores * smp_threads; + *ecx |= CPUID_TOPOLOGY_LEVEL_CORE; + break; + default: + *eax = 0; + *ebx = 0; + *ecx |= CPUID_TOPOLOGY_LEVEL_INVALID; + } + + assert(!(*eax & ~0x1f)); + *ebx &= 0xffff; /* The count doesn't need to be reliable. */ + break; case 0xD: { KVMState *s = cs->kvm_state; uint64_t ena_mask; @@ -2874,12 +2906,37 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) CPUX86State *env = &cpu->env; Error *local_err = NULL; static bool ht_warned; + FeatureWord w; + + if (xcc->kvm_required && !kvm_enabled()) { + char *name = x86_cpu_class_get_model_name(xcc); + error_setg(&local_err, "CPU model '%s' requires KVM", name); + g_free(name); + goto out; + } if (cpu->apic_id < 0) { error_setg(errp, "apic-id property was not initialized properly"); return; } + /*TODO: cpu->host_features incorrectly overwrites features + * set using "feat=on|off". Once we fix this, we can convert + * plus_features & minus_features to global properties + * inside x86_cpu_parse_featurestr() too. + */ + if (cpu->host_features) { + for (w = 0; w < FEATURE_WORDS; w++) { + env->features[w] = + x86_cpu_get_supported_feature_word(w, cpu->migratable); + } + } + + for (w = 0; w < FEATURE_WORDS; w++) { + cpu->env.features[w] |= plus_features[w]; + cpu->env.features[w] &= ~minus_features[w]; + } + if (env->features[FEAT_7_0_EBX] && env->cpuid_level < 7) { env->cpuid_level = 7; } @@ -3206,6 +3263,7 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0), DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0), DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor_id), + DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), DEFINE_PROP_END_OF_LIST() }; diff --git a/target-i386/cpu.h b/target-i386/cpu.h index 0426459bba3e5d3974a3ac8a189da5696c369964..d9ab884c2b8551c2f707508ffbb5b211a55f24d6 100644 --- a/target-i386/cpu.h +++ b/target-i386/cpu.h @@ -636,6 +636,11 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_MWAIT_IBE (1U << 1) /* Interrupts can exit capability */ #define CPUID_MWAIT_EMX (1U << 0) /* enumeration supported */ +/* CPUID[0xB].ECX level types */ +#define CPUID_TOPOLOGY_LEVEL_INVALID (0U << 8) +#define CPUID_TOPOLOGY_LEVEL_SMT (1U << 8) +#define CPUID_TOPOLOGY_LEVEL_CORE (2U << 8) + #ifndef HYPERV_SPINLOCK_NEVER_RETRY #define HYPERV_SPINLOCK_NEVER_RETRY 0xFFFFFFFF #endif @@ -1173,6 +1178,9 @@ struct X86CPU { */ bool enable_pmu; + /* Compatibility bits for old machine types: */ + bool enable_cpuid_0xb; + /* in order to simplify APIC support, we leave this pointer to the user */ struct DeviceState *apic_state;