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