]> Git Repo - qemu.git/blob - target/i386/whpx-all.c
0c15241ae490fc2ab18ce22c0bcf856db6002ff9
[qemu.git] / target / i386 / whpx-all.c
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 "sysemu/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 "qemu/error-report.h"
22 #include "qapi/error.h"
23 #include "migration/blocker.h"
24 #include "whp-dispatch.h"
25
26 #include <WinHvPlatform.h>
27 #include <WinHvEmulation.h>
28
29 struct whpx_state {
30     uint64_t mem_quota;
31     WHV_PARTITION_HANDLE partition;
32 };
33
34 static const WHV_REGISTER_NAME whpx_register_names[] = {
35
36     /* X64 General purpose registers */
37     WHvX64RegisterRax,
38     WHvX64RegisterRcx,
39     WHvX64RegisterRdx,
40     WHvX64RegisterRbx,
41     WHvX64RegisterRsp,
42     WHvX64RegisterRbp,
43     WHvX64RegisterRsi,
44     WHvX64RegisterRdi,
45     WHvX64RegisterR8,
46     WHvX64RegisterR9,
47     WHvX64RegisterR10,
48     WHvX64RegisterR11,
49     WHvX64RegisterR12,
50     WHvX64RegisterR13,
51     WHvX64RegisterR14,
52     WHvX64RegisterR15,
53     WHvX64RegisterRip,
54     WHvX64RegisterRflags,
55
56     /* X64 Segment registers */
57     WHvX64RegisterEs,
58     WHvX64RegisterCs,
59     WHvX64RegisterSs,
60     WHvX64RegisterDs,
61     WHvX64RegisterFs,
62     WHvX64RegisterGs,
63     WHvX64RegisterLdtr,
64     WHvX64RegisterTr,
65
66     /* X64 Table registers */
67     WHvX64RegisterIdtr,
68     WHvX64RegisterGdtr,
69
70     /* X64 Control Registers */
71     WHvX64RegisterCr0,
72     WHvX64RegisterCr2,
73     WHvX64RegisterCr3,
74     WHvX64RegisterCr4,
75     WHvX64RegisterCr8,
76
77     /* X64 Debug Registers */
78     /*
79      * WHvX64RegisterDr0,
80      * WHvX64RegisterDr1,
81      * WHvX64RegisterDr2,
82      * WHvX64RegisterDr3,
83      * WHvX64RegisterDr6,
84      * WHvX64RegisterDr7,
85      */
86
87     /* X64 Floating Point and Vector Registers */
88     WHvX64RegisterXmm0,
89     WHvX64RegisterXmm1,
90     WHvX64RegisterXmm2,
91     WHvX64RegisterXmm3,
92     WHvX64RegisterXmm4,
93     WHvX64RegisterXmm5,
94     WHvX64RegisterXmm6,
95     WHvX64RegisterXmm7,
96     WHvX64RegisterXmm8,
97     WHvX64RegisterXmm9,
98     WHvX64RegisterXmm10,
99     WHvX64RegisterXmm11,
100     WHvX64RegisterXmm12,
101     WHvX64RegisterXmm13,
102     WHvX64RegisterXmm14,
103     WHvX64RegisterXmm15,
104     WHvX64RegisterFpMmx0,
105     WHvX64RegisterFpMmx1,
106     WHvX64RegisterFpMmx2,
107     WHvX64RegisterFpMmx3,
108     WHvX64RegisterFpMmx4,
109     WHvX64RegisterFpMmx5,
110     WHvX64RegisterFpMmx6,
111     WHvX64RegisterFpMmx7,
112     WHvX64RegisterFpControlStatus,
113     WHvX64RegisterXmmControlStatus,
114
115     /* X64 MSRs */
116     WHvX64RegisterTsc,
117     WHvX64RegisterEfer,
118 #ifdef TARGET_X86_64
119     WHvX64RegisterKernelGsBase,
120 #endif
121     WHvX64RegisterApicBase,
122     /* WHvX64RegisterPat, */
123     WHvX64RegisterSysenterCs,
124     WHvX64RegisterSysenterEip,
125     WHvX64RegisterSysenterEsp,
126     WHvX64RegisterStar,
127 #ifdef TARGET_X86_64
128     WHvX64RegisterLstar,
129     WHvX64RegisterCstar,
130     WHvX64RegisterSfmask,
131 #endif
132
133     /* Interrupt / Event Registers */
134     /*
135      * WHvRegisterPendingInterruption,
136      * WHvRegisterInterruptState,
137      * WHvRegisterPendingEvent0,
138      * WHvRegisterPendingEvent1
139      * WHvX64RegisterDeliverabilityNotifications,
140      */
141 };
142
143 struct whpx_register_set {
144     WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)];
145 };
146
147 struct whpx_vcpu {
148     WHV_EMULATOR_HANDLE emulator;
149     bool window_registered;
150     bool interruptable;
151     uint64_t tpr;
152     uint64_t apic_base;
153     bool interruption_pending;
154
155     /* Must be the last field as it may have a tail */
156     WHV_RUN_VP_EXIT_CONTEXT exit_ctx;
157 };
158
159 static bool whpx_allowed;
160 static bool whp_dispatch_initialized;
161 static HMODULE hWinHvPlatform, hWinHvEmulation;
162
163 struct whpx_state whpx_global;
164 struct WHPDispatch whp_dispatch;
165
166
167 /*
168  * VP support
169  */
170
171 static struct whpx_vcpu *get_whpx_vcpu(CPUState *cpu)
172 {
173     return (struct whpx_vcpu *)cpu->hax_vcpu;
174 }
175
176 static WHV_X64_SEGMENT_REGISTER whpx_seg_q2h(const SegmentCache *qs, int v86,
177                                              int r86)
178 {
179     WHV_X64_SEGMENT_REGISTER hs;
180     unsigned flags = qs->flags;
181
182     hs.Base = qs->base;
183     hs.Limit = qs->limit;
184     hs.Selector = qs->selector;
185
186     if (v86) {
187         hs.Attributes = 0;
188         hs.SegmentType = 3;
189         hs.Present = 1;
190         hs.DescriptorPrivilegeLevel = 3;
191         hs.NonSystemSegment = 1;
192
193     } else {
194         hs.Attributes = (flags >> DESC_TYPE_SHIFT);
195
196         if (r86) {
197             /* hs.Base &= 0xfffff; */
198         }
199     }
200
201     return hs;
202 }
203
204 static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs)
205 {
206     SegmentCache qs;
207
208     qs.base = hs->Base;
209     qs.limit = hs->Limit;
210     qs.selector = hs->Selector;
211
212     qs.flags = ((uint32_t)hs->Attributes) << DESC_TYPE_SHIFT;
213
214     return qs;
215 }
216
217 static void whpx_set_registers(CPUState *cpu)
218 {
219     struct whpx_state *whpx = &whpx_global;
220     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
221     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
222     X86CPU *x86_cpu = X86_CPU(cpu);
223     struct whpx_register_set vcxt;
224     HRESULT hr;
225     int idx;
226     int idx_next;
227     int i;
228     int v86, r86;
229
230     assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
231
232     memset(&vcxt, 0, sizeof(struct whpx_register_set));
233
234     v86 = (env->eflags & VM_MASK);
235     r86 = !(env->cr[0] & CR0_PE_MASK);
236
237     vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
238     vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
239
240     idx = 0;
241
242     /* Indexes for first 16 registers match between HV and QEMU definitions */
243     idx_next = 16;
244     for (idx = 0; idx < CPU_NB_REGS; idx += 1) {
245         vcxt.values[idx].Reg64 = (uint64_t)env->regs[idx];
246     }
247     idx = idx_next;
248
249     /* Same goes for RIP and RFLAGS */
250     assert(whpx_register_names[idx] == WHvX64RegisterRip);
251     vcxt.values[idx++].Reg64 = env->eip;
252
253     assert(whpx_register_names[idx] == WHvX64RegisterRflags);
254     vcxt.values[idx++].Reg64 = env->eflags;
255
256     /* Translate 6+4 segment registers. HV and QEMU order matches  */
257     assert(idx == WHvX64RegisterEs);
258     for (i = 0; i < 6; i += 1, idx += 1) {
259         vcxt.values[idx].Segment = whpx_seg_q2h(&env->segs[i], v86, r86);
260     }
261
262     assert(idx == WHvX64RegisterLdtr);
263     vcxt.values[idx++].Segment = whpx_seg_q2h(&env->ldt, 0, 0);
264
265     assert(idx == WHvX64RegisterTr);
266     vcxt.values[idx++].Segment = whpx_seg_q2h(&env->tr, 0, 0);
267
268     assert(idx == WHvX64RegisterIdtr);
269     vcxt.values[idx].Table.Base = env->idt.base;
270     vcxt.values[idx].Table.Limit = env->idt.limit;
271     idx += 1;
272
273     assert(idx == WHvX64RegisterGdtr);
274     vcxt.values[idx].Table.Base = env->gdt.base;
275     vcxt.values[idx].Table.Limit = env->gdt.limit;
276     idx += 1;
277
278     /* CR0, 2, 3, 4, 8 */
279     assert(whpx_register_names[idx] == WHvX64RegisterCr0);
280     vcxt.values[idx++].Reg64 = env->cr[0];
281     assert(whpx_register_names[idx] == WHvX64RegisterCr2);
282     vcxt.values[idx++].Reg64 = env->cr[2];
283     assert(whpx_register_names[idx] == WHvX64RegisterCr3);
284     vcxt.values[idx++].Reg64 = env->cr[3];
285     assert(whpx_register_names[idx] == WHvX64RegisterCr4);
286     vcxt.values[idx++].Reg64 = env->cr[4];
287     assert(whpx_register_names[idx] == WHvX64RegisterCr8);
288     vcxt.values[idx++].Reg64 = vcpu->tpr;
289
290     /* 8 Debug Registers - Skipped */
291
292     /* 16 XMM registers */
293     assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
294     idx_next = idx + 16;
295     for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
296         vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);
297         vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);
298     }
299     idx = idx_next;
300
301     /* 8 FP registers */
302     assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
303     for (i = 0; i < 8; i += 1, idx += 1) {
304         vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);
305         /* vcxt.values[idx].Fp.AsUINT128.High64 =
306                env->fpregs[i].mmx.MMX_Q(1);
307         */
308     }
309
310     /* FP control status register */
311     assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
312     vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;
313     vcxt.values[idx].FpControlStatus.FpStatus =
314         (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
315     vcxt.values[idx].FpControlStatus.FpTag = 0;
316     for (i = 0; i < 8; ++i) {
317         vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;
318     }
319     vcxt.values[idx].FpControlStatus.Reserved = 0;
320     vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;
321     vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;
322     idx += 1;
323
324     /* XMM control status register */
325     assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
326     vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;
327     vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;
328     vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;
329     idx += 1;
330
331     /* MSRs */
332     assert(whpx_register_names[idx] == WHvX64RegisterTsc);
333     vcxt.values[idx++].Reg64 = env->tsc;
334     assert(whpx_register_names[idx] == WHvX64RegisterEfer);
335     vcxt.values[idx++].Reg64 = env->efer;
336 #ifdef TARGET_X86_64
337     assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
338     vcxt.values[idx++].Reg64 = env->kernelgsbase;
339 #endif
340
341     assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
342     vcxt.values[idx++].Reg64 = vcpu->apic_base;
343
344     /* WHvX64RegisterPat - Skipped */
345
346     assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
347     vcxt.values[idx++].Reg64 = env->sysenter_cs;
348     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
349     vcxt.values[idx++].Reg64 = env->sysenter_eip;
350     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
351     vcxt.values[idx++].Reg64 = env->sysenter_esp;
352     assert(whpx_register_names[idx] == WHvX64RegisterStar);
353     vcxt.values[idx++].Reg64 = env->star;
354 #ifdef TARGET_X86_64
355     assert(whpx_register_names[idx] == WHvX64RegisterLstar);
356     vcxt.values[idx++].Reg64 = env->lstar;
357     assert(whpx_register_names[idx] == WHvX64RegisterCstar);
358     vcxt.values[idx++].Reg64 = env->cstar;
359     assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
360     vcxt.values[idx++].Reg64 = env->fmask;
361 #endif
362
363     /* Interrupt / Event Registers - Skipped */
364
365     assert(idx == RTL_NUMBER_OF(whpx_register_names));
366
367     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
368         whpx->partition, cpu->cpu_index,
369         whpx_register_names,
370         RTL_NUMBER_OF(whpx_register_names),
371         &vcxt.values[0]);
372
373     if (FAILED(hr)) {
374         error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
375                      hr);
376     }
377
378     return;
379 }
380
381 static void whpx_get_registers(CPUState *cpu)
382 {
383     struct whpx_state *whpx = &whpx_global;
384     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
385     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
386     X86CPU *x86_cpu = X86_CPU(cpu);
387     struct whpx_register_set vcxt;
388     uint64_t tpr, apic_base;
389     HRESULT hr;
390     int idx;
391     int idx_next;
392     int i;
393
394     assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
395
396     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
397         whpx->partition, cpu->cpu_index,
398         whpx_register_names,
399         RTL_NUMBER_OF(whpx_register_names),
400         &vcxt.values[0]);
401     if (FAILED(hr)) {
402         error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
403                      hr);
404     }
405
406     idx = 0;
407
408     /* Indexes for first 16 registers match between HV and QEMU definitions */
409     idx_next = 16;
410     for (idx = 0; idx < CPU_NB_REGS; idx += 1) {
411         env->regs[idx] = vcxt.values[idx].Reg64;
412     }
413     idx = idx_next;
414
415     /* Same goes for RIP and RFLAGS */
416     assert(whpx_register_names[idx] == WHvX64RegisterRip);
417     env->eip = vcxt.values[idx++].Reg64;
418     assert(whpx_register_names[idx] == WHvX64RegisterRflags);
419     env->eflags = vcxt.values[idx++].Reg64;
420
421     /* Translate 6+4 segment registers. HV and QEMU order matches  */
422     assert(idx == WHvX64RegisterEs);
423     for (i = 0; i < 6; i += 1, idx += 1) {
424         env->segs[i] = whpx_seg_h2q(&vcxt.values[idx].Segment);
425     }
426
427     assert(idx == WHvX64RegisterLdtr);
428     env->ldt = whpx_seg_h2q(&vcxt.values[idx++].Segment);
429     assert(idx == WHvX64RegisterTr);
430     env->tr = whpx_seg_h2q(&vcxt.values[idx++].Segment);
431     assert(idx == WHvX64RegisterIdtr);
432     env->idt.base = vcxt.values[idx].Table.Base;
433     env->idt.limit = vcxt.values[idx].Table.Limit;
434     idx += 1;
435     assert(idx == WHvX64RegisterGdtr);
436     env->gdt.base = vcxt.values[idx].Table.Base;
437     env->gdt.limit = vcxt.values[idx].Table.Limit;
438     idx += 1;
439
440     /* CR0, 2, 3, 4, 8 */
441     assert(whpx_register_names[idx] == WHvX64RegisterCr0);
442     env->cr[0] = vcxt.values[idx++].Reg64;
443     assert(whpx_register_names[idx] == WHvX64RegisterCr2);
444     env->cr[2] = vcxt.values[idx++].Reg64;
445     assert(whpx_register_names[idx] == WHvX64RegisterCr3);
446     env->cr[3] = vcxt.values[idx++].Reg64;
447     assert(whpx_register_names[idx] == WHvX64RegisterCr4);
448     env->cr[4] = vcxt.values[idx++].Reg64;
449     assert(whpx_register_names[idx] == WHvX64RegisterCr8);
450     tpr = vcxt.values[idx++].Reg64;
451     if (tpr != vcpu->tpr) {
452         vcpu->tpr = tpr;
453         cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
454     }
455
456     /* 8 Debug Registers - Skipped */
457
458     /* 16 XMM registers */
459     assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
460     idx_next = idx + 16;
461     for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
462         env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;
463         env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;
464     }
465     idx = idx_next;
466
467     /* 8 FP registers */
468     assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
469     for (i = 0; i < 8; i += 1, idx += 1) {
470         env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;
471         /* env->fpregs[i].mmx.MMX_Q(1) =
472                vcxt.values[idx].Fp.AsUINT128.High64;
473         */
474     }
475
476     /* FP control status register */
477     assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
478     env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;
479     env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;
480     env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;
481     for (i = 0; i < 8; ++i) {
482         env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);
483     }
484     env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;
485     env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;
486     idx += 1;
487
488     /* XMM control status register */
489     assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
490     env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
491     idx += 1;
492
493     /* MSRs */
494     assert(whpx_register_names[idx] == WHvX64RegisterTsc);
495     env->tsc = vcxt.values[idx++].Reg64;
496     assert(whpx_register_names[idx] == WHvX64RegisterEfer);
497     env->efer = vcxt.values[idx++].Reg64;
498 #ifdef TARGET_X86_64
499     assert(whpx_register_names[idx] == WHvX64RegisterKernelGsBase);
500     env->kernelgsbase = vcxt.values[idx++].Reg64;
501 #endif
502
503     assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
504     apic_base = vcxt.values[idx++].Reg64;
505     if (apic_base != vcpu->apic_base) {
506         vcpu->apic_base = apic_base;
507         cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base);
508     }
509
510     /* WHvX64RegisterPat - Skipped */
511
512     assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
513     env->sysenter_cs = vcxt.values[idx++].Reg64;;
514     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEip);
515     env->sysenter_eip = vcxt.values[idx++].Reg64;
516     assert(whpx_register_names[idx] == WHvX64RegisterSysenterEsp);
517     env->sysenter_esp = vcxt.values[idx++].Reg64;
518     assert(whpx_register_names[idx] == WHvX64RegisterStar);
519     env->star = vcxt.values[idx++].Reg64;
520 #ifdef TARGET_X86_64
521     assert(whpx_register_names[idx] == WHvX64RegisterLstar);
522     env->lstar = vcxt.values[idx++].Reg64;
523     assert(whpx_register_names[idx] == WHvX64RegisterCstar);
524     env->cstar = vcxt.values[idx++].Reg64;
525     assert(whpx_register_names[idx] == WHvX64RegisterSfmask);
526     env->fmask = vcxt.values[idx++].Reg64;
527 #endif
528
529     /* Interrupt / Event Registers - Skipped */
530
531     assert(idx == RTL_NUMBER_OF(whpx_register_names));
532
533     return;
534 }
535
536 static HRESULT CALLBACK whpx_emu_ioport_callback(
537     void *ctx,
538     WHV_EMULATOR_IO_ACCESS_INFO *IoAccess)
539 {
540     MemTxAttrs attrs = { 0 };
541     address_space_rw(&address_space_io, IoAccess->Port, attrs,
542                      (uint8_t *)&IoAccess->Data, IoAccess->AccessSize,
543                      IoAccess->Direction);
544     return S_OK;
545 }
546
547 static HRESULT CALLBACK whpx_emu_mmio_callback(
548     void *ctx,
549     WHV_EMULATOR_MEMORY_ACCESS_INFO *ma)
550 {
551     cpu_physical_memory_rw(ma->GpaAddress, ma->Data, ma->AccessSize,
552                            ma->Direction);
553     return S_OK;
554 }
555
556 static HRESULT CALLBACK whpx_emu_getreg_callback(
557     void *ctx,
558     const WHV_REGISTER_NAME *RegisterNames,
559     UINT32 RegisterCount,
560     WHV_REGISTER_VALUE *RegisterValues)
561 {
562     HRESULT hr;
563     struct whpx_state *whpx = &whpx_global;
564     CPUState *cpu = (CPUState *)ctx;
565
566     hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
567         whpx->partition, cpu->cpu_index,
568         RegisterNames, RegisterCount,
569         RegisterValues);
570     if (FAILED(hr)) {
571         error_report("WHPX: Failed to get virtual processor registers,"
572                      " hr=%08lx", hr);
573     }
574
575     return hr;
576 }
577
578 static HRESULT CALLBACK whpx_emu_setreg_callback(
579     void *ctx,
580     const WHV_REGISTER_NAME *RegisterNames,
581     UINT32 RegisterCount,
582     const WHV_REGISTER_VALUE *RegisterValues)
583 {
584     HRESULT hr;
585     struct whpx_state *whpx = &whpx_global;
586     CPUState *cpu = (CPUState *)ctx;
587
588     hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
589         whpx->partition, cpu->cpu_index,
590         RegisterNames, RegisterCount,
591         RegisterValues);
592     if (FAILED(hr)) {
593         error_report("WHPX: Failed to set virtual processor registers,"
594                      " hr=%08lx", hr);
595     }
596
597     /*
598      * The emulator just successfully wrote the register state. We clear the
599      * dirty state so we avoid the double write on resume of the VP.
600      */
601     cpu->vcpu_dirty = false;
602
603     return hr;
604 }
605
606 static HRESULT CALLBACK whpx_emu_translate_callback(
607     void *ctx,
608     WHV_GUEST_VIRTUAL_ADDRESS Gva,
609     WHV_TRANSLATE_GVA_FLAGS TranslateFlags,
610     WHV_TRANSLATE_GVA_RESULT_CODE *TranslationResult,
611     WHV_GUEST_PHYSICAL_ADDRESS *Gpa)
612 {
613     HRESULT hr;
614     struct whpx_state *whpx = &whpx_global;
615     CPUState *cpu = (CPUState *)ctx;
616     WHV_TRANSLATE_GVA_RESULT res;
617
618     hr = whp_dispatch.WHvTranslateGva(whpx->partition, cpu->cpu_index,
619                                       Gva, TranslateFlags, &res, Gpa);
620     if (FAILED(hr)) {
621         error_report("WHPX: Failed to translate GVA, hr=%08lx", hr);
622     } else {
623         *TranslationResult = res.ResultCode;
624     }
625
626     return hr;
627 }
628
629 static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks = {
630     .Size = sizeof(WHV_EMULATOR_CALLBACKS),
631     .WHvEmulatorIoPortCallback = whpx_emu_ioport_callback,
632     .WHvEmulatorMemoryCallback = whpx_emu_mmio_callback,
633     .WHvEmulatorGetVirtualProcessorRegisters = whpx_emu_getreg_callback,
634     .WHvEmulatorSetVirtualProcessorRegisters = whpx_emu_setreg_callback,
635     .WHvEmulatorTranslateGvaPage = whpx_emu_translate_callback,
636 };
637
638 static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx)
639 {
640     HRESULT hr;
641     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
642     WHV_EMULATOR_STATUS emu_status;
643
644     hr = whp_dispatch.WHvEmulatorTryMmioEmulation(
645         vcpu->emulator, cpu,
646         &vcpu->exit_ctx.VpContext, ctx,
647         &emu_status);
648     if (FAILED(hr)) {
649         error_report("WHPX: Failed to parse MMIO access, hr=%08lx", hr);
650         return -1;
651     }
652
653     if (!emu_status.EmulationSuccessful) {
654         error_report("WHPX: Failed to emulate MMIO access with"
655                      " EmulatorReturnStatus: %u", emu_status.AsUINT32);
656         return -1;
657     }
658
659     return 0;
660 }
661
662 static int whpx_handle_portio(CPUState *cpu,
663                               WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx)
664 {
665     HRESULT hr;
666     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
667     WHV_EMULATOR_STATUS emu_status;
668
669     hr = whp_dispatch.WHvEmulatorTryIoEmulation(
670         vcpu->emulator, cpu,
671         &vcpu->exit_ctx.VpContext, ctx,
672         &emu_status);
673     if (FAILED(hr)) {
674         error_report("WHPX: Failed to parse PortIO access, hr=%08lx", hr);
675         return -1;
676     }
677
678     if (!emu_status.EmulationSuccessful) {
679         error_report("WHPX: Failed to emulate PortIO access with"
680                      " EmulatorReturnStatus: %u", emu_status.AsUINT32);
681         return -1;
682     }
683
684     return 0;
685 }
686
687 static int whpx_handle_halt(CPUState *cpu)
688 {
689     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
690     int ret = 0;
691
692     qemu_mutex_lock_iothread();
693     if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
694           (env->eflags & IF_MASK)) &&
695         !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
696         cpu->exception_index = EXCP_HLT;
697         cpu->halted = true;
698         ret = 1;
699     }
700     qemu_mutex_unlock_iothread();
701
702     return ret;
703 }
704
705 static void whpx_vcpu_pre_run(CPUState *cpu)
706 {
707     HRESULT hr;
708     struct whpx_state *whpx = &whpx_global;
709     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
710     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
711     X86CPU *x86_cpu = X86_CPU(cpu);
712     int irq;
713     uint8_t tpr;
714     WHV_X64_PENDING_INTERRUPTION_REGISTER new_int;
715     UINT32 reg_count = 0;
716     WHV_REGISTER_VALUE reg_values[3];
717     WHV_REGISTER_NAME reg_names[3];
718
719     memset(&new_int, 0, sizeof(new_int));
720     memset(reg_values, 0, sizeof(reg_values));
721
722     qemu_mutex_lock_iothread();
723
724     /* Inject NMI */
725     if (!vcpu->interruption_pending &&
726         cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) {
727         if (cpu->interrupt_request & CPU_INTERRUPT_NMI) {
728             cpu->interrupt_request &= ~CPU_INTERRUPT_NMI;
729             vcpu->interruptable = false;
730             new_int.InterruptionType = WHvX64PendingNmi;
731             new_int.InterruptionPending = 1;
732             new_int.InterruptionVector = 2;
733         }
734         if (cpu->interrupt_request & CPU_INTERRUPT_SMI) {
735             cpu->interrupt_request &= ~CPU_INTERRUPT_SMI;
736         }
737     }
738
739     /*
740      * Force the VCPU out of its inner loop to process any INIT requests or
741      * commit pending TPR access.
742      */
743     if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) {
744         if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
745             !(env->hflags & HF_SMM_MASK)) {
746             cpu->exit_request = 1;
747         }
748         if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
749             cpu->exit_request = 1;
750         }
751     }
752
753     /* Get pending hard interruption or replay one that was overwritten */
754     if (!vcpu->interruption_pending &&
755         vcpu->interruptable && (env->eflags & IF_MASK)) {
756         assert(!new_int.InterruptionPending);
757         if (cpu->interrupt_request & CPU_INTERRUPT_HARD) {
758             cpu->interrupt_request &= ~CPU_INTERRUPT_HARD;
759             irq = cpu_get_pic_interrupt(env);
760             if (irq >= 0) {
761                 new_int.InterruptionType = WHvX64PendingInterrupt;
762                 new_int.InterruptionPending = 1;
763                 new_int.InterruptionVector = irq;
764             }
765         }
766     }
767
768     /* Setup interrupt state if new one was prepared */
769     if (new_int.InterruptionPending) {
770         reg_values[reg_count].PendingInterruption = new_int;
771         reg_names[reg_count] = WHvRegisterPendingInterruption;
772         reg_count += 1;
773     }
774
775     /* Sync the TPR to the CR8 if was modified during the intercept */
776     tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
777     if (tpr != vcpu->tpr) {
778         vcpu->tpr = tpr;
779         reg_values[reg_count].Reg64 = tpr;
780         cpu->exit_request = 1;
781         reg_names[reg_count] = WHvX64RegisterCr8;
782         reg_count += 1;
783     }
784
785     /* Update the state of the interrupt delivery notification */
786     if (!vcpu->window_registered &&
787         cpu->interrupt_request & CPU_INTERRUPT_HARD) {
788         reg_values[reg_count].DeliverabilityNotifications.InterruptNotification
789             = 1;
790         vcpu->window_registered = 1;
791         reg_names[reg_count] = WHvX64RegisterDeliverabilityNotifications;
792         reg_count += 1;
793     }
794
795     qemu_mutex_unlock_iothread();
796
797     if (reg_count) {
798         hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
799             whpx->partition, cpu->cpu_index,
800             reg_names, reg_count, reg_values);
801         if (FAILED(hr)) {
802             error_report("WHPX: Failed to set interrupt state registers,"
803                          " hr=%08lx", hr);
804         }
805     }
806
807     return;
808 }
809
810 static void whpx_vcpu_post_run(CPUState *cpu)
811 {
812     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
813     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
814     X86CPU *x86_cpu = X86_CPU(cpu);
815
816     env->eflags = vcpu->exit_ctx.VpContext.Rflags;
817
818     uint64_t tpr = vcpu->exit_ctx.VpContext.Cr8;
819     if (vcpu->tpr != tpr) {
820         vcpu->tpr = tpr;
821         qemu_mutex_lock_iothread();
822         cpu_set_apic_tpr(x86_cpu->apic_state, vcpu->tpr);
823         qemu_mutex_unlock_iothread();
824     }
825
826     vcpu->interruption_pending =
827         vcpu->exit_ctx.VpContext.ExecutionState.InterruptionPending;
828
829     vcpu->interruptable =
830         !vcpu->exit_ctx.VpContext.ExecutionState.InterruptShadow;
831
832     return;
833 }
834
835 static void whpx_vcpu_process_async_events(CPUState *cpu)
836 {
837     struct CPUX86State *env = (CPUArchState *)(cpu->env_ptr);
838     X86CPU *x86_cpu = X86_CPU(cpu);
839     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
840
841     if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) &&
842         !(env->hflags & HF_SMM_MASK)) {
843
844         do_cpu_init(x86_cpu);
845         cpu->vcpu_dirty = true;
846         vcpu->interruptable = true;
847     }
848
849     if (cpu->interrupt_request & CPU_INTERRUPT_POLL) {
850         cpu->interrupt_request &= ~CPU_INTERRUPT_POLL;
851         apic_poll_irq(x86_cpu->apic_state);
852     }
853
854     if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) &&
855          (env->eflags & IF_MASK)) ||
856         (cpu->interrupt_request & CPU_INTERRUPT_NMI)) {
857         cpu->halted = false;
858     }
859
860     if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) {
861         if (!cpu->vcpu_dirty) {
862             whpx_get_registers(cpu);
863         }
864         do_cpu_sipi(x86_cpu);
865     }
866
867     if (cpu->interrupt_request & CPU_INTERRUPT_TPR) {
868         cpu->interrupt_request &= ~CPU_INTERRUPT_TPR;
869         if (!cpu->vcpu_dirty) {
870             whpx_get_registers(cpu);
871         }
872         apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip,
873                                       env->tpr_access_type);
874     }
875
876     return;
877 }
878
879 static int whpx_vcpu_run(CPUState *cpu)
880 {
881     HRESULT hr;
882     struct whpx_state *whpx = &whpx_global;
883     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
884     int ret;
885
886     whpx_vcpu_process_async_events(cpu);
887     if (cpu->halted) {
888         cpu->exception_index = EXCP_HLT;
889         atomic_set(&cpu->exit_request, false);
890         return 0;
891     }
892
893     qemu_mutex_unlock_iothread();
894     cpu_exec_start(cpu);
895
896     do {
897         if (cpu->vcpu_dirty) {
898             whpx_set_registers(cpu);
899             cpu->vcpu_dirty = false;
900         }
901
902         whpx_vcpu_pre_run(cpu);
903
904         if (atomic_read(&cpu->exit_request)) {
905             whpx_vcpu_kick(cpu);
906         }
907
908         hr = whp_dispatch.WHvRunVirtualProcessor(
909             whpx->partition, cpu->cpu_index,
910             &vcpu->exit_ctx, sizeof(vcpu->exit_ctx));
911
912         if (FAILED(hr)) {
913             error_report("WHPX: Failed to exec a virtual processor,"
914                          " hr=%08lx", hr);
915             ret = -1;
916             break;
917         }
918
919         whpx_vcpu_post_run(cpu);
920
921         switch (vcpu->exit_ctx.ExitReason) {
922         case WHvRunVpExitReasonMemoryAccess:
923             ret = whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess);
924             break;
925
926         case WHvRunVpExitReasonX64IoPortAccess:
927             ret = whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess);
928             break;
929
930         case WHvRunVpExitReasonX64InterruptWindow:
931             vcpu->window_registered = 0;
932             ret = 0;
933             break;
934
935         case WHvRunVpExitReasonX64Halt:
936             ret = whpx_handle_halt(cpu);
937             break;
938
939         case WHvRunVpExitReasonCanceled:
940             cpu->exception_index = EXCP_INTERRUPT;
941             ret = 1;
942             break;
943
944         case WHvRunVpExitReasonX64MsrAccess: {
945             WHV_REGISTER_VALUE reg_values[3] = {0};
946             WHV_REGISTER_NAME reg_names[3];
947             UINT32 reg_count;
948
949             reg_names[0] = WHvX64RegisterRip;
950             reg_names[1] = WHvX64RegisterRax;
951             reg_names[2] = WHvX64RegisterRdx;
952
953             reg_values[0].Reg64 =
954                 vcpu->exit_ctx.VpContext.Rip +
955                 vcpu->exit_ctx.VpContext.InstructionLength;
956
957             /*
958              * For all unsupported MSR access we:
959              *     ignore writes
960              *     return 0 on read.
961              */
962             reg_count = vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite ?
963                         1 : 3;
964
965             hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
966                 whpx->partition,
967                 cpu->cpu_index,
968                 reg_names, reg_count,
969                 reg_values);
970
971             if (FAILED(hr)) {
972                 error_report("WHPX: Failed to set MsrAccess state "
973                              " registers, hr=%08lx", hr);
974             }
975             ret = 0;
976             break;
977         }
978         case WHvRunVpExitReasonX64Cpuid: {
979             WHV_REGISTER_VALUE reg_values[5];
980             WHV_REGISTER_NAME reg_names[5];
981             UINT32 reg_count = 5;
982             UINT64 rip, rax, rcx, rdx, rbx;
983
984             memset(reg_values, 0, sizeof(reg_values));
985
986             rip = vcpu->exit_ctx.VpContext.Rip +
987                   vcpu->exit_ctx.VpContext.InstructionLength;
988             switch (vcpu->exit_ctx.CpuidAccess.Rax) {
989             case 1:
990                 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
991                 /* Advertise that we are running on a hypervisor */
992                 rcx =
993                     vcpu->exit_ctx.CpuidAccess.DefaultResultRcx |
994                     CPUID_EXT_HYPERVISOR;
995
996                 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
997                 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
998                 break;
999             case 0x80000001:
1000                 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
1001                 /* Remove any support of OSVW */
1002                 rcx =
1003                     vcpu->exit_ctx.CpuidAccess.DefaultResultRcx &
1004                     ~CPUID_EXT3_OSVW;
1005
1006                 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
1007                 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
1008                 break;
1009             default:
1010                 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
1011                 rcx = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx;
1012                 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
1013                 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
1014             }
1015
1016             reg_names[0] = WHvX64RegisterRip;
1017             reg_names[1] = WHvX64RegisterRax;
1018             reg_names[2] = WHvX64RegisterRcx;
1019             reg_names[3] = WHvX64RegisterRdx;
1020             reg_names[4] = WHvX64RegisterRbx;
1021
1022             reg_values[0].Reg64 = rip;
1023             reg_values[1].Reg64 = rax;
1024             reg_values[2].Reg64 = rcx;
1025             reg_values[3].Reg64 = rdx;
1026             reg_values[4].Reg64 = rbx;
1027
1028             hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
1029                 whpx->partition, cpu->cpu_index,
1030                 reg_names,
1031                 reg_count,
1032                 reg_values);
1033
1034             if (FAILED(hr)) {
1035                 error_report("WHPX: Failed to set CpuidAccess state registers,"
1036                              " hr=%08lx", hr);
1037             }
1038             ret = 0;
1039             break;
1040         }
1041         case WHvRunVpExitReasonNone:
1042         case WHvRunVpExitReasonUnrecoverableException:
1043         case WHvRunVpExitReasonInvalidVpRegisterValue:
1044         case WHvRunVpExitReasonUnsupportedFeature:
1045         case WHvRunVpExitReasonException:
1046         default:
1047             error_report("WHPX: Unexpected VP exit code %d",
1048                          vcpu->exit_ctx.ExitReason);
1049             whpx_get_registers(cpu);
1050             qemu_mutex_lock_iothread();
1051             qemu_system_guest_panicked(cpu_get_crash_info(cpu));
1052             qemu_mutex_unlock_iothread();
1053             break;
1054         }
1055
1056     } while (!ret);
1057
1058     cpu_exec_end(cpu);
1059     qemu_mutex_lock_iothread();
1060     current_cpu = cpu;
1061
1062     atomic_set(&cpu->exit_request, false);
1063
1064     return ret < 0;
1065 }
1066
1067 static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
1068 {
1069     whpx_get_registers(cpu);
1070     cpu->vcpu_dirty = true;
1071 }
1072
1073 static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu,
1074                                                run_on_cpu_data arg)
1075 {
1076     whpx_set_registers(cpu);
1077     cpu->vcpu_dirty = false;
1078 }
1079
1080 static void do_whpx_cpu_synchronize_post_init(CPUState *cpu,
1081                                               run_on_cpu_data arg)
1082 {
1083     whpx_set_registers(cpu);
1084     cpu->vcpu_dirty = false;
1085 }
1086
1087 static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu,
1088                                                run_on_cpu_data arg)
1089 {
1090     cpu->vcpu_dirty = true;
1091 }
1092
1093 /*
1094  * CPU support.
1095  */
1096
1097 void whpx_cpu_synchronize_state(CPUState *cpu)
1098 {
1099     if (!cpu->vcpu_dirty) {
1100         run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL);
1101     }
1102 }
1103
1104 void whpx_cpu_synchronize_post_reset(CPUState *cpu)
1105 {
1106     run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
1107 }
1108
1109 void whpx_cpu_synchronize_post_init(CPUState *cpu)
1110 {
1111     run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
1112 }
1113
1114 void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
1115 {
1116     run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
1117 }
1118
1119 /*
1120  * Vcpu support.
1121  */
1122
1123 static Error *whpx_migration_blocker;
1124
1125 int whpx_init_vcpu(CPUState *cpu)
1126 {
1127     HRESULT hr;
1128     struct whpx_state *whpx = &whpx_global;
1129     struct whpx_vcpu *vcpu;
1130     Error *local_error = NULL;
1131
1132     /* Add migration blockers for all unsupported features of the
1133      * Windows Hypervisor Platform
1134      */
1135     if (whpx_migration_blocker == NULL) {
1136         error_setg(&whpx_migration_blocker,
1137                "State blocked due to non-migratable CPUID feature support,"
1138                "dirty memory tracking support, and XSAVE/XRSTOR support");
1139
1140         (void)migrate_add_blocker(whpx_migration_blocker, &local_error);
1141         if (local_error) {
1142             error_report_err(local_error);
1143             migrate_del_blocker(whpx_migration_blocker);
1144             error_free(whpx_migration_blocker);
1145             return -EINVAL;
1146         }
1147     }
1148
1149     vcpu = g_malloc0(sizeof(struct whpx_vcpu));
1150
1151     if (!vcpu) {
1152         error_report("WHPX: Failed to allocte VCPU context.");
1153         return -ENOMEM;
1154     }
1155
1156     hr = whp_dispatch.WHvEmulatorCreateEmulator(
1157         &whpx_emu_callbacks,
1158         &vcpu->emulator);
1159     if (FAILED(hr)) {
1160         error_report("WHPX: Failed to setup instruction completion support,"
1161                      " hr=%08lx", hr);
1162         g_free(vcpu);
1163         return -EINVAL;
1164     }
1165
1166     hr = whp_dispatch.WHvCreateVirtualProcessor(
1167         whpx->partition, cpu->cpu_index, 0);
1168     if (FAILED(hr)) {
1169         error_report("WHPX: Failed to create a virtual processor,"
1170                      " hr=%08lx", hr);
1171         whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1172         g_free(vcpu);
1173         return -EINVAL;
1174     }
1175
1176     vcpu->interruptable = true;
1177
1178     cpu->vcpu_dirty = true;
1179     cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu;
1180
1181     return 0;
1182 }
1183
1184 int whpx_vcpu_exec(CPUState *cpu)
1185 {
1186     int ret;
1187     int fatal;
1188
1189     for (;;) {
1190         if (cpu->exception_index >= EXCP_INTERRUPT) {
1191             ret = cpu->exception_index;
1192             cpu->exception_index = -1;
1193             break;
1194         }
1195
1196         fatal = whpx_vcpu_run(cpu);
1197
1198         if (fatal) {
1199             error_report("WHPX: Failed to exec a virtual processor");
1200             abort();
1201         }
1202     }
1203
1204     return ret;
1205 }
1206
1207 void whpx_destroy_vcpu(CPUState *cpu)
1208 {
1209     struct whpx_state *whpx = &whpx_global;
1210     struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
1211
1212     whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
1213     whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1214     g_free(cpu->hax_vcpu);
1215     return;
1216 }
1217
1218 void whpx_vcpu_kick(CPUState *cpu)
1219 {
1220     struct whpx_state *whpx = &whpx_global;
1221     whp_dispatch.WHvCancelRunVirtualProcessor(
1222         whpx->partition, cpu->cpu_index, 0);
1223 }
1224
1225 /*
1226  * Memory support.
1227  */
1228
1229 static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
1230                                 void *host_va, int add, int rom,
1231                                 const char *name)
1232 {
1233     struct whpx_state *whpx = &whpx_global;
1234     HRESULT hr;
1235
1236     /*
1237     if (add) {
1238         printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
1239                (void*)start_pa, (void*)size, host_va,
1240                (rom ? "ROM" : "RAM"), name);
1241     } else {
1242         printf("WHPX: DEL PA:%p Size:%p, Host:%p,      '%s'\n",
1243                (void*)start_pa, (void*)size, host_va, name);
1244     }
1245     */
1246
1247     if (add) {
1248         hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
1249                                          host_va,
1250                                          start_pa,
1251                                          size,
1252                                          (WHvMapGpaRangeFlagRead |
1253                                           WHvMapGpaRangeFlagExecute |
1254                                           (rom ? 0 : WHvMapGpaRangeFlagWrite)));
1255     } else {
1256         hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
1257                                            start_pa,
1258                                            size);
1259     }
1260
1261     if (FAILED(hr)) {
1262         error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
1263                      " Host:%p, hr=%08lx",
1264                      (add ? "MAP" : "UNMAP"), name,
1265                      (void *)(uintptr_t)start_pa, (void *)size, host_va, hr);
1266     }
1267 }
1268
1269 static void whpx_process_section(MemoryRegionSection *section, int add)
1270 {
1271     MemoryRegion *mr = section->mr;
1272     hwaddr start_pa = section->offset_within_address_space;
1273     ram_addr_t size = int128_get64(section->size);
1274     unsigned int delta;
1275     uint64_t host_va;
1276
1277     if (!memory_region_is_ram(mr)) {
1278         return;
1279     }
1280
1281     delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
1282     delta &= ~qemu_real_host_page_mask;
1283     if (delta > size) {
1284         return;
1285     }
1286     start_pa += delta;
1287     size -= delta;
1288     size &= qemu_real_host_page_mask;
1289     if (!size || (start_pa & ~qemu_real_host_page_mask)) {
1290         return;
1291     }
1292
1293     host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
1294             + section->offset_within_region + delta;
1295
1296     whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add,
1297                         memory_region_is_rom(mr), mr->name);
1298 }
1299
1300 static void whpx_region_add(MemoryListener *listener,
1301                            MemoryRegionSection *section)
1302 {
1303     memory_region_ref(section->mr);
1304     whpx_process_section(section, 1);
1305 }
1306
1307 static void whpx_region_del(MemoryListener *listener,
1308                            MemoryRegionSection *section)
1309 {
1310     whpx_process_section(section, 0);
1311     memory_region_unref(section->mr);
1312 }
1313
1314 static void whpx_transaction_begin(MemoryListener *listener)
1315 {
1316 }
1317
1318 static void whpx_transaction_commit(MemoryListener *listener)
1319 {
1320 }
1321
1322 static void whpx_log_sync(MemoryListener *listener,
1323                          MemoryRegionSection *section)
1324 {
1325     MemoryRegion *mr = section->mr;
1326
1327     if (!memory_region_is_ram(mr)) {
1328         return;
1329     }
1330
1331     memory_region_set_dirty(mr, 0, int128_get64(section->size));
1332 }
1333
1334 static MemoryListener whpx_memory_listener = {
1335     .begin = whpx_transaction_begin,
1336     .commit = whpx_transaction_commit,
1337     .region_add = whpx_region_add,
1338     .region_del = whpx_region_del,
1339     .log_sync = whpx_log_sync,
1340     .priority = 10,
1341 };
1342
1343 static void whpx_memory_init(void)
1344 {
1345     memory_listener_register(&whpx_memory_listener, &address_space_memory);
1346 }
1347
1348 static void whpx_handle_interrupt(CPUState *cpu, int mask)
1349 {
1350     cpu->interrupt_request |= mask;
1351
1352     if (!qemu_cpu_is_self(cpu)) {
1353         qemu_cpu_kick(cpu);
1354     }
1355 }
1356
1357 /*
1358  * Partition support
1359  */
1360
1361 static int whpx_accel_init(MachineState *ms)
1362 {
1363     struct whpx_state *whpx;
1364     int ret;
1365     HRESULT hr;
1366     WHV_CAPABILITY whpx_cap;
1367     UINT32 whpx_cap_size;
1368     WHV_PARTITION_PROPERTY prop;
1369
1370     whpx = &whpx_global;
1371
1372     if (!init_whp_dispatch()) {
1373         ret = -ENOSYS;
1374         goto error;
1375     }
1376
1377     memset(whpx, 0, sizeof(struct whpx_state));
1378     whpx->mem_quota = ms->ram_size;
1379
1380     hr = whp_dispatch.WHvGetCapability(
1381         WHvCapabilityCodeHypervisorPresent, &whpx_cap,
1382         sizeof(whpx_cap), &whpx_cap_size);
1383     if (FAILED(hr) || !whpx_cap.HypervisorPresent) {
1384         error_report("WHPX: No accelerator found, hr=%08lx", hr);
1385         ret = -ENOSPC;
1386         goto error;
1387     }
1388
1389     hr = whp_dispatch.WHvCreatePartition(&whpx->partition);
1390     if (FAILED(hr)) {
1391         error_report("WHPX: Failed to create partition, hr=%08lx", hr);
1392         ret = -EINVAL;
1393         goto error;
1394     }
1395
1396     memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1397     prop.ProcessorCount = ms->smp.cpus;
1398     hr = whp_dispatch.WHvSetPartitionProperty(
1399         whpx->partition,
1400         WHvPartitionPropertyCodeProcessorCount,
1401         &prop,
1402         sizeof(WHV_PARTITION_PROPERTY));
1403
1404     if (FAILED(hr)) {
1405         error_report("WHPX: Failed to set partition core count to %d,"
1406                      " hr=%08lx", ms->smp.cores, hr);
1407         ret = -EINVAL;
1408         goto error;
1409     }
1410
1411     memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1412     prop.ExtendedVmExits.X64MsrExit = 1;
1413     prop.ExtendedVmExits.X64CpuidExit = 1;
1414     hr = whp_dispatch.WHvSetPartitionProperty(
1415         whpx->partition,
1416         WHvPartitionPropertyCodeExtendedVmExits,
1417         &prop,
1418         sizeof(WHV_PARTITION_PROPERTY));
1419
1420     if (FAILED(hr)) {
1421         error_report("WHPX: Failed to enable partition extended X64MsrExit and"
1422                      " X64CpuidExit hr=%08lx", hr);
1423         ret = -EINVAL;
1424         goto error;
1425     }
1426
1427     UINT32 cpuidExitList[] = {1, 0x80000001};
1428     hr = whp_dispatch.WHvSetPartitionProperty(
1429         whpx->partition,
1430         WHvPartitionPropertyCodeCpuidExitList,
1431         cpuidExitList,
1432         RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
1433
1434     if (FAILED(hr)) {
1435         error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1436                      hr);
1437         ret = -EINVAL;
1438         goto error;
1439     }
1440
1441     hr = whp_dispatch.WHvSetupPartition(whpx->partition);
1442     if (FAILED(hr)) {
1443         error_report("WHPX: Failed to setup partition, hr=%08lx", hr);
1444         ret = -EINVAL;
1445         goto error;
1446     }
1447
1448     whpx_memory_init();
1449
1450     cpu_interrupt_handler = whpx_handle_interrupt;
1451
1452     printf("Windows Hypervisor Platform accelerator is operational\n");
1453     return 0;
1454
1455   error:
1456
1457     if (NULL != whpx->partition) {
1458         whp_dispatch.WHvDeletePartition(whpx->partition);
1459         whpx->partition = NULL;
1460     }
1461
1462
1463     return ret;
1464 }
1465
1466 int whpx_enabled(void)
1467 {
1468     return whpx_allowed;
1469 }
1470
1471 static void whpx_accel_class_init(ObjectClass *oc, void *data)
1472 {
1473     AccelClass *ac = ACCEL_CLASS(oc);
1474     ac->name = "WHPX";
1475     ac->init_machine = whpx_accel_init;
1476     ac->allowed = &whpx_allowed;
1477 }
1478
1479 static const TypeInfo whpx_accel_type = {
1480     .name = ACCEL_CLASS_NAME("whpx"),
1481     .parent = TYPE_ACCEL,
1482     .class_init = whpx_accel_class_init,
1483 };
1484
1485 static void whpx_type_init(void)
1486 {
1487     type_register_static(&whpx_accel_type);
1488 }
1489
1490 bool init_whp_dispatch(void)
1491 {
1492     const char *lib_name;
1493     HMODULE hLib;
1494
1495     if (whp_dispatch_initialized) {
1496         return true;
1497     }
1498
1499     #define WHP_LOAD_FIELD(return_type, function_name, signature) \
1500         whp_dispatch.function_name = \
1501             (function_name ## _t)GetProcAddress(hLib, #function_name); \
1502         if (!whp_dispatch.function_name) { \
1503             error_report("Could not load function %s from library %s.", \
1504                          #function_name, lib_name); \
1505             goto error; \
1506         } \
1507
1508     lib_name = "WinHvPlatform.dll";
1509     hWinHvPlatform = LoadLibrary(lib_name);
1510     if (!hWinHvPlatform) {
1511         error_report("Could not load library %s.", lib_name);
1512         goto error;
1513     }
1514     hLib = hWinHvPlatform;
1515     LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
1516
1517     lib_name = "WinHvEmulation.dll";
1518     hWinHvEmulation = LoadLibrary(lib_name);
1519     if (!hWinHvEmulation) {
1520         error_report("Could not load library %s.", lib_name);
1521         goto error;
1522     }
1523     hLib = hWinHvEmulation;
1524     LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD)
1525
1526     whp_dispatch_initialized = true;
1527     return true;
1528
1529     error:
1530
1531     if (hWinHvPlatform) {
1532         FreeLibrary(hWinHvPlatform);
1533     }
1534     if (hWinHvEmulation) {
1535         FreeLibrary(hWinHvEmulation);
1536     }
1537     return false;
1538 }
1539
1540 type_init(whpx_type_init);
This page took 0.101964 seconds and 2 git commands to generate.