qemu-tech.texi: Remove "QEMU compared to other emulators" section
[qemu/ar7.git] / target / i386 / whpx-all.c
blob31d47320e45c5a55457fe8db395531c9b3dc3450
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/ioport.h"
15 #include "qemu-common.h"
16 #include "sysemu/accel.h"
17 #include "sysemu/whpx.h"
18 #include "sysemu/sysemu.h"
19 #include "sysemu/cpus.h"
20 #include "qemu/main-loop.h"
21 #include "hw/boards.h"
22 #include "qemu/error-report.h"
23 #include "qemu/queue.h"
24 #include "qapi/error.h"
25 #include "migration/blocker.h"
26 #include "whp-dispatch.h"
28 #include <WinHvPlatform.h>
29 #include <WinHvEmulation.h>
31 struct whpx_state {
32 uint64_t mem_quota;
33 WHV_PARTITION_HANDLE partition;
36 static const WHV_REGISTER_NAME whpx_register_names[] = {
38 /* X64 General purpose registers */
39 WHvX64RegisterRax,
40 WHvX64RegisterRcx,
41 WHvX64RegisterRdx,
42 WHvX64RegisterRbx,
43 WHvX64RegisterRsp,
44 WHvX64RegisterRbp,
45 WHvX64RegisterRsi,
46 WHvX64RegisterRdi,
47 WHvX64RegisterR8,
48 WHvX64RegisterR9,
49 WHvX64RegisterR10,
50 WHvX64RegisterR11,
51 WHvX64RegisterR12,
52 WHvX64RegisterR13,
53 WHvX64RegisterR14,
54 WHvX64RegisterR15,
55 WHvX64RegisterRip,
56 WHvX64RegisterRflags,
58 /* X64 Segment registers */
59 WHvX64RegisterEs,
60 WHvX64RegisterCs,
61 WHvX64RegisterSs,
62 WHvX64RegisterDs,
63 WHvX64RegisterFs,
64 WHvX64RegisterGs,
65 WHvX64RegisterLdtr,
66 WHvX64RegisterTr,
68 /* X64 Table registers */
69 WHvX64RegisterIdtr,
70 WHvX64RegisterGdtr,
72 /* X64 Control Registers */
73 WHvX64RegisterCr0,
74 WHvX64RegisterCr2,
75 WHvX64RegisterCr3,
76 WHvX64RegisterCr4,
77 WHvX64RegisterCr8,
79 /* X64 Debug Registers */
81 * WHvX64RegisterDr0,
82 * WHvX64RegisterDr1,
83 * WHvX64RegisterDr2,
84 * WHvX64RegisterDr3,
85 * WHvX64RegisterDr6,
86 * WHvX64RegisterDr7,
89 /* X64 Floating Point and Vector Registers */
90 WHvX64RegisterXmm0,
91 WHvX64RegisterXmm1,
92 WHvX64RegisterXmm2,
93 WHvX64RegisterXmm3,
94 WHvX64RegisterXmm4,
95 WHvX64RegisterXmm5,
96 WHvX64RegisterXmm6,
97 WHvX64RegisterXmm7,
98 WHvX64RegisterXmm8,
99 WHvX64RegisterXmm9,
100 WHvX64RegisterXmm10,
101 WHvX64RegisterXmm11,
102 WHvX64RegisterXmm12,
103 WHvX64RegisterXmm13,
104 WHvX64RegisterXmm14,
105 WHvX64RegisterXmm15,
106 WHvX64RegisterFpMmx0,
107 WHvX64RegisterFpMmx1,
108 WHvX64RegisterFpMmx2,
109 WHvX64RegisterFpMmx3,
110 WHvX64RegisterFpMmx4,
111 WHvX64RegisterFpMmx5,
112 WHvX64RegisterFpMmx6,
113 WHvX64RegisterFpMmx7,
114 WHvX64RegisterFpControlStatus,
115 WHvX64RegisterXmmControlStatus,
117 /* X64 MSRs */
118 WHvX64RegisterTsc,
119 WHvX64RegisterEfer,
120 #ifdef TARGET_X86_64
121 WHvX64RegisterKernelGsBase,
122 #endif
123 WHvX64RegisterApicBase,
124 /* WHvX64RegisterPat, */
125 WHvX64RegisterSysenterCs,
126 WHvX64RegisterSysenterEip,
127 WHvX64RegisterSysenterEsp,
128 WHvX64RegisterStar,
129 #ifdef TARGET_X86_64
130 WHvX64RegisterLstar,
131 WHvX64RegisterCstar,
132 WHvX64RegisterSfmask,
133 #endif
135 /* Interrupt / Event Registers */
137 * WHvRegisterPendingInterruption,
138 * WHvRegisterInterruptState,
139 * WHvRegisterPendingEvent0,
140 * WHvRegisterPendingEvent1
141 * WHvX64RegisterDeliverabilityNotifications,
145 struct whpx_register_set {
146 WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)];
149 struct whpx_vcpu {
150 WHV_EMULATOR_HANDLE emulator;
151 bool window_registered;
152 bool interruptable;
153 uint64_t tpr;
154 uint64_t apic_base;
155 bool interruption_pending;
157 /* Must be the last field as it may have a tail */
158 WHV_RUN_VP_EXIT_CONTEXT exit_ctx;
161 static bool whpx_allowed;
162 static bool whp_dispatch_initialized;
163 static HMODULE hWinHvPlatform, hWinHvEmulation;
165 struct whpx_state whpx_global;
166 struct WHPDispatch whp_dispatch;
170 * VP support
173 static struct whpx_vcpu *get_whpx_vcpu(CPUState *cpu)
175 return (struct whpx_vcpu *)cpu->hax_vcpu;
178 static WHV_X64_SEGMENT_REGISTER whpx_seg_q2h(const SegmentCache *qs, int v86,
179 int r86)
181 WHV_X64_SEGMENT_REGISTER hs;
182 unsigned flags = qs->flags;
184 hs.Base = qs->base;
185 hs.Limit = qs->limit;
186 hs.Selector = qs->selector;
188 if (v86) {
189 hs.Attributes = 0;
190 hs.SegmentType = 3;
191 hs.Present = 1;
192 hs.DescriptorPrivilegeLevel = 3;
193 hs.NonSystemSegment = 1;
195 } else {
196 hs.Attributes = (flags >> DESC_TYPE_SHIFT);
198 if (r86) {
199 /* hs.Base &= 0xfffff; */
203 return hs;
206 static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs)
208 SegmentCache qs;
210 qs.base = hs->Base;
211 qs.limit = hs->Limit;
212 qs.selector = hs->Selector;
214 qs.flags = ((uint32_t)hs->Attributes) << DESC_TYPE_SHIFT;
216 return qs;
219 static void whpx_set_registers(CPUState *cpu)
221 struct whpx_state *whpx = &whpx_global;
222 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
223 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
224 X86CPU *x86_cpu = X86_CPU(cpu);
225 struct whpx_register_set vcxt;
226 HRESULT hr;
227 int idx;
228 int idx_next;
229 int i;
230 int v86, r86;
232 assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
234 memset(&vcxt, 0, sizeof(struct whpx_register_set));
236 v86 = (env->eflags & VM_MASK);
237 r86 = !(env->cr[0] & CR0_PE_MASK);
239 vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
240 vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
242 idx = 0;
244 /* Indexes for first 16 registers match between HV and QEMU definitions */
245 idx_next = 16;
246 for (idx = 0; idx < CPU_NB_REGS; idx += 1) {
247 vcxt.values[idx].Reg64 = (uint64_t)env->regs[idx];
249 idx = idx_next;
251 /* Same goes for RIP and RFLAGS */
252 assert(whpx_register_names[idx] == WHvX64RegisterRip);
253 vcxt.values[idx++].Reg64 = env->eip;
255 assert(whpx_register_names[idx] == WHvX64RegisterRflags);
256 vcxt.values[idx++].Reg64 = env->eflags;
258 /* Translate 6+4 segment registers. HV and QEMU order matches */
259 assert(idx == WHvX64RegisterEs);
260 for (i = 0; i < 6; i += 1, idx += 1) {
261 vcxt.values[idx].Segment = whpx_seg_q2h(&env->segs[i], v86, r86);
264 assert(idx == WHvX64RegisterLdtr);
265 vcxt.values[idx++].Segment = whpx_seg_q2h(&env->ldt, 0, 0);
267 assert(idx == WHvX64RegisterTr);
268 vcxt.values[idx++].Segment = whpx_seg_q2h(&env->tr, 0, 0);
270 assert(idx == WHvX64RegisterIdtr);
271 vcxt.values[idx].Table.Base = env->idt.base;
272 vcxt.values[idx].Table.Limit = env->idt.limit;
273 idx += 1;
275 assert(idx == WHvX64RegisterGdtr);
276 vcxt.values[idx].Table.Base = env->gdt.base;
277 vcxt.values[idx].Table.Limit = env->gdt.limit;
278 idx += 1;
280 /* CR0, 2, 3, 4, 8 */
281 assert(whpx_register_names[idx] == WHvX64RegisterCr0);
282 vcxt.values[idx++].Reg64 = env->cr[0];
283 assert(whpx_register_names[idx] == WHvX64RegisterCr2);
284 vcxt.values[idx++].Reg64 = env->cr[2];
285 assert(whpx_register_names[idx] == WHvX64RegisterCr3);
286 vcxt.values[idx++].Reg64 = env->cr[3];
287 assert(whpx_register_names[idx] == WHvX64RegisterCr4);
288 vcxt.values[idx++].Reg64 = env->cr[4];
289 assert(whpx_register_names[idx] == WHvX64RegisterCr8);
290 vcxt.values[idx++].Reg64 = vcpu->tpr;
292 /* 8 Debug Registers - Skipped */
294 /* 16 XMM registers */
295 assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
296 idx_next = idx + 16;
297 for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
298 vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);
299 vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);
301 idx = idx_next;
303 /* 8 FP registers */
304 assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
305 for (i = 0; i < 8; i += 1, idx += 1) {
306 vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);
307 /* vcxt.values[idx].Fp.AsUINT128.High64 =
308 env->fpregs[i].mmx.MMX_Q(1);
312 /* FP control status register */
313 assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
314 vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;
315 vcxt.values[idx].FpControlStatus.FpStatus =
316 (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
317 vcxt.values[idx].FpControlStatus.FpTag = 0;
318 for (i = 0; i < 8; ++i) {
319 vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;
321 vcxt.values[idx].FpControlStatus.Reserved = 0;
322 vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;
323 vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;
324 idx += 1;
326 /* XMM control status register */
327 assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
328 vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;
329 vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;
330 vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;
331 idx += 1;
333 /* MSRs */
334 assert(whpx_register_names[idx] == WHvX64RegisterTsc);
335 vcxt.values[idx++].Reg64 = env->tsc;
336 assert(whpx_register_names[idx] == WHvX64RegisterEfer);
337 vcxt.values[idx++].Reg64 = env->efer;
338 #ifdef TARGET_X86_64
339 assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
340 vcxt.values[idx++].Reg64 = env->kernelgsbase;
341 #endif
343 assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
344 vcxt.values[idx++].Reg64 = vcpu->apic_base;
346 /* WHvX64RegisterPat - Skipped */
348 assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
349 vcxt.values[idx++].Reg64 = env->sysenter_cs;
350 assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
351 vcxt.values[idx++].Reg64 = env->sysenter_eip;
352 assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
353 vcxt.values[idx++].Reg64 = env->sysenter_esp;
354 assert(whpx_register_names[idx] == WHvX64RegisterStar);
355 vcxt.values[idx++].Reg64 = env->star;
356 #ifdef TARGET_X86_64
357 assert(whpx_register_names[idx] == WHvX64RegisterLstar);
358 vcxt.values[idx++].Reg64 = env->lstar;
359 assert(whpx_register_names[idx] == WHvX64RegisterCstar);
360 vcxt.values[idx++].Reg64 = env->cstar;
361 assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
362 vcxt.values[idx++].Reg64 = env->fmask;
363 #endif
365 /* Interrupt / Event Registers - Skipped */
367 assert(idx == RTL_NUMBER_OF(whpx_register_names));
369 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
370 whpx->partition, cpu->cpu_index,
371 whpx_register_names,
372 RTL_NUMBER_OF(whpx_register_names),
373 &vcxt.values[0]);
375 if (FAILED(hr)) {
376 error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
377 hr);
380 return;
383 static void whpx_get_registers(CPUState *cpu)
385 struct whpx_state *whpx = &whpx_global;
386 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
387 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
388 X86CPU *x86_cpu = X86_CPU(cpu);
389 struct whpx_register_set vcxt;
390 uint64_t tpr, apic_base;
391 HRESULT hr;
392 int idx;
393 int idx_next;
394 int i;
396 assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
398 hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
399 whpx->partition, cpu->cpu_index,
400 whpx_register_names,
401 RTL_NUMBER_OF(whpx_register_names),
402 &vcxt.values[0]);
403 if (FAILED(hr)) {
404 error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
405 hr);
408 idx = 0;
410 /* Indexes for first 16 registers match between HV and QEMU definitions */
411 idx_next = 16;
412 for (idx = 0; idx < CPU_NB_REGS; idx += 1) {
413 env->regs[idx] = vcxt.values[idx].Reg64;
415 idx = idx_next;
417 /* Same goes for RIP and RFLAGS */
418 assert(whpx_register_names[idx] == WHvX64RegisterRip);
419 env->eip = vcxt.values[idx++].Reg64;
420 assert(whpx_register_names[idx] == WHvX64RegisterRflags);
421 env->eflags = vcxt.values[idx++].Reg64;
423 /* Translate 6+4 segment registers. HV and QEMU order matches */
424 assert(idx == WHvX64RegisterEs);
425 for (i = 0; i < 6; i += 1, idx += 1) {
426 env->segs[i] = whpx_seg_h2q(&vcxt.values[idx].Segment);
429 assert(idx == WHvX64RegisterLdtr);
430 env->ldt = whpx_seg_h2q(&vcxt.values[idx++].Segment);
431 assert(idx == WHvX64RegisterTr);
432 env->tr = whpx_seg_h2q(&vcxt.values[idx++].Segment);
433 assert(idx == WHvX64RegisterIdtr);
434 env->idt.base = vcxt.values[idx].Table.Base;
435 env->idt.limit = vcxt.values[idx].Table.Limit;
436 idx += 1;
437 assert(idx == WHvX64RegisterGdtr);
438 env->gdt.base = vcxt.values[idx].Table.Base;
439 env->gdt.limit = vcxt.values[idx].Table.Limit;
440 idx += 1;
442 /* CR0, 2, 3, 4, 8 */
443 assert(whpx_register_names[idx] == WHvX64RegisterCr0);
444 env->cr[0] = vcxt.values[idx++].Reg64;
445 assert(whpx_register_names[idx] == WHvX64RegisterCr2);
446 env->cr[2] = vcxt.values[idx++].Reg64;
447 assert(whpx_register_names[idx] == WHvX64RegisterCr3);
448 env->cr[3] = vcxt.values[idx++].Reg64;
449 assert(whpx_register_names[idx] == WHvX64RegisterCr4);
450 env->cr[4] = vcxt.values[idx++].Reg64;
451 assert(whpx_register_names[idx] == WHvX64RegisterCr8);
452 tpr = vcxt.values[idx++].Reg64;
453 if (tpr != vcpu->tpr) {
454 vcpu->tpr = tpr;
455 cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
458 /* 8 Debug Registers - Skipped */
460 /* 16 XMM registers */
461 assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
462 idx_next = idx + 16;
463 for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
464 env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;
465 env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;
467 idx = idx_next;
469 /* 8 FP registers */
470 assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
471 for (i = 0; i < 8; i += 1, idx += 1) {
472 env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;
473 /* env->fpregs[i].mmx.MMX_Q(1) =
474 vcxt.values[idx].Fp.AsUINT128.High64;
478 /* FP control status register */
479 assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
480 env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;
481 env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;
482 env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;
483 for (i = 0; i < 8; ++i) {
484 env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);
486 env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;
487 env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;
488 idx += 1;
490 /* XMM control status register */
491 assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
492 env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
493 idx += 1;
495 /* MSRs */
496 assert(whpx_register_names[idx] == WHvX64RegisterTsc);
497 env->tsc = vcxt.values[idx++].Reg64;
498 assert(whpx_register_names[idx] == WHvX64RegisterEfer);
499 env->efer = vcxt.values[idx++].Reg64;
500 #ifdef TARGET_X86_64
501 assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
502 env->kernelgsbase = vcxt.values[idx++].Reg64;
503 #endif
505 assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
506 apic_base = vcxt.values[idx++].Reg64;
507 if (apic_base != vcpu->apic_base) {
508 vcpu->apic_base = apic_base;
509 cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base);
512 /* WHvX64RegisterPat - Skipped */
514 assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
515 env->sysenter_cs = vcxt.values[idx++].Reg64;;
516 assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
517 env->sysenter_eip = vcxt.values[idx++].Reg64;
518 assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
519 env->sysenter_esp = vcxt.values[idx++].Reg64;
520 assert(whpx_register_names[idx] == WHvX64RegisterStar);
521 env->star = vcxt.values[idx++].Reg64;
522 #ifdef TARGET_X86_64
523 assert(whpx_register_names[idx] == WHvX64RegisterLstar);
524 env->lstar = vcxt.values[idx++].Reg64;
525 assert(whpx_register_names[idx] == WHvX64RegisterCstar);
526 env->cstar = vcxt.values[idx++].Reg64;
527 assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
528 env->fmask = vcxt.values[idx++].Reg64;
529 #endif
531 /* Interrupt / Event Registers - Skipped */
533 assert(idx == RTL_NUMBER_OF(whpx_register_names));
535 return;
538 static HRESULT CALLBACK whpx_emu_ioport_callback(
539 void *ctx,
540 WHV_EMULATOR_IO_ACCESS_INFO *IoAccess)
542 MemTxAttrs attrs = { 0 };
543 address_space_rw(&address_space_io, IoAccess->Port, attrs,
544 (uint8_t *)&IoAccess->Data, IoAccess->AccessSize,
545 IoAccess->Direction);
546 return S_OK;
549 static HRESULT CALLBACK whpx_emu_mmio_callback(
550 void *ctx,
551 WHV_EMULATOR_MEMORY_ACCESS_INFO *ma)
553 cpu_physical_memory_rw(ma->GpaAddress, ma->Data, ma->AccessSize,
554 ma->Direction);
555 return S_OK;
558 static HRESULT CALLBACK whpx_emu_getreg_callback(
559 void *ctx,
560 const WHV_REGISTER_NAME *RegisterNames,
561 UINT32 RegisterCount,
562 WHV_REGISTER_VALUE *RegisterValues)
564 HRESULT hr;
565 struct whpx_state *whpx = &whpx_global;
566 CPUState *cpu = (CPUState *)ctx;
568 hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
569 whpx->partition, cpu->cpu_index,
570 RegisterNames, RegisterCount,
571 RegisterValues);
572 if (FAILED(hr)) {
573 error_report("WHPX: Failed to get virtual processor registers,"
574 " hr=%08lx", hr);
577 return hr;
580 static HRESULT CALLBACK whpx_emu_setreg_callback(
581 void *ctx,
582 const WHV_REGISTER_NAME *RegisterNames,
583 UINT32 RegisterCount,
584 const WHV_REGISTER_VALUE *RegisterValues)
586 HRESULT hr;
587 struct whpx_state *whpx = &whpx_global;
588 CPUState *cpu = (CPUState *)ctx;
590 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
591 whpx->partition, cpu->cpu_index,
592 RegisterNames, RegisterCount,
593 RegisterValues);
594 if (FAILED(hr)) {
595 error_report("WHPX: Failed to set virtual processor registers,"
596 " hr=%08lx", hr);
600 * The emulator just successfully wrote the register state. We clear the
601 * dirty state so we avoid the double write on resume of the VP.
603 cpu->vcpu_dirty = false;
605 return hr;
608 static HRESULT CALLBACK whpx_emu_translate_callback(
609 void *ctx,
610 WHV_GUEST_VIRTUAL_ADDRESS Gva,
611 WHV_TRANSLATE_GVA_FLAGS TranslateFlags,
612 WHV_TRANSLATE_GVA_RESULT_CODE *TranslationResult,
613 WHV_GUEST_PHYSICAL_ADDRESS *Gpa)
615 HRESULT hr;
616 struct whpx_state *whpx = &whpx_global;
617 CPUState *cpu = (CPUState *)ctx;
618 WHV_TRANSLATE_GVA_RESULT res;
620 hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index,
621 Gva, TranslateFlags, &res, Gpa);
622 if (FAILED(hr)) {
623 error_report("WHPX: Failed to translate GVA, hr=%08lx", hr);
624 } else {
625 *TranslationResult = res.ResultCode;
628 return hr;
631 static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = {
632 .Size = sizeof(WHV_EMULATOR_CALLBACKS),
633 .WHvEmulatorIoPortCallback = whpx_emu_ioport_callback,
634 .WHvEmulatorMemoryCallback = whpx_emu_mmio_callback,
635 .WHvEmulatorGetVirtualProcessorRegisters = whpx_emu_getreg_callback,
636 .WHvEmulatorSetVirtualProcessorRegisters = whpx_emu_setreg_callback,
637 .WHvEmulatorTranslateGvaPage = whpx_emu_translate_callback,
640 static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
642 HRESULT hr;
643 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
644 WHV_EMULATOR_STATUS emu_status;
646 hr = whp_dispatch.WHvEmulatorTryMmioEmulation(
647 vcpu->emulator, cpu,
648 &vcpu->exit_ctx.VpContext, ctx,
649 &emu_status);
650 if (FAILED(hr)) {
651 error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr);
652 return -1;
655 if (!emu_status.EmulationSuccessful) {
656 error_report("WHPX: Failed to emulate MMIO access with"
657 " EmulatorReturnStatus: %u", emu_status.AsUINT32);
658 return -1;
661 return 0;
664 static int whpx_handle_portio(CPUState *cpu,
665 WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx)
667 HRESULT hr;
668 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
669 WHV_EMULATOR_STATUS emu_status;
671 hr = whp_dispatch.WHvEmulatorTryIoEmulation(
672 vcpu->emulator, cpu,
673 &vcpu->exit_ctx.VpContext, ctx,
674 &emu_status);
675 if (FAILED(hr)) {
676 error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr);
677 return -1;
680 if (!emu_status.EmulationSuccessful) {
681 error_report("WHPX: Failed to emulate PortIO access with"
682 " EmulatorReturnStatus: %u", emu_status.AsUINT32);
683 return -1;
686 return 0;
689 static int whpx_handle_halt(CPUState *cpu)
691 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
692 int ret = 0;
694 qemu_mutex_lock_iothread();
695 if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
696 (env->eflags & IF_MASK)) &&
697 !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
698 cpu->exception_index = EXCP_HLT;
699 cpu->halted = true;
700 ret = 1;
702 qemu_mutex_unlock_iothread();
704 return ret;
707 static void whpx_vcpu_pre_run(CPUState *cpu)
709 HRESULT hr;
710 struct whpx_state *whpx = &whpx_global;
711 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
712 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
713 X86CPU *x86_cpu = X86_CPU(cpu);
714 int irq;
715 uint8_t tpr;
716 WHV_X64_PENDING_INTERRUPTION_REGISTER new_int;
717 UINT32 reg_count = 0;
718 WHV_REGISTER_VALUE reg_values[3];
719 WHV_REGISTER_NAME reg_names[3];
721 memset(&new_int, 0, sizeof(new_int));
722 memset(reg_values, 0, sizeof(reg_values));
724 qemu_mutex_lock_iothread();
726 /* Inject NMI */
727 if (!vcpu->interruption_pending &&
728 cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) {
729 if (cpu->interrupt_request & CPU_INTERRUPT_NMI) {
730 cpu->interrupt_request &= ~CPU_INTERRUPT_NMI;
731 vcpu->interruptable = false;
732 new_int.InterruptionType = WHvX64PendingNmi;
733 new_int.InterruptionPending = 1;
734 new_int.InterruptionVector = 2;
736 if (cpu->interrupt_request & CPU_INTERRUPT_SMI) {
737 cpu->interrupt_request &= ~CPU_INTERRUPT_SMI;
742 * Force the VCPU out of its inner loop to process any INIT requests or
743 * commit pending TPR access.
745 if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) {
746 if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
747 !(env->hflags & HF_SMM_MASK)) {
748 cpu->exit_request = 1;
750 if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
751 cpu->exit_request = 1;
755 /* Get pending hard interruption or replay one that was overwritten */
756 if (!vcpu->interruption_pending &&
757 vcpu->interruptable && (env->eflags & IF_MASK)) {
758 assert(!new_int.InterruptionPending);
759 if (cpu->interrupt_request & CPU_INTERRUPT_HARD) {
760 cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
761 irq = cpu_get_pic_interrupt(env);
762 if (irq >= 0) {
763 new_int.InterruptionType = WHvX64PendingInterrupt;
764 new_int.InterruptionPending = 1;
765 new_int.InterruptionVector = irq;
770 /* Setup interrupt state if new one was prepared */
771 if (new_int.InterruptionPending) {
772 reg_values[reg_count].PendingInterruption = new_int;
773 reg_names[reg_count] = WHvRegisterPendingInterruption;
774 reg_count += 1;
777 /* Sync the TPR to the CR8 if was modified during the intercept */
778 tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
779 if (tpr != vcpu->tpr) {
780 vcpu->tpr = tpr;
781 reg_values[reg_count].Reg64 = tpr;
782 cpu->exit_request = 1;
783 reg_names[reg_count] = WHvX64RegisterCr8;
784 reg_count += 1;
787 /* Update the state of the interrupt delivery notification */
788 if (!vcpu->window_registered &&
789 cpu->interrupt_request & CPU_INTERRUPT_HARD) {
790 reg_values[reg_count].DeliverabilityNotifications.InterruptNotification
791 = 1;
792 vcpu->window_registered = 1;
793 reg_names[reg_count] = WHvX64RegisterDeliverabilityNotifications;
794 reg_count += 1;
797 qemu_mutex_unlock_iothread();
799 if (reg_count) {
800 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
801 whpx->partition, cpu->cpu_index,
802 reg_names, reg_count, reg_values);
803 if (FAILED(hr)) {
804 error_report("WHPX: Failed to set interrupt state registers,"
805 " hr=%08lx", hr);
809 return;
812 static void whpx_vcpu_post_run(CPUState *cpu)
814 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
815 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
816 X86CPU *x86_cpu = X86_CPU(cpu);
818 env->eflags = vcpu->exit_ctx.VpContext.Rflags;
820 uint64_t tpr = vcpu->exit_ctx.VpContext.Cr8;
821 if (vcpu->tpr != tpr) {
822 vcpu->tpr = tpr;
823 qemu_mutex_lock_iothread();
824 cpu_set_apic_tpr(x86_cpu->apic_state, vcpu->tpr);
825 qemu_mutex_unlock_iothread();
828 vcpu->interruption_pending =
829 vcpu->exit_ctx.VpContext.ExecutionState.InterruptionPending;
831 vcpu->interruptable =
832 !vcpu->exit_ctx.VpContext.ExecutionState.InterruptShadow;
834 return;
837 static void whpx_vcpu_process_async_events(CPUState *cpu)
839 struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
840 X86CPU *x86_cpu = X86_CPU(cpu);
841 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
843 if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
844 !(env->hflags & HF_SMM_MASK)) {
846 do_cpu_init(x86_cpu);
847 cpu->vcpu_dirty = true;
848 vcpu->interruptable = true;
851 if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
852 cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
853 apic_poll_irq(x86_cpu->apic_state);
856 if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
857 (env->eflags & IF_MASK)) ||
858 (cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
859 cpu->halted = false;
862 if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) {
863 if (!cpu->vcpu_dirty) {
864 whpx_get_registers(cpu);
866 do_cpu_sipi(x86_cpu);
869 if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
870 cpu->interrupt_request &= ~CPU_INTERRUPT_TPR;
871 if (!cpu->vcpu_dirty) {
872 whpx_get_registers(cpu);
874 apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip,
875 env->tpr_access_type);
878 return;
881 static int whpx_vcpu_run(CPUState *cpu)
883 HRESULT hr;
884 struct whpx_state *whpx = &whpx_global;
885 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
886 int ret;
888 whpx_vcpu_process_async_events(cpu);
889 if (cpu->halted) {
890 cpu->exception_index = EXCP_HLT;
891 atomic_set(&cpu->exit_request, false);
892 return 0;
895 qemu_mutex_unlock_iothread();
896 cpu_exec_start(cpu);
898 do {
899 if (cpu->vcpu_dirty) {
900 whpx_set_registers(cpu);
901 cpu->vcpu_dirty = false;
904 whpx_vcpu_pre_run(cpu);
906 if (atomic_read(&cpu->exit_request)) {
907 whpx_vcpu_kick(cpu);
910 hr = whp_dispatch.WHvRunVirtualProcessor(
911 whpx->partition, cpu->cpu_index,
912 &vcpu->exit_ctx, sizeof(vcpu->exit_ctx));
914 if (FAILED(hr)) {
915 error_report("WHPX: Failed to exec a virtual processor,"
916 " hr=%08lx", hr);
917 ret = -1;
918 break;
921 whpx_vcpu_post_run(cpu);
923 switch (vcpu->exit_ctx.ExitReason) {
924 case WHvRunVpExitReasonMemoryAccess:
925 ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
926 break;
928 case WHvRunVpExitReasonX64IoPortAccess:
929 ret = whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess);
930 break;
932 case WHvRunVpExitReasonX64InterruptWindow:
933 vcpu->window_registered = 0;
934 ret = 0;
935 break;
937 case WHvRunVpExitReasonX64Halt:
938 ret = whpx_handle_halt(cpu);
939 break;
941 case WHvRunVpExitReasonCanceled:
942 cpu->exception_index = EXCP_INTERRUPT;
943 ret = 1;
944 break;
946 case WHvRunVpExitReasonX64MsrAccess: {
947 WHV_REGISTER_VALUE reg_values[3] = {0};
948 WHV_REGISTER_NAME reg_names[3];
949 UINT32 reg_count;
951 reg_names[0] = WHvX64RegisterRip;
952 reg_names[1] = WHvX64RegisterRax;
953 reg_names[2] = WHvX64RegisterRdx;
955 reg_values[0].Reg64 =
956 vcpu->exit_ctx.VpContext.Rip +
957 vcpu->exit_ctx.VpContext.InstructionLength;
960 * For all unsupported MSR access we:
961 * ignore writes
962 * return 0 on read.
964 reg_count = vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite ?
965 1 : 3;
967 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
968 whpx->partition,
969 cpu->cpu_index,
970 reg_names, reg_count,
971 reg_values);
973 if (FAILED(hr)) {
974 error_report("WHPX: Failed to set MsrAccess state "
975 " registers, hr=%08lx", hr);
977 ret = 0;
978 break;
980 case WHvRunVpExitReasonX64Cpuid: {
981 WHV_REGISTER_VALUE reg_values[5];
982 WHV_REGISTER_NAME reg_names[5];
983 UINT32 reg_count = 5;
984 UINT64 rip, rax, rcx, rdx, rbx;
986 memset(reg_values, 0, sizeof(reg_values));
988 rip = vcpu->exit_ctx.VpContext.Rip +
989 vcpu->exit_ctx.VpContext.InstructionLength;
990 switch (vcpu->exit_ctx.CpuidAccess.Rax) {
991 case 1:
992 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
993 /* Advertise that we are running on a hypervisor */
994 rcx =
995 vcpu->exit_ctx.CpuidAccess.DefaultResultRcx |
996 CPUID_EXT_HYPERVISOR;
998 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
999 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
1000 break;
1001 case 0x80000001:
1002 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
1003 /* Remove any support of OSVW */
1004 rcx =
1005 vcpu->exit_ctx.CpuidAccess.DefaultResultRcx &
1006 ~CPUID_EXT3_OSVW;
1008 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
1009 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
1010 break;
1011 default:
1012 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
1013 rcx = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx;
1014 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
1015 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
1018 reg_names[0] = WHvX64RegisterRip;
1019 reg_names[1] = WHvX64RegisterRax;
1020 reg_names[2] = WHvX64RegisterRcx;
1021 reg_names[3] = WHvX64RegisterRdx;
1022 reg_names[4] = WHvX64RegisterRbx;
1024 reg_values[0].Reg64 = rip;
1025 reg_values[1].Reg64 = rax;
1026 reg_values[2].Reg64 = rcx;
1027 reg_values[3].Reg64 = rdx;
1028 reg_values[4].Reg64 = rbx;
1030 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
1031 whpx->partition, cpu->cpu_index,
1032 reg_names,
1033 reg_count,
1034 reg_values);
1036 if (FAILED(hr)) {
1037 error_report("WHPX: Failed to set CpuidAccess state registers,"
1038 " hr=%08lx", hr);
1040 ret = 0;
1041 break;
1043 case WHvRunVpExitReasonNone:
1044 case WHvRunVpExitReasonUnrecoverableException:
1045 case WHvRunVpExitReasonInvalidVpRegisterValue:
1046 case WHvRunVpExitReasonUnsupportedFeature:
1047 case WHvRunVpExitReasonException:
1048 default:
1049 error_report("WHPX: Unexpected VP exit code %d",
1050 vcpu->exit_ctx.ExitReason);
1051 whpx_get_registers(cpu);
1052 qemu_mutex_lock_iothread();
1053 qemu_system_guest_panicked(cpu_get_crash_info(cpu));
1054 qemu_mutex_unlock_iothread();
1055 break;
1058 } while (!ret);
1060 cpu_exec_end(cpu);
1061 qemu_mutex_lock_iothread();
1062 current_cpu = cpu;
1064 atomic_set(&cpu->exit_request, false);
1066 return ret < 0;
1069 static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
1071 whpx_get_registers(cpu);
1072 cpu->vcpu_dirty = true;
1075 static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu,
1076 run_on_cpu_data arg)
1078 whpx_set_registers(cpu);
1079 cpu->vcpu_dirty = false;
1082 static void do_whpx_cpu_synchronize_post_init(CPUState *cpu,
1083 run_on_cpu_data arg)
1085 whpx_set_registers(cpu);
1086 cpu->vcpu_dirty = false;
1089 static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu,
1090 run_on_cpu_data arg)
1092 cpu->vcpu_dirty = true;
1096 * CPU support.
1099 void whpx_cpu_synchronize_state(CPUState *cpu)
1101 if (!cpu->vcpu_dirty) {
1102 run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL);
1106 void whpx_cpu_synchronize_post_reset(CPUState *cpu)
1108 run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
1111 void whpx_cpu_synchronize_post_init(CPUState *cpu)
1113 run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
1116 void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
1118 run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
1122 * Vcpu support.
1125 static Error *whpx_migration_blocker;
1127 int whpx_init_vcpu(CPUState *cpu)
1129 HRESULT hr;
1130 struct whpx_state *whpx = &whpx_global;
1131 struct whpx_vcpu *vcpu;
1132 Error *local_error = NULL;
1134 /* Add migration blockers for all unsupported features of the
1135 * Windows Hypervisor Platform
1137 if (whpx_migration_blocker == NULL) {
1138 error_setg(&whpx_migration_blocker,
1139 "State blocked due to non-migratable CPUID feature support,"
1140 "dirty memory tracking support, and XSAVE/XRSTOR support");
1142 (void)migrate_add_blocker(whpx_migration_blocker, &local_error);
1143 if (local_error) {
1144 error_report_err(local_error);
1145 migrate_del_blocker(whpx_migration_blocker);
1146 error_free(whpx_migration_blocker);
1147 return -EINVAL;
1151 vcpu = g_malloc0(sizeof(struct whpx_vcpu));
1153 if (!vcpu) {
1154 error_report("WHPX: Failed to allocte VCPU context.");
1155 return -ENOMEM;
1158 hr = whp_dispatch.WHvEmulatorCreateEmulator(
1159 &whpx_emu_callbacks,
1160 &vcpu->emulator);
1161 if (FAILED(hr)) {
1162 error_report("WHPX: Failed to setup instruction completion support,"
1163 " hr=%08lx", hr);
1164 g_free(vcpu);
1165 return -EINVAL;
1168 hr = whp_dispatch.WHvCreateVirtualProcessor(
1169 whpx->partition, cpu->cpu_index, 0);
1170 if (FAILED(hr)) {
1171 error_report("WHPX: Failed to create a virtual processor,"
1172 " hr=%08lx", hr);
1173 whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1174 g_free(vcpu);
1175 return -EINVAL;
1178 vcpu->interruptable = true;
1180 cpu->vcpu_dirty = true;
1181 cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu;
1183 return 0;
1186 int whpx_vcpu_exec(CPUState *cpu)
1188 int ret;
1189 int fatal;
1191 for (;;) {
1192 if (cpu->exception_index >= EXCP_INTERRUPT) {
1193 ret = cpu->exception_index;
1194 cpu->exception_index = -1;
1195 break;
1198 fatal = whpx_vcpu_run(cpu);
1200 if (fatal) {
1201 error_report("WHPX: Failed to exec a virtual processor");
1202 abort();
1206 return ret;
1209 void whpx_destroy_vcpu(CPUState *cpu)
1211 struct whpx_state *whpx = &whpx_global;
1212 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
1214 whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
1215 whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1216 g_free(cpu->hax_vcpu);
1217 return;
1220 void whpx_vcpu_kick(CPUState *cpu)
1222 struct whpx_state *whpx = &whpx_global;
1223 whp_dispatch.WHvCancelRunVirtualProcessor(
1224 whpx->partition, cpu->cpu_index, 0);
1228 * Memory support.
1231 static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
1232 void *host_va, int add, int rom,
1233 const char *name)
1235 struct whpx_state *whpx = &whpx_global;
1236 HRESULT hr;
1239 if (add) {
1240 printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
1241 (void*)start_pa, (void*)size, host_va,
1242 (rom ? "ROM" : "RAM"), name);
1243 } else {
1244 printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
1245 (void*)start_pa, (void*)size, host_va, name);
1249 if (add) {
1250 hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
1251 host_va,
1252 start_pa,
1253 size,
1254 (WHvMapGpaRangeFlagRead |
1255 WHvMapGpaRangeFlagExecute |
1256 (rom ? 0 : WHvMapGpaRangeFlagWrite)));
1257 } else {
1258 hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
1259 start_pa,
1260 size);
1263 if (FAILED(hr)) {
1264 error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
1265 " Host:%p, hr=%08lx",
1266 (add ? "MAP" : "UNMAP"), name,
1267 (void *)(uintptr_t)start_pa, (void *)size, host_va, hr);
1271 static void whpx_process_section(MemoryRegionSection *section, int add)
1273 MemoryRegion *mr = section->mr;
1274 hwaddr start_pa = section->offset_within_address_space;
1275 ram_addr_t size = int128_get64(section->size);
1276 unsigned int delta;
1277 uint64_t host_va;
1279 if (!memory_region_is_ram(mr)) {
1280 return;
1283 delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
1284 delta &= ~qemu_real_host_page_mask;
1285 if (delta > size) {
1286 return;
1288 start_pa += delta;
1289 size -= delta;
1290 size &= qemu_real_host_page_mask;
1291 if (!size || (start_pa & ~qemu_real_host_page_mask)) {
1292 return;
1295 host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
1296 + section->offset_within_region + delta;
1298 whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add,
1299 memory_region_is_rom(mr), mr->name);
1302 static void whpx_region_add(MemoryListener *listener,
1303 MemoryRegionSection *section)
1305 memory_region_ref(section->mr);
1306 whpx_process_section(section, 1);
1309 static void whpx_region_del(MemoryListener *listener,
1310 MemoryRegionSection *section)
1312 whpx_process_section(section, 0);
1313 memory_region_unref(section->mr);
1316 static void whpx_transaction_begin(MemoryListener *listener)
1320 static void whpx_transaction_commit(MemoryListener *listener)
1324 static void whpx_log_sync(MemoryListener *listener,
1325 MemoryRegionSection *section)
1327 MemoryRegion *mr = section->mr;
1329 if (!memory_region_is_ram(mr)) {
1330 return;
1333 memory_region_set_dirty(mr, 0, int128_get64(section->size));
1336 static MemoryListener whpx_memory_listener = {
1337 .begin = whpx_transaction_begin,
1338 .commit = whpx_transaction_commit,
1339 .region_add = whpx_region_add,
1340 .region_del = whpx_region_del,
1341 .log_sync = whpx_log_sync,
1342 .priority = 10,
1345 static void whpx_memory_init(void)
1347 memory_listener_register(&whpx_memory_listener, &address_space_memory);
1350 static void whpx_handle_interrupt(CPUState *cpu, int mask)
1352 cpu->interrupt_request |= mask;
1354 if (!qemu_cpu_is_self(cpu)) {
1355 qemu_cpu_kick(cpu);
1360 * Partition support
1363 static int whpx_accel_init(MachineState *ms)
1365 struct whpx_state *whpx;
1366 int ret;
1367 HRESULT hr;
1368 WHV_CAPABILITY whpx_cap;
1369 UINT32 whpx_cap_size;
1370 WHV_PARTITION_PROPERTY prop;
1372 whpx = &whpx_global;
1374 if (!init_whp_dispatch()) {
1375 ret = -ENOSYS;
1376 goto error;
1379 memset(whpx, 0, sizeof(struct whpx_state));
1380 whpx->mem_quota = ms->ram_size;
1382 hr = whp_dispatch.WHvGetCapability(
1383 WHvCapabilityCodeHypervisorPresent, &whpx_cap,
1384 sizeof(whpx_cap), &whpx_cap_size);
1385 if (FAILED(hr) || !whpx_cap.HypervisorPresent) {
1386 error_report("WHPX: No accelerator found, hr=%08lx", hr);
1387 ret = -ENOSPC;
1388 goto error;
1391 hr = whp_dispatch.WHvCreatePartition(&whpx->partition);
1392 if (FAILED(hr)) {
1393 error_report("WHPX: Failed to create partition, hr=%08lx", hr);
1394 ret = -EINVAL;
1395 goto error;
1398 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1399 prop.ProcessorCount = smp_cpus;
1400 hr = whp_dispatch.WHvSetPartitionProperty(
1401 whpx->partition,
1402 WHvPartitionPropertyCodeProcessorCount,
1403 &prop,
1404 sizeof(WHV_PARTITION_PROPERTY));
1406 if (FAILED(hr)) {
1407 error_report("WHPX: Failed to set partition core count to %d,"
1408 " hr=%08lx", smp_cores, hr);
1409 ret = -EINVAL;
1410 goto error;
1413 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1414 prop.ExtendedVmExits.X64MsrExit = 1;
1415 prop.ExtendedVmExits.X64CpuidExit = 1;
1416 hr = whp_dispatch.WHvSetPartitionProperty(
1417 whpx->partition,
1418 WHvPartitionPropertyCodeExtendedVmExits,
1419 &prop,
1420 sizeof(WHV_PARTITION_PROPERTY));
1422 if (FAILED(hr)) {
1423 error_report("WHPX: Failed to enable partition extended X64MsrExit and"
1424 " X64CpuidExit hr=%08lx", hr);
1425 ret = -EINVAL;
1426 goto error;
1429 UINT32 cpuidExitList[] = {1, 0x80000001};
1430 hr = whp_dispatch.WHvSetPartitionProperty(
1431 whpx->partition,
1432 WHvPartitionPropertyCodeCpuidExitList,
1433 cpuidExitList,
1434 RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
1436 if (FAILED(hr)) {
1437 error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1438 hr);
1439 ret = -EINVAL;
1440 goto error;
1443 hr = whp_dispatch.WHvSetupPartition(whpx->partition);
1444 if (FAILED(hr)) {
1445 error_report("WHPX: Failed to setup partition, hr=%08lx", hr);
1446 ret = -EINVAL;
1447 goto error;
1450 whpx_memory_init();
1452 cpu_interrupt_handler = whpx_handle_interrupt;
1454 printf("Windows Hypervisor Platform accelerator is operational\n");
1455 return 0;
1457 error:
1459 if (NULL != whpx->partition) {
1460 whp_dispatch.WHvDeletePartition(whpx->partition);
1461 whpx->partition = NULL;
1465 return ret;
1468 int whpx_enabled(void)
1470 return whpx_allowed;
1473 static void whpx_accel_class_init(ObjectClass *oc, void *data)
1475 AccelClass *ac = ACCEL_CLASS(oc);
1476 ac->name = "WHPX";
1477 ac->init_machine = whpx_accel_init;
1478 ac->allowed = &whpx_allowed;
1481 static const TypeInfo whpx_accel_type = {
1482 .name = ACCEL_CLASS_NAME("whpx"),
1483 .parent = TYPE_ACCEL,
1484 .class_init = whpx_accel_class_init,
1487 static void whpx_type_init(void)
1489 type_register_static(&whpx_accel_type);
1492 bool init_whp_dispatch(void)
1494 const char *lib_name;
1495 HMODULE hLib;
1497 if (whp_dispatch_initialized) {
1498 return true;
1501 #define WHP_LOAD_FIELD(return_type, function_name, signature) \
1502 whp_dispatch.function_name = \
1503 (function_name ## _t)GetProcAddress(hLib, #function_name); \
1504 if (!whp_dispatch.function_name) { \
1505 error_report("Could not load function %s from library %s.", \
1506 #function_name, lib_name); \
1507 goto error; \
1510 lib_name = "WinHvPlatform.dll";
1511 hWinHvPlatform = LoadLibrary(lib_name);
1512 if (!hWinHvPlatform) {
1513 error_report("Could not load library %s.", lib_name);
1514 goto error;
1516 hLib = hWinHvPlatform;
1517 LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
1519 lib_name = "WinHvEmulation.dll";
1520 hWinHvEmulation = LoadLibrary(lib_name);
1521 if (!hWinHvEmulation) {
1522 error_report("Could not load library %s.", lib_name);
1523 goto error;
1525 hLib = hWinHvEmulation;
1526 LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD)
1528 whp_dispatch_initialized = true;
1529 return true;
1531 error:
1533 if (hWinHvPlatform) {
1534 FreeLibrary(hWinHvPlatform);
1536 if (hWinHvEmulation) {
1537 FreeLibrary(hWinHvEmulation);
1539 return false;
1542 type_init(whpx_type_init);