From nobody Mon Feb 9 16:34:14 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1517861171216487.6328812369328; Mon, 5 Feb 2018 12:06:11 -0800 (PST) Received: from localhost ([::1]:55089 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ein1m-0004MD-6r for importer@patchew.org; Mon, 05 Feb 2018 15:06:10 -0500 Received: from eggs.gnu.org ([2001:4830:134:3::10]:45237) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1eimSs-00056N-3T for qemu-devel@nongnu.org; Mon, 05 Feb 2018 14:30:10 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1eimSo-0005Fi-3a for qemu-devel@nongnu.org; Mon, 05 Feb 2018 14:30:06 -0500 Received: from mail-wm0-x230.google.com ([2a00:1450:400c:c09::230]:38523) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1eimSn-0005Dn-FM for qemu-devel@nongnu.org; Mon, 05 Feb 2018 14:30:02 -0500 Received: by mail-wm0-x230.google.com with SMTP id 141so27961150wme.3 for ; Mon, 05 Feb 2018 11:30:01 -0800 (PST) Received: from 640k.lan ([82.84.122.246]) by smtp.gmail.com with ESMTPSA id g8sm20029891wra.18.2018.02.05.11.29.57 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 05 Feb 2018 11:29:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=C9XA7dqDMa4sqDM0VyzgOYm3vCUKmYb4NK5LOIq0V2Y=; b=jiFEfF6PTNxUuyidGmORy2CjOU5QwIRDNn1BbQIA3PcwHIosw2kuTUXm9LfZCKI6gl MddbQSKV23tzqzHUNlgL5cQL1yZe+rRTpNT/8+9xwQfjTwfBCiQ61MjtlbALFoH1o0KQ iBI7Rqb/9myrPrtXjcgQY0PGhceAfsD0MgGR8nMQS+iAZXUDLfKmv8XwXpk52DdM/cZK T//H+mnj0HB9xDc20LtA2YPN3Uv46AaS2l9DrYZ0TDLSHelbRqpuFIQuXvY5hfjcuI8/ xy0x2eldj9UWbjR2bYEpzSaAxOWwZbG3r5j4cBklUzo1nyTKdiArQTw7Npjr1EiUtdRK kp8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=C9XA7dqDMa4sqDM0VyzgOYm3vCUKmYb4NK5LOIq0V2Y=; b=AJuRmuS9kqLpO8XDW8J475ZjqUBjOHBMquIMWfkXjqAVMcoP2f6Z06L8PVNotCeTV+ icNsAob4IsoSBE6DppuJ2jr8oGAhfHu4EdT5NyDR77jY+O5TJAEljvB9pLFOC6C6nwQT HjDFdEl9HKddSdf1M0GBWk6pDZapzIc+bXCpQWE+hIaUe7BGZbpFr4ZFxu0soMUn0zVi wYLiTFEtko80SMDJ5UuooOCuO4JXLCtOPklpoc4Ht9TXQOZn3sUgVQrSOlYANxSdq+4k r+p4XBQ9m4RxeboeIntkhfeDUsynWMwIUYJcfBmxt3q4y+KdJ7RKAKRTq6p7N+6l8rbV M4UQ== X-Gm-Message-State: APf1xPCWP/HmLQv9YWL6WLrs2bl/aYxzvRBl5DwtAmaSWOThHM1Bqk3f 4EevKtAYYrmtjwpiTN+r6NmBGsIY X-Google-Smtp-Source: AH8x226DcvEnA8eu+E+sIPKWWad9+iNtPICySNSNImGkkw9a1qD9ZG/36RvPhPqkqM/wWOCerAZ7cw== X-Received: by 10.28.32.15 with SMTP id g15mr307042wmg.22.1517858999520; Mon, 05 Feb 2018 11:29:59 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Date: Mon, 5 Feb 2018 20:29:00 +0100 Message-Id: <1517858941-5538-47-git-send-email-pbonzini@redhat.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1517858941-5538-1-git-send-email-pbonzini@redhat.com> References: <1517858941-5538-1-git-send-email-pbonzini@redhat.com> X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:400c:c09::230 Subject: [Qemu-devel] [PULL 46/47] Introduce the WHPX impl X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: "Justin Terry \(VM\)" Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZohoMail: RDKM_2 RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" From: "Justin Terry (VM)" Implements the Windows Hypervisor Platform accelerator (WHPX) target. Which acts as a hypervisor accelerator for QEMU on the Windows platform. This ena= bles QEMU much greater speed over the emulated x86_64 path's that are taken on Windows today. 1. Adds support for vPartition management. 2. Adds support for vCPU management. 3. Adds support for MMIO/PortIO. 4. Registers the WHPX ACCEL_CLASS. Signed-off-by: Justin Terry (VM) Message-Id: <1516655269-1785-4-git-send-email-juterry@microsoft.com> Signed-off-by: Paolo Bonzini --- target/i386/Makefile.objs | 1 + target/i386/whpx-all.c | 1366 +++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 1367 insertions(+) create mode 100644 target/i386/whpx-all.c diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs index 44103a6..f5c6ef2 100644 --- a/target/i386/Makefile.objs +++ b/target/i386/Makefile.objs @@ -14,3 +14,4 @@ ifdef CONFIG_DARWIN obj-$(CONFIG_HAX) +=3D hax-all.o hax-mem.o hax-darwin.o obj-$(CONFIG_HVF) +=3D hvf/ endif +obj-$(CONFIG_WHPX) +=3D whpx-all.o diff --git a/target/i386/whpx-all.c b/target/i386/whpx-all.c new file mode 100644 index 0000000..0015b27 --- /dev/null +++ b/target/i386/whpx-all.c @@ -0,0 +1,1366 @@ +/* + * QEMU Windows Hypervisor Platform accelerator (WHPX) + * + * Copyright Microsoft Corp. 2017 + * + * This work is licensed under the terms of the GNU GPL, version 2 or late= r. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/address-spaces.h" +#include "exec/exec-all.h" +#include "exec/ioport.h" +#include "qemu-common.h" +#include "strings.h" +#include "sysemu/accel.h" +#include "sysemu/whpx.h" +#include "sysemu/sysemu.h" +#include "sysemu/cpus.h" +#include "qemu/main-loop.h" +#include "hw/boards.h" +#include "qemu/error-report.h" +#include "qemu/queue.h" +#include "qapi/error.h" +#include "migration/blocker.h" + +#include +#include + +struct whpx_state { + uint64_t mem_quota; + WHV_PARTITION_HANDLE partition; + uint32_t exit_ctx_size; +}; + +static const WHV_REGISTER_NAME whpx_register_names[] =3D { + + /* X64 General purpose registers */ + WHvX64RegisterRax, + WHvX64RegisterRcx, + WHvX64RegisterRdx, + WHvX64RegisterRbx, + WHvX64RegisterRsp, + WHvX64RegisterRbp, + WHvX64RegisterRsi, + WHvX64RegisterRdi, + WHvX64RegisterR8, + WHvX64RegisterR9, + WHvX64RegisterR10, + WHvX64RegisterR11, + WHvX64RegisterR12, + WHvX64RegisterR13, + WHvX64RegisterR14, + WHvX64RegisterR15, + WHvX64RegisterRip, + WHvX64RegisterRflags, + + /* X64 Segment registers */ + WHvX64RegisterEs, + WHvX64RegisterCs, + WHvX64RegisterSs, + WHvX64RegisterDs, + WHvX64RegisterFs, + WHvX64RegisterGs, + WHvX64RegisterLdtr, + WHvX64RegisterTr, + + /* X64 Table registers */ + WHvX64RegisterIdtr, + WHvX64RegisterGdtr, + + /* X64 Control Registers */ + WHvX64RegisterCr0, + WHvX64RegisterCr2, + WHvX64RegisterCr3, + WHvX64RegisterCr4, + WHvX64RegisterCr8, + + /* X64 Debug Registers */ + /* + * WHvX64RegisterDr0, + * WHvX64RegisterDr1, + * WHvX64RegisterDr2, + * WHvX64RegisterDr3, + * WHvX64RegisterDr6, + * WHvX64RegisterDr7, + */ + + /* X64 Floating Point and Vector Registers */ + WHvX64RegisterXmm0, + WHvX64RegisterXmm1, + WHvX64RegisterXmm2, + WHvX64RegisterXmm3, + WHvX64RegisterXmm4, + WHvX64RegisterXmm5, + WHvX64RegisterXmm6, + WHvX64RegisterXmm7, + WHvX64RegisterXmm8, + WHvX64RegisterXmm9, + WHvX64RegisterXmm10, + WHvX64RegisterXmm11, + WHvX64RegisterXmm12, + WHvX64RegisterXmm13, + WHvX64RegisterXmm14, + WHvX64RegisterXmm15, + WHvX64RegisterFpMmx0, + WHvX64RegisterFpMmx1, + WHvX64RegisterFpMmx2, + WHvX64RegisterFpMmx3, + WHvX64RegisterFpMmx4, + WHvX64RegisterFpMmx5, + WHvX64RegisterFpMmx6, + WHvX64RegisterFpMmx7, + WHvX64RegisterFpControlStatus, + WHvX64RegisterXmmControlStatus, + + /* X64 MSRs */ + WHvX64RegisterTsc, + WHvX64RegisterEfer, +#ifdef TARGET_X86_64 + WHvX64RegisterKernelGsBase, +#endif + WHvX64RegisterApicBase, + /* WHvX64RegisterPat, */ + WHvX64RegisterSysenterCs, + WHvX64RegisterSysenterEip, + WHvX64RegisterSysenterEsp, + WHvX64RegisterStar, +#ifdef TARGET_X86_64 + WHvX64RegisterLstar, + WHvX64RegisterCstar, + WHvX64RegisterSfmask, +#endif + + /* Interrupt / Event Registers */ + /* + * WHvRegisterPendingInterruption, + * WHvRegisterInterruptState, + * WHvRegisterPendingEvent0, + * WHvRegisterPendingEvent1 + * WHvX64RegisterDeliverabilityNotifications, + */ +}; + +struct whpx_register_set { + WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)]; +}; + +struct whpx_vcpu { + WHV_EMULATOR_HANDLE emulator; + bool window_registered; + bool interruptable; + uint64_t tpr; + uint64_t apic_base; + WHV_X64_PENDING_INTERRUPTION_REGISTER interrupt_in_flight; + + /* Must be the last field as it may have a tail */ + WHV_RUN_VP_EXIT_CONTEXT exit_ctx; +}; + +static bool whpx_allowed; + +struct whpx_state whpx_global; + + +/* + * VP support + */ + +static struct whpx_vcpu *get_whpx_vcpu(CPUState *cpu) +{ + return (struct whpx_vcpu *)cpu->hax_vcpu; +} + +static WHV_X64_SEGMENT_REGISTER whpx_seg_q2h(const SegmentCache *qs, int v= 86, + int r86) +{ + WHV_X64_SEGMENT_REGISTER hs; + unsigned flags =3D qs->flags; + + hs.Base =3D qs->base; + hs.Limit =3D qs->limit; + hs.Selector =3D qs->selector; + + if (v86) { + hs.Attributes =3D 0; + hs.SegmentType =3D 3; + hs.Present =3D 1; + hs.DescriptorPrivilegeLevel =3D 3; + hs.NonSystemSegment =3D 1; + + } else { + hs.Attributes =3D (flags >> DESC_TYPE_SHIFT); + + if (r86) { + /* hs.Base &=3D 0xfffff; */ + } + } + + return hs; +} + +static SegmentCache whpx_seg_h2q(const WHV_X64_SEGMENT_REGISTER *hs) +{ + SegmentCache qs; + + qs.base =3D hs->Base; + qs.limit =3D hs->Limit; + qs.selector =3D hs->Selector; + + qs.flags =3D ((uint32_t)hs->Attributes) << DESC_TYPE_SHIFT; + + return qs; +} + +static void whpx_set_registers(CPUState *cpu) +{ + struct whpx_state *whpx =3D &whpx_global; + struct whpx_vcpu *vcpu =3D get_whpx_vcpu(cpu); + struct CPUX86State *env =3D (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu =3D X86_CPU(cpu); + struct whpx_register_set vcxt =3D {0}; + HRESULT hr; + int idx =3D 0; + int i; + int v86, r86; + + assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + + v86 =3D (env->eflags & VM_MASK); + r86 =3D !(env->cr[0] & CR0_PE_MASK); + + vcpu->tpr =3D cpu_get_apic_tpr(x86_cpu->apic_state); + vcpu->apic_base =3D cpu_get_apic_base(x86_cpu->apic_state); + + /* Indexes for first 16 registers match between HV and QEMU definition= s */ + for (idx =3D 0; idx < CPU_NB_REGS64; idx +=3D 1) { + vcxt.values[idx].Reg64 =3D env->regs[idx]; + } + + /* Same goes for RIP and RFLAGS */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterRip); + vcxt.values[idx++].Reg64 =3D env->eip; + + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterRflags); + vcxt.values[idx++].Reg64 =3D env->eflags; + + /* Translate 6+4 segment registers. HV and QEMU order matches */ + assert(idx =3D=3D WHvX64RegisterEs); + for (i =3D 0; i < 6; i +=3D 1, idx +=3D 1) { + vcxt.values[idx].Segment =3D whpx_seg_q2h(&env->segs[i], v86, r86); + } + + assert(idx =3D=3D WHvX64RegisterLdtr); + vcxt.values[idx++].Segment =3D whpx_seg_q2h(&env->ldt, 0, 0); + + assert(idx =3D=3D WHvX64RegisterTr); + vcxt.values[idx++].Segment =3D whpx_seg_q2h(&env->tr, 0, 0); + + assert(idx =3D=3D WHvX64RegisterIdtr); + vcxt.values[idx].Table.Base =3D env->idt.base; + vcxt.values[idx].Table.Limit =3D env->idt.limit; + idx +=3D 1; + + assert(idx =3D=3D WHvX64RegisterGdtr); + vcxt.values[idx].Table.Base =3D env->gdt.base; + vcxt.values[idx].Table.Limit =3D env->gdt.limit; + idx +=3D 1; + + /* CR0, 2, 3, 4, 8 */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr0); + vcxt.values[idx++].Reg64 =3D env->cr[0]; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr2); + vcxt.values[idx++].Reg64 =3D env->cr[2]; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr3); + vcxt.values[idx++].Reg64 =3D env->cr[3]; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr4); + vcxt.values[idx++].Reg64 =3D env->cr[4]; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr8); + vcxt.values[idx++].Reg64 =3D vcpu->tpr; + + /* 8 Debug Registers - Skipped */ + + /* 16 XMM registers */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterXmm0); + for (i =3D 0; i < 16; i +=3D 1, idx +=3D 1) { + vcxt.values[idx].Reg128.Low64 =3D env->xmm_regs[i].ZMM_Q(0); + vcxt.values[idx].Reg128.High64 =3D env->xmm_regs[i].ZMM_Q(1); + } + + /* 8 FP registers */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterFpMmx0); + for (i =3D 0; i < 8; i +=3D 1, idx +=3D 1) { + vcxt.values[idx].Fp.AsUINT128.Low64 =3D env->fpregs[i].mmx.MMX_Q(0= ); + /* vcxt.values[idx].Fp.AsUINT128.High64 =3D + env->fpregs[i].mmx.MMX_Q(1); + */ + } + + /* FP control status register */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterFpControlStatus); + vcxt.values[idx].FpControlStatus.FpControl =3D env->fpuc; + vcxt.values[idx].FpControlStatus.FpStatus =3D + (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + vcxt.values[idx].FpControlStatus.FpTag =3D 0; + for (i =3D 0; i < 8; ++i) { + vcxt.values[idx].FpControlStatus.FpTag |=3D (!env->fptags[i]) << i; + } + vcxt.values[idx].FpControlStatus.Reserved =3D 0; + vcxt.values[idx].FpControlStatus.LastFpOp =3D env->fpop; + vcxt.values[idx].FpControlStatus.LastFpRip =3D env->fpip; + idx +=3D 1; + + /* XMM control status register */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterXmmControlStatus); + vcxt.values[idx].XmmControlStatus.LastFpRdp =3D 0; + vcxt.values[idx].XmmControlStatus.XmmStatusControl =3D env->mxcsr; + vcxt.values[idx].XmmControlStatus.XmmStatusControlMask =3D 0x0000ffff; + idx +=3D 1; + + /* MSRs */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterTsc); + vcxt.values[idx++].Reg64 =3D env->tsc; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterEfer); + vcxt.values[idx++].Reg64 =3D env->efer; +#ifdef TARGET_X86_64 + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterKernelGsBase); + vcxt.values[idx++].Reg64 =3D env->kernelgsbase; +#endif + + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterApicBase); + vcxt.values[idx++].Reg64 =3D vcpu->apic_base; + + /* WHvX64RegisterPat - Skipped */ + + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterSysenterCs); + vcxt.values[idx++].Reg64 =3D env->sysenter_cs; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterSysenterEip); + vcxt.values[idx++].Reg64 =3D env->sysenter_eip; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterSysenterEsp); + vcxt.values[idx++].Reg64 =3D env->sysenter_esp; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterStar); + vcxt.values[idx++].Reg64 =3D env->star; +#ifdef TARGET_X86_64 + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterLstar); + vcxt.values[idx++].Reg64 =3D env->lstar; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCstar); + vcxt.values[idx++].Reg64 =3D env->cstar; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterSfmask); + vcxt.values[idx++].Reg64 =3D env->fmask; +#endif + + /* Interrupt / Event Registers - Skipped */ + + assert(idx =3D=3D RTL_NUMBER_OF(whpx_register_names)); + + hr =3D WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + whpx_register_names, + RTL_NUMBER_OF(whpx_register_names= ), + &vcxt.values[0]); + + if (FAILED(hr)) { + error_report("WHPX: Failed to set virtual processor context, hr=3D= %08lx", + hr); + __debugbreak(); + } + + return; +} + +static void whpx_get_registers(CPUState *cpu) +{ + struct whpx_state *whpx =3D &whpx_global; + struct whpx_vcpu *vcpu =3D get_whpx_vcpu(cpu); + struct CPUX86State *env =3D (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu =3D X86_CPU(cpu); + struct whpx_register_set vcxt; + uint64_t tpr, apic_base; + HRESULT hr; + int idx =3D 0; + int i; + + assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu)); + + hr =3D WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + whpx_register_names, + RTL_NUMBER_OF(whpx_register_names= ), + &vcxt.values[0]); + if (FAILED(hr)) { + error_report("WHPX: Failed to get virtual processor context, hr=3D= %08lx", + hr); + __debugbreak(); + } + + /* Indexes for first 16 registers match between HV and QEMU definition= s */ + for (idx =3D 0; idx < CPU_NB_REGS64; idx +=3D 1) { + env->regs[idx] =3D vcxt.values[idx].Reg64; + } + + /* Same goes for RIP and RFLAGS */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterRip); + env->eip =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterRflags); + env->eflags =3D vcxt.values[idx++].Reg64; + + /* Translate 6+4 segment registers. HV and QEMU order matches */ + assert(idx =3D=3D WHvX64RegisterEs); + for (i =3D 0; i < 6; i +=3D 1, idx +=3D 1) { + env->segs[i] =3D whpx_seg_h2q(&vcxt.values[idx].Segment); + } + + assert(idx =3D=3D WHvX64RegisterLdtr); + env->ldt =3D whpx_seg_h2q(&vcxt.values[idx++].Segment); + assert(idx =3D=3D WHvX64RegisterTr); + env->tr =3D whpx_seg_h2q(&vcxt.values[idx++].Segment); + assert(idx =3D=3D WHvX64RegisterIdtr); + env->idt.base =3D vcxt.values[idx].Table.Base; + env->idt.limit =3D vcxt.values[idx].Table.Limit; + idx +=3D 1; + assert(idx =3D=3D WHvX64RegisterGdtr); + env->gdt.base =3D vcxt.values[idx].Table.Base; + env->gdt.limit =3D vcxt.values[idx].Table.Limit; + idx +=3D 1; + + /* CR0, 2, 3, 4, 8 */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr0); + env->cr[0] =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr2); + env->cr[2] =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr3); + env->cr[3] =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr4); + env->cr[4] =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCr8); + tpr =3D vcxt.values[idx++].Reg64; + if (tpr !=3D vcpu->tpr) { + vcpu->tpr =3D tpr; + cpu_set_apic_tpr(x86_cpu->apic_state, tpr); + } + + /* 8 Debug Registers - Skipped */ + + /* 16 XMM registers */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterXmm0); + for (i =3D 0; i < 16; i +=3D 1, idx +=3D 1) { + env->xmm_regs[i].ZMM_Q(0) =3D vcxt.values[idx].Reg128.Low64; + env->xmm_regs[i].ZMM_Q(1) =3D vcxt.values[idx].Reg128.High64; + } + + /* 8 FP registers */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterFpMmx0); + for (i =3D 0; i < 8; i +=3D 1, idx +=3D 1) { + env->fpregs[i].mmx.MMX_Q(0) =3D vcxt.values[idx].Fp.AsUINT128.Low6= 4; + /* env->fpregs[i].mmx.MMX_Q(1) =3D + vcxt.values[idx].Fp.AsUINT128.High64; + */ + } + + /* FP control status register */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterFpControlStatus); + env->fpuc =3D vcxt.values[idx].FpControlStatus.FpControl; + env->fpstt =3D (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7; + env->fpus =3D vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800; + for (i =3D 0; i < 8; ++i) { + env->fptags[i] =3D !((vcxt.values[idx].FpControlStatus.FpTag >> i)= & 1); + } + env->fpop =3D vcxt.values[idx].FpControlStatus.LastFpOp; + env->fpip =3D vcxt.values[idx].FpControlStatus.LastFpRip; + idx +=3D 1; + + /* XMM control status register */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterXmmControlStatus); + env->mxcsr =3D vcxt.values[idx].XmmControlStatus.XmmStatusControl; + idx +=3D 1; + + /* MSRs */ + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterTsc); + env->tsc =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterEfer); + env->efer =3D vcxt.values[idx++].Reg64; +#ifdef TARGET_X86_64 + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterKernelGsBase); + env->kernelgsbase =3D vcxt.values[idx++].Reg64; +#endif + + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterApicBase); + apic_base =3D vcxt.values[idx++].Reg64; + if (apic_base !=3D vcpu->apic_base) { + vcpu->apic_base =3D apic_base; + cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base); + } + + /* WHvX64RegisterPat - Skipped */ + + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterSysenterCs); + env->sysenter_cs =3D vcxt.values[idx++].Reg64;; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterSysenterEip); + env->sysenter_eip =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterSysenterEsp); + env->sysenter_esp =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterStar); + env->star =3D vcxt.values[idx++].Reg64; +#ifdef TARGET_X86_64 + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterLstar); + env->lstar =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterCstar); + env->cstar =3D vcxt.values[idx++].Reg64; + assert(whpx_register_names[idx] =3D=3D WHvX64RegisterSfmask); + env->fmask =3D vcxt.values[idx++].Reg64; +#endif + + /* Interrupt / Event Registers - Skipped */ + + assert(idx =3D=3D RTL_NUMBER_OF(whpx_register_names)); + + return; +} + +static HRESULT CALLBACK whpx_emu_ioport_callback( + void *ctx, + WHV_EMULATOR_IO_ACCESS_INFO *IoAccess) +{ + MemTxAttrs attrs =3D { 0 }; + address_space_rw(&address_space_io, IoAccess->Port, attrs, + (uint8_t *)&IoAccess->Data, IoAccess->AccessSize, + IoAccess->Direction); + return S_OK; +} + +static HRESULT CALLBACK whpx_emu_memio_callback( + void *ctx, + WHV_EMULATOR_MEMORY_ACCESS_INFO *ma) +{ + cpu_physical_memory_rw(ma->GpaAddress, ma->Data, ma->AccessSize, + ma->Direction); + return S_OK; +} + +static HRESULT CALLBACK whpx_emu_getreg_callback( + void *ctx, + const WHV_REGISTER_NAME *RegisterNames, + UINT32 RegisterCount, + WHV_REGISTER_VALUE *RegisterValues) +{ + HRESULT hr; + struct whpx_state *whpx =3D &whpx_global; + CPUState *cpu =3D (CPUState *)ctx; + + hr =3D WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + RegisterNames, RegisterCount, + RegisterValues); + if (FAILED(hr)) { + error_report("WHPX: Failed to get virtual processor registers," + " hr=3D%08lx", hr); + __debugbreak(); + } + + return hr; +} + +static HRESULT CALLBACK whpx_emu_setreg_callback( + void *ctx, + const WHV_REGISTER_NAME *RegisterNames, + UINT32 RegisterCount, + const WHV_REGISTER_VALUE *RegisterValues) +{ + HRESULT hr; + struct whpx_state *whpx =3D &whpx_global; + CPUState *cpu =3D (CPUState *)ctx; + + hr =3D WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + RegisterNames, RegisterCount, + RegisterValues); + if (FAILED(hr)) { + error_report("WHPX: Failed to set virtual processor registers," + " hr=3D%08lx", hr); + __debugbreak(); + } + + /* + * The emulator just successfully wrote the register state. We clear t= he + * dirty state so we avoid the double write on resume of the VP. + */ + cpu->vcpu_dirty =3D false; + + return hr; +} + +static HRESULT CALLBACK whpx_emu_translate_callback( + void *ctx, + WHV_GUEST_VIRTUAL_ADDRESS Gva, + WHV_TRANSLATE_GVA_FLAGS TranslateFlags, + WHV_TRANSLATE_GVA_RESULT_CODE *TranslationResult, + WHV_GUEST_PHYSICAL_ADDRESS *Gpa) +{ + HRESULT hr; + struct whpx_state *whpx =3D &whpx_global; + CPUState *cpu =3D (CPUState *)ctx; + WHV_TRANSLATE_GVA_RESULT res; + + hr =3D WHvTranslateGva(whpx->partition, cpu->cpu_index, + Gva, TranslateFlags, &res, Gpa); + if (FAILED(hr)) { + error_report("WHPX: Failed to translate GVA, hr=3D%08lx", hr); + __debugbreak(); + } else { + *TranslationResult =3D res.ResultCode; + } + + return hr; +} + +static const WHV_EMULATOR_CALLBACKS whpx_emu_callbacks =3D { + .WHvEmulatorIoPortCallback =3D whpx_emu_ioport_callback, + .WHvEmulatorMemoryCallback =3D whpx_emu_memio_callback, + .WHvEmulatorGetVirtualProcessorRegisters =3D whpx_emu_getreg_callback, + .WHvEmulatorSetVirtualProcessorRegisters =3D whpx_emu_setreg_callback, + .WHvEmulatorTranslateGvaPage =3D whpx_emu_translate_callback, +}; + +static int whpx_handle_mmio(CPUState *cpu, WHV_MEMORY_ACCESS_CONTEXT *ctx) +{ + HRESULT hr; + struct whpx_vcpu *vcpu =3D get_whpx_vcpu(cpu); + WHV_EMULATOR_STATUS emu_status; + + hr =3D WHvEmulatorTryMmioEmulation(vcpu->emulator, cpu, ctx, &emu_stat= us); + if (FAILED(hr)) { + __debugbreak(); + error_report("WHPX: Failed to parse MMIO access, hr=3D%08lx", hr); + return -1; + } + + if (!emu_status.EmulationSuccessful) { + __debugbreak(); + error_report("WHPX: Failed to emulate MMIO access"); + return -1; + } + + return 0; +} + +static int whpx_handle_portio(CPUState *cpu, + WHV_X64_IO_PORT_ACCESS_CONTEXT *ctx) +{ + HRESULT hr; + struct whpx_vcpu *vcpu =3D get_whpx_vcpu(cpu); + WHV_EMULATOR_STATUS emu_status; + + hr =3D WHvEmulatorTryIoEmulation(vcpu->emulator, cpu, ctx, &emu_status= ); + if (FAILED(hr)) { + __debugbreak(); + error_report("WHPX: Failed to parse PortIO access, hr=3D%08lx", hr= ); + return -1; + } + + if (!emu_status.EmulationSuccessful) { + __debugbreak(); + error_report("WHPX: Failed to emulate PortMMIO access"); + return -1; + } + + return 0; +} + +static int whpx_handle_halt(CPUState *cpu) +{ + struct CPUX86State *env =3D (CPUArchState *)(cpu->env_ptr); + int ret =3D 0; + + qemu_mutex_lock_iothread(); + if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK)) && + !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu->exception_index =3D EXCP_HLT; + cpu->halted =3D true; + ret =3D 1; + } + qemu_mutex_unlock_iothread(); + + return ret; +} + +static void whpx_vcpu_pre_run(CPUState *cpu) +{ + HRESULT hr; + struct whpx_state *whpx =3D &whpx_global; + struct whpx_vcpu *vcpu =3D get_whpx_vcpu(cpu); + struct CPUX86State *env =3D (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu =3D X86_CPU(cpu); + int irq; + WHV_X64_PENDING_INTERRUPTION_REGISTER new_int =3D {0}; + UINT32 reg_count =3D 0; + WHV_REGISTER_VALUE reg_values[3] =3D {0}; + WHV_REGISTER_NAME reg_names[3]; + + qemu_mutex_lock_iothread(); + + /* Inject NMI */ + if (!vcpu->interrupt_in_flight.InterruptionPending && + cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { + if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { + cpu->interrupt_request &=3D ~CPU_INTERRUPT_NMI; + vcpu->interruptable =3D false; + new_int.InterruptionType =3D WHvX64PendingNmi; + new_int.InterruptionPending =3D 1; + new_int.InterruptionVector =3D 2; + } + if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + qemu_mutex_lock_iothread(); + cpu->interrupt_request &=3D ~CPU_INTERRUPT_SMI; + __debugbreak(); + qemu_mutex_unlock_iothread(); + } + } + + /* + * Force the VCPU out of its inner loop to process any INIT requests or + * commit pending TPR access. + */ + if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR))= { + if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + !(env->hflags & HF_SMM_MASK)) { + cpu->exit_request =3D 1; + } + if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + cpu->exit_request =3D 1; + } + } + + /* Get pending hard interruption or replay one that was overwritten */ + if (!vcpu->interrupt_in_flight.InterruptionPending && + vcpu->interruptable && (env->eflags & IF_MASK)) { + assert(!new_int.InterruptionPending); + if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + cpu->interrupt_request &=3D ~CPU_INTERRUPT_HARD; + irq =3D cpu_get_pic_interrupt(env); + if (irq >=3D 0) { + new_int.InterruptionType =3D WHvX64PendingInterrupt; + new_int.InterruptionPending =3D 1; + new_int.InterruptionVector =3D irq; + } + } + } + + /* Setup interrupt state if new one was prepared */ + if (new_int.InterruptionPending) { + reg_values[reg_count].PendingInterruption =3D new_int; + reg_names[reg_count] =3D WHvRegisterPendingInterruption; + reg_count +=3D 1; + } + + /* Sync the TPR to the CR8 if was modified during the intercept */ + reg_values[reg_count].Reg64 =3D cpu_get_apic_tpr(x86_cpu->apic_state); + if (reg_values[reg_count].Reg64 !=3D vcpu->tpr) { + vcpu->tpr =3D reg_values[reg_count].Reg64; + cpu->exit_request =3D 1; + reg_names[reg_count] =3D WHvX64RegisterCr8; + reg_count +=3D 1; + } + + /* Update the state of the interrupt delivery notification */ + if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + reg_values[reg_count].DeliverabilityNotifications.InterruptNotific= ation + =3D 1; + if (vcpu->window_registered !=3D 1) { + vcpu->window_registered =3D 1; + } + reg_names[reg_count] =3D WHvX64RegisterDeliverabilityNotifications; + reg_count +=3D 1; + } + + qemu_mutex_unlock_iothread(); + + if (reg_count) { + hr =3D WHvSetVirtualProcessorRegisters(whpx->partition, cpu->cpu_i= ndex, + reg_names, reg_count, reg_val= ues); + if (FAILED(hr)) { + error_report("WHPX: Failed to set interrupt state registers," + " hr=3D%08lx", hr); + __debugbreak(); + } + } + + return; +} + +static void whpx_vcpu_post_run(CPUState *cpu) +{ + HRESULT hr; + struct whpx_state *whpx =3D &whpx_global; + struct whpx_vcpu *vcpu =3D get_whpx_vcpu(cpu); + struct CPUX86State *env =3D (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu =3D X86_CPU(cpu); + WHV_REGISTER_VALUE reg_values[4]; + const WHV_REGISTER_NAME reg_names[4] =3D { + WHvX64RegisterRflags, + WHvX64RegisterCr8, + WHvRegisterPendingInterruption, + WHvRegisterInterruptState, + }; + + hr =3D WHvGetVirtualProcessorRegisters(whpx->partition, cpu->cpu_index, + reg_names, 4, reg_values); + if (FAILED(hr)) { + error_report("WHPX: Failed to get interrupt state regusters," + " hr=3D%08lx", hr); + __debugbreak(); + vcpu->interruptable =3D false; + return; + } + + assert(reg_names[0] =3D=3D WHvX64RegisterRflags); + env->eflags =3D reg_values[0].Reg64; + + assert(reg_names[1] =3D=3D WHvX64RegisterCr8); + if (vcpu->tpr !=3D reg_values[1].Reg64) { + vcpu->tpr =3D reg_values[1].Reg64; + qemu_mutex_lock_iothread(); + cpu_set_apic_tpr(x86_cpu->apic_state, vcpu->tpr); + qemu_mutex_unlock_iothread(); + } + + assert(reg_names[2] =3D=3D WHvRegisterPendingInterruption); + vcpu->interrupt_in_flight =3D reg_values[2].PendingInterruption; + + assert(reg_names[3] =3D=3D WHvRegisterInterruptState); + vcpu->interruptable =3D !reg_values[3].InterruptState.InterruptShadow; + + return; +} + +static void whpx_vcpu_process_async_events(CPUState *cpu) +{ + struct CPUX86State *env =3D (CPUArchState *)(cpu->env_ptr); + X86CPU *x86_cpu =3D X86_CPU(cpu); + struct whpx_vcpu *vcpu =3D get_whpx_vcpu(cpu); + + if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + !(env->hflags & HF_SMM_MASK)) { + + do_cpu_init(x86_cpu); + cpu->vcpu_dirty =3D true; + vcpu->interruptable =3D true; + } + + if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + cpu->interrupt_request &=3D ~CPU_INTERRUPT_POLL; + apic_poll_irq(x86_cpu->apic_state); + } + + if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + (env->eflags & IF_MASK)) || + (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu->halted =3D false; + } + + if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { + if (!cpu->vcpu_dirty) { + whpx_get_registers(cpu); + } + do_cpu_sipi(x86_cpu); + } + + if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + cpu->interrupt_request &=3D ~CPU_INTERRUPT_TPR; + if (!cpu->vcpu_dirty) { + whpx_get_registers(cpu); + } + apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, + env->tpr_access_type); + } + + return; +} + +static int whpx_vcpu_run(CPUState *cpu) +{ + HRESULT hr; + struct whpx_state *whpx =3D &whpx_global; + struct whpx_vcpu *vcpu =3D get_whpx_vcpu(cpu); + int ret; + + whpx_vcpu_process_async_events(cpu); + if (cpu->halted) { + cpu->exception_index =3D EXCP_HLT; + atomic_set(&cpu->exit_request, false); + return 0; + } + + qemu_mutex_unlock_iothread(); + cpu_exec_start(cpu); + + do { + if (cpu->vcpu_dirty) { + whpx_set_registers(cpu); + cpu->vcpu_dirty =3D false; + } + + whpx_vcpu_pre_run(cpu); + + if (atomic_read(&cpu->exit_request)) { + whpx_vcpu_kick(cpu); + } + + for (;;) { + hr =3D WHvRunVirtualProcessor(whpx->partition, cpu->cpu_index, + &vcpu->exit_ctx, whpx->exit_ctx_si= ze); + + if (SUCCEEDED(hr) && (vcpu->exit_ctx.ExitReason =3D=3D + WHvRunVpExitReasonAlerted)) { + WHvCancelRunVirtualProcessor(whpx->partition, cpu->cpu_ind= ex, + 0); + } else { + break; + } + } + + if (FAILED(hr)) { + error_report("WHPX: Failed to exec a virtual processor," + " hr=3D%08lx", hr); + ret =3D -1; + break; + } + + whpx_vcpu_post_run(cpu); + + switch (vcpu->exit_ctx.ExitReason) { + case WHvRunVpExitReasonMemoryAccess: + ret =3D whpx_handle_mmio(cpu, &vcpu->exit_ctx.MemoryAccess); + break; + + case WHvRunVpExitReasonX64IoPortAccess: + ret =3D whpx_handle_portio(cpu, &vcpu->exit_ctx.IoPortAccess); + break; + + case WHvRunVpExitReasonX64InterruptWindow: + vcpu->window_registered =3D 0; + break; + + case WHvRunVpExitReasonX64Halt: + ret =3D whpx_handle_halt(cpu); + break; + + case WHvRunVpExitReasonCanceled: + cpu->exception_index =3D EXCP_INTERRUPT; + ret =3D 1; + break; + + case WHvRunVpExitReasonNone: + case WHvRunVpExitReasonUnrecoverableException: + case WHvRunVpExitReasonInvalidVpRegisterValue: + case WHvRunVpExitReasonUnsupportedFeature: + case WHvRunVpExitReasonX64MsrAccess: + case WHvRunVpExitReasonX64Cpuid: + case WHvRunVpExitReasonException: + case WHvRunVpExitReasonAlerted: + default: + error_report("WHPX: Unexpected VP exit code %d", + vcpu->exit_ctx.ExitReason); + whpx_get_registers(cpu); + qemu_mutex_lock_iothread(); + qemu_system_guest_panicked(cpu_get_crash_info(cpu)); + qemu_mutex_unlock_iothread(); + break; + } + + } while (!ret); + + cpu_exec_end(cpu); + qemu_mutex_lock_iothread(); + current_cpu =3D cpu; + + atomic_set(&cpu->exit_request, false); + + return ret < 0; +} + +static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data a= rg) +{ + whpx_get_registers(cpu); + cpu->vcpu_dirty =3D true; +} + +static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu, + run_on_cpu_data arg) +{ + whpx_set_registers(cpu); + cpu->vcpu_dirty =3D false; +} + +static void do_whpx_cpu_synchronize_post_init(CPUState *cpu, + run_on_cpu_data arg) +{ + whpx_set_registers(cpu); + cpu->vcpu_dirty =3D false; +} + +static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, + run_on_cpu_data arg) +{ + cpu->vcpu_dirty =3D true; +} + +/* + * CPU support. + */ + +void whpx_cpu_synchronize_state(CPUState *cpu) +{ + if (!cpu->vcpu_dirty) { + run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL); + } +} + +void whpx_cpu_synchronize_post_reset(CPUState *cpu) +{ + run_on_cpu(cpu, do_whpx_cpu_synchronize_post_reset, RUN_ON_CPU_NULL); +} + +void whpx_cpu_synchronize_post_init(CPUState *cpu) +{ + run_on_cpu(cpu, do_whpx_cpu_synchronize_post_init, RUN_ON_CPU_NULL); +} + +void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) +{ + run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL); +} + +/* + * Vcpu support. + */ + +static Error *whpx_migration_blocker; + +int whpx_init_vcpu(CPUState *cpu) +{ + HRESULT hr; + struct whpx_state *whpx =3D &whpx_global; + struct whpx_vcpu *vcpu; + Error *local_error =3D NULL; + + /* Add migration blockers for all unsupported features of the + * Windows Hypervisor Platform + */ + if (whpx_migration_blocker =3D=3D NULL) { + error_setg(&whpx_migration_blocker, + "State blocked due to non-migratable CPUID feature support," + "dirty memory tracking support, and XSAVE/XRSTOR support"); + + (void)migrate_add_blocker(whpx_migration_blocker, &local_error); + if (local_error) { + error_report_err(local_error); + error_free(whpx_migration_blocker); + migrate_del_blocker(whpx_migration_blocker); + return -EINVAL; + } + } + + vcpu =3D g_malloc0(FIELD_OFFSET(struct whpx_vcpu, exit_ctx) + + whpx->exit_ctx_size); + + if (!vcpu) { + error_report("WHPX: Failed to allocte VCPU context."); + return -ENOMEM; + } + + hr =3D WHvEmulatorCreateEmulator(whpx_emu_callbacks, &vcpu->emulator); + if (FAILED(hr)) { + error_report("WHPX: Failed to setup instruction completion support= ," + " hr=3D%08lx", hr); + g_free(vcpu); + return -EINVAL; + } + + hr =3D WHvCreateVirtualProcessor(whpx->partition, cpu->cpu_index, 0); + if (FAILED(hr)) { + error_report("WHPX: Failed to create a virtual processor," + " hr=3D%08lx", hr); + WHvEmulatorDestroyEmulator(vcpu->emulator); + g_free(vcpu); + return -EINVAL; + } + + vcpu->interruptable =3D true; + + cpu->vcpu_dirty =3D true; + cpu->hax_vcpu =3D (struct hax_vcpu_state *)vcpu; + + return 0; +} + +int whpx_vcpu_exec(CPUState *cpu) +{ + int ret; + int fatal; + + for (;;) { + if (cpu->exception_index >=3D EXCP_INTERRUPT) { + ret =3D cpu->exception_index; + cpu->exception_index =3D -1; + break; + } + + fatal =3D whpx_vcpu_run(cpu); + + if (fatal) { + error_report("WHPX: Failed to exec a virtual processor"); + abort(); + } + } + + return ret; +} + +void whpx_destroy_vcpu(CPUState *cpu) +{ + struct whpx_state *whpx =3D &whpx_global; + struct whpx_vcpu *vcpu =3D get_whpx_vcpu(cpu); + + WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index); + WHvEmulatorDestroyEmulator(vcpu->emulator); + g_free(cpu->hax_vcpu); + return; +} + +void whpx_vcpu_kick(CPUState *cpu) +{ + struct whpx_state *whpx =3D &whpx_global; + WHvCancelRunVirtualProcessor(whpx->partition, cpu->cpu_index, 0); +} + +/* + * Memory support. + */ + +static void whpx_update_mapping(hwaddr start_pa, ram_addr_t size, + void *host_va, int add, int rom, + const char *name) +{ + struct whpx_state *whpx =3D &whpx_global; + HRESULT hr; + + /* + if (add) { + printf("WHPX: ADD PA:%p Size:%p, Host:%p, %s, '%s'\n", + (void*)start_pa, (void*)size, host_va, + (rom ? "ROM" : "RAM"), name); + } else { + printf("WHPX: DEL PA:%p Size:%p, Host:%p, '%s'\n", + (void*)start_pa, (void*)size, host_va, name); + } + */ + + if (add) { + hr =3D WHvMapGpaRange(whpx->partition, + host_va, + start_pa, + size, + (WHvMapGpaRangeFlagRead | + WHvMapGpaRangeFlagExecute | + (rom ? 0 : WHvMapGpaRangeFlagWrite))); + } else { + hr =3D WHvUnmapGpaRange(whpx->partition, + start_pa, + size); + } + + if (FAILED(hr)) { + error_report("WHPX: Failed to %s GPA range '%s' PA:%p, Size:%p byt= es," + " Host:%p, hr=3D%08lx", + (add ? "MAP" : "UNMAP"), name, + (void *)start_pa, (void *)size, host_va, hr); + } +} + +static void whpx_process_section(MemoryRegionSection *section, int add) +{ + MemoryRegion *mr =3D section->mr; + hwaddr start_pa =3D section->offset_within_address_space; + ram_addr_t size =3D int128_get64(section->size); + unsigned int delta; + uint64_t host_va; + + if (!memory_region_is_ram(mr)) { + return; + } + + delta =3D qemu_real_host_page_size - (start_pa & ~qemu_real_host_page_= mask); + delta &=3D ~qemu_real_host_page_mask; + if (delta > size) { + return; + } + start_pa +=3D delta; + size -=3D delta; + size &=3D qemu_real_host_page_mask; + if (!size || (start_pa & ~qemu_real_host_page_mask)) { + return; + } + + host_va =3D (uintptr_t)memory_region_get_ram_ptr(mr) + + section->offset_within_region + delta; + + whpx_update_mapping(start_pa, size, (void *)host_va, add, + memory_region_is_rom(mr), mr->name); +} + +static void whpx_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + memory_region_ref(section->mr); + whpx_process_section(section, 1); +} + +static void whpx_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + whpx_process_section(section, 0); + memory_region_unref(section->mr); +} + +static void whpx_transaction_begin(MemoryListener *listener) +{ +} + +static void whpx_transaction_commit(MemoryListener *listener) +{ +} + +static void whpx_log_sync(MemoryListener *listener, + MemoryRegionSection *section) +{ + MemoryRegion *mr =3D section->mr; + + if (!memory_region_is_ram(mr)) { + return; + } + + memory_region_set_dirty(mr, 0, int128_get64(section->size)); +} + +static MemoryListener whpx_memory_listener =3D { + .begin =3D whpx_transaction_begin, + .commit =3D whpx_transaction_commit, + .region_add =3D whpx_region_add, + .region_del =3D whpx_region_del, + .log_sync =3D whpx_log_sync, + .priority =3D 10, +}; + +static void whpx_memory_init(void) +{ + memory_listener_register(&whpx_memory_listener, &address_space_memory); +} + +static void whpx_handle_interrupt(CPUState *cpu, int mask) +{ + cpu->interrupt_request |=3D mask; + + if (!qemu_cpu_is_self(cpu)) { + qemu_cpu_kick(cpu); + } +} + +/* + * Partition support + */ + +static int whpx_accel_init(MachineState *ms) +{ + struct whpx_state *whpx; + int ret; + HRESULT hr; + WHV_CAPABILITY whpx_cap; + WHV_PARTITION_PROPERTY prop; + + whpx =3D &whpx_global; + + memset(whpx, 0, sizeof(struct whpx_state)); + whpx->mem_quota =3D ms->ram_size; + + hr =3D WHvGetCapability(WHvCapabilityCodeHypervisorPresent, &whpx_cap, + sizeof(whpx_cap)); + if (FAILED(hr) || !whpx_cap.HypervisorPresent) { + error_report("WHPX: No accelerator found, hr=3D%08lx", hr); + ret =3D -ENOSPC; + goto error; + } + + hr =3D WHvCreatePartition(&whpx->partition); + if (FAILED(hr)) { + error_report("WHPX: Failed to create partition, hr=3D%08lx", hr); + ret =3D -EINVAL; + goto error; + } + + memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY)); + prop.PropertyCode =3D WHvPartitionPropertyCodeProcessorCount; + prop.ProcessorCount =3D smp_cpus; + hr =3D WHvSetPartitionProperty(whpx->partition, + &prop, + sizeof(WHV_PARTITION_PROPERTY)); + + if (FAILED(hr)) { + error_report("WHPX: Failed to set partition core count to %d," + " hr=3D%08lx", smp_cores, hr); + ret =3D -EINVAL; + goto error; + } + + hr =3D WHvSetupPartition(whpx->partition); + if (FAILED(hr)) { + error_report("WHPX: Failed to setup partition, hr=3D%08lx", hr); + ret =3D -EINVAL; + goto error; + } + + whpx->exit_ctx_size =3D WHvGetRunExitContextSize(); + assert(whpx->exit_ctx_size); + + whpx_memory_init(); + + cpu_interrupt_handler =3D whpx_handle_interrupt; + + printf("Windows Hypervisor Platform accelerator is operational\n"); + return 0; + + error: + + if (NULL !=3D whpx->partition) { + WHvDeletePartition(whpx->partition); + whpx->partition =3D NULL; + } + + + return ret; +} + +int whpx_enabled(void) +{ + return whpx_allowed; +} + +static void whpx_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac =3D ACCEL_CLASS(oc); + ac->name =3D "WHPX"; + ac->init_machine =3D whpx_accel_init; + ac->allowed =3D &whpx_allowed; +} + +static const TypeInfo whpx_accel_type =3D { + .name =3D ACCEL_CLASS_NAME("whpx"), + .parent =3D TYPE_ACCEL, + .class_init =3D whpx_accel_class_init, +}; + +static void whpx_type_init(void) +{ + type_register_static(&whpx_accel_type); +} + +type_init(whpx_type_init); --=20 1.8.3.1