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
;
37 static const WHV_REGISTER_NAME whpx_register_names
[] = {
39 /* X64 General purpose registers */
59 /* X64 Segment registers */
69 /* X64 Table registers */
73 /* X64 Control Registers */
80 /* X64 Debug Registers */
90 /* X64 Floating Point and Vector Registers */
107 WHvX64RegisterFpMmx0
,
108 WHvX64RegisterFpMmx1
,
109 WHvX64RegisterFpMmx2
,
110 WHvX64RegisterFpMmx3
,
111 WHvX64RegisterFpMmx4
,
112 WHvX64RegisterFpMmx5
,
113 WHvX64RegisterFpMmx6
,
114 WHvX64RegisterFpMmx7
,
115 WHvX64RegisterFpControlStatus
,
116 WHvX64RegisterXmmControlStatus
,
122 WHvX64RegisterKernelGsBase
,
124 WHvX64RegisterApicBase
,
125 /* WHvX64RegisterPat, */
126 WHvX64RegisterSysenterCs
,
127 WHvX64RegisterSysenterEip
,
128 WHvX64RegisterSysenterEsp
,
133 WHvX64RegisterSfmask
,
136 /* Interrupt / Event Registers */
138 * WHvRegisterPendingInterruption,
139 * WHvRegisterInterruptState,
140 * WHvRegisterPendingEvent0,
141 * WHvRegisterPendingEvent1
142 * WHvX64RegisterDeliverabilityNotifications,
146 struct whpx_register_set
{
147 WHV_REGISTER_VALUE values
[RTL_NUMBER_OF(whpx_register_names
)];
151 WHV_EMULATOR_HANDLE emulator
;
152 bool window_registered
;
156 bool interruption_pending
;
158 /* Must be the last field as it may have a tail */
159 WHV_RUN_VP_EXIT_CONTEXT exit_ctx
;
162 static bool whpx_allowed
;
164 struct whpx_state whpx_global
;
171 static struct whpx_vcpu
*get_whpx_vcpu(CPUState
*cpu
)
173 return (struct whpx_vcpu
*)cpu
->hax_vcpu
;
176 static WHV_X64_SEGMENT_REGISTER
whpx_seg_q2h(const SegmentCache
*qs
, int v86
,
179 WHV_X64_SEGMENT_REGISTER hs
;
180 unsigned flags
= qs
->flags
;
183 hs
.Limit
= qs
->limit
;
184 hs
.Selector
= qs
->selector
;
190 hs
.DescriptorPrivilegeLevel
= 3;
191 hs
.NonSystemSegment
= 1;
194 hs
.Attributes
= (flags
>> DESC_TYPE_SHIFT
);
197 /* hs.Base &= 0xfffff; */
204 static SegmentCache
whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER
*hs
)
209 qs
.limit
= hs
->Limit
;
210 qs
.selector
= hs
->Selector
;
212 qs
.flags
= ((uint32_t)hs
->Attributes
) << DESC_TYPE_SHIFT
;
217 static void whpx_set_registers(CPUState
*cpu
)
219 struct whpx_state
*whpx
= &whpx_global
;
220 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
221 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
222 X86CPU
*x86_cpu
= X86_CPU(cpu
);
223 struct whpx_register_set vcxt
= {0};
229 assert(cpu_is_stopped(cpu
) || qemu_cpu_is_self(cpu
));
231 v86
= (env
->eflags
& VM_MASK
);
232 r86
= !(env
->cr
[0] & CR0_PE_MASK
);
234 vcpu
->tpr
= cpu_get_apic_tpr(x86_cpu
->apic_state
);
235 vcpu
->apic_base
= cpu_get_apic_base(x86_cpu
->apic_state
);
237 /* Indexes for first 16 registers match between HV and QEMU definitions */
238 for (idx
= 0; idx
< CPU_NB_REGS64
; idx
+= 1) {
239 vcxt
.values
[idx
].Reg64
= env
->regs
[idx
];
242 /* Same goes for RIP and RFLAGS */
243 assert(whpx_register_names
[idx
] == WHvX64RegisterRip
);
244 vcxt
.values
[idx
++].Reg64
= env
->eip
;
246 assert(whpx_register_names
[idx
] == WHvX64RegisterRflags
);
247 vcxt
.values
[idx
++].Reg64
= env
->eflags
;
249 /* Translate 6+4 segment registers. HV and QEMU order matches */
250 assert(idx
== WHvX64RegisterEs
);
251 for (i
= 0; i
< 6; i
+= 1, idx
+= 1) {
252 vcxt
.values
[idx
].Segment
= whpx_seg_q2h(&env
->segs
[i
], v86
, r86
);
255 assert(idx
== WHvX64RegisterLdtr
);
256 vcxt
.values
[idx
++].Segment
= whpx_seg_q2h(&env
->ldt
, 0, 0);
258 assert(idx
== WHvX64RegisterTr
);
259 vcxt
.values
[idx
++].Segment
= whpx_seg_q2h(&env
->tr
, 0, 0);
261 assert(idx
== WHvX64RegisterIdtr
);
262 vcxt
.values
[idx
].Table
.Base
= env
->idt
.base
;
263 vcxt
.values
[idx
].Table
.Limit
= env
->idt
.limit
;
266 assert(idx
== WHvX64RegisterGdtr
);
267 vcxt
.values
[idx
].Table
.Base
= env
->gdt
.base
;
268 vcxt
.values
[idx
].Table
.Limit
= env
->gdt
.limit
;
271 /* CR0, 2, 3, 4, 8 */
272 assert(whpx_register_names
[idx
] == WHvX64RegisterCr0
);
273 vcxt
.values
[idx
++].Reg64
= env
->cr
[0];
274 assert(whpx_register_names
[idx
] == WHvX64RegisterCr2
);
275 vcxt
.values
[idx
++].Reg64
= env
->cr
[2];
276 assert(whpx_register_names
[idx
] == WHvX64RegisterCr3
);
277 vcxt
.values
[idx
++].Reg64
= env
->cr
[3];
278 assert(whpx_register_names
[idx
] == WHvX64RegisterCr4
);
279 vcxt
.values
[idx
++].Reg64
= env
->cr
[4];
280 assert(whpx_register_names
[idx
] == WHvX64RegisterCr8
);
281 vcxt
.values
[idx
++].Reg64
= vcpu
->tpr
;
283 /* 8 Debug Registers - Skipped */
285 /* 16 XMM registers */
286 assert(whpx_register_names
[idx
] == WHvX64RegisterXmm0
);
287 for (i
= 0; i
< 16; i
+= 1, idx
+= 1) {
288 vcxt
.values
[idx
].Reg128
.Low64
= env
->xmm_regs
[i
].ZMM_Q(0);
289 vcxt
.values
[idx
].Reg128
.High64
= env
->xmm_regs
[i
].ZMM_Q(1);
293 assert(whpx_register_names
[idx
] == WHvX64RegisterFpMmx0
);
294 for (i
= 0; i
< 8; i
+= 1, idx
+= 1) {
295 vcxt
.values
[idx
].Fp
.AsUINT128
.Low64
= env
->fpregs
[i
].mmx
.MMX_Q(0);
296 /* vcxt.values[idx].Fp.AsUINT128.High64 =
297 env->fpregs[i].mmx.MMX_Q(1);
301 /* FP control status register */
302 assert(whpx_register_names
[idx
] == WHvX64RegisterFpControlStatus
);
303 vcxt
.values
[idx
].FpControlStatus
.FpControl
= env
->fpuc
;
304 vcxt
.values
[idx
].FpControlStatus
.FpStatus
=
305 (env
->fpus
& ~0x3800) | (env
->fpstt
& 0x7) << 11;
306 vcxt
.values
[idx
].FpControlStatus
.FpTag
= 0;
307 for (i
= 0; i
< 8; ++i
) {
308 vcxt
.values
[idx
].FpControlStatus
.FpTag
|= (!env
->fptags
[i
]) << i
;
310 vcxt
.values
[idx
].FpControlStatus
.Reserved
= 0;
311 vcxt
.values
[idx
].FpControlStatus
.LastFpOp
= env
->fpop
;
312 vcxt
.values
[idx
].FpControlStatus
.LastFpRip
= env
->fpip
;
315 /* XMM control status register */
316 assert(whpx_register_names
[idx
] == WHvX64RegisterXmmControlStatus
);
317 vcxt
.values
[idx
].XmmControlStatus
.LastFpRdp
= 0;
318 vcxt
.values
[idx
].XmmControlStatus
.XmmStatusControl
= env
->mxcsr
;
319 vcxt
.values
[idx
].XmmControlStatus
.XmmStatusControlMask
= 0x0000ffff;
323 assert(whpx_register_names
[idx
] == WHvX64RegisterTsc
);
324 vcxt
.values
[idx
++].Reg64
= env
->tsc
;
325 assert(whpx_register_names
[idx
] == WHvX64RegisterEfer
);
326 vcxt
.values
[idx
++].Reg64
= env
->efer
;
328 assert(whpx_register_names
[idx
] == WHvX64RegisterKernelGsBase
);
329 vcxt
.values
[idx
++].Reg64
= env
->kernelgsbase
;
332 assert(whpx_register_names
[idx
] == WHvX64RegisterApicBase
);
333 vcxt
.values
[idx
++].Reg64
= vcpu
->apic_base
;
335 /* WHvX64RegisterPat - Skipped */
337 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterCs
);
338 vcxt
.values
[idx
++].Reg64
= env
->sysenter_cs
;
339 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEip
);
340 vcxt
.values
[idx
++].Reg64
= env
->sysenter_eip
;
341 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEsp
);
342 vcxt
.values
[idx
++].Reg64
= env
->sysenter_esp
;
343 assert(whpx_register_names
[idx
] == WHvX64RegisterStar
);
344 vcxt
.values
[idx
++].Reg64
= env
->star
;
346 assert(whpx_register_names
[idx
] == WHvX64RegisterLstar
);
347 vcxt
.values
[idx
++].Reg64
= env
->lstar
;
348 assert(whpx_register_names
[idx
] == WHvX64RegisterCstar
);
349 vcxt
.values
[idx
++].Reg64
= env
->cstar
;
350 assert(whpx_register_names
[idx
] == WHvX64RegisterSfmask
);
351 vcxt
.values
[idx
++].Reg64
= env
->fmask
;
354 /* Interrupt / Event Registers - Skipped */
356 assert(idx
== RTL_NUMBER_OF(whpx_register_names
));
358 hr
= WHvSetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
360 RTL_NUMBER_OF(whpx_register_names
),
364 error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
371 static void whpx_get_registers(CPUState
*cpu
)
373 struct whpx_state
*whpx
= &whpx_global
;
374 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
375 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
376 X86CPU
*x86_cpu
= X86_CPU(cpu
);
377 struct whpx_register_set vcxt
;
378 uint64_t tpr
, apic_base
;
383 assert(cpu_is_stopped(cpu
) || qemu_cpu_is_self(cpu
));
385 hr
= WHvGetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
387 RTL_NUMBER_OF(whpx_register_names
),
390 error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
394 /* Indexes for first 16 registers match between HV and QEMU definitions */
395 for (idx
= 0; idx
< CPU_NB_REGS64
; idx
+= 1) {
396 env
->regs
[idx
] = vcxt
.values
[idx
].Reg64
;
399 /* Same goes for RIP and RFLAGS */
400 assert(whpx_register_names
[idx
] == WHvX64RegisterRip
);
401 env
->eip
= vcxt
.values
[idx
++].Reg64
;
402 assert(whpx_register_names
[idx
] == WHvX64RegisterRflags
);
403 env
->eflags
= vcxt
.values
[idx
++].Reg64
;
405 /* Translate 6+4 segment registers. HV and QEMU order matches */
406 assert(idx
== WHvX64RegisterEs
);
407 for (i
= 0; i
< 6; i
+= 1, idx
+= 1) {
408 env
->segs
[i
] = whpx_seg_h2q(&vcxt
.values
[idx
].Segment
);
411 assert(idx
== WHvX64RegisterLdtr
);
412 env
->ldt
= whpx_seg_h2q(&vcxt
.values
[idx
++].Segment
);
413 assert(idx
== WHvX64RegisterTr
);
414 env
->tr
= whpx_seg_h2q(&vcxt
.values
[idx
++].Segment
);
415 assert(idx
== WHvX64RegisterIdtr
);
416 env
->idt
.base
= vcxt
.values
[idx
].Table
.Base
;
417 env
->idt
.limit
= vcxt
.values
[idx
].Table
.Limit
;
419 assert(idx
== WHvX64RegisterGdtr
);
420 env
->gdt
.base
= vcxt
.values
[idx
].Table
.Base
;
421 env
->gdt
.limit
= vcxt
.values
[idx
].Table
.Limit
;
424 /* CR0, 2, 3, 4, 8 */
425 assert(whpx_register_names
[idx
] == WHvX64RegisterCr0
);
426 env
->cr
[0] = vcxt
.values
[idx
++].Reg64
;
427 assert(whpx_register_names
[idx
] == WHvX64RegisterCr2
);
428 env
->cr
[2] = vcxt
.values
[idx
++].Reg64
;
429 assert(whpx_register_names
[idx
] == WHvX64RegisterCr3
);
430 env
->cr
[3] = vcxt
.values
[idx
++].Reg64
;
431 assert(whpx_register_names
[idx
] == WHvX64RegisterCr4
);
432 env
->cr
[4] = vcxt
.values
[idx
++].Reg64
;
433 assert(whpx_register_names
[idx
] == WHvX64RegisterCr8
);
434 tpr
= vcxt
.values
[idx
++].Reg64
;
435 if (tpr
!= vcpu
->tpr
) {
437 cpu_set_apic_tpr(x86_cpu
->apic_state
, tpr
);
440 /* 8 Debug Registers - Skipped */
442 /* 16 XMM registers */
443 assert(whpx_register_names
[idx
] == WHvX64RegisterXmm0
);
444 for (i
= 0; i
< 16; i
+= 1, idx
+= 1) {
445 env
->xmm_regs
[i
].ZMM_Q(0) = vcxt
.values
[idx
].Reg128
.Low64
;
446 env
->xmm_regs
[i
].ZMM_Q(1) = vcxt
.values
[idx
].Reg128
.High64
;
450 assert(whpx_register_names
[idx
] == WHvX64RegisterFpMmx0
);
451 for (i
= 0; i
< 8; i
+= 1, idx
+= 1) {
452 env
->fpregs
[i
].mmx
.MMX_Q(0) = vcxt
.values
[idx
].Fp
.AsUINT128
.Low64
;
453 /* env->fpregs[i].mmx.MMX_Q(1) =
454 vcxt.values[idx].Fp.AsUINT128.High64;
458 /* FP control status register */
459 assert(whpx_register_names
[idx
] == WHvX64RegisterFpControlStatus
);
460 env
->fpuc
= vcxt
.values
[idx
].FpControlStatus
.FpControl
;
461 env
->fpstt
= (vcxt
.values
[idx
].FpControlStatus
.FpStatus
>> 11) & 0x7;
462 env
->fpus
= vcxt
.values
[idx
].FpControlStatus
.FpStatus
& ~0x3800;
463 for (i
= 0; i
< 8; ++i
) {
464 env
->fptags
[i
] = !((vcxt
.values
[idx
].FpControlStatus
.FpTag
>> i
) & 1);
466 env
->fpop
= vcxt
.values
[idx
].FpControlStatus
.LastFpOp
;
467 env
->fpip
= vcxt
.values
[idx
].FpControlStatus
.LastFpRip
;
470 /* XMM control status register */
471 assert(whpx_register_names
[idx
] == WHvX64RegisterXmmControlStatus
);
472 env
->mxcsr
= vcxt
.values
[idx
].XmmControlStatus
.XmmStatusControl
;
476 assert(whpx_register_names
[idx
] == WHvX64RegisterTsc
);
477 env
->tsc
= vcxt
.values
[idx
++].Reg64
;
478 assert(whpx_register_names
[idx
] == WHvX64RegisterEfer
);
479 env
->efer
= vcxt
.values
[idx
++].Reg64
;
481 assert(whpx_register_names
[idx
] == WHvX64RegisterKernelGsBase
);
482 env
->kernelgsbase
= vcxt
.values
[idx
++].Reg64
;
485 assert(whpx_register_names
[idx
] == WHvX64RegisterApicBase
);
486 apic_base
= vcxt
.values
[idx
++].Reg64
;
487 if (apic_base
!= vcpu
->apic_base
) {
488 vcpu
->apic_base
= apic_base
;
489 cpu_set_apic_base(x86_cpu
->apic_state
, vcpu
->apic_base
);
492 /* WHvX64RegisterPat - Skipped */
494 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterCs
);
495 env
->sysenter_cs
= vcxt
.values
[idx
++].Reg64
;;
496 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEip
);
497 env
->sysenter_eip
= vcxt
.values
[idx
++].Reg64
;
498 assert(whpx_register_names
[idx
] == WHvX64RegisterSysenterEsp
);
499 env
->sysenter_esp
= vcxt
.values
[idx
++].Reg64
;
500 assert(whpx_register_names
[idx
] == WHvX64RegisterStar
);
501 env
->star
= vcxt
.values
[idx
++].Reg64
;
503 assert(whpx_register_names
[idx
] == WHvX64RegisterLstar
);
504 env
->lstar
= vcxt
.values
[idx
++].Reg64
;
505 assert(whpx_register_names
[idx
] == WHvX64RegisterCstar
);
506 env
->cstar
= vcxt
.values
[idx
++].Reg64
;
507 assert(whpx_register_names
[idx
] == WHvX64RegisterSfmask
);
508 env
->fmask
= vcxt
.values
[idx
++].Reg64
;
511 /* Interrupt / Event Registers - Skipped */
513 assert(idx
== RTL_NUMBER_OF(whpx_register_names
));
518 static HRESULT CALLBACK
whpx_emu_ioport_callback(
520 WHV_EMULATOR_IO_ACCESS_INFO
*IoAccess
)
522 MemTxAttrs attrs
= { 0 };
523 address_space_rw(&address_space_io
, IoAccess
->Port
, attrs
,
524 (uint8_t *)&IoAccess
->Data
, IoAccess
->AccessSize
,
525 IoAccess
->Direction
);
529 static HRESULT CALLBACK
whpx_emu_mmio_callback(
531 WHV_EMULATOR_MEMORY_ACCESS_INFO
*ma
)
533 cpu_physical_memory_rw(ma
->GpaAddress
, ma
->Data
, ma
->AccessSize
,
538 static HRESULT CALLBACK
whpx_emu_getreg_callback(
540 const WHV_REGISTER_NAME
*RegisterNames
,
541 UINT32 RegisterCount
,
542 WHV_REGISTER_VALUE
*RegisterValues
)
545 struct whpx_state
*whpx
= &whpx_global
;
546 CPUState
*cpu
= (CPUState
*)ctx
;
548 hr
= WHvGetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
549 RegisterNames
, RegisterCount
,
552 error_report("WHPX: Failed to get virtual processor registers,"
559 static HRESULT CALLBACK
whpx_emu_setreg_callback(
561 const WHV_REGISTER_NAME
*RegisterNames
,
562 UINT32 RegisterCount
,
563 const WHV_REGISTER_VALUE
*RegisterValues
)
566 struct whpx_state
*whpx
= &whpx_global
;
567 CPUState
*cpu
= (CPUState
*)ctx
;
569 hr
= WHvSetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
570 RegisterNames
, RegisterCount
,
573 error_report("WHPX: Failed to set virtual processor registers,"
578 * The emulator just successfully wrote the register state. We clear the
579 * dirty state so we avoid the double write on resume of the VP.
581 cpu
->vcpu_dirty
= false;
586 static HRESULT CALLBACK
whpx_emu_translate_callback(
588 WHV_GUEST_VIRTUAL_ADDRESS Gva
,
589 WHV_TRANSLATE_GVA_FLAGS TranslateFlags
,
590 WHV_TRANSLATE_GVA_RESULT_CODE
*TranslationResult
,
591 WHV_GUEST_PHYSICAL_ADDRESS
*Gpa
)
594 struct whpx_state
*whpx
= &whpx_global
;
595 CPUState
*cpu
= (CPUState
*)ctx
;
596 WHV_TRANSLATE_GVA_RESULT res
;
598 hr
= WHvTranslateGva(whpx
->partition
, cpu
->cpu_index
,
599 Gva
, TranslateFlags
, &res
, Gpa
);
601 error_report("WHPX: Failed to translate GVA, hr=%08lx", hr
);
603 *TranslationResult
= res
.ResultCode
;
609 static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks
= {
610 .Size
= sizeof(WHV_EMULATOR_CALLBACKS
),
611 .WHvEmulatorIoPortCallback
= whpx_emu_ioport_callback
,
612 .WHvEmulatorMemoryCallback
= whpx_emu_mmio_callback
,
613 .WHvEmulatorGetVirtualProcessorRegisters
= whpx_emu_getreg_callback
,
614 .WHvEmulatorSetVirtualProcessorRegisters
= whpx_emu_setreg_callback
,
615 .WHvEmulatorTranslateGvaPage
= whpx_emu_translate_callback
,
618 static int whpx_handle_mmio(CPUState
*cpu
, WHV_MEMORY_ACCESS_CONTEXT
*ctx
)
621 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
622 WHV_EMULATOR_STATUS emu_status
;
624 hr
= WHvEmulatorTryMmioEmulation(vcpu
->emulator
, cpu
,
625 &vcpu
->exit_ctx
.VpContext
, ctx
,
628 error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr
);
632 if (!emu_status
.EmulationSuccessful
) {
633 error_report("WHPX: Failed to emulate MMIO access");
640 static int whpx_handle_portio(CPUState
*cpu
,
641 WHV_X64_IO_PORT_ACCESS_CONTEXT
*ctx
)
644 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
645 WHV_EMULATOR_STATUS emu_status
;
647 hr
= WHvEmulatorTryIoEmulation(vcpu
->emulator
, cpu
,
648 &vcpu
->exit_ctx
.VpContext
, ctx
,
651 error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr
);
655 if (!emu_status
.EmulationSuccessful
) {
656 error_report("WHPX: Failed to emulate PortMMIO access");
663 static int whpx_handle_halt(CPUState
*cpu
)
665 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
668 qemu_mutex_lock_iothread();
669 if (!((cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) &&
670 (env
->eflags
& IF_MASK
)) &&
671 !(cpu
->interrupt_request
& CPU_INTERRUPT_NMI
)) {
672 cpu
->exception_index
= EXCP_HLT
;
676 qemu_mutex_unlock_iothread();
681 static void whpx_vcpu_pre_run(CPUState
*cpu
)
684 struct whpx_state
*whpx
= &whpx_global
;
685 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
686 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
687 X86CPU
*x86_cpu
= X86_CPU(cpu
);
690 WHV_X64_PENDING_INTERRUPTION_REGISTER new_int
= {0};
691 UINT32 reg_count
= 0;
692 WHV_REGISTER_VALUE reg_values
[3] = {0};
693 WHV_REGISTER_NAME reg_names
[3];
695 qemu_mutex_lock_iothread();
698 if (!vcpu
->interruption_pending
&&
699 cpu
->interrupt_request
& (CPU_INTERRUPT_NMI
| CPU_INTERRUPT_SMI
)) {
700 if (cpu
->interrupt_request
& CPU_INTERRUPT_NMI
) {
701 cpu
->interrupt_request
&= ~CPU_INTERRUPT_NMI
;
702 vcpu
->interruptable
= false;
703 new_int
.InterruptionType
= WHvX64PendingNmi
;
704 new_int
.InterruptionPending
= 1;
705 new_int
.InterruptionVector
= 2;
707 if (cpu
->interrupt_request
& CPU_INTERRUPT_SMI
) {
708 cpu
->interrupt_request
&= ~CPU_INTERRUPT_SMI
;
713 * Force the VCPU out of its inner loop to process any INIT requests or
714 * commit pending TPR access.
716 if (cpu
->interrupt_request
& (CPU_INTERRUPT_INIT
| CPU_INTERRUPT_TPR
)) {
717 if ((cpu
->interrupt_request
& CPU_INTERRUPT_INIT
) &&
718 !(env
->hflags
& HF_SMM_MASK
)) {
719 cpu
->exit_request
= 1;
721 if (cpu
->interrupt_request
& CPU_INTERRUPT_TPR
) {
722 cpu
->exit_request
= 1;
726 /* Get pending hard interruption or replay one that was overwritten */
727 if (!vcpu
->interruption_pending
&&
728 vcpu
->interruptable
&& (env
->eflags
& IF_MASK
)) {
729 assert(!new_int
.InterruptionPending
);
730 if (cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) {
731 cpu
->interrupt_request
&= ~CPU_INTERRUPT_HARD
;
732 irq
= cpu_get_pic_interrupt(env
);
734 new_int
.InterruptionType
= WHvX64PendingInterrupt
;
735 new_int
.InterruptionPending
= 1;
736 new_int
.InterruptionVector
= irq
;
741 /* Setup interrupt state if new one was prepared */
742 if (new_int
.InterruptionPending
) {
743 reg_values
[reg_count
].PendingInterruption
= new_int
;
744 reg_names
[reg_count
] = WHvRegisterPendingInterruption
;
748 /* Sync the TPR to the CR8 if was modified during the intercept */
749 tpr
= cpu_get_apic_tpr(x86_cpu
->apic_state
);
750 if (tpr
!= vcpu
->tpr
) {
752 reg_values
[reg_count
].Reg64
= tpr
;
753 cpu
->exit_request
= 1;
754 reg_names
[reg_count
] = WHvX64RegisterCr8
;
758 /* Update the state of the interrupt delivery notification */
759 if (!vcpu
->window_registered
&&
760 cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) {
761 reg_values
[reg_count
].DeliverabilityNotifications
.InterruptNotification
763 vcpu
->window_registered
= 1;
764 reg_names
[reg_count
] = WHvX64RegisterDeliverabilityNotifications
;
768 qemu_mutex_unlock_iothread();
771 hr
= WHvSetVirtualProcessorRegisters(whpx
->partition
, cpu
->cpu_index
,
772 reg_names
, reg_count
, reg_values
);
774 error_report("WHPX: Failed to set interrupt state registers,"
782 static void whpx_vcpu_post_run(CPUState
*cpu
)
784 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
785 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
786 X86CPU
*x86_cpu
= X86_CPU(cpu
);
788 env
->eflags
= vcpu
->exit_ctx
.VpContext
.Rflags
;
790 uint64_t tpr
= vcpu
->exit_ctx
.VpContext
.Cr8
;
791 if (vcpu
->tpr
!= tpr
) {
793 qemu_mutex_lock_iothread();
794 cpu_set_apic_tpr(x86_cpu
->apic_state
, vcpu
->tpr
);
795 qemu_mutex_unlock_iothread();
798 vcpu
->interruption_pending
=
799 vcpu
->exit_ctx
.VpContext
.ExecutionState
.InterruptionPending
;
801 vcpu
->interruptable
=
802 !vcpu
->exit_ctx
.VpContext
.ExecutionState
.InterruptShadow
;
807 static void whpx_vcpu_process_async_events(CPUState
*cpu
)
809 struct CPUX86State
*env
= (CPUArchState
*)(cpu
->env_ptr
);
810 X86CPU
*x86_cpu
= X86_CPU(cpu
);
811 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
813 if ((cpu
->interrupt_request
& CPU_INTERRUPT_INIT
) &&
814 !(env
->hflags
& HF_SMM_MASK
)) {
816 do_cpu_init(x86_cpu
);
817 cpu
->vcpu_dirty
= true;
818 vcpu
->interruptable
= true;
821 if (cpu
->interrupt_request
& CPU_INTERRUPT_POLL
) {
822 cpu
->interrupt_request
&= ~CPU_INTERRUPT_POLL
;
823 apic_poll_irq(x86_cpu
->apic_state
);
826 if (((cpu
->interrupt_request
& CPU_INTERRUPT_HARD
) &&
827 (env
->eflags
& IF_MASK
)) ||
828 (cpu
->interrupt_request
& CPU_INTERRUPT_NMI
)) {
832 if (cpu
->interrupt_request
& CPU_INTERRUPT_SIPI
) {
833 if (!cpu
->vcpu_dirty
) {
834 whpx_get_registers(cpu
);
836 do_cpu_sipi(x86_cpu
);
839 if (cpu
->interrupt_request
& CPU_INTERRUPT_TPR
) {
840 cpu
->interrupt_request
&= ~CPU_INTERRUPT_TPR
;
841 if (!cpu
->vcpu_dirty
) {
842 whpx_get_registers(cpu
);
844 apic_handle_tpr_access_report(x86_cpu
->apic_state
, env
->eip
,
845 env
->tpr_access_type
);
851 static int whpx_vcpu_run(CPUState
*cpu
)
854 struct whpx_state
*whpx
= &whpx_global
;
855 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
858 whpx_vcpu_process_async_events(cpu
);
860 cpu
->exception_index
= EXCP_HLT
;
861 atomic_set(&cpu
->exit_request
, false);
865 qemu_mutex_unlock_iothread();
869 if (cpu
->vcpu_dirty
) {
870 whpx_set_registers(cpu
);
871 cpu
->vcpu_dirty
= false;
874 whpx_vcpu_pre_run(cpu
);
876 if (atomic_read(&cpu
->exit_request
)) {
880 hr
= WHvRunVirtualProcessor(whpx
->partition
, cpu
->cpu_index
,
881 &vcpu
->exit_ctx
, sizeof(vcpu
->exit_ctx
));
884 error_report("WHPX: Failed to exec a virtual processor,"
890 whpx_vcpu_post_run(cpu
);
892 switch (vcpu
->exit_ctx
.ExitReason
) {
893 case WHvRunVpExitReasonMemoryAccess
:
894 ret
= whpx_handle_mmio(cpu
, &vcpu
->exit_ctx
.MemoryAccess
);
897 case WHvRunVpExitReasonX64IoPortAccess
:
898 ret
= whpx_handle_portio(cpu
, &vcpu
->exit_ctx
.IoPortAccess
);
901 case WHvRunVpExitReasonX64InterruptWindow
:
902 vcpu
->window_registered
= 0;
905 case WHvRunVpExitReasonX64Halt
:
906 ret
= whpx_handle_halt(cpu
);
909 case WHvRunVpExitReasonCanceled
:
910 cpu
->exception_index
= EXCP_INTERRUPT
;
914 case WHvRunVpExitReasonX64Cpuid
: {
915 WHV_REGISTER_VALUE reg_values
[5] = {0};
916 WHV_REGISTER_NAME reg_names
[5];
917 UINT32 reg_count
= 5;
918 UINT64 rip
, rax
, rcx
, rdx
, rbx
;
920 rip
= vcpu
->exit_ctx
.VpContext
.Rip
+
921 vcpu
->exit_ctx
.VpContext
.InstructionLength
;
922 switch (vcpu
->exit_ctx
.CpuidAccess
.Rax
) {
924 rax
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRax
;
925 /* Advertise that we are running on a hypervisor */
927 vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRcx
|
928 CPUID_EXT_HYPERVISOR
;
930 rdx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRdx
;
931 rbx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRbx
;
934 rax
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRax
;
935 rcx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRcx
;
936 rdx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRdx
;
937 rbx
= vcpu
->exit_ctx
.CpuidAccess
.DefaultResultRbx
;
940 reg_names
[0] = WHvX64RegisterRip
;
941 reg_names
[1] = WHvX64RegisterRax
;
942 reg_names
[2] = WHvX64RegisterRcx
;
943 reg_names
[3] = WHvX64RegisterRdx
;
944 reg_names
[4] = WHvX64RegisterRbx
;
946 reg_values
[0].Reg64
= rip
;
947 reg_values
[1].Reg64
= rax
;
948 reg_values
[2].Reg64
= rcx
;
949 reg_values
[3].Reg64
= rdx
;
950 reg_values
[4].Reg64
= rbx
;
952 hr
= WHvSetVirtualProcessorRegisters(whpx
->partition
,
959 error_report("WHPX: Failed to set CpuidAccess state registers,"
965 case WHvRunVpExitReasonNone
:
966 case WHvRunVpExitReasonUnrecoverableException
:
967 case WHvRunVpExitReasonInvalidVpRegisterValue
:
968 case WHvRunVpExitReasonUnsupportedFeature
:
969 case WHvRunVpExitReasonX64MsrAccess
:
970 case WHvRunVpExitReasonException
:
972 error_report("WHPX: Unexpected VP exit code %d",
973 vcpu
->exit_ctx
.ExitReason
);
974 whpx_get_registers(cpu
);
975 qemu_mutex_lock_iothread();
976 qemu_system_guest_panicked(cpu_get_crash_info(cpu
));
977 qemu_mutex_unlock_iothread();
984 qemu_mutex_lock_iothread();
987 atomic_set(&cpu
->exit_request
, false);
992 static void do_whpx_cpu_synchronize_state(CPUState
*cpu
, run_on_cpu_data arg
)
994 whpx_get_registers(cpu
);
995 cpu
->vcpu_dirty
= true;
998 static void do_whpx_cpu_synchronize_post_reset(CPUState
*cpu
,
1001 whpx_set_registers(cpu
);
1002 cpu
->vcpu_dirty
= false;
1005 static void do_whpx_cpu_synchronize_post_init(CPUState
*cpu
,
1006 run_on_cpu_data arg
)
1008 whpx_set_registers(cpu
);
1009 cpu
->vcpu_dirty
= false;
1012 static void do_whpx_cpu_synchronize_pre_loadvm(CPUState
*cpu
,
1013 run_on_cpu_data arg
)
1015 cpu
->vcpu_dirty
= true;
1022 void whpx_cpu_synchronize_state(CPUState
*cpu
)
1024 if (!cpu
->vcpu_dirty
) {
1025 run_on_cpu(cpu
, do_whpx_cpu_synchronize_state
, RUN_ON_CPU_NULL
);
1029 void whpx_cpu_synchronize_post_reset(CPUState
*cpu
)
1031 run_on_cpu(cpu
, do_whpx_cpu_synchronize_post_reset
, RUN_ON_CPU_NULL
);
1034 void whpx_cpu_synchronize_post_init(CPUState
*cpu
)
1036 run_on_cpu(cpu
, do_whpx_cpu_synchronize_post_init
, RUN_ON_CPU_NULL
);
1039 void whpx_cpu_synchronize_pre_loadvm(CPUState
*cpu
)
1041 run_on_cpu(cpu
, do_whpx_cpu_synchronize_pre_loadvm
, RUN_ON_CPU_NULL
);
1048 static Error
*whpx_migration_blocker
;
1050 int whpx_init_vcpu(CPUState
*cpu
)
1053 struct whpx_state
*whpx
= &whpx_global
;
1054 struct whpx_vcpu
*vcpu
;
1055 Error
*local_error
= NULL
;
1057 /* Add migration blockers for all unsupported features of the
1058 * Windows Hypervisor Platform
1060 if (whpx_migration_blocker
== NULL
) {
1061 error_setg(&whpx_migration_blocker
,
1062 "State blocked due to non-migratable CPUID feature support,"
1063 "dirty memory tracking support, and XSAVE/XRSTOR support");
1065 (void)migrate_add_blocker(whpx_migration_blocker
, &local_error
);
1067 error_report_err(local_error
);
1068 error_free(whpx_migration_blocker
);
1069 migrate_del_blocker(whpx_migration_blocker
);
1074 vcpu
= g_malloc0(sizeof(struct whpx_vcpu
));
1077 error_report("WHPX: Failed to allocte VCPU context.");
1081 hr
= WHvEmulatorCreateEmulator(&whpx_emu_callbacks
, &vcpu
->emulator
);
1083 error_report("WHPX: Failed to setup instruction completion support,"
1089 hr
= WHvCreateVirtualProcessor(whpx
->partition
, cpu
->cpu_index
, 0);
1091 error_report("WHPX: Failed to create a virtual processor,"
1093 WHvEmulatorDestroyEmulator(vcpu
->emulator
);
1098 vcpu
->interruptable
= true;
1100 cpu
->vcpu_dirty
= true;
1101 cpu
->hax_vcpu
= (struct hax_vcpu_state
*)vcpu
;
1106 int whpx_vcpu_exec(CPUState
*cpu
)
1112 if (cpu
->exception_index
>= EXCP_INTERRUPT
) {
1113 ret
= cpu
->exception_index
;
1114 cpu
->exception_index
= -1;
1118 fatal
= whpx_vcpu_run(cpu
);
1121 error_report("WHPX: Failed to exec a virtual processor");
1129 void whpx_destroy_vcpu(CPUState
*cpu
)
1131 struct whpx_state
*whpx
= &whpx_global
;
1132 struct whpx_vcpu
*vcpu
= get_whpx_vcpu(cpu
);
1134 WHvDeleteVirtualProcessor(whpx
->partition
, cpu
->cpu_index
);
1135 WHvEmulatorDestroyEmulator(vcpu
->emulator
);
1136 g_free(cpu
->hax_vcpu
);
1140 void whpx_vcpu_kick(CPUState
*cpu
)
1142 struct whpx_state
*whpx
= &whpx_global
;
1143 WHvCancelRunVirtualProcessor(whpx
->partition
, cpu
->cpu_index
, 0);
1150 static void whpx_update_mapping(hwaddr start_pa
, ram_addr_t size
,
1151 void *host_va
, int add
, int rom
,
1154 struct whpx_state
*whpx
= &whpx_global
;
1159 printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
1160 (void*)start_pa, (void*)size, host_va,
1161 (rom ? "ROM" : "RAM"), name);
1163 printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
1164 (void*)start_pa, (void*)size, host_va, name);
1169 hr
= WHvMapGpaRange(whpx
->partition
,
1173 (WHvMapGpaRangeFlagRead
|
1174 WHvMapGpaRangeFlagExecute
|
1175 (rom
? 0 : WHvMapGpaRangeFlagWrite
)));
1177 hr
= WHvUnmapGpaRange(whpx
->partition
,
1183 error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
1184 " Host:%p, hr=%08lx",
1185 (add
? "MAP" : "UNMAP"), name
,
1186 (void *)start_pa
, (void *)size
, host_va
, hr
);
1190 static void whpx_process_section(MemoryRegionSection
*section
, int add
)
1192 MemoryRegion
*mr
= section
->mr
;
1193 hwaddr start_pa
= section
->offset_within_address_space
;
1194 ram_addr_t size
= int128_get64(section
->size
);
1198 if (!memory_region_is_ram(mr
)) {
1202 delta
= qemu_real_host_page_size
- (start_pa
& ~qemu_real_host_page_mask
);
1203 delta
&= ~qemu_real_host_page_mask
;
1209 size
&= qemu_real_host_page_mask
;
1210 if (!size
|| (start_pa
& ~qemu_real_host_page_mask
)) {
1214 host_va
= (uintptr_t)memory_region_get_ram_ptr(mr
)
1215 + section
->offset_within_region
+ delta
;
1217 whpx_update_mapping(start_pa
, size
, (void *)host_va
, add
,
1218 memory_region_is_rom(mr
), mr
->name
);
1221 static void whpx_region_add(MemoryListener
*listener
,
1222 MemoryRegionSection
*section
)
1224 memory_region_ref(section
->mr
);
1225 whpx_process_section(section
, 1);
1228 static void whpx_region_del(MemoryListener
*listener
,
1229 MemoryRegionSection
*section
)
1231 whpx_process_section(section
, 0);
1232 memory_region_unref(section
->mr
);
1235 static void whpx_transaction_begin(MemoryListener
*listener
)
1239 static void whpx_transaction_commit(MemoryListener
*listener
)
1243 static void whpx_log_sync(MemoryListener
*listener
,
1244 MemoryRegionSection
*section
)
1246 MemoryRegion
*mr
= section
->mr
;
1248 if (!memory_region_is_ram(mr
)) {
1252 memory_region_set_dirty(mr
, 0, int128_get64(section
->size
));
1255 static MemoryListener whpx_memory_listener
= {
1256 .begin
= whpx_transaction_begin
,
1257 .commit
= whpx_transaction_commit
,
1258 .region_add
= whpx_region_add
,
1259 .region_del
= whpx_region_del
,
1260 .log_sync
= whpx_log_sync
,
1264 static void whpx_memory_init(void)
1266 memory_listener_register(&whpx_memory_listener
, &address_space_memory
);
1269 static void whpx_handle_interrupt(CPUState
*cpu
, int mask
)
1271 cpu
->interrupt_request
|= mask
;
1273 if (!qemu_cpu_is_self(cpu
)) {
1282 static int whpx_accel_init(MachineState
*ms
)
1284 struct whpx_state
*whpx
;
1287 WHV_CAPABILITY whpx_cap
;
1288 UINT32 whpx_cap_size
;
1289 WHV_PARTITION_PROPERTY prop
;
1291 whpx
= &whpx_global
;
1293 memset(whpx
, 0, sizeof(struct whpx_state
));
1294 whpx
->mem_quota
= ms
->ram_size
;
1296 hr
= WHvGetCapability(WHvCapabilityCodeHypervisorPresent
, &whpx_cap
,
1297 sizeof(whpx_cap
), &whpx_cap_size
);
1298 if (FAILED(hr
) || !whpx_cap
.HypervisorPresent
) {
1299 error_report("WHPX: No accelerator found, hr=%08lx", hr
);
1304 hr
= WHvCreatePartition(&whpx
->partition
);
1306 error_report("WHPX: Failed to create partition, hr=%08lx", hr
);
1311 memset(&prop
, 0, sizeof(WHV_PARTITION_PROPERTY
));
1312 prop
.ProcessorCount
= smp_cpus
;
1313 hr
= WHvSetPartitionProperty(whpx
->partition
,
1314 WHvPartitionPropertyCodeProcessorCount
,
1316 sizeof(WHV_PARTITION_PROPERTY
));
1319 error_report("WHPX: Failed to set partition core count to %d,"
1320 " hr=%08lx", smp_cores
, hr
);
1325 memset(&prop
, 0, sizeof(WHV_PARTITION_PROPERTY
));
1326 prop
.ExtendedVmExits
.X64CpuidExit
= 1;
1327 hr
= WHvSetPartitionProperty(whpx
->partition
,
1328 WHvPartitionPropertyCodeExtendedVmExits
,
1330 sizeof(WHV_PARTITION_PROPERTY
));
1333 error_report("WHPX: Failed to enable partition extended X64CpuidExit"
1339 UINT32 cpuidExitList
[] = {1};
1340 hr
= WHvSetPartitionProperty(whpx
->partition
,
1341 WHvPartitionPropertyCodeCpuidExitList
,
1343 RTL_NUMBER_OF(cpuidExitList
) * sizeof(UINT32
));
1346 error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1352 hr
= WHvSetupPartition(whpx
->partition
);
1354 error_report("WHPX: Failed to setup partition, hr=%08lx", hr
);
1361 cpu_interrupt_handler
= whpx_handle_interrupt
;
1363 printf("Windows Hypervisor Platform accelerator is operational\n");
1368 if (NULL
!= whpx
->partition
) {
1369 WHvDeletePartition(whpx
->partition
);
1370 whpx
->partition
= NULL
;
1377 int whpx_enabled(void)
1379 return whpx_allowed
;
1382 static void whpx_accel_class_init(ObjectClass
*oc
, void *data
)
1384 AccelClass
*ac
= ACCEL_CLASS(oc
);
1386 ac
->init_machine
= whpx_accel_init
;
1387 ac
->allowed
= &whpx_allowed
;
1390 static const TypeInfo whpx_accel_type
= {
1391 .name
= ACCEL_CLASS_NAME("whpx"),
1392 .parent
= TYPE_ACCEL
,
1393 .class_init
= whpx_accel_class_init
,
1396 static void whpx_type_init(void)
1398 type_register_static(&whpx_accel_type
);
1401 type_init(whpx_type_init
);