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/exec-all.h"
15 #include "exec/ioport.h"
16 #include "qemu-common.h"
18 #include "sysemu/accel.h"
19 #include "sysemu/whpx.h"
20 #include "sysemu/sysemu.h"
21 #include "sysemu/cpus.h"
22 #include "qemu/main-loop.h"
23 #include "hw/boards.h"
24 #include "qemu/error-report.h"
25 #include "qemu/queue.h"
26 #include "qapi/error.h"
27 #include "migration/blocker.h"
29 #include <winhvplatform.h>
30 #include <winhvemulation.h>
34 WHV_PARTITION_HANDLE partition
;
35 uint32_t exit_ctx_size
;
38 static const WHV_REGISTER_NAME whpx_register_names
[] = {
40 /* X64 General purpose registers */
60 /* X64 Segment registers */
70 /* X64 Table registers */
74 /* X64 Control Registers */
81 /* X64 Debug Registers */
91 /* X64 Floating Point and Vector Registers */
108 WHvX64RegisterFpMmx0
,
109 WHvX64RegisterFpMmx1
,
110 WHvX64RegisterFpMmx2
,
111 WHvX64RegisterFpMmx3
,
112 WHvX64RegisterFpMmx4
,
113 WHvX64RegisterFpMmx5
,
114 WHvX64RegisterFpMmx6
,
115 WHvX64RegisterFpMmx7
,
116 WHvX64RegisterFpControlStatus
,
117 WHvX64RegisterXmmControlStatus
,
123 WHvX64RegisterKernelGsBase
,
125 WHvX64RegisterApicBase
,
126 /* WHvX64RegisterPat, */
127 WHvX64RegisterSysenterCs
,
128 WHvX64RegisterSysenterEip
,
129 WHvX64RegisterSysenterEsp
,
134 WHvX64RegisterSfmask
,
137 /* Interrupt / Event Registers */
139 * WHvRegisterPendingInterruption,
140 * WHvRegisterInterruptState,
141 * WHvRegisterPendingEvent0,
142 * WHvRegisterPendingEvent1
143 * WHvX64RegisterDeliverabilityNotifications,
147 struct whpx_register_set
{
148 WHV_REGISTER_VALUE values
[RTL_NUMBER_OF(whpx_register_names
)];
152 WHV_EMULATOR_HANDLE emulator
;
153 bool window_registered
;
157 WHV_X64_PENDING_INTERRUPTION_REGISTER interrupt_in_flight
;
159 /* Must be the last field as it may have a tail */
160 WHV_RUN_VP_EXIT_CONTEXT exit_ctx
;
163 static bool whpx_allowed
;
165 struct whpx_state whpx_global
;
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
= {0};
230 assert(cpu_is_stopped(cpu
) || qemu_cpu_is_self(cpu
));
232 v86
= (env
->eflags
& VM_MASK
);
233 r86
= !(env
->cr
[0] & CR0_PE_MASK
);
235 vcpu
->tpr
= cpu_get_apic_tpr(x86_cpu
->apic_state
);
236 vcpu
->apic_base
= cpu_get_apic_base(x86_cpu
->apic_state
);
238 /* Indexes for first 16 registers match between HV and QEMU definitions */
239 for (idx
= 0; idx
< CPU_NB_REGS64
; idx
+= 1) {
240 vcxt
.values
[idx
].Reg64
= env
->regs
[idx
];
243 /* Same goes for RIP and RFLAGS */
244 assert(whpx_register_names
[idx
] == WHvX64RegisterRip
);
245 vcxt
.values
[idx
++].Reg64
= env
->eip
;
247 assert(whpx_register_names
[idx
] == WHvX64RegisterRflags
);
248 vcxt
.values
[idx
++].Reg64
= env
->eflags
;
250 /* Translate 6+4 segment registers. HV and QEMU order matches */
251 assert(idx
== WHvX64RegisterEs
);
252 for (i
= 0; i
< 6; i
+= 1, idx
+= 1) {
253 vcxt
.values
[idx
].Segment
= whpx_seg_q2h(&env
->segs
[i
], v86
, r86
);
256 assert(idx
== WHvX64RegisterLdtr
);
257 vcxt
.values
[idx
++].Segment
= whpx_seg_q2h(&env
->ldt
, 0, 0);
259 assert(idx
== WHvX64RegisterTr
);
260 vcxt
.values
[idx
++].Segment
= whpx_seg_q2h(&env
->tr
, 0, 0);
262 assert(idx
== WHvX64RegisterIdtr
);
263 vcxt
.values
[idx
].Table
.Base
= env
->idt
.base
;
264 vcxt
.values
[idx
].Table
.Limit
= env
->idt
.limit
;
267 assert(idx
== WHvX64RegisterGdtr
);
268 vcxt
.values
[idx
].Table
.Base
= env
->gdt
.base
;
269 vcxt
.values
[idx
].Table
.Limit
= env
->gdt
.limit
;
272 /* CR0, 2, 3, 4, 8 */
273 assert(whpx_register_names
[idx
] == WHvX64RegisterCr0
);
274 vcxt
.values
[idx
++].Reg64
= env
->cr
[0];
275 assert(whpx_register_names
[idx
] == WHvX64RegisterCr2
);
276 vcxt
.values
[idx
++].Reg64
= env
->cr
[2];
277 assert(whpx_register_names
[idx
] == WHvX64RegisterCr3
);
278 vcxt
.values
[idx
++].Reg64
= env
->cr
[3];
279 assert(whpx_register_names
[idx
] == WHvX64RegisterCr4
);
280 vcxt
.values
[idx
++].Reg64
= env
->cr
[4];
281 assert(whpx_register_names
[idx
] == WHvX64RegisterCr8
);
282 vcxt
.values
[idx
++].Reg64
= vcpu
->tpr
;
284 /* 8 Debug Registers - Skipped */
286 /* 16 XMM registers */
287 assert(whpx_register_names
[idx
] == WHvX64RegisterXmm0
);
288 for (i
= 0; i
< 16; i
+= 1, idx
+= 1) {
289 vcxt
.values
[idx
].Reg128
.Low64
= env
->xmm_regs
[i
].ZMM_Q(0);
290 vcxt
.values
[idx
].Reg128
.High64
= env
->xmm_regs
[i
].ZMM_Q(1);
294 assert(whpx_register_names
[idx
] == WHvX64RegisterFpMmx0
);
295 for (i
= 0; i
< 8; i
+= 1, idx
+= 1) {
296 vcxt
.values
[idx
].Fp
.AsUINT128
.Low64
= env
->fpregs
[i
].mmx
.MMX_Q(0);
297 /* vcxt.values[idx].Fp.AsUINT128.High64 =
298 env->fpregs[i].mmx.MMX_Q(1);
302 /* FP control status register */
303 assert(whpx_register_names
[idx
] == WHvX64RegisterFpControlStatus
);
304 vcxt
.values
[idx
].FpControlStatus
.FpControl
= env
->fpuc
;
305 vcxt
.values
[idx
].FpControlStatus
.FpStatus
=
306 (env
->fpus
& ~0x3800) | (env
->fpstt
& 0x7) << 11;
307 vcxt
.values
[idx
].FpControlStatus
.FpTag
= 0;
308 for (i
= 0; i
< 8; ++i
) {
309 vcxt
.values
[idx
].FpControlStatus
.FpTag
|= (!env
->fptags
[i
]) << i
;
311 vcxt
.values
[idx
].FpControlStatus
.Reserved
= 0;
312 vcxt
.values
[idx
].FpControlStatus
.LastFpOp
= env
->fpop
;
313 vcxt
.values
[idx
].FpControlStatus
.LastFpRip
= env
->fpip
;
316 /* XMM control status register */
317 assert(whpx_register_names
[idx
] == WHvX64RegisterXmmControlStatus
);
318 vcxt
.values
[idx
].XmmControlStatus
.LastFpRdp
= 0;
319 vcxt
.values
[idx
].XmmControlStatus
.XmmStatusControl
= env
->mxcsr
;
320 vcxt
.values
[idx
].XmmControlStatus
.XmmStatusControlMask
= 0x0000ffff;
324 assert(whpx_register_names
[idx
] == WHvX64RegisterTsc
);
325 vcxt
.values
[idx
++].Reg64
= env
->tsc
;
326 assert(whpx_register_names
[idx
] == WHvX64RegisterEfer
);
327 vcxt
.values
[idx
++].Reg64
= env
->efer
;
329 assert(whpx_register_names
[idx
] == WHvX64RegisterKernelGsBase
);
330 vcxt
.values
[idx
++].Reg64
= env
->kernelgsbase
;
333 assert(whpx_register_names
[idx
] == WHvX64RegisterApicBase
);
334 vcxt
.values
[idx
++].Reg64
= vcpu
->apic_base
;
336 /* WHvX64RegisterPat - Skipped */
338 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterCs
);
339 vcxt
.values
[idx
++].Reg64
= env
->sysenter_cs
;
340 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEip
);
341 vcxt
.values
[idx
++].Reg64
= env
->sysenter_eip
;
342 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEsp
);
343 vcxt
.values
[idx
++].Reg64
= env
->sysenter_esp
;
344 assert(whpx_register_names
[idx
] == WHvX64RegisterStar
);
345 vcxt
.values
[idx
++].Reg64
= env
->star
;
347 assert(whpx_register_names
[idx
] == WHvX64RegisterLstar
);
348 vcxt
.values
[idx
++].Reg64
= env
->lstar
;
349 assert(whpx_register_names
[idx
] == WHvX64RegisterCstar
);
350 vcxt
.values
[idx
++].Reg64
= env
->cstar
;
351 assert(whpx_register_names
[idx
] == WHvX64RegisterSfmask
);
352 vcxt
.values
[idx
++].Reg64
= env
->fmask
;
355 /* Interrupt / Event Registers - Skipped */
357 assert(idx
== RTL_NUMBER_OF(whpx_register_names
));
359 hr
= WHvSetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
361 RTL_NUMBER_OF(whpx_register_names
),
365 error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
373 static void whpx_get_registers(CPUState
*cpu
)
375 struct whpx_state
*whpx
= &whpx_global
;
376 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
377 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
378 X86CPU
*x86_cpu
= X86_CPU(cpu
);
379 struct whpx_register_set vcxt
;
380 uint64_t tpr
, apic_base
;
385 assert(cpu_is_stopped(cpu
) || qemu_cpu_is_self(cpu
));
387 hr
= WHvGetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
389 RTL_NUMBER_OF(whpx_register_names
),
392 error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
397 /* Indexes for first 16 registers match between HV and QEMU definitions */
398 for (idx
= 0; idx
< CPU_NB_REGS64
; idx
+= 1) {
399 env
->regs
[idx
] = vcxt
.values
[idx
].Reg64
;
402 /* Same goes for RIP and RFLAGS */
403 assert(whpx_register_names
[idx
] == WHvX64RegisterRip
);
404 env
->eip
= vcxt
.values
[idx
++].Reg64
;
405 assert(whpx_register_names
[idx
] == WHvX64RegisterRflags
);
406 env
->eflags
= vcxt
.values
[idx
++].Reg64
;
408 /* Translate 6+4 segment registers. HV and QEMU order matches */
409 assert(idx
== WHvX64RegisterEs
);
410 for (i
= 0; i
< 6; i
+= 1, idx
+= 1) {
411 env
->segs
[i
] = whpx_seg_h2q(&vcxt
.values
[idx
].Segment
);
414 assert(idx
== WHvX64RegisterLdtr
);
415 env
->ldt
= whpx_seg_h2q(&vcxt
.values
[idx
++].Segment
);
416 assert(idx
== WHvX64RegisterTr
);
417 env
->tr
= whpx_seg_h2q(&vcxt
.values
[idx
++].Segment
);
418 assert(idx
== WHvX64RegisterIdtr
);
419 env
->idt
.base
= vcxt
.values
[idx
].Table
.Base
;
420 env
->idt
.limit
= vcxt
.values
[idx
].Table
.Limit
;
422 assert(idx
== WHvX64RegisterGdtr
);
423 env
->gdt
.base
= vcxt
.values
[idx
].Table
.Base
;
424 env
->gdt
.limit
= vcxt
.values
[idx
].Table
.Limit
;
427 /* CR0, 2, 3, 4, 8 */
428 assert(whpx_register_names
[idx
] == WHvX64RegisterCr0
);
429 env
->cr
[0] = vcxt
.values
[idx
++].Reg64
;
430 assert(whpx_register_names
[idx
] == WHvX64RegisterCr2
);
431 env
->cr
[2] = vcxt
.values
[idx
++].Reg64
;
432 assert(whpx_register_names
[idx
] == WHvX64RegisterCr3
);
433 env
->cr
[3] = vcxt
.values
[idx
++].Reg64
;
434 assert(whpx_register_names
[idx
] == WHvX64RegisterCr4
);
435 env
->cr
[4] = vcxt
.values
[idx
++].Reg64
;
436 assert(whpx_register_names
[idx
] == WHvX64RegisterCr8
);
437 tpr
= vcxt
.values
[idx
++].Reg64
;
438 if (tpr
!= vcpu
->tpr
) {
440 cpu_set_apic_tpr(x86_cpu
->apic_state
, tpr
);
443 /* 8 Debug Registers - Skipped */
445 /* 16 XMM registers */
446 assert(whpx_register_names
[idx
] == WHvX64RegisterXmm0
);
447 for (i
= 0; i
< 16; i
+= 1, idx
+= 1) {
448 env
->xmm_regs
[i
].ZMM_Q(0) = vcxt
.values
[idx
].Reg128
.Low64
;
449 env
->xmm_regs
[i
].ZMM_Q(1) = vcxt
.values
[idx
].Reg128
.High64
;
453 assert(whpx_register_names
[idx
] == WHvX64RegisterFpMmx0
);
454 for (i
= 0; i
< 8; i
+= 1, idx
+= 1) {
455 env
->fpregs
[i
].mmx
.MMX_Q(0) = vcxt
.values
[idx
].Fp
.AsUINT128
.Low64
;
456 /* env->fpregs[i].mmx.MMX_Q(1) =
457 vcxt.values[idx].Fp.AsUINT128.High64;
461 /* FP control status register */
462 assert(whpx_register_names
[idx
] == WHvX64RegisterFpControlStatus
);
463 env
->fpuc
= vcxt
.values
[idx
].FpControlStatus
.FpControl
;
464 env
->fpstt
= (vcxt
.values
[idx
].FpControlStatus
.FpStatus
>> 11) & 0x7;
465 env
->fpus
= vcxt
.values
[idx
].FpControlStatus
.FpStatus
& ~0x3800;
466 for (i
= 0; i
< 8; ++i
) {
467 env
->fptags
[i
] = !((vcxt
.values
[idx
].FpControlStatus
.FpTag
>> i
) & 1);
469 env
->fpop
= vcxt
.values
[idx
].FpControlStatus
.LastFpOp
;
470 env
->fpip
= vcxt
.values
[idx
].FpControlStatus
.LastFpRip
;
473 /* XMM control status register */
474 assert(whpx_register_names
[idx
] == WHvX64RegisterXmmControlStatus
);
475 env
->mxcsr
= vcxt
.values
[idx
].XmmControlStatus
.XmmStatusControl
;
479 assert(whpx_register_names
[idx
] == WHvX64RegisterTsc
);
480 env
->tsc
= vcxt
.values
[idx
++].Reg64
;
481 assert(whpx_register_names
[idx
] == WHvX64RegisterEfer
);
482 env
->efer
= vcxt
.values
[idx
++].Reg64
;
484 assert(whpx_register_names
[idx
] == WHvX64RegisterKernelGsBase
);
485 env
->kernelgsbase
= vcxt
.values
[idx
++].Reg64
;
488 assert(whpx_register_names
[idx
] == WHvX64RegisterApicBase
);
489 apic_base
= vcxt
.values
[idx
++].Reg64
;
490 if (apic_base
!= vcpu
->apic_base
) {
491 vcpu
->apic_base
= apic_base
;
492 cpu_set_apic_base(x86_cpu
->apic_state
, vcpu
->apic_base
);
495 /* WHvX64RegisterPat - Skipped */
497 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterCs
);
498 env
->sysenter_cs
= vcxt
.values
[idx
++].Reg64
;;
499 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEip
);
500 env
->sysenter_eip
= vcxt
.values
[idx
++].Reg64
;
501 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEsp
);
502 env
->sysenter_esp
= vcxt
.values
[idx
++].Reg64
;
503 assert(whpx_register_names
[idx
] == WHvX64RegisterStar
);
504 env
->star
= vcxt
.values
[idx
++].Reg64
;
506 assert(whpx_register_names
[idx
] == WHvX64RegisterLstar
);
507 env
->lstar
= vcxt
.values
[idx
++].Reg64
;
508 assert(whpx_register_names
[idx
] == WHvX64RegisterCstar
);
509 env
->cstar
= vcxt
.values
[idx
++].Reg64
;
510 assert(whpx_register_names
[idx
] == WHvX64RegisterSfmask
);
511 env
->fmask
= vcxt
.values
[idx
++].Reg64
;
514 /* Interrupt / Event Registers - Skipped */
516 assert(idx
== RTL_NUMBER_OF(whpx_register_names
));
521 static HRESULT CALLBACK
whpx_emu_ioport_callback(
523 WHV_EMULATOR_IO_ACCESS_INFO
*IoAccess
)
525 MemTxAttrs attrs
= { 0 };
526 address_space_rw(&address_space_io
, IoAccess
->Port
, attrs
,
527 (uint8_t *)&IoAccess
->Data
, IoAccess
->AccessSize
,
528 IoAccess
->Direction
);
532 static HRESULT CALLBACK
whpx_emu_memio_callback(
534 WHV_EMULATOR_MEMORY_ACCESS_INFO
*ma
)
536 cpu_physical_memory_rw(ma
->GpaAddress
, ma
->Data
, ma
->AccessSize
,
541 static HRESULT CALLBACK
whpx_emu_getreg_callback(
543 const WHV_REGISTER_NAME
*RegisterNames
,
544 UINT32 RegisterCount
,
545 WHV_REGISTER_VALUE
*RegisterValues
)
548 struct whpx_state
*whpx
= &whpx_global
;
549 CPUState
*cpu
= (CPUState
*)ctx
;
551 hr
= WHvGetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
552 RegisterNames
, RegisterCount
,
555 error_report("WHPX: Failed to get virtual processor registers,"
563 static HRESULT CALLBACK
whpx_emu_setreg_callback(
565 const WHV_REGISTER_NAME
*RegisterNames
,
566 UINT32 RegisterCount
,
567 const WHV_REGISTER_VALUE
*RegisterValues
)
570 struct whpx_state
*whpx
= &whpx_global
;
571 CPUState
*cpu
= (CPUState
*)ctx
;
573 hr
= WHvSetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
574 RegisterNames
, RegisterCount
,
577 error_report("WHPX: Failed to set virtual processor registers,"
583 * The emulator just successfully wrote the register state. We clear the
584 * dirty state so we avoid the double write on resume of the VP.
586 cpu
->vcpu_dirty
= false;
591 static HRESULT CALLBACK
whpx_emu_translate_callback(
593 WHV_GUEST_VIRTUAL_ADDRESS Gva
,
594 WHV_TRANSLATE_GVA_FLAGS TranslateFlags
,
595 WHV_TRANSLATE_GVA_RESULT_CODE
*TranslationResult
,
596 WHV_GUEST_PHYSICAL_ADDRESS
*Gpa
)
599 struct whpx_state
*whpx
= &whpx_global
;
600 CPUState
*cpu
= (CPUState
*)ctx
;
601 WHV_TRANSLATE_GVA_RESULT res
;
603 hr
= WHvTranslateGva(whpx
->partition
, cpu
->cpu_index
,
604 Gva
, TranslateFlags
, &res
, Gpa
);
606 error_report("WHPX: Failed to translate GVA, hr=%08lx", hr
);
609 *TranslationResult
= res
.ResultCode
;
615 static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks
= {
616 .WHvEmulatorIoPortCallback
= whpx_emu_ioport_callback
,
617 .WHvEmulatorMemoryCallback
= whpx_emu_memio_callback
,
618 .WHvEmulatorGetVirtualProcessorRegisters
= whpx_emu_getreg_callback
,
619 .WHvEmulatorSetVirtualProcessorRegisters
= whpx_emu_setreg_callback
,
620 .WHvEmulatorTranslateGvaPage
= whpx_emu_translate_callback
,
623 static int whpx_handle_mmio(CPUState
*cpu
, WHV_MEMORY_ACCESS_CONTEXT
*ctx
)
626 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
627 WHV_EMULATOR_STATUS emu_status
;
629 hr
= WHvEmulatorTryMmioEmulation(vcpu
->emulator
, cpu
, ctx
, &emu_status
);
632 error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr
);
636 if (!emu_status
.EmulationSuccessful
) {
638 error_report("WHPX: Failed to emulate MMIO access");
645 static int whpx_handle_portio(CPUState
*cpu
,
646 WHV_X64_IO_PORT_ACCESS_CONTEXT
*ctx
)
649 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
650 WHV_EMULATOR_STATUS emu_status
;
652 hr
= WHvEmulatorTryIoEmulation(vcpu
->emulator
, cpu
, ctx
, &emu_status
);
655 error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr
);
659 if (!emu_status
.EmulationSuccessful
) {
661 error_report("WHPX: Failed to emulate PortMMIO access");
668 static int whpx_handle_halt(CPUState
*cpu
)
670 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
673 qemu_mutex_lock_iothread();
674 if (!((cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) &&
675 (env
->eflags
& IF_MASK
)) &&
676 !(cpu
->interrupt_request
& CPU_INTERRUPT_NMI
)) {
677 cpu
->exception_index
= EXCP_HLT
;
681 qemu_mutex_unlock_iothread();
686 static void whpx_vcpu_pre_run(CPUState
*cpu
)
689 struct whpx_state
*whpx
= &whpx_global
;
690 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
691 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
692 X86CPU
*x86_cpu
= X86_CPU(cpu
);
694 WHV_X64_PENDING_INTERRUPTION_REGISTER new_int
= {0};
695 UINT32 reg_count
= 0;
696 WHV_REGISTER_VALUE reg_values
[3] = {0};
697 WHV_REGISTER_NAME reg_names
[3];
699 qemu_mutex_lock_iothread();
702 if (!vcpu
->interrupt_in_flight
.InterruptionPending
&&
703 cpu
->interrupt_request
& (CPU_INTERRUPT_NMI
| CPU_INTERRUPT_SMI
)) {
704 if (cpu
->interrupt_request
& CPU_INTERRUPT_NMI
) {
705 cpu
->interrupt_request
&= ~CPU_INTERRUPT_NMI
;
706 vcpu
->interruptable
= false;
707 new_int
.InterruptionType
= WHvX64PendingNmi
;
708 new_int
.InterruptionPending
= 1;
709 new_int
.InterruptionVector
= 2;
711 if (cpu
->interrupt_request
& CPU_INTERRUPT_SMI
) {
712 qemu_mutex_lock_iothread();
713 cpu
->interrupt_request
&= ~CPU_INTERRUPT_SMI
;
715 qemu_mutex_unlock_iothread();
720 * Force the VCPU out of its inner loop to process any INIT requests or
721 * commit pending TPR access.
723 if (cpu
->interrupt_request
& (CPU_INTERRUPT_INIT
| CPU_INTERRUPT_TPR
)) {
724 if ((cpu
->interrupt_request
& CPU_INTERRUPT_INIT
) &&
725 !(env
->hflags
& HF_SMM_MASK
)) {
726 cpu
->exit_request
= 1;
728 if (cpu
->interrupt_request
& CPU_INTERRUPT_TPR
) {
729 cpu
->exit_request
= 1;
733 /* Get pending hard interruption or replay one that was overwritten */
734 if (!vcpu
->interrupt_in_flight
.InterruptionPending
&&
735 vcpu
->interruptable
&& (env
->eflags
& IF_MASK
)) {
736 assert(!new_int
.InterruptionPending
);
737 if (cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) {
738 cpu
->interrupt_request
&= ~CPU_INTERRUPT_HARD
;
739 irq
= cpu_get_pic_interrupt(env
);
741 new_int
.InterruptionType
= WHvX64PendingInterrupt
;
742 new_int
.InterruptionPending
= 1;
743 new_int
.InterruptionVector
= irq
;
748 /* Setup interrupt state if new one was prepared */
749 if (new_int
.InterruptionPending
) {
750 reg_values
[reg_count
].PendingInterruption
= new_int
;
751 reg_names
[reg_count
] = WHvRegisterPendingInterruption
;
755 /* Sync the TPR to the CR8 if was modified during the intercept */
756 reg_values
[reg_count
].Reg64
= cpu_get_apic_tpr(x86_cpu
->apic_state
);
757 if (reg_values
[reg_count
].Reg64
!= vcpu
->tpr
) {
758 vcpu
->tpr
= reg_values
[reg_count
].Reg64
;
759 cpu
->exit_request
= 1;
760 reg_names
[reg_count
] = WHvX64RegisterCr8
;
764 /* Update the state of the interrupt delivery notification */
765 if (cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) {
766 reg_values
[reg_count
].DeliverabilityNotifications
.InterruptNotification
768 if (vcpu
->window_registered
!= 1) {
769 vcpu
->window_registered
= 1;
771 reg_names
[reg_count
] = WHvX64RegisterDeliverabilityNotifications
;
775 qemu_mutex_unlock_iothread();
778 hr
= WHvSetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
779 reg_names
, reg_count
, reg_values
);
781 error_report("WHPX: Failed to set interrupt state registers,"
790 static void whpx_vcpu_post_run(CPUState
*cpu
)
793 struct whpx_state
*whpx
= &whpx_global
;
794 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
795 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
796 X86CPU
*x86_cpu
= X86_CPU(cpu
);
797 WHV_REGISTER_VALUE reg_values
[4];
798 const WHV_REGISTER_NAME reg_names
[4] = {
799 WHvX64RegisterRflags
,
801 WHvRegisterPendingInterruption
,
802 WHvRegisterInterruptState
,
805 hr
= WHvGetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
806 reg_names
, 4, reg_values
);
808 error_report("WHPX: Failed to get interrupt state regusters,"
811 vcpu
->interruptable
= false;
815 assert(reg_names
[0] == WHvX64RegisterRflags
);
816 env
->eflags
= reg_values
[0].Reg64
;
818 assert(reg_names
[1] == WHvX64RegisterCr8
);
819 if (vcpu
->tpr
!= reg_values
[1].Reg64
) {
820 vcpu
->tpr
= reg_values
[1].Reg64
;
821 qemu_mutex_lock_iothread();
822 cpu_set_apic_tpr(x86_cpu
->apic_state
, vcpu
->tpr
);
823 qemu_mutex_unlock_iothread();
826 assert(reg_names
[2] == WHvRegisterPendingInterruption
);
827 vcpu
->interrupt_in_flight
= reg_values
[2].PendingInterruption
;
829 assert(reg_names
[3] == WHvRegisterInterruptState
);
830 vcpu
->interruptable
= !reg_values
[3].InterruptState
.InterruptShadow
;
835 static void whpx_vcpu_process_async_events(CPUState
*cpu
)
837 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
838 X86CPU
*x86_cpu
= X86_CPU(cpu
);
839 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
841 if ((cpu
->interrupt_request
& CPU_INTERRUPT_INIT
) &&
842 !(env
->hflags
& HF_SMM_MASK
)) {
844 do_cpu_init(x86_cpu
);
845 cpu
->vcpu_dirty
= true;
846 vcpu
->interruptable
= true;
849 if (cpu
->interrupt_request
& CPU_INTERRUPT_POLL
) {
850 cpu
->interrupt_request
&= ~CPU_INTERRUPT_POLL
;
851 apic_poll_irq(x86_cpu
->apic_state
);
854 if (((cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) &&
855 (env
->eflags
& IF_MASK
)) ||
856 (cpu
->interrupt_request
& CPU_INTERRUPT_NMI
)) {
860 if (cpu
->interrupt_request
& CPU_INTERRUPT_SIPI
) {
861 if (!cpu
->vcpu_dirty
) {
862 whpx_get_registers(cpu
);
864 do_cpu_sipi(x86_cpu
);
867 if (cpu
->interrupt_request
& CPU_INTERRUPT_TPR
) {
868 cpu
->interrupt_request
&= ~CPU_INTERRUPT_TPR
;
869 if (!cpu
->vcpu_dirty
) {
870 whpx_get_registers(cpu
);
872 apic_handle_tpr_access_report(x86_cpu
->apic_state
, env
->eip
,
873 env
->tpr_access_type
);
879 static int whpx_vcpu_run(CPUState
*cpu
)
882 struct whpx_state
*whpx
= &whpx_global
;
883 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
886 whpx_vcpu_process_async_events(cpu
);
888 cpu
->exception_index
= EXCP_HLT
;
889 atomic_set(&cpu
->exit_request
, false);
893 qemu_mutex_unlock_iothread();
897 if (cpu
->vcpu_dirty
) {
898 whpx_set_registers(cpu
);
899 cpu
->vcpu_dirty
= false;
902 whpx_vcpu_pre_run(cpu
);
904 if (atomic_read(&cpu
->exit_request
)) {
909 hr
= WHvRunVirtualProcessor(whpx
->partition
, cpu
->cpu_index
,
910 &vcpu
->exit_ctx
, whpx
->exit_ctx_size
);
912 if (SUCCEEDED(hr
) && (vcpu
->exit_ctx
.ExitReason
==
913 WHvRunVpExitReasonAlerted
)) {
914 WHvCancelRunVirtualProcessor(whpx
->partition
, cpu
->cpu_index
,
922 error_report("WHPX: Failed to exec a virtual processor,"
928 whpx_vcpu_post_run(cpu
);
930 switch (vcpu
->exit_ctx
.ExitReason
) {
931 case WHvRunVpExitReasonMemoryAccess
:
932 ret
= whpx_handle_mmio(cpu
, &vcpu
->exit_ctx
.MemoryAccess
);
935 case WHvRunVpExitReasonX64IoPortAccess
:
936 ret
= whpx_handle_portio(cpu
, &vcpu
->exit_ctx
.IoPortAccess
);
939 case WHvRunVpExitReasonX64InterruptWindow
:
940 vcpu
->window_registered
= 0;
943 case WHvRunVpExitReasonX64Halt
:
944 ret
= whpx_handle_halt(cpu
);
947 case WHvRunVpExitReasonCanceled
:
948 cpu
->exception_index
= EXCP_INTERRUPT
;
952 case WHvRunVpExitReasonNone
:
953 case WHvRunVpExitReasonUnrecoverableException
:
954 case WHvRunVpExitReasonInvalidVpRegisterValue
:
955 case WHvRunVpExitReasonUnsupportedFeature
:
956 case WHvRunVpExitReasonX64MsrAccess
:
957 case WHvRunVpExitReasonX64Cpuid
:
958 case WHvRunVpExitReasonException
:
959 case WHvRunVpExitReasonAlerted
:
961 error_report("WHPX: Unexpected VP exit code %d",
962 vcpu
->exit_ctx
.ExitReason
);
963 whpx_get_registers(cpu
);
964 qemu_mutex_lock_iothread();
965 qemu_system_guest_panicked(cpu_get_crash_info(cpu
));
966 qemu_mutex_unlock_iothread();
973 qemu_mutex_lock_iothread();
976 atomic_set(&cpu
->exit_request
, false);
981 static void do_whpx_cpu_synchronize_state(CPUState
*cpu
, run_on_cpu_data arg
)
983 whpx_get_registers(cpu
);
984 cpu
->vcpu_dirty
= true;
987 static void do_whpx_cpu_synchronize_post_reset(CPUState
*cpu
,
990 whpx_set_registers(cpu
);
991 cpu
->vcpu_dirty
= false;
994 static void do_whpx_cpu_synchronize_post_init(CPUState
*cpu
,
997 whpx_set_registers(cpu
);
998 cpu
->vcpu_dirty
= false;
1001 static void do_whpx_cpu_synchronize_pre_loadvm(CPUState
*cpu
,
1002 run_on_cpu_data arg
)
1004 cpu
->vcpu_dirty
= true;
1011 void whpx_cpu_synchronize_state(CPUState
*cpu
)
1013 if (!cpu
->vcpu_dirty
) {
1014 run_on_cpu(cpu
, do_whpx_cpu_synchronize_state
, RUN_ON_CPU_NULL
);
1018 void whpx_cpu_synchronize_post_reset(CPUState
*cpu
)
1020 run_on_cpu(cpu
, do_whpx_cpu_synchronize_post_reset
, RUN_ON_CPU_NULL
);
1023 void whpx_cpu_synchronize_post_init(CPUState
*cpu
)
1025 run_on_cpu(cpu
, do_whpx_cpu_synchronize_post_init
, RUN_ON_CPU_NULL
);
1028 void whpx_cpu_synchronize_pre_loadvm(CPUState
*cpu
)
1030 run_on_cpu(cpu
, do_whpx_cpu_synchronize_pre_loadvm
, RUN_ON_CPU_NULL
);
1037 static Error
*whpx_migration_blocker
;
1039 int whpx_init_vcpu(CPUState
*cpu
)
1042 struct whpx_state
*whpx
= &whpx_global
;
1043 struct whpx_vcpu
*vcpu
;
1044 Error
*local_error
= NULL
;
1046 /* Add migration blockers for all unsupported features of the
1047 * Windows Hypervisor Platform
1049 if (whpx_migration_blocker
== NULL
) {
1050 error_setg(&whpx_migration_blocker
,
1051 "State blocked due to non-migratable CPUID feature support,"
1052 "dirty memory tracking support, and XSAVE/XRSTOR support");
1054 (void)migrate_add_blocker(whpx_migration_blocker
, &local_error
);
1056 error_report_err(local_error
);
1057 error_free(whpx_migration_blocker
);
1058 migrate_del_blocker(whpx_migration_blocker
);
1063 vcpu
= g_malloc0(FIELD_OFFSET(struct whpx_vcpu
, exit_ctx
) +
1064 whpx
->exit_ctx_size
);
1067 error_report("WHPX: Failed to allocte VCPU context.");
1071 hr
= WHvEmulatorCreateEmulator(whpx_emu_callbacks
, &vcpu
->emulator
);
1073 error_report("WHPX: Failed to setup instruction completion support,"
1079 hr
= WHvCreateVirtualProcessor(whpx
->partition
, cpu
->cpu_index
, 0);
1081 error_report("WHPX: Failed to create a virtual processor,"
1083 WHvEmulatorDestroyEmulator(vcpu
->emulator
);
1088 vcpu
->interruptable
= true;
1090 cpu
->vcpu_dirty
= true;
1091 cpu
->hax_vcpu
= (struct hax_vcpu_state
*)vcpu
;
1096 int whpx_vcpu_exec(CPUState
*cpu
)
1102 if (cpu
->exception_index
>= EXCP_INTERRUPT
) {
1103 ret
= cpu
->exception_index
;
1104 cpu
->exception_index
= -1;
1108 fatal
= whpx_vcpu_run(cpu
);
1111 error_report("WHPX: Failed to exec a virtual processor");
1119 void whpx_destroy_vcpu(CPUState
*cpu
)
1121 struct whpx_state
*whpx
= &whpx_global
;
1122 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
1124 WHvDeleteVirtualProcessor(whpx
->partition
, cpu
->cpu_index
);
1125 WHvEmulatorDestroyEmulator(vcpu
->emulator
);
1126 g_free(cpu
->hax_vcpu
);
1130 void whpx_vcpu_kick(CPUState
*cpu
)
1132 struct whpx_state
*whpx
= &whpx_global
;
1133 WHvCancelRunVirtualProcessor(whpx
->partition
, cpu
->cpu_index
, 0);
1140 static void whpx_update_mapping(hwaddr start_pa
, ram_addr_t size
,
1141 void *host_va
, int add
, int rom
,
1144 struct whpx_state
*whpx
= &whpx_global
;
1149 printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
1150 (void*)start_pa, (void*)size, host_va,
1151 (rom ? "ROM" : "RAM"), name);
1153 printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
1154 (void*)start_pa, (void*)size, host_va, name);
1159 hr
= WHvMapGpaRange(whpx
->partition
,
1163 (WHvMapGpaRangeFlagRead
|
1164 WHvMapGpaRangeFlagExecute
|
1165 (rom
? 0 : WHvMapGpaRangeFlagWrite
)));
1167 hr
= WHvUnmapGpaRange(whpx
->partition
,
1173 error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
1174 " Host:%p, hr=%08lx",
1175 (add
? "MAP" : "UNMAP"), name
,
1176 (void *)start_pa
, (void *)size
, host_va
, hr
);
1180 static void whpx_process_section(MemoryRegionSection
*section
, int add
)
1182 MemoryRegion
*mr
= section
->mr
;
1183 hwaddr start_pa
= section
->offset_within_address_space
;
1184 ram_addr_t size
= int128_get64(section
->size
);
1188 if (!memory_region_is_ram(mr
)) {
1192 delta
= qemu_real_host_page_size
- (start_pa
& ~qemu_real_host_page_mask
);
1193 delta
&= ~qemu_real_host_page_mask
;
1199 size
&= qemu_real_host_page_mask
;
1200 if (!size
|| (start_pa
& ~qemu_real_host_page_mask
)) {
1204 host_va
= (uintptr_t)memory_region_get_ram_ptr(mr
)
1205 + section
->offset_within_region
+ delta
;
1207 whpx_update_mapping(start_pa
, size
, (void *)host_va
, add
,
1208 memory_region_is_rom(mr
), mr
->name
);
1211 static void whpx_region_add(MemoryListener
*listener
,
1212 MemoryRegionSection
*section
)
1214 memory_region_ref(section
->mr
);
1215 whpx_process_section(section
, 1);
1218 static void whpx_region_del(MemoryListener
*listener
,
1219 MemoryRegionSection
*section
)
1221 whpx_process_section(section
, 0);
1222 memory_region_unref(section
->mr
);
1225 static void whpx_transaction_begin(MemoryListener
*listener
)
1229 static void whpx_transaction_commit(MemoryListener
*listener
)
1233 static void whpx_log_sync(MemoryListener
*listener
,
1234 MemoryRegionSection
*section
)
1236 MemoryRegion
*mr
= section
->mr
;
1238 if (!memory_region_is_ram(mr
)) {
1242 memory_region_set_dirty(mr
, 0, int128_get64(section
->size
));
1245 static MemoryListener whpx_memory_listener
= {
1246 .begin
= whpx_transaction_begin
,
1247 .commit
= whpx_transaction_commit
,
1248 .region_add
= whpx_region_add
,
1249 .region_del
= whpx_region_del
,
1250 .log_sync
= whpx_log_sync
,
1254 static void whpx_memory_init(void)
1256 memory_listener_register(&whpx_memory_listener
, &address_space_memory
);
1259 static void whpx_handle_interrupt(CPUState
*cpu
, int mask
)
1261 cpu
->interrupt_request
|= mask
;
1263 if (!qemu_cpu_is_self(cpu
)) {
1272 static int whpx_accel_init(MachineState
*ms
)
1274 struct whpx_state
*whpx
;
1277 WHV_CAPABILITY whpx_cap
;
1278 WHV_PARTITION_PROPERTY prop
;
1280 whpx
= &whpx_global
;
1282 memset(whpx
, 0, sizeof(struct whpx_state
));
1283 whpx
->mem_quota
= ms
->ram_size
;
1285 hr
= WHvGetCapability(WHvCapabilityCodeHypervisorPresent
, &whpx_cap
,
1287 if (FAILED(hr
) || !whpx_cap
.HypervisorPresent
) {
1288 error_report("WHPX: No accelerator found, hr=%08lx", hr
);
1293 hr
= WHvCreatePartition(&whpx
->partition
);
1295 error_report("WHPX: Failed to create partition, hr=%08lx", hr
);
1300 memset(&prop
, 0, sizeof(WHV_PARTITION_PROPERTY
));
1301 prop
.PropertyCode
= WHvPartitionPropertyCodeProcessorCount
;
1302 prop
.ProcessorCount
= smp_cpus
;
1303 hr
= WHvSetPartitionProperty(whpx
->partition
,
1305 sizeof(WHV_PARTITION_PROPERTY
));
1308 error_report("WHPX: Failed to set partition core count to %d,"
1309 " hr=%08lx", smp_cores
, hr
);
1314 hr
= WHvSetupPartition(whpx
->partition
);
1316 error_report("WHPX: Failed to setup partition, hr=%08lx", hr
);
1321 whpx
->exit_ctx_size
= WHvGetRunExitContextSize();
1322 assert(whpx
->exit_ctx_size
);
1326 cpu_interrupt_handler
= whpx_handle_interrupt
;
1328 printf("Windows Hypervisor Platform accelerator is operational\n");
1333 if (NULL
!= whpx
->partition
) {
1334 WHvDeletePartition(whpx
->partition
);
1335 whpx
->partition
= NULL
;
1342 int whpx_enabled(void)
1344 return whpx_allowed
;
1347 static void whpx_accel_class_init(ObjectClass
*oc
, void *data
)
1349 AccelClass
*ac
= ACCEL_CLASS(oc
);
1351 ac
->init_machine
= whpx_accel_init
;
1352 ac
->allowed
= &whpx_allowed
;
1355 static const TypeInfo whpx_accel_type
= {
1356 .name
= ACCEL_CLASS_NAME("whpx"),
1357 .parent
= TYPE_ACCEL
,
1358 .class_init
= whpx_accel_class_init
,
1361 static void whpx_type_init(void)
1363 type_register_static(&whpx_accel_type
);
1366 type_init(whpx_type_init
);