]> git.proxmox.com Git - mirror_qemu.git/blame - target/i386/whpx-all.c
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging
[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;
e7ca549f 935 ret = 0;
812d49f2
JTV
936 break;
937
938 case WHvRunVpExitReasonX64Halt:
939 ret = whpx_handle_halt(cpu);
940 break;
941
942 case WHvRunVpExitReasonCanceled:
943 cpu->exception_index = EXCP_INTERRUPT;
944 ret = 1;
945 break;
946
e7ca549f
JTV
947 case WHvRunVpExitReasonX64MsrAccess: {
948 WHV_REGISTER_VALUE reg_values[3] = {0};
949 WHV_REGISTER_NAME reg_names[3];
950 UINT32 reg_count;
951
952 reg_names[0] = WHvX64RegisterRip;
953 reg_names[1] = WHvX64RegisterRax;
954 reg_names[2] = WHvX64RegisterRdx;
955
956 reg_values[0].Reg64 =
957 vcpu->exit_ctx.VpContext.Rip +
958 vcpu->exit_ctx.VpContext.InstructionLength;
959
960 /*
961 * For all unsupported MSR access we:
962 * ignore writes
963 * return 0 on read.
964 */
965 reg_count = vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite ?
966 1 : 3;
967
968 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
969 whpx->partition,
970 cpu->cpu_index,
971 reg_names, reg_count,
972 reg_values);
973
974 if (FAILED(hr)) {
975 error_report("WHPX: Failed to set MsrAccess state "
976 " registers, hr=%08lx", hr);
977 }
978 ret = 0;
979 break;
980 }
7becac84 981 case WHvRunVpExitReasonX64Cpuid: {
c3942bf2 982 WHV_REGISTER_VALUE reg_values[5];
7becac84
JTV
983 WHV_REGISTER_NAME reg_names[5];
984 UINT32 reg_count = 5;
985 UINT64 rip, rax, rcx, rdx, rbx;
986
c3942bf2
LP
987 memset(reg_values, 0, sizeof(reg_values));
988
7becac84
JTV
989 rip = vcpu->exit_ctx.VpContext.Rip +
990 vcpu->exit_ctx.VpContext.InstructionLength;
991 switch (vcpu->exit_ctx.CpuidAccess.Rax) {
992 case 1:
993 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
994 /* Advertise that we are running on a hypervisor */
995 rcx =
996 vcpu->exit_ctx.CpuidAccess.DefaultResultRcx |
997 CPUID_EXT_HYPERVISOR;
998
e1753a7e
JTV
999 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
1000 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
1001 break;
1002 case 0x80000001:
1003 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
1004 /* Remove any support of OSVW */
1005 rcx =
1006 vcpu->exit_ctx.CpuidAccess.DefaultResultRcx &
1007 ~CPUID_EXT3_OSVW;
1008
7becac84
JTV
1009 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
1010 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
1011 break;
1012 default:
1013 rax = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
1014 rcx = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx;
1015 rdx = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
1016 rbx = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
1017 }
1018
1019 reg_names[0] = WHvX64RegisterRip;
1020 reg_names[1] = WHvX64RegisterRax;
1021 reg_names[2] = WHvX64RegisterRcx;
1022 reg_names[3] = WHvX64RegisterRdx;
1023 reg_names[4] = WHvX64RegisterRbx;
1024
1025 reg_values[0].Reg64 = rip;
1026 reg_values[1].Reg64 = rax;
1027 reg_values[2].Reg64 = rcx;
1028 reg_values[3].Reg64 = rdx;
1029 reg_values[4].Reg64 = rbx;
1030
327fccb2
LP
1031 hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
1032 whpx->partition, cpu->cpu_index,
1033 reg_names,
1034 reg_count,
1035 reg_values);
7becac84
JTV
1036
1037 if (FAILED(hr)) {
1038 error_report("WHPX: Failed to set CpuidAccess state registers,"
1039 " hr=%08lx", hr);
1040 }
1041 ret = 0;
1042 break;
1043 }
812d49f2
JTV
1044 case WHvRunVpExitReasonNone:
1045 case WHvRunVpExitReasonUnrecoverableException:
1046 case WHvRunVpExitReasonInvalidVpRegisterValue:
1047 case WHvRunVpExitReasonUnsupportedFeature:
812d49f2 1048 case WHvRunVpExitReasonException:
812d49f2
JTV
1049 default:
1050 error_report("WHPX: Unexpected VP exit code %d",
1051 vcpu->exit_ctx.ExitReason);
1052 whpx_get_registers(cpu);
1053 qemu_mutex_lock_iothread();
1054 qemu_system_guest_panicked(cpu_get_crash_info(cpu));
1055 qemu_mutex_unlock_iothread();
1056 break;
1057 }
1058
1059 } while (!ret);
1060
1061 cpu_exec_end(cpu);
1062 qemu_mutex_lock_iothread();
1063 current_cpu = cpu;
1064
1065 atomic_set(&cpu->exit_request, false);
1066
1067 return ret < 0;
1068}
1069
1070static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg)
1071{
1072 whpx_get_registers(cpu);
1073 cpu->vcpu_dirty = true;
1074}
1075
1076static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu,
1077 run_on_cpu_data arg)
1078{
1079 whpx_set_registers(cpu);
1080 cpu->vcpu_dirty = false;
1081}
1082
1083static void do_whpx_cpu_synchronize_post_init(CPUState *cpu,
1084 run_on_cpu_data arg)
1085{
1086 whpx_set_registers(cpu);
1087 cpu->vcpu_dirty = false;
1088}
1089
1090static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu,
1091 run_on_cpu_data arg)
1092{
1093 cpu->vcpu_dirty = true;
1094}
1095
1096/*
1097 * CPU support.
1098 */
1099
1100void whpx_cpu_synchronize_state(CPUState *cpu)
1101{
1102 if (!cpu->vcpu_dirty) {
1103 run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL);
1104 }
1105}
1106
1107void whpx_cpu_synchronize_post_reset(CPUState *cpu)
1108{
1109 run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL);
1110}
1111
1112void whpx_cpu_synchronize_post_init(CPUState *cpu)
1113{
1114 run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL);
1115}
1116
1117void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu)
1118{
1119 run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
1120}
1121
1122/*
1123 * Vcpu support.
1124 */
1125
1126static Error *whpx_migration_blocker;
1127
1128int whpx_init_vcpu(CPUState *cpu)
1129{
1130 HRESULT hr;
1131 struct whpx_state *whpx = &whpx_global;
1132 struct whpx_vcpu *vcpu;
1133 Error *local_error = NULL;
1134
1135 /* Add migration blockers for all unsupported features of the
1136 * Windows Hypervisor Platform
1137 */
1138 if (whpx_migration_blocker == NULL) {
1139 error_setg(&whpx_migration_blocker,
1140 "State blocked due to non-migratable CPUID feature support,"
1141 "dirty memory tracking support, and XSAVE/XRSTOR support");
1142
1143 (void)migrate_add_blocker(whpx_migration_blocker, &local_error);
1144 if (local_error) {
1145 error_report_err(local_error);
812d49f2 1146 migrate_del_blocker(whpx_migration_blocker);
327fccb2 1147 error_free(whpx_migration_blocker);
812d49f2
JTV
1148 return -EINVAL;
1149 }
1150 }
1151
e2940978 1152 vcpu = g_malloc0(sizeof(struct whpx_vcpu));
812d49f2
JTV
1153
1154 if (!vcpu) {
1155 error_report("WHPX: Failed to allocte VCPU context.");
1156 return -ENOMEM;
1157 }
1158
327fccb2
LP
1159 hr = whp_dispatch.WHvEmulatorCreateEmulator(
1160 &whpx_emu_callbacks,
1161 &vcpu->emulator);
812d49f2
JTV
1162 if (FAILED(hr)) {
1163 error_report("WHPX: Failed to setup instruction completion support,"
1164 " hr=%08lx", hr);
1165 g_free(vcpu);
1166 return -EINVAL;
1167 }
1168
327fccb2
LP
1169 hr = whp_dispatch.WHvCreateVirtualProcessor(
1170 whpx->partition, cpu->cpu_index, 0);
812d49f2
JTV
1171 if (FAILED(hr)) {
1172 error_report("WHPX: Failed to create a virtual processor,"
1173 " hr=%08lx", hr);
327fccb2 1174 whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
812d49f2
JTV
1175 g_free(vcpu);
1176 return -EINVAL;
1177 }
1178
1179 vcpu->interruptable = true;
1180
1181 cpu->vcpu_dirty = true;
1182 cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu;
1183
1184 return 0;
1185}
1186
1187int whpx_vcpu_exec(CPUState *cpu)
1188{
1189 int ret;
1190 int fatal;
1191
1192 for (;;) {
1193 if (cpu->exception_index >= EXCP_INTERRUPT) {
1194 ret = cpu->exception_index;
1195 cpu->exception_index = -1;
1196 break;
1197 }
1198
1199 fatal = whpx_vcpu_run(cpu);
1200
1201 if (fatal) {
1202 error_report("WHPX: Failed to exec a virtual processor");
1203 abort();
1204 }
1205 }
1206
1207 return ret;
1208}
1209
1210void whpx_destroy_vcpu(CPUState *cpu)
1211{
1212 struct whpx_state *whpx = &whpx_global;
1213 struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
1214
327fccb2
LP
1215 whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
1216 whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
812d49f2
JTV
1217 g_free(cpu->hax_vcpu);
1218 return;
1219}
1220
1221void whpx_vcpu_kick(CPUState *cpu)
1222{
1223 struct whpx_state *whpx = &whpx_global;
327fccb2
LP
1224 whp_dispatch.WHvCancelRunVirtualProcessor(
1225 whpx->partition, cpu->cpu_index, 0);
812d49f2
JTV
1226}
1227
1228/*
1229 * Memory support.
1230 */
1231
1232static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
1233 void *host_va, int add, int rom,
1234 const char *name)
1235{
1236 struct whpx_state *whpx = &whpx_global;
1237 HRESULT hr;
1238
1239 /*
1240 if (add) {
1241 printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
1242 (void*)start_pa, (void*)size, host_va,
1243 (rom ? "ROM" : "RAM"), name);
1244 } else {
1245 printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n",
1246 (void*)start_pa, (void*)size, host_va, name);
1247 }
1248 */
1249
1250 if (add) {
327fccb2
LP
1251 hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
1252 host_va,
1253 start_pa,
1254 size,
1255 (WHvMapGpaRangeFlagRead |
1256 WHvMapGpaRangeFlagExecute |
1257 (rom ? 0 : WHvMapGpaRangeFlagWrite)));
812d49f2 1258 } else {
327fccb2
LP
1259 hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
1260 start_pa,
1261 size);
812d49f2
JTV
1262 }
1263
1264 if (FAILED(hr)) {
1265 error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
1266 " Host:%p, hr=%08lx",
1267 (add ? "MAP" : "UNMAP"), name,
c3942bf2 1268 (void *)(uintptr_t)start_pa, (void *)size, host_va, hr);
812d49f2
JTV
1269 }
1270}
1271
1272static void whpx_process_section(MemoryRegionSection *section, int add)
1273{
1274 MemoryRegion *mr = section->mr;
1275 hwaddr start_pa = section->offset_within_address_space;
1276 ram_addr_t size = int128_get64(section->size);
1277 unsigned int delta;
1278 uint64_t host_va;
1279
1280 if (!memory_region_is_ram(mr)) {
1281 return;
1282 }
1283
1284 delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
1285 delta &= ~qemu_real_host_page_mask;
1286 if (delta > size) {
1287 return;
1288 }
1289 start_pa += delta;
1290 size -= delta;
1291 size &= qemu_real_host_page_mask;
1292 if (!size || (start_pa & ~qemu_real_host_page_mask)) {
1293 return;
1294 }
1295
1296 host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
1297 + section->offset_within_region + delta;
1298
c3942bf2
LP
1299 whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add,
1300 memory_region_is_rom(mr), mr->name);
812d49f2
JTV
1301}
1302
1303static void whpx_region_add(MemoryListener *listener,
1304 MemoryRegionSection *section)
1305{
1306 memory_region_ref(section->mr);
1307 whpx_process_section(section, 1);
1308}
1309
1310static void whpx_region_del(MemoryListener *listener,
1311 MemoryRegionSection *section)
1312{
1313 whpx_process_section(section, 0);
1314 memory_region_unref(section->mr);
1315}
1316
1317static void whpx_transaction_begin(MemoryListener *listener)
1318{
1319}
1320
1321static void whpx_transaction_commit(MemoryListener *listener)
1322{
1323}
1324
1325static void whpx_log_sync(MemoryListener *listener,
1326 MemoryRegionSection *section)
1327{
1328 MemoryRegion *mr = section->mr;
1329
1330 if (!memory_region_is_ram(mr)) {
1331 return;
1332 }
1333
1334 memory_region_set_dirty(mr, 0, int128_get64(section->size));
1335}
1336
1337static MemoryListener whpx_memory_listener = {
1338 .begin = whpx_transaction_begin,
1339 .commit = whpx_transaction_commit,
1340 .region_add = whpx_region_add,
1341 .region_del = whpx_region_del,
1342 .log_sync = whpx_log_sync,
1343 .priority = 10,
1344};
1345
1346static void whpx_memory_init(void)
1347{
1348 memory_listener_register(&whpx_memory_listener, &address_space_memory);
1349}
1350
1351static void whpx_handle_interrupt(CPUState *cpu, int mask)
1352{
1353 cpu->interrupt_request |= mask;
1354
1355 if (!qemu_cpu_is_self(cpu)) {
1356 qemu_cpu_kick(cpu);
1357 }
1358}
1359
1360/*
1361 * Partition support
1362 */
1363
1364static int whpx_accel_init(MachineState *ms)
1365{
1366 struct whpx_state *whpx;
1367 int ret;
1368 HRESULT hr;
1369 WHV_CAPABILITY whpx_cap;
3907e631 1370 UINT32 whpx_cap_size;
812d49f2
JTV
1371 WHV_PARTITION_PROPERTY prop;
1372
1373 whpx = &whpx_global;
1374
327fccb2
LP
1375 if (!init_whp_dispatch()) {
1376 ret = -ENOSYS;
1377 goto error;
1378 }
1379
812d49f2
JTV
1380 memset(whpx, 0, sizeof(struct whpx_state));
1381 whpx->mem_quota = ms->ram_size;
1382
327fccb2
LP
1383 hr = whp_dispatch.WHvGetCapability(
1384 WHvCapabilityCodeHypervisorPresent, &whpx_cap,
1385 sizeof(whpx_cap), &whpx_cap_size);
812d49f2
JTV
1386 if (FAILED(hr) || !whpx_cap.HypervisorPresent) {
1387 error_report("WHPX: No accelerator found, hr=%08lx", hr);
1388 ret = -ENOSPC;
1389 goto error;
1390 }
1391
327fccb2 1392 hr = whp_dispatch.WHvCreatePartition(&whpx->partition);
812d49f2
JTV
1393 if (FAILED(hr)) {
1394 error_report("WHPX: Failed to create partition, hr=%08lx", hr);
1395 ret = -EINVAL;
1396 goto error;
1397 }
1398
1399 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
812d49f2 1400 prop.ProcessorCount = smp_cpus;
327fccb2
LP
1401 hr = whp_dispatch.WHvSetPartitionProperty(
1402 whpx->partition,
1403 WHvPartitionPropertyCodeProcessorCount,
1404 &prop,
1405 sizeof(WHV_PARTITION_PROPERTY));
812d49f2
JTV
1406
1407 if (FAILED(hr)) {
1408 error_report("WHPX: Failed to set partition core count to %d,"
1409 " hr=%08lx", smp_cores, hr);
1410 ret = -EINVAL;
1411 goto error;
7becac84
JTV
1412 }
1413
1414 memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
e7ca549f 1415 prop.ExtendedVmExits.X64MsrExit = 1;
7becac84 1416 prop.ExtendedVmExits.X64CpuidExit = 1;
327fccb2
LP
1417 hr = whp_dispatch.WHvSetPartitionProperty(
1418 whpx->partition,
1419 WHvPartitionPropertyCodeExtendedVmExits,
1420 &prop,
1421 sizeof(WHV_PARTITION_PROPERTY));
7becac84
JTV
1422
1423 if (FAILED(hr)) {
e7ca549f
JTV
1424 error_report("WHPX: Failed to enable partition extended X64MsrExit and"
1425 " X64CpuidExit hr=%08lx", hr);
7becac84
JTV
1426 ret = -EINVAL;
1427 goto error;
1428 }
1429
e1753a7e 1430 UINT32 cpuidExitList[] = {1, 0x80000001};
327fccb2
LP
1431 hr = whp_dispatch.WHvSetPartitionProperty(
1432 whpx->partition,
1433 WHvPartitionPropertyCodeCpuidExitList,
1434 cpuidExitList,
1435 RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
e1753a7e 1436
7becac84
JTV
1437 if (FAILED(hr)) {
1438 error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1439 hr);
1440 ret = -EINVAL;
1441 goto error;
812d49f2
JTV
1442 }
1443
327fccb2 1444 hr = whp_dispatch.WHvSetupPartition(whpx->partition);
812d49f2
JTV
1445 if (FAILED(hr)) {
1446 error_report("WHPX: Failed to setup partition, hr=%08lx", hr);
1447 ret = -EINVAL;
1448 goto error;
1449 }
1450
812d49f2
JTV
1451 whpx_memory_init();
1452
1453 cpu_interrupt_handler = whpx_handle_interrupt;
1454
1455 printf("Windows Hypervisor Platform accelerator is operational\n");
1456 return 0;
1457
1458 error:
1459
1460 if (NULL != whpx->partition) {
327fccb2 1461 whp_dispatch.WHvDeletePartition(whpx->partition);
812d49f2
JTV
1462 whpx->partition = NULL;
1463 }
1464
1465
1466 return ret;
1467}
1468
1469int whpx_enabled(void)
1470{
1471 return whpx_allowed;
1472}
1473
1474static void whpx_accel_class_init(ObjectClass *oc, void *data)
1475{
1476 AccelClass *ac = ACCEL_CLASS(oc);
1477 ac->name = "WHPX";
1478 ac->init_machine = whpx_accel_init;
1479 ac->allowed = &whpx_allowed;
1480}
1481
1482static const TypeInfo whpx_accel_type = {
1483 .name = ACCEL_CLASS_NAME("whpx"),
1484 .parent = TYPE_ACCEL,
1485 .class_init = whpx_accel_class_init,
1486};
1487
1488static void whpx_type_init(void)
1489{
1490 type_register_static(&whpx_accel_type);
1491}
1492
327fccb2
LP
1493bool init_whp_dispatch(void)
1494{
1495 const char *lib_name;
1496 HMODULE hLib;
1497
1498 if (whp_dispatch_initialized) {
1499 return true;
1500 }
1501
1502 #define WHP_LOAD_FIELD(return_type, function_name, signature) \
1503 whp_dispatch.function_name = \
1504 (function_name ## _t)GetProcAddress(hLib, #function_name); \
1505 if (!whp_dispatch.function_name) { \
1506 error_report("Could not load function %s from library %s.", \
1507 #function_name, lib_name); \
1508 goto error; \
1509 } \
1510
1511 lib_name = "WinHvPlatform.dll";
1512 hWinHvPlatform = LoadLibrary(lib_name);
1513 if (!hWinHvPlatform) {
1514 error_report("Could not load library %s.", lib_name);
1515 goto error;
1516 }
1517 hLib = hWinHvPlatform;
1518 LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
1519
1520 lib_name = "WinHvEmulation.dll";
1521 hWinHvEmulation = LoadLibrary(lib_name);
1522 if (!hWinHvEmulation) {
1523 error_report("Could not load library %s.", lib_name);
1524 goto error;
1525 }
1526 hLib = hWinHvEmulation;
1527 LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD)
1528
1529 whp_dispatch_initialized = true;
1530 return true;
1531
1532 error:
1533
1534 if (hWinHvPlatform) {
1535 FreeLibrary(hWinHvPlatform);
1536 }
1537 if (hWinHvEmulation) {
1538 FreeLibrary(hWinHvEmulation);
1539 }
1540 return false;
1541}
1542
812d49f2 1543type_init(whpx_type_init);