@samitouri / QOSamiQemu / commits / 676b3ba2d0

target/i386/mshv: migrate XSAVE state

We implement fn's that roundtrip XSAVE state in migration. We are using the xsave_helper routines to move individual components from CPUX86State to an xsave_buf and then we have to compact the buffer to XSAVEC format, which is what the hypervisor expects. And the same applies in the other direction for restoring state from the hypervisor. Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com> Link: https://lore.kernel.org/r/20260417105618.3621-32-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Magnus Kulke committed Apr 17, 2026 at 12:56 UTC 676b3ba2d0674af272d31a2bee10d5aef4a5ff07
2 files changed +100 -2
target/i386/meson.build
+1 -1
@@ -12,7 +12,7 @@ i386_ss.add(when: 'CONFIG_KVM', if_true: files('xsave_helper.c', 'host-cpu.c'))
12 i386_ss.add(when: 'CONFIG_HVF', if_true: files('xsave_helper.c', 'host-cpu.c'))
13 i386_ss.add(when: 'CONFIG_WHPX', if_true: files('xsave_helper.c', 'host-cpu.c'))
14 i386_ss.add(when: 'CONFIG_NVMM', if_true: files('host-cpu.c'))
15 -i386_ss.add(when: 'CONFIG_MSHV', if_true: files('host-cpu.c'))
15 +i386_ss.add(when: 'CONFIG_MSHV', if_true: files('xsave_helper.c', 'host-cpu.c'))
16
17 i386_system_ss = ss.source_set()
18 i386_system_ss.add(files(
target/i386/mshv/mshv-cpu.c
+99 -1
@@ -112,6 +112,78 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
112
113 static int set_special_regs(const CPUState *cpu);
114
115 +static int get_xsave_state(CPUState *cpu)
116 +{
117 + X86CPU *x86cpu = X86_CPU(cpu);
118 + CPUX86State *env = &x86cpu->env;
119 + int cpu_fd = mshv_vcpufd(cpu);
120 + int ret;
121 + void *xsavec_buf;
122 + const size_t page = HV_HYP_PAGE_SIZE;
123 + size_t xsavec_buf_len = page;
124 +
125 + /* TODO: should properly determine xsavec size based on CPUID */
126 + xsavec_buf = qemu_memalign(page, xsavec_buf_len);
127 + memset(xsavec_buf, 0, xsavec_buf_len);
128 +
129 + struct mshv_get_set_vp_state args = {
130 + .type = MSHV_VP_STATE_XSAVE,
131 + .buf_sz = xsavec_buf_len,
132 + .buf_ptr = (uintptr_t)xsavec_buf,
133 + };
134 +
135 + ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, &args);
136 + if (ret < 0) {
137 + error_report("failed to get xsave state: %s", strerror(errno));
138 + return -errno;
139 + }
140 +
141 + ret = decompact_xsave_area(xsavec_buf, xsavec_buf_len, env);
142 + g_free(xsavec_buf);
143 + if (ret < 0) {
144 + error_report("failed to decompact xsave area");
145 + return ret;
146 + }
147 + x86_cpu_xrstor_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
148 +
149 + return 0;
150 +}
151 +
152 +static int set_xsave_state(const CPUState *cpu)
153 +{
154 + X86CPU *x86cpu = X86_CPU(cpu);
155 + CPUX86State *env = &x86cpu->env;
156 + int cpu_fd = mshv_vcpufd(cpu);
157 + int ret;
158 + void *xsavec_buf;
159 + size_t page = HV_HYP_PAGE_SIZE, xsavec_buf_len;
160 +
161 + /* allocate and populate compacted buffer */
162 + xsavec_buf = qemu_memalign(page, page);
163 + xsavec_buf_len = page;
164 +
165 + /* save registers to standard format buffer */
166 + x86_cpu_xsave_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
167 +
168 + /* store compacted version of xsave area in xsavec_buf */
169 + compact_xsave_area(env, xsavec_buf, xsavec_buf_len);
170 +
171 + struct mshv_get_set_vp_state args = {
172 + .type = MSHV_VP_STATE_XSAVE,
173 + .buf_sz = xsavec_buf_len,
174 + .buf_ptr = (uintptr_t)xsavec_buf,
175 + };
176 +
177 + ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, &args);
178 + g_free(xsavec_buf);
179 + if (ret < 0) {
180 + error_report("failed to set xsave state: %s", strerror(errno));
181 + return -errno;
182 + }
183 +
184 + return 0;
185 +}
186 +
187 static void populate_fpu(const hv_register_assoc *assocs, X86CPU *x86cpu)
188 {
189 union hv_register_value value;
@@ -717,6 +789,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu)
789 return ret;
790 }
791
792 + ret = get_xsave_state(cpu);
793 + if (ret < 0) {
794 + return ret;
795 + }
796 +
797 ret = get_vcpu_events(cpu);
798 if (ret < 0) {
799 return ret;
@@ -1263,6 +1340,11 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu)
1340 return ret;
1341 }
1342
1343 + ret = set_xsave_state(cpu);
1344 + if (ret < 0) {
1345 + return ret;
1346 + }
1347 +
1348 ret = set_vcpu_events(cpu);
1349 if (ret < 0) {
1350 return ret;
@@ -1817,9 +1899,10 @@ void mshv_arch_init_vcpu(CPUState *cpu)
1899 X86CPU *x86_cpu = X86_CPU(cpu);
1900 CPUX86State *env = &x86_cpu->env;
1901 AccelCPUState *state = cpu->accel;
1820 - size_t page = HV_HYP_PAGE_SIZE;
1902 + size_t page = HV_HYP_PAGE_SIZE, xsave_len;
1903 void *mem = qemu_memalign(page, 2 * page);
1904 int ret;
1905 + X86XSaveHeader *header;
1906
1907 /* sanity check, to make sure we don't overflow the page */
1908 QEMU_BUILD_BUG_ON((MAX_REGISTER_COUNT
@@ -1833,6 +1916,17 @@ void mshv_arch_init_vcpu(CPUState *cpu)
1916
1917 env->emu_mmio_buf = g_new(char, 4096);
1918
1919 + /* Initialize XSAVE buffer page-aligned */
1920 + /* TODO: pick proper size based on CPUID */
1921 + xsave_len = page;
1922 + env->xsave_buf = qemu_memalign(page, xsave_len);
1923 + env->xsave_buf_len = xsave_len;
1924 + memset(env->xsave_buf, 0, env->xsave_buf_len);
1925 +
1926 + /* we need to set the compacted format bit in xsave header for mshv */
1927 + header = (X86XSaveHeader *)(env->xsave_buf + sizeof(X86LegacyXSaveArea));
1928 + header->xcomp_bv = header->xstate_bv | (1ULL << 63);
1929 +
1930 /*
1931 * TODO: populate topology info:
1932 * X86CPUTopoInfo *topo_info = &env->topo_info;
@@ -1857,6 +1951,10 @@ void mshv_arch_destroy_vcpu(CPUState *cpu)
1951 g_free(state->hvcall_args.base);
1952 state->hvcall_args = (MshvHvCallArgs){0};
1953 g_clear_pointer(&env->emu_mmio_buf, g_free);
1954 +
1955 + qemu_vfree(env->xsave_buf);
1956 + env->xsave_buf = NULL;
1957 + env->xsave_buf_len = 0;
1958 }
1959
1960 uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg)