target/i386/mshv: migrate STIMER state
This part of Synic state is retrieved via a mem-aligned page. We declare the required space (size reference: rust-vmm/mshv) as a buffer on the VM state struct for inclusion in a migration. Other than other SynIC features, STIMER doesn't depend on SCONTROL being set. Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com> Link: https://lore.kernel.org/r/20260710101534.664604-9-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Magnus Kulke committed
Jul 10, 2026 at 12:15 UTC
f2c8f7bcd424e17aea74fdb84d739887f1d52aa1
5 files changed
+90
include/system/mshv_int.h
+2
@@ -144,5 +144,7 @@ int mshv_set_simp(int cpu_fd, const uint8_t *page);
144
int mshv_get_siefp(int cpu_fd, uint8_t *page);
145
int mshv_set_siefp(int cpu_fd, const uint8_t *page);
146
bool mshv_synic_enabled(const CPUState *cpu);
147
+int mshv_get_synthetic_timers(int cpu_fd, uint8_t *state);
148
+int mshv_set_synthetic_timers(int cpu_fd, const uint8_t *state);
149
150
#endif
target/i386/cpu.h
+5
@@ -45,6 +45,10 @@
45
#define ELF_MACHINE_UNAME "i686"
46
#endif
47
48
+#ifdef CONFIG_MSHV
49
+#define MSHV_STIMERS_STATE_SIZE 200
50
+#endif
51
+
52
enum {
53
R_EAX = 0,
54
R_ECX = 1,
@@ -2305,6 +2309,7 @@ typedef struct CPUArchState {
2309
#if defined(CONFIG_MSHV)
2310
uint8_t hv_simp_page[HV_HYP_PAGE_SIZE];
2311
uint8_t hv_siefp_page[HV_HYP_PAGE_SIZE];
2312
+ uint8_t hv_synthetic_timers_state[MSHV_STIMERS_STATE_SIZE];
2313
#endif
2314
2315
uint64_t mcg_cap;
target/i386/machine.c
+20
@@ -10,6 +10,7 @@
10
#include "exec/watchpoint.h"
11
#include "system/kvm.h"
12
#include "system/kvm_xen.h"
13
+#include "system/mshv.h"
14
#include "system/tcg.h"
15
16
#include "qemu/error-report.h"
@@ -952,6 +953,24 @@ static const VMStateDescription vmstate_msr_hyperv_reenlightenment = {
953
};
954
955
#ifdef CONFIG_MSHV
956
+
957
+static bool mshv_synthetic_timers_needed(void *opaque)
958
+{
959
+ /* Always migrate synthetic timers */
960
+ return mshv_enabled();
961
+}
962
+
963
+static const VMStateDescription vmstate_mshv_synthetic_timers = {
964
+ .name = "cpu/mshv_synthetic_timers",
965
+ .version_id = 1,
966
+ .minimum_version_id = 1,
967
+ .needed = mshv_synthetic_timers_needed,
968
+ .fields = (const VMStateField[]) {
969
+ VMSTATE_BUFFER(env.hv_synthetic_timers_state, X86CPU),
970
+ VMSTATE_END_OF_LIST()
971
+ }
972
+};
973
+
974
static bool mshv_synic_vp_state_needed(void *opaque)
975
{
976
X86CPU *cpu = opaque;
@@ -1941,6 +1960,7 @@ const VMStateDescription vmstate_x86_cpu = {
1960
#endif
1961
#ifdef CONFIG_MSHV
1962
&vmstate_mshv_synic_vp_state,
1963
+ &vmstate_mshv_synthetic_timers,
1964
#endif
1965
NULL
1966
}
target/i386/mshv/mshv-cpu.c
+12
@@ -118,6 +118,12 @@ static int get_synic_state(CPUState *cpu)
118
int cpu_fd = mshv_vcpufd(cpu);
119
int ret;
120
121
+ ret = mshv_get_synthetic_timers(cpu_fd, env->hv_synthetic_timers_state);
122
+ if (ret < 0) {
123
+ error_report("failed to get synthetic timers");
124
+ return -1;
125
+ }
126
+
127
/* SIMP/SIEFP can only be read when SynIC is enabled */
128
if (!mshv_synic_enabled(cpu)) {
129
return 0;
@@ -1420,6 +1426,12 @@ static int set_synic_state(const CPUState *cpu)
1426
int cpu_fd = mshv_vcpufd(cpu);
1427
int ret;
1428
1429
+ ret = mshv_set_synthetic_timers(cpu_fd, env->hv_synthetic_timers_state);
1430
+ if (ret < 0) {
1431
+ error_report("failed to set synthetic timers state");
1432
+ return -1;
1433
+ }
1434
+
1435
/* SIMP/SIEFP can only be written when SynIC is enabled */
1436
if (!mshv_synic_enabled(cpu)) {
1437
return 0;
target/i386/mshv/synic.c
+51
@@ -54,6 +54,57 @@ static int set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state)
54
return 0;
55
}
56
57
+int mshv_get_synthetic_timers(int cpu_fd, uint8_t *state)
58
+{
59
+ int ret;
60
+ void *buffer;
61
+ struct mshv_get_set_vp_state args = {0};
62
+
63
+ buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE);
64
+ args.buf_ptr = (uint64_t)buffer;
65
+ args.buf_sz = HV_HYP_PAGE_SIZE;
66
+ args.type = MSHV_VP_STATE_SYNTHETIC_TIMERS;
67
+
68
+ ret = get_vp_state(cpu_fd, &args);
69
+
70
+ if (ret < 0) {
71
+ qemu_vfree(buffer);
72
+ error_report("failed to get synthetic timers");
73
+ return -1;
74
+ }
75
+
76
+ memcpy(state, buffer, MSHV_STIMERS_STATE_SIZE);
77
+ qemu_vfree(buffer);
78
+
79
+ return 0;
80
+}
81
+
82
+int mshv_set_synthetic_timers(int cpu_fd, const uint8_t *state)
83
+{
84
+ int ret;
85
+ void *buffer;
86
+ struct mshv_get_set_vp_state args = {0};
87
+
88
+ buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE);
89
+ memset(buffer, 0, HV_HYP_PAGE_SIZE);
90
+ args.buf_ptr = (uint64_t)buffer;
91
+ args.buf_sz = HV_HYP_PAGE_SIZE;
92
+ args.type = MSHV_VP_STATE_SYNTHETIC_TIMERS;
93
+
94
+ assert(state);
95
+ memcpy(buffer, state, MSHV_STIMERS_STATE_SIZE);
96
+
97
+ ret = set_vp_state(cpu_fd, &args);
98
+ qemu_vfree(buffer);
99
+
100
+ if (ret < 0) {
101
+ error_report("failed to set synthetic timers");
102
+ return -1;
103
+ }
104
+
105
+ return 0;
106
+}
107
+
108
int mshv_get_simp(int cpu_fd, uint8_t *page)
109
{
110
int ret;