i386: Don't automatically enable FEAT_KVM_HINTS bits
[qemu.git] / target / i386 / whpx-all.c
blob58435178a4a99f3310de3120b941f9da947ccf75
1 /*
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.
9 */
11 #include "qemu/osdep.h"
12 #include "cpu.h"
13 #include "exec/address-spaces.h"
14 #include "exec/exec-all.h"
15 #include "exec/ioport.h"
16 #include "qemu-common.h"
17 #include "strings.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>
32 struct whpx_state {
33 uint64_t mem_quota;
34 WHV_PARTITION_HANDLE partition;
37 static const WHV_REGISTER_NAME whpx_register_names[] = {
39 /* X64 General purpose registers */
40 WHvX64RegisterRax,
41 WHvX64RegisterRcx,
42 WHvX64RegisterRdx,
43 WHvX64RegisterRbx,
44 WHvX64RegisterRsp,
45 WHvX64RegisterRbp,
46 WHvX64RegisterRsi,
47 WHvX64RegisterRdi,
48 WHvX64RegisterR8,
49 WHvX64RegisterR9,
50 WHvX64RegisterR10,
51 WHvX64RegisterR11,
52 WHvX64RegisterR12,
53 WHvX64RegisterR13,
54 WHvX64RegisterR14,
55 WHvX64RegisterR15,
56 WHvX64RegisterRip,
57 WHvX64RegisterRflags,
59 /* X64 Segment registers */
60 WHvX64RegisterEs,
61 WHvX64RegisterCs,
62 WHvX64RegisterSs,
63 WHvX64RegisterDs,
64 WHvX64RegisterFs,
65 WHvX64RegisterGs,
66 WHvX64RegisterLdtr,
67 WHvX64RegisterTr,
69 /* X64 Table registers */
70 WHvX64RegisterIdtr,
71 WHvX64RegisterGdtr,
73 /* X64 Control Registers */
74 WHvX64RegisterCr0,
75 WHvX64RegisterCr2,
76 WHvX64RegisterCr3,
77 WHvX64RegisterCr4,
78 WHvX64RegisterCr8,
80 /* X64 Debug Registers */
82 * WHvX64RegisterDr0,
83 * WHvX64RegisterDr1,
84 * WHvX64RegisterDr2,
85 * WHvX64RegisterDr3,
86 * WHvX64RegisterDr6,
87 * WHvX64RegisterDr7,
90 /* X64 Floating Point and Vector Registers */
91 WHvX64RegisterXmm0,
92 WHvX64RegisterXmm1,
93 WHvX64RegisterXmm2,
94 WHvX64RegisterXmm3,
95 WHvX64RegisterXmm4,
96 WHvX64RegisterXmm5,
97 WHvX64RegisterXmm6,
98 WHvX64RegisterXmm7,
99 WHvX64RegisterXmm8,
100 WHvX64RegisterXmm9,
101 WHvX64RegisterXmm10,
102 WHvX64RegisterXmm11,
103 WHvX64RegisterXmm12,
104 WHvX64RegisterXmm13,
105 WHvX64RegisterXmm14,
106 WHvX64RegisterXmm15,
107 WHvX64RegisterFpMmx0,
108 WHvX64RegisterFpMmx1,
109 WHvX64RegisterFpMmx2,
110 WHvX64RegisterFpMmx3,
111 WHvX64RegisterFpMmx4,
112 WHvX64RegisterFpMmx5,
113 WHvX64RegisterFpMmx6,
114 WHvX64RegisterFpMmx7,
115 WHvX64RegisterFpControlStatus,
116 WHvX64RegisterXmmControlStatus,
118 /* X64 MSRs */
119 WHvX64RegisterTsc,
120 WHvX64RegisterEfer,
121 #ifdef TARGET_X86_64
122 WHvX64RegisterKernelGsBase,
123 #endif
124 WHvX64RegisterApicBase,
125 /* WHvX64RegisterPat, */
126 WHvX64RegisterSysenterCs,
127 WHvX64RegisterSysenterEip,
128 WHvX64RegisterSysenterEsp,
129 WHvX64RegisterStar,
130 #ifdef TARGET_X86_64
131 WHvX64RegisterLstar,
132 WHvX64RegisterCstar,
133 WHvX64RegisterSfmask,
134 #endif
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)];
150 struct whpx_vcpu {
151 WHV_EMULATOR_HANDLE emulator;
152 bool window_registered;
153 bool interruptable;
154 uint64_t tpr;
155 uint64_t apic_base;
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;
168 * VP support
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,
177 int r86)
179 WHV_X64_SEGMENT_REGISTER hs;
180 unsigned flags = qs->flags;
182 hs.Base = qs->base;
183 hs.Limit = qs->limit;
184 hs.Selector = qs->selector;
186 if (v86) {
187 hs.Attributes = 0;
188 hs.SegmentType = 3;
189 hs.Present = 1;
190 hs.DescriptorPrivilegeLevel = 3;
191 hs.NonSystemSegment = 1;
193 } else {
194 hs.Attributes = (flags >> DESC_TYPE_SHIFT);
196 if (r86) {
197 /* hs.Base &= 0xfffff; */
201 return hs;
204 static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs)
206 SegmentCache qs;
208 qs.base = hs->Base;
209 qs.limit = hs->Limit;
210 qs.selector = hs->Selector;
212 qs.flags = ((uint32_t)hs->Attributes) << DESC_TYPE_SHIFT;
214 return qs;
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};
224 HRESULT hr;
225 int idx = 0;
226 int i;
227 int v86, r86;
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;
264 idx += 1;
266 assert(idx == WHvX64RegisterGdtr);
267 vcxt.values[idx].Table.Base = env->gdt.base;
268 vcxt.values[idx].Table.Limit = env->gdt.limit;
269 idx += 1;
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);
292 /* 8 FP registers */
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;
313 idx += 1;
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;
320 idx += 1;
322 /* MSRs */
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;
327 #ifdef TARGET_X86_64
328 assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
329 vcxt.values[idx++].Reg64 = env->kernelgsbase;
330 #endif
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;
345 #ifdef TARGET_X86_64
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;
352 #endif
354 /* Interrupt / Event Registers - Skipped */
356 assert(idx == RTL_NUMBER_OF(whpx_register_names));
358 hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index,
359 whpx_register_names,
360 RTL_NUMBER_OF(whpx_register_names),
361 &vcxt.values[0]);
363 if (FAILED(hr)) {
364 error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
365 hr);
368 return;
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;
379 HRESULT hr;
380 int idx = 0;
381 int i;
383 assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
385 hr = WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index,
386 whpx_register_names,
387 RTL_NUMBER_OF(whpx_register_names),
388 &vcxt.values[0]);
389 if (FAILED(hr)) {
390 error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
391 hr);
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;
418 idx += 1;
419 assert(idx == WHvX64RegisterGdtr);
420 env->gdt.base = vcxt.values[idx].Table.Base;
421 env->gdt.limit = vcxt.values[idx].Table.Limit;
422 idx += 1;
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) {
436 vcpu->tpr = 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;
449 /* 8 FP registers */
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;
468 idx += 1;
470 /* XMM control status register */
471 assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
472 env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
473 idx += 1;
475 /* MSRs */
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;
480 #ifdef TARGET_X86_64
481 assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
482 env->kernelgsbase = vcxt.values[idx++].Reg64;
483 #endif
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;
502 #ifdef TARGET_X86_64
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;
509 #endif
511 /* Interrupt / Event Registers - Skipped */
513 assert(idx == RTL_NUMBER_OF(whpx_register_names));
515 return;
518 static HRESULT CALLBACK whpx_emu_ioport_callback(
519 void *ctx,
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);
526 return S_OK;
529 static HRESULT CALLBACK whpx_emu_mmio_callback(
530 void *ctx,
531 WHV_EMULATOR_MEMORY_ACCESS_INFO *ma)
533 cpu_physical_memory_rw(ma->GpaAddress, ma->Data, ma->AccessSize,
534 ma->Direction);
535 return S_OK;
538 static HRESULT CALLBACK whpx_emu_getreg_callback(
539 void *ctx,
540 const WHV_REGISTER_NAME *RegisterNames,
541 UINT32 RegisterCount,
542 WHV_REGISTER_VALUE *RegisterValues)
544 HRESULT hr;
545 struct whpx_state *whpx = &whpx_global;
546 CPUState *cpu = (CPUState *)ctx;
548 hr = WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index,
549 RegisterNames, RegisterCount,
550 RegisterValues);
551 if (FAILED(hr)) {
552 error_report("WHPX: Failed to get virtual processor registers,"
553 " hr=%08lx", hr);
556 return hr;
559 static HRESULT CALLBACK whpx_emu_setreg_callback(
560 void *ctx,
561 const WHV_REGISTER_NAME *RegisterNames,
562 UINT32 RegisterCount,
563 const WHV_REGISTER_VALUE *RegisterValues)
565 HRESULT hr;
566 struct whpx_state *whpx = &whpx_global;
567 CPUState *cpu = (CPUState *)ctx;
569 hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index,
570 RegisterNames, RegisterCount,
571 RegisterValues);
572 if (FAILED(hr)) {
573 error_report("WHPX: Failed to set virtual processor registers,"
574 " hr=%08lx", hr);
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;
583 return hr;
586 static HRESULT CALLBACK whpx_emu_translate_callback(
587 void *ctx,
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)
593 HRESULT hr;
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);
600 if (FAILED(hr)) {
601 error_report("WHPX: Failed to translate GVA, hr=%08lx", hr);
602 } else {
603 *TranslationResult = res.ResultCode;
606 return hr;
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)
620 HRESULT hr;
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,
626 &emu_status);
627 if (FAILED(hr)) {
628 error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr);
629 return -1;
632 if (!emu_status.EmulationSuccessful) {
633 error_report("WHPX: Failed to emulate MMIO access");
634 return -1;
637 return 0;
640 static int whpx_handle_portio(CPUState *cpu,
641 WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx)
643 HRESULT hr;
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,
649 &emu_status);
650 if (FAILED(hr)) {
651 error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr);
652 return -1;
655 if (!emu_status.EmulationSuccessful) {
656 error_report("WHPX: Failed to emulate PortMMIO access");
657 return -1;
660 return 0;
663 static int whpx_handle_halt(CPUState *cpu)
665 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
666 int ret = 0;
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;
673 cpu->halted = true;
674 ret = 1;
676 qemu_mutex_unlock_iothread();
678 return ret;
681 static void whpx_vcpu_pre_run(CPUState *cpu)
683 HRESULT hr;
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);
688 int irq;
689 uint8_t tpr;
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();
697 /* Inject NMI */
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);
733 if (irq >= 0) {
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;
745 reg_count += 1;
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) {
751 vcpu->tpr = tpr;
752 reg_values[reg_count].Reg64 = tpr;
753 cpu->exit_request = 1;
754 reg_names[reg_count] = WHvX64RegisterCr8;
755 reg_count += 1;
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
762 = 1;
763 vcpu->window_registered = 1;
764 reg_names[reg_count] = WHvX64RegisterDeliverabilityNotifications;
765 reg_count += 1;
768 qemu_mutex_unlock_iothread();
770 if (reg_count) {
771 hr = WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index,
772 reg_names, reg_count, reg_values);
773 if (FAILED(hr)) {
774 error_report("WHPX: Failed to set interrupt state registers,"
775 " hr=%08lx", hr);
779 return;
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) {
792 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;
804 return;
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)) {
829 cpu->halted = false;
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);
848 return;
851 static int whpx_vcpu_run(CPUState *cpu)
853 HRESULT hr;
854 struct whpx_state *whpx = &whpx_global;
855 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
856 int ret;
858 whpx_vcpu_process_async_events(cpu);
859 if (cpu->halted) {
860 cpu->exception_index = EXCP_HLT;
861 atomic_set(&cpu->exit_request, false);
862 return 0;
865 qemu_mutex_unlock_iothread();
866 cpu_exec_start(cpu);
868 do {
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)) {
877 whpx_vcpu_kick(cpu);
880 hr = WHvRunVirtualProcessor(whpx->partition, cpu->cpu_index,
881 &vcpu->exit_ctx, sizeof(vcpu->exit_ctx));
883 if (FAILED(hr)) {
884 error_report("WHPX: Failed to exec a virtual processor,"
885 " hr=%08lx", hr);
886 ret = -1;
887 break;
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);
895 break;
897 case WHvRunVpExitReasonX64IoPortAccess:
898 ret = whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess);
899 break;
901 case WHvRunVpExitReasonX64InterruptWindow:
902 vcpu->window_registered = 0;
903 break;
905 case WHvRunVpExitReasonX64Halt:
906 ret = whpx_handle_halt(cpu);
907 break;
909 case WHvRunVpExitReasonCanceled:
910 cpu->exception_index = EXCP_INTERRUPT;
911 ret = 1;
912 break;
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) {
923 case 1:
924 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
925 /* Advertise that we are running on a hypervisor */
926 rcx =
927 vcpu->exit_ctx.CpuidAccess.DefaultResultRcx |
928 CPUID_EXT_HYPERVISOR;
930 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
931 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
932 break;
933 default:
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,
953 cpu->cpu_index,
954 reg_names,
955 reg_count,
956 reg_values);
958 if (FAILED(hr)) {
959 error_report("WHPX: Failed to set CpuidAccess state registers,"
960 " hr=%08lx", hr);
962 ret = 0;
963 break;
965 case WHvRunVpExitReasonNone:
966 case WHvRunVpExitReasonUnrecoverableException:
967 case WHvRunVpExitReasonInvalidVpRegisterValue:
968 case WHvRunVpExitReasonUnsupportedFeature:
969 case WHvRunVpExitReasonX64MsrAccess:
970 case WHvRunVpExitReasonException:
971 default:
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();
978 break;
981 } while (!ret);
983 cpu_exec_end(cpu);
984 qemu_mutex_lock_iothread();
985 current_cpu = cpu;
987 atomic_set(&cpu->exit_request, false);
989 return ret < 0;
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,
999 run_on_cpu_data arg)
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;
1019 * CPU support.
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);
1045 * Vcpu support.
1048 static Error *whpx_migration_blocker;
1050 int whpx_init_vcpu(CPUState *cpu)
1052 HRESULT hr;
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);
1066 if (local_error) {
1067 error_report_err(local_error);
1068 error_free(whpx_migration_blocker);
1069 migrate_del_blocker(whpx_migration_blocker);
1070 return -EINVAL;
1074 vcpu = g_malloc0(sizeof(struct whpx_vcpu));
1076 if (!vcpu) {
1077 error_report("WHPX: Failed to allocte VCPU context.");
1078 return -ENOMEM;
1081 hr = WHvEmulatorCreateEmulator(&whpx_emu_callbacks, &vcpu->emulator);
1082 if (FAILED(hr)) {
1083 error_report("WHPX: Failed to setup instruction completion support,"
1084 " hr=%08lx", hr);
1085 g_free(vcpu);
1086 return -EINVAL;
1089 hr = WHvCreateVirtualProcessor(whpx->partition, cpu->cpu_index, 0);
1090 if (FAILED(hr)) {
1091 error_report("WHPX: Failed to create a virtual processor,"
1092 " hr=%08lx", hr);
1093 WHvEmulatorDestroyEmulator(vcpu->emulator);
1094 g_free(vcpu);
1095 return -EINVAL;
1098 vcpu->interruptable = true;
1100 cpu->vcpu_dirty = true;
1101 cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu;
1103 return 0;
1106 int whpx_vcpu_exec(CPUState *cpu)
1108 int ret;
1109 int fatal;
1111 for (;;) {
1112 if (cpu->exception_index >= EXCP_INTERRUPT) {
1113 ret = cpu->exception_index;
1114 cpu->exception_index = -1;
1115 break;
1118 fatal = whpx_vcpu_run(cpu);
1120 if (fatal) {
1121 error_report("WHPX: Failed to exec a virtual processor");
1122 abort();
1126 return ret;
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);
1137 return;
1140 void whpx_vcpu_kick(CPUState *cpu)
1142 struct whpx_state *whpx = &whpx_global;
1143 WHvCancelRunVirtualProcessor(whpx->partition, cpu->cpu_index, 0);
1147 * Memory support.
1150 static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
1151 void *host_va, int add, int rom,
1152 const char *name)
1154 struct whpx_state *whpx = &whpx_global;
1155 HRESULT hr;
1158 if (add) {
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);
1162 } else {
1163 printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
1164 (void*)start_pa, (void*)size, host_va, name);
1168 if (add) {
1169 hr = WHvMapGpaRange(whpx->partition,
1170 host_va,
1171 start_pa,
1172 size,
1173 (WHvMapGpaRangeFlagRead |
1174 WHvMapGpaRangeFlagExecute |
1175 (rom ? 0 : WHvMapGpaRangeFlagWrite)));
1176 } else {
1177 hr = WHvUnmapGpaRange(whpx->partition,
1178 start_pa,
1179 size);
1182 if (FAILED(hr)) {
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);
1195 unsigned int delta;
1196 uint64_t host_va;
1198 if (!memory_region_is_ram(mr)) {
1199 return;
1202 delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
1203 delta &= ~qemu_real_host_page_mask;
1204 if (delta > size) {
1205 return;
1207 start_pa += delta;
1208 size -= delta;
1209 size &= qemu_real_host_page_mask;
1210 if (!size || (start_pa & ~qemu_real_host_page_mask)) {
1211 return;
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)) {
1249 return;
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,
1261 .priority = 10,
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)) {
1274 qemu_cpu_kick(cpu);
1279 * Partition support
1282 static int whpx_accel_init(MachineState *ms)
1284 struct whpx_state *whpx;
1285 int ret;
1286 HRESULT hr;
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);
1300 ret = -ENOSPC;
1301 goto error;
1304 hr = WHvCreatePartition(&whpx->partition);
1305 if (FAILED(hr)) {
1306 error_report("WHPX: Failed to create partition, hr=%08lx", hr);
1307 ret = -EINVAL;
1308 goto error;
1311 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1312 prop.ProcessorCount = smp_cpus;
1313 hr = WHvSetPartitionProperty(whpx->partition,
1314 WHvPartitionPropertyCodeProcessorCount,
1315 &prop,
1316 sizeof(WHV_PARTITION_PROPERTY));
1318 if (FAILED(hr)) {
1319 error_report("WHPX: Failed to set partition core count to %d,"
1320 " hr=%08lx", smp_cores, hr);
1321 ret = -EINVAL;
1322 goto error;
1325 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1326 prop.ExtendedVmExits.X64CpuidExit = 1;
1327 hr = WHvSetPartitionProperty(whpx->partition,
1328 WHvPartitionPropertyCodeExtendedVmExits,
1329 &prop,
1330 sizeof(WHV_PARTITION_PROPERTY));
1332 if (FAILED(hr)) {
1333 error_report("WHPX: Failed to enable partition extended X64CpuidExit"
1334 " hr=%08lx", hr);
1335 ret = -EINVAL;
1336 goto error;
1339 UINT32 cpuidExitList[] = {1};
1340 hr = WHvSetPartitionProperty(whpx->partition,
1341 WHvPartitionPropertyCodeCpuidExitList,
1342 cpuidExitList,
1343 RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
1345 if (FAILED(hr)) {
1346 error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1347 hr);
1348 ret = -EINVAL;
1349 goto error;
1352 hr = WHvSetupPartition(whpx->partition);
1353 if (FAILED(hr)) {
1354 error_report("WHPX: Failed to setup partition, hr=%08lx", hr);
1355 ret = -EINVAL;
1356 goto error;
1359 whpx_memory_init();
1361 cpu_interrupt_handler = whpx_handle_interrupt;
1363 printf("Windows Hypervisor Platform accelerator is operational\n");
1364 return 0;
1366 error:
1368 if (NULL != whpx->partition) {
1369 WHvDeletePartition(whpx->partition);
1370 whpx->partition = NULL;
1374 return ret;
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);
1385 ac->name = "WHPX";
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);