target/i386/mshv: migrate SIMP and SIEFP state
This part SynIC state is retrieved from the hypervisor via aligned state pages: - Add new synic source file - Centralize the synic_enabled() check - r/w pages from the hyper via aligned pages - only handle pages when synic is enabled - add buffers for migration to VM state Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com> Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com> Link: https://lore.kernel.org/r/20260710101534.664604-8-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Magnus Kulke committed
Jul 10, 2026 at 12:15 UTC
e514ff235b41c5582becc821b79f466d62f90c83
7 files changed
+260
-5
include/system/mshv_int.h
+7
@@ -138,4 +138,11 @@ int mshv_init_msrs(const CPUState *cpu);
138
int mshv_get_msrs(CPUState *cpu);
139
int mshv_set_msrs(const CPUState *cpu);
140
141
+/* synic */
142
+int mshv_get_simp(int cpu_fd, uint8_t *page);
143
+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
+
148
#endif
target/i386/cpu.h
+5
@@ -33,6 +33,7 @@
33
#include "qemu/cpu-float.h"
34
#include "qemu/timer.h"
35
#include "standard-headers/asm-x86/kvm_para.h"
36
+#include "hw/hyperv/hvgdk_mini.h"
37
38
#define XEN_NR_VIRQS 24
39
@@ -2301,6 +2302,10 @@ typedef struct CPUArchState {
2302
#if defined(CONFIG_HVF) || defined(CONFIG_MSHV) || defined(CONFIG_WHPX)
2303
void *emu_mmio_buf;
2304
#endif
2305
+#if defined(CONFIG_MSHV)
2306
+ uint8_t hv_simp_page[HV_HYP_PAGE_SIZE];
2307
+ uint8_t hv_siefp_page[HV_HYP_PAGE_SIZE];
2308
+#endif
2309
2310
uint64_t mcg_cap;
2311
uint64_t mcg_ctl;
target/i386/machine.c
+26
@@ -951,6 +951,29 @@ static const VMStateDescription vmstate_msr_hyperv_reenlightenment = {
951
}
952
};
953
954
+#ifdef CONFIG_MSHV
955
+static bool mshv_synic_vp_state_needed(void *opaque)
956
+{
957
+ X86CPU *cpu = opaque;
958
+ CPUX86State *env = &cpu->env;
959
+
960
+ /* Only migrate SIMP/SIEFP if SynIC is enabled */
961
+ return env->msr_hv_synic_control & 1;
962
+}
963
+
964
+static const VMStateDescription vmstate_mshv_synic_vp_state = {
965
+ .name = "cpu/mshv_synic_vp_state",
966
+ .version_id = 1,
967
+ .minimum_version_id = 1,
968
+ .needed = mshv_synic_vp_state_needed,
969
+ .fields = (const VMStateField[]) {
970
+ VMSTATE_BUFFER(env.hv_simp_page, X86CPU),
971
+ VMSTATE_BUFFER(env.hv_siefp_page, X86CPU),
972
+ VMSTATE_END_OF_LIST()
973
+ }
974
+};
975
+#endif
976
+
977
static bool avx512_needed(void *opaque)
978
{
979
X86CPU *cpu = opaque;
@@ -1915,6 +1938,9 @@ const VMStateDescription vmstate_x86_cpu = {
1938
&vmstate_cet,
1939
#ifdef TARGET_X86_64
1940
&vmstate_apx,
1941
+#endif
1942
+#ifdef CONFIG_MSHV
1943
+ &vmstate_mshv_synic_vp_state,
1944
#endif
1945
NULL
1946
}
target/i386/mshv/meson.build
+1
@@ -3,6 +3,7 @@ i386_mshv_ss = ss.source_set()
3
i386_mshv_ss.add(files(
4
'mshv-cpu.c',
5
'msr.c',
6
+ 'synic.c',
7
))
8
9
i386_system_ss.add_all(when: 'CONFIG_MSHV', if_true: i386_mshv_ss)
target/i386/mshv/mshv-cpu.c
+64
@@ -111,6 +111,33 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
111
112
static int set_special_regs(const CPUState *cpu);
113
114
+static int get_synic_state(CPUState *cpu)
115
+{
116
+ X86CPU *x86cpu = X86_CPU(cpu);
117
+ CPUX86State *env = &x86cpu->env;
118
+ int cpu_fd = mshv_vcpufd(cpu);
119
+ int ret;
120
+
121
+ /* SIMP/SIEFP can only be read when SynIC is enabled */
122
+ if (!mshv_synic_enabled(cpu)) {
123
+ return 0;
124
+ }
125
+
126
+ ret = mshv_get_simp(cpu_fd, env->hv_simp_page);
127
+ if (ret < 0) {
128
+ error_report("failed to get simp state");
129
+ return -1;
130
+ }
131
+
132
+ ret = mshv_get_siefp(cpu_fd, env->hv_siefp_page);
133
+ if (ret < 0) {
134
+ error_report("failed to get siefp state");
135
+ return -1;
136
+ }
137
+
138
+ return 0;
139
+}
140
+
141
static int get_xsave_state(CPUState *cpu)
142
{
143
X86CPU *x86cpu = X86_CPU(cpu);
@@ -969,6 +996,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu)
996
return ret;
997
}
998
999
+ ret = get_synic_state(cpu);
1000
+ if (ret < 0) {
1001
+ return ret;
1002
+ }
1003
+
1004
ret = get_vcpu_events(cpu);
1005
if (ret < 0) {
1006
return ret;
@@ -1381,6 +1413,33 @@ static int set_xc_reg(const CPUState *cpu)
1413
return 0;
1414
}
1415
1416
+static int set_synic_state(const CPUState *cpu)
1417
+{
1418
+ X86CPU *x86cpu = X86_CPU(cpu);
1419
+ CPUX86State *env = &x86cpu->env;
1420
+ int cpu_fd = mshv_vcpufd(cpu);
1421
+ int ret;
1422
+
1423
+ /* SIMP/SIEFP can only be written when SynIC is enabled */
1424
+ if (!mshv_synic_enabled(cpu)) {
1425
+ return 0;
1426
+ }
1427
+
1428
+ ret = mshv_set_simp(cpu_fd, env->hv_simp_page);
1429
+ if (ret < 0) {
1430
+ error_report("failed to set simp state");
1431
+ return -1;
1432
+ }
1433
+
1434
+ ret = mshv_set_siefp(cpu_fd, env->hv_siefp_page);
1435
+ if (ret < 0) {
1436
+ error_report("failed to set siefp state");
1437
+ return -1;
1438
+ }
1439
+
1440
+ return 0;
1441
+}
1442
+
1443
int mshv_arch_store_vcpu_state(const CPUState *cpu)
1444
{
1445
int ret;
@@ -1422,6 +1481,11 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu)
1481
return ret;
1482
}
1483
1484
+ ret = set_synic_state(cpu);
1485
+ if (ret < 0) {
1486
+ return ret;
1487
+ }
1488
+
1489
ret = set_vcpu_events(cpu);
1490
if (ret < 0) {
1491
return ret;
target/i386/mshv/msr.c
+2
-5
@@ -332,7 +332,6 @@ int mshv_get_msrs(CPUState *cpu)
332
size_t i, j;
333
uint32_t name;
334
X86CPU *x86cpu = X86_CPU(cpu);
335
- bool synic_enabled;
335
336
set_hv_name_in_assocs(assocs, n_assocs);
337
@@ -360,8 +359,7 @@ int mshv_get_msrs(CPUState *cpu)
359
store_in_env(cpu, assocs, n_assocs);
360
361
/* Read SINT MSRs only if SynIC is enabled */
363
- synic_enabled = x86cpu->env.msr_hv_synic_control & 1;
364
- if (synic_enabled) {
362
+ if (mshv_synic_enabled(cpu)) {
363
QEMU_BUILD_BUG_ON(MSHV_MSR_TOTAL_COUNT < HV_SINT_COUNT);
364
365
for (i = 0; i < HV_SINT_COUNT; i++) {
@@ -415,7 +413,6 @@ int mshv_set_msrs(const CPUState *cpu)
413
int ret;
414
size_t i, j;
415
X86CPU *x86cpu = X86_CPU(cpu);
418
- bool synic_enabled = x86cpu->env.msr_hv_synic_control & 1;
416
417
load_from_env(cpu, assocs, n_assocs);
418
@@ -449,7 +446,7 @@ int mshv_set_msrs(const CPUState *cpu)
446
}
447
448
/* SINT MSRs can only be written if SCONTROL has been set, so we split */
452
- if (synic_enabled) {
449
+ if (mshv_synic_enabled(cpu)) {
450
QEMU_BUILD_BUG_ON(MSHV_MSR_TOTAL_COUNT < HV_SINT_COUNT);
451
452
for (i = 0; i < HV_SINT_COUNT; i++) {
target/i386/mshv/synic.c
new
+155
@@ -0,0 +1,155 @@
1
+/*
2
+ * QEMU MSHV SynIC support
3
+ *
4
+ * Copyright Microsoft, Corp. 2026
5
+ *
6
+ * Authors: Magnus Kulke <magnuskulke@microsoft.com>
7
+ *
8
+ * SPDX-License-Identifier: GPL-2.0-or-later
9
+ */
10
+
11
+#include "qemu/osdep.h"
12
+#include "qemu/memalign.h"
13
+#include "qemu/error-report.h"
14
+
15
+#include "system/mshv.h"
16
+#include "system/mshv_int.h"
17
+
18
+#include "linux/mshv.h"
19
+#include "hw/hyperv/hvgdk_mini.h"
20
+#include "cpu.h"
21
+
22
+#include <sys/ioctl.h>
23
+
24
+bool mshv_synic_enabled(const CPUState *cpu)
25
+{
26
+ X86CPU *x86cpu = X86_CPU(cpu);
27
+
28
+ return x86cpu->env.msr_hv_synic_control & 1;
29
+}
30
+
31
+static int get_vp_state(int cpu_fd, struct mshv_get_set_vp_state *state)
32
+{
33
+ int ret;
34
+
35
+ ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, state);
36
+ if (ret < 0) {
37
+ error_report("failed to get vp state: %s", strerror(errno));
38
+ return -1;
39
+ }
40
+
41
+ return 0;
42
+}
43
+
44
+static int set_vp_state(int cpu_fd, const struct mshv_get_set_vp_state *state)
45
+{
46
+ int ret;
47
+
48
+ ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, state);
49
+ if (ret < 0) {
50
+ error_report("failed to set vp state: %s", strerror(errno));
51
+ return -1;
52
+ }
53
+
54
+ return 0;
55
+}
56
+
57
+int mshv_get_simp(int cpu_fd, uint8_t *page)
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_SIMP;
67
+
68
+ ret = get_vp_state(cpu_fd, &args);
69
+
70
+ if (ret < 0) {
71
+ qemu_vfree(buffer);
72
+ error_report("failed to get simp");
73
+ return -1;
74
+ }
75
+
76
+ memcpy(page, buffer, HV_HYP_PAGE_SIZE);
77
+ qemu_vfree(buffer);
78
+
79
+ return 0;
80
+}
81
+
82
+int mshv_set_simp(int cpu_fd, const uint8_t *page)
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
+ args.buf_ptr = (uint64_t)buffer;
90
+ args.buf_sz = HV_HYP_PAGE_SIZE;
91
+ args.type = MSHV_VP_STATE_SIMP;
92
+
93
+ assert(page);
94
+ memcpy(buffer, page, HV_HYP_PAGE_SIZE);
95
+
96
+ ret = set_vp_state(cpu_fd, &args);
97
+ qemu_vfree(buffer);
98
+
99
+ if (ret < 0) {
100
+ error_report("failed to set simp");
101
+ return -1;
102
+ }
103
+
104
+ return 0;
105
+}
106
+
107
+int mshv_get_siefp(int cpu_fd, uint8_t *page)
108
+{
109
+ int ret;
110
+ void *buffer;
111
+ struct mshv_get_set_vp_state args = {0};
112
+
113
+ buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE);
114
+ args.buf_ptr = (uint64_t)buffer;
115
+ args.buf_sz = HV_HYP_PAGE_SIZE;
116
+ args.type = MSHV_VP_STATE_SIEFP,
117
+
118
+ ret = get_vp_state(cpu_fd, &args);
119
+
120
+ if (ret < 0) {
121
+ qemu_vfree(buffer);
122
+ error_report("failed to get siefp");
123
+ return -1;
124
+ }
125
+
126
+ memcpy(page, buffer, HV_HYP_PAGE_SIZE);
127
+ qemu_vfree(buffer);
128
+
129
+ return 0;
130
+}
131
+
132
+int mshv_set_siefp(int cpu_fd, const uint8_t *page)
133
+{
134
+ int ret;
135
+ void *buffer;
136
+ struct mshv_get_set_vp_state args = {0};
137
+
138
+ buffer = qemu_memalign(HV_HYP_PAGE_SIZE, HV_HYP_PAGE_SIZE);
139
+ args.buf_ptr = (uint64_t)buffer;
140
+ args.buf_sz = HV_HYP_PAGE_SIZE;
141
+ args.type = MSHV_VP_STATE_SIEFP,
142
+
143
+ assert(page);
144
+ memcpy(buffer, page, HV_HYP_PAGE_SIZE);
145
+
146
+ ret = set_vp_state(cpu_fd, &args);
147
+ qemu_vfree(buffer);
148
+
149
+ if (ret < 0) {
150
+ error_report("failed to set simp");
151
+ return -1;
152
+ }
153
+
154
+ return 0;
155
+}