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"
17 #include "sysemu/accel.h"
18 #include "sysemu/whpx.h"
19 #include "sysemu/sysemu.h"
20 #include "sysemu/cpus.h"
21 #include "qemu/main-loop.h"
22 #include "hw/boards.h"
23 #include "qemu/error-report.h"
24 #include "qemu/queue.h"
25 #include "qapi/error.h"
26 #include "migration/blocker.h"
27 #include "whp-dispatch.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;
163 static bool whp_dispatch_initialized;
164 static HMODULE hWinHvPlatform, hWinHvEmulation;
166 struct whpx_state whpx_global;
167 struct WHPDispatch whp_dispatch;
174 static struct whpx_vcpu *get_whpx_vcpu(CPUState *cpu)
176 return (struct whpx_vcpu *)cpu->hax_vcpu;
179 static WHV_X64_SEGMENT_REGISTER whpx_seg_q2h(const SegmentCache *qs, int v86,
182 WHV_X64_SEGMENT_REGISTER hs;
183 unsigned flags = qs->flags;
186 hs.Limit = qs->limit;
187 hs.Selector = qs->selector;
193 hs.DescriptorPrivilegeLevel = 3;
194 hs.NonSystemSegment = 1;
197 hs.Attributes = (flags >> DESC_TYPE_SHIFT);
200 /* hs.Base &= 0xfffff; */
207 static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs)
212 qs.limit = hs->Limit;
213 qs.selector = hs->Selector;
215 qs.flags = ((uint32_t)hs->Attributes) << DESC_TYPE_SHIFT;
220 static void whpx_set_registers(CPUState *cpu)
222 struct whpx_state *whpx = &whpx_global;
223 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
224 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
225 X86CPU *x86_cpu = X86_CPU(cpu);
226 struct whpx_register_set vcxt = {0};
232 assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
234 v86 = (env->eflags & VM_MASK);
235 r86 = !(env->cr[0] & CR0_PE_MASK);
237 vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
238 vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
240 /* Indexes for first 16 registers match between HV and QEMU definitions */
241 for (idx = 0; idx < CPU_NB_REGS64; idx += 1) {
242 vcxt.values[idx].Reg64 = env->regs[idx];
245 /* Same goes for RIP and RFLAGS */
246 assert(whpx_register_names[idx] == WHvX64RegisterRip);
247 vcxt.values[idx++].Reg64 = env->eip;
249 assert(whpx_register_names[idx] == WHvX64RegisterRflags);
250 vcxt.values[idx++].Reg64 = env->eflags;
252 /* Translate 6+4 segment registers. HV and QEMU order matches */
253 assert(idx == WHvX64RegisterEs);
254 for (i = 0; i < 6; i += 1, idx += 1) {
255 vcxt.values[idx].Segment = whpx_seg_q2h(&env->segs[i], v86, r86);
258 assert(idx == WHvX64RegisterLdtr);
259 vcxt.values[idx++].Segment = whpx_seg_q2h(&env->ldt, 0, 0);
261 assert(idx == WHvX64RegisterTr);
262 vcxt.values[idx++].Segment = whpx_seg_q2h(&env->tr, 0, 0);
264 assert(idx == WHvX64RegisterIdtr);
265 vcxt.values[idx].Table.Base = env->idt.base;
266 vcxt.values[idx].Table.Limit = env->idt.limit;
269 assert(idx == WHvX64RegisterGdtr);
270 vcxt.values[idx].Table.Base = env->gdt.base;
271 vcxt.values[idx].Table.Limit = env->gdt.limit;
274 /* CR0, 2, 3, 4, 8 */
275 assert(whpx_register_names[idx] == WHvX64RegisterCr0);
276 vcxt.values[idx++].Reg64 = env->cr[0];
277 assert(whpx_register_names[idx] == WHvX64RegisterCr2);
278 vcxt.values[idx++].Reg64 = env->cr[2];
279 assert(whpx_register_names[idx] == WHvX64RegisterCr3);
280 vcxt.values[idx++].Reg64 = env->cr[3];
281 assert(whpx_register_names[idx] == WHvX64RegisterCr4);
282 vcxt.values[idx++].Reg64 = env->cr[4];
283 assert(whpx_register_names[idx] == WHvX64RegisterCr8);
284 vcxt.values[idx++].Reg64 = vcpu->tpr;
286 /* 8 Debug Registers - Skipped */
288 /* 16 XMM registers */
289 assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
290 for (i = 0; i < 16; i += 1, idx += 1) {
291 vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);
292 vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);
296 assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
297 for (i = 0; i < 8; i += 1, idx += 1) {
298 vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);
299 /* vcxt.values[idx].Fp.AsUINT128.High64 =
300 env->fpregs[i].mmx.MMX_Q(1);
304 /* FP control status register */
305 assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
306 vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;
307 vcxt.values[idx].FpControlStatus.FpStatus =
308 (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
309 vcxt.values[idx].FpControlStatus.FpTag = 0;
310 for (i = 0; i < 8; ++i) {
311 vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;
313 vcxt.values[idx].FpControlStatus.Reserved = 0;
314 vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;
315 vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;
318 /* XMM control status register */
319 assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
320 vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;
321 vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;
322 vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;
326 assert(whpx_register_names[idx] == WHvX64RegisterTsc);
327 vcxt.values[idx++].Reg64 = env->tsc;
328 assert(whpx_register_names[idx] == WHvX64RegisterEfer);
329 vcxt.values[idx++].Reg64 = env->efer;
331 assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
332 vcxt.values[idx++].Reg64 = env->kernelgsbase;
335 assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
336 vcxt.values[idx++].Reg64 = vcpu->apic_base;
338 /* WHvX64RegisterPat - Skipped */
340 assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
341 vcxt.values[idx++].Reg64 = env->sysenter_cs;
342 assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
343 vcxt.values[idx++].Reg64 = env->sysenter_eip;
344 assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
345 vcxt.values[idx++].Reg64 = env->sysenter_esp;
346 assert(whpx_register_names[idx] == WHvX64RegisterStar);
347 vcxt.values[idx++].Reg64 = env->star;
349 assert(whpx_register_names[idx] == WHvX64RegisterLstar);
350 vcxt.values[idx++].Reg64 = env->lstar;
351 assert(whpx_register_names[idx] == WHvX64RegisterCstar);
352 vcxt.values[idx++].Reg64 = env->cstar;
353 assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
354 vcxt.values[idx++].Reg64 = env->fmask;
357 /* Interrupt / Event Registers - Skipped */
359 assert(idx == RTL_NUMBER_OF(whpx_register_names));
361 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
362 whpx->partition, cpu->cpu_index,
364 RTL_NUMBER_OF(whpx_register_names),
368 error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
375 static void whpx_get_registers(CPUState *cpu)
377 struct whpx_state *whpx = &whpx_global;
378 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
379 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
380 X86CPU *x86_cpu = X86_CPU(cpu);
381 struct whpx_register_set vcxt;
382 uint64_t tpr, apic_base;
387 assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
389 hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
390 whpx->partition, cpu->cpu_index,
392 RTL_NUMBER_OF(whpx_register_names),
395 error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
399 /* Indexes for first 16 registers match between HV and QEMU definitions */
400 for (idx = 0; idx < CPU_NB_REGS64; idx += 1) {
401 env->regs[idx] = vcxt.values[idx].Reg64;
404 /* Same goes for RIP and RFLAGS */
405 assert(whpx_register_names[idx] == WHvX64RegisterRip);
406 env->eip = vcxt.values[idx++].Reg64;
407 assert(whpx_register_names[idx] == WHvX64RegisterRflags);
408 env->eflags = vcxt.values[idx++].Reg64;
410 /* Translate 6+4 segment registers. HV and QEMU order matches */
411 assert(idx == WHvX64RegisterEs);
412 for (i = 0; i < 6; i += 1, idx += 1) {
413 env->segs[i] = whpx_seg_h2q(&vcxt.values[idx].Segment);
416 assert(idx == WHvX64RegisterLdtr);
417 env->ldt = whpx_seg_h2q(&vcxt.values[idx++].Segment);
418 assert(idx == WHvX64RegisterTr);
419 env->tr = whpx_seg_h2q(&vcxt.values[idx++].Segment);
420 assert(idx == WHvX64RegisterIdtr);
421 env->idt.base = vcxt.values[idx].Table.Base;
422 env->idt.limit = vcxt.values[idx].Table.Limit;
424 assert(idx == WHvX64RegisterGdtr);
425 env->gdt.base = vcxt.values[idx].Table.Base;
426 env->gdt.limit = vcxt.values[idx].Table.Limit;
429 /* CR0, 2, 3, 4, 8 */
430 assert(whpx_register_names[idx] == WHvX64RegisterCr0);
431 env->cr[0] = vcxt.values[idx++].Reg64;
432 assert(whpx_register_names[idx] == WHvX64RegisterCr2);
433 env->cr[2] = vcxt.values[idx++].Reg64;
434 assert(whpx_register_names[idx] == WHvX64RegisterCr3);
435 env->cr[3] = vcxt.values[idx++].Reg64;
436 assert(whpx_register_names[idx] == WHvX64RegisterCr4);
437 env->cr[4] = vcxt.values[idx++].Reg64;
438 assert(whpx_register_names[idx] == WHvX64RegisterCr8);
439 tpr = vcxt.values[idx++].Reg64;
440 if (tpr != vcpu->tpr) {
442 cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
445 /* 8 Debug Registers - Skipped */
447 /* 16 XMM registers */
448 assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
449 for (i = 0; i < 16; i += 1, idx += 1) {
450 env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;
451 env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;
455 assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
456 for (i = 0; i < 8; i += 1, idx += 1) {
457 env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;
458 /* env->fpregs[i].mmx.MMX_Q(1) =
459 vcxt.values[idx].Fp.AsUINT128.High64;
463 /* FP control status register */
464 assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
465 env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;
466 env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;
467 env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;
468 for (i = 0; i < 8; ++i) {
469 env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);
471 env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;
472 env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;
475 /* XMM control status register */
476 assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
477 env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
481 assert(whpx_register_names[idx] == WHvX64RegisterTsc);
482 env->tsc = vcxt.values[idx++].Reg64;
483 assert(whpx_register_names[idx] == WHvX64RegisterEfer);
484 env->efer = vcxt.values[idx++].Reg64;
486 assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
487 env->kernelgsbase = vcxt.values[idx++].Reg64;
490 assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
491 apic_base = vcxt.values[idx++].Reg64;
492 if (apic_base != vcpu->apic_base) {
493 vcpu->apic_base = apic_base;
494 cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base);
497 /* WHvX64RegisterPat - Skipped */
499 assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
500 env->sysenter_cs = vcxt.values[idx++].Reg64;;
501 assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
502 env->sysenter_eip = vcxt.values[idx++].Reg64;
503 assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
504 env->sysenter_esp = vcxt.values[idx++].Reg64;
505 assert(whpx_register_names[idx] == WHvX64RegisterStar);
506 env->star = vcxt.values[idx++].Reg64;
508 assert(whpx_register_names[idx] == WHvX64RegisterLstar);
509 env->lstar = vcxt.values[idx++].Reg64;
510 assert(whpx_register_names[idx] == WHvX64RegisterCstar);
511 env->cstar = vcxt.values[idx++].Reg64;
512 assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
513 env->fmask = vcxt.values[idx++].Reg64;
516 /* Interrupt / Event Registers - Skipped */
518 assert(idx == RTL_NUMBER_OF(whpx_register_names));
523 static HRESULT CALLBACK whpx_emu_ioport_callback(
525 WHV_EMULATOR_IO_ACCESS_INFO *IoAccess)
527 MemTxAttrs attrs = { 0 };
528 address_space_rw(&address_space_io, IoAccess->Port, attrs,
529 (uint8_t *)&IoAccess->Data, IoAccess->AccessSize,
530 IoAccess->Direction);
534 static HRESULT CALLBACK whpx_emu_mmio_callback(
536 WHV_EMULATOR_MEMORY_ACCESS_INFO *ma)
538 cpu_physical_memory_rw(ma->GpaAddress, ma->Data, ma->AccessSize,
543 static HRESULT CALLBACK whpx_emu_getreg_callback(
545 const WHV_REGISTER_NAME *RegisterNames,
546 UINT32 RegisterCount,
547 WHV_REGISTER_VALUE *RegisterValues)
550 struct whpx_state *whpx = &whpx_global;
551 CPUState *cpu = (CPUState *)ctx;
553 hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
554 whpx->partition, cpu->cpu_index,
555 RegisterNames, RegisterCount,
558 error_report("WHPX: Failed to get virtual processor registers,"
565 static HRESULT CALLBACK whpx_emu_setreg_callback(
567 const WHV_REGISTER_NAME *RegisterNames,
568 UINT32 RegisterCount,
569 const WHV_REGISTER_VALUE *RegisterValues)
572 struct whpx_state *whpx = &whpx_global;
573 CPUState *cpu = (CPUState *)ctx;
575 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
576 whpx->partition, cpu->cpu_index,
577 RegisterNames, RegisterCount,
580 error_report("WHPX: Failed to set virtual processor registers,"
585 * The emulator just successfully wrote the register state. We clear the
586 * dirty state so we avoid the double write on resume of the VP.
588 cpu->vcpu_dirty = false;
593 static HRESULT CALLBACK whpx_emu_translate_callback(
595 WHV_GUEST_VIRTUAL_ADDRESS Gva,
596 WHV_TRANSLATE_GVA_FLAGS TranslateFlags,
597 WHV_TRANSLATE_GVA_RESULT_CODE *TranslationResult,
598 WHV_GUEST_PHYSICAL_ADDRESS *Gpa)
601 struct whpx_state *whpx = &whpx_global;
602 CPUState *cpu = (CPUState *)ctx;
603 WHV_TRANSLATE_GVA_RESULT res;
605 hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index,
606 Gva, TranslateFlags, &res, Gpa);
608 error_report("WHPX: Failed to translate GVA, hr=%08lx", hr);
610 *TranslationResult = res.ResultCode;
616 static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = {
617 .Size = sizeof(WHV_EMULATOR_CALLBACKS),
618 .WHvEmulatorIoPortCallback = whpx_emu_ioport_callback,
619 .WHvEmulatorMemoryCallback = whpx_emu_mmio_callback,
620 .WHvEmulatorGetVirtualProcessorRegisters = whpx_emu_getreg_callback,
621 .WHvEmulatorSetVirtualProcessorRegisters = whpx_emu_setreg_callback,
622 .WHvEmulatorTranslateGvaPage = whpx_emu_translate_callback,
625 static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
628 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
629 WHV_EMULATOR_STATUS emu_status;
631 hr = whp_dispatch.WHvEmulatorTryMmioEmulation(
633 &vcpu->exit_ctx.VpContext, ctx,
636 error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr);
640 if (!emu_status.EmulationSuccessful) {
641 error_report("WHPX: Failed to emulate MMIO access with"
642 " EmulatorReturnStatus: %u", emu_status.AsUINT32);
649 static int whpx_handle_portio(CPUState *cpu,
650 WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx)
653 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
654 WHV_EMULATOR_STATUS emu_status;
656 hr = whp_dispatch.WHvEmulatorTryIoEmulation(
658 &vcpu->exit_ctx.VpContext, ctx,
661 error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr);
665 if (!emu_status.EmulationSuccessful) {
666 error_report("WHPX: Failed to emulate PortIO access with"
667 " EmulatorReturnStatus: %u", emu_status.AsUINT32);
674 static int whpx_handle_halt(CPUState *cpu)
676 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
679 qemu_mutex_lock_iothread();
680 if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
681 (env->eflags & IF_MASK)) &&
682 !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
683 cpu->exception_index = EXCP_HLT;
687 qemu_mutex_unlock_iothread();
692 static void whpx_vcpu_pre_run(CPUState *cpu)
695 struct whpx_state *whpx = &whpx_global;
696 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
697 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
698 X86CPU *x86_cpu = X86_CPU(cpu);
701 WHV_X64_PENDING_INTERRUPTION_REGISTER new_int = {0};
702 UINT32 reg_count = 0;
703 WHV_REGISTER_VALUE reg_values[3] = {0};
704 WHV_REGISTER_NAME reg_names[3];
706 qemu_mutex_lock_iothread();
709 if (!vcpu->interruption_pending &&
710 cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) {
711 if (cpu->interrupt_request & CPU_INTERRUPT_NMI) {
712 cpu->interrupt_request &= ~CPU_INTERRUPT_NMI;
713 vcpu->interruptable = false;
714 new_int.InterruptionType = WHvX64PendingNmi;
715 new_int.InterruptionPending = 1;
716 new_int.InterruptionVector = 2;
718 if (cpu->interrupt_request & CPU_INTERRUPT_SMI) {
719 cpu->interrupt_request &= ~CPU_INTERRUPT_SMI;
724 * Force the VCPU out of its inner loop to process any INIT requests or
725 * commit pending TPR access.
727 if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) {
728 if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
729 !(env->hflags & HF_SMM_MASK)) {
730 cpu->exit_request = 1;
732 if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
733 cpu->exit_request = 1;
737 /* Get pending hard interruption or replay one that was overwritten */
738 if (!vcpu->interruption_pending &&
739 vcpu->interruptable && (env->eflags & IF_MASK)) {
740 assert(!new_int.InterruptionPending);
741 if (cpu->interrupt_request & CPU_INTERRUPT_HARD) {
742 cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
743 irq = cpu_get_pic_interrupt(env);
745 new_int.InterruptionType = WHvX64PendingInterrupt;
746 new_int.InterruptionPending = 1;
747 new_int.InterruptionVector = irq;
752 /* Setup interrupt state if new one was prepared */
753 if (new_int.InterruptionPending) {
754 reg_values[reg_count].PendingInterruption = new_int;
755 reg_names[reg_count] = WHvRegisterPendingInterruption;
759 /* Sync the TPR to the CR8 if was modified during the intercept */
760 tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
761 if (tpr != vcpu->tpr) {
763 reg_values[reg_count].Reg64 = tpr;
764 cpu->exit_request = 1;
765 reg_names[reg_count] = WHvX64RegisterCr8;
769 /* Update the state of the interrupt delivery notification */
770 if (!vcpu->window_registered &&
771 cpu->interrupt_request & CPU_INTERRUPT_HARD) {
772 reg_values[reg_count].DeliverabilityNotifications.InterruptNotification
774 vcpu->window_registered = 1;
775 reg_names[reg_count] = WHvX64RegisterDeliverabilityNotifications;
779 qemu_mutex_unlock_iothread();
782 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
783 whpx->partition, cpu->cpu_index,
784 reg_names, reg_count, reg_values);
786 error_report("WHPX: Failed to set interrupt state registers,"
794 static void whpx_vcpu_post_run(CPUState *cpu)
796 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
797 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
798 X86CPU *x86_cpu = X86_CPU(cpu);
800 env->eflags = vcpu->exit_ctx.VpContext.Rflags;
802 uint64_t tpr = vcpu->exit_ctx.VpContext.Cr8;
803 if (vcpu->tpr != tpr) {
805 qemu_mutex_lock_iothread();
806 cpu_set_apic_tpr(x86_cpu->apic_state, vcpu->tpr);
807 qemu_mutex_unlock_iothread();
810 vcpu->interruption_pending =
811 vcpu->exit_ctx.VpContext.ExecutionState.InterruptionPending;
813 vcpu->interruptable =
814 !vcpu->exit_ctx.VpContext.ExecutionState.InterruptShadow;
819 static void whpx_vcpu_process_async_events(CPUState *cpu)
821 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
822 X86CPU *x86_cpu = X86_CPU(cpu);
823 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
825 if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
826 !(env->hflags & HF_SMM_MASK)) {
828 do_cpu_init(x86_cpu);
829 cpu->vcpu_dirty = true;
830 vcpu->interruptable = true;
833 if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
834 cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
835 apic_poll_irq(x86_cpu->apic_state);
838 if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
839 (env->eflags & IF_MASK)) ||
840 (cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
844 if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) {
845 if (!cpu->vcpu_dirty) {
846 whpx_get_registers(cpu);
848 do_cpu_sipi(x86_cpu);
851 if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
852 cpu->interrupt_request &= ~CPU_INTERRUPT_TPR;
853 if (!cpu->vcpu_dirty) {
854 whpx_get_registers(cpu);
856 apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip,
857 env->tpr_access_type);
863 static int whpx_vcpu_run(CPUState *cpu)
866 struct whpx_state *whpx = &whpx_global;
867 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
870 whpx_vcpu_process_async_events(cpu);
872 cpu->exception_index = EXCP_HLT;
873 atomic_set(&cpu->exit_request, false);
877 qemu_mutex_unlock_iothread();
881 if (cpu->vcpu_dirty) {
882 whpx_set_registers(cpu);
883 cpu->vcpu_dirty = false;
886 whpx_vcpu_pre_run(cpu);
888 if (atomic_read(&cpu->exit_request)) {
892 hr = whp_dispatch.WHvRunVirtualProcessor(
893 whpx->partition, cpu->cpu_index,
894 &vcpu->exit_ctx, sizeof(vcpu->exit_ctx));
897 error_report("WHPX: Failed to exec a virtual processor,"
903 whpx_vcpu_post_run(cpu);
905 switch (vcpu->exit_ctx.ExitReason) {
906 case WHvRunVpExitReasonMemoryAccess:
907 ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
910 case WHvRunVpExitReasonX64IoPortAccess:
911 ret = whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess);
914 case WHvRunVpExitReasonX64InterruptWindow:
915 vcpu->window_registered = 0;
918 case WHvRunVpExitReasonX64Halt:
919 ret = whpx_handle_halt(cpu);
922 case WHvRunVpExitReasonCanceled:
923 cpu->exception_index = EXCP_INTERRUPT;
927 case WHvRunVpExitReasonX64Cpuid: {
928 WHV_REGISTER_VALUE reg_values[5] = {0};
929 WHV_REGISTER_NAME reg_names[5];
930 UINT32 reg_count = 5;
931 UINT64 rip, rax, rcx, rdx, rbx;
933 rip = vcpu->exit_ctx.VpContext.Rip +
934 vcpu->exit_ctx.VpContext.InstructionLength;
935 switch (vcpu->exit_ctx.CpuidAccess.Rax) {
937 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
938 /* Advertise that we are running on a hypervisor */
940 vcpu->exit_ctx.CpuidAccess.DefaultResultRcx |
941 CPUID_EXT_HYPERVISOR;
943 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
944 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
947 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
948 rcx = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx;
949 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
950 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
953 reg_names[0] = WHvX64RegisterRip;
954 reg_names[1] = WHvX64RegisterRax;
955 reg_names[2] = WHvX64RegisterRcx;
956 reg_names[3] = WHvX64RegisterRdx;
957 reg_names[4] = WHvX64RegisterRbx;
959 reg_values[0].Reg64 = rip;
960 reg_values[1].Reg64 = rax;
961 reg_values[2].Reg64 = rcx;
962 reg_values[3].Reg64 = rdx;
963 reg_values[4].Reg64 = rbx;
965 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
966 whpx->partition, cpu->cpu_index,
972 error_report("WHPX: Failed to set CpuidAccess state registers,"
978 case WHvRunVpExitReasonNone:
979 case WHvRunVpExitReasonUnrecoverableException:
980 case WHvRunVpExitReasonInvalidVpRegisterValue:
981 case WHvRunVpExitReasonUnsupportedFeature:
982 case WHvRunVpExitReasonX64MsrAccess:
983 case WHvRunVpExitReasonException:
985 error_report("WHPX: Unexpected VP exit code %d",
986 vcpu->exit_ctx.ExitReason);
987 whpx_get_registers(cpu);
988 qemu_mutex_lock_iothread();
989 qemu_system_guest_panicked(cpu_get_crash_info(cpu));
990 qemu_mutex_unlock_iothread();
997 qemu_mutex_lock_iothread();
1000 atomic_set(&cpu->exit_request, false);
1005 static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
1007 whpx_get_registers(cpu);
1008 cpu->vcpu_dirty = true;
1011 static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu,
1012 run_on_cpu_data arg)
1014 whpx_set_registers(cpu);
1015 cpu->vcpu_dirty = false;
1018 static void do_whpx_cpu_synchronize_post_init(CPUState *cpu,
1019 run_on_cpu_data arg)
1021 whpx_set_registers(cpu);
1022 cpu->vcpu_dirty = false;
1025 static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu,
1026 run_on_cpu_data arg)
1028 cpu->vcpu_dirty = true;
1035 void whpx_cpu_synchronize_state(CPUState *cpu)
1037 if (!cpu->vcpu_dirty) {
1038 run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL);
1042 void whpx_cpu_synchronize_post_reset(CPUState *cpu)
1044 run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
1047 void whpx_cpu_synchronize_post_init(CPUState *cpu)
1049 run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
1052 void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
1054 run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
1061 static Error *whpx_migration_blocker;
1063 int whpx_init_vcpu(CPUState *cpu)
1066 struct whpx_state *whpx = &whpx_global;
1067 struct whpx_vcpu *vcpu;
1068 Error *local_error = NULL;
1070 /* Add migration blockers for all unsupported features of the
1071 * Windows Hypervisor Platform
1073 if (whpx_migration_blocker == NULL) {
1074 error_setg(&whpx_migration_blocker,
1075 "State blocked due to non-migratable CPUID feature support,"
1076 "dirty memory tracking support, and XSAVE/XRSTOR support");
1078 (void)migrate_add_blocker(whpx_migration_blocker, &local_error);
1080 error_report_err(local_error);
1081 migrate_del_blocker(whpx_migration_blocker);
1082 error_free(whpx_migration_blocker);
1087 vcpu = g_malloc0(sizeof(struct whpx_vcpu));
1090 error_report("WHPX: Failed to allocte VCPU context.");
1094 hr = whp_dispatch.WHvEmulatorCreateEmulator(
1095 &whpx_emu_callbacks,
1098 error_report("WHPX: Failed to setup instruction completion support,"
1104 hr = whp_dispatch.WHvCreateVirtualProcessor(
1105 whpx->partition, cpu->cpu_index, 0);
1107 error_report("WHPX: Failed to create a virtual processor,"
1109 whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1114 vcpu->interruptable = true;
1116 cpu->vcpu_dirty = true;
1117 cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu;
1122 int whpx_vcpu_exec(CPUState *cpu)
1128 if (cpu->exception_index >= EXCP_INTERRUPT) {
1129 ret = cpu->exception_index;
1130 cpu->exception_index = -1;
1134 fatal = whpx_vcpu_run(cpu);
1137 error_report("WHPX: Failed to exec a virtual processor");
1145 void whpx_destroy_vcpu(CPUState *cpu)
1147 struct whpx_state *whpx = &whpx_global;
1148 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
1150 whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
1151 whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1152 g_free(cpu->hax_vcpu);
1156 void whpx_vcpu_kick(CPUState *cpu)
1158 struct whpx_state *whpx = &whpx_global;
1159 whp_dispatch.WHvCancelRunVirtualProcessor(
1160 whpx->partition, cpu->cpu_index, 0);
1167 static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
1168 void *host_va, int add, int rom,
1171 struct whpx_state *whpx = &whpx_global;
1176 printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
1177 (void*)start_pa, (void*)size, host_va,
1178 (rom ? "ROM" : "RAM"), name);
1180 printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
1181 (void*)start_pa, (void*)size, host_va, name);
1186 hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
1190 (WHvMapGpaRangeFlagRead |
1191 WHvMapGpaRangeFlagExecute |
1192 (rom ? 0 : WHvMapGpaRangeFlagWrite)));
1194 hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
1200 error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
1201 " Host:%p, hr=%08lx",
1202 (add ? "MAP" : "UNMAP"), name,
1203 (void *)start_pa, (void *)size, host_va, hr);
1207 static void whpx_process_section(MemoryRegionSection *section, int add)
1209 MemoryRegion *mr = section->mr;
1210 hwaddr start_pa = section->offset_within_address_space;
1211 ram_addr_t size = int128_get64(section->size);
1215 if (!memory_region_is_ram(mr)) {
1219 delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
1220 delta &= ~qemu_real_host_page_mask;
1226 size &= qemu_real_host_page_mask;
1227 if (!size || (start_pa & ~qemu_real_host_page_mask)) {
1231 host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
1232 + section->offset_within_region + delta;
1234 whpx_update_mapping(start_pa, size, (void *)host_va, add,
1235 memory_region_is_rom(mr), mr->name);
1238 static void whpx_region_add(MemoryListener *listener,
1239 MemoryRegionSection *section)
1241 memory_region_ref(section->mr);
1242 whpx_process_section(section, 1);
1245 static void whpx_region_del(MemoryListener *listener,
1246 MemoryRegionSection *section)
1248 whpx_process_section(section, 0);
1249 memory_region_unref(section->mr);
1252 static void whpx_transaction_begin(MemoryListener *listener)
1256 static void whpx_transaction_commit(MemoryListener *listener)
1260 static void whpx_log_sync(MemoryListener *listener,
1261 MemoryRegionSection *section)
1263 MemoryRegion *mr = section->mr;
1265 if (!memory_region_is_ram(mr)) {
1269 memory_region_set_dirty(mr, 0, int128_get64(section->size));
1272 static MemoryListener whpx_memory_listener = {
1273 .begin = whpx_transaction_begin,
1274 .commit = whpx_transaction_commit,
1275 .region_add = whpx_region_add,
1276 .region_del = whpx_region_del,
1277 .log_sync = whpx_log_sync,
1281 static void whpx_memory_init(void)
1283 memory_listener_register(&whpx_memory_listener, &address_space_memory);
1286 static void whpx_handle_interrupt(CPUState *cpu, int mask)
1288 cpu->interrupt_request |= mask;
1290 if (!qemu_cpu_is_self(cpu)) {
1299 static int whpx_accel_init(MachineState *ms)
1301 struct whpx_state *whpx;
1304 WHV_CAPABILITY whpx_cap;
1305 UINT32 whpx_cap_size;
1306 WHV_PARTITION_PROPERTY prop;
1308 whpx = &whpx_global;
1310 if (!init_whp_dispatch()) {
1315 memset(whpx, 0, sizeof(struct whpx_state));
1316 whpx->mem_quota = ms->ram_size;
1318 hr = whp_dispatch.WHvGetCapability(
1319 WHvCapabilityCodeHypervisorPresent, &whpx_cap,
1320 sizeof(whpx_cap), &whpx_cap_size);
1321 if (FAILED(hr) || !whpx_cap.HypervisorPresent) {
1322 error_report("WHPX: No accelerator found, hr=%08lx", hr);
1327 hr = whp_dispatch.WHvCreatePartition(&whpx->partition);
1329 error_report("WHPX: Failed to create partition, hr=%08lx", hr);
1334 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1335 prop.ProcessorCount = smp_cpus;
1336 hr = whp_dispatch.WHvSetPartitionProperty(
1338 WHvPartitionPropertyCodeProcessorCount,
1340 sizeof(WHV_PARTITION_PROPERTY));
1343 error_report("WHPX: Failed to set partition core count to %d,"
1344 " hr=%08lx", smp_cores, hr);
1349 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1350 prop.ExtendedVmExits.X64CpuidExit = 1;
1351 hr = whp_dispatch.WHvSetPartitionProperty(
1353 WHvPartitionPropertyCodeExtendedVmExits,
1355 sizeof(WHV_PARTITION_PROPERTY));
1358 error_report("WHPX: Failed to enable partition extended X64CpuidExit"
1364 UINT32 cpuidExitList[] = {1};
1365 hr = whp_dispatch.WHvSetPartitionProperty(
1367 WHvPartitionPropertyCodeCpuidExitList,
1369 RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
1371 error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1377 hr = whp_dispatch.WHvSetupPartition(whpx->partition);
1379 error_report("WHPX: Failed to setup partition, hr=%08lx", hr);
1386 cpu_interrupt_handler = whpx_handle_interrupt;
1388 printf("Windows Hypervisor Platform accelerator is operational\n");
1393 if (NULL != whpx->partition) {
1394 whp_dispatch.WHvDeletePartition(whpx->partition);
1395 whpx->partition = NULL;
1402 int whpx_enabled(void)
1404 return whpx_allowed;
1407 static void whpx_accel_class_init(ObjectClass *oc, void *data)
1409 AccelClass *ac = ACCEL_CLASS(oc);
1411 ac->init_machine = whpx_accel_init;
1412 ac->allowed = &whpx_allowed;
1415 static const TypeInfo whpx_accel_type = {
1416 .name = ACCEL_CLASS_NAME("whpx"),
1417 .parent = TYPE_ACCEL,
1418 .class_init = whpx_accel_class_init,
1421 static void whpx_type_init(void)
1423 type_register_static(&whpx_accel_type);
1426 bool init_whp_dispatch(void)
1428 const char *lib_name;
1431 if (whp_dispatch_initialized) {
1435 #define WHP_LOAD_FIELD(return_type, function_name, signature) \
1436 whp_dispatch.function_name = \
1437 (function_name ## _t)GetProcAddress(hLib, #function_name); \
1438 if (!whp_dispatch.function_name) { \
1439 error_report("Could not load function %s from library %s.", \
1440 #function_name, lib_name); \
1444 lib_name = "WinHvPlatform.dll";
1445 hWinHvPlatform = LoadLibrary(lib_name);
1446 if (!hWinHvPlatform) {
1447 error_report("Could not load library %s.", lib_name);
1450 hLib = hWinHvPlatform;
1451 LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
1453 lib_name = "WinHvEmulation.dll";
1454 hWinHvEmulation = LoadLibrary(lib_name);
1455 if (!hWinHvEmulation) {
1456 error_report("Could not load library %s.", lib_name);
1459 hLib = hWinHvEmulation;
1460 LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD)
1462 whp_dispatch_initialized = true;
1467 if (hWinHvPlatform) {
1468 FreeLibrary(hWinHvPlatform);
1470 if (hWinHvEmulation) {
1471 FreeLibrary(hWinHvEmulation);
1476 type_init(whpx_type_init);