From: Mohamed Mediouni <mohamed@unpredictable.fr>
To: qemu-devel@nongnu.org
Cc: Wei Liu <wei.liu@kernel.org>,
Roman Bolshakov <rbolshakov@ddn.com>,
Pedro Barbuda <pbarbuda@microsoft.com>,
Zhao Liu <zhao1.liu@intel.com>,
Paolo Bonzini <pbonzini@redhat.com>,
Mohamed Mediouni <mohamed@unpredictable.fr>,
Phil Dennis-Jordan <phil@philjordan.eu>
Subject: [PATCH 2/4] whpx: i386: x2apic emulation
Date: Thu, 2 Apr 2026 07:33:24 +0200 [thread overview]
Message-ID: <20260402053326.23402-3-mohamed@unpredictable.fr> (raw)
In-Reply-To: <20260402053326.23402-1-mohamed@unpredictable.fr>
Add x2apic emulation to WHPX for the kernel-irqchip=off case.
Unfortunately, it looks like there isn't a workaround available
for proper behavior of PIC interrupts when kernel-irqchip=on
for Windows 10. The OS is out of support outside of extended
security updates so this will not be addressed.
On Windows 11, x2apic will be enabled straight away because
Linux enables it even without an IOMMU when using Hyper-V.
For Windows 10, you need: -device intel-iommu,intremap=on,eim=on
The performance impact is worthwhile for multicore guests.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 128 +++++++++++++++++++++++++++++++++++-
1 file changed, 127 insertions(+), 1 deletion(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index e56ae2b343..f06474c31b 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -1082,6 +1082,8 @@ HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions)
/* Register for MSR and CPUID exits */
memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
prop.ExtendedVmExits.X64MsrExit = 1;
+ prop.ExtendedVmExits.X64CpuidExit = 1;
+
if (exceptions != 0) {
prop.ExtendedVmExits.ExceptionExit = 1;
}
@@ -1898,6 +1900,12 @@ int whpx_vcpu_run(CPUState *cpu)
WHV_REGISTER_NAME reg_names[3];
UINT32 reg_count;
bool is_known_msr = 0;
+ uint64_t val;
+
+ if (vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
+ val = ((uint32_t)vcpu->exit_ctx.MsrAccess.Rax) |
+ ((uint64_t)(vcpu->exit_ctx.MsrAccess.Rdx) << 32);
+ }
reg_names[0] = WHvX64RegisterRip;
reg_names[1] = WHvX64RegisterRax;
@@ -1911,7 +1919,47 @@ int whpx_vcpu_run(CPUState *cpu)
&& !vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite
&& !whpx_irqchip_in_kernel()) {
is_known_msr = 1;
- reg_values[1].Reg32 = (uint32_t)X86_CPU(cpu)->env.apic_bus_freq;
+ val = X86_CPU(cpu)->env.apic_bus_freq;
+ }
+
+ if (!whpx_irqchip_in_kernel() &&
+ vcpu->exit_ctx.MsrAccess.MsrNumber == MSR_IA32_APICBASE) {
+ is_known_msr = 1;
+ if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
+ /* Read path unreachable on Hyper-V */
+ abort();
+ } else {
+ WHV_REGISTER_VALUE reg = {.Reg64 = val};
+ int ret = cpu_set_apic_base(X86_CPU(cpu)->apic_state, val);
+ if (ret < 0) {
+ x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ }
+ whpx_set_reg(cpu, WHvX64RegisterApicBase, reg);
+ }
+ }
+
+ if (!whpx_irqchip_in_kernel() &&
+ vcpu->exit_ctx.MsrAccess.MsrNumber >= MSR_APIC_START &&
+ vcpu->exit_ctx.MsrAccess.MsrNumber <= MSR_APIC_END) {
+ int index = vcpu->exit_ctx.MsrAccess.MsrNumber - MSR_APIC_START;
+ int ret;
+ is_known_msr = 1;
+ if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
+ bql_lock();
+ ret = apic_msr_read(X86_CPU(cpu)->apic_state, index, &val);
+ bql_unlock();
+ reg_values[1].Reg64 = val;
+ if (ret < 0) {
+ x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ }
+ } else {
+ bql_lock();
+ ret = apic_msr_write(X86_CPU(cpu)->apic_state, index, val);
+ bql_unlock();
+ if (ret < 0) {
+ x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ }
+ }
}
/*
* For all unsupported MSR access we:
@@ -1921,6 +1969,11 @@ int whpx_vcpu_run(CPUState *cpu)
reg_count = vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite ?
1 : 3;
+ if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
+ reg_values[1].Reg32 = (uint32_t)val;
+ reg_values[2].Reg32 = (uint32_t)(val >> 32);
+ }
+
if (!is_known_msr) {
trace_whpx_unsupported_msr_access(vcpu->exit_ctx.MsrAccess.MsrNumber,
vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite);
@@ -1939,6 +1992,47 @@ int whpx_vcpu_run(CPUState *cpu)
ret = 0;
break;
}
+ case WHvRunVpExitReasonX64Cpuid: {
+ WHV_REGISTER_VALUE reg_values[5] = {0};
+ WHV_REGISTER_NAME reg_names[5];
+ UINT32 reg_count = 5;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
+ reg_names[0] = WHvX64RegisterRip;
+ reg_names[1] = WHvX64RegisterRax;
+ reg_names[2] = WHvX64RegisterRcx;
+ reg_names[3] = WHvX64RegisterRdx;
+ reg_names[4] = WHvX64RegisterRbx;
+
+ reg_values[0].Reg64 =
+ vcpu->exit_ctx.VpContext.Rip +
+ vcpu->exit_ctx.VpContext.InstructionLength;
+
+ reg_values[1].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRax;
+ reg_values[2].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRcx;
+ reg_values[3].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRdx;
+ reg_values[4].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
+
+ if (vcpu->exit_ctx.CpuidAccess.Rax == 1) {
+ if (cpu_has_x2apic_feature(env)) {
+ reg_values[2].Reg64 |= CPUID_EXT_X2APIC;
+ }
+ }
+
+ hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
+ whpx->partition,
+ cpu->cpu_index,
+ reg_names, reg_count,
+ reg_values);
+
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to set CpuidAccess state "
+ " registers, hr=%08lx", hr);
+ }
+ ret = 0;
+ break;
+ }
case WHvRunVpExitReasonException:
whpx_get_registers(cpu, WHPX_LEVEL_FULL_STATE);
@@ -2136,6 +2230,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
WHV_PROCESSOR_FEATURES_BANKS processor_features;
WHV_PROCESSOR_PERFMON_FEATURES perfmon_features;
bool is_legacy_os = false;
+ UINT32 cpuidExitList[] = {1};
whpx = &whpx_global;
@@ -2354,6 +2449,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
/* Register for MSR and CPUID exits */
memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
prop.ExtendedVmExits.X64MsrExit = 1;
+ prop.ExtendedVmExits.X64CpuidExit = 1;
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
@@ -2366,6 +2462,36 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
goto error;
}
+ memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
+ prop.X64MsrExitBitmap.UnhandledMsrs = 1;
+ if (!whpx_irqchip_in_kernel()) {
+ prop.X64MsrExitBitmap.ApicBaseMsrWrite = 1;
+ }
+
+ hr = whp_dispatch.WHvSetPartitionProperty(
+ whpx->partition,
+ WHvPartitionPropertyCodeX64MsrExitBitmap,
+ &prop,
+ sizeof(WHV_PARTITION_PROPERTY));
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to set MSR exit bitmap, hr=%08lx", hr);
+ ret = -EINVAL;
+ goto error;
+ }
+
+ hr = whp_dispatch.WHvSetPartitionProperty(
+ whpx->partition,
+ WHvPartitionPropertyCodeCpuidExitList,
+ cpuidExitList,
+ RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
+
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to set partition CpuidExitList hr=%08lx",
+ hr);
+ ret = -EINVAL;
+ goto error;
+ }
+
/*
* We do not want to intercept any exceptions from the guest,
* until we actually start debugging with gdb.
--
2.50.1 (Apple Git-155)
next prev parent reply other threads:[~2026-04-02 5:35 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-02 5:33 [PATCH 0/4] whpx: i386: x2apic emulation for kernel-irqchip=off, feature probing Mohamed Mediouni
2026-04-02 5:33 ` [PATCH 1/4] target/i386: emulate: include name of unhandled instruction Mohamed Mediouni
2026-04-02 5:33 ` Mohamed Mediouni [this message]
2026-04-02 5:33 ` [PATCH 3/4] whpx: i386: wire up feature probing Mohamed Mediouni
2026-04-02 5:33 ` [PATCH 4/4] whpx: i386: disable TbFlushHypercalls for emulated LAPIC Mohamed Mediouni
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260402053326.23402-3-mohamed@unpredictable.fr \
--to=mohamed@unpredictable.fr \
--cc=pbarbuda@microsoft.com \
--cc=pbonzini@redhat.com \
--cc=phil@philjordan.eu \
--cc=qemu-devel@nongnu.org \
--cc=rbolshakov@ddn.com \
--cc=wei.liu@kernel.org \
--cc=zhao1.liu@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.