2 * QEMU Windows Hypervisor Platform accelerator (WHPX)
4 * Copyright Microsoft Corp. 2017
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
11 #include "qemu/osdep.h"
13 #include "exec/address-spaces.h"
14 #include "exec/ioport.h"
15 #include "qemu-common.h"
16 #include "sysemu/accel.h"
17 #include "sysemu/whpx.h"
18 #include "sysemu/cpus.h"
19 #include "sysemu/runstate.h"
20 #include "qemu/main-loop.h"
21 #include "hw/boards.h"
22 #include "qemu/error-report.h"
23 #include "qapi/error.h"
24 #include "migration/blocker.h"
25 #include "whp-dispatch.h"
27 #include <WinHvPlatform.h>
28 #include <WinHvEmulation.h>
32 WHV_PARTITION_HANDLE partition
;
35 static const WHV_REGISTER_NAME whpx_register_names
[] = {
37 /* X64 General purpose registers */
57 /* X64 Segment registers */
67 /* X64 Table registers */
71 /* X64 Control Registers */
78 /* X64 Debug Registers */
88 /* X64 Floating Point and Vector Registers */
105 WHvX64RegisterFpMmx0
,
106 WHvX64RegisterFpMmx1
,
107 WHvX64RegisterFpMmx2
,
108 WHvX64RegisterFpMmx3
,
109 WHvX64RegisterFpMmx4
,
110 WHvX64RegisterFpMmx5
,
111 WHvX64RegisterFpMmx6
,
112 WHvX64RegisterFpMmx7
,
113 WHvX64RegisterFpControlStatus
,
114 WHvX64RegisterXmmControlStatus
,
120 WHvX64RegisterKernelGsBase
,
122 WHvX64RegisterApicBase
,
123 /* WHvX64RegisterPat, */
124 WHvX64RegisterSysenterCs
,
125 WHvX64RegisterSysenterEip
,
126 WHvX64RegisterSysenterEsp
,
131 WHvX64RegisterSfmask
,
134 /* Interrupt / Event Registers */
136 * WHvRegisterPendingInterruption,
137 * WHvRegisterInterruptState,
138 * WHvRegisterPendingEvent0,
139 * WHvRegisterPendingEvent1
140 * WHvX64RegisterDeliverabilityNotifications,
144 struct whpx_register_set
{
145 WHV_REGISTER_VALUE values
[RTL_NUMBER_OF(whpx_register_names
)];
149 WHV_EMULATOR_HANDLE emulator
;
150 bool window_registered
;
154 bool interruption_pending
;
156 /* Must be the last field as it may have a tail */
157 WHV_RUN_VP_EXIT_CONTEXT exit_ctx
;
160 static bool whpx_allowed
;
161 static bool whp_dispatch_initialized
;
162 static HMODULE hWinHvPlatform
, hWinHvEmulation
;
164 struct whpx_state whpx_global
;
165 struct WHPDispatch whp_dispatch
;
172 static struct whpx_vcpu
*get_whpx_vcpu(CPUState
*cpu
)
174 return (struct whpx_vcpu
*)cpu
->hax_vcpu
;
177 static WHV_X64_SEGMENT_REGISTER
whpx_seg_q2h(const SegmentCache
*qs
, int v86
,
180 WHV_X64_SEGMENT_REGISTER hs
;
181 unsigned flags
= qs
->flags
;
184 hs
.Limit
= qs
->limit
;
185 hs
.Selector
= qs
->selector
;
191 hs
.DescriptorPrivilegeLevel
= 3;
192 hs
.NonSystemSegment
= 1;
195 hs
.Attributes
= (flags
>> DESC_TYPE_SHIFT
);
198 /* hs.Base &= 0xfffff; */
205 static SegmentCache
whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER
*hs
)
210 qs
.limit
= hs
->Limit
;
211 qs
.selector
= hs
->Selector
;
213 qs
.flags
= ((uint32_t)hs
->Attributes
) << DESC_TYPE_SHIFT
;
218 static void whpx_set_registers(CPUState
*cpu
)
220 struct whpx_state
*whpx
= &whpx_global
;
221 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
222 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
223 X86CPU
*x86_cpu
= X86_CPU(cpu
);
224 struct whpx_register_set vcxt
;
231 assert(cpu_is_stopped(cpu
) || qemu_cpu_is_self(cpu
));
233 memset(&vcxt
, 0, sizeof(struct whpx_register_set
));
235 v86
= (env
->eflags
& VM_MASK
);
236 r86
= !(env
->cr
[0] & CR0_PE_MASK
);
238 vcpu
->tpr
= cpu_get_apic_tpr(x86_cpu
->apic_state
);
239 vcpu
->apic_base
= cpu_get_apic_base(x86_cpu
->apic_state
);
243 /* Indexes for first 16 registers match between HV and QEMU definitions */
245 for (idx
= 0; idx
< CPU_NB_REGS
; idx
+= 1) {
246 vcxt
.values
[idx
].Reg64
= (uint64_t)env
->regs
[idx
];
250 /* Same goes for RIP and RFLAGS */
251 assert(whpx_register_names
[idx
] == WHvX64RegisterRip
);
252 vcxt
.values
[idx
++].Reg64
= env
->eip
;
254 assert(whpx_register_names
[idx
] == WHvX64RegisterRflags
);
255 vcxt
.values
[idx
++].Reg64
= env
->eflags
;
257 /* Translate 6+4 segment registers. HV and QEMU order matches */
258 assert(idx
== WHvX64RegisterEs
);
259 for (i
= 0; i
< 6; i
+= 1, idx
+= 1) {
260 vcxt
.values
[idx
].Segment
= whpx_seg_q2h(&env
->segs
[i
], v86
, r86
);
263 assert(idx
== WHvX64RegisterLdtr
);
264 vcxt
.values
[idx
++].Segment
= whpx_seg_q2h(&env
->ldt
, 0, 0);
266 assert(idx
== WHvX64RegisterTr
);
267 vcxt
.values
[idx
++].Segment
= whpx_seg_q2h(&env
->tr
, 0, 0);
269 assert(idx
== WHvX64RegisterIdtr
);
270 vcxt
.values
[idx
].Table
.Base
= env
->idt
.base
;
271 vcxt
.values
[idx
].Table
.Limit
= env
->idt
.limit
;
274 assert(idx
== WHvX64RegisterGdtr
);
275 vcxt
.values
[idx
].Table
.Base
= env
->gdt
.base
;
276 vcxt
.values
[idx
].Table
.Limit
= env
->gdt
.limit
;
279 /* CR0, 2, 3, 4, 8 */
280 assert(whpx_register_names
[idx
] == WHvX64RegisterCr0
);
281 vcxt
.values
[idx
++].Reg64
= env
->cr
[0];
282 assert(whpx_register_names
[idx
] == WHvX64RegisterCr2
);
283 vcxt
.values
[idx
++].Reg64
= env
->cr
[2];
284 assert(whpx_register_names
[idx
] == WHvX64RegisterCr3
);
285 vcxt
.values
[idx
++].Reg64
= env
->cr
[3];
286 assert(whpx_register_names
[idx
] == WHvX64RegisterCr4
);
287 vcxt
.values
[idx
++].Reg64
= env
->cr
[4];
288 assert(whpx_register_names
[idx
] == WHvX64RegisterCr8
);
289 vcxt
.values
[idx
++].Reg64
= vcpu
->tpr
;
291 /* 8 Debug Registers - Skipped */
293 /* 16 XMM registers */
294 assert(whpx_register_names
[idx
] == WHvX64RegisterXmm0
);
296 for (i
= 0; i
< sizeof(env
->xmm_regs
) / sizeof(ZMMReg
); i
+= 1, idx
+= 1) {
297 vcxt
.values
[idx
].Reg128
.Low64
= env
->xmm_regs
[i
].ZMM_Q(0);
298 vcxt
.values
[idx
].Reg128
.High64
= env
->xmm_regs
[i
].ZMM_Q(1);
303 assert(whpx_register_names
[idx
] == WHvX64RegisterFpMmx0
);
304 for (i
= 0; i
< 8; i
+= 1, idx
+= 1) {
305 vcxt
.values
[idx
].Fp
.AsUINT128
.Low64
= env
->fpregs
[i
].mmx
.MMX_Q(0);
306 /* vcxt.values[idx].Fp.AsUINT128.High64 =
307 env->fpregs[i].mmx.MMX_Q(1);
311 /* FP control status register */
312 assert(whpx_register_names
[idx
] == WHvX64RegisterFpControlStatus
);
313 vcxt
.values
[idx
].FpControlStatus
.FpControl
= env
->fpuc
;
314 vcxt
.values
[idx
].FpControlStatus
.FpStatus
=
315 (env
->fpus
& ~0x3800) | (env
->fpstt
& 0x7) << 11;
316 vcxt
.values
[idx
].FpControlStatus
.FpTag
= 0;
317 for (i
= 0; i
< 8; ++i
) {
318 vcxt
.values
[idx
].FpControlStatus
.FpTag
|= (!env
->fptags
[i
]) << i
;
320 vcxt
.values
[idx
].FpControlStatus
.Reserved
= 0;
321 vcxt
.values
[idx
].FpControlStatus
.LastFpOp
= env
->fpop
;
322 vcxt
.values
[idx
].FpControlStatus
.LastFpRip
= env
->fpip
;
325 /* XMM control status register */
326 assert(whpx_register_names
[idx
] == WHvX64RegisterXmmControlStatus
);
327 vcxt
.values
[idx
].XmmControlStatus
.LastFpRdp
= 0;
328 vcxt
.values
[idx
].XmmControlStatus
.XmmStatusControl
= env
->mxcsr
;
329 vcxt
.values
[idx
].XmmControlStatus
.XmmStatusControlMask
= 0x0000ffff;
333 assert(whpx_register_names
[idx
] == WHvX64RegisterTsc
);
334 vcxt
.values
[idx
++].Reg64
= env
->tsc
;
335 assert(whpx_register_names
[idx
] == WHvX64RegisterEfer
);
336 vcxt
.values
[idx
++].Reg64
= env
->efer
;
338 assert(whpx_register_names
[idx
] == WHvX64RegisterKernelGsBase
);
339 vcxt
.values
[idx
++].Reg64
= env
->kernelgsbase
;
342 assert(whpx_register_names
[idx
] == WHvX64RegisterApicBase
);
343 vcxt
.values
[idx
++].Reg64
= vcpu
->apic_base
;
345 /* WHvX64RegisterPat - Skipped */
347 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterCs
);
348 vcxt
.values
[idx
++].Reg64
= env
->sysenter_cs
;
349 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEip
);
350 vcxt
.values
[idx
++].Reg64
= env
->sysenter_eip
;
351 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEsp
);
352 vcxt
.values
[idx
++].Reg64
= env
->sysenter_esp
;
353 assert(whpx_register_names
[idx
] == WHvX64RegisterStar
);
354 vcxt
.values
[idx
++].Reg64
= env
->star
;
356 assert(whpx_register_names
[idx
] == WHvX64RegisterLstar
);
357 vcxt
.values
[idx
++].Reg64
= env
->lstar
;
358 assert(whpx_register_names
[idx
] == WHvX64RegisterCstar
);
359 vcxt
.values
[idx
++].Reg64
= env
->cstar
;
360 assert(whpx_register_names
[idx
] == WHvX64RegisterSfmask
);
361 vcxt
.values
[idx
++].Reg64
= env
->fmask
;
364 /* Interrupt / Event Registers - Skipped */
366 assert(idx
== RTL_NUMBER_OF(whpx_register_names
));
368 hr
= whp_dispatch
.WHvSetVirtualProcessorRegisters(
369 whpx
->partition
, cpu
->cpu_index
,
371 RTL_NUMBER_OF(whpx_register_names
),
375 error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
382 static void whpx_get_registers(CPUState
*cpu
)
384 struct whpx_state
*whpx
= &whpx_global
;
385 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
386 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
387 X86CPU
*x86_cpu
= X86_CPU(cpu
);
388 struct whpx_register_set vcxt
;
389 uint64_t tpr
, apic_base
;
395 assert(cpu_is_stopped(cpu
) || qemu_cpu_is_self(cpu
));
397 hr
= whp_dispatch
.WHvGetVirtualProcessorRegisters(
398 whpx
->partition
, cpu
->cpu_index
,
400 RTL_NUMBER_OF(whpx_register_names
),
403 error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
409 /* Indexes for first 16 registers match between HV and QEMU definitions */
411 for (idx
= 0; idx
< CPU_NB_REGS
; idx
+= 1) {
412 env
->regs
[idx
] = vcxt
.values
[idx
].Reg64
;
416 /* Same goes for RIP and RFLAGS */
417 assert(whpx_register_names
[idx
] == WHvX64RegisterRip
);
418 env
->eip
= vcxt
.values
[idx
++].Reg64
;
419 assert(whpx_register_names
[idx
] == WHvX64RegisterRflags
);
420 env
->eflags
= vcxt
.values
[idx
++].Reg64
;
422 /* Translate 6+4 segment registers. HV and QEMU order matches */
423 assert(idx
== WHvX64RegisterEs
);
424 for (i
= 0; i
< 6; i
+= 1, idx
+= 1) {
425 env
->segs
[i
] = whpx_seg_h2q(&vcxt
.values
[idx
].Segment
);
428 assert(idx
== WHvX64RegisterLdtr
);
429 env
->ldt
= whpx_seg_h2q(&vcxt
.values
[idx
++].Segment
);
430 assert(idx
== WHvX64RegisterTr
);
431 env
->tr
= whpx_seg_h2q(&vcxt
.values
[idx
++].Segment
);
432 assert(idx
== WHvX64RegisterIdtr
);
433 env
->idt
.base
= vcxt
.values
[idx
].Table
.Base
;
434 env
->idt
.limit
= vcxt
.values
[idx
].Table
.Limit
;
436 assert(idx
== WHvX64RegisterGdtr
);
437 env
->gdt
.base
= vcxt
.values
[idx
].Table
.Base
;
438 env
->gdt
.limit
= vcxt
.values
[idx
].Table
.Limit
;
441 /* CR0, 2, 3, 4, 8 */
442 assert(whpx_register_names
[idx
] == WHvX64RegisterCr0
);
443 env
->cr
[0] = vcxt
.values
[idx
++].Reg64
;
444 assert(whpx_register_names
[idx
] == WHvX64RegisterCr2
);
445 env
->cr
[2] = vcxt
.values
[idx
++].Reg64
;
446 assert(whpx_register_names
[idx
] == WHvX64RegisterCr3
);
447 env
->cr
[3] = vcxt
.values
[idx
++].Reg64
;
448 assert(whpx_register_names
[idx
] == WHvX64RegisterCr4
);
449 env
->cr
[4] = vcxt
.values
[idx
++].Reg64
;
450 assert(whpx_register_names
[idx
] == WHvX64RegisterCr8
);
451 tpr
= vcxt
.values
[idx
++].Reg64
;
452 if (tpr
!= vcpu
->tpr
) {
454 cpu_set_apic_tpr(x86_cpu
->apic_state
, tpr
);
457 /* 8 Debug Registers - Skipped */
459 /* 16 XMM registers */
460 assert(whpx_register_names
[idx
] == WHvX64RegisterXmm0
);
462 for (i
= 0; i
< sizeof(env
->xmm_regs
) / sizeof(ZMMReg
); i
+= 1, idx
+= 1) {
463 env
->xmm_regs
[i
].ZMM_Q(0) = vcxt
.values
[idx
].Reg128
.Low64
;
464 env
->xmm_regs
[i
].ZMM_Q(1) = vcxt
.values
[idx
].Reg128
.High64
;
469 assert(whpx_register_names
[idx
] == WHvX64RegisterFpMmx0
);
470 for (i
= 0; i
< 8; i
+= 1, idx
+= 1) {
471 env
->fpregs
[i
].mmx
.MMX_Q(0) = vcxt
.values
[idx
].Fp
.AsUINT128
.Low64
;
472 /* env->fpregs[i].mmx.MMX_Q(1) =
473 vcxt.values[idx].Fp.AsUINT128.High64;
477 /* FP control status register */
478 assert(whpx_register_names
[idx
] == WHvX64RegisterFpControlStatus
);
479 env
->fpuc
= vcxt
.values
[idx
].FpControlStatus
.FpControl
;
480 env
->fpstt
= (vcxt
.values
[idx
].FpControlStatus
.FpStatus
>> 11) & 0x7;
481 env
->fpus
= vcxt
.values
[idx
].FpControlStatus
.FpStatus
& ~0x3800;
482 for (i
= 0; i
< 8; ++i
) {
483 env
->fptags
[i
] = !((vcxt
.values
[idx
].FpControlStatus
.FpTag
>> i
) & 1);
485 env
->fpop
= vcxt
.values
[idx
].FpControlStatus
.LastFpOp
;
486 env
->fpip
= vcxt
.values
[idx
].FpControlStatus
.LastFpRip
;
489 /* XMM control status register */
490 assert(whpx_register_names
[idx
] == WHvX64RegisterXmmControlStatus
);
491 env
->mxcsr
= vcxt
.values
[idx
].XmmControlStatus
.XmmStatusControl
;
495 assert(whpx_register_names
[idx
] == WHvX64RegisterTsc
);
496 env
->tsc
= vcxt
.values
[idx
++].Reg64
;
497 assert(whpx_register_names
[idx
] == WHvX64RegisterEfer
);
498 env
->efer
= vcxt
.values
[idx
++].Reg64
;
500 assert(whpx_register_names
[idx
] == WHvX64RegisterKernelGsBase
);
501 env
->kernelgsbase
= vcxt
.values
[idx
++].Reg64
;
504 assert(whpx_register_names
[idx
] == WHvX64RegisterApicBase
);
505 apic_base
= vcxt
.values
[idx
++].Reg64
;
506 if (apic_base
!= vcpu
->apic_base
) {
507 vcpu
->apic_base
= apic_base
;
508 cpu_set_apic_base(x86_cpu
->apic_state
, vcpu
->apic_base
);
511 /* WHvX64RegisterPat - Skipped */
513 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterCs
);
514 env
->sysenter_cs
= vcxt
.values
[idx
++].Reg64
;;
515 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEip
);
516 env
->sysenter_eip
= vcxt
.values
[idx
++].Reg64
;
517 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEsp
);
518 env
->sysenter_esp
= vcxt
.values
[idx
++].Reg64
;
519 assert(whpx_register_names
[idx
] == WHvX64RegisterStar
);
520 env
->star
= vcxt
.values
[idx
++].Reg64
;
522 assert(whpx_register_names
[idx
] == WHvX64RegisterLstar
);
523 env
->lstar
= vcxt
.values
[idx
++].Reg64
;
524 assert(whpx_register_names
[idx
] == WHvX64RegisterCstar
);
525 env
->cstar
= vcxt
.values
[idx
++].Reg64
;
526 assert(whpx_register_names
[idx
] == WHvX64RegisterSfmask
);
527 env
->fmask
= vcxt
.values
[idx
++].Reg64
;
530 /* Interrupt / Event Registers - Skipped */
532 assert(idx
== RTL_NUMBER_OF(whpx_register_names
));
537 static HRESULT CALLBACK
whpx_emu_ioport_callback(
539 WHV_EMULATOR_IO_ACCESS_INFO
*IoAccess
)
541 MemTxAttrs attrs
= { 0 };
542 address_space_rw(&address_space_io
, IoAccess
->Port
, attrs
,
543 (uint8_t *)&IoAccess
->Data
, IoAccess
->AccessSize
,
544 IoAccess
->Direction
);
548 static HRESULT CALLBACK
whpx_emu_mmio_callback(
550 WHV_EMULATOR_MEMORY_ACCESS_INFO
*ma
)
552 cpu_physical_memory_rw(ma
->GpaAddress
, ma
->Data
, ma
->AccessSize
,
557 static HRESULT CALLBACK
whpx_emu_getreg_callback(
559 const WHV_REGISTER_NAME
*RegisterNames
,
560 UINT32 RegisterCount
,
561 WHV_REGISTER_VALUE
*RegisterValues
)
564 struct whpx_state
*whpx
= &whpx_global
;
565 CPUState
*cpu
= (CPUState
*)ctx
;
567 hr
= whp_dispatch
.WHvGetVirtualProcessorRegisters(
568 whpx
->partition
, cpu
->cpu_index
,
569 RegisterNames
, RegisterCount
,
572 error_report("WHPX: Failed to get virtual processor registers,"
579 static HRESULT CALLBACK
whpx_emu_setreg_callback(
581 const WHV_REGISTER_NAME
*RegisterNames
,
582 UINT32 RegisterCount
,
583 const WHV_REGISTER_VALUE
*RegisterValues
)
586 struct whpx_state
*whpx
= &whpx_global
;
587 CPUState
*cpu
= (CPUState
*)ctx
;
589 hr
= whp_dispatch
.WHvSetVirtualProcessorRegisters(
590 whpx
->partition
, cpu
->cpu_index
,
591 RegisterNames
, RegisterCount
,
594 error_report("WHPX: Failed to set virtual processor registers,"
599 * The emulator just successfully wrote the register state. We clear the
600 * dirty state so we avoid the double write on resume of the VP.
602 cpu
->vcpu_dirty
= false;
607 static HRESULT CALLBACK
whpx_emu_translate_callback(
609 WHV_GUEST_VIRTUAL_ADDRESS Gva
,
610 WHV_TRANSLATE_GVA_FLAGS TranslateFlags
,
611 WHV_TRANSLATE_GVA_RESULT_CODE
*TranslationResult
,
612 WHV_GUEST_PHYSICAL_ADDRESS
*Gpa
)
615 struct whpx_state
*whpx
= &whpx_global
;
616 CPUState
*cpu
= (CPUState
*)ctx
;
617 WHV_TRANSLATE_GVA_RESULT res
;
619 hr
= whp_dispatch
.WHvTranslateGva(whpx
->partition
, cpu
->cpu_index
,
620 Gva
, TranslateFlags
, &res
, Gpa
);
622 error_report("WHPX: Failed to translate GVA, hr=%08lx", hr
);
624 *TranslationResult
= res
.ResultCode
;
630 static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks
= {
631 .Size
= sizeof(WHV_EMULATOR_CALLBACKS
),
632 .WHvEmulatorIoPortCallback
= whpx_emu_ioport_callback
,
633 .WHvEmulatorMemoryCallback
= whpx_emu_mmio_callback
,
634 .WHvEmulatorGetVirtualProcessorRegisters
= whpx_emu_getreg_callback
,
635 .WHvEmulatorSetVirtualProcessorRegisters
= whpx_emu_setreg_callback
,
636 .WHvEmulatorTranslateGvaPage
= whpx_emu_translate_callback
,
639 static int whpx_handle_mmio(CPUState
*cpu
, WHV_MEMORY_ACCESS_CONTEXT
*ctx
)
642 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
643 WHV_EMULATOR_STATUS emu_status
;
645 hr
= whp_dispatch
.WHvEmulatorTryMmioEmulation(
647 &vcpu
->exit_ctx
.VpContext
, ctx
,
650 error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr
);
654 if (!emu_status
.EmulationSuccessful
) {
655 error_report("WHPX: Failed to emulate MMIO access with"
656 " EmulatorReturnStatus: %u", emu_status
.AsUINT32
);
663 static int whpx_handle_portio(CPUState
*cpu
,
664 WHV_X64_IO_PORT_ACCESS_CONTEXT
*ctx
)
667 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
668 WHV_EMULATOR_STATUS emu_status
;
670 hr
= whp_dispatch
.WHvEmulatorTryIoEmulation(
672 &vcpu
->exit_ctx
.VpContext
, ctx
,
675 error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr
);
679 if (!emu_status
.EmulationSuccessful
) {
680 error_report("WHPX: Failed to emulate PortIO access with"
681 " EmulatorReturnStatus: %u", emu_status
.AsUINT32
);
688 static int whpx_handle_halt(CPUState
*cpu
)
690 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
693 qemu_mutex_lock_iothread();
694 if (!((cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) &&
695 (env
->eflags
& IF_MASK
)) &&
696 !(cpu
->interrupt_request
& CPU_INTERRUPT_NMI
)) {
697 cpu
->exception_index
= EXCP_HLT
;
701 qemu_mutex_unlock_iothread();
706 static void whpx_vcpu_pre_run(CPUState
*cpu
)
709 struct whpx_state
*whpx
= &whpx_global
;
710 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
711 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
712 X86CPU
*x86_cpu
= X86_CPU(cpu
);
715 WHV_X64_PENDING_INTERRUPTION_REGISTER new_int
;
716 UINT32 reg_count
= 0;
717 WHV_REGISTER_VALUE reg_values
[3];
718 WHV_REGISTER_NAME reg_names
[3];
720 memset(&new_int
, 0, sizeof(new_int
));
721 memset(reg_values
, 0, sizeof(reg_values
));
723 qemu_mutex_lock_iothread();
726 if (!vcpu
->interruption_pending
&&
727 cpu
->interrupt_request
& (CPU_INTERRUPT_NMI
| CPU_INTERRUPT_SMI
)) {
728 if (cpu
->interrupt_request
& CPU_INTERRUPT_NMI
) {
729 cpu
->interrupt_request
&= ~CPU_INTERRUPT_NMI
;
730 vcpu
->interruptable
= false;
731 new_int
.InterruptionType
= WHvX64PendingNmi
;
732 new_int
.InterruptionPending
= 1;
733 new_int
.InterruptionVector
= 2;
735 if (cpu
->interrupt_request
& CPU_INTERRUPT_SMI
) {
736 cpu
->interrupt_request
&= ~CPU_INTERRUPT_SMI
;
741 * Force the VCPU out of its inner loop to process any INIT requests or
742 * commit pending TPR access.
744 if (cpu
->interrupt_request
& (CPU_INTERRUPT_INIT
| CPU_INTERRUPT_TPR
)) {
745 if ((cpu
->interrupt_request
& CPU_INTERRUPT_INIT
) &&
746 !(env
->hflags
& HF_SMM_MASK
)) {
747 cpu
->exit_request
= 1;
749 if (cpu
->interrupt_request
& CPU_INTERRUPT_TPR
) {
750 cpu
->exit_request
= 1;
754 /* Get pending hard interruption or replay one that was overwritten */
755 if (!vcpu
->interruption_pending
&&
756 vcpu
->interruptable
&& (env
->eflags
& IF_MASK
)) {
757 assert(!new_int
.InterruptionPending
);
758 if (cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) {
759 cpu
->interrupt_request
&= ~CPU_INTERRUPT_HARD
;
760 irq
= cpu_get_pic_interrupt(env
);
762 new_int
.InterruptionType
= WHvX64PendingInterrupt
;
763 new_int
.InterruptionPending
= 1;
764 new_int
.InterruptionVector
= irq
;
769 /* Setup interrupt state if new one was prepared */
770 if (new_int
.InterruptionPending
) {
771 reg_values
[reg_count
].PendingInterruption
= new_int
;
772 reg_names
[reg_count
] = WHvRegisterPendingInterruption
;
776 /* Sync the TPR to the CR8 if was modified during the intercept */
777 tpr
= cpu_get_apic_tpr(x86_cpu
->apic_state
);
778 if (tpr
!= vcpu
->tpr
) {
780 reg_values
[reg_count
].Reg64
= tpr
;
781 cpu
->exit_request
= 1;
782 reg_names
[reg_count
] = WHvX64RegisterCr8
;
786 /* Update the state of the interrupt delivery notification */
787 if (!vcpu
->window_registered
&&
788 cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) {
789 reg_values
[reg_count
].DeliverabilityNotifications
.InterruptNotification
791 vcpu
->window_registered
= 1;
792 reg_names
[reg_count
] = WHvX64RegisterDeliverabilityNotifications
;
796 qemu_mutex_unlock_iothread();
799 hr
= whp_dispatch
.WHvSetVirtualProcessorRegisters(
800 whpx
->partition
, cpu
->cpu_index
,
801 reg_names
, reg_count
, reg_values
);
803 error_report("WHPX: Failed to set interrupt state registers,"
811 static void whpx_vcpu_post_run(CPUState
*cpu
)
813 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
814 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
815 X86CPU
*x86_cpu
= X86_CPU(cpu
);
817 env
->eflags
= vcpu
->exit_ctx
.VpContext
.Rflags
;
819 uint64_t tpr
= vcpu
->exit_ctx
.VpContext
.Cr8
;
820 if (vcpu
->tpr
!= tpr
) {
822 qemu_mutex_lock_iothread();
823 cpu_set_apic_tpr(x86_cpu
->apic_state
, vcpu
->tpr
);
824 qemu_mutex_unlock_iothread();
827 vcpu
->interruption_pending
=
828 vcpu
->exit_ctx
.VpContext
.ExecutionState
.InterruptionPending
;
830 vcpu
->interruptable
=
831 !vcpu
->exit_ctx
.VpContext
.ExecutionState
.InterruptShadow
;
836 static void whpx_vcpu_process_async_events(CPUState
*cpu
)
838 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
839 X86CPU
*x86_cpu
= X86_CPU(cpu
);
840 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
842 if ((cpu
->interrupt_request
& CPU_INTERRUPT_INIT
) &&
843 !(env
->hflags
& HF_SMM_MASK
)) {
845 do_cpu_init(x86_cpu
);
846 cpu
->vcpu_dirty
= true;
847 vcpu
->interruptable
= true;
850 if (cpu
->interrupt_request
& CPU_INTERRUPT_POLL
) {
851 cpu
->interrupt_request
&= ~CPU_INTERRUPT_POLL
;
852 apic_poll_irq(x86_cpu
->apic_state
);
855 if (((cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) &&
856 (env
->eflags
& IF_MASK
)) ||
857 (cpu
->interrupt_request
& CPU_INTERRUPT_NMI
)) {
861 if (cpu
->interrupt_request
& CPU_INTERRUPT_SIPI
) {
862 if (!cpu
->vcpu_dirty
) {
863 whpx_get_registers(cpu
);
865 do_cpu_sipi(x86_cpu
);
868 if (cpu
->interrupt_request
& CPU_INTERRUPT_TPR
) {
869 cpu
->interrupt_request
&= ~CPU_INTERRUPT_TPR
;
870 if (!cpu
->vcpu_dirty
) {
871 whpx_get_registers(cpu
);
873 apic_handle_tpr_access_report(x86_cpu
->apic_state
, env
->eip
,
874 env
->tpr_access_type
);
880 static int whpx_vcpu_run(CPUState
*cpu
)
883 struct whpx_state
*whpx
= &whpx_global
;
884 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
887 whpx_vcpu_process_async_events(cpu
);
889 cpu
->exception_index
= EXCP_HLT
;
890 atomic_set(&cpu
->exit_request
, false);
894 qemu_mutex_unlock_iothread();
898 if (cpu
->vcpu_dirty
) {
899 whpx_set_registers(cpu
);
900 cpu
->vcpu_dirty
= false;
903 whpx_vcpu_pre_run(cpu
);
905 if (atomic_read(&cpu
->exit_request
)) {
909 hr
= whp_dispatch
.WHvRunVirtualProcessor(
910 whpx
->partition
, cpu
->cpu_index
,
911 &vcpu
->exit_ctx
, sizeof(vcpu
->exit_ctx
));
914 error_report("WHPX: Failed to exec a virtual processor,"
920 whpx_vcpu_post_run(cpu
);
922 switch (vcpu
->exit_ctx
.ExitReason
) {
923 case WHvRunVpExitReasonMemoryAccess
:
924 ret
= whpx_handle_mmio(cpu
, &vcpu
->exit_ctx
.MemoryAccess
);
927 case WHvRunVpExitReasonX64IoPortAccess
:
928 ret
= whpx_handle_portio(cpu
, &vcpu
->exit_ctx
.IoPortAccess
);
931 case WHvRunVpExitReasonX64InterruptWindow
:
932 vcpu
->window_registered
= 0;
936 case WHvRunVpExitReasonX64Halt
:
937 ret
= whpx_handle_halt(cpu
);
940 case WHvRunVpExitReasonCanceled
:
941 cpu
->exception_index
= EXCP_INTERRUPT
;
945 case WHvRunVpExitReasonX64MsrAccess
: {
946 WHV_REGISTER_VALUE reg_values
[3] = {0};
947 WHV_REGISTER_NAME reg_names
[3];
950 reg_names
[0] = WHvX64RegisterRip
;
951 reg_names
[1] = WHvX64RegisterRax
;
952 reg_names
[2] = WHvX64RegisterRdx
;
954 reg_values
[0].Reg64
=
955 vcpu
->exit_ctx
.VpContext
.Rip
+
956 vcpu
->exit_ctx
.VpContext
.InstructionLength
;
959 * For all unsupported MSR access we:
963 reg_count
= vcpu
->exit_ctx
.MsrAccess
.AccessInfo
.IsWrite
?
966 hr
= whp_dispatch
.WHvSetVirtualProcessorRegisters(
969 reg_names
, reg_count
,
973 error_report("WHPX: Failed to set MsrAccess state "
974 " registers, hr=%08lx", hr
);
979 case WHvRunVpExitReasonX64Cpuid
: {
980 WHV_REGISTER_VALUE reg_values
[5];
981 WHV_REGISTER_NAME reg_names
[5];
982 UINT32 reg_count
= 5;
983 UINT64 rip
, rax
, rcx
, rdx
, rbx
;
985 memset(reg_values
, 0, sizeof(reg_values
));
987 rip
= vcpu
->exit_ctx
.VpContext
.Rip
+
988 vcpu
->exit_ctx
.VpContext
.InstructionLength
;
989 switch (vcpu
->exit_ctx
.CpuidAccess
.Rax
) {
991 rax
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRax
;
992 /* Advertise that we are running on a hypervisor */
994 vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRcx
|
995 CPUID_EXT_HYPERVISOR
;
997 rdx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRdx
;
998 rbx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRbx
;
1001 rax
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRax
;
1002 /* Remove any support of OSVW */
1004 vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRcx
&
1007 rdx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRdx
;
1008 rbx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRbx
;
1011 rax
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRax
;
1012 rcx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRcx
;
1013 rdx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRdx
;
1014 rbx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRbx
;
1017 reg_names
[0] = WHvX64RegisterRip
;
1018 reg_names
[1] = WHvX64RegisterRax
;
1019 reg_names
[2] = WHvX64RegisterRcx
;
1020 reg_names
[3] = WHvX64RegisterRdx
;
1021 reg_names
[4] = WHvX64RegisterRbx
;
1023 reg_values
[0].Reg64
= rip
;
1024 reg_values
[1].Reg64
= rax
;
1025 reg_values
[2].Reg64
= rcx
;
1026 reg_values
[3].Reg64
= rdx
;
1027 reg_values
[4].Reg64
= rbx
;
1029 hr
= whp_dispatch
.WHvSetVirtualProcessorRegisters(
1030 whpx
->partition
, cpu
->cpu_index
,
1036 error_report("WHPX: Failed to set CpuidAccess state registers,"
1042 case WHvRunVpExitReasonNone
:
1043 case WHvRunVpExitReasonUnrecoverableException
:
1044 case WHvRunVpExitReasonInvalidVpRegisterValue
:
1045 case WHvRunVpExitReasonUnsupportedFeature
:
1046 case WHvRunVpExitReasonException
:
1048 error_report("WHPX: Unexpected VP exit code %d",
1049 vcpu
->exit_ctx
.ExitReason
);
1050 whpx_get_registers(cpu
);
1051 qemu_mutex_lock_iothread();
1052 qemu_system_guest_panicked(cpu_get_crash_info(cpu
));
1053 qemu_mutex_unlock_iothread();
1060 qemu_mutex_lock_iothread();
1063 atomic_set(&cpu
->exit_request
, false);
1068 static void do_whpx_cpu_synchronize_state(CPUState
*cpu
, run_on_cpu_data arg
)
1070 whpx_get_registers(cpu
);
1071 cpu
->vcpu_dirty
= true;
1074 static void do_whpx_cpu_synchronize_post_reset(CPUState
*cpu
,
1075 run_on_cpu_data arg
)
1077 whpx_set_registers(cpu
);
1078 cpu
->vcpu_dirty
= false;
1081 static void do_whpx_cpu_synchronize_post_init(CPUState
*cpu
,
1082 run_on_cpu_data arg
)
1084 whpx_set_registers(cpu
);
1085 cpu
->vcpu_dirty
= false;
1088 static void do_whpx_cpu_synchronize_pre_loadvm(CPUState
*cpu
,
1089 run_on_cpu_data arg
)
1091 cpu
->vcpu_dirty
= true;
1098 void whpx_cpu_synchronize_state(CPUState
*cpu
)
1100 if (!cpu
->vcpu_dirty
) {
1101 run_on_cpu(cpu
, do_whpx_cpu_synchronize_state
, RUN_ON_CPU_NULL
);
1105 void whpx_cpu_synchronize_post_reset(CPUState
*cpu
)
1107 run_on_cpu(cpu
, do_whpx_cpu_synchronize_post_reset
, RUN_ON_CPU_NULL
);
1110 void whpx_cpu_synchronize_post_init(CPUState
*cpu
)
1112 run_on_cpu(cpu
, do_whpx_cpu_synchronize_post_init
, RUN_ON_CPU_NULL
);
1115 void whpx_cpu_synchronize_pre_loadvm(CPUState
*cpu
)
1117 run_on_cpu(cpu
, do_whpx_cpu_synchronize_pre_loadvm
, RUN_ON_CPU_NULL
);
1124 static Error
*whpx_migration_blocker
;
1126 int whpx_init_vcpu(CPUState
*cpu
)
1129 struct whpx_state
*whpx
= &whpx_global
;
1130 struct whpx_vcpu
*vcpu
;
1131 Error
*local_error
= NULL
;
1133 /* Add migration blockers for all unsupported features of the
1134 * Windows Hypervisor Platform
1136 if (whpx_migration_blocker
== NULL
) {
1137 error_setg(&whpx_migration_blocker
,
1138 "State blocked due to non-migratable CPUID feature support,"
1139 "dirty memory tracking support, and XSAVE/XRSTOR support");
1141 (void)migrate_add_blocker(whpx_migration_blocker
, &local_error
);
1143 error_report_err(local_error
);
1144 migrate_del_blocker(whpx_migration_blocker
);
1145 error_free(whpx_migration_blocker
);
1150 vcpu
= g_malloc0(sizeof(struct whpx_vcpu
));
1153 error_report("WHPX: Failed to allocte VCPU context.");
1157 hr
= whp_dispatch
.WHvEmulatorCreateEmulator(
1158 &whpx_emu_callbacks
,
1161 error_report("WHPX: Failed to setup instruction completion support,"
1167 hr
= whp_dispatch
.WHvCreateVirtualProcessor(
1168 whpx
->partition
, cpu
->cpu_index
, 0);
1170 error_report("WHPX: Failed to create a virtual processor,"
1172 whp_dispatch
.WHvEmulatorDestroyEmulator(vcpu
->emulator
);
1177 vcpu
->interruptable
= true;
1179 cpu
->vcpu_dirty
= true;
1180 cpu
->hax_vcpu
= (struct hax_vcpu_state
*)vcpu
;
1185 int whpx_vcpu_exec(CPUState
*cpu
)
1191 if (cpu
->exception_index
>= EXCP_INTERRUPT
) {
1192 ret
= cpu
->exception_index
;
1193 cpu
->exception_index
= -1;
1197 fatal
= whpx_vcpu_run(cpu
);
1200 error_report("WHPX: Failed to exec a virtual processor");
1208 void whpx_destroy_vcpu(CPUState
*cpu
)
1210 struct whpx_state
*whpx
= &whpx_global
;
1211 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
1213 whp_dispatch
.WHvDeleteVirtualProcessor(whpx
->partition
, cpu
->cpu_index
);
1214 whp_dispatch
.WHvEmulatorDestroyEmulator(vcpu
->emulator
);
1215 g_free(cpu
->hax_vcpu
);
1219 void whpx_vcpu_kick(CPUState
*cpu
)
1221 struct whpx_state
*whpx
= &whpx_global
;
1222 whp_dispatch
.WHvCancelRunVirtualProcessor(
1223 whpx
->partition
, cpu
->cpu_index
, 0);
1230 static void whpx_update_mapping(hwaddr start_pa
, ram_addr_t size
,
1231 void *host_va
, int add
, int rom
,
1234 struct whpx_state
*whpx
= &whpx_global
;
1239 printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
1240 (void*)start_pa, (void*)size, host_va,
1241 (rom ? "ROM" : "RAM"), name);
1243 printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
1244 (void*)start_pa, (void*)size, host_va, name);
1249 hr
= whp_dispatch
.WHvMapGpaRange(whpx
->partition
,
1253 (WHvMapGpaRangeFlagRead
|
1254 WHvMapGpaRangeFlagExecute
|
1255 (rom
? 0 : WHvMapGpaRangeFlagWrite
)));
1257 hr
= whp_dispatch
.WHvUnmapGpaRange(whpx
->partition
,
1263 error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
1264 " Host:%p, hr=%08lx",
1265 (add
? "MAP" : "UNMAP"), name
,
1266 (void *)(uintptr_t)start_pa
, (void *)size
, host_va
, hr
);
1270 static void whpx_process_section(MemoryRegionSection
*section
, int add
)
1272 MemoryRegion
*mr
= section
->mr
;
1273 hwaddr start_pa
= section
->offset_within_address_space
;
1274 ram_addr_t size
= int128_get64(section
->size
);
1278 if (!memory_region_is_ram(mr
)) {
1282 delta
= qemu_real_host_page_size
- (start_pa
& ~qemu_real_host_page_mask
);
1283 delta
&= ~qemu_real_host_page_mask
;
1289 size
&= qemu_real_host_page_mask
;
1290 if (!size
|| (start_pa
& ~qemu_real_host_page_mask
)) {
1294 host_va
= (uintptr_t)memory_region_get_ram_ptr(mr
)
1295 + section
->offset_within_region
+ delta
;
1297 whpx_update_mapping(start_pa
, size
, (void *)(uintptr_t)host_va
, add
,
1298 memory_region_is_rom(mr
), mr
->name
);
1301 static void whpx_region_add(MemoryListener
*listener
,
1302 MemoryRegionSection
*section
)
1304 memory_region_ref(section
->mr
);
1305 whpx_process_section(section
, 1);
1308 static void whpx_region_del(MemoryListener
*listener
,
1309 MemoryRegionSection
*section
)
1311 whpx_process_section(section
, 0);
1312 memory_region_unref(section
->mr
);
1315 static void whpx_transaction_begin(MemoryListener
*listener
)
1319 static void whpx_transaction_commit(MemoryListener
*listener
)
1323 static void whpx_log_sync(MemoryListener
*listener
,
1324 MemoryRegionSection
*section
)
1326 MemoryRegion
*mr
= section
->mr
;
1328 if (!memory_region_is_ram(mr
)) {
1332 memory_region_set_dirty(mr
, 0, int128_get64(section
->size
));
1335 static MemoryListener whpx_memory_listener
= {
1336 .begin
= whpx_transaction_begin
,
1337 .commit
= whpx_transaction_commit
,
1338 .region_add
= whpx_region_add
,
1339 .region_del
= whpx_region_del
,
1340 .log_sync
= whpx_log_sync
,
1344 static void whpx_memory_init(void)
1346 memory_listener_register(&whpx_memory_listener
, &address_space_memory
);
1349 static void whpx_handle_interrupt(CPUState
*cpu
, int mask
)
1351 cpu
->interrupt_request
|= mask
;
1353 if (!qemu_cpu_is_self(cpu
)) {
1359 * Load the functions from the given library, using the given handle. If a
1360 * handle is provided, it is used, otherwise the library is opened. The
1361 * handle will be updated on return with the opened one.
1363 static bool load_whp_dispatch_fns(HMODULE
*handle
,
1364 WHPFunctionList function_list
)
1366 HMODULE hLib
= *handle
;
1368 #define WINHV_PLATFORM_DLL "WinHvPlatform.dll"
1369 #define WINHV_EMULATION_DLL "WinHvEmulation.dll"
1370 #define WHP_LOAD_FIELD(return_type, function_name, signature) \
1371 whp_dispatch.function_name = \
1372 (function_name ## _t)GetProcAddress(hLib, #function_name); \
1373 if (!whp_dispatch.function_name) { \
1374 error_report("Could not load function %s", #function_name); \
1378 #define WHP_LOAD_LIB(lib_name, handle_lib) \
1379 if (!handle_lib) { \
1380 handle_lib = LoadLibrary(lib_name); \
1381 if (!handle_lib) { \
1382 error_report("Could not load library %s.", lib_name); \
1387 switch (function_list) {
1388 case WINHV_PLATFORM_FNS_DEFAULT
:
1389 WHP_LOAD_LIB(WINHV_PLATFORM_DLL
, hLib
)
1390 LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD
)
1393 case WINHV_EMULATION_FNS_DEFAULT
:
1394 WHP_LOAD_LIB(WINHV_EMULATION_DLL
, hLib
)
1395 LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD
)
1414 static int whpx_accel_init(MachineState
*ms
)
1416 struct whpx_state
*whpx
;
1419 WHV_CAPABILITY whpx_cap
;
1420 UINT32 whpx_cap_size
;
1421 WHV_PARTITION_PROPERTY prop
;
1423 whpx
= &whpx_global
;
1425 if (!init_whp_dispatch()) {
1430 memset(whpx
, 0, sizeof(struct whpx_state
));
1431 whpx
->mem_quota
= ms
->ram_size
;
1433 hr
= whp_dispatch
.WHvGetCapability(
1434 WHvCapabilityCodeHypervisorPresent
, &whpx_cap
,
1435 sizeof(whpx_cap
), &whpx_cap_size
);
1436 if (FAILED(hr
) || !whpx_cap
.HypervisorPresent
) {
1437 error_report("WHPX: No accelerator found, hr=%08lx", hr
);
1442 hr
= whp_dispatch
.WHvCreatePartition(&whpx
->partition
);
1444 error_report("WHPX: Failed to create partition, hr=%08lx", hr
);
1449 memset(&prop
, 0, sizeof(WHV_PARTITION_PROPERTY
));
1450 prop
.ProcessorCount
= ms
->smp
.cpus
;
1451 hr
= whp_dispatch
.WHvSetPartitionProperty(
1453 WHvPartitionPropertyCodeProcessorCount
,
1455 sizeof(WHV_PARTITION_PROPERTY
));
1458 error_report("WHPX: Failed to set partition core count to %d,"
1459 " hr=%08lx", ms
->smp
.cores
, hr
);
1464 memset(&prop
, 0, sizeof(WHV_PARTITION_PROPERTY
));
1465 prop
.ExtendedVmExits
.X64MsrExit
= 1;
1466 prop
.ExtendedVmExits
.X64CpuidExit
= 1;
1467 hr
= whp_dispatch
.WHvSetPartitionProperty(
1469 WHvPartitionPropertyCodeExtendedVmExits
,
1471 sizeof(WHV_PARTITION_PROPERTY
));
1474 error_report("WHPX: Failed to enable partition extended X64MsrExit and"
1475 " X64CpuidExit hr=%08lx", hr
);
1480 UINT32 cpuidExitList
[] = {1, 0x80000001};
1481 hr
= whp_dispatch
.WHvSetPartitionProperty(
1483 WHvPartitionPropertyCodeCpuidExitList
,
1485 RTL_NUMBER_OF(cpuidExitList
) * sizeof(UINT32
));
1488 error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1494 hr
= whp_dispatch
.WHvSetupPartition(whpx
->partition
);
1496 error_report("WHPX: Failed to setup partition, hr=%08lx", hr
);
1503 cpu_interrupt_handler
= whpx_handle_interrupt
;
1505 printf("Windows Hypervisor Platform accelerator is operational\n");
1510 if (NULL
!= whpx
->partition
) {
1511 whp_dispatch
.WHvDeletePartition(whpx
->partition
);
1512 whpx
->partition
= NULL
;
1519 int whpx_enabled(void)
1521 return whpx_allowed
;
1524 static void whpx_accel_class_init(ObjectClass
*oc
, void *data
)
1526 AccelClass
*ac
= ACCEL_CLASS(oc
);
1528 ac
->init_machine
= whpx_accel_init
;
1529 ac
->allowed
= &whpx_allowed
;
1532 static const TypeInfo whpx_accel_type
= {
1533 .name
= ACCEL_CLASS_NAME("whpx"),
1534 .parent
= TYPE_ACCEL
,
1535 .class_init
= whpx_accel_class_init
,
1538 static void whpx_type_init(void)
1540 type_register_static(&whpx_accel_type
);
1543 bool init_whp_dispatch(void)
1545 if (whp_dispatch_initialized
) {
1549 if (!load_whp_dispatch_fns(&hWinHvPlatform
, WINHV_PLATFORM_FNS_DEFAULT
)) {
1553 if (!load_whp_dispatch_fns(&hWinHvEmulation
, WINHV_EMULATION_FNS_DEFAULT
)) {
1557 whp_dispatch_initialized
= true;
1561 if (hWinHvPlatform
) {
1562 FreeLibrary(hWinHvPlatform
);
1565 if (hWinHvEmulation
) {
1566 FreeLibrary(hWinHvEmulation
);
1572 type_init(whpx_type_init
);