提交 a91a4e7d 编写于 作者: P Peter Maydell

Merge remote-tracking branch 'remotes/ehabkost/tags/x86-pull-request' into staging

x86 and machine queue, 2016-07-07

Highlights:
* Improvements on global property error handling
* Translate -cpu options to global properties
* LMCE support

# gpg: Signature made Thu 07 Jul 2016 20:59:01 BST
# gpg:                using RSA key 0x2807936F984DC5A6
# gpg: Good signature from "Eduardo Habkost <ehabkost@redhat.com>"
# Primary key fingerprint: 5A32 2FD5 ABC4 D3DB ACCF  D1AA 2807 936F 984D C5A6

* remotes/ehabkost/tags/x86-pull-request:
  target-i386: Enable LMCE for '-cpu host' if supported by host
  target-i386: Publish advised value of MSR_IA32_FEATURE_CONTROL via fw_cfg
  target-i386: kvm: Add basic Intel LMCE support
  target-i386: Report hyperv feature words through qom
  target-i386: Show host and VM TSC frequencies on mismatch
  pc: Parse CPU features only once
  arm: virt: Parse cpu_model only once
  cpu: Use CPUClass->parse_features() as convertor to global properties
  target-i386: Avoid using locals outside their scope
  target-i386: TCG can support CPUID.07H:EBX.erms
  target-sparc: Use sparc_cpu_parse_features() directly
  vl: Set errp to &error_abort on machine compat_props
  machine: Add machine_register_compat_props() function
  qdev: GlobalProperty.errp field
  qdev: Eliminate qemu_add_globals() function
  qdev: Don't stop applying globals on first error
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
...@@ -1176,6 +1176,10 @@ static void machvirt_init(MachineState *machine) ...@@ -1176,6 +1176,10 @@ static void machvirt_init(MachineState *machine)
VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state); VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
VirtGuestInfo *guest_info = &guest_info_state->info; VirtGuestInfo *guest_info = &guest_info_state->info;
char **cpustr; char **cpustr;
ObjectClass *oc;
const char *typename;
CPUClass *cc;
Error *err = NULL;
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
if (!cpu_model) { if (!cpu_model) {
...@@ -1259,26 +1263,24 @@ static void machvirt_init(MachineState *machine) ...@@ -1259,26 +1263,24 @@ static void machvirt_init(MachineState *machine)
create_fdt(vbi); create_fdt(vbi);
for (n = 0; n < smp_cpus; n++) { oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]);
ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]); if (!oc) {
CPUClass *cc = CPU_CLASS(oc); error_report("Unable to find CPU definition");
Object *cpuobj; exit(1);
Error *err = NULL; }
char *cpuopts = g_strdup(cpustr[1]); typename = object_class_get_name(oc);
if (!oc) {
error_report("Unable to find CPU definition");
exit(1);
}
cpuobj = object_new(object_class_get_name(oc));
/* Handle any CPU options specified by the user */ /* convert -smp CPU options specified by the user into global props */
cc->parse_features(CPU(cpuobj), cpuopts, &err); cc = CPU_CLASS(oc);
g_free(cpuopts); cc->parse_features(typename, cpustr[1], &err);
if (err) { g_strfreev(cpustr);
error_report_err(err); if (err) {
exit(1); error_report_err(err);
} exit(1);
}
for (n = 0; n < smp_cpus; n++) {
Object *cpuobj = object_new(typename);
if (!vms->secure) { if (!vms->secure) {
object_property_set_bool(cpuobj, false, "has_el3", NULL); object_property_set_bool(cpuobj, false, "has_el3", NULL);
...@@ -1309,7 +1311,6 @@ static void machvirt_init(MachineState *machine) ...@@ -1309,7 +1311,6 @@ static void machvirt_init(MachineState *machine)
object_property_set_bool(cpuobj, true, "realized", NULL); object_property_set_bool(cpuobj, true, "realized", NULL);
} }
g_strfreev(cpustr);
fdt_add_timer_nodes(vbi, gic_version); fdt_add_timer_nodes(vbi, gic_version);
fdt_add_cpu_nodes(vbi); fdt_add_cpu_nodes(vbi);
fdt_add_psci_node(vbi); fdt_add_psci_node(vbi);
......
...@@ -560,6 +560,24 @@ static void machine_class_finalize(ObjectClass *klass, void *data) ...@@ -560,6 +560,24 @@ static void machine_class_finalize(ObjectClass *klass, void *data)
} }
} }
void machine_register_compat_props(MachineState *machine)
{
MachineClass *mc = MACHINE_GET_CLASS(machine);
int i;
GlobalProperty *p;
if (!mc->compat_props) {
return;
}
for (i = 0; i < mc->compat_props->len; i++) {
p = g_array_index(mc->compat_props, GlobalProperty *, i);
/* Machine compat_props must never cause errors: */
p->errp = &error_abort;
qdev_prop_register_global(p);
}
}
static const TypeInfo machine_info = { static const TypeInfo machine_info = {
.name = TYPE_MACHINE, .name = TYPE_MACHINE,
.parent = TYPE_OBJECT, .parent = TYPE_OBJECT,
......
/* /*
* qdev property parsing and global properties * qdev property parsing
* (parts specific for qemu-system-*) * (parts specific for qemu-system-*)
* *
* This file is based on code from hw/qdev-properties.c from * This file is based on code from hw/qdev-properties.c from
...@@ -394,22 +394,3 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd) ...@@ -394,22 +394,3 @@ void qdev_set_nic_properties(DeviceState *dev, NICInfo *nd)
} }
nd->instantiated = 1; nd->instantiated = 1;
} }
static int qdev_add_one_global(void *opaque, QemuOpts *opts, Error **errp)
{
GlobalProperty *g;
g = g_malloc0(sizeof(*g));
g->driver = qemu_opt_get(opts, "driver");
g->property = qemu_opt_get(opts, "property");
g->value = qemu_opt_get(opts, "value");
g->user_provided = true;
qdev_prop_register_global(g);
return 0;
}
void qemu_add_globals(void)
{
qemu_opts_foreach(qemu_find_opts("global"),
qdev_add_one_global, NULL, NULL);
}
...@@ -1085,10 +1085,14 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev, ...@@ -1085,10 +1085,14 @@ static void qdev_prop_set_globals_for_type(DeviceState *dev,
prop->used = true; prop->used = true;
object_property_parse(OBJECT(dev), prop->value, prop->property, &err); object_property_parse(OBJECT(dev), prop->value, prop->property, &err);
if (err != NULL) { if (err != NULL) {
assert(prop->user_provided); error_prepend(&err, "can't apply global %s.%s=%s: ",
error_reportf_err(err, "Warning: global %s.%s=%s ignored: ", prop->driver, prop->property, prop->value);
prop->driver, prop->property, prop->value); if (prop->errp) {
return; error_propagate(prop->errp, err);
} else {
assert(prop->user_provided);
error_reportf_err(err, "Warning: ");
}
} }
} }
} }
......
...@@ -1039,21 +1039,17 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level) ...@@ -1039,21 +1039,17 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level)
} }
} }
static X86CPU *pc_new_cpu(const char *cpu_model, int64_t apic_id, static X86CPU *pc_new_cpu(const char *typename, int64_t apic_id,
Error **errp) Error **errp)
{ {
X86CPU *cpu = NULL; X86CPU *cpu = NULL;
Error *local_err = NULL; Error *local_err = NULL;
cpu = cpu_x86_create(cpu_model, &local_err); cpu = X86_CPU(object_new(typename));
if (local_err != NULL) {
goto out;
}
object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err); object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err);
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
out:
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
object_unref(OBJECT(cpu)); object_unref(OBJECT(cpu));
...@@ -1065,7 +1061,8 @@ out: ...@@ -1065,7 +1061,8 @@ out:
void pc_hot_add_cpu(const int64_t id, Error **errp) void pc_hot_add_cpu(const int64_t id, Error **errp)
{ {
X86CPU *cpu; X86CPU *cpu;
MachineState *machine = MACHINE(qdev_get_machine()); ObjectClass *oc;
PCMachineState *pcms = PC_MACHINE(qdev_get_machine());
int64_t apic_id = x86_cpu_apic_id_from_index(id); int64_t apic_id = x86_cpu_apic_id_from_index(id);
Error *local_err = NULL; Error *local_err = NULL;
...@@ -1093,7 +1090,9 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) ...@@ -1093,7 +1090,9 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
return; return;
} }
cpu = pc_new_cpu(machine->cpu_model, apic_id, &local_err); assert(pcms->possible_cpus->cpus[0].cpu); /* BSP is always present */
oc = OBJECT_CLASS(CPU_GET_CLASS(pcms->possible_cpus->cpus[0].cpu));
cpu = pc_new_cpu(object_class_get_name(oc), apic_id, &local_err);
if (local_err) { if (local_err) {
error_propagate(errp, local_err); error_propagate(errp, local_err);
return; return;
...@@ -1104,6 +1103,10 @@ void pc_hot_add_cpu(const int64_t id, Error **errp) ...@@ -1104,6 +1103,10 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
void pc_cpus_init(PCMachineState *pcms) void pc_cpus_init(PCMachineState *pcms)
{ {
int i; int i;
CPUClass *cc;
ObjectClass *oc;
const char *typename;
gchar **model_pieces;
X86CPU *cpu = NULL; X86CPU *cpu = NULL;
MachineState *machine = MACHINE(pcms); MachineState *machine = MACHINE(pcms);
...@@ -1116,6 +1119,22 @@ void pc_cpus_init(PCMachineState *pcms) ...@@ -1116,6 +1119,22 @@ void pc_cpus_init(PCMachineState *pcms)
#endif #endif
} }
model_pieces = g_strsplit(machine->cpu_model, ",", 2);
if (!model_pieces[0]) {
error_report("Invalid/empty CPU model name");
exit(1);
}
oc = cpu_class_by_name(TYPE_X86_CPU, model_pieces[0]);
if (oc == NULL) {
error_report("Unable to find CPU definition: %s", model_pieces[0]);
exit(1);
}
typename = object_class_get_name(oc);
cc = CPU_CLASS(oc);
cc->parse_features(typename, model_pieces[1], &error_fatal);
g_strfreev(model_pieces);
/* Calculates the limit to CPU APIC ID values /* Calculates the limit to CPU APIC ID values
* *
* Limit for the APIC ID value, so that all * Limit for the APIC ID value, so that all
...@@ -1136,7 +1155,7 @@ void pc_cpus_init(PCMachineState *pcms) ...@@ -1136,7 +1155,7 @@ void pc_cpus_init(PCMachineState *pcms)
pcms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(i); pcms->possible_cpus->cpus[i].arch_id = x86_cpu_apic_id_from_index(i);
pcms->possible_cpus->len++; pcms->possible_cpus->len++;
if (i < smp_cpus) { if (i < smp_cpus) {
cpu = pc_new_cpu(machine->cpu_model, x86_cpu_apic_id_from_index(i), cpu = pc_new_cpu(typename, x86_cpu_apic_id_from_index(i),
&error_fatal); &error_fatal);
pcms->possible_cpus->cpus[i].cpu = CPU(cpu); pcms->possible_cpus->cpus[i].cpu = CPU(cpu);
object_unref(OBJECT(cpu)); object_unref(OBJECT(cpu));
...@@ -1147,6 +1166,34 @@ void pc_cpus_init(PCMachineState *pcms) ...@@ -1147,6 +1166,34 @@ void pc_cpus_init(PCMachineState *pcms)
smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]); smbios_set_cpuid(cpu->env.cpuid_version, cpu->env.features[FEAT_1_EDX]);
} }
static void pc_build_feature_control_file(PCMachineState *pcms)
{
X86CPU *cpu = X86_CPU(pcms->possible_cpus->cpus[0].cpu);
CPUX86State *env = &cpu->env;
uint32_t unused, ecx, edx;
uint64_t feature_control_bits = 0;
uint64_t *val;
cpu_x86_cpuid(env, 1, 0, &unused, &unused, &ecx, &edx);
if (ecx & CPUID_EXT_VMX) {
feature_control_bits |= FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX;
}
if ((edx & (CPUID_EXT2_MCE | CPUID_EXT2_MCA)) ==
(CPUID_EXT2_MCE | CPUID_EXT2_MCA) &&
(env->mcg_cap & MCG_LMCE_P)) {
feature_control_bits |= FEATURE_CONTROL_LMCE;
}
if (!feature_control_bits) {
return;
}
val = g_malloc(sizeof(*val));
*val = cpu_to_le64(feature_control_bits | FEATURE_CONTROL_LOCKED);
fw_cfg_add_file(pcms->fw_cfg, "etc/msr_feature_control", val, sizeof(*val));
}
static static
void pc_machine_done(Notifier *notifier, void *data) void pc_machine_done(Notifier *notifier, void *data)
{ {
...@@ -1174,6 +1221,7 @@ void pc_machine_done(Notifier *notifier, void *data) ...@@ -1174,6 +1221,7 @@ void pc_machine_done(Notifier *notifier, void *data)
acpi_setup(); acpi_setup();
if (pcms->fw_cfg) { if (pcms->fw_cfg) {
pc_build_smbios(pcms->fw_cfg); pc_build_smbios(pcms->fw_cfg);
pc_build_feature_control_file(pcms);
} }
} }
......
...@@ -40,6 +40,7 @@ int machine_kvm_shadow_mem(MachineState *machine); ...@@ -40,6 +40,7 @@ int machine_kvm_shadow_mem(MachineState *machine);
int machine_phandle_start(MachineState *machine); int machine_phandle_start(MachineState *machine);
bool machine_dump_guest_core(MachineState *machine); bool machine_dump_guest_core(MachineState *machine);
bool machine_mem_merge(MachineState *machine); bool machine_mem_merge(MachineState *machine);
void machine_register_compat_props(MachineState *machine);
/** /**
* CPUArchId: * CPUArchId:
......
...@@ -259,6 +259,9 @@ struct PropertyInfo { ...@@ -259,6 +259,9 @@ struct PropertyInfo {
* @user_provided: Set to true if property comes from user-provided config * @user_provided: Set to true if property comes from user-provided config
* (command-line or config file). * (command-line or config file).
* @used: Set to true if property was used when initializing a device. * @used: Set to true if property was used when initializing a device.
* @errp: Error destination, used like first argument of error_setg()
* in case property setting fails later. If @errp is NULL, we
* print warnings instead of ignoring errors silently.
*/ */
typedef struct GlobalProperty { typedef struct GlobalProperty {
const char *driver; const char *driver;
...@@ -266,6 +269,7 @@ typedef struct GlobalProperty { ...@@ -266,6 +269,7 @@ typedef struct GlobalProperty {
const char *value; const char *value;
bool user_provided; bool user_provided;
bool used; bool used;
Error **errp;
} GlobalProperty; } GlobalProperty;
/*** Board API. This should go away once we have a machine config file. ***/ /*** Board API. This should go away once we have a machine config file. ***/
......
...@@ -12,7 +12,6 @@ void qemu_add_opts(QemuOptsList *list); ...@@ -12,7 +12,6 @@ void qemu_add_opts(QemuOptsList *list);
void qemu_add_drive_opts(QemuOptsList *list); void qemu_add_drive_opts(QemuOptsList *list);
int qemu_set_option(const char *str); int qemu_set_option(const char *str);
int qemu_global_option(const char *str); int qemu_global_option(const char *str);
void qemu_add_globals(void);
void qemu_config_write(FILE *fp); void qemu_config_write(FILE *fp);
int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname); int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname);
......
...@@ -134,7 +134,7 @@ typedef struct CPUClass { ...@@ -134,7 +134,7 @@ typedef struct CPUClass {
/*< public >*/ /*< public >*/
ObjectClass *(*class_by_name)(const char *cpu_model); ObjectClass *(*class_by_name)(const char *cpu_model);
void (*parse_features)(CPUState *cpu, char *str, Error **errp); void (*parse_features)(const char *typename, char *str, Error **errp);
void (*reset)(CPUState *cpu); void (*reset)(CPUState *cpu);
int reset_dump_flags; int reset_dump_flags;
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include "exec/log.h" #include "exec/log.h"
#include "qemu/error-report.h" #include "qemu/error-report.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "hw/qdev-properties.h"
bool cpu_exists(int64_t id) bool cpu_exists(int64_t id)
{ {
...@@ -46,7 +47,7 @@ bool cpu_exists(int64_t id) ...@@ -46,7 +47,7 @@ bool cpu_exists(int64_t id)
CPUState *cpu_generic_init(const char *typename, const char *cpu_model) CPUState *cpu_generic_init(const char *typename, const char *cpu_model)
{ {
char *str, *name, *featurestr; char *str, *name, *featurestr;
CPUState *cpu; CPUState *cpu = NULL;
ObjectClass *oc; ObjectClass *oc;
CPUClass *cc; CPUClass *cc;
Error *err = NULL; Error *err = NULL;
...@@ -60,16 +61,18 @@ CPUState *cpu_generic_init(const char *typename, const char *cpu_model) ...@@ -60,16 +61,18 @@ CPUState *cpu_generic_init(const char *typename, const char *cpu_model)
return NULL; return NULL;
} }
cpu = CPU(object_new(object_class_get_name(oc))); cc = CPU_CLASS(oc);
cc = CPU_GET_CLASS(cpu);
featurestr = strtok(NULL, ","); featurestr = strtok(NULL, ",");
cc->parse_features(cpu, featurestr, &err); /* TODO: all callers of cpu_generic_init() need to be converted to
* call parse_features() only once, before calling cpu_generic_init().
*/
cc->parse_features(object_class_get_name(oc), featurestr, &err);
g_free(str); g_free(str);
if (err != NULL) { if (err != NULL) {
goto out; goto out;
} }
cpu = CPU(object_new(object_class_get_name(oc)));
object_property_set_bool(OBJECT(cpu), true, "realized", &err); object_property_set_bool(OBJECT(cpu), true, "realized", &err);
out: out:
...@@ -282,25 +285,37 @@ static ObjectClass *cpu_common_class_by_name(const char *cpu_model) ...@@ -282,25 +285,37 @@ static ObjectClass *cpu_common_class_by_name(const char *cpu_model)
return NULL; return NULL;
} }
static void cpu_common_parse_features(CPUState *cpu, char *features, static void cpu_common_parse_features(const char *typename, char *features,
Error **errp) Error **errp)
{ {
char *featurestr; /* Single "key=value" string being parsed */ char *featurestr; /* Single "key=value" string being parsed */
char *val; char *val;
Error *err = NULL; static bool cpu_globals_initialized;
/* TODO: all callers of ->parse_features() need to be changed to
* call it only once, so we can remove this check (or change it
* to assert(!cpu_globals_initialized).
* Current callers of ->parse_features() are:
* - cpu_generic_init()
*/
if (cpu_globals_initialized) {
return;
}
cpu_globals_initialized = true;
featurestr = features ? strtok(features, ",") : NULL; featurestr = features ? strtok(features, ",") : NULL;
while (featurestr) { while (featurestr) {
val = strchr(featurestr, '='); val = strchr(featurestr, '=');
if (val) { if (val) {
GlobalProperty *prop = g_new0(typeof(*prop), 1);
*val = 0; *val = 0;
val++; val++;
object_property_parse(OBJECT(cpu), val, featurestr, &err); prop->driver = typename;
if (err) { prop->property = g_strdup(featurestr);
error_propagate(errp, err); prop->value = g_strdup(val);
return; prop->errp = &error_fatal;
} qdev_prop_register_global(prop);
} else { } else {
error_setg(errp, "Expected key=value format, found %s.", error_setg(errp, "Expected key=value format, found %s.",
featurestr); featurestr);
......
...@@ -245,6 +245,47 @@ static const char *kvm_feature_name[] = { ...@@ -245,6 +245,47 @@ static const char *kvm_feature_name[] = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
}; };
static const char *hyperv_priv_feature_name[] = {
NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */,
NULL /* hv_msr_synic_access */, NULL /* hv_msr_stimer_access */,
NULL /* hv_msr_apic_access */, NULL /* hv_msr_hypercall_access */,
NULL /* hv_vpindex_access */, NULL /* hv_msr_reset_access */,
NULL /* hv_msr_stats_access */, NULL /* hv_reftsc_access */,
NULL /* hv_msr_idle_access */, NULL /* hv_msr_frequency_access */,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
};
static const char *hyperv_ident_feature_name[] = {
NULL /* hv_create_partitions */, NULL /* hv_access_partition_id */,
NULL /* hv_access_memory_pool */, NULL /* hv_adjust_message_buffers */,
NULL /* hv_post_messages */, NULL /* hv_signal_events */,
NULL /* hv_create_port */, NULL /* hv_connect_port */,
NULL /* hv_access_stats */, NULL, NULL, NULL /* hv_debugging */,
NULL /* hv_cpu_power_management */, NULL /* hv_configure_profiler */,
NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
};
static const char *hyperv_misc_feature_name[] = {
NULL /* hv_mwait */, NULL /* hv_guest_debugging */,
NULL /* hv_perf_monitor */, NULL /* hv_cpu_dynamic_part */,
NULL /* hv_hypercall_params_xmm */, NULL /* hv_guest_idle_state */,
NULL, NULL,
NULL, NULL, NULL /* hv_guest_crash_msr */, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL,
};
static const char *svm_feature_name[] = { static const char *svm_feature_name[] = {
"npt", "lbrv", "svm_lock", "nrip_save", "npt", "lbrv", "svm_lock", "nrip_save",
"tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists", "tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists",
...@@ -358,10 +399,11 @@ static const char *cpuid_6_feature_name[] = { ...@@ -358,10 +399,11 @@ static const char *cpuid_6_feature_name[] = {
#define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \ #define TCG_7_0_EBX_FEATURES (CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_SMAP | \
CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \ CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ADX | \
CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \ CPUID_7_0_EBX_PCOMMIT | CPUID_7_0_EBX_CLFLUSHOPT | \
CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE) CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_FSGSBASE | \
CPUID_7_0_EBX_ERMS)
/* missing: /* missing:
CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2, CPUID_7_0_EBX_HLE, CPUID_7_0_EBX_AVX2,
CPUID_7_0_EBX_ERMS, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM, CPUID_7_0_EBX_INVPCID, CPUID_7_0_EBX_RTM,
CPUID_7_0_EBX_RDSEED */ CPUID_7_0_EBX_RDSEED */
#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_OSPKE) #define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_OSPKE)
#define TCG_APM_FEATURES 0 #define TCG_APM_FEATURES 0
...@@ -411,6 +453,18 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { ...@@ -411,6 +453,18 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
.cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX, .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX,
.tcg_features = TCG_KVM_FEATURES, .tcg_features = TCG_KVM_FEATURES,
}, },
[FEAT_HYPERV_EAX] = {
.feat_names = hyperv_priv_feature_name,
.cpuid_eax = 0x40000003, .cpuid_reg = R_EAX,
},
[FEAT_HYPERV_EBX] = {
.feat_names = hyperv_ident_feature_name,
.cpuid_eax = 0x40000003, .cpuid_reg = R_EBX,
},
[FEAT_HYPERV_EDX] = {
.feat_names = hyperv_misc_feature_name,
.cpuid_eax = 0x40000003, .cpuid_reg = R_EDX,
},
[FEAT_SVM] = { [FEAT_SVM] = {
.feat_names = svm_feature_name, .feat_names = svm_feature_name,
.cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX, .cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX,
...@@ -1493,6 +1547,17 @@ static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, ...@@ -1493,6 +1547,17 @@ static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w,
#ifdef CONFIG_KVM #ifdef CONFIG_KVM
static bool lmce_supported(void)
{
uint64_t mce_cap;
if (kvm_ioctl(kvm_state, KVM_X86_GET_MCE_CAP_SUPPORTED, &mce_cap) < 0) {
return false;
}
return !!(mce_cap & MCG_LMCE_P);
}
static int cpu_x86_fill_model_id(char *str) static int cpu_x86_fill_model_id(char *str)
{ {
uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0; uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
...@@ -1565,6 +1630,10 @@ static void host_x86_cpu_initfn(Object *obj) ...@@ -1565,6 +1630,10 @@ static void host_x86_cpu_initfn(Object *obj)
env->cpuid_level = kvm_arch_get_supported_cpuid(s, 0x0, 0, R_EAX); 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_xlevel = kvm_arch_get_supported_cpuid(s, 0x80000000, 0, R_EAX);
env->cpuid_xlevel2 = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX); env->cpuid_xlevel2 = kvm_arch_get_supported_cpuid(s, 0xC0000000, 0, R_EAX);
if (lmce_supported()) {
object_property_set_bool(OBJECT(cpu), true, "lmce", &error_abort);
}
} }
object_property_set_bool(OBJECT(cpu), true, "pmu", &error_abort); object_property_set_bool(OBJECT(cpu), true, "pmu", &error_abort);
...@@ -1957,12 +2026,17 @@ static FeatureWordArray minus_features = { 0 }; ...@@ -1957,12 +2026,17 @@ static FeatureWordArray minus_features = { 0 };
/* Parse "+feature,-feature,feature=foo" CPU feature string /* Parse "+feature,-feature,feature=foo" CPU feature string
*/ */
static void x86_cpu_parse_featurestr(CPUState *cs, char *features, static void x86_cpu_parse_featurestr(const char *typename, char *features,
Error **errp) Error **errp)
{ {
X86CPU *cpu = X86_CPU(cs);
char *featurestr; /* Single 'key=value" string being parsed */ char *featurestr; /* Single 'key=value" string being parsed */
Error *local_err = NULL; Error *local_err = NULL;
static bool cpu_globals_initialized;
if (cpu_globals_initialized) {
return;
}
cpu_globals_initialized = true;
if (!features) { if (!features) {
return; return;
...@@ -1974,6 +2048,8 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, ...@@ -1974,6 +2048,8 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
const char *name; const char *name;
const char *val = NULL; const char *val = NULL;
char *eq = NULL; char *eq = NULL;
char num[32];
GlobalProperty *prop;
/* Compatibility syntax: */ /* Compatibility syntax: */
if (featurestr[0] == '+') { if (featurestr[0] == '+') {
...@@ -1999,7 +2075,6 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, ...@@ -1999,7 +2075,6 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
if (!strcmp(name, "tsc-freq")) { if (!strcmp(name, "tsc-freq")) {
int64_t tsc_freq; int64_t tsc_freq;
char *err; char *err;
char num[32];
tsc_freq = qemu_strtosz_suffix_unit(val, &err, tsc_freq = qemu_strtosz_suffix_unit(val, &err,
QEMU_STRTOSZ_DEFSUFFIX_B, 1000); QEMU_STRTOSZ_DEFSUFFIX_B, 1000);
...@@ -2012,7 +2087,12 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features, ...@@ -2012,7 +2087,12 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
name = "tsc-frequency"; name = "tsc-frequency";
} }
object_property_parse(OBJECT(cpu), val, name, &local_err); prop = g_new0(typeof(*prop), 1);
prop->driver = typename;
prop->property = g_strdup(name);
prop->value = g_strdup(val);
prop->errp = &error_fatal;
qdev_prop_register_global(prop);
} }
if (local_err) { if (local_err) {
...@@ -2197,47 +2277,6 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) ...@@ -2197,47 +2277,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;
ObjectClass *oc;
gchar **model_pieces;
char *name, *features;
Error *error = NULL;
model_pieces = g_strsplit(cpu_model, ",", 2);
if (!model_pieces[0]) {
error_setg(&error, "Invalid/empty CPU model name");
goto out;
}
name = model_pieces[0];
features = model_pieces[1];
oc = x86_cpu_class_by_name(name);
if (oc == NULL) {
error_setg(&error, "Unable to find CPU definition: %s", name);
goto out;
}
cpu = X86_CPU(object_new(object_class_get_name(oc)));
x86_cpu_parse_featurestr(CPU(cpu), features, &error);
if (error) {
goto out;
}
out:
if (error != NULL) {
error_propagate(errp, error);
if (cpu) {
object_unref(OBJECT(cpu));
cpu = NULL;
}
}
g_strfreev(model_pieces);
return cpu;
}
X86CPU *cpu_x86_init(const char *cpu_model) X86CPU *cpu_x86_init(const char *cpu_model)
{ {
return X86_CPU(cpu_generic_init(TYPE_X86_CPU, cpu_model)); return X86_CPU(cpu_generic_init(TYPE_X86_CPU, cpu_model));
...@@ -2815,7 +2854,8 @@ static void mce_init(X86CPU *cpu) ...@@ -2815,7 +2854,8 @@ static void mce_init(X86CPU *cpu)
if (((cenv->cpuid_version >> 8) & 0xf) >= 6 if (((cenv->cpuid_version >> 8) & 0xf) >= 6
&& (cenv->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == && (cenv->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) ==
(CPUID_MCE | CPUID_MCA)) { (CPUID_MCE | CPUID_MCA)) {
cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF; cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF |
(cpu->enable_lmce ? MCG_LMCE_P : 0);
cenv->mcg_ctl = ~(uint64_t)0; cenv->mcg_ctl = ~(uint64_t)0;
for (bank = 0; bank < MCE_BANKS_DEF; bank++) { for (bank = 0; bank < MCE_BANKS_DEF; bank++) {
cenv->mce_banks[bank * 4] = ~(uint64_t)0; cenv->mce_banks[bank * 4] = ~(uint64_t)0;
...@@ -3262,6 +3302,7 @@ static Property x86_cpu_properties[] = { ...@@ -3262,6 +3302,7 @@ static Property x86_cpu_properties[] = {
DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0), DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0),
DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor_id), DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor_id),
DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true),
DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false),
DEFINE_PROP_END_OF_LIST() DEFINE_PROP_END_OF_LIST()
}; };
......
...@@ -292,6 +292,7 @@ ...@@ -292,6 +292,7 @@
#define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */ #define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */
#define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */ #define MCG_SER_P (1ULL<<24) /* MCA recovery/new status bits */
#define MCG_LMCE_P (1ULL<<27) /* Local Machine Check Supported */
#define MCE_CAP_DEF (MCG_CTL_P|MCG_SER_P) #define MCE_CAP_DEF (MCG_CTL_P|MCG_SER_P)
#define MCE_BANKS_DEF 10 #define MCE_BANKS_DEF 10
...@@ -301,6 +302,9 @@ ...@@ -301,6 +302,9 @@
#define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */ #define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */
#define MCG_STATUS_EIPV (1ULL<<1) /* ip points to correct instruction */ #define MCG_STATUS_EIPV (1ULL<<1) /* ip points to correct instruction */
#define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */ #define MCG_STATUS_MCIP (1ULL<<2) /* machine check in progress */
#define MCG_STATUS_LMCE (1ULL<<3) /* Local MCE signaled */
#define MCG_EXT_CTL_LMCE_EN (1ULL<<0) /* Local MCE enabled */
#define MCI_STATUS_VAL (1ULL<<63) /* valid error */ #define MCI_STATUS_VAL (1ULL<<63) /* valid error */
#define MCI_STATUS_OVER (1ULL<<62) /* previous errors lost */ #define MCI_STATUS_OVER (1ULL<<62) /* previous errors lost */
...@@ -328,6 +332,10 @@ ...@@ -328,6 +332,10 @@
#define MSR_TSC_ADJUST 0x0000003b #define MSR_TSC_ADJUST 0x0000003b
#define MSR_IA32_TSCDEADLINE 0x6e0 #define MSR_IA32_TSCDEADLINE 0x6e0
#define FEATURE_CONTROL_LOCKED (1<<0)
#define FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX (1<<2)
#define FEATURE_CONTROL_LMCE (1<<20)
#define MSR_P6_PERFCTR0 0xc1 #define MSR_P6_PERFCTR0 0xc1
#define MSR_IA32_SMBASE 0x9e #define MSR_IA32_SMBASE 0x9e
...@@ -343,6 +351,7 @@ ...@@ -343,6 +351,7 @@
#define MSR_MCG_CAP 0x179 #define MSR_MCG_CAP 0x179
#define MSR_MCG_STATUS 0x17a #define MSR_MCG_STATUS 0x17a
#define MSR_MCG_CTL 0x17b #define MSR_MCG_CTL 0x17b
#define MSR_MCG_EXT_CTL 0x4d0
#define MSR_P6_EVNTSEL0 0x186 #define MSR_P6_EVNTSEL0 0x186
...@@ -440,6 +449,9 @@ typedef enum FeatureWord { ...@@ -440,6 +449,9 @@ typedef enum FeatureWord {
FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */ FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */
FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */ FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */ FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */
FEAT_HYPERV_EAX, /* CPUID[4000_0003].EAX */
FEAT_HYPERV_EBX, /* CPUID[4000_0003].EBX */
FEAT_HYPERV_EDX, /* CPUID[4000_0003].EDX */
FEAT_SVM, /* CPUID[8000_000A].EDX */ FEAT_SVM, /* CPUID[8000_000A].EDX */
FEAT_XSAVE, /* CPUID[EAX=0xd,ECX=1].EAX */ FEAT_XSAVE, /* CPUID[EAX=0xd,ECX=1].EAX */
FEAT_6_EAX, /* CPUID[6].EAX */ FEAT_6_EAX, /* CPUID[6].EAX */
...@@ -1111,6 +1123,7 @@ typedef struct CPUX86State { ...@@ -1111,6 +1123,7 @@ typedef struct CPUX86State {
uint64_t mcg_cap; uint64_t mcg_cap;
uint64_t mcg_ctl; uint64_t mcg_ctl;
uint64_t mcg_ext_ctl;
uint64_t mce_banks[MCE_BANKS_DEF*4]; uint64_t mce_banks[MCE_BANKS_DEF*4];
uint64_t tsc_aux; uint64_t tsc_aux;
...@@ -1178,6 +1191,12 @@ struct X86CPU { ...@@ -1178,6 +1191,12 @@ struct X86CPU {
*/ */
bool enable_pmu; bool enable_pmu;
/* LMCE support can be enabled/disabled via cpu option 'lmce=on/off'. It is
* disabled by default to avoid breaking migration between QEMU with
* different LMCE configurations.
*/
bool enable_lmce;
/* Compatibility bits for old machine types: */ /* Compatibility bits for old machine types: */
bool enable_cpuid_0xb; bool enable_cpuid_0xb;
...@@ -1234,7 +1253,6 @@ void x86_cpu_exec_enter(CPUState *cpu); ...@@ -1234,7 +1253,6 @@ void x86_cpu_exec_enter(CPUState *cpu);
void x86_cpu_exec_exit(CPUState *cpu); void x86_cpu_exec_exit(CPUState *cpu);
X86CPU *cpu_x86_init(const char *cpu_model); X86CPU *cpu_x86_init(const char *cpu_model);
X86CPU *cpu_x86_create(const char *cpu_model, Error **errp);
void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf); void x86_cpu_list(FILE *f, fprintf_function cpu_fprintf);
int cpu_x86_support_mca_broadcast(CPUX86State *env); int cpu_x86_support_mca_broadcast(CPUX86State *env);
......
...@@ -106,6 +106,8 @@ static int has_xsave; ...@@ -106,6 +106,8 @@ static int has_xsave;
static int has_xcrs; static int has_xcrs;
static int has_pit_state2; static int has_pit_state2;
static bool has_msr_mcg_ext_ctl;
static struct kvm_cpuid2 *cpuid_cache; static struct kvm_cpuid2 *cpuid_cache;
int kvm_has_pit_state2(void) int kvm_has_pit_state2(void)
...@@ -382,10 +384,12 @@ static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap, ...@@ -382,10 +384,12 @@ static int kvm_get_mce_cap_supported(KVMState *s, uint64_t *mce_cap,
static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code)
{ {
CPUState *cs = CPU(cpu);
CPUX86State *env = &cpu->env; CPUX86State *env = &cpu->env;
uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN | uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN |
MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S; MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S;
uint64_t mcg_status = MCG_STATUS_MCIP; uint64_t mcg_status = MCG_STATUS_MCIP;
int flags = 0;
if (code == BUS_MCEERR_AR) { if (code == BUS_MCEERR_AR) {
status |= MCI_STATUS_AR | 0x134; status |= MCI_STATUS_AR | 0x134;
...@@ -394,10 +398,19 @@ static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code) ...@@ -394,10 +398,19 @@ static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code)
status |= 0xc0; status |= 0xc0;
mcg_status |= MCG_STATUS_RIPV; mcg_status |= MCG_STATUS_RIPV;
} }
flags = cpu_x86_support_mca_broadcast(env) ? MCE_INJECT_BROADCAST : 0;
/* We need to read back the value of MSR_EXT_MCG_CTL that was set by the
* guest kernel back into env->mcg_ext_ctl.
*/
cpu_synchronize_state(cs);
if (env->mcg_ext_ctl & MCG_EXT_CTL_LMCE_EN) {
mcg_status |= MCG_STATUS_LMCE;
flags = 0;
}
cpu_x86_inject_mce(NULL, cpu, 9, status, mcg_status, paddr, cpu_x86_inject_mce(NULL, cpu, 9, status, mcg_status, paddr,
(MCM_ADDR_PHYS << 6) | 0xc, (MCM_ADDR_PHYS << 6) | 0xc, flags);
cpu_x86_support_mca_broadcast(env) ?
MCE_INJECT_BROADCAST : 0);
} }
static void hardware_memory_error(void) static void hardware_memory_error(void)
...@@ -566,7 +579,9 @@ static int kvm_arch_set_tsc_khz(CPUState *cs) ...@@ -566,7 +579,9 @@ static int kvm_arch_set_tsc_khz(CPUState *cs)
-ENOTSUP; -ENOTSUP;
if (cur_freq <= 0 || cur_freq != env->tsc_khz) { if (cur_freq <= 0 || cur_freq != env->tsc_khz) {
error_report("warning: TSC frequency mismatch between " error_report("warning: TSC frequency mismatch between "
"VM and host, and TSC scaling unavailable"); "VM (%" PRId64 " kHz) and host (%d kHz), "
"and TSC scaling unavailable",
env->tsc_khz, cur_freq);
return r; return r;
} }
} }
...@@ -574,6 +589,64 @@ static int kvm_arch_set_tsc_khz(CPUState *cs) ...@@ -574,6 +589,64 @@ static int kvm_arch_set_tsc_khz(CPUState *cs)
return 0; return 0;
} }
static int hyperv_handle_properties(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
CPUX86State *env = &cpu->env;
if (cpu->hyperv_relaxed_timing) {
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE;
}
if (cpu->hyperv_vapic) {
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE;
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_APIC_ACCESS_AVAILABLE;
has_msr_hv_vapic = true;
}
if (cpu->hyperv_time &&
kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) > 0) {
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_HYPERCALL_AVAILABLE;
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_TIME_REF_COUNT_AVAILABLE;
env->features[FEAT_HYPERV_EAX] |= 0x200;
has_msr_hv_tsc = true;
}
if (cpu->hyperv_crash && has_msr_hv_crash) {
env->features[FEAT_HYPERV_EDX] |= HV_X64_GUEST_CRASH_MSR_AVAILABLE;
}
env->features[FEAT_HYPERV_EDX] |= HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE;
if (cpu->hyperv_reset && has_msr_hv_reset) {
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_RESET_AVAILABLE;
}
if (cpu->hyperv_vpindex && has_msr_hv_vpindex) {
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_VP_INDEX_AVAILABLE;
}
if (cpu->hyperv_runtime && has_msr_hv_runtime) {
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_VP_RUNTIME_AVAILABLE;
}
if (cpu->hyperv_synic) {
int sint;
if (!has_msr_hv_synic ||
kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0)) {
fprintf(stderr, "Hyper-V SynIC is not supported by kernel\n");
return -ENOSYS;
}
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_SYNIC_AVAILABLE;
env->msr_hv_synic_version = HV_SYNIC_VERSION_1;
for (sint = 0; sint < ARRAY_SIZE(env->msr_hv_synic_sint); sint++) {
env->msr_hv_synic_sint[sint] = HV_SYNIC_SINT_MASKED;
}
}
if (cpu->hyperv_stimer) {
if (!has_msr_hv_stimer) {
fprintf(stderr, "Hyper-V timers aren't supported by kernel\n");
return -ENOSYS;
}
env->features[FEAT_HYPERV_EAX] |= HV_X64_MSR_SYNTIMER_AVAILABLE;
}
return 0;
}
static Error *invtsc_mig_blocker; static Error *invtsc_mig_blocker;
#define KVM_MAX_CPUID_ENTRIES 100 #define KVM_MAX_CPUID_ENTRIES 100
...@@ -633,56 +706,14 @@ int kvm_arch_init_vcpu(CPUState *cs) ...@@ -633,56 +706,14 @@ int kvm_arch_init_vcpu(CPUState *cs)
c = &cpuid_data.entries[cpuid_i++]; c = &cpuid_data.entries[cpuid_i++];
c->function = HYPERV_CPUID_FEATURES; c->function = HYPERV_CPUID_FEATURES;
if (cpu->hyperv_relaxed_timing) { r = hyperv_handle_properties(cs);
c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE; if (r) {
} return r;
if (cpu->hyperv_vapic) {
c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE;
c->eax |= HV_X64_MSR_APIC_ACCESS_AVAILABLE;
has_msr_hv_vapic = true;
}
if (cpu->hyperv_time &&
kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) > 0) {
c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE;
c->eax |= HV_X64_MSR_TIME_REF_COUNT_AVAILABLE;
c->eax |= 0x200;
has_msr_hv_tsc = true;
}
if (cpu->hyperv_crash && has_msr_hv_crash) {
c->edx |= HV_X64_GUEST_CRASH_MSR_AVAILABLE;
}
c->edx |= HV_X64_CPU_DYNAMIC_PARTITIONING_AVAILABLE;
if (cpu->hyperv_reset && has_msr_hv_reset) {
c->eax |= HV_X64_MSR_RESET_AVAILABLE;
}
if (cpu->hyperv_vpindex && has_msr_hv_vpindex) {
c->eax |= HV_X64_MSR_VP_INDEX_AVAILABLE;
}
if (cpu->hyperv_runtime && has_msr_hv_runtime) {
c->eax |= HV_X64_MSR_VP_RUNTIME_AVAILABLE;
} }
if (cpu->hyperv_synic) { c->eax = env->features[FEAT_HYPERV_EAX];
int sint; c->ebx = env->features[FEAT_HYPERV_EBX];
c->edx = env->features[FEAT_HYPERV_EDX];
if (!has_msr_hv_synic ||
kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0)) {
fprintf(stderr, "Hyper-V SynIC is not supported by kernel\n");
return -ENOSYS;
}
c->eax |= HV_X64_MSR_SYNIC_AVAILABLE;
env->msr_hv_synic_version = HV_SYNIC_VERSION_1;
for (sint = 0; sint < ARRAY_SIZE(env->msr_hv_synic_sint); sint++) {
env->msr_hv_synic_sint[sint] = HV_SYNIC_SINT_MASKED;
}
}
if (cpu->hyperv_stimer) {
if (!has_msr_hv_stimer) {
fprintf(stderr, "Hyper-V timers aren't supported by kernel\n");
return -ENOSYS;
}
c->eax |= HV_X64_MSR_SYNTIMER_AVAILABLE;
}
c = &cpuid_data.entries[cpuid_i++]; c = &cpuid_data.entries[cpuid_i++];
c->function = HYPERV_CPUID_ENLIGHTMENT_INFO; c->function = HYPERV_CPUID_ENLIGHTMENT_INFO;
if (cpu->hyperv_relaxed_timing) { if (cpu->hyperv_relaxed_timing) {
...@@ -865,6 +896,10 @@ int kvm_arch_init_vcpu(CPUState *cs) ...@@ -865,6 +896,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
unsupported_caps = env->mcg_cap & ~(mcg_cap | MCG_CAP_BANKS_MASK); unsupported_caps = env->mcg_cap & ~(mcg_cap | MCG_CAP_BANKS_MASK);
if (unsupported_caps) { if (unsupported_caps) {
if (unsupported_caps & MCG_LMCE_P) {
error_report("kvm: LMCE not supported");
return -ENOTSUP;
}
error_report("warning: Unsupported MCG_CAP bits: 0x%" PRIx64, error_report("warning: Unsupported MCG_CAP bits: 0x%" PRIx64,
unsupported_caps); unsupported_caps);
} }
...@@ -885,6 +920,10 @@ int kvm_arch_init_vcpu(CPUState *cs) ...@@ -885,6 +920,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
!!(c->ecx & CPUID_EXT_SMX); !!(c->ecx & CPUID_EXT_SMX);
} }
if (env->mcg_cap & MCG_LMCE_P) {
has_msr_mcg_ext_ctl = has_msr_feature_control = true;
}
c = cpuid_find_entry(&cpuid_data.cpuid, 0x80000007, 0); c = cpuid_find_entry(&cpuid_data.cpuid, 0x80000007, 0);
if (c && (c->edx & 1<<8) && invtsc_mig_blocker == NULL) { if (c && (c->edx & 1<<8) && invtsc_mig_blocker == NULL) {
/* for migration */ /* for migration */
...@@ -1705,6 +1744,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level) ...@@ -1705,6 +1744,9 @@ static int kvm_put_msrs(X86CPU *cpu, int level)
kvm_msr_entry_add(cpu, MSR_MCG_STATUS, env->mcg_status); kvm_msr_entry_add(cpu, MSR_MCG_STATUS, env->mcg_status);
kvm_msr_entry_add(cpu, MSR_MCG_CTL, env->mcg_ctl); kvm_msr_entry_add(cpu, MSR_MCG_CTL, env->mcg_ctl);
if (has_msr_mcg_ext_ctl) {
kvm_msr_entry_add(cpu, MSR_MCG_EXT_CTL, env->mcg_ext_ctl);
}
for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) { for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, env->mce_banks[i]); kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, env->mce_banks[i]);
} }
...@@ -2008,6 +2050,9 @@ static int kvm_get_msrs(X86CPU *cpu) ...@@ -2008,6 +2050,9 @@ static int kvm_get_msrs(X86CPU *cpu)
if (env->mcg_cap) { if (env->mcg_cap) {
kvm_msr_entry_add(cpu, MSR_MCG_STATUS, 0); kvm_msr_entry_add(cpu, MSR_MCG_STATUS, 0);
kvm_msr_entry_add(cpu, MSR_MCG_CTL, 0); kvm_msr_entry_add(cpu, MSR_MCG_CTL, 0);
if (has_msr_mcg_ext_ctl) {
kvm_msr_entry_add(cpu, MSR_MCG_EXT_CTL, 0);
}
for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) { for (i = 0; i < (env->mcg_cap & 0xff) * 4; i++) {
kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, 0); kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, 0);
} }
...@@ -2136,6 +2181,9 @@ static int kvm_get_msrs(X86CPU *cpu) ...@@ -2136,6 +2181,9 @@ static int kvm_get_msrs(X86CPU *cpu)
case MSR_MCG_CTL: case MSR_MCG_CTL:
env->mcg_ctl = msrs[i].data; env->mcg_ctl = msrs[i].data;
break; break;
case MSR_MCG_EXT_CTL:
env->mcg_ext_ctl = msrs[i].data;
break;
case MSR_IA32_MISC_ENABLE: case MSR_IA32_MISC_ENABLE:
env->msr_ia32_misc_enable = msrs[i].data; env->msr_ia32_misc_enable = msrs[i].data;
break; break;
......
...@@ -896,6 +896,24 @@ static const VMStateDescription vmstate_tsc_khz = { ...@@ -896,6 +896,24 @@ static const VMStateDescription vmstate_tsc_khz = {
} }
}; };
static bool mcg_ext_ctl_needed(void *opaque)
{
X86CPU *cpu = opaque;
CPUX86State *env = &cpu->env;
return cpu->enable_lmce && env->mcg_ext_ctl;
}
static const VMStateDescription vmstate_mcg_ext_ctl = {
.name = "cpu/mcg_ext_ctl",
.version_id = 1,
.minimum_version_id = 1,
.needed = mcg_ext_ctl_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT64(env.mcg_ext_ctl, X86CPU),
VMSTATE_END_OF_LIST()
}
};
VMStateDescription vmstate_x86_cpu = { VMStateDescription vmstate_x86_cpu = {
.name = "cpu", .name = "cpu",
.version_id = 12, .version_id = 12,
...@@ -1022,6 +1040,7 @@ VMStateDescription vmstate_x86_cpu = { ...@@ -1022,6 +1040,7 @@ VMStateDescription vmstate_x86_cpu = {
#ifdef TARGET_X86_64 #ifdef TARGET_X86_64
&vmstate_pkru, &vmstate_pkru,
#endif #endif
&vmstate_mcg_ext_ctl,
NULL NULL
} }
}; };
...@@ -101,9 +101,11 @@ static void cpu_sparc_disas_set_info(CPUState *cpu, disassemble_info *info) ...@@ -101,9 +101,11 @@ static void cpu_sparc_disas_set_info(CPUState *cpu, disassemble_info *info)
#endif #endif
} }
static void sparc_cpu_parse_features(CPUState *cs, char *features,
Error **errp);
static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model) static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model)
{ {
CPUClass *cc = CPU_GET_CLASS(cpu);
CPUSPARCState *env = &cpu->env; CPUSPARCState *env = &cpu->env;
char *s = g_strdup(cpu_model); char *s = g_strdup(cpu_model);
char *featurestr, *name = strtok(s, ","); char *featurestr, *name = strtok(s, ",");
...@@ -119,7 +121,7 @@ static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model) ...@@ -119,7 +121,7 @@ static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model)
memcpy(env->def, def, sizeof(*def)); memcpy(env->def, def, sizeof(*def));
featurestr = strtok(NULL, ","); featurestr = strtok(NULL, ",");
cc->parse_features(CPU(cpu), featurestr, &err); sparc_cpu_parse_features(CPU(cpu), featurestr, &err);
g_free(s); g_free(s);
if (err) { if (err) {
error_report_err(err); error_report_err(err);
...@@ -840,7 +842,6 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) ...@@ -840,7 +842,6 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data)
scc->parent_reset = cc->reset; scc->parent_reset = cc->reset;
cc->reset = sparc_cpu_reset; cc->reset = sparc_cpu_reset;
cc->parse_features = sparc_cpu_parse_features;
cc->has_work = sparc_cpu_has_work; cc->has_work = sparc_cpu_has_work;
cc->do_interrupt = sparc_cpu_do_interrupt; cc->do_interrupt = sparc_cpu_do_interrupt;
cc->cpu_exec_interrupt = sparc_cpu_exec_interrupt; cc->cpu_exec_interrupt = sparc_cpu_exec_interrupt;
......
...@@ -2913,6 +2913,19 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size, ...@@ -2913,6 +2913,19 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size,
loc_pop(&loc); loc_pop(&loc);
} }
static int global_init_func(void *opaque, QemuOpts *opts, Error **errp)
{
GlobalProperty *g;
g = g_malloc0(sizeof(*g));
g->driver = qemu_opt_get(opts, "driver");
g->property = qemu_opt_get(opts, "property");
g->value = qemu_opt_get(opts, "value");
g->user_provided = true;
qdev_prop_register_global(g);
return 0;
}
int main(int argc, char **argv, char **envp) int main(int argc, char **argv, char **envp)
{ {
int i; int i;
...@@ -4435,14 +4448,10 @@ int main(int argc, char **argv, char **envp) ...@@ -4435,14 +4448,10 @@ int main(int argc, char **argv, char **envp)
exit (i == 1 ? 1 : 0); exit (i == 1 ? 1 : 0);
} }
if (machine_class->compat_props) { machine_register_compat_props(current_machine);
GlobalProperty *p;
for (i = 0; i < machine_class->compat_props->len; i++) { qemu_opts_foreach(qemu_find_opts("global"),
p = g_array_index(machine_class->compat_props, GlobalProperty *, i); global_init_func, NULL, NULL);
qdev_prop_register_global(p);
}
}
qemu_add_globals();
/* This checkpoint is required by replay to separate prior clock /* This checkpoint is required by replay to separate prior clock
reading from the other reads, because timer polling functions query reading from the other reads, because timer polling functions query
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册