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