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