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