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