@@ -1963,6 +1963,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data)
/* BIOS ACPI tables: 128K. Other BIOS datastructures: less than 4K reported
* to be used at the moment, 32K should be enough for a while. */
pcmc->acpi_data_size = 0x20000 + 0x8000;
+ pcmc->save_tsc_khz = true;
mc->get_hotplug_handler = pc_get_hotpug_handler;
mc->cpu_index_to_socket_id = pc_cpu_index_to_socket_id;
mc->default_boot_order = "cad";
@@ -432,9 +432,11 @@ DEFINE_I440FX_MACHINE(v2_6, "pc-i440fx-2.6", NULL,
static void pc_i440fx_2_5_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_i440fx_2_6_machine_options(m);
m->alias = NULL;
m->is_default = 0;
+ pcmc->save_tsc_khz = false;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
}
@@ -360,8 +360,10 @@ DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL,
static void pc_q35_2_5_machine_options(MachineClass *m)
{
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(m);
pc_q35_2_6_machine_options(m);
m->alias = NULL;
+ pcmc->save_tsc_khz = false;
SET_MACHINE_COMPAT(m, PC_COMPAT_2_5);
}
@@ -120,6 +120,9 @@ struct PCMachineClass {
bool has_reserved_memory;
bool enforce_aligned_dimm;
bool broken_reserved_end;
+
+ /* TSC rate migration: */
+ bool save_tsc_khz;
};
#define TYPE_PC_MACHINE "generic-pc-machine"
@@ -1 +1 @@
-Subproject commit 01a84bea2d28a19d2405c1ecac4bdef17683cc0c
+Subproject commit 33fbe13a3e2a01e0ba1087a8feed801a0451db21
@@ -1728,7 +1728,7 @@ static void x86_cpuid_set_tsc_freq(Object *obj, Visitor *v, void *opaque,
return;
}
- cpu->env.tsc_khz = value / 1000;
+ cpu->env.tsc_khz = cpu->env.user_tsc_khz = value / 1000;
}
static void x86_cpuid_get_apic_id(Object *obj, Visitor *v, void *opaque,
@@ -978,6 +978,7 @@ typedef struct CPUX86State {
uint32_t sipi_vector;
bool tsc_valid;
int64_t tsc_khz;
+ int64_t user_tsc_khz; /* for sanity check only */
void *kvm_xsave_buf;
uint64_t mcg_cap;
@@ -2507,6 +2507,15 @@ int kvm_arch_put_registers(CPUState *cpu, int level)
}
}
+ if (level == KVM_PUT_FULL_STATE) {
+ /* We don't check for kvm_arch_set_tsc_khz() errors here,
+ * because TSC frequency mismatch shouldn't abort migration,
+ * unless the user explicitly asked for a more strict TSC
+ * setting (e.g. using an explicit "tsc-freq" option).
+ */
+ kvm_arch_set_tsc_khz(cpu);
+ }
+
ret = kvm_getput_regs(x86_cpu, 1);
if (ret < 0) {
return ret;
@@ -6,6 +6,8 @@
#include "cpu.h"
#include "sysemu/kvm.h"
+#include "qemu/error-report.h"
+
static const VMStateDescription vmstate_segment = {
.name = "segment",
.version_id = 1,
@@ -331,6 +333,13 @@ static int cpu_post_load(void *opaque, int version_id)
CPUX86State *env = &cpu->env;
int i;
+ if (env->tsc_khz && env->user_tsc_khz &&
+ env->tsc_khz != env->user_tsc_khz) {
+ error_report("Mismatch between user-specified TSC frequency and "
+ "migrated TSC frequency");
+ return -EINVAL;
+ }
+
/*
* Real mode guest segments register DPL should be zero.
* Older KVM version were setting it wrongly.
@@ -839,6 +848,26 @@ static const VMStateDescription vmstate_xss = {
}
};
+static bool tsc_khz_needed(void *opaque)
+{
+ X86CPU *cpu = opaque;
+ CPUX86State *env = &cpu->env;
+ MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
+ PCMachineClass *pcmc = PC_MACHINE_CLASS(mc);
+ return env->tsc_khz && pcmc->save_tsc_khz;
+}
+
+static const VMStateDescription vmstate_tsc_khz = {
+ .name = "cpu/tsc_khz",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = tsc_khz_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_INT64(env.tsc_khz, X86CPU),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
VMStateDescription vmstate_x86_cpu = {
.name = "cpu",
.version_id = 12,
@@ -961,6 +990,7 @@ VMStateDescription vmstate_x86_cpu = {
&vmstate_msr_hyperv_stimer,
&vmstate_avx512,
&vmstate_xss,
+ &vmstate_tsc_khz,
NULL
}
};