mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-11-05 20:35:44 +00:00
x86 and machine queue, 2016-07-07
Highlights: * Improvements on global property error handling * Translate -cpu options to global properties * LMCE support -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJXfrSFAAoJECgHk2+YTcWmrxIP/2eROlseez3HqwNT8ZuPKQVz gsM1WaRr66ZscpNL7+zEA4CAIOTYnq+f3oYzKO2KT6HIBdVIdiU+c6B87xOVfpdf WwedDysT9ye073zPW8afiH4d0WHsVoluKfJvCFNynauB1a0eU2sKHS/wnZihOryh Cruen2SUd4qB14Wp+DXGwDlrJouaqvsqnss+UkYTeMGISQMZbBjbPW7S+gr7W1A5 LCsl0Y2hR3Agco5+8i+KBmfW9Z0+V99ACIq9v6Tf7Iz6s7QkizX1nPPKlpbZtDxz /HYQgYTCAHGGppdNb+wq32SUCMxDZVbwczdMQVEaSBMdfWhw+7QY37QZPWaSoyTE NrSQMrsAB5d1nZ3g4I7aeiVRYlmG9XH+kF0GSJ1p3wlnwllF7pLCQwWxKVTldXui 53ldm8ex27MtciHUCxCda0LhOlToIMDjupevCzsHfJgKvqaE4AmHVHfQmKq38xgi x6T0M6TR6UXD4beQuGWb/P7OJtZSsaMWZqYmPYNgR66cSrWFPAQv/dX+uG6cd2YT 8BVnmw4SY/PGoTaU1zNF1VeoL5Ldzr1EZwuliWqRbLoprzz3LG8yOnZag2XI2RIX EJRGHkSqxK1yJlrFGn+RqKjmRSvXXzWRCdTP8UcFPl8wZkmiXbkeX1sYeJwsXa81 UXZ3lDd8JF6LgIyE2wB7 =sBs3 -----END PGP SIGNATURE----- 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: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
a91a4e7d8c
16 changed files with 386 additions and 179 deletions
|
@ -1176,6 +1176,10 @@ static void machvirt_init(MachineState *machine)
|
|||
VirtGuestInfoState *guest_info_state = g_malloc0(sizeof *guest_info_state);
|
||||
VirtGuestInfo *guest_info = &guest_info_state->info;
|
||||
char **cpustr;
|
||||
ObjectClass *oc;
|
||||
const char *typename;
|
||||
CPUClass *cc;
|
||||
Error *err = NULL;
|
||||
bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0);
|
||||
|
||||
if (!cpu_model) {
|
||||
|
@ -1259,26 +1263,24 @@ static void machvirt_init(MachineState *machine)
|
|||
|
||||
create_fdt(vbi);
|
||||
|
||||
oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]);
|
||||
if (!oc) {
|
||||
error_report("Unable to find CPU definition");
|
||||
exit(1);
|
||||
}
|
||||
typename = object_class_get_name(oc);
|
||||
|
||||
/* convert -smp CPU options specified by the user into global props */
|
||||
cc = CPU_CLASS(oc);
|
||||
cc->parse_features(typename, cpustr[1], &err);
|
||||
g_strfreev(cpustr);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (n = 0; n < smp_cpus; n++) {
|
||||
ObjectClass *oc = cpu_class_by_name(TYPE_ARM_CPU, cpustr[0]);
|
||||
CPUClass *cc = CPU_CLASS(oc);
|
||||
Object *cpuobj;
|
||||
Error *err = NULL;
|
||||
char *cpuopts = g_strdup(cpustr[1]);
|
||||
|
||||
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 */
|
||||
cc->parse_features(CPU(cpuobj), cpuopts, &err);
|
||||
g_free(cpuopts);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
exit(1);
|
||||
}
|
||||
Object *cpuobj = object_new(typename);
|
||||
|
||||
if (!vms->secure) {
|
||||
object_property_set_bool(cpuobj, false, "has_el3", NULL);
|
||||
|
@ -1309,7 +1311,6 @@ static void machvirt_init(MachineState *machine)
|
|||
|
||||
object_property_set_bool(cpuobj, true, "realized", NULL);
|
||||
}
|
||||
g_strfreev(cpustr);
|
||||
fdt_add_timer_nodes(vbi, gic_version);
|
||||
fdt_add_cpu_nodes(vbi);
|
||||
fdt_add_psci_node(vbi);
|
||||
|
|
|
@ -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 = {
|
||||
.name = TYPE_MACHINE,
|
||||
.parent = TYPE_OBJECT,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* qdev property parsing and global properties
|
||||
* qdev property parsing
|
||||
* (parts specific for qemu-system-*)
|
||||
*
|
||||
* 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)
|
|||
}
|
||||
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,
|
|||
prop->used = true;
|
||||
object_property_parse(OBJECT(dev), prop->value, prop->property, &err);
|
||||
if (err != NULL) {
|
||||
assert(prop->user_provided);
|
||||
error_reportf_err(err, "Warning: global %s.%s=%s ignored: ",
|
||||
prop->driver, prop->property, prop->value);
|
||||
return;
|
||||
error_prepend(&err, "can't apply global %s.%s=%s: ",
|
||||
prop->driver, prop->property, prop->value);
|
||||
if (prop->errp) {
|
||||
error_propagate(prop->errp, err);
|
||||
} else {
|
||||
assert(prop->user_provided);
|
||||
error_reportf_err(err, "Warning: ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
66
hw/i386/pc.c
66
hw/i386/pc.c
|
@ -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)
|
||||
{
|
||||
X86CPU *cpu = NULL;
|
||||
Error *local_err = NULL;
|
||||
|
||||
cpu = cpu_x86_create(cpu_model, &local_err);
|
||||
if (local_err != NULL) {
|
||||
goto out;
|
||||
}
|
||||
cpu = X86_CPU(object_new(typename));
|
||||
|
||||
object_property_set_int(OBJECT(cpu), apic_id, "apic-id", &local_err);
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &local_err);
|
||||
|
||||
out:
|
||||
if (local_err) {
|
||||
error_propagate(errp, local_err);
|
||||
object_unref(OBJECT(cpu));
|
||||
|
@ -1065,7 +1061,8 @@ out:
|
|||
void pc_hot_add_cpu(const int64_t id, Error **errp)
|
||||
{
|
||||
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);
|
||||
Error *local_err = NULL;
|
||||
|
||||
|
@ -1093,7 +1090,9 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
|
|||
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) {
|
||||
error_propagate(errp, local_err);
|
||||
return;
|
||||
|
@ -1104,6 +1103,10 @@ void pc_hot_add_cpu(const int64_t id, Error **errp)
|
|||
void pc_cpus_init(PCMachineState *pcms)
|
||||
{
|
||||
int i;
|
||||
CPUClass *cc;
|
||||
ObjectClass *oc;
|
||||
const char *typename;
|
||||
gchar **model_pieces;
|
||||
X86CPU *cpu = NULL;
|
||||
MachineState *machine = MACHINE(pcms);
|
||||
|
||||
|
@ -1116,6 +1119,22 @@ void pc_cpus_init(PCMachineState *pcms)
|
|||
#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
|
||||
*
|
||||
* Limit for the APIC ID value, so that all
|
||||
|
@ -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->len++;
|
||||
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);
|
||||
pcms->possible_cpus->cpus[i].cpu = CPU(cpu);
|
||||
object_unref(OBJECT(cpu));
|
||||
|
@ -1147,6 +1166,34 @@ void pc_cpus_init(PCMachineState *pcms)
|
|||
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
|
||||
void pc_machine_done(Notifier *notifier, void *data)
|
||||
{
|
||||
|
@ -1174,6 +1221,7 @@ void pc_machine_done(Notifier *notifier, void *data)
|
|||
acpi_setup();
|
||||
if (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);
|
|||
int machine_phandle_start(MachineState *machine);
|
||||
bool machine_dump_guest_core(MachineState *machine);
|
||||
bool machine_mem_merge(MachineState *machine);
|
||||
void machine_register_compat_props(MachineState *machine);
|
||||
|
||||
/**
|
||||
* CPUArchId:
|
||||
|
|
|
@ -259,6 +259,9 @@ struct PropertyInfo {
|
|||
* @user_provided: Set to true if property comes from user-provided config
|
||||
* (command-line or config file).
|
||||
* @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 {
|
||||
const char *driver;
|
||||
|
@ -266,6 +269,7 @@ typedef struct GlobalProperty {
|
|||
const char *value;
|
||||
bool user_provided;
|
||||
bool used;
|
||||
Error **errp;
|
||||
} GlobalProperty;
|
||||
|
||||
/*** Board API. This should go away once we have a machine config file. ***/
|
||||
|
|
|
@ -12,7 +12,6 @@ void qemu_add_opts(QemuOptsList *list);
|
|||
void qemu_add_drive_opts(QemuOptsList *list);
|
||||
int qemu_set_option(const char *str);
|
||||
int qemu_global_option(const char *str);
|
||||
void qemu_add_globals(void);
|
||||
|
||||
void qemu_config_write(FILE *fp);
|
||||
int qemu_config_parse(FILE *fp, QemuOptsList **lists, const char *fname);
|
||||
|
|
|
@ -134,7 +134,7 @@ typedef struct CPUClass {
|
|||
/*< public >*/
|
||||
|
||||
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);
|
||||
int reset_dump_flags;
|
||||
|
|
39
qom/cpu.c
39
qom/cpu.c
|
@ -28,6 +28,7 @@
|
|||
#include "exec/log.h"
|
||||
#include "qemu/error-report.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/qdev-properties.h"
|
||||
|
||||
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)
|
||||
{
|
||||
char *str, *name, *featurestr;
|
||||
CPUState *cpu;
|
||||
CPUState *cpu = NULL;
|
||||
ObjectClass *oc;
|
||||
CPUClass *cc;
|
||||
Error *err = NULL;
|
||||
|
@ -60,16 +61,18 @@ CPUState *cpu_generic_init(const char *typename, const char *cpu_model)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
cpu = CPU(object_new(object_class_get_name(oc)));
|
||||
cc = CPU_GET_CLASS(cpu);
|
||||
|
||||
cc = CPU_CLASS(oc);
|
||||
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);
|
||||
if (err != NULL) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
cpu = CPU(object_new(object_class_get_name(oc)));
|
||||
object_property_set_bool(OBJECT(cpu), true, "realized", &err);
|
||||
|
||||
out:
|
||||
|
@ -282,25 +285,37 @@ static ObjectClass *cpu_common_class_by_name(const char *cpu_model)
|
|||
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)
|
||||
{
|
||||
char *featurestr; /* Single "key=value" string being parsed */
|
||||
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;
|
||||
|
||||
while (featurestr) {
|
||||
val = strchr(featurestr, '=');
|
||||
if (val) {
|
||||
GlobalProperty *prop = g_new0(typeof(*prop), 1);
|
||||
*val = 0;
|
||||
val++;
|
||||
object_property_parse(OBJECT(cpu), val, featurestr, &err);
|
||||
if (err) {
|
||||
error_propagate(errp, err);
|
||||
return;
|
||||
}
|
||||
prop->driver = typename;
|
||||
prop->property = g_strdup(featurestr);
|
||||
prop->value = g_strdup(val);
|
||||
prop->errp = &error_fatal;
|
||||
qdev_prop_register_global(prop);
|
||||
} else {
|
||||
error_setg(errp, "Expected key=value format, found %s.",
|
||||
featurestr);
|
||||
|
|
|
@ -245,6 +245,47 @@ static const char *kvm_feature_name[] = {
|
|||
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[] = {
|
||||
"npt", "lbrv", "svm_lock", "nrip_save",
|
||||
"tsc_scale", "vmcb_clean", "flushbyasid", "decodeassists",
|
||||
|
@ -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 | \
|
||||
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_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:
|
||||
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 */
|
||||
#define TCG_7_0_ECX_FEATURES (CPUID_7_0_ECX_PKU | CPUID_7_0_ECX_OSPKE)
|
||||
#define TCG_APM_FEATURES 0
|
||||
|
@ -411,6 +453,18 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = {
|
|||
.cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX,
|
||||
.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_names = svm_feature_name,
|
||||
.cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX,
|
||||
|
@ -1493,6 +1547,17 @@ static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w,
|
|||
|
||||
#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)
|
||||
{
|
||||
uint32_t eax = 0, ebx = 0, ecx = 0, edx = 0;
|
||||
|
@ -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_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 (lmce_supported()) {
|
||||
object_property_set_bool(OBJECT(cpu), true, "lmce", &error_abort);
|
||||
}
|
||||
}
|
||||
|
||||
object_property_set_bool(OBJECT(cpu), true, "pmu", &error_abort);
|
||||
|
@ -1957,12 +2026,17 @@ static FeatureWordArray minus_features = { 0 };
|
|||
|
||||
/* 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)
|
||||
{
|
||||
X86CPU *cpu = X86_CPU(cs);
|
||||
char *featurestr; /* Single 'key=value" string being parsed */
|
||||
Error *local_err = NULL;
|
||||
static bool cpu_globals_initialized;
|
||||
|
||||
if (cpu_globals_initialized) {
|
||||
return;
|
||||
}
|
||||
cpu_globals_initialized = true;
|
||||
|
||||
if (!features) {
|
||||
return;
|
||||
|
@ -1974,6 +2048,8 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
|
|||
const char *name;
|
||||
const char *val = NULL;
|
||||
char *eq = NULL;
|
||||
char num[32];
|
||||
GlobalProperty *prop;
|
||||
|
||||
/* Compatibility syntax: */
|
||||
if (featurestr[0] == '+') {
|
||||
|
@ -1999,7 +2075,6 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
|
|||
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);
|
||||
|
@ -2012,7 +2087,12 @@ static void x86_cpu_parse_featurestr(CPUState *cs, char *features,
|
|||
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) {
|
||||
|
@ -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)
|
||||
{
|
||||
return X86_CPU(cpu_generic_init(TYPE_X86_CPU, cpu_model));
|
||||
|
@ -2815,7 +2854,8 @@ static void mce_init(X86CPU *cpu)
|
|||
if (((cenv->cpuid_version >> 8) & 0xf) >= 6
|
||||
&& (cenv->features[FEAT_1_EDX] & (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;
|
||||
for (bank = 0; bank < MCE_BANKS_DEF; bank++) {
|
||||
cenv->mce_banks[bank * 4] = ~(uint64_t)0;
|
||||
|
@ -3262,6 +3302,7 @@ static Property x86_cpu_properties[] = {
|
|||
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_BOOL("lmce", X86CPU, enable_lmce, false),
|
||||
DEFINE_PROP_END_OF_LIST()
|
||||
};
|
||||
|
||||
|
|
|
@ -292,6 +292,7 @@
|
|||
|
||||
#define MCG_CTL_P (1ULL<<8) /* MCG_CAP register available */
|
||||
#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_BANKS_DEF 10
|
||||
|
@ -301,6 +302,9 @@
|
|||
#define MCG_STATUS_RIPV (1ULL<<0) /* restart ip valid */
|
||||
#define MCG_STATUS_EIPV (1ULL<<1) /* ip points to correct instruction */
|
||||
#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_OVER (1ULL<<62) /* previous errors lost */
|
||||
|
@ -328,6 +332,10 @@
|
|||
#define MSR_TSC_ADJUST 0x0000003b
|
||||
#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_IA32_SMBASE 0x9e
|
||||
|
@ -343,6 +351,7 @@
|
|||
#define MSR_MCG_CAP 0x179
|
||||
#define MSR_MCG_STATUS 0x17a
|
||||
#define MSR_MCG_CTL 0x17b
|
||||
#define MSR_MCG_EXT_CTL 0x4d0
|
||||
|
||||
#define MSR_P6_EVNTSEL0 0x186
|
||||
|
||||
|
@ -440,6 +449,9 @@ typedef enum FeatureWord {
|
|||
FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */
|
||||
FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
|
||||
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_XSAVE, /* CPUID[EAX=0xd,ECX=1].EAX */
|
||||
FEAT_6_EAX, /* CPUID[6].EAX */
|
||||
|
@ -1111,6 +1123,7 @@ typedef struct CPUX86State {
|
|||
|
||||
uint64_t mcg_cap;
|
||||
uint64_t mcg_ctl;
|
||||
uint64_t mcg_ext_ctl;
|
||||
uint64_t mce_banks[MCE_BANKS_DEF*4];
|
||||
|
||||
uint64_t tsc_aux;
|
||||
|
@ -1178,6 +1191,12 @@ struct X86CPU {
|
|||
*/
|
||||
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: */
|
||||
bool enable_cpuid_0xb;
|
||||
|
||||
|
@ -1234,7 +1253,6 @@ void x86_cpu_exec_enter(CPUState *cpu);
|
|||
void x86_cpu_exec_exit(CPUState *cpu);
|
||||
|
||||
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);
|
||||
int cpu_x86_support_mca_broadcast(CPUX86State *env);
|
||||
|
||||
|
|
|
@ -106,6 +106,8 @@ static int has_xsave;
|
|||
static int has_xcrs;
|
||||
static int has_pit_state2;
|
||||
|
||||
static bool has_msr_mcg_ext_ctl;
|
||||
|
||||
static struct kvm_cpuid2 *cpuid_cache;
|
||||
|
||||
int kvm_has_pit_state2(void)
|
||||
|
@ -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)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
CPUX86State *env = &cpu->env;
|
||||
uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN |
|
||||
MCI_STATUS_MISCV | MCI_STATUS_ADDRV | MCI_STATUS_S;
|
||||
uint64_t mcg_status = MCG_STATUS_MCIP;
|
||||
int flags = 0;
|
||||
|
||||
if (code == BUS_MCEERR_AR) {
|
||||
status |= MCI_STATUS_AR | 0x134;
|
||||
|
@ -394,10 +398,19 @@ static void kvm_mce_inject(X86CPU *cpu, hwaddr paddr, int code)
|
|||
status |= 0xc0;
|
||||
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,
|
||||
(MCM_ADDR_PHYS << 6) | 0xc,
|
||||
cpu_x86_support_mca_broadcast(env) ?
|
||||
MCE_INJECT_BROADCAST : 0);
|
||||
(MCM_ADDR_PHYS << 6) | 0xc, flags);
|
||||
}
|
||||
|
||||
static void hardware_memory_error(void)
|
||||
|
@ -566,7 +579,9 @@ static int kvm_arch_set_tsc_khz(CPUState *cs)
|
|||
-ENOTSUP;
|
||||
if (cur_freq <= 0 || cur_freq != env->tsc_khz) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -574,6 +589,64 @@ static int kvm_arch_set_tsc_khz(CPUState *cs)
|
|||
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;
|
||||
|
||||
#define KVM_MAX_CPUID_ENTRIES 100
|
||||
|
@ -633,56 +706,14 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
|||
|
||||
c = &cpuid_data.entries[cpuid_i++];
|
||||
c->function = HYPERV_CPUID_FEATURES;
|
||||
if (cpu->hyperv_relaxed_timing) {
|
||||
c->eax |= HV_X64_MSR_HYPERCALL_AVAILABLE;
|
||||
r = hyperv_handle_properties(cs);
|
||||
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) {
|
||||
int sint;
|
||||
c->eax = env->features[FEAT_HYPERV_EAX];
|
||||
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->function = HYPERV_CPUID_ENLIGHTMENT_INFO;
|
||||
if (cpu->hyperv_relaxed_timing) {
|
||||
|
@ -865,6 +896,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
|||
|
||||
unsupported_caps = env->mcg_cap & ~(mcg_cap | MCG_CAP_BANKS_MASK);
|
||||
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,
|
||||
unsupported_caps);
|
||||
}
|
||||
|
@ -885,6 +920,10 @@ int kvm_arch_init_vcpu(CPUState *cs)
|
|||
!!(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);
|
||||
if (c && (c->edx & 1<<8) && invtsc_mig_blocker == NULL) {
|
||||
/* for migration */
|
||||
|
@ -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_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++) {
|
||||
kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, env->mce_banks[i]);
|
||||
}
|
||||
|
@ -2008,6 +2050,9 @@ static int kvm_get_msrs(X86CPU *cpu)
|
|||
if (env->mcg_cap) {
|
||||
kvm_msr_entry_add(cpu, MSR_MCG_STATUS, 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++) {
|
||||
kvm_msr_entry_add(cpu, MSR_MC0_CTL + i, 0);
|
||||
}
|
||||
|
@ -2136,6 +2181,9 @@ static int kvm_get_msrs(X86CPU *cpu)
|
|||
case MSR_MCG_CTL:
|
||||
env->mcg_ctl = msrs[i].data;
|
||||
break;
|
||||
case MSR_MCG_EXT_CTL:
|
||||
env->mcg_ext_ctl = msrs[i].data;
|
||||
break;
|
||||
case MSR_IA32_MISC_ENABLE:
|
||||
env->msr_ia32_misc_enable = msrs[i].data;
|
||||
break;
|
||||
|
|
|
@ -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 = {
|
||||
.name = "cpu",
|
||||
.version_id = 12,
|
||||
|
@ -1022,6 +1040,7 @@ VMStateDescription vmstate_x86_cpu = {
|
|||
#ifdef TARGET_X86_64
|
||||
&vmstate_pkru,
|
||||
#endif
|
||||
&vmstate_mcg_ext_ctl,
|
||||
NULL
|
||||
}
|
||||
};
|
||||
|
|
|
@ -101,9 +101,11 @@ static void cpu_sparc_disas_set_info(CPUState *cpu, disassemble_info *info)
|
|||
#endif
|
||||
}
|
||||
|
||||
static void sparc_cpu_parse_features(CPUState *cs, char *features,
|
||||
Error **errp);
|
||||
|
||||
static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model)
|
||||
{
|
||||
CPUClass *cc = CPU_GET_CLASS(cpu);
|
||||
CPUSPARCState *env = &cpu->env;
|
||||
char *s = g_strdup(cpu_model);
|
||||
char *featurestr, *name = strtok(s, ",");
|
||||
|
@ -119,7 +121,7 @@ static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model)
|
|||
memcpy(env->def, def, sizeof(*def));
|
||||
|
||||
featurestr = strtok(NULL, ",");
|
||||
cc->parse_features(CPU(cpu), featurestr, &err);
|
||||
sparc_cpu_parse_features(CPU(cpu), featurestr, &err);
|
||||
g_free(s);
|
||||
if (err) {
|
||||
error_report_err(err);
|
||||
|
@ -840,7 +842,6 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data)
|
|||
scc->parent_reset = cc->reset;
|
||||
cc->reset = sparc_cpu_reset;
|
||||
|
||||
cc->parse_features = sparc_cpu_parse_features;
|
||||
cc->has_work = sparc_cpu_has_work;
|
||||
cc->do_interrupt = sparc_cpu_do_interrupt;
|
||||
cc->cpu_exec_interrupt = sparc_cpu_exec_interrupt;
|
||||
|
|
25
vl.c
25
vl.c
|
@ -2913,6 +2913,19 @@ static void set_memory_options(uint64_t *ram_slots, ram_addr_t *maxram_size,
|
|||
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 i;
|
||||
|
@ -4435,14 +4448,10 @@ int main(int argc, char **argv, char **envp)
|
|||
exit (i == 1 ? 1 : 0);
|
||||
}
|
||||
|
||||
if (machine_class->compat_props) {
|
||||
GlobalProperty *p;
|
||||
for (i = 0; i < machine_class->compat_props->len; i++) {
|
||||
p = g_array_index(machine_class->compat_props, GlobalProperty *, i);
|
||||
qdev_prop_register_global(p);
|
||||
}
|
||||
}
|
||||
qemu_add_globals();
|
||||
machine_register_compat_props(current_machine);
|
||||
|
||||
qemu_opts_foreach(qemu_find_opts("global"),
|
||||
global_init_func, NULL, NULL);
|
||||
|
||||
/* This checkpoint is required by replay to separate prior clock
|
||||
reading from the other reads, because timer polling functions query
|
||||
|
|
Loading…
Reference in a new issue