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        (void)migrate_add_blocker(whpx_migration_blocker, &local_error);
1350        if (local_error) {
1351            error_report_err(local_error);
1352            migrate_del_blocker(whpx_migration_blocker);
1353            error_free(whpx_migration_blocker);
1354            ret = -EINVAL;
1355            goto error;
1356        }
1357    }
1358
1359    vcpu = g_malloc0(sizeof(struct whpx_vcpu));
1360
1361    if (!vcpu) {
1362        error_report("WHPX: Failed to allocte VCPU context.");
1363        ret = -ENOMEM;
1364        goto error;
1365    }
1366
1367    hr = whp_dispatch.WHvEmulatorCreateEmulator(
1368        &whpx_emu_callbacks,
1369        &vcpu->emulator);
1370    if (FAILED(hr)) {
1371        error_report("WHPX: Failed to setup instruction completion support,"
1372                     " hr=%08lx", hr);
1373        ret = -EINVAL;
1374        goto error;
1375    }
1376
1377    hr = whp_dispatch.WHvCreateVirtualProcessor(
1378        whpx->partition, cpu->cpu_index, 0);
1379    if (FAILED(hr)) {
1380        error_report("WHPX: Failed to create a virtual processor,"
1381                     " hr=%08lx", hr);
1382        whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1383        ret = -EINVAL;
1384        goto error;
1385    }
1386
1387    /*
1388     * vcpu's TSC frequency is either specified by user, or use the value
1389     * provided by Hyper-V if the former is not present. In the latter case, we
1390     * query it from Hyper-V and record in env->tsc_khz, so that vcpu's TSC
1391     * frequency can be migrated later via this field.
1392     */
1393    if (!env->tsc_khz) {
1394        hr = whp_dispatch.WHvGetCapability(
1395            WHvCapabilityCodeProcessorClockFrequency, &freq, sizeof(freq),
1396                NULL);
1397        if (hr != WHV_E_UNKNOWN_CAPABILITY) {
1398            if (FAILED(hr)) {
1399                printf("WHPX: Failed to query tsc frequency, hr=0x%08lx\n", hr);
1400            } else {
1401                env->tsc_khz = freq / 1000; /* Hz to KHz */
1402            }
1403        }
1404    }
1405
1406    env->apic_bus_freq = HYPERV_APIC_BUS_FREQUENCY;
1407    hr = whp_dispatch.WHvGetCapability(
1408        WHvCapabilityCodeInterruptClockFrequency, &freq, sizeof(freq), NULL);
1409    if (hr != WHV_E_UNKNOWN_CAPABILITY) {
1410        if (FAILED(hr)) {
1411            printf("WHPX: Failed to query apic bus frequency hr=0x%08lx\n", hr);
1412        } else {
1413            env->apic_bus_freq = freq;
1414        }
1415    }
1416
1417    /*
1418     * If the vmware cpuid frequency leaf option is set, and we have a valid
1419     * tsc value, trap the corresponding cpuid's.
1420     */
1421    if (x86_cpu->vmware_cpuid_freq && env->tsc_khz) {
1422        UINT32 cpuidExitList[] = {1, 0x80000001, 0x40000000, 0x40000010};
1423
1424        hr = whp_dispatch.WHvSetPartitionProperty(
1425                whpx->partition,
1426                WHvPartitionPropertyCodeCpuidExitList,
1427                cpuidExitList,
1428                RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
1429
1430        if (FAILED(hr)) {
1431            error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1432                        hr);
1433            ret = -EINVAL;
1434            goto error;
1435        }
1436    }
1437
1438    vcpu->interruptable = true;
1439    cpu->vcpu_dirty = true;
1440    cpu->hax_vcpu = (struct hax_vcpu_state *)vcpu;
1441    max_vcpu_index = max(max_vcpu_index, cpu->cpu_index);
1442    qemu_add_vm_change_state_handler(whpx_cpu_update_state, cpu->env_ptr);
1443
1444    return 0;
1445
1446error:
1447    g_free(vcpu);
1448
1449    return ret;
1450}
1451
1452int whpx_vcpu_exec(CPUState *cpu)
1453{
1454    int ret;
1455    int fatal;
1456
1457    for (;;) {
1458        if (cpu->exception_index >= EXCP_INTERRUPT) {
1459            ret = cpu->exception_index;
1460            cpu->exception_index = -1;
1461            break;
1462        }
1463
1464        fatal = whpx_vcpu_run(cpu);
1465
1466        if (fatal) {
1467            error_report("WHPX: Failed to exec a virtual processor");
1468            abort();
1469        }
1470    }
1471
1472    return ret;
1473}
1474
1475void whpx_destroy_vcpu(CPUState *cpu)
1476{
1477    struct whpx_state *whpx = &whpx_global;
1478    struct whpx_vcpu *vcpu = get_whpx_vcpu(cpu);
1479
1480    whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index);
1481    whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator);
1482    g_free(cpu->hax_vcpu);
1483    return;
1484}
1485
1486void whpx_vcpu_kick(CPUState *cpu)
1487{
1488    struct whpx_state *whpx = &whpx_global;
1489    whp_dispatch.WHvCancelRunVirtualProcessor(
1490        whpx->partition, cpu->cpu_index, 0);
1491}
1492
1493/*
1494 * Memory support.
1495 */
1496
1497static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size,
1498                                void *host_va, int add, int rom,
1499                                const char *name)
1500{
1501    struct whpx_state *whpx = &whpx_global;
1502    HRESULT hr;
1503
1504    /*
1505    if (add) {
1506        printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n",
1507               (void*)start_pa, (void*)size, host_va,
1508               (rom ? "ROM" : "RAM"), name);
1509    } else {
1510        printf("WHPX: DEL PA:%p Size:%p, Host:%p,      '%s'\n",
1511               (void*)start_pa, (void*)size, host_va, name);
1512    }
1513    */
1514
1515    if (add) {
1516        hr = whp_dispatch.WHvMapGpaRange(whpx->partition,
1517                                         host_va,
1518                                         start_pa,
1519                                         size,
1520                                         (WHvMapGpaRangeFlagRead |
1521                                          WHvMapGpaRangeFlagExecute |
1522                                          (rom ? 0 : WHvMapGpaRangeFlagWrite)));
1523    } else {
1524        hr = whp_dispatch.WHvUnmapGpaRange(whpx->partition,
1525                                           start_pa,
1526                                           size);
1527    }
1528
1529    if (FAILED(hr)) {
1530        error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p bytes,"
1531                     " Host:%p, hr=%08lx",
1532                     (add ? "MAP" : "UNMAP"), name,
1533                     (void *)(uintptr_t)start_pa, (void *)size, host_va, hr);
1534    }
1535}
1536
1537static void whpx_process_section(MemoryRegionSection *section, int add)
1538{
1539    MemoryRegion *mr = section->mr;
1540    hwaddr start_pa = section->offset_within_address_space;
1541    ram_addr_t size = int128_get64(section->size);
1542    unsigned int delta;
1543    uint64_t host_va;
1544
1545    if (!memory_region_is_ram(mr)) {
1546        return;
1547    }
1548
1549    delta = qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_mask);
1550    delta &= ~qemu_real_host_page_mask;
1551    if (delta > size) {
1552        return;
1553    }
1554    start_pa += delta;
1555    size -= delta;
1556    size &= qemu_real_host_page_mask;
1557    if (!size || (start_pa & ~qemu_real_host_page_mask)) {
1558        return;
1559    }
1560
1561    host_va = (uintptr_t)memory_region_get_ram_ptr(mr)
1562            + section->offset_within_region + delta;
1563
1564    whpx_update_mapping(start_pa, size, (void *)(uintptr_t)host_va, add,
1565                        memory_region_is_rom(mr), mr->name);
1566}
1567
1568static void whpx_region_add(MemoryListener *listener,
1569                           MemoryRegionSection *section)
1570{
1571    memory_region_ref(section->mr);
1572    whpx_process_section(section, 1);
1573}
1574
1575static void whpx_region_del(MemoryListener *listener,
1576                           MemoryRegionSection *section)
1577{
1578    whpx_process_section(section, 0);
1579    memory_region_unref(section->mr);
1580}
1581
1582static void whpx_transaction_begin(MemoryListener *listener)
1583{
1584}
1585
1586static void whpx_transaction_commit(MemoryListener *listener)
1587{
1588}
1589
1590static void whpx_log_sync(MemoryListener *listener,
1591                         MemoryRegionSection *section)
1592{
1593    MemoryRegion *mr = section->mr;
1594
1595    if (!memory_region_is_ram(mr)) {
1596        return;
1597    }
1598
1599    memory_region_set_dirty(mr, 0, int128_get64(section->size));
1600}
1601
1602static MemoryListener whpx_memory_listener = {
1603    .begin = whpx_transaction_begin,
1604    .commit = whpx_transaction_commit,
1605    .region_add = whpx_region_add,
1606    .region_del = whpx_region_del,
1607    .log_sync = whpx_log_sync,
1608    .priority = 10,
1609};
1610
1611static void whpx_memory_init(void)
1612{
1613    memory_listener_register(&whpx_memory_listener, &address_space_memory);
1614}
1615
1616/*
1617 * Load the functions from the given library, using the given handle. If a
1618 * handle is provided, it is used, otherwise the library is opened. The
1619 * handle will be updated on return with the opened one.
1620 */
1621static bool load_whp_dispatch_fns(HMODULE *handle,
1622    WHPFunctionList function_list)
1623{
1624    HMODULE hLib = *handle;
1625
1626    #define WINHV_PLATFORM_DLL "WinHvPlatform.dll"
1627    #define WINHV_EMULATION_DLL "WinHvEmulation.dll"
1628    #define WHP_LOAD_FIELD_OPTIONAL(return_type, function_name, signature) \
1629        whp_dispatch.function_name = \
1630            (function_name ## _t)GetProcAddress(hLib, #function_name); \
1631
1632    #define WHP_LOAD_FIELD(return_type, function_name, signature) \
1633        whp_dispatch.function_name = \
1634            (function_name ## _t)GetProcAddress(hLib, #function_name); \
1635        if (!whp_dispatch.function_name) { \
1636            error_report("Could not load function %s", #function_name); \
1637            goto error; \
1638        } \
1639
1640    #define WHP_LOAD_LIB(lib_name, handle_lib) \
1641    if (!handle_lib) { \
1642        handle_lib = LoadLibrary(lib_name); \
1643        if (!handle_lib) { \
1644            error_report("Could not load library %s.", lib_name); \
1645            goto error; \
1646        } \
1647    } \
1648
1649    switch (function_list) {
1650    case WINHV_PLATFORM_FNS_DEFAULT:
1651        WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
1652        LIST_WINHVPLATFORM_FUNCTIONS(WHP_LOAD_FIELD)
1653        break;
1654
1655    case WINHV_EMULATION_FNS_DEFAULT:
1656        WHP_LOAD_LIB(WINHV_EMULATION_DLL, hLib)
1657        LIST_WINHVEMULATION_FUNCTIONS(WHP_LOAD_FIELD)
1658        break;
1659
1660    case WINHV_PLATFORM_FNS_SUPPLEMENTAL:
1661        WHP_LOAD_LIB(WINHV_PLATFORM_DLL, hLib)
1662        LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL(WHP_LOAD_FIELD_OPTIONAL)
1663        break;
1664    }
1665
1666    *handle = hLib;
1667    return true;
1668
1669error:
1670    if (hLib) {
1671        FreeLibrary(hLib);
1672    }
1673
1674    return false;
1675}
1676
1677static void whpx_set_kernel_irqchip(Object *obj, Visitor *v,
1678                                   const char *name, void *opaque,
1679                                   Error **errp)
1680{
1681    struct whpx_state *whpx = &whpx_global;
1682    OnOffSplit mode;
1683
1684    if (!visit_type_OnOffSplit(v, name, &mode, errp)) {
1685        return;
1686    }
1687
1688    switch (mode) {
1689    case ON_OFF_SPLIT_ON:
1690        whpx->kernel_irqchip_allowed = true;
1691        whpx->kernel_irqchip_required = true;
1692        break;
1693
1694    case ON_OFF_SPLIT_OFF:
1695        whpx->kernel_irqchip_allowed = false;
1696        whpx->kernel_irqchip_required = false;
1697        break;
1698
1699    case ON_OFF_SPLIT_SPLIT:
1700        error_setg(errp, "WHPX: split irqchip currently not supported");
1701        error_append_hint(errp,
1702            "Try without kernel-irqchip or with kernel-irqchip=on|off");
1703        break;
1704
1705    default:
1706        /*
1707         * The value was checked in visit_type_OnOffSplit() above. If
1708         * we get here, then something is wrong in QEMU.
1709         */
1710        abort();
1711    }
1712}
1713
1714/*
1715 * Partition support
1716 */
1717
1718static int whpx_accel_init(MachineState *ms)
1719{
1720    struct whpx_state *whpx;
1721    int ret;
1722    HRESULT hr;
1723    WHV_CAPABILITY whpx_cap;
1724    UINT32 whpx_cap_size;
1725    WHV_PARTITION_PROPERTY prop;
1726    UINT32 cpuidExitList[] = {1, 0x80000001};
1727    WHV_CAPABILITY_FEATURES features = {0};
1728
1729    whpx = &whpx_global;
1730
1731    if (!init_whp_dispatch()) {
1732        ret = -ENOSYS;
1733        goto error;
1734    }
1735
1736    whpx->mem_quota = ms->ram_size;
1737
1738    hr = whp_dispatch.WHvGetCapability(
1739        WHvCapabilityCodeHypervisorPresent, &whpx_cap,
1740        sizeof(whpx_cap), &whpx_cap_size);
1741    if (FAILED(hr) || !whpx_cap.HypervisorPresent) {
1742        error_report("WHPX: No accelerator found, hr=%08lx", hr);
1743        ret = -ENOSPC;
1744        goto error;
1745    }
1746
1747    hr = whp_dispatch.WHvGetCapability(
1748        WHvCapabilityCodeFeatures, &features, sizeof(features), NULL);
1749    if (FAILED(hr)) {
1750        error_report("WHPX: Failed to query capabilities, hr=%08lx", hr);
1751        ret = -EINVAL;
1752        goto error;
1753    }
1754
1755    hr = whp_dispatch.WHvCreatePartition(&whpx->partition);
1756    if (FAILED(hr)) {
1757        error_report("WHPX: Failed to create partition, hr=%08lx", hr);
1758        ret = -EINVAL;
1759        goto error;
1760    }
1761
1762    memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1763    prop.ProcessorCount = ms->smp.cpus;
1764    hr = whp_dispatch.WHvSetPartitionProperty(
1765        whpx->partition,
1766        WHvPartitionPropertyCodeProcessorCount,
1767        &prop,
1768        sizeof(WHV_PARTITION_PROPERTY));
1769
1770    if (FAILED(hr)) {
1771        error_report("WHPX: Failed to set partition core count to %d,"
1772                     " hr=%08lx", ms->smp.cores, hr);
1773        ret = -EINVAL;
1774        goto error;
1775    }
1776
1777    /*
1778     * Error out if WHP doesn't support apic emulation and user is requiring
1779     * it.
1780     */
1781    if (whpx->kernel_irqchip_required && (!features.LocalApicEmulation ||
1782            !whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2)) {
1783        error_report("WHPX: kernel irqchip requested, but unavailable. "
1784            "Try without kernel-irqchip or with kernel-irqchip=off");
1785        ret = -EINVAL;
1786        goto error;
1787    }
1788
1789    if (whpx->kernel_irqchip_allowed && features.LocalApicEmulation &&
1790        whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2) {
1791        WHV_X64_LOCAL_APIC_EMULATION_MODE mode =
1792            WHvX64LocalApicEmulationModeXApic;
1793        printf("WHPX: setting APIC emulation mode in the hypervisor\n");
1794        hr = whp_dispatch.WHvSetPartitionProperty(
1795            whpx->partition,
1796            WHvPartitionPropertyCodeLocalApicEmulationMode,
1797            &mode,
1798            sizeof(mode));
1799        if (FAILED(hr)) {
1800            error_report("WHPX: Failed to enable kernel irqchip hr=%08lx", hr);
1801            if (whpx->kernel_irqchip_required) {
1802                error_report("WHPX: kernel irqchip requested, but unavailable");
1803                ret = -EINVAL;
1804                goto error;
1805            }
1806        } else {
1807            whpx->apic_in_platform = true;
1808        }
1809    }
1810
1811    /* Register for MSR and CPUID exits */
1812    memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
1813    prop.ExtendedVmExits.X64MsrExit = 1;
1814    prop.ExtendedVmExits.X64CpuidExit = 1;
1815    if (whpx_apic_in_platform()) {
1816        prop.ExtendedVmExits.X64ApicInitSipiExitTrap = 1;
1817    }
1818
1819    hr = whp_dispatch.WHvSetPartitionProperty(
1820            whpx->partition,
1821            WHvPartitionPropertyCodeExtendedVmExits,
1822            &prop,
1823            sizeof(WHV_PARTITION_PROPERTY));
1824    if (FAILED(hr)) {
1825        error_report("WHPX: Failed to enable MSR & CPUIDexit, hr=%08lx", hr);
1826        ret = -EINVAL;
1827        goto error;
1828    }
1829
1830    hr = whp_dispatch.WHvSetPartitionProperty(
1831        whpx->partition,
1832        WHvPartitionPropertyCodeCpuidExitList,
1833        cpuidExitList,
1834        RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
1835
1836    if (FAILED(hr)) {
1837        error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
1838                     hr);
1839        ret = -EINVAL;
1840        goto error;
1841    }
1842
1843    hr = whp_dispatch.WHvSetupPartition(whpx->partition);
1844    if (FAILED(hr)) {
1845        error_report("WHPX: Failed to setup partition, hr=%08lx", hr);
1846        ret = -EINVAL;
1847        goto error;
1848    }
1849
1850    whpx_memory_init();
1851
1852    printf("Windows Hypervisor Platform accelerator is operational\n");
1853    return 0;
1854
1855error:
1856
1857    if (NULL != whpx->partition) {
1858        whp_dispatch.WHvDeletePartition(whpx->partition);
1859        whpx->partition = NULL;
1860    }
1861
1862    return ret;
1863}
1864
1865int whpx_enabled(void)
1866{
1867    return whpx_allowed;
1868}
1869
1870bool whpx_apic_in_platform(void) {
1871    return whpx_global.apic_in_platform;
1872}
1873
1874static void whpx_accel_class_init(ObjectClass *oc, void *data)
1875{
1876    AccelClass *ac = ACCEL_CLASS(oc);
1877    ac->name = "WHPX";
1878    ac->init_machine = whpx_accel_init;
1879    ac->allowed = &whpx_allowed;
1880
1881    object_class_property_add(oc, "kernel-irqchip", "on|off|split",
1882        NULL, whpx_set_kernel_irqchip,
1883        NULL, NULL);
1884    object_class_property_set_description(oc, "kernel-irqchip",
1885        "Configure WHPX in-kernel irqchip");
1886}
1887
1888static void whpx_accel_instance_init(Object *obj)
1889{
1890    struct whpx_state *whpx = &whpx_global;
1891
1892    memset(whpx, 0, sizeof(struct whpx_state));
1893    /* Turn on kernel-irqchip, by default */
1894    whpx->kernel_irqchip_allowed = true;
1895}
1896
1897static const TypeInfo whpx_accel_type = {
1898    .name = ACCEL_CLASS_NAME("whpx"),
1899    .parent = TYPE_ACCEL,
1900    .instance_init = whpx_accel_instance_init,
1901    .class_init = whpx_accel_class_init,
1902};
1903
1904static void whpx_type_init(void)
1905{
1906    type_register_static(&whpx_accel_type);
1907}
1908
1909bool init_whp_dispatch(void)
1910{
1911    if (whp_dispatch_initialized) {
1912        return true;
1913    }
1914
1915    if (!load_whp_dispatch_fns(&hWinHvPlatform, WINHV_PLATFORM_FNS_DEFAULT)) {
1916        goto error;
1917    }
1918
1919    if (!load_whp_dispatch_fns(&hWinHvEmulation, WINHV_EMULATION_FNS_DEFAULT)) {
1920        goto error;
1921    }
1922
1923    assert(load_whp_dispatch_fns(&hWinHvPlatform,
1924        WINHV_PLATFORM_FNS_SUPPLEMENTAL));
1925    whp_dispatch_initialized = true;
1926
1927    return true;
1928error:
1929    if (hWinHvPlatform) {
1930        FreeLibrary(hWinHvPlatform);
1931    }
1932
1933    if (hWinHvEmulation) {
1934        FreeLibrary(hWinHvEmulation);
1935    }
1936
1937    return false;
1938}
1939
1940type_init(whpx_type_init);
1941