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>
This commit is contained in:
Magnus Kulke
2026-04-17 12:56:15 +02:00
committed by Paolo Bonzini
parent b2c0cc6300
commit 676b3ba2d0
2 changed files with 100 additions and 2 deletions

View File

@@ -12,7 +12,7 @@ i386_ss.add(when: 'CONFIG_KVM', if_true: files('xsave_helper.c', 'host-cpu.c'))
i386_ss.add(when: 'CONFIG_HVF', if_true: files('xsave_helper.c', 'host-cpu.c'))
i386_ss.add(when: 'CONFIG_WHPX', if_true: files('xsave_helper.c', 'host-cpu.c'))
i386_ss.add(when: 'CONFIG_NVMM', if_true: files('host-cpu.c'))
i386_ss.add(when: 'CONFIG_MSHV', if_true: files('host-cpu.c'))
i386_ss.add(when: 'CONFIG_MSHV', if_true: files('xsave_helper.c', 'host-cpu.c'))
i386_system_ss = ss.source_set()
i386_system_ss.add(files(

View File

@@ -112,6 +112,78 @@ static enum hv_register_name FPU_REGISTER_NAMES[26] = {
static int set_special_regs(const CPUState *cpu);
static int get_xsave_state(CPUState *cpu)
{
X86CPU *x86cpu = X86_CPU(cpu);
CPUX86State *env = &x86cpu->env;
int cpu_fd = mshv_vcpufd(cpu);
int ret;
void *xsavec_buf;
const size_t page = HV_HYP_PAGE_SIZE;
size_t xsavec_buf_len = page;
/* TODO: should properly determine xsavec size based on CPUID */
xsavec_buf = qemu_memalign(page, xsavec_buf_len);
memset(xsavec_buf, 0, xsavec_buf_len);
struct mshv_get_set_vp_state args = {
.type = MSHV_VP_STATE_XSAVE,
.buf_sz = xsavec_buf_len,
.buf_ptr = (uintptr_t)xsavec_buf,
};
ret = ioctl(cpu_fd, MSHV_GET_VP_STATE, &args);
if (ret < 0) {
error_report("failed to get xsave state: %s", strerror(errno));
return -errno;
}
ret = decompact_xsave_area(xsavec_buf, xsavec_buf_len, env);
g_free(xsavec_buf);
if (ret < 0) {
error_report("failed to decompact xsave area");
return ret;
}
x86_cpu_xrstor_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
return 0;
}
static int set_xsave_state(const CPUState *cpu)
{
X86CPU *x86cpu = X86_CPU(cpu);
CPUX86State *env = &x86cpu->env;
int cpu_fd = mshv_vcpufd(cpu);
int ret;
void *xsavec_buf;
size_t page = HV_HYP_PAGE_SIZE, xsavec_buf_len;
/* allocate and populate compacted buffer */
xsavec_buf = qemu_memalign(page, page);
xsavec_buf_len = page;
/* save registers to standard format buffer */
x86_cpu_xsave_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
/* store compacted version of xsave area in xsavec_buf */
compact_xsave_area(env, xsavec_buf, xsavec_buf_len);
struct mshv_get_set_vp_state args = {
.type = MSHV_VP_STATE_XSAVE,
.buf_sz = xsavec_buf_len,
.buf_ptr = (uintptr_t)xsavec_buf,
};
ret = ioctl(cpu_fd, MSHV_SET_VP_STATE, &args);
g_free(xsavec_buf);
if (ret < 0) {
error_report("failed to set xsave state: %s", strerror(errno));
return -errno;
}
return 0;
}
static void populate_fpu(const hv_register_assoc *assocs, X86CPU *x86cpu)
{
union hv_register_value value;
@@ -717,6 +789,11 @@ int mshv_arch_load_vcpu_state(CPUState *cpu)
return ret;
}
ret = get_xsave_state(cpu);
if (ret < 0) {
return ret;
}
ret = get_vcpu_events(cpu);
if (ret < 0) {
return ret;
@@ -1263,6 +1340,11 @@ int mshv_arch_store_vcpu_state(const CPUState *cpu)
return ret;
}
ret = set_xsave_state(cpu);
if (ret < 0) {
return ret;
}
ret = set_vcpu_events(cpu);
if (ret < 0) {
return ret;
@@ -1817,9 +1899,10 @@ void mshv_arch_init_vcpu(CPUState *cpu)
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
AccelCPUState *state = cpu->accel;
size_t page = HV_HYP_PAGE_SIZE;
size_t page = HV_HYP_PAGE_SIZE, xsave_len;
void *mem = qemu_memalign(page, 2 * page);
int ret;
X86XSaveHeader *header;
/* sanity check, to make sure we don't overflow the page */
QEMU_BUILD_BUG_ON((MAX_REGISTER_COUNT
@@ -1833,6 +1916,17 @@ void mshv_arch_init_vcpu(CPUState *cpu)
env->emu_mmio_buf = g_new(char, 4096);
/* Initialize XSAVE buffer page-aligned */
/* TODO: pick proper size based on CPUID */
xsave_len = page;
env->xsave_buf = qemu_memalign(page, xsave_len);
env->xsave_buf_len = xsave_len;
memset(env->xsave_buf, 0, env->xsave_buf_len);
/* we need to set the compacted format bit in xsave header for mshv */
header = (X86XSaveHeader *)(env->xsave_buf + sizeof(X86LegacyXSaveArea));
header->xcomp_bv = header->xstate_bv | (1ULL << 63);
/*
* TODO: populate topology info:
* X86CPUTopoInfo *topo_info = &env->topo_info;
@@ -1857,6 +1951,10 @@ void mshv_arch_destroy_vcpu(CPUState *cpu)
g_free(state->hvcall_args.base);
state->hvcall_args = (MshvHvCallArgs){0};
g_clear_pointer(&env->emu_mmio_buf, g_free);
qemu_vfree(env->xsave_buf);
env->xsave_buf = NULL;
env->xsave_buf_len = 0;
}
uint32_t mshv_get_supported_cpuid(uint32_t func, uint32_t idx, int reg)