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