#include "qemu/config-file.h"
#include "qapi/qmp/qerror.h"
+#include "qapi-types.h"
+#include "qapi-visit.h"
#include "qapi/visitor.h"
#include "sysemu/arch_init.h"
-#include "hyperv.h"
-
#include "hw/hw.h"
#if defined(CONFIG_KVM)
#include <linux/kvm_para.h>
typedef struct FeatureWordInfo {
const char **feat_names;
- uint32_t cpuid_eax; /* Input EAX for CPUID */
- int cpuid_reg; /* R_* register constant */
+ uint32_t cpuid_eax; /* Input EAX for CPUID */
+ bool cpuid_needs_ecx; /* CPUID instruction uses ECX as input */
+ uint32_t cpuid_ecx; /* Input ECX value for CPUID */
+ int cpuid_reg; /* output register (R_* constant) */
} FeatureWordInfo;
static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
},
[FEAT_7_0_EBX] = {
.feat_names = cpuid_7_0_ebx_feature_name,
- .cpuid_eax = 7, .cpuid_reg = R_EBX,
+ .cpuid_eax = 7,
+ .cpuid_needs_ecx = true, .cpuid_ecx = 0,
+ .cpuid_reg = R_EBX,
},
};
+typedef struct X86RegisterInfo32 {
+ /* Name of register */
+ const char *name;
+ /* QAPI enum value register */
+ X86CPURegister32 qapi_enum;
+} X86RegisterInfo32;
+
+#define REGISTER(reg) \
+ [R_##reg] = { .name = #reg, .qapi_enum = X86_C_P_U_REGISTER32_##reg }
+X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = {
+ REGISTER(EAX),
+ REGISTER(ECX),
+ REGISTER(EDX),
+ REGISTER(EBX),
+ REGISTER(ESP),
+ REGISTER(EBP),
+ REGISTER(ESI),
+ REGISTER(EDI),
+};
+#undef REGISTER
+
+
const char *get_register_name_32(unsigned int reg)
{
- static const char *reg_names[CPU_NB_REGS32] = {
- [R_EAX] = "EAX",
- [R_ECX] = "ECX",
- [R_EDX] = "EDX",
- [R_EBX] = "EBX",
- [R_ESP] = "ESP",
- [R_EBP] = "EBP",
- [R_ESI] = "ESI",
- [R_EDI] = "EDI",
- };
-
- if (reg > CPU_NB_REGS32) {
+ if (reg >= CPU_NB_REGS32) {
return NULL;
}
- return reg_names[reg];
+ return x86_reg_info_32[reg].name;
}
/* collects per-function cpuid data
.level = 1,
.vendor = CPUID_VENDOR_INTEL,
.family = 4,
- .model = 0,
+ .model = 8,
.stepping = 0,
.features[FEAT_1_EDX] =
I486_FEATURES,
/* Some CPUs got no CPUID_SEP */
.features[FEAT_1_ECX] =
CPUID_EXT_SSE3 | CPUID_EXT_MONITOR | CPUID_EXT_SSSE3 |
- CPUID_EXT_DSCPL | CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR,
+ CPUID_EXT_DSCPL | CPUID_EXT_EST | CPUID_EXT_TM2 | CPUID_EXT_XTPR |
+ CPUID_EXT_MOVBE,
.features[FEAT_8000_0001_EDX] =
(PPRO_FEATURES & CPUID_EXT2_AMD_ALIASES) |
CPUID_EXT2_NX,
},
{
.name = "Conroe",
- .level = 2,
+ .level = 4,
.vendor = CPUID_VENDOR_INTEL,
.family = 6,
- .model = 2,
+ .model = 15,
.stepping = 3,
.features[FEAT_1_EDX] =
CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
},
{
.name = "Penryn",
- .level = 2,
+ .level = 4,
.vendor = CPUID_VENDOR_INTEL,
.family = 6,
- .model = 2,
+ .model = 23,
.stepping = 3,
.features[FEAT_1_EDX] =
CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
},
{
.name = "Nehalem",
- .level = 2,
+ .level = 4,
.vendor = CPUID_VENDOR_INTEL,
.family = 6,
- .model = 2,
+ .model = 26,
.stepping = 3,
.features[FEAT_1_EDX] =
CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX |
},
};
+/**
+ * x86_cpu_compat_set_features:
+ * @cpu_model: CPU model name to be changed. If NULL, all CPU models are changed
+ * @w: Identifies the feature word to be changed.
+ * @feat_add: Feature bits to be added to feature word
+ * @feat_remove: Feature bits to be removed from feature word
+ *
+ * Change CPU model feature bits for compatibility.
+ *
+ * This function may be used by machine-type compatibility functions
+ * to enable or disable feature bits on specific CPU models.
+ */
+void x86_cpu_compat_set_features(const char *cpu_model, FeatureWord w,
+ uint32_t feat_add, uint32_t feat_remove)
+{
+ x86_def_t *def;
+ int i;
+ for (i = 0; i < ARRAY_SIZE(builtin_x86_defs); i++) {
+ def = &builtin_x86_defs[i];
+ if (!cpu_model || !strcmp(cpu_model, def->name)) {
+ def->features[w] |= feat_add;
+ def->features[w] &= ~feat_remove;
+ }
+ }
+}
+
#ifdef CONFIG_KVM
static int cpu_x86_fill_model_id(char *str)
{
cpu->env.cpuid_apic_id = value;
}
-static int cpu_x86_find_by_name(x86_def_t *x86_cpu_def, const char *name)
+/* Generic getter for "feature-words" and "filtered-features" properties */
+static void x86_cpu_get_feature_words(Object *obj, Visitor *v, void *opaque,
+ const char *name, Error **errp)
+{
+ uint32_t *array = (uint32_t *)opaque;
+ FeatureWord w;
+ Error *err = NULL;
+ X86CPUFeatureWordInfo word_infos[FEATURE_WORDS] = { };
+ X86CPUFeatureWordInfoList list_entries[FEATURE_WORDS] = { };
+ X86CPUFeatureWordInfoList *list = NULL;
+
+ for (w = 0; w < FEATURE_WORDS; w++) {
+ FeatureWordInfo *wi = &feature_word_info[w];
+ X86CPUFeatureWordInfo *qwi = &word_infos[w];
+ qwi->cpuid_input_eax = wi->cpuid_eax;
+ qwi->has_cpuid_input_ecx = wi->cpuid_needs_ecx;
+ qwi->cpuid_input_ecx = wi->cpuid_ecx;
+ qwi->cpuid_register = x86_reg_info_32[wi->cpuid_reg].qapi_enum;
+ qwi->features = array[w];
+
+ /* List will be in reverse order, but order shouldn't matter */
+ list_entries[w].next = list;
+ list_entries[w].value = &word_infos[w];
+ list = &list_entries[w];
+ }
+
+ visit_type_X86CPUFeatureWordInfoList(v, &list, "feature-words", &err);
+ error_propagate(errp, err);
+}
+
+static int cpu_x86_find_by_name(X86CPU *cpu, x86_def_t *x86_cpu_def,
+ const char *name)
{
x86_def_t *def;
+ Error *err = NULL;
int i;
if (name == NULL) {
}
if (kvm_enabled() && strcmp(name, "host") == 0) {
kvm_cpu_fill_host(x86_cpu_def);
+ object_property_set_bool(OBJECT(cpu), true, "pmu", &err);
+ assert_no_error(err);
return 0;
}
object_property_parse(OBJECT(cpu), num, "tsc-frequency", errp);
} else if (!strcmp(featurestr, "hv-spinlocks")) {
char *err;
+ const int min = 0xFFF;
numvalue = strtoul(val, &err, 0);
if (!*val || *err) {
error_setg(errp, "bad numerical value %s", val);
goto out;
}
- hyperv_set_spinlock_retries(numvalue);
+ if (numvalue < min) {
+ fprintf(stderr, "hv-spinlocks value shall always be >= 0x%x"
+ ", fixup will be removed in future versions\n",
+ min);
+ numvalue = min;
+ }
+ cpu->hyperv_spinlock_attempts = numvalue;
} else {
error_setg(errp, "unrecognized feature %s", featurestr);
goto out;
} else if (!strcmp(featurestr, "enforce")) {
check_cpuid = enforce_cpuid = 1;
} else if (!strcmp(featurestr, "hv_relaxed")) {
- hyperv_enable_relaxed_timing(true);
+ cpu->hyperv_relaxed_timing = true;
} else if (!strcmp(featurestr, "hv_vapic")) {
- hyperv_enable_vapic_recommended(true);
+ cpu->hyperv_vapic = true;
} else {
error_setg(errp, "feature string `%s' not in format (+feature|"
"-feature|feature=xyz)", featurestr);
{
CPUX86State *env = &cpu->env;
KVMState *s = kvm_state;
+ FeatureWord w;
- env->features[FEAT_1_EDX] &=
- kvm_arch_get_supported_cpuid(s, 1, 0, R_EDX);
- env->features[FEAT_1_ECX] &=
- kvm_arch_get_supported_cpuid(s, 1, 0, R_ECX);
- env->features[FEAT_8000_0001_EDX] &=
- kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_EDX);
- env->features[FEAT_8000_0001_ECX] &=
- kvm_arch_get_supported_cpuid(s, 0x80000001, 0, R_ECX);
- env->features[FEAT_SVM] &=
- kvm_arch_get_supported_cpuid(s, 0x8000000A, 0, R_EDX);
- env->features[FEAT_7_0_EBX] &=
- kvm_arch_get_supported_cpuid(s, 7, 0, R_EBX);
- env->features[FEAT_KVM] &=
- kvm_arch_get_supported_cpuid(s, KVM_CPUID_FEATURES, 0, R_EAX);
- env->features[FEAT_C000_0001_EDX] &=
- kvm_arch_get_supported_cpuid(s, 0xC0000001, 0, R_EDX);
-
+ for (w = 0; w < FEATURE_WORDS; w++) {
+ FeatureWordInfo *wi = &feature_word_info[w];
+ uint32_t host_feat = kvm_arch_get_supported_cpuid(s, wi->cpuid_eax,
+ wi->cpuid_ecx,
+ wi->cpuid_reg);
+ uint32_t requested_features = env->features[w];
+ env->features[w] &= host_feat;
+ cpu->filtered_features[w] = requested_features & ~env->features[w];
+ }
}
#endif
memset(def, 0, sizeof(*def));
- if (cpu_x86_find_by_name(def, name) < 0) {
+ if (cpu_x86_find_by_name(cpu, def, name) < 0) {
error_setg(errp, "Unable to find CPU definition: %s", name);
return;
}
CPUX86State *env;
gchar **model_pieces;
char *name, *features;
+ char *typename;
Error *error = NULL;
model_pieces = g_strsplit(cpu_model, ",", 2);
goto out;
}
+ /* Emulate per-model subclasses for global properties */
+ typename = g_strdup_printf("%s-" TYPE_X86_CPU, name);
+ qdev_prop_set_globals_for_type(DEVICE(cpu), typename, &error);
+ g_free(typename);
+ if (error) {
+ goto out;
+ }
+
cpu_x86_parse_featurestr(cpu, features, &error);
if (error) {
goto out;
}
out:
- error_propagate(errp, error);
+ if (error != NULL) {
+ error_propagate(errp, error);
+ object_unref(OBJECT(cpu));
+ cpu = NULL;
+ }
g_strfreev(model_pieces);
return cpu;
}
out:
if (error) {
- fprintf(stderr, "%s\n", error_get_pretty(error));
+ error_report("%s", error_get_pretty(error));
error_free(error);
if (cpu != NULL) {
object_unref(OBJECT(cpu));
break;
case 0xA:
/* Architectural Performance Monitoring Leaf */
- if (kvm_enabled()) {
+ if (kvm_enabled() && cpu->enable_pmu) {
KVMState *s = cs->kvm_state;
*eax = kvm_arch_get_supported_cpuid(s, 0xA, count, R_EAX);
CPUX86State *env = &cpu->env;
int i;
- if (qemu_loglevel_mask(CPU_LOG_RESET)) {
- qemu_log("CPU Reset (CPU %d)\n", s->cpu_index);
- log_cpu_state(env, CPU_DUMP_FPU | CPU_DUMP_CCOP);
- }
-
xcc->parent_reset(s);
static void x86_cpu_realizefn(DeviceState *dev, Error **errp)
{
+ CPUState *cs = CPU(dev);
X86CPU *cpu = X86_CPU(dev);
X86CPUClass *xcc = X86_CPU_GET_CLASS(dev);
CPUX86State *env = &cpu->env;
#endif
mce_init(cpu);
- qemu_init_vcpu(&cpu->env);
+ qemu_init_vcpu(cs);
x86_cpu_apic_realize(cpu, &local_err);
if (local_err != NULL) {
goto out;
}
- cpu_reset(CPU(cpu));
+ cpu_reset(cs);
xcc->parent_realize(dev, &local_err);
out:
object_property_add(obj, "apic-id", "int",
x86_cpuid_get_apic_id,
x86_cpuid_set_apic_id, NULL, NULL, NULL);
-
+ object_property_add(obj, "feature-words", "X86CPUFeatureWordInfo",
+ x86_cpu_get_feature_words,
+ NULL, NULL, (void *)env->features, NULL);
+ object_property_add(obj, "filtered-features", "X86CPUFeatureWordInfo",
+ x86_cpu_get_feature_words,
+ NULL, NULL, (void *)cpu->filtered_features, NULL);
+
+ cpu->hyperv_spinlock_attempts = HYPERV_SPINLOCK_NEVER_RETRY;
env->cpuid_apic_id = x86_cpu_apic_id_from_index(cs->cpu_index);
/* init various static tables used in TCG mode */
return env->cpuid_apic_id;
}
+static bool x86_cpu_get_paging_enabled(const CPUState *cs)
+{
+ X86CPU *cpu = X86_CPU(cs);
+
+ return cpu->env.cr[0] & CR0_PG_MASK;
+}
+
+static void x86_cpu_set_pc(CPUState *cs, vaddr value)
+{
+ X86CPU *cpu = X86_CPU(cs);
+
+ cpu->env.eip = value;
+}
+
+static void x86_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
+{
+ X86CPU *cpu = X86_CPU(cs);
+
+ cpu->env.eip = tb->pc - tb->cs_base;
+}
+
+static Property x86_cpu_properties[] = {
+ DEFINE_PROP_BOOL("pmu", X86CPU, enable_pmu, false),
+ DEFINE_PROP_END_OF_LIST()
+};
+
static void x86_cpu_common_class_init(ObjectClass *oc, void *data)
{
X86CPUClass *xcc = X86_CPU_CLASS(oc);
xcc->parent_realize = dc->realize;
dc->realize = x86_cpu_realizefn;
dc->bus_type = TYPE_ICC_BUS;
+ dc->props = x86_cpu_properties;
xcc->parent_reset = cc->reset;
cc->reset = x86_cpu_reset;
+ cc->reset_dump_flags = CPU_DUMP_FPU | CPU_DUMP_CCOP;
cc->do_interrupt = x86_cpu_do_interrupt;
+ cc->dump_state = x86_cpu_dump_state;
+ cc->set_pc = x86_cpu_set_pc;
+ cc->synchronize_from_tb = x86_cpu_synchronize_from_tb;
+ cc->gdb_read_register = x86_cpu_gdb_read_register;
+ cc->gdb_write_register = x86_cpu_gdb_write_register;
+ cc->get_arch_id = x86_cpu_get_arch_id;
+ cc->get_paging_enabled = x86_cpu_get_paging_enabled;
#ifndef CONFIG_USER_ONLY
+ cc->get_memory_mapping = x86_cpu_get_memory_mapping;
+ cc->get_phys_page_debug = x86_cpu_get_phys_page_debug;
cc->write_elf64_note = x86_cpu_write_elf64_note;
cc->write_elf64_qemunote = x86_cpu_write_elf64_qemunote;
cc->write_elf32_note = x86_cpu_write_elf32_note;
cc->write_elf32_qemunote = x86_cpu_write_elf32_qemunote;
+ cc->vmsd = &vmstate_x86_cpu;
#endif
- cpu_class_set_vmsd(cc, &vmstate_x86_cpu);
-
- cc->get_arch_id = x86_cpu_get_arch_id;
+ cc->gdb_num_core_regs = CPU_NB_REGS * 2 + 25;
}
static const TypeInfo x86_cpu_type_info = {