@samitouri / QOSamiQemu / commits / cfaa3b6c95

whpx: xsave support

Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr> Link: https://lore.kernel.org/r/20260422214225.2242-30-mohamed@unpredictable.fr Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Mohamed Mediouni committed Apr 22, 2026 at 23:42 UTC cfaa3b6c95972359c5a7299649b0f3408f03eeeb
3 files changed +293 -104
include/system/whpx-internal.h
+16
@@ -99,6 +99,22 @@ void whpx_apic_get(APICCommonState *s);
99 UINT32 StateSize)) \
100 X(HRESULT, WHvResetPartition, \
101 (WHV_PARTITION_HANDLE Partition)) \
102 + X(HRESULT, WHvGetVirtualProcessorXsaveState, \
103 + (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \
104 + PVOID Buffer, \
105 + UINT32 BufferSizeInBytes, UINT32 *BytesWritten)) \
106 + X(HRESULT, WHvSetVirtualProcessorXsaveState, \
107 + (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \
108 + PVOID Buffer, \
109 + UINT32 BufferSizeInBytes)) \
110 + X(HRESULT, WHvGetVirtualProcessorState, \
111 + (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \
112 + WHV_VIRTUAL_PROCESSOR_STATE_TYPE StateType, PVOID Buffer, \
113 + UINT32 BufferSizeInBytes, UINT32 *BytesWritten)) \
114 + X(HRESULT, WHvSetVirtualProcessorState, \
115 + (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \
116 + WHV_VIRTUAL_PROCESSOR_STATE_TYPE StateType, PVOID Buffer, \
117 + UINT32 BufferSizeInBytes)) \
118 LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL_ARCH(X)
119
120 #define WHP_DEFINE_TYPE(return_type, function_name, signature) \
target/i386/meson.build
+1 -1
@@ -10,7 +10,7 @@ i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c', 'confidential-guest
10 # x86 cpu type
11 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('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'))
16
target/i386/whpx/whpx-all.c
+276 -103
@@ -10,6 +10,7 @@
10
11 #include "qemu/osdep.h"
12 #include "cpu.h"
13 +#include "qemu/typedefs.h"
14 #include "system/address-spaces.h"
15 #include "system/ioport.h"
16 #include "gdbstub/helpers.h"
@@ -20,6 +21,7 @@
21 #include "system/cpus.h"
22 #include "system/runstate.h"
23 #include "qemu/main-loop.h"
24 +#include "qemu/memalign.h"
25 #include "hw/core/boards.h"
26 #include "hw/intc/ioapic.h"
27 #include "hw/intc/i8259.h"
@@ -108,34 +110,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = {
110 * WHvX64RegisterDr7,
111 */
112
111 - /* X64 Floating Point and Vector Registers */
112 - WHvX64RegisterXmm0,
113 - WHvX64RegisterXmm1,
114 - WHvX64RegisterXmm2,
115 - WHvX64RegisterXmm3,
116 - WHvX64RegisterXmm4,
117 - WHvX64RegisterXmm5,
118 - WHvX64RegisterXmm6,
119 - WHvX64RegisterXmm7,
120 - WHvX64RegisterXmm8,
121 - WHvX64RegisterXmm9,
122 - WHvX64RegisterXmm10,
123 - WHvX64RegisterXmm11,
124 - WHvX64RegisterXmm12,
125 - WHvX64RegisterXmm13,
126 - WHvX64RegisterXmm14,
127 - WHvX64RegisterXmm15,
128 - WHvX64RegisterFpMmx0,
129 - WHvX64RegisterFpMmx1,
130 - WHvX64RegisterFpMmx2,
131 - WHvX64RegisterFpMmx3,
132 - WHvX64RegisterFpMmx4,
133 - WHvX64RegisterFpMmx5,
134 - WHvX64RegisterFpMmx6,
135 - WHvX64RegisterFpMmx7,
136 - WHvX64RegisterFpControlStatus,
137 - WHvX64RegisterXmmControlStatus,
138 -
113 /* X64 MSRs */
114 WHvX64RegisterEfer,
115 #ifdef TARGET_X86_64
@@ -182,6 +156,36 @@ static const WHV_REGISTER_NAME whpx_register_names_for_vmexit[] = {
156 WHvX64RegisterR15,
157 };
158
159 +static const WHV_REGISTER_NAME whpx_register_names_legacy_fp[] = {
160 + /* X64 Floating Point and Vector Registers (non-xsave) */
161 + WHvX64RegisterXmm0,
162 + WHvX64RegisterXmm1,
163 + WHvX64RegisterXmm2,
164 + WHvX64RegisterXmm3,
165 + WHvX64RegisterXmm4,
166 + WHvX64RegisterXmm5,
167 + WHvX64RegisterXmm6,
168 + WHvX64RegisterXmm7,
169 + WHvX64RegisterXmm8,
170 + WHvX64RegisterXmm9,
171 + WHvX64RegisterXmm10,
172 + WHvX64RegisterXmm11,
173 + WHvX64RegisterXmm12,
174 + WHvX64RegisterXmm13,
175 + WHvX64RegisterXmm14,
176 + WHvX64RegisterXmm15,
177 + WHvX64RegisterFpMmx0,
178 + WHvX64RegisterFpMmx1,
179 + WHvX64RegisterFpMmx2,
180 + WHvX64RegisterFpMmx3,
181 + WHvX64RegisterFpMmx4,
182 + WHvX64RegisterFpMmx5,
183 + WHvX64RegisterFpMmx6,
184 + WHvX64RegisterFpMmx7,
185 + WHvX64RegisterFpControlStatus,
186 + WHvX64RegisterXmmControlStatus,
187 +};
188 +
189 struct whpx_register_set {
190 WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)];
191 };
@@ -392,6 +396,123 @@ static int whpx_set_tsc(CPUState *cpu)
396 return 0;
397 }
398
399 +static bool whpx_is_xsave_enabled(CPUState *cpu)
400 +{
401 + CPUX86State *env = &X86_CPU(cpu)->env;
402 + return env->cr[4] & CR4_OSXSAVE_MASK;
403 +}
404 +
405 +static size_t whpx_get_xsave_max_len(void)
406 +{
407 + return whpx_get_supported_cpuid(0xd, 0, R_ECX);
408 +}
409 +
410 +static int whpx_set_xsave_state(const CPUState *cpu)
411 +{
412 + struct whpx_state *whpx = &whpx_global;
413 + X86CPU *x86cpu = X86_CPU(cpu);
414 + CPUX86State *env = &x86cpu->env;
415 + HRESULT hr;
416 + void *xsavec_buf;
417 + size_t page = qemu_real_host_page_size();
418 + size_t xsavec_buf_len;
419 +
420 + /* allocate and populate compacted buffer */
421 + xsavec_buf_len = whpx_get_xsave_max_len();
422 + xsavec_buf = qemu_memalign(page, xsavec_buf_len);
423 +
424 + /* save registers to standard format buffer */
425 + x86_cpu_xsave_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
426 +
427 + /* store compacted version of xsave area in xsavec_buf */
428 + compact_xsave_area(env, xsavec_buf, xsavec_buf_len);
429 +
430 + if (!whpx_is_legacy_os()) {
431 + hr = whp_dispatch.WHvSetVirtualProcessorState(
432 + whpx->partition, cpu->cpu_index,
433 + WHvVirtualProcessorStateTypeXsaveState,
434 + xsavec_buf,
435 + xsavec_buf_len);
436 + } else {
437 + hr = whp_dispatch.WHvSetVirtualProcessorXsaveState(
438 + whpx->partition, cpu->cpu_index,
439 + xsavec_buf,
440 + xsavec_buf_len);
441 + }
442 +
443 + qemu_vfree(xsavec_buf);
444 + if (FAILED(hr)) {
445 + error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
446 + hr);
447 + }
448 +
449 + return 0;
450 +}
451 +
452 +static void whpx_set_legacy_fp_registers(CPUState *cpu, WHPXStateLevel level)
453 +{
454 + struct whpx_state *whpx = &whpx_global;
455 + X86CPU *x86_cpu = X86_CPU(cpu);
456 + CPUX86State *env = &x86_cpu->env;
457 + struct whpx_register_set vcxt;
458 + HRESULT hr;
459 + int idx = 0;
460 + int i;
461 + int idx_next;
462 +
463 + assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
464 +
465 + /* 16 XMM registers */
466 + assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmm0);
467 + idx_next = idx + 16;
468 + for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
469 + vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);
470 + vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);
471 + }
472 + idx = idx_next;
473 +
474 + /* 8 FP registers */
475 + assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpMmx0);
476 + for (i = 0; i < 8; i += 1, idx += 1) {
477 + vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);
478 + /* vcxt.values[idx].Fp.AsUINT128.High64 =
479 + env->fpregs[i].mmx.MMX_Q(1);
480 + */
481 + }
482 +
483 + /* FP control status register */
484 + assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpControlStatus);
485 + vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;
486 + vcxt.values[idx].FpControlStatus.FpStatus =
487 + (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
488 + vcxt.values[idx].FpControlStatus.FpTag = 0;
489 + for (i = 0; i < 8; ++i) {
490 + vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;
491 + }
492 + vcxt.values[idx].FpControlStatus.Reserved = 0;
493 + vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;
494 + vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;
495 + idx += 1;
496 +
497 + /* XMM control status register */
498 + assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmmControlStatus);
499 + vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;
500 + vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;
501 + vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;
502 + idx += 1;
503 +
504 + hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
505 + whpx->partition, cpu->cpu_index,
506 + whpx_register_names_legacy_fp,
507 + idx,
508 + &vcxt.values[0]);
509 +
510 + if (FAILED(hr)) {
511 + error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
512 + hr);
513 + }
514 +}
515 +
516 void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
517 {
518 struct whpx_state *whpx = &whpx_global;
@@ -491,45 +612,11 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
612 */
613 whpx_set_xcrs(cpu);
614
494 - /* 16 XMM registers */
495 - assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
496 - idx_next = idx + 16;
497 - for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
498 - vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);
499 - vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);
500 - }
501 - idx = idx_next;
502 -
503 - /* 8 FP registers */
504 - assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
505 - for (i = 0; i < 8; i += 1, idx += 1) {
506 - vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);
507 - /* vcxt.values[idx].Fp.AsUINT128.High64 =
508 - env->fpregs[i].mmx.MMX_Q(1);
509 - */
510 - }
511 -
512 - /* FP control status register */
513 - assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
514 - vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;
515 - vcxt.values[idx].FpControlStatus.FpStatus =
516 - (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
517 - vcxt.values[idx].FpControlStatus.FpTag = 0;
518 - for (i = 0; i < 8; ++i) {
519 - vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;
615 + if (whpx_is_xsave_enabled(cpu)) {
616 + whpx_set_xsave_state(cpu);
617 + } else {
618 + whpx_set_legacy_fp_registers(cpu, level);
619 }
521 - vcxt.values[idx].FpControlStatus.Reserved = 0;
522 - vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;
523 - vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;
524 - idx += 1;
525 -
526 - /* XMM control status register */
527 - assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
528 - vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;
529 - vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;
530 - vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;
531 - idx += 1;
532 -
620 /* MSRs */
621 assert(whpx_register_names[idx] == WHvX64RegisterEfer);
622 vcxt.values[idx++].Reg64 = env->efer;
@@ -662,6 +749,110 @@ static void whpx_get_registers_for_vmexit(CPUState *cpu, WHPXStateLevel level)
749 x86_update_hflags(env);
750 }
751
752 +static void whpx_get_legacy_fp_registers(CPUState *cpu, WHPXStateLevel level)
753 +{
754 + struct whpx_state *whpx = &whpx_global;
755 + X86CPU *x86_cpu = X86_CPU(cpu);
756 + CPUX86State *env = &x86_cpu->env;
757 + struct whpx_register_set vcxt;
758 + HRESULT hr;
759 + int i;
760 + int idx;
761 + int idx_next;
762 +
763 + assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
764 +
765 + hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
766 + whpx->partition, cpu->cpu_index,
767 + whpx_register_names_legacy_fp,
768 + RTL_NUMBER_OF(whpx_register_names_legacy_fp),
769 + &vcxt.values[0]);
770 +
771 + if (FAILED(hr)) {
772 + error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
773 + hr);
774 + }
775 +
776 + idx = 0;
777 + /* 16 XMM registers */
778 + assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmm0);
779 + idx_next = idx + 16;
780 + for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
781 + env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;
782 + env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;
783 + }
784 + idx = idx_next;
785 +
786 + /* 8 FP registers */
787 + assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpMmx0);
788 + for (i = 0; i < 8; i += 1, idx += 1) {
789 + env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;
790 + /* env->fpregs[i].mmx.MMX_Q(1) =
791 + vcxt.values[idx].Fp.AsUINT128.High64;
792 + */
793 + }
794 +
795 + /* FP control status register */
796 + assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpControlStatus);
797 + env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;
798 + env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;
799 + env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;
800 + for (i = 0; i < 8; ++i) {
801 + env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);
802 + }
803 + env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;
804 + env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;
805 + idx += 1;
806 +
807 + /* XMM control status register */
808 + assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmmControlStatus);
809 + env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
810 + idx += 1;
811 +}
812 +
813 +static int whpx_get_xsave_state(CPUState *cpu)
814 +{
815 + struct whpx_state *whpx = &whpx_global;
816 + X86CPU *x86cpu = X86_CPU(cpu);
817 + CPUX86State *env = &x86cpu->env;
818 + int ret;
819 + HRESULT hr;
820 + void *xsavec_buf;
821 + const size_t page = qemu_real_host_page_size();
822 + size_t xsavec_buf_len = whpx_get_xsave_max_len();
823 + UINT32 bytes_written;
824 +
825 + xsavec_buf = qemu_memalign(page, xsavec_buf_len);
826 + memset(xsavec_buf, 0, xsavec_buf_len);
827 +
828 + if (!whpx_is_legacy_os()) {
829 + hr = whp_dispatch.WHvGetVirtualProcessorState(
830 + whpx->partition, cpu->cpu_index,
831 + WHvVirtualProcessorStateTypeXsaveState,
832 + xsavec_buf,
833 + xsavec_buf_len, &bytes_written);
834 + } else {
835 + hr = whp_dispatch.WHvGetVirtualProcessorXsaveState(
836 + whpx->partition, cpu->cpu_index,
837 + xsavec_buf,
838 + xsavec_buf_len, &bytes_written);
839 + }
840 + if (FAILED(hr) || bytes_written == 0) {
841 + error_report("failed to get xsave state: %s", strerror(errno));
842 + return -errno;
843 + }
844 +
845 + ret = decompact_xsave_area(xsavec_buf, xsavec_buf_len, env);
846 + qemu_vfree(xsavec_buf);
847 + if (ret < 0) {
848 + error_report("failed to decompact xsave area");
849 + return ret;
850 + }
851 + x86_cpu_xrstor_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
852 +
853 + return 0;
854 +}
855 +
856 void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
857 {
858 struct whpx_state *whpx = &whpx_global;
@@ -758,40 +949,11 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
949 */
950 whpx_get_xcrs(cpu);
951
761 - /* 16 XMM registers */
762 - assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
763 - idx_next = idx + 16;
764 - for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
765 - env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;
766 - env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;
767 - }
768 - idx = idx_next;
769 -
770 - /* 8 FP registers */
771 - assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
772 - for (i = 0; i < 8; i += 1, idx += 1) {
773 - env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;
774 - /* env->fpregs[i].mmx.MMX_Q(1) =
775 - vcxt.values[idx].Fp.AsUINT128.High64;
776 - */
777 - }
778 -
779 - /* FP control status register */
780 - assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
781 - env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;
782 - env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;
783 - env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;
784 - for (i = 0; i < 8; ++i) {
785 - env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);
952 + if (whpx_is_xsave_enabled(cpu)) {
953 + whpx_get_xsave_state(cpu);
954 + } else {
955 + whpx_get_legacy_fp_registers(cpu, level);
956 }
787 - env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;
788 - env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;
789 - idx += 1;
790 -
791 - /* XMM control status register */
792 - assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
793 - env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
794 - idx += 1;
957
958 /* MSRs */
959 assert(whpx_register_names[idx] == WHvX64RegisterEfer);
@@ -1586,6 +1748,9 @@ void whpx_arch_destroy_vcpu(CPUState *cpu)
1748 X86CPU *x86cpu = X86_CPU(cpu);
1749 CPUX86State *env = &x86cpu->env;
1750 g_free(env->emu_mmio_buf);
1751 + qemu_vfree(env->xsave_buf);
1752 + env->xsave_buf = NULL;
1753 + env->xsave_buf_len = 0;
1754 }
1755
1756 /* Returns the address of the next instruction that is about to be executed. */
@@ -2446,6 +2611,9 @@ int whpx_init_vcpu(CPUState *cpu)
2611 Error *local_error = NULL;
2612 X86CPU *x86_cpu = X86_CPU(cpu);
2613 CPUX86State *env = &x86_cpu->env;
2614 + X86XSaveHeader *header;
2615 + size_t page_size = qemu_real_host_page_size();
2616 + size_t xsave_len;
2617 UINT64 freq = 0;
2618 int ret;
2619
@@ -2522,6 +2690,15 @@ int whpx_init_vcpu(CPUState *cpu)
2690 qemu_add_vm_change_state_handler(whpx_cpu_update_state, env);
2691
2692 env->emu_mmio_buf = g_new(char, 4096);
2693 + /* Initialize XSAVE buffer page-aligned */
2694 + xsave_len = whpx_get_xsave_max_len();
2695 + env->xsave_buf = qemu_memalign(page_size, xsave_len);
2696 + env->xsave_buf_len = xsave_len;
2697 + memset(env->xsave_buf, 0, env->xsave_buf_len);
2698 +
2699 + /* we need to set the compacted format bit in xsave header for Hyper-V */
2700 + header = (X86XSaveHeader *)(env->xsave_buf + sizeof(X86LegacyXSaveArea));
2701 + header->xcomp_bv = header->xstate_bv | (1ULL << 63);
2702
2703 return 0;
2704
@@ -2722,10 +2899,6 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
2899 error_report("WHPX: Failed to query XSAVE capability, hr=%08lx", hr);
2900 }
2901
2725 - if (!whpx_has_xsave()) {
2726 - printf("WHPX: Partition is not XSAVE capable\n");
2727 - }
2728 -
2902 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
2903 prop.ProcessorCount = ms->smp.cpus;
2904 hr = whp_dispatch.WHvSetPartitionProperty(