Commit fcc504b770 for qemu.org
commit fcc504b77076b936ce79e33d9be68d6b73cc5dbe
Author: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Mon Nov 9 13:39:49 2026 +0100
target/i386/mshv: add MshvPutState for vcpu writes
Currently QEMU marks a vcpu state as dirty also on merely inspectional
reads, e.g. via "info registers", which triggers a writeback to the
hypervisor. For MSRs that cause a side-effect when written to this is
undesired.
We introduce an MshvPutState flag into the writeback logic to differentiate
"runtime" from "full" and "reset" writebacks that should occur e.g. after
a migration or startup.
Discrete side-effectful MSRs are excluded in runtime writebacks. So far
we only mark one MSR as not runtime-safe, but probably more will be
added eventually.
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
Reviewed-by: Wei Liu <wei.liu@kernel.org>
Reviewed-by: Mohamed Mediouni <mohamed@unpredictable.fr>
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index 5519768cc2..cd5b5d1863 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -764,10 +764,10 @@ static int mshv_cpu_exec(CPUState *cpu)
do {
if (cpu->vcpu_dirty) {
- ret = mshv_arch_store_vcpu_state(cpu);
+ ret = mshv_arch_store_vcpu_state(cpu, MSHV_PUT_RUNTIME_STATE);
if (ret) {
- error_report("Failed to put registers after init: %s",
- strerror(-ret));
+ error_report("Failed to put registers at runtime: %s",
+ strerror(-ret));
ret = -1;
break;
}
@@ -895,7 +895,7 @@ static void mshv_start_vcpu_thread(CPUState *cpu)
static void do_mshv_cpu_synchronize_post_init(CPUState *cpu,
run_on_cpu_data arg)
{
- int ret = mshv_arch_store_vcpu_state(cpu);
+ int ret = mshv_arch_store_vcpu_state(cpu, MSHV_PUT_FULL_STATE);
if (ret < 0) {
error_report("Failed to put registers after init: %s", strerror(-ret));
abort();
@@ -911,7 +911,7 @@ static void mshv_cpu_synchronize_post_init(CPUState *cpu)
static void mshv_cpu_synchronize_post_reset(CPUState *cpu)
{
- int ret = mshv_arch_store_vcpu_state(cpu);
+ int ret = mshv_arch_store_vcpu_state(cpu, MSHV_PUT_RESET_STATE);
if (ret) {
error_report("Failed to put registers after reset: %s",
strerror(-ret));
diff --git a/include/system/mshv_int.h b/include/system/mshv_int.h
index 89b44f387a..01da21affe 100644
--- a/include/system/mshv_int.h
+++ b/include/system/mshv_int.h
@@ -99,6 +99,15 @@ typedef enum MshvVmExit {
MshvVmExitDebug = 3,
} MshvVmExit;
+typedef enum MshvPutState {
+ /* state subset only touched by the VCPU itself during runtime */
+ MSHV_PUT_RUNTIME_STATE = 1,
+ /* state subset modified during VCPU reset */
+ MSHV_PUT_RESET_STATE = 2,
+ /* full state set, modified during initialization or on vmload */
+ MSHV_PUT_FULL_STATE = 3,
+} MshvPutState;
+
void mshv_init_mmio_emu(void);
int mshv_create_vcpu(int vm_fd, uint8_t vp_index, int *cpu_fd);
void mshv_remove_vcpu(int vm_fd, int cpu_fd);
@@ -108,7 +117,7 @@ int mshv_set_generic_regs(const CPUState *cpu, const hv_register_assoc *assocs,
size_t n_regs);
int mshv_get_generic_regs(CPUState *cpu, hv_register_assoc *assocs,
size_t n_regs);
-int mshv_arch_store_vcpu_state(const CPUState *cpu);
+int mshv_arch_store_vcpu_state(const CPUState *cpu, MshvPutState state);
int mshv_arch_load_vcpu_state(CPUState *cpu);
int mshv_arch_set_partition_msrs(const CPUState *cpu);
int mshv_arch_set_mp_state(const CPUState *cpu);
@@ -153,7 +162,7 @@ void mshv_log_global_stop(MemoryListener *listener);
/* msr */
int mshv_init_msrs(const CPUState *cpu);
int mshv_get_msrs(CPUState *cpu);
-int mshv_set_msrs(const CPUState *cpu);
+int mshv_set_msrs(const CPUState *cpu, MshvPutState put_state);
/* synic */
int mshv_get_simp(int cpu_fd, uint8_t *page);
diff --git a/target/i386/mshv/mshv-cpu.c b/target/i386/mshv/mshv-cpu.c
index e3c155da2e..c145bbc447 100644
--- a/target/i386/mshv/mshv-cpu.c
+++ b/target/i386/mshv/mshv-cpu.c
@@ -1511,7 +1511,13 @@ static int set_synic_state(const CPUState *cpu)
return 0;
}
-int mshv_arch_store_vcpu_state(const CPUState *cpu)
+/*
+ * The put_state param is passed to decide whether we need a full restoration
+ * of all registers (e.g. after a migration ) or just a subset during runtime
+ * (e.g after "info registers"). The latter should avoid writing registers that
+ * cause a side-effect.
+ */
+int mshv_arch_store_vcpu_state(const CPUState *cpu, MshvPutState put_state)
{
int ret;
@@ -1541,7 +1547,7 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu)
return ret;
}
- ret = mshv_set_msrs(cpu);
+ ret = mshv_set_msrs(cpu, put_state);
if (ret < 0) {
return ret;
}
diff --git a/target/i386/mshv/msr.c b/target/i386/mshv/msr.c
index 96bd552916..a06844cc99 100644
--- a/target/i386/mshv/msr.c
+++ b/target/i386/mshv/msr.c
@@ -324,6 +324,20 @@ static bool msr_supported(uint32_t name)
return true;
}
+static bool msr_is_runtime_safe(uint32_t name)
+{
+ switch (name) {
+ /*
+ * HV_X64_REGISTER_TSC will invalidate the guest's tsc_page if written to
+ * while the guest's timer is not frozen.
+ */
+ case HV_X64_REGISTER_TSC:
+ return false;
+ default:
+ return true;
+ }
+}
+
int mshv_get_msrs(CPUState *cpu)
{
int ret = 0;
@@ -406,13 +420,14 @@ static void load_from_env(const CPUState *cpu, struct hv_register_assoc *assocs,
load_from_env_mtrr_phys(cpu, &assocs[mtrr_offset], MSHV_MTRR_MSR_COUNT);
}
-int mshv_set_msrs(const CPUState *cpu)
+int mshv_set_msrs(const CPUState *cpu, MshvPutState put_state)
{
size_t n_assocs = MSHV_MSR_TOTAL_COUNT;
struct hv_register_assoc assocs[MSHV_MSR_TOTAL_COUNT] = { 0 };
int ret;
size_t i, j;
X86CPU *x86cpu = X86_CPU(cpu);
+ bool runtime = (put_state <= MSHV_PUT_RUNTIME_STATE);
load_from_env(cpu, assocs, n_assocs);
@@ -432,6 +447,10 @@ int mshv_set_msrs(const CPUState *cpu)
continue;
}
+ if (runtime && !msr_is_runtime_safe(name)) {
+ continue;
+ }
+
if (j != i) {
assocs[j] = assocs[i];
}