@samitouri / QOSamiQemu / commits / e514ff235b

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 +}