xref: /openbmc/qemu/target/i386/whpx/whpx-all.c (revision f101c9fe)
1 /*
2  * QEMU Windows Hypervisor Platform accelerator (WHPX)
3  *
4  * Copyright Microsoft Corp. 2017
5  *
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.
8  *
9  */
10 
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 "qemu/accel.h"
17 #include "sysemu/whpx.h"
18 #include "sysemu/cpus.h"
19 #include "sysemu/runstate.h"
20 #include "qemu/main-loop.h"
21 #include "hw/boards.h"
22 #include "hw/i386/ioapic.h"
23 #include "hw/i386/apic_internal.h"
24 #include "qemu/error-report.h"
25 #include "qapi/error.h"
26 #include "qapi/qapi-types-common.h"
27 #include "qapi/qapi-visit-common.h"
28 #include "migration/blocker.h"
29 #include <winerror.h>
30 
31 #include "whpx-internal.h"
32 #include "whpx-accel-ops.h"
33 
34 #include <WinHvPlatform.h>
35 #include <WinHvEmulation.h>
36 
37 #define HYPERV_APIC_BUS_FREQUENCY      (200000000ULL)
38 
39 static const WHV_REGISTER_NAME whpx_register_names[] = {
40 
41     /* X64 General purpose registers */
42     WHvX64RegisterRax,
43     WHvX64RegisterRcx,
44     WHvX64RegisterRdx,
45     WHvX64RegisterRbx,
46     WHvX64RegisterRsp,
47     WHvX64RegisterRbp,
48     WHvX64RegisterRsi,
49     WHvX64RegisterRdi,
50     WHvX64RegisterR8,
51     WHvX64RegisterR9,
52     WHvX64RegisterR10,
53     WHvX64RegisterR11,
54     WHvX64RegisterR12,
55     WHvX64RegisterR13,
56     WHvX64RegisterR14,
57     WHvX64RegisterR15,
58     WHvX64RegisterRip,
59     WHvX64RegisterRflags,
60 
61     /* X64 Segment registers */
62     WHvX64RegisterEs,
63     WHvX64RegisterCs,
64     WHvX64RegisterSs,
65     WHvX64RegisterDs,
66     WHvX64RegisterFs,
67     WHvX64RegisterGs,
68     WHvX64RegisterLdtr,
69     WHvX64RegisterTr,
70 
71     /* X64 Table registers */
72     WHvX64RegisterIdtr,
73     WHvX64RegisterGdtr,
74 
75     /* X64 Control Registers */
76     WHvX64RegisterCr0,
77     WHvX64RegisterCr2,
78     WHvX64RegisterCr3,
79     WHvX64RegisterCr4,
80     WHvX64RegisterCr8,
81 
82     /* X64 Debug Registers */
83     /*
84      * WHvX64RegisterDr0,
85      * WHvX64RegisterDr1,
86      * WHvX64RegisterDr2,
87      * WHvX64RegisterDr3,
88      * WHvX64RegisterDr6,
89      * WHvX64RegisterDr7,
90      */
91 
92     /* X64 Floating Point and Vector Registers */
93     WHvX64RegisterXmm0,
94     WHvX64RegisterXmm1,
95     WHvX64RegisterXmm2,
96     WHvX64RegisterXmm3,
97     WHvX64RegisterXmm4,
98     WHvX64RegisterXmm5,
99     WHvX64RegisterXmm6,
100     WHvX64RegisterXmm7,
101     WHvX64RegisterXmm8,
102     WHvX64RegisterXmm9,
103     WHvX64RegisterXmm10,
104     WHvX64RegisterXmm11,
105     WHvX64RegisterXmm12,
106     WHvX64RegisterXmm13,
107     WHvX64RegisterXmm14,
108     WHvX64RegisterXmm15,
109     WHvX64RegisterFpMmx0,
110     WHvX64RegisterFpMmx1,
111     WHvX64RegisterFpMmx2,
112     WHvX64RegisterFpMmx3,
113     WHvX64RegisterFpMmx4,
114     WHvX64RegisterFpMmx5,
115     WHvX64RegisterFpMmx6,
116     WHvX64RegisterFpMmx7,
117     WHvX64RegisterFpControlStatus,
118     WHvX64RegisterXmmControlStatus,
119 
120     /* X64 MSRs */
121     WHvX64RegisterEfer,
122 #ifdef TARGET_X86_64
123     WHvX64RegisterKernelGsBase,
124 #endif
125     WHvX64RegisterApicBase,
126     /* WHvX64RegisterPat, */
127     WHvX64RegisterSysenterCs,
128     WHvX64RegisterSysenterEip,
129     WHvX64RegisterSysenterEsp,
130     WHvX64RegisterStar,
131 #ifdef TARGET_X86_64
132     WHvX64RegisterLstar,
133     WHvX64RegisterCstar,
134     WHvX64RegisterSfmask,
135 #endif
136 
137     /* Interrupt / Event Registers */
138     /*
139      * WHvRegisterPendingInterruption,
140      * WHvRegisterInterruptState,
141      * WHvRegisterPendingEvent0,
142      * WHvRegisterPendingEvent1
143      * WHvX64RegisterDeliverabilityNotifications,
144      */
145 };
146 
147 struct whpx_register_set {
148     WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)];
149 };
150 
151 struct whpx_vcpu {
152     WHV_EMULATOR_HANDLE emulator;
153     bool window_registered;
154     bool interruptable;
155     bool ready_for_pic_interrupt;
156     uint64_t tpr;
157     uint64_t apic_base;
158     bool interruption_pending;
159 
160     /* Must be the last field as it may have a tail */
161     WHV_RUN_VP_EXIT_CONTEXT exit_ctx;
162 };
163 
164 static bool whpx_allowed;
165 static bool whp_dispatch_initialized;
166 static HMODULE hWinHvPlatform, hWinHvEmulation;
167 static uint32_t max_vcpu_index;
168 struct whpx_state whpx_global;
169 struct WHPDispatch whp_dispatch;
170 
171 
172 /*
173  * VP support
174  */
175 
176 static struct whpx_vcpu *get_whpx_vcpu(CPUState *cpu)
177 {
178     return (struct whpx_vcpu *)cpu->hax_vcpu;
179 }
180 
181 static WHV_X64_SEGMENT_REGISTER whpx_seg_q2h(const SegmentCache *qs, int v86,
182                                              int r86)
183 {
184     WHV_X64_SEGMENT_REGISTER hs;
185     unsigned flags = qs->flags;
186 
187     hs.Base = qs->base;
188     hs.Limit = qs->limit;
189     hs.Selector = qs->selector;
190 
191     if (v86) {
192         hs.Attributes = 0;
193         hs.SegmentType = 3;
194         hs.Present = 1;
195         hs.DescriptorPrivilegeLevel = 3;
196         hs.NonSystemSegment = 1;
197 
198     } else {
199         hs.Attributes = (flags >> DESC_TYPE_SHIFT);
200 
201         if (r86) {
202             /* hs.Base &= 0xfffff; */
203         }
204     }
205 
206     return hs;
207 }
208 
209 static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs)
210 {
211     SegmentCache qs;
212 
213     qs.base = hs->Base;
214     qs.limit = hs->Limit;
215     qs.selector = hs->Selector;
216 
217     qs.flags = ((uint32_t)hs->Attributes) << DESC_TYPE_SHIFT;
218 
219     return qs;
220 }
221 
222 static int whpx_set_tsc(CPUState *cpu)
223 {
224     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
225     WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc;
226     WHV_REGISTER_VALUE tsc_val;
227     HRESULT hr;
228     struct whpx_state *whpx = &whpx_global;
229 
230     /*
231      * Suspend the partition prior to setting the TSC to reduce the variance
232      * in TSC across vCPUs. When the first vCPU runs post suspend, the
233      * partition is automatically resumed.
234      */
235     if (whp_dispatch.WHvSuspendPartitionTime) {
236 
237         /*
238          * Unable to suspend partition while setting TSC is not a fatal
239          * error. It just increases the likelihood of TSC variance between
240          * vCPUs and some guest OS are able to handle that just fine.
241          */
242         hr = whp_dispatch.WHvSuspendPartitionTime(whpx->partition);
243         if (FAILED(hr)) {
244             warn_report("WHPX: Failed to suspend partition, hr=%08lx", hr);
245         }
246     }
247 
248     tsc_val.Reg64 = env->tsc;
249     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
250         whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val);
251     if (FAILED(hr)) {
252         error_report("WHPX: Failed to set TSC, hr=%08lx", hr);
253         return -1;
254     }
255 
256     return 0;
257 }
258 
259 static void whpx_set_registers(CPUState *cpu, int level)
260 {
261     struct whpx_state *whpx = &whpx_global;
262     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
263     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
264     X86CPU *x86_cpu = X86_CPU(cpu);
265     struct whpx_register_set vcxt;
266     HRESULT hr;
267     int idx;
268     int idx_next;
269     int i;
270     int v86, r86;
271 
272     assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
273 
274     /*
275      * Following MSRs have side effects on the guest or are too heavy for
276      * runtime. Limit them to full state update.
277      */
278     if (level >= WHPX_SET_RESET_STATE) {
279         whpx_set_tsc(cpu);
280     }
281 
282     memset(&vcxt, 0, sizeof(struct whpx_register_set));
283 
284     v86 = (env->eflags & VM_MASK);
285     r86 = !(env->cr[0] & CR0_PE_MASK);
286 
287     vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
288     vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
289 
290     idx = 0;
291 
292     /* Indexes for first 16 registers match between HV and QEMU definitions */
293     idx_next = 16;
294     for (idx = 0; idx < CPU_NB_REGS; idx += 1) {
295         vcxt.values[idx].Reg64 = (uint64_t)env->regs[idx];
296     }
297     idx = idx_next;
298 
299     /* Same goes for RIP and RFLAGS */
300     assert(whpx_register_names[idx] == WHvX64RegisterRip);
301     vcxt.values[idx++].Reg64 = env->eip;
302 
303     assert(whpx_register_names[idx] == WHvX64RegisterRflags);
304     vcxt.values[idx++].Reg64 = env->eflags;
305 
306     /* Translate 6+4 segment registers. HV and QEMU order matches  */
307     assert(idx == WHvX64RegisterEs);
308     for (i = 0; i < 6; i += 1, idx += 1) {
309         vcxt.values[idx].Segment = whpx_seg_q2h(&env->segs[i], v86, r86);
310     }
311 
312     assert(idx == WHvX64RegisterLdtr);
313     vcxt.values[idx++].Segment = whpx_seg_q2h(&env->ldt, 0, 0);
314 
315     assert(idx == WHvX64RegisterTr);
316     vcxt.values[idx++].Segment = whpx_seg_q2h(&env->tr, 0, 0);
317 
318     assert(idx == WHvX64RegisterIdtr);
319     vcxt.values[idx].Table.Base = env->idt.base;
320     vcxt.values[idx].Table.Limit = env->idt.limit;
321     idx += 1;
322 
323     assert(idx == WHvX64RegisterGdtr);
324     vcxt.values[idx].Table.Base = env->gdt.base;
325     vcxt.values[idx].Table.Limit = env->gdt.limit;
326     idx += 1;
327 
328     /* CR0, 2, 3, 4, 8 */
329     assert(whpx_register_names[idx] == WHvX64RegisterCr0);
330     vcxt.values[idx++].Reg64 = env->cr[0];
331     assert(whpx_register_names[idx] == WHvX64RegisterCr2);
332     vcxt.values[idx++].Reg64 = env->cr[2];
333     assert(whpx_register_names[idx] == WHvX64RegisterCr3);
334     vcxt.values[idx++].Reg64 = env->cr[3];
335     assert(whpx_register_names[idx] == WHvX64RegisterCr4);
336     vcxt.values[idx++].Reg64 = env->cr[4];
337     assert(whpx_register_names[idx] == WHvX64RegisterCr8);
338     vcxt.values[idx++].Reg64 = vcpu->tpr;
339 
340     /* 8 Debug Registers - Skipped */
341 
342     /* 16 XMM registers */
343     assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
344     idx_next = idx + 16;
345     for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
346         vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);
347         vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);
348     }
349     idx = idx_next;
350 
351     /* 8 FP registers */
352     assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
353     for (i = 0; i < 8; i += 1, idx += 1) {
354         vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);
355         /* vcxt.values[idx].Fp.AsUINT128.High64 =
356                env->fpregs[i].mmx.MMX_Q(1);
357         */
358     }
359 
360     /* FP control status register */
361     assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
362     vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;
363     vcxt.values[idx].FpControlStatus.FpStatus =
364         (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
365     vcxt.values[idx].FpControlStatus.FpTag = 0;
366     for (i = 0; i < 8; ++i) {
367         vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;
368     }
369     vcxt.values[idx].FpControlStatus.Reserved = 0;
370     vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;
371     vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;
372     idx += 1;
373 
374     /* XMM control status register */
375     assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
376     vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;
377     vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;
378     vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;
379     idx += 1;
380 
381     /* MSRs */
382     assert(whpx_register_names[idx] == WHvX64RegisterEfer);
383     vcxt.values[idx++].Reg64 = env->efer;
384 #ifdef TARGET_X86_64
385     assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
386     vcxt.values[idx++].Reg64 = env->kernelgsbase;
387 #endif
388 
389     assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
390     vcxt.values[idx++].Reg64 = vcpu->apic_base;
391 
392     /* WHvX64RegisterPat - Skipped */
393 
394     assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
395     vcxt.values[idx++].Reg64 = env->sysenter_cs;
396     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
397     vcxt.values[idx++].Reg64 = env->sysenter_eip;
398     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
399     vcxt.values[idx++].Reg64 = env->sysenter_esp;
400     assert(whpx_register_names[idx] == WHvX64RegisterStar);
401     vcxt.values[idx++].Reg64 = env->star;
402 #ifdef TARGET_X86_64
403     assert(whpx_register_names[idx] == WHvX64RegisterLstar);
404     vcxt.values[idx++].Reg64 = env->lstar;
405     assert(whpx_register_names[idx] == WHvX64RegisterCstar);
406     vcxt.values[idx++].Reg64 = env->cstar;
407     assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
408     vcxt.values[idx++].Reg64 = env->fmask;
409 #endif
410 
411     /* Interrupt / Event Registers - Skipped */
412 
413     assert(idx == RTL_NUMBER_OF(whpx_register_names));
414 
415     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
416         whpx->partition, cpu->cpu_index,
417         whpx_register_names,
418         RTL_NUMBER_OF(whpx_register_names),
419         &vcxt.values[0]);
420 
421     if (FAILED(hr)) {
422         error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
423                      hr);
424     }
425 
426     return;
427 }
428 
429 static int whpx_get_tsc(CPUState *cpu)
430 {
431     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
432     WHV_REGISTER_NAME tsc_reg = WHvX64RegisterTsc;
433     WHV_REGISTER_VALUE tsc_val;
434     HRESULT hr;
435     struct whpx_state *whpx = &whpx_global;
436 
437     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
438         whpx->partition, cpu->cpu_index, &tsc_reg, 1, &tsc_val);
439     if (FAILED(hr)) {
440         error_report("WHPX: Failed to get TSC, hr=%08lx", hr);
441         return -1;
442     }
443 
444     env->tsc = tsc_val.Reg64;
445     return 0;
446 }
447 
448 static void whpx_get_registers(CPUState *cpu)
449 {
450     struct whpx_state *whpx = &whpx_global;
451     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
452     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
453     X86CPU *x86_cpu = X86_CPU(cpu);
454     struct whpx_register_set vcxt;
455     uint64_t tpr, apic_base;
456     HRESULT hr;
457     int idx;
458     int idx_next;
459     int i;
460 
461     assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
462 
463     if (!env->tsc_valid) {
464         whpx_get_tsc(cpu);
465         env->tsc_valid = !runstate_is_running();
466     }
467 
468     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
469         whpx->partition, cpu->cpu_index,
470         whpx_register_names,
471         RTL_NUMBER_OF(whpx_register_names),
472         &vcxt.values[0]);
473     if (FAILED(hr)) {
474         error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
475                      hr);
476     }
477 
478     idx = 0;
479 
480     /* Indexes for first 16 registers match between HV and QEMU definitions */
481     idx_next = 16;
482     for (idx = 0; idx < CPU_NB_REGS; idx += 1) {
483         env->regs[idx] = vcxt.values[idx].Reg64;
484     }
485     idx = idx_next;
486 
487     /* Same goes for RIP and RFLAGS */
488     assert(whpx_register_names[idx] == WHvX64RegisterRip);
489     env->eip = vcxt.values[idx++].Reg64;
490     assert(whpx_register_names[idx] == WHvX64RegisterRflags);
491     env->eflags = vcxt.values[idx++].Reg64;
492 
493     /* Translate 6+4 segment registers. HV and QEMU order matches  */
494     assert(idx == WHvX64RegisterEs);
495     for (i = 0; i < 6; i += 1, idx += 1) {
496         env->segs[i] = whpx_seg_h2q(&vcxt.values[idx].Segment);
497     }
498 
499     assert(idx == WHvX64RegisterLdtr);
500     env->ldt = whpx_seg_h2q(&vcxt.values[idx++].Segment);
501     assert(idx == WHvX64RegisterTr);
502     env->tr = whpx_seg_h2q(&vcxt.values[idx++].Segment);
503     assert(idx == WHvX64RegisterIdtr);
504     env->idt.base = vcxt.values[idx].Table.Base;
505     env->idt.limit = vcxt.values[idx].Table.Limit;
506     idx += 1;
507     assert(idx == WHvX64RegisterGdtr);
508     env->gdt.base = vcxt.values[idx].Table.Base;
509     env->gdt.limit = vcxt.values[idx].Table.Limit;
510     idx += 1;
511 
512     /* CR0, 2, 3, 4, 8 */
513     assert(whpx_register_names[idx] == WHvX64RegisterCr0);
514     env->cr[0] = vcxt.values[idx++].Reg64;
515     assert(whpx_register_names[idx] == WHvX64RegisterCr2);
516     env->cr[2] = vcxt.values[idx++].Reg64;
517     assert(whpx_register_names[idx] == WHvX64RegisterCr3);
518     env->cr[3] = vcxt.values[idx++].Reg64;
519     assert(whpx_register_names[idx] == WHvX64RegisterCr4);
520     env->cr[4] = vcxt.values[idx++].Reg64;
521     assert(whpx_register_names[idx] == WHvX64RegisterCr8);
522     tpr = vcxt.values[idx++].Reg64;
523     if (tpr != vcpu->tpr) {
524         vcpu->tpr = tpr;
525         cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
526     }
527 
528     /* 8 Debug Registers - Skipped */
529 
530     /* 16 XMM registers */
531     assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
532     idx_next = idx + 16;
533     for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
534         env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;
535         env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;
536     }
537     idx = idx_next;
538 
539     /* 8 FP registers */
540     assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
541     for (i = 0; i < 8; i += 1, idx += 1) {
542         env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;
543         /* env->fpregs[i].mmx.MMX_Q(1) =
544                vcxt.values[idx].Fp.AsUINT128.High64;
545         */
546     }
547 
548     /* FP control status register */
549     assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
550     env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;
551     env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;
552     env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;
553     for (i = 0; i < 8; ++i) {
554         env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);
555     }
556     env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;
557     env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;
558     idx += 1;
559 
560     /* XMM control status register */
561     assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
562     env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
563     idx += 1;
564 
565     /* MSRs */
566     assert(whpx_register_names[idx] == WHvX64RegisterEfer);
567     env->efer = vcxt.values[idx++].Reg64;
568 #ifdef TARGET_X86_64
569     assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
570     env->kernelgsbase = vcxt.values[idx++].Reg64;
571 #endif
572 
573     assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
574     apic_base = vcxt.values[idx++].Reg64;
575     if (apic_base != vcpu->apic_base) {
576         vcpu->apic_base = apic_base;
577         cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base);
578     }
579 
580     /* WHvX64RegisterPat - Skipped */
581 
582     assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
583     env->sysenter_cs = vcxt.values[idx++].Reg64;
584     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
585     env->sysenter_eip = vcxt.values[idx++].Reg64;
586     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
587     env->sysenter_esp = vcxt.values[idx++].Reg64;
588     assert(whpx_register_names[idx] == WHvX64RegisterStar);
589     env->star = vcxt.values[idx++].Reg64;
590 #ifdef TARGET_X86_64
591     assert(whpx_register_names[idx] == WHvX64RegisterLstar);
592     env->lstar = vcxt.values[idx++].Reg64;
593     assert(whpx_register_names[idx] == WHvX64RegisterCstar);
594     env->cstar = vcxt.values[idx++].Reg64;
595     assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
596     env->fmask = vcxt.values[idx++].Reg64;
597 #endif
598 
599     /* Interrupt / Event Registers - Skipped */
600 
601     assert(idx == RTL_NUMBER_OF(whpx_register_names));
602 
603     if (whpx_apic_in_platform()) {
604         whpx_apic_get(x86_cpu->apic_state);
605     }
606 
607     return;
608 }
609 
610 static HRESULT CALLBACK whpx_emu_ioport_callback(
611     void *ctx,
612     WHV_EMULATOR_IO_ACCESS_INFO *IoAccess)
613 {
614     MemTxAttrs attrs = { 0 };
615     address_space_rw(&address_space_io, IoAccess->Port, attrs,
616                      &IoAccess->Data, IoAccess->AccessSize,
617                      IoAccess->Direction);
618     return S_OK;
619 }
620 
621 static HRESULT CALLBACK whpx_emu_mmio_callback(
622     void *ctx,
623     WHV_EMULATOR_MEMORY_ACCESS_INFO *ma)
624 {
625     cpu_physical_memory_rw(ma->GpaAddress, ma->Data, ma->AccessSize,
626                            ma->Direction);
627     return S_OK;
628 }
629 
630 static HRESULT CALLBACK whpx_emu_getreg_callback(
631     void *ctx,
632     const WHV_REGISTER_NAME *RegisterNames,
633     UINT32 RegisterCount,
634     WHV_REGISTER_VALUE *RegisterValues)
635 {
636     HRESULT hr;
637     struct whpx_state *whpx = &whpx_global;
638     CPUState *cpu = (CPUState *)ctx;
639 
640     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
641         whpx->partition, cpu->cpu_index,
642         RegisterNames, RegisterCount,
643         RegisterValues);
644     if (FAILED(hr)) {
645         error_report("WHPX: Failed to get virtual processor registers,"
646                      " hr=%08lx", hr);
647     }
648 
649     return hr;
650 }
651 
652 static HRESULT CALLBACK whpx_emu_setreg_callback(
653     void *ctx,
654     const WHV_REGISTER_NAME *RegisterNames,
655     UINT32 RegisterCount,
656     const WHV_REGISTER_VALUE *RegisterValues)
657 {
658     HRESULT hr;
659     struct whpx_state *whpx = &whpx_global;
660     CPUState *cpu = (CPUState *)ctx;
661 
662     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
663         whpx->partition, cpu->cpu_index,
664         RegisterNames, RegisterCount,
665         RegisterValues);
666     if (FAILED(hr)) {
667         error_report("WHPX: Failed to set virtual processor registers,"
668                      " hr=%08lx", hr);
669     }
670 
671     /*
672      * The emulator just successfully wrote the register state. We clear the
673      * dirty state so we avoid the double write on resume of the VP.
674      */
675     cpu->vcpu_dirty = false;
676 
677     return hr;
678 }
679 
680 static HRESULT CALLBACK whpx_emu_translate_callback(
681     void *ctx,
682     WHV_GUEST_VIRTUAL_ADDRESS Gva,
683     WHV_TRANSLATE_GVA_FLAGS TranslateFlags,
684     WHV_TRANSLATE_GVA_RESULT_CODE *TranslationResult,
685     WHV_GUEST_PHYSICAL_ADDRESS *Gpa)
686 {
687     HRESULT hr;
688     struct whpx_state *whpx = &whpx_global;
689     CPUState *cpu = (CPUState *)ctx;
690     WHV_TRANSLATE_GVA_RESULT res;
691 
692     hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index,
693                                       Gva, TranslateFlags, &res, Gpa);
694     if (FAILED(hr)) {
695         error_report("WHPX: Failed to translate GVA, hr=%08lx", hr);
696     } else {
697         *TranslationResult = res.ResultCode;
698     }
699 
700     return hr;
701 }
702 
703 static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = {
704     .Size = sizeof(WHV_EMULATOR_CALLBACKS),
705     .WHvEmulatorIoPortCallback = whpx_emu_ioport_callback,
706     .WHvEmulatorMemoryCallback = whpx_emu_mmio_callback,
707     .WHvEmulatorGetVirtualProcessorRegisters = whpx_emu_getreg_callback,
708     .WHvEmulatorSetVirtualProcessorRegisters = whpx_emu_setreg_callback,
709     .WHvEmulatorTranslateGvaPage = whpx_emu_translate_callback,
710 };
711 
712 static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
713 {
714     HRESULT hr;
715     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
716     WHV_EMULATOR_STATUS emu_status;
717 
718     hr = whp_dispatch.WHvEmulatorTryMmioEmulation(
719         vcpu->emulator, cpu,
720         &vcpu->exit_ctx.VpContext, ctx,
721         &emu_status);
722     if (FAILED(hr)) {
723         error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr);
724         return -1;
725     }
726 
727     if (!emu_status.EmulationSuccessful) {
728         error_report("WHPX: Failed to emulate MMIO access with"
729                      " EmulatorReturnStatus: %u", emu_status.AsUINT32);
730         return -1;
731     }
732 
733     return 0;
734 }
735 
736 static int whpx_handle_portio(CPUState *cpu,
737                               WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx)
738 {
739     HRESULT hr;
740     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
741     WHV_EMULATOR_STATUS emu_status;
742 
743     hr = whp_dispatch.WHvEmulatorTryIoEmulation(
744         vcpu->emulator, cpu,
745         &vcpu->exit_ctx.VpContext, ctx,
746         &emu_status);
747     if (FAILED(hr)) {
748         error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr);
749         return -1;
750     }
751 
752     if (!emu_status.EmulationSuccessful) {
753         error_report("WHPX: Failed to emulate PortIO access with"
754                      " EmulatorReturnStatus: %u", emu_status.AsUINT32);
755         return -1;
756     }
757 
758     return 0;
759 }
760 
761 static int whpx_handle_halt(CPUState *cpu)
762 {
763     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
764     int ret = 0;
765 
766     qemu_mutex_lock_iothread();
767     if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
768           (env->eflags & IF_MASK)) &&
769         !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
770         cpu->exception_index = EXCP_HLT;
771         cpu->halted = true;
772         ret = 1;
773     }
774     qemu_mutex_unlock_iothread();
775 
776     return ret;
777 }
778 
779 static void whpx_vcpu_pre_run(CPUState *cpu)
780 {
781     HRESULT hr;
782     struct whpx_state *whpx = &whpx_global;
783     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
784     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
785     X86CPU *x86_cpu = X86_CPU(cpu);
786     int irq;
787     uint8_t tpr;
788     WHV_X64_PENDING_INTERRUPTION_REGISTER new_int;
789     UINT32 reg_count = 0;
790     WHV_REGISTER_VALUE reg_values[3];
791     WHV_REGISTER_NAME reg_names[3];
792 
793     memset(&new_int, 0, sizeof(new_int));
794     memset(reg_values, 0, sizeof(reg_values));
795 
796     qemu_mutex_lock_iothread();
797 
798     /* Inject NMI */
799     if (!vcpu->interruption_pending &&
800         cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) {
801         if (cpu->interrupt_request & CPU_INTERRUPT_NMI) {
802             cpu->interrupt_request &= ~CPU_INTERRUPT_NMI;
803             vcpu->interruptable = false;
804             new_int.InterruptionType = WHvX64PendingNmi;
805             new_int.InterruptionPending = 1;
806             new_int.InterruptionVector = 2;
807         }
808         if (cpu->interrupt_request & CPU_INTERRUPT_SMI) {
809             cpu->interrupt_request &= ~CPU_INTERRUPT_SMI;
810         }
811     }
812 
813     /*
814      * Force the VCPU out of its inner loop to process any INIT requests or
815      * commit pending TPR access.
816      */
817     if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) {
818         if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
819             !(env->hflags & HF_SMM_MASK)) {
820             cpu->exit_request = 1;
821         }
822         if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
823             cpu->exit_request = 1;
824         }
825     }
826 
827     /* Get pending hard interruption or replay one that was overwritten */
828     if (!whpx_apic_in_platform()) {
829         if (!vcpu->interruption_pending &&
830             vcpu->interruptable && (env->eflags & IF_MASK)) {
831             assert(!new_int.InterruptionPending);
832             if (cpu->interrupt_request & CPU_INTERRUPT_HARD) {
833                 cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
834                 irq = cpu_get_pic_interrupt(env);
835                 if (irq >= 0) {
836                     new_int.InterruptionType = WHvX64PendingInterrupt;
837                     new_int.InterruptionPending = 1;
838                     new_int.InterruptionVector = irq;
839                 }
840             }
841         }
842 
843         /* Setup interrupt state if new one was prepared */
844         if (new_int.InterruptionPending) {
845             reg_values[reg_count].PendingInterruption = new_int;
846             reg_names[reg_count] = WHvRegisterPendingInterruption;
847             reg_count += 1;
848         }
849     } else if (vcpu->ready_for_pic_interrupt &&
850                (cpu->interrupt_request & CPU_INTERRUPT_HARD)) {
851         cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
852         irq = cpu_get_pic_interrupt(env);
853         if (irq >= 0) {
854             reg_names[reg_count] = WHvRegisterPendingEvent;
855             reg_values[reg_count].ExtIntEvent = (WHV_X64_PENDING_EXT_INT_EVENT)
856             {
857                 .EventPending = 1,
858                 .EventType = WHvX64PendingEventExtInt,
859                 .Vector = irq,
860             };
861             reg_count += 1;
862         }
863      }
864 
865     /* Sync the TPR to the CR8 if was modified during the intercept */
866     tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
867     if (tpr != vcpu->tpr) {
868         vcpu->tpr = tpr;
869         reg_values[reg_count].Reg64 = tpr;
870         cpu->exit_request = 1;
871         reg_names[reg_count] = WHvX64RegisterCr8;
872         reg_count += 1;
873     }
874 
875     /* Update the state of the interrupt delivery notification */
876     if (!vcpu->window_registered &&
877         cpu->interrupt_request & CPU_INTERRUPT_HARD) {
878         reg_values[reg_count].DeliverabilityNotifications =
879             (WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER) {
880                 .InterruptNotification = 1
881             };
882         vcpu->window_registered = 1;
883         reg_names[reg_count] = WHvX64RegisterDeliverabilityNotifications;
884         reg_count += 1;
885     }
886 
887     qemu_mutex_unlock_iothread();
888     vcpu->ready_for_pic_interrupt = false;
889 
890     if (reg_count) {
891         hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
892             whpx->partition, cpu->cpu_index,
893             reg_names, reg_count, reg_values);
894         if (FAILED(hr)) {
895             error_report("WHPX: Failed to set interrupt state registers,"
896                          " hr=%08lx", hr);
897         }
898     }
899 
900     return;
901 }
902 
903 static void whpx_vcpu_post_run(CPUState *cpu)
904 {
905     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
906     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
907     X86CPU *x86_cpu = X86_CPU(cpu);
908 
909     env->eflags = vcpu->exit_ctx.VpContext.Rflags;
910 
911     uint64_t tpr = vcpu->exit_ctx.VpContext.Cr8;
912     if (vcpu->tpr != tpr) {
913         vcpu->tpr = tpr;
914         qemu_mutex_lock_iothread();
915         cpu_set_apic_tpr(x86_cpu->apic_state, vcpu->tpr);
916         qemu_mutex_unlock_iothread();
917     }
918 
919     vcpu->interruption_pending =
920         vcpu->exit_ctx.VpContext.ExecutionState.InterruptionPending;
921 
922     vcpu->interruptable =
923         !vcpu->exit_ctx.VpContext.ExecutionState.InterruptShadow;
924 
925     return;
926 }
927 
928 static void whpx_vcpu_process_async_events(CPUState *cpu)
929 {
930     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
931     X86CPU *x86_cpu = X86_CPU(cpu);
932     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
933 
934     if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
935         !(env->hflags & HF_SMM_MASK)) {
936         whpx_cpu_synchronize_state(cpu);
937         do_cpu_init(x86_cpu);
938         vcpu->interruptable = true;
939     }
940 
941     if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
942         cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
943         apic_poll_irq(x86_cpu->apic_state);
944     }
945 
946     if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
947          (env->eflags & IF_MASK)) ||
948         (cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
949         cpu->halted = false;
950     }
951 
952     if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) {
953         whpx_cpu_synchronize_state(cpu);
954         do_cpu_sipi(x86_cpu);
955     }
956 
957     if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
958         cpu->interrupt_request &= ~CPU_INTERRUPT_TPR;
959         whpx_cpu_synchronize_state(cpu);
960         apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip,
961                                       env->tpr_access_type);
962     }
963 
964     return;
965 }
966 
967 static int whpx_vcpu_run(CPUState *cpu)
968 {
969     HRESULT hr;
970     struct whpx_state *whpx = &whpx_global;
971     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
972     int ret;
973 
974     whpx_vcpu_process_async_events(cpu);
975     if (cpu->halted && !whpx_apic_in_platform()) {
976         cpu->exception_index = EXCP_HLT;
977         qatomic_set(&cpu->exit_request, false);
978         return 0;
979     }
980 
981     qemu_mutex_unlock_iothread();
982     cpu_exec_start(cpu);
983 
984     do {
985         if (cpu->vcpu_dirty) {
986             whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE);
987             cpu->vcpu_dirty = false;
988         }
989 
990         whpx_vcpu_pre_run(cpu);
991 
992         if (qatomic_read(&cpu->exit_request)) {
993             whpx_vcpu_kick(cpu);
994         }
995 
996         hr = whp_dispatch.WHvRunVirtualProcessor(
997             whpx->partition, cpu->cpu_index,
998             &vcpu->exit_ctx, sizeof(vcpu->exit_ctx));
999 
1000         if (FAILED(hr)) {
1001             error_report("WHPX: Failed to exec a virtual processor,"
1002                          " hr=%08lx", hr);
1003             ret = -1;
1004             break;
1005         }
1006 
1007         whpx_vcpu_post_run(cpu);
1008 
1009         switch (vcpu->exit_ctx.ExitReason) {
1010         case WHvRunVpExitReasonMemoryAccess:
1011             ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
1012             break;
1013 
1014         case WHvRunVpExitReasonX64IoPortAccess:
1015             ret = whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess);
1016             break;
1017 
1018         case WHvRunVpExitReasonX64InterruptWindow:
1019             vcpu->ready_for_pic_interrupt = 1;
1020             vcpu->window_registered = 0;
1021             ret = 0;
1022             break;
1023 
1024         case WHvRunVpExitReasonX64ApicEoi:
1025             assert(whpx_apic_in_platform());
1026             ioapic_eoi_broadcast(vcpu->exit_ctx.ApicEoi.InterruptVector);
1027             break;
1028 
1029         case WHvRunVpExitReasonX64Halt:
1030             ret = whpx_handle_halt(cpu);
1031             break;
1032 
1033         case WHvRunVpExitReasonX64ApicInitSipiTrap: {
1034             WHV_INTERRUPT_CONTROL ipi = {0};
1035             uint64_t icr = vcpu->exit_ctx.ApicInitSipi.ApicIcr;
1036             uint32_t delivery_mode =
1037                 (icr & APIC_ICR_DELIV_MOD) >> APIC_ICR_DELIV_MOD_SHIFT;
1038             int dest_shorthand =
1039                 (icr & APIC_ICR_DEST_SHORT) >> APIC_ICR_DEST_SHORT_SHIFT;
1040             bool broadcast = false;
1041             bool include_self = false;
1042             uint32_t i;
1043 
1044             /* We only registered for INIT and SIPI exits. */
1045             if ((delivery_mode != APIC_DM_INIT) &&
1046                 (delivery_mode != APIC_DM_SIPI)) {
1047                 error_report(
1048                     "WHPX: Unexpected APIC exit that is not a INIT or SIPI");
1049                 break;
1050             }
1051 
1052             if (delivery_mode == APIC_DM_INIT) {
1053                 ipi.Type = WHvX64InterruptTypeInit;
1054             } else {
1055                 ipi.Type = WHvX64InterruptTypeSipi;
1056             }
1057 
1058             ipi.DestinationMode =
1059                 ((icr & APIC_ICR_DEST_MOD) >> APIC_ICR_DEST_MOD_SHIFT) ?
1060                     WHvX64InterruptDestinationModeLogical :
1061                     WHvX64InterruptDestinationModePhysical;
1062 
1063             ipi.TriggerMode =
1064                 ((icr & APIC_ICR_TRIGGER_MOD) >> APIC_ICR_TRIGGER_MOD_SHIFT) ?
1065                     WHvX64InterruptTriggerModeLevel :
1066                     WHvX64InterruptTriggerModeEdge;
1067 
1068             ipi.Vector = icr & APIC_VECTOR_MASK;
1069             switch (dest_shorthand) {
1070             /* no shorthand. Bits 56-63 contain the destination. */
1071             case 0:
1072                 ipi.Destination = (icr >> 56) & APIC_VECTOR_MASK;
1073                 hr = whp_dispatch.WHvRequestInterrupt(whpx->partition,
1074                         &ipi, sizeof(ipi));
1075                 if (FAILED(hr)) {
1076                     error_report("WHPX: Failed to request interrupt  hr=%08lx",
1077                         hr);
1078                 }
1079 
1080                 break;
1081 
1082             /* self */
1083             case 1:
1084                 include_self = true;
1085                 break;
1086 
1087             /* broadcast, including self */
1088             case 2:
1089                 broadcast = true;
1090                 include_self = true;
1091                 break;
1092 
1093             /* broadcast, excluding self */
1094             case 3:
1095                 broadcast = true;
1096                 break;
1097             }
1098 
1099             if (!broadcast && !include_self) {
1100                 break;
1101             }
1102 
1103             for (i = 0; i <= max_vcpu_index; i++) {
1104                 if (i == cpu->cpu_index && !include_self) {
1105                     continue;
1106                 }
1107 
1108                 /*
1109                  * Assuming that APIC Ids are identity mapped since
1110                  * WHvX64RegisterApicId & WHvX64RegisterInitialApicId registers
1111                  * are not handled yet and the hypervisor doesn't allow the
1112                  * guest to modify the APIC ID.
1113                  */
1114                 ipi.Destination = i;
1115                 hr = whp_dispatch.WHvRequestInterrupt(whpx->partition,
1116                         &ipi, sizeof(ipi));
1117                 if (FAILED(hr)) {
1118                     error_report(
1119                         "WHPX: Failed to request SIPI for %d,  hr=%08lx",
1120                         i, hr);
1121                 }
1122             }
1123 
1124             break;
1125         }
1126 
1127         case WHvRunVpExitReasonCanceled:
1128             cpu->exception_index = EXCP_INTERRUPT;
1129             ret = 1;
1130             break;
1131 
1132         case WHvRunVpExitReasonX64MsrAccess: {
1133             WHV_REGISTER_VALUE reg_values[3] = {0};
1134             WHV_REGISTER_NAME reg_names[3];
1135             UINT32 reg_count;
1136 
1137             reg_names[0] = WHvX64RegisterRip;
1138             reg_names[1] = WHvX64RegisterRax;
1139             reg_names[2] = WHvX64RegisterRdx;
1140 
1141             reg_values[0].Reg64 =
1142                 vcpu->exit_ctx.VpContext.Rip +
1143                 vcpu->exit_ctx.VpContext.InstructionLength;
1144 
1145             /*
1146              * For all unsupported MSR access we:
1147              *     ignore writes
1148              *     return 0 on read.
1149              */
1150             reg_count = vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite ?
1151                         1 : 3;
1152 
1153             hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
1154                 whpx->partition,
1155                 cpu->cpu_index,
1156                 reg_names, reg_count,
1157                 reg_values);
1158 
1159             if (FAILED(hr)) {
1160                 error_report("WHPX: Failed to set MsrAccess state "
1161                              " registers, hr=%08lx", hr);
1162             }
1163             ret = 0;
1164             break;
1165         }
1166         case WHvRunVpExitReasonX64Cpuid: {
1167             WHV_REGISTER_VALUE reg_values[5];
1168             WHV_REGISTER_NAME reg_names[5];
1169             UINT32 reg_count = 5;
1170             UINT64 cpuid_fn, rip = 0, rax = 0, rcx = 0, rdx = 0, rbx = 0;
1171             X86CPU *x86_cpu = X86_CPU(cpu);
1172             CPUX86State *env = &x86_cpu->env;
1173 
1174             memset(reg_values, 0, sizeof(reg_values));
1175 
1176             rip = vcpu->exit_ctx.VpContext.Rip +
1177                   vcpu->exit_ctx.VpContext.InstructionLength;
1178             cpuid_fn = vcpu->exit_ctx.CpuidAccess.Rax;
1179 
1180             /*
1181              * Ideally, these should be supplied to the hypervisor during VCPU
1182              * initialization and it should be able to satisfy this request.
1183              * But, currently, WHPX doesn't support setting CPUID values in the
1184              * hypervisor once the partition has been setup, which is too late
1185              * since VCPUs are realized later. For now, use the values from
1186              * QEMU to satisfy these requests, until WHPX adds support for
1187              * being able to set these values in the hypervisor at runtime.
1188              */
1189             cpu_x86_cpuid(env, cpuid_fn, 0, (UINT32 *)&rax, (UINT32 *)&rbx,
1190                 (UINT32 *)&rcx, (UINT32 *)&rdx);
1191             switch (cpuid_fn) {
1192             case 0x40000000:
1193                 /* Expose the vmware cpu frequency cpuid leaf */
1194                 rax = 0x40000010;
1195                 rbx = rcx = rdx = 0;
1196                 break;
1197 
1198             case 0x40000010:
1199                 rax = env->tsc_khz;
1200                 rbx = env->apic_bus_freq / 1000; /* Hz to KHz */
1201                 rcx = rdx = 0;
1202                 break;
1203 
1204             case 0x80000001:
1205                 /* Remove any support of OSVW */
1206                 rcx &= ~CPUID_EXT3_OSVW;
1207                 break;
1208             }
1209 
1210             reg_names[0] = WHvX64RegisterRip;
1211             reg_names[1] = WHvX64RegisterRax;
1212             reg_names[2] = WHvX64RegisterRcx;
1213             reg_names[3] = WHvX64RegisterRdx;
1214             reg_names[4] = WHvX64RegisterRbx;
1215 
1216             reg_values[0].Reg64 = rip;
1217             reg_values[1].Reg64 = rax;
1218             reg_values[2].Reg64 = rcx;
1219             reg_values[3].Reg64 = rdx;
1220             reg_values[4].Reg64 = rbx;
1221 
1222             hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
1223                 whpx->partition, cpu->cpu_index,
1224                 reg_names,
1225                 reg_count,
1226                 reg_values);
1227 
1228             if (FAILED(hr)) {
1229                 error_report("WHPX: Failed to set CpuidAccess state registers,"
1230                              " hr=%08lx", hr);
1231             }
1232             ret = 0;
1233             break;
1234         }
1235         case WHvRunVpExitReasonNone:
1236         case WHvRunVpExitReasonUnrecoverableException:
1237         case WHvRunVpExitReasonInvalidVpRegisterValue:
1238         case WHvRunVpExitReasonUnsupportedFeature:
1239         case WHvRunVpExitReasonException:
1240         default:
1241             error_report("WHPX: Unexpected VP exit code %d",
1242                          vcpu->exit_ctx.ExitReason);
1243             whpx_get_registers(cpu);
1244             qemu_mutex_lock_iothread();
1245             qemu_system_guest_panicked(cpu_get_crash_info(cpu));
1246             qemu_mutex_unlock_iothread();
1247             break;
1248         }
1249 
1250     } while (!ret);
1251 
1252     cpu_exec_end(cpu);
1253     qemu_mutex_lock_iothread();
1254     current_cpu = cpu;
1255 
1256     qatomic_set(&cpu->exit_request, false);
1257 
1258     return ret < 0;
1259 }
1260 
1261 static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
1262 {
1263     if (!cpu->vcpu_dirty) {
1264         whpx_get_registers(cpu);
1265         cpu->vcpu_dirty = true;
1266     }
1267 }
1268 
1269 static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu,
1270                                                run_on_cpu_data arg)
1271 {
1272     whpx_set_registers(cpu, WHPX_SET_RESET_STATE);
1273     cpu->vcpu_dirty = false;
1274 }
1275 
1276 static void do_whpx_cpu_synchronize_post_init(CPUState *cpu,
1277                                               run_on_cpu_data arg)
1278 {
1279     whpx_set_registers(cpu, WHPX_SET_FULL_STATE);
1280     cpu->vcpu_dirty = false;
1281 }
1282 
1283 static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu,
1284                                                run_on_cpu_data arg)
1285 {
1286     cpu->vcpu_dirty = true;
1287 }
1288 
1289 /*
1290  * CPU support.
1291  */
1292 
1293 void whpx_cpu_synchronize_state(CPUState *cpu)
1294 {
1295     if (!cpu->vcpu_dirty) {
1296         run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL);
1297     }
1298 }
1299 
1300 void whpx_cpu_synchronize_post_reset(CPUState *cpu)
1301 {
1302     run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
1303 }
1304 
1305 void whpx_cpu_synchronize_post_init(CPUState *cpu)
1306 {
1307     run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
1308 }
1309 
1310 void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
1311 {
1312     run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
1313 }
1314 
1315 /*
1316  * Vcpu support.
1317  */
1318 
1319 static Error *whpx_migration_blocker;
1320 
1321 static void whpx_cpu_update_state(void *opaque, bool running, RunState state)
1322 {
1323     CPUX86State *env = opaque;
1324 
1325     if (running) {
1326         env->tsc_valid = false;
1327     }
1328 }
1329 
1330 int whpx_init_vcpu(CPUState *cpu)
1331 {
1332     HRESULT hr;
1333     struct whpx_state *whpx = &whpx_global;
1334     struct whpx_vcpu *vcpu = NULL;
1335     Error *local_error = NULL;
1336     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
1337     X86CPU *x86_cpu = X86_CPU(cpu);
1338     UINT64 freq = 0;
1339     int ret;
1340 
1341     /* Add migration blockers for all unsupported features of the
1342      * Windows Hypervisor Platform
1343      */
1344     if (whpx_migration_blocker == NULL) {
1345         error_setg(&whpx_migration_blocker,
1346                "State blocked due to non-migratable CPUID feature support,"
1347                "dirty memory tracking support, and XSAVE/XRSTOR support");
1348 
1349         (void)migrate_add_blocker(whpx_migration_blocker, &local_error);
1350         if (local_error) {
1351             error_report_err(local_error);
1352             migrate_del_blocker(whpx_migration_blocker);
1353             error_free(whpx_migration_blocker);
1354             ret = -EINVAL;
1355             goto error;
1356         }
1357     }
1358 
1359     vcpu = g_malloc0(sizeof(struct whpx_vcpu));
1360 
1361     if (!vcpu) {
1362         error_report("WHPX: Failed to allocte VCPU context.");
1363         ret = -ENOMEM;
1364         goto error;
1365     }
1366 
1367     hr = whp_dispatch.WHvEmulatorCreateEmulator(
1368         &whpx_emu_callbacks,
1369         &vcpu->emulator);
1370     if (FAILED(hr)) {
1371         error_report("WHPX: Failed to setup instruction completion support,"
1372                      " hr=%08lx", hr);
1373         ret = -EINVAL;
1374         goto error;
1375     }
1376 
1377     hr = whp_dispatch.WHvCreateVirtualProcessor(
1378         whpx->partition, cpu->cpu_index, 0);
1379     if (FAILED(hr)) {
1380         error_report("WHPX: Failed to create a virtual processor,"
1381                      " hr=%08lx", hr);
1382         whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1383         ret = -EINVAL;
1384         goto error;
1385     }
1386 
1387     /*
1388      * vcpu's TSC frequency is either specified by user, or use the value
1389      * provided by Hyper-V if the former is not present. In the latter case, we
1390      * query it from Hyper-V and record in env->tsc_khz, so that vcpu's TSC
1391      * frequency can be migrated later via this field.
1392      */
1393     if (!env->tsc_khz) {
1394         hr = whp_dispatch.WHvGetCapability(
1395             WHvCapabilityCodeProcessorClockFrequency, &freq, sizeof(freq),
1396                 NULL);
1397         if (hr != WHV_E_UNKNOWN_CAPABILITY) {
1398             if (FAILED(hr)) {
1399                 printf("WHPX: Failed to query tsc frequency, hr=0x%08lx\n", hr);
1400             } else {
1401                 env->tsc_khz = freq / 1000; /* Hz to KHz */
1402             }
1403         }
1404     }
1405 
1406     env->apic_bus_freq = HYPERV_APIC_BUS_FREQUENCY;
1407     hr = whp_dispatch.WHvGetCapability(
1408         WHvCapabilityCodeInterruptClockFrequency, &freq, sizeof(freq), NULL);
1409     if (hr != WHV_E_UNKNOWN_CAPABILITY) {
1410         if (FAILED(hr)) {
1411             printf("WHPX: Failed to query apic bus frequency hr=0x%08lx\n", hr);
1412         } else {
1413             env->apic_bus_freq = freq;
1414         }
1415     }
1416 
1417     /*
1418      * If the vmware cpuid frequency leaf option is set, and we have a valid
1419      * tsc value, trap the corresponding cpuid's.
1420      */
1421     if (x86_cpu->vmware_cpuid_freq && env->tsc_khz) {
1422         UINT32 cpuidExitList[] = {1, 0x80000001, 0x40000000, 0x40000010};
1423 
1424         hr = whp_dispatch.WHvSetPartitionProperty(
1425                 whpx->partition,
1426                 WHvPartitionPropertyCodeCpuidExitList,
1427                 cpuidExitList,
1428                 RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
1429 
1430         if (FAILED(hr)) {
1431             error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1432                         hr);
1433             ret = -EINVAL;
1434             goto error;
1435         }
1436     }
1437 
1438     vcpu->interruptable = true;
1439     cpu->vcpu_dirty = true;
1440     cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu;
1441     max_vcpu_index = max(max_vcpu_index, cpu->cpu_index);
1442     qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr);
1443 
1444     return 0;
1445 
1446 error:
1447     g_free(vcpu);
1448 
1449     return ret;
1450 }
1451 
1452 int whpx_vcpu_exec(CPUState *cpu)
1453 {
1454     int ret;
1455     int fatal;
1456 
1457     for (;;) {
1458         if (cpu->exception_index >= EXCP_INTERRUPT) {
1459             ret = cpu->exception_index;
1460             cpu->exception_index = -1;
1461             break;
1462         }
1463 
1464         fatal = whpx_vcpu_run(cpu);
1465 
1466         if (fatal) {
1467             error_report("WHPX: Failed to exec a virtual processor");
1468             abort();
1469         }
1470     }
1471 
1472     return ret;
1473 }
1474 
1475 void whpx_destroy_vcpu(CPUState *cpu)
1476 {
1477     struct whpx_state *whpx = &whpx_global;
1478     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
1479 
1480     whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
1481     whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1482     g_free(cpu->hax_vcpu);
1483     return;
1484 }
1485 
1486 void whpx_vcpu_kick(CPUState *cpu)
1487 {
1488     struct whpx_state *whpx = &whpx_global;
1489     whp_dispatch.WHvCancelRunVirtualProcessor(
1490         whpx->partition, cpu->cpu_index, 0);
1491 }
1492 
1493 /*
1494  * Memory support.
1495  */
1496 
1497 static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
1498                                 void *host_va, int add, int rom,
1499                                 const char *name)
1500 {
1501     struct whpx_state *whpx = &whpx_global;
1502     HRESULT hr;
1503 
1504     /*
1505     if (add) {
1506         printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
1507                (void*)start_pa, (void*)size, host_va,
1508                (rom ? "ROM" : "RAM"), name);
1509     } else {
1510         printf("WHPX: DEL PA:%p Size:%p, Host:%p,      '%s'\n",
1511                (void*)start_pa, (void*)size, host_va, name);
1512     }
1513     */
1514 
1515     if (add) {
1516         hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
1517                                          host_va,
1518                                          start_pa,
1519                                          size,
1520                                          (WHvMapGpaRangeFlagRead |
1521                                           WHvMapGpaRangeFlagExecute |
1522                                           (rom ? 0 : WHvMapGpaRangeFlagWrite)));
1523     } else {
1524         hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
1525                                            start_pa,
1526                                            size);
1527     }
1528 
1529     if (FAILED(hr)) {
1530         error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
1531                      " Host:%p, hr=%08lx",
1532                      (add ? "MAP" : "UNMAP"), name,
1533                      (void *)(uintptr_t)start_pa, (void *)size, host_va, hr);
1534     }
1535 }
1536 
1537 static void whpx_process_section(MemoryRegionSection *section, int add)
1538 {
1539     MemoryRegion *mr = section->mr;
1540     hwaddr start_pa = section->offset_within_address_space;
1541     ram_addr_t size = int128_get64(section->size);
1542     unsigned int delta;
1543     uint64_t host_va;
1544 
1545     if (!memory_region_is_ram(mr)) {
1546         return;
1547     }
1548 
1549     delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
1550     delta &= ~qemu_real_host_page_mask;
1551     if (delta > size) {
1552         return;
1553     }
1554     start_pa += delta;
1555     size -= delta;
1556     size &= qemu_real_host_page_mask;
1557     if (!size || (start_pa & ~qemu_real_host_page_mask)) {
1558         return;
1559     }
1560 
1561     host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
1562             + section->offset_within_region + delta;
1563 
1564     whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add,
1565                         memory_region_is_rom(mr), mr->name);
1566 }
1567 
1568 static void whpx_region_add(MemoryListener *listener,
1569                            MemoryRegionSection *section)
1570 {
1571     memory_region_ref(section->mr);
1572     whpx_process_section(section, 1);
1573 }
1574 
1575 static void whpx_region_del(MemoryListener *listener,
1576                            MemoryRegionSection *section)
1577 {
1578     whpx_process_section(section, 0);
1579     memory_region_unref(section->mr);
1580 }
1581 
1582 static void whpx_transaction_begin(MemoryListener *listener)
1583 {
1584 }
1585 
1586 static void whpx_transaction_commit(MemoryListener *listener)
1587 {
1588 }
1589 
1590 static void whpx_log_sync(MemoryListener *listener,
1591                          MemoryRegionSection *section)
1592 {
1593     MemoryRegion *mr = section->mr;
1594 
1595     if (!memory_region_is_ram(mr)) {
1596         return;
1597     }
1598 
1599     memory_region_set_dirty(mr, 0, int128_get64(section->size));
1600 }
1601 
1602 static MemoryListener whpx_memory_listener = {
1603     .begin = whpx_transaction_begin,
1604     .commit = whpx_transaction_commit,
1605     .region_add = whpx_region_add,
1606     .region_del = whpx_region_del,
1607     .log_sync = whpx_log_sync,
1608     .priority = 10,
1609 };
1610 
1611 static void whpx_memory_init(void)
1612 {
1613     memory_listener_register(&whpx_memory_listener, &address_space_memory);
1614 }
1615 
1616 /*
1617  * Load the functions from the given library, using the given handle. If a
1618  * handle is provided, it is used, otherwise the library is opened. The
1619  * handle will be updated on return with the opened one.
1620  */
1621 static bool load_whp_dispatch_fns(HMODULE *handle,
1622     WHPFunctionList function_list)
1623 {
1624     HMODULE hLib = *handle;
1625 
1626     #define WINHV_PLATFORM_DLL "WinHvPlatform.dll"
1627     #define WINHV_EMULATION_DLL "WinHvEmulation.dll"
1628     #define WHP_LOAD_FIELD_OPTIONAL(return_type, function_name, signature) \
1629         whp_dispatch.function_name = \
1630             (function_name ## _t)GetProcAddress(hLib, #function_name); \
1631 
1632     #define WHP_LOAD_FIELD(return_type, function_name, signature) \
1633         whp_dispatch.function_name = \
1634             (function_name ## _t)GetProcAddress(hLib, #function_name); \
1635         if (!whp_dispatch.function_name) { \
1636             error_report("Could not load function %s", #function_name); \
1637             goto error; \
1638         } \
1639 
1640     #define WHP_LOAD_LIB(lib_name, handle_lib) \
1641     if (!handle_lib) { \
1642         handle_lib = LoadLibrary(lib_name); \
1643         if (!handle_lib) { \
1644             error_report("Could not load library %s.", lib_name); \
1645             goto error; \
1646         } \
1647     } \
1648 
1649     switch (function_list) {
1650     case WINHV_PLATFORM_FNS_DEFAULT:
1651         WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
1652         LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
1653         break;
1654 
1655     case WINHV_EMULATION_FNS_DEFAULT:
1656         WHP_LOAD_LIB(WINHV_EMULATION_DLL, hLib)
1657         LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD)
1658         break;
1659 
1660     case WINHV_PLATFORM_FNS_SUPPLEMENTAL:
1661         WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
1662         LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_LOAD_FIELD_OPTIONAL)
1663         break;
1664     }
1665 
1666     *handle = hLib;
1667     return true;
1668 
1669 error:
1670     if (hLib) {
1671         FreeLibrary(hLib);
1672     }
1673 
1674     return false;
1675 }
1676 
1677 static void whpx_set_kernel_irqchip(Object *obj, Visitor *v,
1678                                    const char *name, void *opaque,
1679                                    Error **errp)
1680 {
1681     struct whpx_state *whpx = &whpx_global;
1682     OnOffSplit mode;
1683 
1684     if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
1685         return;
1686     }
1687 
1688     switch (mode) {
1689     case ON_OFF_SPLIT_ON:
1690         whpx->kernel_irqchip_allowed = true;
1691         whpx->kernel_irqchip_required = true;
1692         break;
1693 
1694     case ON_OFF_SPLIT_OFF:
1695         whpx->kernel_irqchip_allowed = false;
1696         whpx->kernel_irqchip_required = false;
1697         break;
1698 
1699     case ON_OFF_SPLIT_SPLIT:
1700         error_setg(errp, "WHPX: split irqchip currently not supported");
1701         error_append_hint(errp,
1702             "Try without kernel-irqchip or with kernel-irqchip=on|off");
1703         break;
1704 
1705     default:
1706         /*
1707          * The value was checked in visit_type_OnOffSplit() above. If
1708          * we get here, then something is wrong in QEMU.
1709          */
1710         abort();
1711     }
1712 }
1713 
1714 /*
1715  * Partition support
1716  */
1717 
1718 static int whpx_accel_init(MachineState *ms)
1719 {
1720     struct whpx_state *whpx;
1721     int ret;
1722     HRESULT hr;
1723     WHV_CAPABILITY whpx_cap;
1724     UINT32 whpx_cap_size;
1725     WHV_PARTITION_PROPERTY prop;
1726     UINT32 cpuidExitList[] = {1, 0x80000001};
1727     WHV_CAPABILITY_FEATURES features = {0};
1728 
1729     whpx = &whpx_global;
1730 
1731     if (!init_whp_dispatch()) {
1732         ret = -ENOSYS;
1733         goto error;
1734     }
1735 
1736     whpx->mem_quota = ms->ram_size;
1737 
1738     hr = whp_dispatch.WHvGetCapability(
1739         WHvCapabilityCodeHypervisorPresent, &whpx_cap,
1740         sizeof(whpx_cap), &whpx_cap_size);
1741     if (FAILED(hr) || !whpx_cap.HypervisorPresent) {
1742         error_report("WHPX: No accelerator found, hr=%08lx", hr);
1743         ret = -ENOSPC;
1744         goto error;
1745     }
1746 
1747     hr = whp_dispatch.WHvGetCapability(
1748         WHvCapabilityCodeFeatures, &features, sizeof(features), NULL);
1749     if (FAILED(hr)) {
1750         error_report("WHPX: Failed to query capabilities, hr=%08lx", hr);
1751         ret = -EINVAL;
1752         goto error;
1753     }
1754 
1755     hr = whp_dispatch.WHvCreatePartition(&whpx->partition);
1756     if (FAILED(hr)) {
1757         error_report("WHPX: Failed to create partition, hr=%08lx", hr);
1758         ret = -EINVAL;
1759         goto error;
1760     }
1761 
1762     memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1763     prop.ProcessorCount = ms->smp.cpus;
1764     hr = whp_dispatch.WHvSetPartitionProperty(
1765         whpx->partition,
1766         WHvPartitionPropertyCodeProcessorCount,
1767         &prop,
1768         sizeof(WHV_PARTITION_PROPERTY));
1769 
1770     if (FAILED(hr)) {
1771         error_report("WHPX: Failed to set partition core count to %d,"
1772                      " hr=%08lx", ms->smp.cores, hr);
1773         ret = -EINVAL;
1774         goto error;
1775     }
1776 
1777     /*
1778      * Error out if WHP doesn't support apic emulation and user is requiring
1779      * it.
1780      */
1781     if (whpx->kernel_irqchip_required && (!features.LocalApicEmulation ||
1782             !whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2)) {
1783         error_report("WHPX: kernel irqchip requested, but unavailable. "
1784             "Try without kernel-irqchip or with kernel-irqchip=off");
1785         ret = -EINVAL;
1786         goto error;
1787     }
1788 
1789     if (whpx->kernel_irqchip_allowed && features.LocalApicEmulation &&
1790         whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2) {
1791         WHV_X64_LOCAL_APIC_EMULATION_MODE mode =
1792             WHvX64LocalApicEmulationModeXApic;
1793         printf("WHPX: setting APIC emulation mode in the hypervisor\n");
1794         hr = whp_dispatch.WHvSetPartitionProperty(
1795             whpx->partition,
1796             WHvPartitionPropertyCodeLocalApicEmulationMode,
1797             &mode,
1798             sizeof(mode));
1799         if (FAILED(hr)) {
1800             error_report("WHPX: Failed to enable kernel irqchip hr=%08lx", hr);
1801             if (whpx->kernel_irqchip_required) {
1802                 error_report("WHPX: kernel irqchip requested, but unavailable");
1803                 ret = -EINVAL;
1804                 goto error;
1805             }
1806         } else {
1807             whpx->apic_in_platform = true;
1808         }
1809     }
1810 
1811     /* Register for MSR and CPUID exits */
1812     memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1813     prop.ExtendedVmExits.X64MsrExit = 1;
1814     prop.ExtendedVmExits.X64CpuidExit = 1;
1815     if (whpx_apic_in_platform()) {
1816         prop.ExtendedVmExits.X64ApicInitSipiExitTrap = 1;
1817     }
1818 
1819     hr = whp_dispatch.WHvSetPartitionProperty(
1820             whpx->partition,
1821             WHvPartitionPropertyCodeExtendedVmExits,
1822             &prop,
1823             sizeof(WHV_PARTITION_PROPERTY));
1824     if (FAILED(hr)) {
1825         error_report("WHPX: Failed to enable MSR & CPUIDexit, hr=%08lx", hr);
1826         ret = -EINVAL;
1827         goto error;
1828     }
1829 
1830     hr = whp_dispatch.WHvSetPartitionProperty(
1831         whpx->partition,
1832         WHvPartitionPropertyCodeCpuidExitList,
1833         cpuidExitList,
1834         RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
1835 
1836     if (FAILED(hr)) {
1837         error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1838                      hr);
1839         ret = -EINVAL;
1840         goto error;
1841     }
1842 
1843     hr = whp_dispatch.WHvSetupPartition(whpx->partition);
1844     if (FAILED(hr)) {
1845         error_report("WHPX: Failed to setup partition, hr=%08lx", hr);
1846         ret = -EINVAL;
1847         goto error;
1848     }
1849 
1850     whpx_memory_init();
1851 
1852     printf("Windows Hypervisor Platform accelerator is operational\n");
1853     return 0;
1854 
1855 error:
1856 
1857     if (NULL != whpx->partition) {
1858         whp_dispatch.WHvDeletePartition(whpx->partition);
1859         whpx->partition = NULL;
1860     }
1861 
1862     return ret;
1863 }
1864 
1865 int whpx_enabled(void)
1866 {
1867     return whpx_allowed;
1868 }
1869 
1870 bool whpx_apic_in_platform(void) {
1871     return whpx_global.apic_in_platform;
1872 }
1873 
1874 static void whpx_accel_class_init(ObjectClass *oc, void *data)
1875 {
1876     AccelClass *ac = ACCEL_CLASS(oc);
1877     ac->name = "WHPX";
1878     ac->init_machine = whpx_accel_init;
1879     ac->allowed = &whpx_allowed;
1880 
1881     object_class_property_add(oc, "kernel-irqchip", "on|off|split",
1882         NULL, whpx_set_kernel_irqchip,
1883         NULL, NULL);
1884     object_class_property_set_description(oc, "kernel-irqchip",
1885         "Configure WHPX in-kernel irqchip");
1886 }
1887 
1888 static void whpx_accel_instance_init(Object *obj)
1889 {
1890     struct whpx_state *whpx = &whpx_global;
1891 
1892     memset(whpx, 0, sizeof(struct whpx_state));
1893     /* Turn on kernel-irqchip, by default */
1894     whpx->kernel_irqchip_allowed = true;
1895 }
1896 
1897 static const TypeInfo whpx_accel_type = {
1898     .name = ACCEL_CLASS_NAME("whpx"),
1899     .parent = TYPE_ACCEL,
1900     .instance_init = whpx_accel_instance_init,
1901     .class_init = whpx_accel_class_init,
1902 };
1903 
1904 static void whpx_type_init(void)
1905 {
1906     type_register_static(&whpx_accel_type);
1907 }
1908 
1909 bool init_whp_dispatch(void)
1910 {
1911     if (whp_dispatch_initialized) {
1912         return true;
1913     }
1914 
1915     if (!load_whp_dispatch_fns(&hWinHvPlatform, WINHV_PLATFORM_FNS_DEFAULT)) {
1916         goto error;
1917     }
1918 
1919     if (!load_whp_dispatch_fns(&hWinHvEmulation, WINHV_EMULATION_FNS_DEFAULT)) {
1920         goto error;
1921     }
1922 
1923     assert(load_whp_dispatch_fns(&hWinHvPlatform,
1924         WINHV_PLATFORM_FNS_SUPPLEMENTAL));
1925     whp_dispatch_initialized = true;
1926 
1927     return true;
1928 error:
1929     if (hWinHvPlatform) {
1930         FreeLibrary(hWinHvPlatform);
1931     }
1932 
1933     if (hWinHvEmulation) {
1934         FreeLibrary(hWinHvEmulation);
1935     }
1936 
1937     return false;
1938 }
1939 
1940 type_init(whpx_type_init);
1941