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