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