{"id":2219420,"url":"http://patchwork.ozlabs.org/api/1.0/patches/2219420/?format=json","project":{"id":14,"url":"http://patchwork.ozlabs.org/api/1.0/projects/14/?format=json","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":"<20260402234724.88276-3-mohamed@unpredictable.fr>","date":"2026-04-02T23:47:19","name":"[v3,2/7] whpx: i386: x2apic emulation","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"ebac3004b414e1e140587e833b3fa5c9dcb9877e","submitter":{"id":91318,"url":"http://patchwork.ozlabs.org/api/1.0/people/91318/?format=json","name":"Mohamed Mediouni","email":"mohamed@unpredictable.fr"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/qemu-devel/patch/20260402234724.88276-3-mohamed@unpredictable.fr/mbox/","series":[{"id":498566,"url":"http://patchwork.ozlabs.org/api/1.0/series/498566/?format=json","date":"2026-04-02T23:47:19","name":"whpx: i386: x2apic emulation for kernel-irqchip=off, feature probing","version":3,"mbox":"http://patchwork.ozlabs.org/series/498566/mbox/"}],"check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2219420/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=TqaNRcCW;\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=lists.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"],"Received":["from lists.gnu.org (lists.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 4fmz8T06vDz1yFv\n\tfor <incoming@patchwork.ozlabs.org>; Fri, 03 Apr 2026 10:48:55 +1100 (AEDT)","from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1w8RlD-0004G2-0F; Thu, 02 Apr 2026 19:47:39 -0400","from eggs.gnu.org ([2001:470:142:3::10])\n by lists.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 1w8RlA-0004FS-RJ\n for qemu-devel@nongnu.org; Thu, 02 Apr 2026 19:47:36 -0400","from p-west2-cluster6-host8-snip4-1.eps.apple.com ([57.103.70.54]\n helo=outbound.mr.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 1w8Rl9-00042n-01\n for qemu-devel@nongnu.org; Thu, 02 Apr 2026 19:47:36 -0400","from outbound.mr.icloud.com (unknown [127.0.0.2])\n by p00-icloudmta-asmtp-us-west-2a-60-percent-6 (Postfix) with ESMTPS id\n F21FA1800149; Thu, 02 Apr 2026 23:47:32 +0000 (UTC)","from localhost.localdomain (unknown [17.57.152.38])\n by p00-icloudmta-asmtp-us-west-2a-60-percent-6 (Postfix) with ESMTPSA id\n 0BEE5180013B; Thu, 02 Apr 2026 23:47:30 +0000 (UTC)"],"Dkim-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=unpredictable.fr;\n s=sig1; t=1775173654; x=1777765654;\n bh=NEY/B0hsRoX1RMH5wKACHOnKD7aE0mtEoOch9NbWUDg=;\n h=From:To:Subject:Date:Message-ID:MIME-Version:x-icloud-hme;\n b=TqaNRcCWjwNhIkHggXzd6V/TMJNoyfvvQpiu9THSZOwjUvCeyM/1ULtN1tcVLy0gm1t7D3C9hkAOr5vbiM7Hvfx0/KSXG+Gm5UD1RviZm4gH928dedbStsaroPwAQks1V1xmgiAItzRk/GDuCQxXP2F9FGuB5hU3iKHGa2peXRcu8WOnN/bSnqVZbtmCnaudZTcWpO5f1z+9MC6G6YQIAI5L8qD77HVd7n4xsMCHRWRGSpo0Wnfsks/rsaiKy/XVFPqM3HhEG1uqcufTGdqvy+x9nJ6+fqSgzF+Y+2l2xh49/YSFq6EB9EIP8BUP/PpNQj9FDSsRuiokMVVK7atNMQ==","mail-alias-created-date":"1752046281608","From":"Mohamed Mediouni <mohamed@unpredictable.fr>","To":"qemu-devel@nongnu.org","Cc":"Mohamed Mediouni <mohamed@unpredictable.fr>,\n Phil Dennis-Jordan <phil@philjordan.eu>,\n Roman Bolshakov <rbolshakov@ddn.com>, Zhao Liu <zhao1.liu@intel.com>,\n Paolo Bonzini <pbonzini@redhat.com>,\n Pedro Barbuda <pbarbuda@microsoft.com>, Wei Liu <wei.liu@kernel.org>","Subject":"[PATCH v3 2/7] whpx: i386: x2apic emulation","Date":"Fri,  3 Apr 2026 01:47:19 +0200","Message-ID":"<20260402234724.88276-3-mohamed@unpredictable.fr>","X-Mailer":"git-send-email 2.50.1","In-Reply-To":"<20260402234724.88276-1-mohamed@unpredictable.fr>","References":"<20260402234724.88276-1-mohamed@unpredictable.fr>","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","X-Authority-Info-Out":"v=2.4 cv=OYyVzxTY c=1 sm=1 tr=0 ts=69cf0015\n cx=c_apl:c_pps:t_out a=9OgfyREA4BUYbbCgc0Y0oA==:117\n a=9OgfyREA4BUYbbCgc0Y0oA==:17 a=A5OVakUREuEA:10 a=VkNPw1HP01LnGYTKEx00:22\n a=nj4AhdDZwDx412jv8JQA:9","X-Proofpoint-ORIG-GUID":"Ago7ehYfFiazsGYK1lAGwq8BZd8rSQEw","X-Proofpoint-Spam-Details-Enc":"AW1haW4tMjYwNDAyMDIxMyBTYWx0ZWRfX30wcXgZHZMxo\n tH3713KRkbCpJjugypWEYdHVMEh5yBAD9csJg3zc0dRiEWiIyQN+gza9puTCo3dr3vcupDdVG8I\n tQ2+uMqXnUglfsr4rg8D2kHYrs+3RMvu/dpMqSqm75mWNNDvySTDD/jKsNWaBvD+6WzszuNdCNi\n Px5j1VncP5t9BznDyy2je0bGq1a/n5VC2y46EV01ft/YPGGWFl1y5oLWzTvswVY78oAMXs/7MS5\n 3R6kXSWVF5jZhfBWVI0qYoDXH5ick2X4Mlmy0FRUbplV0MC0GdgaIr+H9O8kXZKHi2qaOL7BE+i\n 9XwTIvk7rKDyX9x58ETt+HXsBwvtFtwvx4Isvr5cAJzaYZCj2Wf2MTbK1IZ8ds=","X-Proofpoint-GUID":"Ago7ehYfFiazsGYK1lAGwq8BZd8rSQEw","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-02_04,2026-04-02_05,2025-10-01_01","X-Proofpoint-Spam-Details":"rule=notspam policy=default score=0 phishscore=0\n mlxlogscore=688 spamscore=0 bulkscore=0 adultscore=0 malwarescore=0\n mlxscore=0 suspectscore=0 lowpriorityscore=0 clxscore=1030 classifier=spam\n authscore=0 adjust=0 reason=mlx scancount=1 engine=8.22.0-2601150000\n definitions=main-2604020213","Received-SPF":"pass client-ip=57.103.70.54;\n envelope-from=mohamed@unpredictable.fr; helo=outbound.mr.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_VALIDITY_CERTIFIED_BLOCKED=0.001,\n RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001,\n 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":"Add x2apic emulation to WHPX for the kernel-irqchip=off case.\n\nUnfortunately, it looks like there isn't a workaround available\nfor proper behavior of PIC interrupts when kernel-irqchip=on\nfor Windows 10. The OS is out of support outside of extended\nsecurity updates so this will not be addressed.\n\nOn Windows 11, x2apic will be enabled straight away because\nLinux enables it even without an IOMMU when using Hyper-V.\n\nFor Windows 10, you need: -device intel-iommu,intremap=on,eim=on\n\nThe performance impact is worthwhile for multicore guests.\n\nSigned-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>\n---\n target/i386/whpx/whpx-all.c | 134 +++++++++++++++++++++++++++++++++++-\n 1 file changed, 133 insertions(+), 1 deletion(-)","diff":"diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c\nindex e56ae2b343..4127440c0c 100644\n--- a/target/i386/whpx/whpx-all.c\n+++ b/target/i386/whpx/whpx-all.c\n@@ -1082,6 +1082,8 @@ HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions)\n     /* Register for MSR and CPUID exits */\n     memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));\n     prop.ExtendedVmExits.X64MsrExit = 1;\n+    prop.ExtendedVmExits.X64CpuidExit = 1;\n+\n     if (exceptions != 0) {\n         prop.ExtendedVmExits.ExceptionExit = 1;\n     }\n@@ -1898,6 +1900,18 @@ int whpx_vcpu_run(CPUState *cpu)\n             WHV_REGISTER_NAME reg_names[3];\n             UINT32 reg_count;\n             bool is_known_msr = 0; \n+            uint64_t val;\n+\n+            if (vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {\n+                val = ((uint32_t)vcpu->exit_ctx.MsrAccess.Rax) |\n+                    ((uint64_t)(vcpu->exit_ctx.MsrAccess.Rdx) << 32);\n+            } else {\n+                /*\n+                 * Workaround for [-Werror=maybe-uninitialized]\n+                 * with GCC. Not needed with Clang.\n+                 */\n+                val = 0;\n+            }\n \n             reg_names[0] = WHvX64RegisterRip;\n             reg_names[1] = WHvX64RegisterRax;\n@@ -1911,7 +1925,47 @@ int whpx_vcpu_run(CPUState *cpu)\n                 && !vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite\n                 && !whpx_irqchip_in_kernel()) {\n                 is_known_msr = 1;\n-                reg_values[1].Reg32 = (uint32_t)X86_CPU(cpu)->env.apic_bus_freq;\n+                val = X86_CPU(cpu)->env.apic_bus_freq;\n+            }\n+\n+            if (!whpx_irqchip_in_kernel() &&\n+                vcpu->exit_ctx.MsrAccess.MsrNumber == MSR_IA32_APICBASE) {\n+                is_known_msr = 1;\n+                if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {\n+                    /* Read path unreachable on Hyper-V */\n+                    abort();\n+                } else {\n+                    WHV_REGISTER_VALUE reg = {.Reg64 = val};\n+                    int msr_ret = cpu_set_apic_base(X86_CPU(cpu)->apic_state, val);\n+                    if (msr_ret < 0) {\n+                        x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);\n+                    }\n+                    whpx_set_reg(cpu, WHvX64RegisterApicBase, reg);\n+                }\n+            }\n+\n+            if (!whpx_irqchip_in_kernel() &&\n+                vcpu->exit_ctx.MsrAccess.MsrNumber >= MSR_APIC_START &&\n+                vcpu->exit_ctx.MsrAccess.MsrNumber <= MSR_APIC_END) {\n+                int index = vcpu->exit_ctx.MsrAccess.MsrNumber - MSR_APIC_START;\n+                int msr_ret;\n+                is_known_msr = 1;\n+                if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {\n+                    bql_lock();\n+                    msr_ret = apic_msr_read(X86_CPU(cpu)->apic_state, index, &val);\n+                    bql_unlock();\n+                    reg_values[1].Reg64 = val;\n+                    if (msr_ret < 0) {\n+                        x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);\n+                    }\n+                } else {\n+                    bql_lock();\n+                    msr_ret = apic_msr_write(X86_CPU(cpu)->apic_state, index, val);\n+                    bql_unlock();\n+                    if (msr_ret < 0) {\n+                        x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);\n+                    }\n+                }\n             }\n             /*\n              * For all unsupported MSR access we:\n@@ -1921,6 +1975,11 @@ int whpx_vcpu_run(CPUState *cpu)\n             reg_count = vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite ?\n                         1 : 3;\n \n+            if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {\n+                reg_values[1].Reg32 = (uint32_t)val;\n+                reg_values[2].Reg32 = (uint32_t)(val >> 32);\n+            }\n+\n             if (!is_known_msr) {\n                 trace_whpx_unsupported_msr_access(vcpu->exit_ctx.MsrAccess.MsrNumber,\n                     vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite);\n@@ -1939,6 +1998,47 @@ int whpx_vcpu_run(CPUState *cpu)\n             ret = 0;\n             break;\n         }\n+        case WHvRunVpExitReasonX64Cpuid: {\n+            WHV_REGISTER_VALUE reg_values[5] = {0};\n+            WHV_REGISTER_NAME reg_names[5];\n+            UINT32 reg_count = 5;\n+            X86CPU *x86_cpu = X86_CPU(cpu);\n+            CPUX86State *env = &x86_cpu->env;\n+\n+            reg_names[0] = WHvX64RegisterRip;\n+            reg_names[1] = WHvX64RegisterRax;\n+            reg_names[2] = WHvX64RegisterRcx;\n+            reg_names[3] = WHvX64RegisterRdx;\n+            reg_names[4] = WHvX64RegisterRbx;\n+\n+            reg_values[0].Reg64 =\n+                vcpu->exit_ctx.VpContext.Rip +\n+                vcpu->exit_ctx.VpContext.InstructionLength;\n+\n+            reg_values[1].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;\n+            reg_values[2].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx;\n+            reg_values[3].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;\n+            reg_values[4].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;\n+\n+            if (vcpu->exit_ctx.CpuidAccess.Rax == 1) {\n+                if (cpu_has_x2apic_feature(env)) {\n+                    reg_values[2].Reg64 |= CPUID_EXT_X2APIC;\n+                }\n+            }\n+\n+            hr = whp_dispatch.WHvSetVirtualProcessorRegisters(\n+                whpx->partition,\n+                cpu->cpu_index,\n+                reg_names, reg_count,\n+                reg_values);\n+\n+            if (FAILED(hr)) {\n+                error_report(\"WHPX: Failed to set CpuidAccess state \"\n+                             \" registers, hr=%08lx\", hr);\n+            }\n+            ret = 0;\n+            break;\n+        }\n         case WHvRunVpExitReasonException:\n             whpx_get_registers(cpu, WHPX_LEVEL_FULL_STATE);\n \n@@ -2136,6 +2236,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)\n     WHV_PROCESSOR_FEATURES_BANKS processor_features;\n     WHV_PROCESSOR_PERFMON_FEATURES perfmon_features;\n     bool is_legacy_os = false;\n+    UINT32 cpuidExitList[] = {1};\n \n     whpx = &whpx_global;\n \n@@ -2354,6 +2455,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)\n     /* Register for MSR and CPUID exits */\n     memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));\n     prop.ExtendedVmExits.X64MsrExit = 1;\n+    prop.ExtendedVmExits.X64CpuidExit = 1;\n \n     hr = whp_dispatch.WHvSetPartitionProperty(\n             whpx->partition,\n@@ -2366,6 +2468,36 @@ int whpx_accel_init(AccelState *as, MachineState *ms)\n         goto error;\n     }\n \n+    memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));\n+    prop.X64MsrExitBitmap.UnhandledMsrs = 1;\n+    if (!whpx_irqchip_in_kernel()) {\n+        prop.X64MsrExitBitmap.ApicBaseMsrWrite = 1;\n+    }\n+\n+    hr = whp_dispatch.WHvSetPartitionProperty(\n+            whpx->partition,\n+            WHvPartitionPropertyCodeX64MsrExitBitmap,\n+            &prop,\n+            sizeof(WHV_PARTITION_PROPERTY));\n+    if (FAILED(hr)) {\n+        error_report(\"WHPX: Failed to set MSR exit bitmap, hr=%08lx\", hr);\n+        ret = -EINVAL;\n+        goto error;\n+    }\n+\n+    hr = whp_dispatch.WHvSetPartitionProperty(\n+        whpx->partition,\n+        WHvPartitionPropertyCodeCpuidExitList,\n+        cpuidExitList,\n+        RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));\n+\n+    if (FAILED(hr)) {\n+        error_report(\"WHPX: Failed to set partition CpuidExitList hr=%08lx\",\n+                     hr);\n+        ret = -EINVAL;\n+        goto error;\n+    }\n+\n     /*\n      * We do not want to intercept any exceptions from the guest,\n      * until we actually start debugging with gdb.\n","prefixes":["v3","2/7"]}