get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/1.1/patches/2225091/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2225091,
    "url": "http://patchwork.ozlabs.org/api/1.1/patches/2225091/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260420104248.86702-32-mohamed@unpredictable.fr/",
    "project": {
        "id": 14,
        "url": "http://patchwork.ozlabs.org/api/1.1/projects/14/?format=api",
        "name": "QEMU Development",
        "link_name": "qemu-devel",
        "list_id": "qemu-devel.nongnu.org",
        "list_email": "qemu-devel@nongnu.org",
        "web_url": "",
        "scm_url": "",
        "webscm_url": ""
    },
    "msgid": "<20260420104248.86702-32-mohamed@unpredictable.fr>",
    "date": "2026-04-20T10:42:41",
    "name": "[v2,31/38] whpx: xsave support",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "c55216a00f1b1cc999a27d96ab7b149ea8f81de8",
    "submitter": {
        "id": 91318,
        "url": "http://patchwork.ozlabs.org/api/1.1/people/91318/?format=api",
        "name": "Mohamed Mediouni",
        "email": "mohamed@unpredictable.fr"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/qemu-devel/patch/20260420104248.86702-32-mohamed@unpredictable.fr/mbox/",
    "series": [
        {
            "id": 500592,
            "url": "http://patchwork.ozlabs.org/api/1.1/series/500592/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/qemu-devel/list/?series=500592",
            "date": "2026-04-20T10:42:10",
            "name": "WHPX x86 updates for QEMU 11.1",
            "version": 2,
            "mbox": "http://patchwork.ozlabs.org/series/500592/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2225091/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2225091/checks/",
    "tags": {},
    "headers": {
        "Return-Path": "<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>",
        "X-Original-To": "incoming@patchwork.ozlabs.org",
        "Delivered-To": "patchwork-incoming@legolas.ozlabs.org",
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=unpredictable.fr header.i=@unpredictable.fr\n header.a=rsa-sha256 header.s=sig1 header.b=V7XdVDc1;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"
        ],
        "Received": [
            "from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fzhzg1p70z1yCv\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 20 Apr 2026 20:48:31 +1000 (AEST)",
            "from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wEm71-0008LP-5Q; Mon, 20 Apr 2026 06:44:19 -0400",
            "from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <mohamed@unpredictable.fr>)\n id 1wEm70-0008Ko-6p\n for qemu-devel@nongnu.org; Mon, 20 Apr 2026 06:44:18 -0400",
            "from p-west3-cluster4-host11-snip4-10.eps.apple.com ([57.103.74.221]\n helo=outbound.ms.icloud.com)\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <mohamed@unpredictable.fr>)\n id 1wEm6w-0000m2-7s\n for qemu-devel@nongnu.org; Mon, 20 Apr 2026 06:44:17 -0400",
            "from outbound.ms.icloud.com (unknown [127.0.0.2])\n by p00-icloudmta-asmtp-us-west-3a-100-percent-1 (Postfix) with ESMTPS id\n 251371800451; Mon, 20 Apr 2026 10:44:11 +0000 (UTC)",
            "from localhost.localdomain (unknown [17.57.154.37])\n by p00-icloudmta-asmtp-us-west-3a-100-percent-1 (Postfix) with ESMTPSA id\n 9669D1800159; Mon, 20 Apr 2026 10:44:08 +0000 (UTC)"
        ],
        "Dkim-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=unpredictable.fr;\n s=sig1; t=1776681853; x=1779273853;\n bh=tbMliXtkqk8dPHBK0hLkGecREvcpTdLtake6KyI3WL8=;\n h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme;\n b=V7XdVDc1+XTOwx6v5+f57H3xAUMNtmfBwphIFmv8pOhEmEaS3VOHBOTJSsrpaxAUdzwQdaEJqqEtgbl/3L+kCJ+qOXlbuxzfznTqfMLomzEAITFM7eJzEgGEhvaoHOLJC07l7/mbVymCgzqfcxy7Acfkva859Dp+6sjusij+lOcVgLF+6BLHKEjRLovxmnRzICOL/F//ffvmYFQa9b7vENBb8jECvYJ0wGGfeXtGrqFGVvddcvkr4VgxBOcmoHNMellPPzCTotMoiQfDdt0rioRXzGxlAH8UMrQiW4swZ1zVzenYu6wX5/xPhS/7lJUO6PalDSbBApTfG2j3ylkR8A==",
        "mail-alias-created-date": "1752046281608",
        "From": "Mohamed Mediouni <mohamed@unpredictable.fr>",
        "To": "qemu-devel@nongnu.org",
        "Cc": "qemu-arm@nongnu.org, Mohamed Mediouni <mohamed@unpredictable.fr>,\n Paolo Bonzini <pbonzini@redhat.com>,\n Phil Dennis-Jordan <phil@philjordan.eu>,\n Roman Bolshakov <rbolshakov@ddn.com>,\n Pierrick Bouvier <pierrick.bouvier@linaro.org>,\n Pedro Barbuda <pbarbuda@microsoft.com>, Wei Liu <wei.liu@kernel.org>,\n \"Michael S. Tsirkin\" <mst@redhat.com>,\n Peter Maydell <peter.maydell@linaro.org>, Zhao Liu <zhao1.liu@intel.com>",
        "Subject": "[PATCH v2 31/38] whpx: xsave support",
        "Date": "Mon, 20 Apr 2026 12:42:41 +0200",
        "Message-ID": "<20260420104248.86702-32-mohamed@unpredictable.fr>",
        "X-Mailer": "git-send-email 2.50.1",
        "In-Reply-To": "<20260420104248.86702-1-mohamed@unpredictable.fr>",
        "References": "<20260420104248.86702-1-mohamed@unpredictable.fr>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-Proofpoint-ORIG-GUID": "bO-wrEJn44fpQzUefPwBq6egDJYePCFq",
        "X-Proofpoint-GUID": "bO-wrEJn44fpQzUefPwBq6egDJYePCFq",
        "X-Authority-Info-Out": "v=2.4 cv=cKftc1eN c=1 sm=1 tr=0 ts=69e6037b\n cx=c_apl:c_pps:t_out a=qkKslKyYc0ctBTeLUVfTFg==:117 a=A5OVakUREuEA:10\n a=VkNPw1HP01LnGYTKEx00:22 a=pIzSZyhXsI4QXQPOM_AA:9",
        "X-Proofpoint-Spam-Details-Enc": "AW1haW4tMjYwNDIwMDEwNCBTYWx0ZWRfXxELx/m+9X/Ic\n Q9UioB4ugv0c07Qj2JgxzQUo2Z4YKyEy7nycL/uX3WjK2YSzB3OGfJmr+hcO9xVnhLwsjOLcIIs\n m2vdwg2jcrun/33wLp3ImmdhNuIcT2K37wLIzGNjnu1ohal5ws6/2aJOZgZ9AZZSUwA0vp53pAA\n MHofpj2BzA4nkmXWSu8lTkGOtuNHIS2z2XTO/UxRKPs3ULb/ZItADrA1QEtbH16Eb/diBdUKNIQ\n +4qFlzlhhufIioUO5rd9SMC7155sRVR1P3KK0S/aIFQaLY5GNJqUhmymIXyDH05C1zoV6M1DSYa\n XxqVAB5UX5C6t9vgffnGvc1NpH6T+xNX5bAHc7jwszu0oE1s8o655zQ8XevM2Y=",
        "X-Proofpoint-Virus-Version": "vendor=baseguard\n engine=ICAP:2.0.293,Aquarius:18.0.1143,Hydra:6.1.51,FMLib:17.12.100.49\n definitions=2026-04-20_02,2026-04-17_04,2025-10-01_01",
        "X-Proofpoint-Spam-Details": "rule=notspam policy=default score=0 suspectscore=0\n lowpriorityscore=0 malwarescore=0 clxscore=1030\n mlxlogscore=999 adultscore=0\n mlxscore=0 bulkscore=0 phishscore=0 spamscore=0 classifier=spam authscore=0\n adjust=0 reason=mlx scancount=1 engine=8.22.0-2601150000\n definitions=main-2604200104",
        "Received-SPF": "pass client-ip=57.103.74.221;\n envelope-from=mohamed@unpredictable.fr; helo=outbound.ms.icloud.com",
        "X-Spam_score_int": "-27",
        "X-Spam_score": "-2.8",
        "X-Spam_bar": "--",
        "X-Spam_report": "(-2.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,\n DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1,\n RCVD_IN_DNSWL_LOW=-0.7, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001,\n SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no",
        "X-Spam_action": "no action",
        "X-BeenThere": "qemu-devel@nongnu.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "qemu development <qemu-devel.nongnu.org>",
        "List-Unsubscribe": "<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>",
        "List-Archive": "<https://lists.nongnu.org/archive/html/qemu-devel>",
        "List-Post": "<mailto:qemu-devel@nongnu.org>",
        "List-Help": "<mailto:qemu-devel-request@nongnu.org?subject=help>",
        "List-Subscribe": "<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>",
        "Errors-To": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org",
        "Sender": "qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"
    },
    "content": "Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>\n---\n include/system/whpx-internal.h |  16 ++\n target/i386/whpx/whpx-all.c    | 379 ++++++++++++++++++++++++---------\n 2 files changed, 292 insertions(+), 103 deletions(-)",
    "diff": "diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h\nindex 86639627b3..0aae83bd7c 100644\n--- a/include/system/whpx-internal.h\n+++ b/include/system/whpx-internal.h\n@@ -99,6 +99,22 @@ void whpx_apic_get(APICCommonState *s);\n          UINT32 StateSize)) \\\n   X(HRESULT, WHvResetPartition, \\\n         (WHV_PARTITION_HANDLE Partition)) \\\n+  X(HRESULT, WHvGetVirtualProcessorXsaveState, \\\n+        (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \\\n+        PVOID Buffer, \\\n+        UINT32 BufferSizeInBytes, UINT32 *BytesWritten)) \\\n+  X(HRESULT, WHvSetVirtualProcessorXsaveState, \\\n+        (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \\\n+        PVOID Buffer, \\\n+        UINT32 BufferSizeInBytes)) \\\n+  X(HRESULT, WHvGetVirtualProcessorState, \\\n+        (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \\\n+        WHV_VIRTUAL_PROCESSOR_STATE_TYPE StateType, PVOID Buffer, \\\n+        UINT32 BufferSizeInBytes, UINT32 *BytesWritten)) \\\n+  X(HRESULT, WHvSetVirtualProcessorState, \\\n+        (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \\\n+        WHV_VIRTUAL_PROCESSOR_STATE_TYPE StateType, PVOID Buffer, \\\n+        UINT32 BufferSizeInBytes)) \\\n   LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL_ARCH(X)\n \n #define WHP_DEFINE_TYPE(return_type, function_name, signature) \\\ndiff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c\nindex 045dbdfdc8..9b2488b801 100644\n--- a/target/i386/whpx/whpx-all.c\n+++ b/target/i386/whpx/whpx-all.c\n@@ -10,6 +10,7 @@\n \n #include \"qemu/osdep.h\"\n #include \"cpu.h\"\n+#include \"qemu/typedefs.h\"\n #include \"system/address-spaces.h\"\n #include \"system/ioport.h\"\n #include \"gdbstub/helpers.h\"\n@@ -20,6 +21,7 @@\n #include \"system/cpus.h\"\n #include \"system/runstate.h\"\n #include \"qemu/main-loop.h\"\n+#include \"qemu/memalign.h\"\n #include \"hw/core/boards.h\"\n #include \"hw/intc/ioapic.h\"\n #include \"hw/intc/i8259.h\"\n@@ -108,34 +110,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = {\n      * WHvX64RegisterDr7,\n      */\n \n-    /* X64 Floating Point and Vector Registers */\n-    WHvX64RegisterXmm0,\n-    WHvX64RegisterXmm1,\n-    WHvX64RegisterXmm2,\n-    WHvX64RegisterXmm3,\n-    WHvX64RegisterXmm4,\n-    WHvX64RegisterXmm5,\n-    WHvX64RegisterXmm6,\n-    WHvX64RegisterXmm7,\n-    WHvX64RegisterXmm8,\n-    WHvX64RegisterXmm9,\n-    WHvX64RegisterXmm10,\n-    WHvX64RegisterXmm11,\n-    WHvX64RegisterXmm12,\n-    WHvX64RegisterXmm13,\n-    WHvX64RegisterXmm14,\n-    WHvX64RegisterXmm15,\n-    WHvX64RegisterFpMmx0,\n-    WHvX64RegisterFpMmx1,\n-    WHvX64RegisterFpMmx2,\n-    WHvX64RegisterFpMmx3,\n-    WHvX64RegisterFpMmx4,\n-    WHvX64RegisterFpMmx5,\n-    WHvX64RegisterFpMmx6,\n-    WHvX64RegisterFpMmx7,\n-    WHvX64RegisterFpControlStatus,\n-    WHvX64RegisterXmmControlStatus,\n-\n     /* X64 MSRs */\n     WHvX64RegisterEfer,\n #ifdef TARGET_X86_64\n@@ -182,6 +156,36 @@ static const WHV_REGISTER_NAME whpx_register_names_for_vmexit[] = {\n     WHvX64RegisterR15,\n };\n \n+static const WHV_REGISTER_NAME whpx_register_names_legacy_fp[] = {\n+    /* X64 Floating Point and Vector Registers (non-xsave) */\n+    WHvX64RegisterXmm0,\n+    WHvX64RegisterXmm1,\n+    WHvX64RegisterXmm2,\n+    WHvX64RegisterXmm3,\n+    WHvX64RegisterXmm4,\n+    WHvX64RegisterXmm5,\n+    WHvX64RegisterXmm6,\n+    WHvX64RegisterXmm7,\n+    WHvX64RegisterXmm8,\n+    WHvX64RegisterXmm9,\n+    WHvX64RegisterXmm10,\n+    WHvX64RegisterXmm11,\n+    WHvX64RegisterXmm12,\n+    WHvX64RegisterXmm13,\n+    WHvX64RegisterXmm14,\n+    WHvX64RegisterXmm15,\n+    WHvX64RegisterFpMmx0,\n+    WHvX64RegisterFpMmx1,\n+    WHvX64RegisterFpMmx2,\n+    WHvX64RegisterFpMmx3,\n+    WHvX64RegisterFpMmx4,\n+    WHvX64RegisterFpMmx5,\n+    WHvX64RegisterFpMmx6,\n+    WHvX64RegisterFpMmx7,\n+    WHvX64RegisterFpControlStatus,\n+    WHvX64RegisterXmmControlStatus,\n+};\n+\n struct whpx_register_set {\n     WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)];\n };\n@@ -392,6 +396,123 @@ static int whpx_set_tsc(CPUState *cpu)\n     return 0;\n }\n \n+static bool whpx_is_xsave_enabled(CPUState *cpu)\n+{\n+    CPUX86State *env = &X86_CPU(cpu)->env;\n+    return env->cr[4] & CR4_OSXSAVE_MASK;\n+}\n+\n+static size_t whpx_get_xsave_max_len(void)\n+{\n+    return whpx_get_supported_cpuid(0xd, 0, R_ECX);\n+}\n+\n+static int whpx_set_xsave_state(const CPUState *cpu)\n+{\n+    struct whpx_state *whpx = &whpx_global;\n+    X86CPU *x86cpu = X86_CPU(cpu);\n+    CPUX86State *env = &x86cpu->env;\n+    HRESULT hr;\n+    void *xsavec_buf;\n+    size_t page = qemu_real_host_page_size();\n+    size_t xsavec_buf_len;\n+\n+    /* allocate and populate compacted buffer */\n+    xsavec_buf_len = whpx_get_xsave_max_len();\n+    xsavec_buf = qemu_memalign(page, xsavec_buf_len);\n+\n+    /* save registers to standard format buffer */\n+    x86_cpu_xsave_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);\n+\n+    /* store compacted version of xsave area in xsavec_buf */\n+    compact_xsave_area(env, xsavec_buf, xsavec_buf_len);\n+\n+    if (!whpx_is_legacy_os()) {\n+        hr = whp_dispatch.WHvSetVirtualProcessorState(\n+            whpx->partition, cpu->cpu_index,\n+            WHvVirtualProcessorStateTypeXsaveState,\n+            xsavec_buf,\n+            xsavec_buf_len);\n+    } else {\n+        hr = whp_dispatch.WHvSetVirtualProcessorXsaveState(\n+            whpx->partition, cpu->cpu_index,\n+            xsavec_buf,\n+            xsavec_buf_len);\n+    }\n+\n+    qemu_vfree(xsavec_buf);\n+    if (FAILED(hr)) {\n+        error_report(\"WHPX: Failed to get virtual processor context, hr=%08lx\",\n+                     hr);\n+    }\n+\n+    return 0;\n+}\n+\n+static void whpx_set_legacy_fp_registers(CPUState *cpu, WHPXStateLevel level)\n+{\n+    struct whpx_state *whpx = &whpx_global;\n+    X86CPU *x86_cpu = X86_CPU(cpu);\n+    CPUX86State *env = &x86_cpu->env;\n+    struct whpx_register_set vcxt;\n+    HRESULT hr;\n+    int idx = 0;\n+    int i;\n+    int idx_next;\n+\n+    assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));\n+\n+    /* 16 XMM registers */\n+    assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmm0);\n+    idx_next = idx + 16;\n+    for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {\n+        vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);\n+        vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);\n+    }\n+    idx = idx_next;\n+\n+    /* 8 FP registers */\n+    assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpMmx0);\n+    for (i = 0; i < 8; i += 1, idx += 1) {\n+        vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);\n+        /* vcxt.values[idx].Fp.AsUINT128.High64 =\n+               env->fpregs[i].mmx.MMX_Q(1);\n+        */\n+    }\n+\n+    /* FP control status register */\n+    assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpControlStatus);\n+    vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;\n+    vcxt.values[idx].FpControlStatus.FpStatus =\n+        (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;\n+    vcxt.values[idx].FpControlStatus.FpTag = 0;\n+    for (i = 0; i < 8; ++i) {\n+        vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;\n+    }\n+    vcxt.values[idx].FpControlStatus.Reserved = 0;\n+    vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;\n+    vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;\n+    idx += 1;\n+\n+    /* XMM control status register */\n+    assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmmControlStatus);\n+    vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;\n+    vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;\n+    vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;\n+    idx += 1;\n+\n+    hr = whp_dispatch.WHvSetVirtualProcessorRegisters(\n+        whpx->partition, cpu->cpu_index,\n+        whpx_register_names_legacy_fp,\n+        idx,\n+        &vcxt.values[0]);\n+\n+    if (FAILED(hr)) {\n+        error_report(\"WHPX: Failed to set virtual processor context, hr=%08lx\",\n+                     hr);\n+    }\n+}\n+\n void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)\n {\n     struct whpx_state *whpx = &whpx_global;\n@@ -491,45 +612,11 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)\n          */\n         whpx_set_xcrs(cpu);\n \n-        /* 16 XMM registers */\n-        assert(whpx_register_names[idx] == WHvX64RegisterXmm0);\n-        idx_next = idx + 16;\n-        for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {\n-            vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);\n-            vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);\n-        }\n-        idx = idx_next;\n-\n-        /* 8 FP registers */\n-        assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);\n-        for (i = 0; i < 8; i += 1, idx += 1) {\n-            vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);\n-            /* vcxt.values[idx].Fp.AsUINT128.High64 =\n-                       env->fpregs[i].mmx.MMX_Q(1);\n-            */\n-        }\n-\n-        /* FP control status register */\n-        assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);\n-        vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;\n-        vcxt.values[idx].FpControlStatus.FpStatus =\n-            (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;\n-        vcxt.values[idx].FpControlStatus.FpTag = 0;\n-        for (i = 0; i < 8; ++i) {\n-            vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;\n+        if (whpx_is_xsave_enabled(cpu)) {\n+            whpx_set_xsave_state(cpu);\n+        } else {\n+            whpx_set_legacy_fp_registers(cpu, level);\n         }\n-        vcxt.values[idx].FpControlStatus.Reserved = 0;\n-        vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;\n-        vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;\n-        idx += 1;\n-\n-        /* XMM control status register */\n-        assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);\n-        vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;\n-        vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;\n-        vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;\n-        idx += 1;\n-\n         /* MSRs */\n         assert(whpx_register_names[idx] == WHvX64RegisterEfer);\n         vcxt.values[idx++].Reg64 = env->efer;\n@@ -662,6 +749,110 @@ static void whpx_get_registers_for_vmexit(CPUState *cpu, WHPXStateLevel level)\n     x86_update_hflags(env);\n }\n \n+static void whpx_get_legacy_fp_registers(CPUState *cpu, WHPXStateLevel level)\n+{\n+    struct whpx_state *whpx = &whpx_global;\n+    X86CPU *x86_cpu = X86_CPU(cpu);\n+    CPUX86State *env = &x86_cpu->env;\n+    struct whpx_register_set vcxt;\n+    HRESULT hr;\n+    int i;\n+    int idx;\n+    int idx_next;\n+\n+    assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));\n+\n+    hr = whp_dispatch.WHvGetVirtualProcessorRegisters(\n+        whpx->partition, cpu->cpu_index,\n+        whpx_register_names_legacy_fp,\n+        RTL_NUMBER_OF(whpx_register_names_legacy_fp),\n+        &vcxt.values[0]);\n+\n+    if (FAILED(hr)) {\n+        error_report(\"WHPX: Failed to get virtual processor context, hr=%08lx\",\n+                     hr);\n+    }\n+\n+    idx = 0;\n+    /* 16 XMM registers */\n+    assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmm0);\n+    idx_next = idx + 16;\n+    for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {\n+        env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;\n+        env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;\n+    }\n+    idx = idx_next;\n+\n+    /* 8 FP registers */\n+    assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpMmx0);\n+    for (i = 0; i < 8; i += 1, idx += 1) {\n+        env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;\n+        /* env->fpregs[i].mmx.MMX_Q(1) =\n+               vcxt.values[idx].Fp.AsUINT128.High64;\n+        */\n+    }\n+\n+    /* FP control status register */\n+    assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpControlStatus);\n+    env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;\n+    env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;\n+    env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;\n+    for (i = 0; i < 8; ++i) {\n+        env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);\n+    }\n+    env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;\n+    env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;\n+    idx += 1;\n+\n+    /* XMM control status register */\n+    assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmmControlStatus);\n+    env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;\n+    idx += 1;\n+}\n+\n+static int whpx_get_xsave_state(CPUState *cpu)\n+{\n+    struct whpx_state *whpx = &whpx_global;\n+    X86CPU *x86cpu = X86_CPU(cpu);\n+    CPUX86State *env = &x86cpu->env;\n+    int ret;\n+    HRESULT hr;\n+    void *xsavec_buf;\n+    const size_t page = qemu_real_host_page_size();\n+    size_t xsavec_buf_len = whpx_get_xsave_max_len();\n+    UINT32 bytes_written;\n+\n+    xsavec_buf = qemu_memalign(page, xsavec_buf_len);\n+    memset(xsavec_buf, 0, xsavec_buf_len);\n+\n+    if (!whpx_is_legacy_os()) {\n+        hr = whp_dispatch.WHvGetVirtualProcessorState(\n+            whpx->partition, cpu->cpu_index,\n+            WHvVirtualProcessorStateTypeXsaveState,\n+            xsavec_buf,\n+            xsavec_buf_len, &bytes_written);\n+    } else {\n+        hr = whp_dispatch.WHvGetVirtualProcessorXsaveState(\n+            whpx->partition, cpu->cpu_index,\n+            xsavec_buf,\n+            xsavec_buf_len, &bytes_written);\n+    }\n+    if (FAILED(hr) || bytes_written == 0) {\n+        error_report(\"failed to get xsave state: %s\", strerror(errno));\n+        return -errno;\n+    }\n+\n+    ret = decompact_xsave_area(xsavec_buf, xsavec_buf_len, env);\n+    qemu_vfree(xsavec_buf);\n+    if (ret < 0) {\n+        error_report(\"failed to decompact xsave area\");\n+        return ret;\n+    }\n+    x86_cpu_xrstor_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);\n+\n+    return 0;\n+}\n+\n void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)\n {\n     struct whpx_state *whpx = &whpx_global;\n@@ -758,40 +949,11 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)\n      */\n     whpx_get_xcrs(cpu);\n \n-    /* 16 XMM registers */\n-    assert(whpx_register_names[idx] == WHvX64RegisterXmm0);\n-    idx_next = idx + 16;\n-    for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {\n-        env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;\n-        env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;\n-    }\n-    idx = idx_next;\n-\n-    /* 8 FP registers */\n-    assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);\n-    for (i = 0; i < 8; i += 1, idx += 1) {\n-        env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;\n-        /* env->fpregs[i].mmx.MMX_Q(1) =\n-               vcxt.values[idx].Fp.AsUINT128.High64;\n-        */\n-    }\n-\n-    /* FP control status register */\n-    assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);\n-    env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;\n-    env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;\n-    env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;\n-    for (i = 0; i < 8; ++i) {\n-        env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);\n+    if (whpx_is_xsave_enabled(cpu)) {\n+        whpx_get_xsave_state(cpu);\n+    } else {\n+        whpx_get_legacy_fp_registers(cpu, level);\n     }\n-    env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;\n-    env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;\n-    idx += 1;\n-\n-    /* XMM control status register */\n-    assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);\n-    env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;\n-    idx += 1;\n \n     /* MSRs */\n     assert(whpx_register_names[idx] == WHvX64RegisterEfer);\n@@ -1525,6 +1687,9 @@ void whpx_arch_destroy_vcpu(CPUState *cpu)\n     X86CPU *x86cpu = X86_CPU(cpu);\n     CPUX86State *env = &x86cpu->env;\n     g_free(env->emu_mmio_buf);\n+    qemu_vfree(env->xsave_buf);\n+    env->xsave_buf = NULL;\n+    env->xsave_buf_len = 0;\n }\n \n /* Returns the address of the next instruction that is about to be executed. */\n@@ -2393,6 +2558,9 @@ int whpx_init_vcpu(CPUState *cpu)\n     Error *local_error = NULL;\n     X86CPU *x86_cpu = X86_CPU(cpu);\n     CPUX86State *env = &x86_cpu->env;\n+    X86XSaveHeader *header;\n+    size_t page_size = qemu_real_host_page_size();\n+    size_t xsave_len;\n     UINT64 freq = 0;\n     int ret;\n \n@@ -2469,6 +2637,15 @@ int whpx_init_vcpu(CPUState *cpu)\n     qemu_add_vm_change_state_handler(whpx_cpu_update_state, env);\n \n     env->emu_mmio_buf = g_new(char, 4096);\n+    /* Initialize XSAVE buffer page-aligned */\n+    xsave_len = whpx_get_xsave_max_len();\n+    env->xsave_buf = qemu_memalign(page_size, xsave_len);\n+    env->xsave_buf_len = xsave_len;\n+    memset(env->xsave_buf, 0, env->xsave_buf_len);\n+\n+    /* we need to set the compacted format bit in xsave header for Hyper-V */\n+    header = (X86XSaveHeader *)(env->xsave_buf + sizeof(X86LegacyXSaveArea));\n+    header->xcomp_bv = header->xstate_bv | (1ULL << 63);\n \n     return 0;\n \n@@ -2628,10 +2805,6 @@ int whpx_accel_init(AccelState *as, MachineState *ms)\n         error_report(\"WHPX: Failed to query XSAVE capability, hr=%08lx\", hr);\n     }\n \n-    if (!whpx_has_xsave()) {\n-        printf(\"WHPX: Partition is not XSAVE capable\\n\");\n-    }\n-\n     memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));\n     prop.ProcessorCount = ms->smp.cpus;\n     hr = whp_dispatch.WHvSetPartitionProperty(\n",
    "prefixes": [
        "v2",
        "31/38"
    ]
}