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);