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