* [PATCH v3 01/37] target/i386: emulate: include name of unhandled instruction
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 02/37] whpx: i386: x2apic emulation Mohamed Mediouni
` (36 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Instead of just the command number, include the instruction name to make debugging easier.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/emulate/x86_emu.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/target/i386/emulate/x86_emu.c b/target/i386/emulate/x86_emu.c
index 55b1a68eb6..c2da1a133f 100644
--- a/target/i386/emulate/x86_emu.c
+++ b/target/i386/emulate/x86_emu.c
@@ -1399,8 +1399,8 @@ static void init_cmd_handler(void)
bool exec_instruction(CPUX86State *env, struct x86_decode *ins)
{
if (!_cmd_handler[ins->cmd].handler) {
- printf("Unimplemented handler (" TARGET_FMT_lx ") for %d (%x %x)\n",
- env->eip,
+ printf("Unimplemented handler (" TARGET_FMT_lx ") for %s - %d (%x %x)\n",
+ env->eip, decode_cmd_to_string(ins->cmd),
ins->cmd, ins->opcode[0],
ins->opcode_len > 1 ? ins->opcode[1] : 0);
env->eip += ins->len;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 02/37] whpx: i386: x2apic emulation
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 01/37] target/i386: emulate: include name of unhandled instruction Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 03/37] whpx: i386: wire up feature probing Mohamed Mediouni
` (35 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
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.
The performance boost is quite visible for multicore guests.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 134 +++++++++++++++++++++++++++++++++++-
1 file changed, 133 insertions(+), 1 deletion(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index e56ae2b343..4127440c0c 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,18 @@ 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);
+ } else {
+ /*
+ * Workaround for [-Werror=maybe-uninitialized]
+ * with GCC. Not needed with Clang.
+ */
+ val = 0;
+ }
reg_names[0] = WHvX64RegisterRip;
reg_names[1] = WHvX64RegisterRax;
@@ -1911,7 +1925,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 msr_ret = cpu_set_apic_base(X86_CPU(cpu)->apic_state, val);
+ if (msr_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 msr_ret;
+ is_known_msr = 1;
+ if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
+ bql_lock();
+ msr_ret = apic_msr_read(X86_CPU(cpu)->apic_state, index, &val);
+ bql_unlock();
+ reg_values[1].Reg64 = val;
+ if (msr_ret < 0) {
+ x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ }
+ } else {
+ bql_lock();
+ msr_ret = apic_msr_write(X86_CPU(cpu)->apic_state, index, val);
+ bql_unlock();
+ if (msr_ret < 0) {
+ x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ }
+ }
}
/*
* For all unsupported MSR access we:
@@ -1921,6 +1975,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 +1998,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 +2236,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 +2455,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 +2468,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)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 03/37] whpx: i386: wire up feature probing
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 01/37] target/i386: emulate: include name of unhandled instruction Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 02/37] whpx: i386: x2apic emulation Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 04/37] whpx: i386: disable TbFlushHypercalls for emulated LAPIC Mohamed Mediouni
` (34 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Windows 10 doesn't have the API for this, so using this
only for Windows 11.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
include/system/whpx-internal.h | 9 ++
target/i386/cpu.c | 17 +++
target/i386/whpx/meson.build | 1 +
target/i386/whpx/whpx-all.c | 165 +++++++++++++++++++++++++++-
target/i386/whpx/whpx-cpu-legacy.c | 171 +++++++++++++++++++++++++++++
target/i386/whpx/whpx-i386.h | 12 ++
6 files changed, 370 insertions(+), 5 deletions(-)
create mode 100644 target/i386/whpx/whpx-cpu-legacy.c
create mode 100644 target/i386/whpx/whpx-i386.h
diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h
index 8482901f71..5902124b63 100644
--- a/include/system/whpx-internal.h
+++ b/include/system/whpx-internal.h
@@ -73,6 +73,14 @@ void whpx_apic_get(APICCommonState *s);
X(HRESULT, WHvGetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, WHV_REGISTER_VALUE* RegisterValues)) \
X(HRESULT, WHvSetVirtualProcessorRegisters, (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, const WHV_REGISTER_NAME* RegisterNames, UINT32 RegisterCount, const WHV_REGISTER_VALUE* RegisterValues)) \
+#ifdef __x86_64__
+#define LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL_ARCH(X) \
+ X(HRESULT, WHvGetVirtualProcessorCpuidOutput, \
+ (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, UINT32 Eax, \
+ UINT32 Ecx, WHV_CPUID_OUTPUT *CpuidOutput))
+#else
+#define LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL_ARCH(X)
+#endif
/*
* These are supplemental functions that may not be present
* on all versions and are not critical for basic functionality.
@@ -89,6 +97,7 @@ void whpx_apic_get(APICCommonState *s);
UINT32 StateSize)) \
X(HRESULT, WHvResetPartition, \
(WHV_PARTITION_HANDLE Partition)) \
+ LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL_ARCH(X)
#define WHP_DEFINE_TYPE(return_type, function_name, signature) \
typedef return_type (WINAPI *function_name ## _t) signature;
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index c6fd1dc00e..a5f1a1a8fd 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -26,6 +26,8 @@
#include "tcg/helper-tcg.h"
#include "exec/translation-block.h"
#include "system/hvf.h"
+#include "system/whpx.h"
+#include "whpx/whpx-i386.h"
#include "hvf/hvf-i386.h"
#include "kvm/kvm_i386.h"
#include "kvm/tdx.h"
@@ -8087,6 +8089,16 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w)
r = hvf_get_supported_cpuid(wi->cpuid.eax,
wi->cpuid.ecx,
wi->cpuid.reg);
+ } else if (whpx_enabled()) {
+ switch (wi->type) {
+ case CPUID_FEATURE_WORD:
+ r = whpx_get_supported_cpuid(wi->cpuid.eax, wi->cpuid.ecx,
+ wi->cpuid.reg);
+ break;
+ case MSR_FEATURE_WORD:
+ r = whpx_get_supported_msr_feature(wi->msr.index);
+ break;
+ }
} else if (tcg_enabled() || qtest_enabled()) {
r = wi->tcg_features;
} else {
@@ -8168,6 +8180,11 @@ static void x86_cpu_get_supported_cpuid(uint32_t func, uint32_t index,
*ebx = hvf_get_supported_cpuid(func, index, R_EBX);
*ecx = hvf_get_supported_cpuid(func, index, R_ECX);
*edx = hvf_get_supported_cpuid(func, index, R_EDX);
+ } else if (whpx_enabled()) {
+ *eax = whpx_get_supported_cpuid(func, index, R_EAX);
+ *ebx = whpx_get_supported_cpuid(func, index, R_EBX);
+ *ecx = whpx_get_supported_cpuid(func, index, R_ECX);
+ *edx = whpx_get_supported_cpuid(func, index, R_EDX);
} else {
*eax = 0;
*ebx = 0;
diff --git a/target/i386/whpx/meson.build b/target/i386/whpx/meson.build
index c3aaaff9fd..1c6a4ce377 100644
--- a/target/i386/whpx/meson.build
+++ b/target/i386/whpx/meson.build
@@ -1,4 +1,5 @@
i386_system_ss.add(when: 'CONFIG_WHPX', if_true: files(
'whpx-all.c',
'whpx-apic.c',
+ 'whpx-cpu-legacy.c'
))
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 4127440c0c..d211b3f2ef 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -36,6 +36,7 @@
#include "system/whpx-accel-ops.h"
#include "system/whpx-all.h"
#include "system/whpx-common.h"
+#include "whpx-i386.h"
#include "emulate/x86_decode.h"
#include "emulate/x86_emu.h"
@@ -49,6 +50,8 @@
/* for kernel-irqchip=off */
#define HV_X64_MSR_APIC_FREQUENCY 0x40000023
+static bool is_modern_os = true;
+
static const WHV_REGISTER_NAME whpx_register_names[] = {
/* X64 General purpose registers */
@@ -265,11 +268,30 @@ typedef enum WhpxStepMode {
static uint32_t max_vcpu_index;
static WHV_PROCESSOR_XSAVE_FEATURES whpx_xsave_cap;
-static bool whpx_has_xsave(void)
+bool whpx_has_xsave(void)
{
return whpx_xsave_cap.XsaveSupport;
}
+bool whpx_has_xsaves(void)
+{
+ return whpx_xsave_cap.XsaveSupervisorSupport;
+}
+
+static bool whpx_rdtsc_cap;
+
+bool whpx_has_rdtscp(void)
+{
+ return whpx_rdtsc_cap;
+}
+
+static bool whpx_invpcid_cap;
+
+bool whpx_has_invpcid(void)
+{
+ return whpx_invpcid_cap;
+}
+
static WHV_X64_SEGMENT_REGISTER whpx_seg_q2h(const SegmentCache *qs, int v86,
int r86)
{
@@ -1062,6 +1084,137 @@ static void whpx_init_emu(void)
init_emu(&whpx_x86_emul_ops);
}
+bool whpx_is_legacy_os(void)
+{
+ return !is_modern_os;
+}
+
+uint32_t whpx_get_supported_cpuid(uint32_t func, uint32_t idx, int reg)
+{
+ WHV_CPUID_OUTPUT output = {};
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t cpu_index = 0;
+ bool temp_cpu = true;
+ HRESULT hr;
+
+ /* Legacy OSes don't have WHvGetVirtualProcessorCpuidOutput */
+ if (whpx_is_legacy_os()) {
+ return whpx_get_supported_cpuid_legacy(func, idx, reg);
+ }
+
+ hr = whp_dispatch.WHvCreateVirtualProcessor(
+ whpx_global.partition, cpu_index, 0);
+
+ /* This means that the CPU already exists... */
+ if (FAILED(hr)) {
+ temp_cpu = false;
+ }
+
+ hr = whp_dispatch.WHvGetVirtualProcessorCpuidOutput(whpx_global.partition,
+ cpu_index, func, idx, &output);
+
+ if (FAILED(hr)) {
+ abort();
+ }
+
+ if (temp_cpu) {
+ hr = whp_dispatch.WHvDeleteVirtualProcessor(whpx_global.partition, cpu_index);
+ if (FAILED(hr)) {
+ abort();
+ }
+ }
+
+ eax = output.Eax;
+ ebx = output.Ebx;
+ ecx = output.Ecx;
+ edx = output.Edx;
+
+ /*
+ * We can emulate X2APIC even for the kernel-irqchip=off case.
+ * CPUID_EXT_HYPERVISOR and CPUID_HT should be considered present
+ * always, so report them as unconditionally supported here.
+ */
+ if (func == 1) {
+ ecx |= CPUID_EXT_X2APIC;
+ ecx |= CPUID_EXT_HYPERVISOR;
+ edx |= CPUID_HT;
+ }
+
+ switch (reg) {
+ case R_EAX:
+ return eax;
+ case R_EBX:
+ return ebx;
+ case R_ECX:
+ return ecx;
+ case R_EDX:
+ return edx;
+ default:
+ return 0;
+ }
+}
+
+uint64_t whpx_get_supported_msr_feature(uint32_t index)
+{
+ WHV_CAPABILITY_CODE cap;
+ uint64_t val = 0;
+
+ switch (index) {
+ case MSR_IA32_VMX_BASIC:
+ cap = WHvCapabilityCodeVmxBasic;
+ break;
+ case MSR_IA32_VMX_MISC:
+ cap = WHvCapabilityCodeVmxMisc;
+ break;
+ case MSR_IA32_VMX_CR0_FIXED0:
+ cap = WHvCapabilityCodeVmxCr0Fixed0;
+ break;
+ case MSR_IA32_VMX_CR0_FIXED1:
+ cap = WHvCapabilityCodeVmxCr0Fixed1;
+ break;
+ case MSR_IA32_VMX_CR4_FIXED0:
+ cap = WHvCapabilityCodeVmxCr4Fixed0;
+ break;
+ case MSR_IA32_VMX_CR4_FIXED1:
+ cap = WHvCapabilityCodeVmxCr4Fixed1;
+ break;
+ case MSR_IA32_VMX_VMCS_ENUM:
+ cap = WHvCapabilityCodeVmxVmcsEnum;
+ break;
+ case MSR_IA32_VMX_PROCBASED_CTLS2:
+ cap = WHvCapabilityCodeVmxProcbasedCtls2;
+ break;
+ case MSR_IA32_VMX_EPT_VPID_CAP:
+ cap = WHvCapabilityCodeVmxEptVpidCap;
+ break;
+ case MSR_IA32_VMX_TRUE_PINBASED_CTLS:
+ cap = WHvCapabilityCodeVmxPinbasedCtls;
+ break;
+ case MSR_IA32_VMX_TRUE_PROCBASED_CTLS:
+ cap = WHvCapabilityCodeVmxProcbasedCtls;
+ break;
+ case MSR_IA32_VMX_TRUE_ENTRY_CTLS:
+ cap = WHvCapabilityCodeVmxTrueEntryCtls;
+ break;
+ case MSR_IA32_VMX_TRUE_EXIT_CTLS:
+ cap = WHvCapabilityCodeVmxTrueExitCtls;
+ break;
+ default:
+ cap = 0;
+ }
+
+ if (cap != 0) {
+ HRESULT hr = whp_dispatch.WHvGetCapability(
+ cap, &val, sizeof(val),
+ NULL);
+ if (FAILED(hr)) {
+ return 0;
+ }
+ return val;
+ }
+ return 0;
+}
+
/*
* Controls whether we should intercept various exceptions on the guest,
* namely breakpoint/single-step events.
@@ -2235,7 +2388,6 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
WHV_CAPABILITY_FEATURES features = {0};
WHV_PROCESSOR_FEATURES_BANKS processor_features;
WHV_PROCESSOR_PERFMON_FEATURES perfmon_features;
- bool is_legacy_os = false;
UINT32 cpuidExitList[] = {1};
whpx = &whpx_global;
@@ -2355,6 +2507,9 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
goto error;
}
+ whpx_rdtsc_cap = processor_features.Bank0.RdtscpSupport;
+ whpx_invpcid_cap = processor_features.Bank0.InvpcidSupport;
+
if (whpx_irqchip_in_kernel() && processor_features.Bank1.NestedVirtSupport) {
memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
prop.NestedVirtualization = 1;
@@ -2395,7 +2550,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
if (FAILED(hr)) {
warn_report("WHPX: Failed to get performance "
"monitoring features, hr=%08lx", hr);
- is_legacy_os = true;
+ is_modern_os = false;
} else {
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
@@ -2435,7 +2590,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
synthetic_features.Bank0.DirectSyntheticTimers = 1;
}
- if (!is_legacy_os && whpx->hyperv_enlightenments_allowed) {
+ if (is_modern_os && whpx->hyperv_enlightenments_allowed) {
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
WHvPartitionPropertyCodeSyntheticProcessorFeaturesBanks,
@@ -2446,7 +2601,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
ret = -EINVAL;
goto error;
}
- } else if (is_legacy_os && whpx->hyperv_enlightenments_required) {
+ } else if (!is_modern_os && whpx->hyperv_enlightenments_required) {
error_report("Hyper-V enlightenments not available on legacy Windows");
ret = -EINVAL;
goto error;
diff --git a/target/i386/whpx/whpx-cpu-legacy.c b/target/i386/whpx/whpx-cpu-legacy.c
new file mode 100644
index 0000000000..477429b460
--- /dev/null
+++ b/target/i386/whpx/whpx-cpu-legacy.c
@@ -0,0 +1,171 @@
+/*
+ * i386 CPUID helper functions
+ *
+ * Copyright (c) 2003 Fabrice Bellard
+ * Copyright (c) 2017 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * cpuid
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/cpuid.h"
+#include "host/cpuinfo.h"
+#include "cpu.h"
+#include "emulate/x86.h"
+#include "whpx-i386.h"
+
+static bool cached_xcr0;
+static uint64_t supported_xcr0;
+
+static void cache_host_xcr0(void)
+{
+ if (cached_xcr0) {
+ return;
+ }
+
+ if (whpx_has_xsave()) {
+ uint64_t host_xcr0 = xgetbv_low(0);
+
+ /* Only show xcr0 bits corresponding to usable features. */
+ supported_xcr0 = host_xcr0 & (XSTATE_FP_MASK |
+ XSTATE_SSE_MASK | XSTATE_YMM_MASK |
+ XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK |
+ XSTATE_Hi16_ZMM_MASK);
+ if ((supported_xcr0 & (XSTATE_FP_MASK | XSTATE_SSE_MASK)) !=
+ (XSTATE_FP_MASK | XSTATE_SSE_MASK)) {
+ supported_xcr0 = 0;
+ }
+ }
+
+ cached_xcr0 = true;
+}
+
+uint32_t whpx_get_supported_cpuid_legacy(uint32_t func, uint32_t idx,
+ int reg)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ cache_host_xcr0();
+ host_cpuid(func, idx, &eax, &ebx, &ecx, &edx);
+
+ switch (func) {
+ case 0:
+ eax = eax < (uint32_t)0xd ? eax : (uint32_t)0xd;
+ break;
+ case 1:
+ edx &= CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC |
+ CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC |
+ CPUID_SEP | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV |
+ CPUID_PAT | CPUID_PSE36 | CPUID_CLFLUSH | CPUID_MMX |
+ CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS | CPUID_HT;
+ ecx &= CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 |
+ CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID |
+ CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_MOVBE |
+ CPUID_EXT_POPCNT | CPUID_EXT_AES |
+ (supported_xcr0 ? CPUID_EXT_XSAVE : 0) |
+ CPUID_EXT_AVX | CPUID_EXT_F16C | CPUID_EXT_RDRAND;
+ ecx |= CPUID_EXT_HYPERVISOR;
+ ecx |= CPUID_EXT_X2APIC;
+ edx |= CPUID_HT;
+ break;
+ case 6:
+ eax = CPUID_6_EAX_ARAT;
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+ break;
+ case 7:
+ if (idx == 0) {
+ ebx &= CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 |
+ CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 |
+ CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 |
+ CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_RTM |
+ CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX |
+ CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_AVX512IFMA |
+ CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512PF |
+ CPUID_7_0_EBX_AVX512ER | CPUID_7_0_EBX_AVX512CD |
+ CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB |
+ CPUID_7_0_EBX_AVX512DQ | CPUID_7_0_EBX_SHA_NI |
+ CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512VL |
+ CPUID_7_0_EBX_INVPCID;
+
+ if (!whpx_has_invpcid()) {
+ ebx &= ~CPUID_7_0_EBX_INVPCID;
+ }
+
+ ecx &= CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_AVX512_VPOPCNTDQ |
+ CPUID_7_0_ECX_RDPID;
+ edx &= CPUID_7_0_EDX_AVX512_4VNNIW | CPUID_7_0_EDX_AVX512_4FMAPS;
+ } else {
+ ebx = 0;
+ ecx = 0;
+ edx = 0;
+ }
+ eax = 0;
+ break;
+ case 0xD:
+ if (!supported_xcr0 || idx >= 63 ||
+ (idx > 1 && !(supported_xcr0 & (UINT64_C(1) << idx)))) {
+ eax = ebx = ecx = edx = 0;
+ break;
+ }
+
+ if (idx == 0) {
+ eax = supported_xcr0;
+ } else if (idx == 1) {
+ eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1;
+ if (!whpx_has_xsaves()) {
+ eax &= ~CPUID_XSAVE_XSAVES;
+ }
+ }
+ break;
+ case 0x80000001:
+ /* LM only if HVF in 64-bit mode */
+ edx &= CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC |
+ CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC |
+ CPUID_EXT2_SYSCALL | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV |
+ CPUID_PAT | CPUID_PSE36 | CPUID_EXT2_MMXEXT | CPUID_MMX |
+ CPUID_FXSR | CPUID_EXT2_FXSR | CPUID_EXT2_PDPE1GB | CPUID_EXT2_3DNOWEXT |
+ CPUID_EXT2_3DNOW | CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX;
+ if (!(whpx_has_rdtscp())) {
+ edx &= ~CPUID_EXT2_RDTSCP;
+ }
+ ecx &= CPUID_EXT3_LAHF_LM | CPUID_EXT3_CMP_LEG | CPUID_EXT3_CR8LEG |
+ CPUID_EXT3_ABM | CPUID_EXT3_SSE4A | CPUID_EXT3_MISALIGNSSE |
+ CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_OSVW | CPUID_EXT3_XOP |
+ CPUID_EXT3_FMA4 | CPUID_EXT3_TBM;
+ break;
+ case 0x80000007:
+ edx &= CPUID_APM_INVTSC;
+ eax = ebx = ecx = 0;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (reg) {
+ case R_EAX:
+ return eax;
+ case R_EBX:
+ return ebx;
+ case R_ECX:
+ return ecx;
+ case R_EDX:
+ return edx;
+ default:
+ return 0;
+ }
+}
diff --git a/target/i386/whpx/whpx-i386.h b/target/i386/whpx/whpx-i386.h
new file mode 100644
index 0000000000..a1cf732862
--- /dev/null
+++ b/target/i386/whpx/whpx-i386.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+uint32_t whpx_get_supported_cpuid(uint32_t func, uint32_t idx, int reg);
+uint64_t whpx_get_supported_msr_feature(uint32_t index);
+bool whpx_is_legacy_os(void);
+
+uint32_t whpx_get_supported_cpuid_legacy(uint32_t func, uint32_t idx,
+ int reg);
+bool whpx_has_xsave(void);
+bool whpx_has_xsaves(void);
+bool whpx_has_rdtscp(void);
+bool whpx_has_invpcid(void);
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 04/37] whpx: i386: disable TbFlushHypercalls for emulated LAPIC
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (2 preceding siblings ...)
2026-04-22 21:41 ` [PATCH v3 03/37] whpx: i386: wire up feature probing Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 05/37] whpx: i386: enable x2apic by default for user-mode LAPIC Mohamed Mediouni
` (33 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
AccessHypercallRegs was present twice so clean that up.
Remove TbFlushHypercalls (and its extended Gva range sub-feature)
from the user-mode LAPIC case as it behaves oddly there.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index d211b3f2ef..8f03616cb9 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2577,10 +2577,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
synthetic_features.Bank0.AccessPartitionReferenceTsc = 1;
synthetic_features.Bank0.AccessHypercallRegs = 1;
synthetic_features.Bank0.AccessFrequencyRegs = 1;
- synthetic_features.Bank0.EnableExtendedGvaRangesForFlushVirtualAddressList = 1;
synthetic_features.Bank0.AccessVpIndex = 1;
- synthetic_features.Bank0.AccessHypercallRegs = 1;
- synthetic_features.Bank0.TbFlushHypercalls = 1;
if (whpx_irqchip_in_kernel()) {
synthetic_features.Bank0.AccessSynicRegs = 1;
@@ -2588,6 +2585,12 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
synthetic_features.Bank0.AccessIntrCtrlRegs = 1;
synthetic_features.Bank0.SyntheticClusterIpi = 1;
synthetic_features.Bank0.DirectSyntheticTimers = 1;
+ /*
+ * These technically work without the Hyper-V LAPIC
+ * but behave oddly for multi-core VMs.
+ */
+ synthetic_features.Bank0.TbFlushHypercalls = 1;
+ synthetic_features.Bank0.EnableExtendedGvaRangesForFlushVirtualAddressList = 1;
}
if (is_modern_os && whpx->hyperv_enlightenments_allowed) {
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 05/37] whpx: i386: enable x2apic by default for user-mode LAPIC
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (3 preceding siblings ...)
2026-04-22 21:41 ` [PATCH v3 04/37] whpx: i386: disable TbFlushHypercalls for emulated LAPIC Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 06/37] whpx: i386: reintroduce enlightenments for Windows 10 Mohamed Mediouni
` (32 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 8f03616cb9..4954e0557a 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2366,11 +2366,18 @@ error:
return ret;
}
+static PropValue whpx_default_props[] = {
+ { "x2apic", "on" },
+ { NULL, NULL },
+};
+
+
void whpx_cpu_instance_init(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
host_cpu_instance_init(cpu);
+ x86_cpu_apply_props(cpu, whpx_default_props);
}
/*
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 06/37] whpx: i386: reintroduce enlightenments for Windows 10
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (4 preceding siblings ...)
2026-04-22 21:41 ` [PATCH v3 05/37] whpx: i386: enable x2apic by default for user-mode LAPIC Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 07/37] whpx: i386: introduce proper cpuid support Mohamed Mediouni
` (31 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Was removed in 2c08624 but it's still useful for
Windows 10 so reintroduce it there.
And this time, actually make it work by reporting
the hypervisor bit in CPUID.
Pretend to be vmware to be able to use vmport's functionality.
If the vmware frequency leaf is disabled, pretend to be
KVM, with the only capability reported being X2APIC support.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
accel/whpx/whpx-common.c | 2 ++
include/system/whpx-internal.h | 1 +
target/arm/whpx/whpx-all.c | 1 +
target/i386/whpx/whpx-all.c | 63 +++++++++++++++++++++++++++++-----
4 files changed, 58 insertions(+), 9 deletions(-)
diff --git a/accel/whpx/whpx-common.c b/accel/whpx/whpx-common.c
index b813a5d9d2..59be996aef 100644
--- a/accel/whpx/whpx-common.c
+++ b/accel/whpx/whpx-common.c
@@ -550,6 +550,8 @@ static void whpx_accel_instance_init(Object *obj)
whpx->hyperv_enlightenments_allowed = true;
whpx->hyperv_enlightenments_required = false;
+ /* Value determined at whpx_accel_init */
+ whpx->hyperv_enlightenments_enabled = false;
}
static const TypeInfo whpx_accel_type = {
diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h
index 5902124b63..cf782cf5f8 100644
--- a/include/system/whpx-internal.h
+++ b/include/system/whpx-internal.h
@@ -45,6 +45,7 @@ struct whpx_state {
bool hyperv_enlightenments_allowed;
bool hyperv_enlightenments_required;
+ bool hyperv_enlightenments_enabled;
};
diff --git a/target/arm/whpx/whpx-all.c b/target/arm/whpx/whpx-all.c
index bbf0f6be96..4019a513aa 100644
--- a/target/arm/whpx/whpx-all.c
+++ b/target/arm/whpx/whpx-all.c
@@ -968,6 +968,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
* as they're not needed for performance.
*/
if (whpx->hyperv_enlightenments_required) {
+ whpx->hyperv_enlightenments_enabled = true;
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
WHvPartitionPropertyCodeSyntheticProcessorFeaturesBanks,
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 4954e0557a..f9f330c038 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2168,14 +2168,57 @@ int whpx_vcpu_run(CPUState *cpu)
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;
+ if (whpx_is_legacy_os()) {
+ 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;
+ } else {
+ cpu_x86_cpuid(env, vcpu->exit_ctx.CpuidAccess.Rax,
+ vcpu->exit_ctx.CpuidAccess.Rcx,
+ (UINT32 *)®_values[1].Reg32,
+ (UINT32 *)®_values[4].Reg32, (UINT32 *)®_values[2].Reg32,
+ (UINT32 *)®_values[3].Reg32);
+ }
+
+ if (!whpx->hyperv_enlightenments_enabled) {
+ switch (vcpu->exit_ctx.CpuidAccess.Rax) {
+ case 1:
+ reg_values[2].Reg64 |= CPUID_EXT_HYPERVISOR;
+ break;
+ case 0x40000000:
+ /*
+ * Use vmware_cpuid_freq as a proxy to report VMware.
+ * This is to get the TSC/APIC frequency query functionality
+ * provided through vmport, as Linux doesn't use leaf
+ * 0x40000010 for getting those frequencies.
+ */
+ if (x86_cpu->vmware_cpuid_freq) {
+ reg_values[1].Reg64 = 0x40000010;
+ reg_values[4].Reg64 = 0x61774d56;
+ reg_values[2].Reg64 = 0x4d566572;
+ reg_values[3].Reg64 = 0x65726177;
+ } else {
+ /* report KVM otherwise if that's disabled */
+ reg_values[1].Reg64 = 0x40000001;
+ reg_values[4].Reg64 = 0x4b4d564b;
+ reg_values[2].Reg64 = 0x564b4d56;
+ reg_values[3].Reg64 = 0x4d;
+ }
+ break;
+ case 0x40000001:
+ if (!x86_cpu->vmware_cpuid_freq) {
+ /* KVM reporting of X2APIC support */
+ reg_values[1].Reg64 = reg_values[4].Reg64 =
+ reg_values[2].Reg64 = 1 << 15;
+ }
+ break;
+ case 0x40000010:
+ if (x86_cpu->vmware_cpuid_freq) {
+ reg_values[1].Reg64 = env->tsc_khz;
+ reg_values[4].Reg64 = env->apic_bus_freq / 1000; /* Hz to KHz */
+ }
+ break;
}
}
@@ -2396,6 +2439,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
WHV_PROCESSOR_FEATURES_BANKS processor_features;
WHV_PROCESSOR_PERFMON_FEATURES perfmon_features;
UINT32 cpuidExitList[] = {1};
+ UINT32 cpuidExitList_nohyperv[] = {1, 0x40000000, 0x40000001, 0x40000010};
whpx = &whpx_global;
@@ -2601,6 +2645,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
}
if (is_modern_os && whpx->hyperv_enlightenments_allowed) {
+ whpx->hyperv_enlightenments_enabled = true;
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
WHvPartitionPropertyCodeSyntheticProcessorFeaturesBanks,
@@ -2653,7 +2698,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
WHvPartitionPropertyCodeCpuidExitList,
- cpuidExitList,
+ whpx->hyperv_enlightenments_enabled ? cpuidExitList : cpuidExitList_nohyperv,
RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
if (FAILED(hr)) {
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 07/37] whpx: i386: introduce proper cpuid support
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (5 preceding siblings ...)
2026-04-22 21:41 ` [PATCH v3 06/37] whpx: i386: reintroduce enlightenments for Windows 10 Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 08/37] whpx: i386: kernel-irqchip=off fixes Mohamed Mediouni
` (30 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Unlike the implementation in QEMU 10.2, this one works.
It's not optimal though as it doesn't use the Hyper-V support for this.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 136 +++++++++++++++++++++++++----
target/i386/whpx/whpx-cpu-legacy.c | 15 +---
2 files changed, 122 insertions(+), 29 deletions(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index f9f330c038..73e351d895 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2168,18 +2168,11 @@ int whpx_vcpu_run(CPUState *cpu)
vcpu->exit_ctx.VpContext.Rip +
vcpu->exit_ctx.VpContext.InstructionLength;
- if (whpx_is_legacy_os()) {
- 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;
- } else {
- cpu_x86_cpuid(env, vcpu->exit_ctx.CpuidAccess.Rax,
- vcpu->exit_ctx.CpuidAccess.Rcx,
- (UINT32 *)®_values[1].Reg32,
- (UINT32 *)®_values[4].Reg32, (UINT32 *)®_values[2].Reg32,
- (UINT32 *)®_values[3].Reg32);
- }
+ cpu_x86_cpuid(env, vcpu->exit_ctx.CpuidAccess.Rax,
+ vcpu->exit_ctx.CpuidAccess.Rcx,
+ (UINT32 *)®_values[1].Reg32,
+ (UINT32 *)®_values[4].Reg32, (UINT32 *)®_values[2].Reg32,
+ (UINT32 *)®_values[3].Reg32);
if (!whpx->hyperv_enlightenments_enabled) {
switch (vcpu->exit_ctx.CpuidAccess.Rax) {
@@ -2220,6 +2213,68 @@ int whpx_vcpu_run(CPUState *cpu)
}
break;
}
+ } else {
+ switch (vcpu->exit_ctx.CpuidAccess.Rax) {
+ case 0x40000000:
+ case 0x40000001:
+ case 0x40000010:
+ 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;
+ break;
+ }
+ }
+
+ if (vcpu->exit_ctx.CpuidAccess.Rax == 0x1) {
+ if (cpu_has_x2apic_feature(env)) {
+ reg_values[2].Reg64 |= CPUID_EXT_X2APIC;
+ } else {
+ reg_values[2].Reg32 &= ~CPUID_EXT_X2APIC;
+ }
+ }
+
+ /* Dynamic depending on XCR0 and XSS, so query DefaultResult */
+ if (vcpu->exit_ctx.CpuidAccess.Rax == 0x07
+ && vcpu->exit_ctx.CpuidAccess.Rcx == 0) {
+ if (vcpu->exit_ctx.CpuidAccess.DefaultResultRdx
+ & CPUID_7_0_EDX_CET_IBT) {
+ reg_values[3].Reg32 |= CPUID_7_0_EDX_CET_IBT;
+ } else {
+ reg_values[3].Reg32 &= ~CPUID_7_0_EDX_CET_IBT;
+ }
+
+ if (vcpu->exit_ctx.CpuidAccess.DefaultResultRcx
+ & CPUID_7_0_ECX_CET_SHSTK) {
+ reg_values[2].Reg32 |= CPUID_7_0_ECX_CET_SHSTK;
+ } else {
+ reg_values[2].Reg32 &= ~CPUID_7_0_ECX_CET_SHSTK;
+ }
+
+ if (vcpu->exit_ctx.CpuidAccess.DefaultResultRcx
+ & CPUID_7_0_ECX_OSPKE) {
+ reg_values[2].Reg32 |= CPUID_7_0_ECX_OSPKE;
+ } else {
+ reg_values[2].Reg32 &= ~CPUID_7_0_ECX_OSPKE;
+ }
+ }
+
+ /* CPUID[0xD,{1,2}].EBX are dynamic depending on guest features. */
+ if (vcpu->exit_ctx.CpuidAccess.Rax == 0xd) {
+ if (vcpu->exit_ctx.CpuidAccess.Rcx == 1
+ || vcpu->exit_ctx.CpuidAccess.Rcx == 2) {
+ reg_values[4].Reg64 = vcpu->exit_ctx.CpuidAccess.DefaultResultRbx;
+ }
+ }
+
+ /* OSXSAVE is dynamic. Do this instead of syncing CR4 */
+ if (vcpu->exit_ctx.CpuidAccess.Rax == 1) {
+ if (vcpu->exit_ctx.CpuidAccess.DefaultResultRcx
+ & CPUID_EXT_OSXSAVE) {
+ reg_values[2].Reg32 |= CPUID_EXT_OSXSAVE;
+ } else {
+ reg_values[2].Reg32 &= ~CPUID_EXT_OSXSAVE;
+ }
}
hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
@@ -2409,6 +2464,45 @@ error:
return ret;
}
+static void whpx_cpu_xsave_init(void)
+{
+ static bool first = true;
+ int i;
+
+ if (!first) {
+ return;
+ }
+ first = false;
+
+ /* x87 and SSE states are in the legacy region of the XSAVE area. */
+ x86_ext_save_areas[XSTATE_FP_BIT].offset = 0;
+ x86_ext_save_areas[XSTATE_SSE_BIT].offset = 0;
+
+ for (i = XSTATE_SSE_BIT + 1; i < XSAVE_STATE_AREA_COUNT; i++) {
+ ExtSaveArea *esa = &x86_ext_save_areas[i];
+
+ if (esa->size) {
+ int sz = whpx_get_supported_cpuid(0xd, i, R_EAX);
+ if (sz != 0) {
+ assert(esa->size == sz);
+ esa->offset = whpx_get_supported_cpuid(0xd, i, R_EBX);
+ }
+ }
+ }
+}
+
+static void whpx_cpu_max_instance_init(X86CPU *cpu)
+{
+ CPUX86State *env = &cpu->env;
+
+ env->cpuid_min_level =
+ whpx_get_supported_cpuid(0x0, 0, R_EAX);
+ env->cpuid_min_xlevel =
+ whpx_get_supported_cpuid(0x80000000, 0, R_EAX);
+ env->cpuid_min_xlevel2 =
+ whpx_get_supported_cpuid(0xC0000000, 0, R_EAX);
+}
+
static PropValue whpx_default_props[] = {
{ "x2apic", "on" },
{ NULL, NULL },
@@ -2418,9 +2512,18 @@ static PropValue whpx_default_props[] = {
void whpx_cpu_instance_init(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
+ X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu);
host_cpu_instance_init(cpu);
x86_cpu_apply_props(cpu, whpx_default_props);
+
+ if (xcc->max_features) {
+ whpx_cpu_max_instance_init(cpu);
+ }
+
+ if (whpx_has_xsave()) {
+ whpx_cpu_xsave_init();
+ }
}
/*
@@ -2438,8 +2541,11 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
WHV_CAPABILITY_FEATURES features = {0};
WHV_PROCESSOR_FEATURES_BANKS processor_features;
WHV_PROCESSOR_PERFMON_FEATURES perfmon_features;
- UINT32 cpuidExitList[] = {1};
- UINT32 cpuidExitList_nohyperv[] = {1, 0x40000000, 0x40000001, 0x40000010};
+
+ UINT32 cpuidExitList[] = {0x0, 0x1, 0x6, 0x7, 0xb, 0xd, 0x14, 0x24, 0x29, 0x1E,
+ 0x40000000, 0x40000001, 0x40000010, 0x80000000, 0x80000001,
+ 0x80000002, 0x80000003, 0x80000004, 0x80000007, 0x80000008,
+ 0x8000000A, 0x80000021, 0x80000022, 0xC0000000, 0xC0000001};
whpx = &whpx_global;
@@ -2698,7 +2804,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
WHvPartitionPropertyCodeCpuidExitList,
- whpx->hyperv_enlightenments_enabled ? cpuidExitList : cpuidExitList_nohyperv,
+ cpuidExitList,
RTL_NUMBER_OF(cpuidExitList) * sizeof(UINT32));
if (FAILED(hr)) {
diff --git a/target/i386/whpx/whpx-cpu-legacy.c b/target/i386/whpx/whpx-cpu-legacy.c
index 477429b460..d341e6f4fd 100644
--- a/target/i386/whpx/whpx-cpu-legacy.c
+++ b/target/i386/whpx/whpx-cpu-legacy.c
@@ -4,20 +4,7 @@
* Copyright (c) 2003 Fabrice Bellard
* Copyright (c) 2017 Google Inc.
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * cpuid
+ * SPDX-License-Identifier: LGPL-2.1-or-later
*/
#include "qemu/osdep.h"
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 08/37] whpx: i386: kernel-irqchip=off fixes
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (6 preceding siblings ...)
2026-04-22 21:41 ` [PATCH v3 07/37] whpx: i386: introduce proper cpuid support Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 09/37] whpx: i386: use WHvX64RegisterCr8 only when kernel-irqchip=off Mohamed Mediouni
` (29 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
This was really... quite broken. After fixing this,
Windows boots with kernel-irqchip=off.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
include/system/whpx-common.h | 1 +
target/i386/whpx/whpx-all.c | 43 +++++-------------------------------
2 files changed, 7 insertions(+), 37 deletions(-)
diff --git a/include/system/whpx-common.h b/include/system/whpx-common.h
index 04289afd97..3406c20fec 100644
--- a/include/system/whpx-common.h
+++ b/include/system/whpx-common.h
@@ -4,6 +4,7 @@
struct AccelCPUState {
bool window_registered;
+ int window_priority;
bool interruptable;
bool ready_for_pic_interrupt;
uint64_t tpr;
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 73e351d895..d470c5b9d3 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -22,6 +22,8 @@
#include "qemu/main-loop.h"
#include "hw/core/boards.h"
#include "hw/intc/ioapic.h"
+#include "hw/intc/i8259.h"
+#include "hw/i386/x86.h"
#include "hw/i386/apic_internal.h"
#include "qemu/error-report.h"
#include "qapi/error.h"
@@ -390,28 +392,6 @@ static int whpx_set_tsc(CPUState *cpu)
return 0;
}
-/*
- * The CR8 register in the CPU is mapped to the TPR register of the APIC,
- * however, they use a slightly different encoding. Specifically:
- *
- * APIC.TPR[bits 7:4] = CR8[bits 3:0]
- *
- * This mechanism is described in section 10.8.6.1 of Volume 3 of Intel 64
- * and IA-32 Architectures Software Developer's Manual.
- *
- * The functions below translate the value of CR8 to TPR and vice versa.
- */
-
-static uint64_t whpx_apic_tpr_to_cr8(uint64_t tpr)
-{
- return tpr >> 4;
-}
-
-static uint64_t whpx_cr8_to_apic_tpr(uint64_t cr8)
-{
- return cr8 << 4;
-}
-
void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
{
struct whpx_state *whpx = &whpx_global;
@@ -440,7 +420,7 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
v86 = (env->eflags & VM_MASK);
r86 = !(env->cr[0] & CR0_PE_MASK);
- vcpu->tpr = whpx_apic_tpr_to_cr8(cpu_get_apic_tpr(x86_cpu->apic_state));
+ vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
idx = 0;
@@ -711,17 +691,6 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
hr);
}
- if (whpx_irqchip_in_kernel()) {
- /*
- * Fetch the TPR value from the emulated APIC. It may get overwritten
- * below with the value from CR8 returned by
- * WHvGetVirtualProcessorRegisters().
- */
- whpx_apic_get(x86_cpu->apic_state);
- vcpu->tpr = whpx_apic_tpr_to_cr8(
- cpu_get_apic_tpr(x86_cpu->apic_state));
- }
-
idx = 0;
/* Indexes for first 16 registers match between HV and QEMU definitions */
@@ -770,7 +739,7 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
tpr = vcxt.values[idx++].Reg64;
if (tpr != vcpu->tpr) {
vcpu->tpr = tpr;
- cpu_set_apic_tpr(x86_cpu->apic_state, whpx_cr8_to_apic_tpr(tpr));
+ cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
}
/* 8 Debug Registers - Skipped */
@@ -1775,7 +1744,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
}
/* Sync the TPR to the CR8 if was modified during the intercept */
- tpr = whpx_apic_tpr_to_cr8(cpu_get_apic_tpr(x86_cpu->apic_state));
+ tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
if (tpr != vcpu->tpr) {
vcpu->tpr = tpr;
reg_values[reg_count].Reg64 = tpr;
@@ -1822,7 +1791,7 @@ static void whpx_vcpu_post_run(CPUState *cpu)
if (vcpu->tpr != tpr) {
vcpu->tpr = tpr;
bql_lock();
- cpu_set_apic_tpr(x86_cpu->apic_state, whpx_cr8_to_apic_tpr(vcpu->tpr));
+ cpu_set_apic_tpr(x86_cpu->apic_state, vcpu->tpr);
bql_unlock();
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 09/37] whpx: i386: use WHvX64RegisterCr8 only when kernel-irqchip=off
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (7 preceding siblings ...)
2026-04-22 21:41 ` [PATCH v3 08/37] whpx: i386: kernel-irqchip=off fixes Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 10/37] whpx: i386: disable kernel-irqchip on Windows 10 when PIC enabled Mohamed Mediouni
` (28 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
When kernel-irqchip=on, manage TPR as part of the APIC state instead entirely.
This fixes some failure to set state errors.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 37 ++++++++++++++++++++++---------------
1 file changed, 22 insertions(+), 15 deletions(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index d470c5b9d3..03c146dfb8 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -95,7 +95,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = {
WHvX64RegisterCr2,
WHvX64RegisterCr3,
WHvX64RegisterCr4,
- WHvX64RegisterCr8,
/* X64 Debug Registers */
/*
@@ -478,8 +477,11 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
vcxt.values[idx++].Reg64 = env->cr[3];
assert(whpx_register_names[idx] == WHvX64RegisterCr4);
vcxt.values[idx++].Reg64 = env->cr[4];
- assert(whpx_register_names[idx] == WHvX64RegisterCr8);
- vcxt.values[idx++].Reg64 = vcpu->tpr;
+ /* For kernel-irqchip=on, TPR is managed as part of APIC state */
+ if (!whpx_irqchip_in_kernel()) {
+ WHV_REGISTER_VALUE cr8 = {.Reg64 = vcpu->tpr};
+ whpx_set_reg(cpu, WHvX64RegisterCr8, cr8);
+ }
/* 8 Debug Registers - Skipped */
@@ -735,11 +737,14 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
env->cr[3] = vcxt.values[idx++].Reg64;
assert(whpx_register_names[idx] == WHvX64RegisterCr4);
env->cr[4] = vcxt.values[idx++].Reg64;
- assert(whpx_register_names[idx] == WHvX64RegisterCr8);
- tpr = vcxt.values[idx++].Reg64;
- if (tpr != vcpu->tpr) {
- vcpu->tpr = tpr;
- cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
+
+ /* For kernel-irqchip=on, TPR is managed as part of APIC state */
+ if (!whpx_irqchip_in_kernel()) {
+ tpr = vcpu->exit_ctx.VpContext.Cr8;
+ if (tpr != vcpu->tpr) {
+ vcpu->tpr = tpr;
+ cpu_set_apic_tpr(x86_cpu->apic_state, tpr);
+ }
}
/* 8 Debug Registers - Skipped */
@@ -1745,7 +1750,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
/* Sync the TPR to the CR8 if was modified during the intercept */
tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
- if (tpr != vcpu->tpr) {
+ if (!whpx_irqchip_in_kernel() && tpr != vcpu->tpr) {
vcpu->tpr = tpr;
reg_values[reg_count].Reg64 = tpr;
qatomic_set(&cpu->exit_request, true);
@@ -1787,12 +1792,14 @@ static void whpx_vcpu_post_run(CPUState *cpu)
env->eflags = vcpu->exit_ctx.VpContext.Rflags;
- uint64_t tpr = vcpu->exit_ctx.VpContext.Cr8;
- if (vcpu->tpr != tpr) {
- vcpu->tpr = tpr;
- bql_lock();
- cpu_set_apic_tpr(x86_cpu->apic_state, vcpu->tpr);
- bql_unlock();
+ if (!whpx_irqchip_in_kernel()) {
+ uint64_t tpr = vcpu->exit_ctx.VpContext.Cr8;
+ if (vcpu->tpr != tpr) {
+ vcpu->tpr = tpr;
+ bql_lock();
+ cpu_set_apic_tpr(x86_cpu->apic_state, vcpu->tpr);
+ bql_unlock();
+ }
}
vcpu->interruption_pending =
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 10/37] whpx: i386: disable kernel-irqchip on Windows 10 when PIC enabled
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (8 preceding siblings ...)
2026-04-22 21:41 ` [PATCH v3 09/37] whpx: i386: use WHvX64RegisterCr8 only when kernel-irqchip=off Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:41 ` [PATCH v3 11/37] whpx: i386: IO port fast path cleanup Mohamed Mediouni
` (27 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Move WHvCapabilityCodeProcessorPerfmonFeatures queries
as that's how we distinguish if on a legacy OS.
Now that Windows guests are booting, disable kernel-irqchip=on
by default for Windows 10 when the PIC is enabled.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 69 +++++++++++++++++++++----------------
1 file changed, 39 insertions(+), 30 deletions(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 03c146dfb8..ab8e97787f 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2523,6 +2523,13 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
0x80000002, 0x80000003, 0x80000004, 0x80000007, 0x80000008,
0x8000000A, 0x80000021, 0x80000022, 0xC0000000, 0xC0000001};
+ X86MachineState *x86ms = X86_MACHINE(ms);
+ bool pic_enabled = false;
+
+ if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) {
+ pic_enabled = true;
+ }
+
whpx = &whpx_global;
if (!init_whp_dispatch()) {
@@ -2594,6 +2601,35 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
goto error;
}
+ /* Enable supported performance monitoring capabilities */
+ hr = whp_dispatch.WHvGetCapability(
+ WHvCapabilityCodeProcessorPerfmonFeatures, &perfmon_features,
+ sizeof(WHV_PROCESSOR_PERFMON_FEATURES), &whpx_cap_size);
+ /*
+ * Relying on this is a crutch to maintain Windows 10 support.
+ *
+ * WHvCapabilityCodeProcessorPerfmonFeatures and
+ * WHvPartitionPropertyCodeSyntheticProcessorFeaturesBanks
+ * are implemented starting from Windows Server 2022 (build 20348).
+ */
+ if (FAILED(hr)) {
+ warn_report("WHPX: Failed to get performance "
+ "monitoring features, hr=%08lx", hr);
+ is_modern_os = false;
+ } else {
+ hr = whp_dispatch.WHvSetPartitionProperty(
+ whpx->partition,
+ WHvPartitionPropertyCodeProcessorPerfmonFeatures,
+ &perfmon_features,
+ sizeof(WHV_PROCESSOR_PERFMON_FEATURES));
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to set performance "
+ "monitoring features, hr=%08lx", hr);
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+
/*
* Error out if WHP doesn't support apic emulation and user is requiring
* it.
@@ -2606,8 +2642,9 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
goto error;
}
- if (whpx->kernel_irqchip_allowed && features.LocalApicEmulation &&
- whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2) {
+ if (whpx->kernel_irqchip_allowed && !(whpx_is_legacy_os() && pic_enabled
+ && !whpx->kernel_irqchip_required) && features.LocalApicEmulation
+ && whp_dispatch.WHvSetVirtualProcessorInterruptControllerState2) {
WHV_X64_LOCAL_APIC_EMULATION_MODE mode =
WHvX64LocalApicEmulationModeX2Apic;
hr = whp_dispatch.WHvSetPartitionProperty(
@@ -2669,34 +2706,6 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
goto error;
}
- /* Enable supported performance monitoring capabilities */
- hr = whp_dispatch.WHvGetCapability(
- WHvCapabilityCodeProcessorPerfmonFeatures, &perfmon_features,
- sizeof(WHV_PROCESSOR_PERFMON_FEATURES), &whpx_cap_size);
- /*
- * Relying on this is a crutch to maintain Windows 10 support.
- *
- * WHvCapabilityCodeProcessorPerfmonFeatures and
- * WHvPartitionPropertyCodeSyntheticProcessorFeaturesBanks
- * are implemented starting from Windows Server 2022 (build 20348).
- */
- if (FAILED(hr)) {
- warn_report("WHPX: Failed to get performance "
- "monitoring features, hr=%08lx", hr);
- is_modern_os = false;
- } else {
- hr = whp_dispatch.WHvSetPartitionProperty(
- whpx->partition,
- WHvPartitionPropertyCodeProcessorPerfmonFeatures,
- &perfmon_features,
- sizeof(WHV_PROCESSOR_PERFMON_FEATURES));
- if (FAILED(hr)) {
- error_report("WHPX: Failed to set performance "
- "monitoring features, hr=%08lx", hr);
- ret = -EINVAL;
- goto error;
- }
- }
/* Enable synthetic processor features */
WHV_SYNTHETIC_PROCESSOR_FEATURES_BANKS synthetic_features;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 11/37] whpx: i386: IO port fast path cleanup
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (9 preceding siblings ...)
2026-04-22 21:41 ` [PATCH v3 10/37] whpx: i386: disable kernel-irqchip on Windows 10 when PIC enabled Mohamed Mediouni
@ 2026-04-22 21:41 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 12/37] whpx: i386: disable enlightenments and LAPIC for isapc Mohamed Mediouni
` (26 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:41 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
vmport calls synchronise_state within an I/O port read.
Support that properly.
What was there before worked because of a side effect of
whpx_get_reg synchronising context if cpu->vcpu_dirty.
Remove that whpx_get_reg call in whpx_bump_rip too as it's no longer
needed now.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index ab8e97787f..75f0f062d4 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -881,7 +881,6 @@ static void handle_io(CPUState *env, uint16_t port, void *buffer,
static void whpx_bump_rip(CPUState *cpu, WHV_RUN_VP_EXIT_CONTEXT *exit_ctx)
{
WHV_REGISTER_VALUE reg;
- whpx_get_reg(cpu, WHvX64RegisterRip, ®);
reg.Reg64 = exit_ctx->VpContext.Rip + exit_ctx->VpContext.InstructionLength;
whpx_set_reg(cpu, WHvX64RegisterRip, reg);
}
@@ -909,13 +908,23 @@ static int whpx_handle_portio(CPUState *cpu,
} else {
reg.Reg64 = (uint64_t)val;
}
- whpx_bump_rip(cpu, exit_ctx);
- whpx_set_reg(cpu, WHvX64RegisterRax, reg);
+ /* vmport calls cpu_synchronize_state on an I/O port read */
+ if (!cpu->vcpu_dirty) {
+ whpx_bump_rip(cpu, exit_ctx);
+ whpx_set_reg(cpu, WHvX64RegisterRax, reg);
+ } else {
+ env->eip = exit_ctx->VpContext.Rip + exit_ctx->VpContext.InstructionLength;
+ env->regs[R_EAX] = reg.Reg64;
+ }
return 0;
} else if (!ctx->AccessInfo.StringOp && ctx->AccessInfo.IsWrite) {
RAX(env) = ctx->Rax;
handle_io(cpu, ctx->PortNumber, &RAX(env), 1, ctx->AccessInfo.AccessSize, 1);
- whpx_bump_rip(cpu, exit_ctx);
+ if (!cpu->vcpu_dirty) {
+ whpx_bump_rip(cpu, exit_ctx);
+ } else {
+ env->eip = exit_ctx->VpContext.Rip + exit_ctx->VpContext.InstructionLength;
+ }
return 0;
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 12/37] whpx: i386: disable enlightenments and LAPIC for isapc
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (10 preceding siblings ...)
2026-04-22 21:41 ` [PATCH v3 11/37] whpx: i386: IO port fast path cleanup Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 13/37] whpx: i386: interrupt priority support Mohamed Mediouni
` (25 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
The isapc machine doesn't have an APIC. And Hyper-V enlightenments
don't sound too useful to have there so disable those.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 75f0f062d4..cbcf1de7ae 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2546,6 +2546,14 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
goto error;
}
+ /* for isapc, disable Hyper-V enlightenments and LAPIC */
+ if (!strcmp(MACHINE_GET_CLASS(ms)->name, "isapc")) {
+ whpx->kernel_irqchip_allowed = false;
+ whpx->kernel_irqchip_required = false;
+ whpx->hyperv_enlightenments_allowed = false;
+ whpx->hyperv_enlightenments_required = false;
+ }
+
whpx->mem_quota = ms->ram_size;
hr = whp_dispatch.WHvGetCapability(
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 13/37] whpx: i386: interrupt priority support
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (11 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 12/37] whpx: i386: disable enlightenments and LAPIC for isapc Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 14/37] hw/intc: apic: disallow APIC reads when disabled Mohamed Mediouni
` (24 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Implement APIC IRR interrupt priorities.
Even with kernel-irqchip=off, Hyper-V is aware of interrupt priorities
and implements CR8/TPR, with the InterruptPriority field being followed.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 24 ++++++++++++++++++++----
1 file changed, 20 insertions(+), 4 deletions(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index cbcf1de7ae..012fa6d021 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -1673,6 +1673,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
UINT32 reg_count = 0;
WHV_REGISTER_VALUE reg_values[3];
WHV_REGISTER_NAME reg_names[3];
+ int irr = apic_get_highest_priority_irr(x86_cpu->apic_state);
memset(&new_int, 0, sizeof(new_int));
memset(reg_values, 0, sizeof(reg_values));
@@ -1708,10 +1709,20 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
}
}
+ if (irr == -1) {
+ if (isa_pic != NULL && pic_get_output(isa_pic)) {
+ /* In case it's a PIC interrupt */
+ irr = 0;
+ } else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) {
+ abort();
+ }
+ }
+
/* Get pending hard interruption or replay one that was overwritten */
if (!whpx_irqchip_in_kernel()) {
if (!vcpu->interruption_pending &&
- vcpu->interruptable && (env->eflags & IF_MASK)) {
+ vcpu->interruptable && (env->eflags & IF_MASK)
+ && (vcpu->tpr < irr || irr == 0)) {
assert(!new_int.InterruptionPending);
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) {
cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD);
@@ -1768,13 +1779,17 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
}
/* Update the state of the interrupt delivery notification */
- if (!vcpu->window_registered &&
+ if ((!vcpu->window_registered ||
+ (vcpu->window_priority < irr && vcpu->window_priority != 0) ||
+ (irr == 0 && vcpu->window_priority != 0)) &&
cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) {
reg_values[reg_count].DeliverabilityNotifications =
(WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER) {
- .InterruptNotification = 1
+ .InterruptNotification = 1,
+ .InterruptPriority = irr >> 4
};
vcpu->window_registered = 1;
+ vcpu->window_priority = irr;
reg_names[reg_count] = WHvX64RegisterDeliverabilityNotifications;
reg_count += 1;
}
@@ -1788,7 +1803,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu)
reg_names, reg_count, reg_values);
if (FAILED(hr)) {
error_report("WHPX: Failed to set interrupt state registers,"
- " hr=%08lx", hr);
+ " hr=%08lx, InterruptPriority=%i", hr, irr >> 4);
}
}
}
@@ -2004,6 +2019,7 @@ int whpx_vcpu_run(CPUState *cpu)
case WHvRunVpExitReasonX64InterruptWindow:
vcpu->ready_for_pic_interrupt = 1;
vcpu->window_registered = 0;
+ vcpu->window_priority = 0;
ret = 0;
break;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 14/37] hw/intc: apic: disallow APIC reads when disabled
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (12 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 13/37] whpx: i386: interrupt priority support Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 15/37] whpx: i386: fix CPUID[1:EDX].APIC reporting Mohamed Mediouni
` (23 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
!APICBASE_ENABLE + attempting to read xAPIC registers is not an allowed combination.
And neither is x2APIC enabled + attempting to read xAPIC registers
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
hw/intc/apic.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/hw/intc/apic.c b/hw/intc/apic.c
index 8766ed00b9..e5ea831261 100644
--- a/hw/intc/apic.c
+++ b/hw/intc/apic.c
@@ -875,6 +875,15 @@ static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size)
return -1;
}
+ /* if the xAPIC is disabled, return early. */
+ if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE)) {
+ return 0xffffffff;
+ }
+
+ if (is_x2apic_mode(s)) {
+ return 0xffffffff;
+ }
+
index = (addr >> 4) & 0xff;
apic_register_read(s, index, &val);
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 15/37] whpx: i386: fix CPUID[1:EDX].APIC reporting
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (13 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 14/37] hw/intc: apic: disallow APIC reads when disabled Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 16/37] whpx: i386: set apicbase value only on success Mohamed Mediouni
` (22 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Hyper-V always has CPUID[1:EDX].APIC set, even when the APIC isn't enabled yet.
Work around this by also using the APICBASE trap for kernel-irqchip=on.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
include/system/whpx-common.h | 1 -
target/i386/whpx/whpx-all.c | 34 ++++++++---------
target/i386/whpx/whpx-apic.c | 71 ++++++++++++++++++++++++++++++++++--
3 files changed, 84 insertions(+), 22 deletions(-)
diff --git a/include/system/whpx-common.h b/include/system/whpx-common.h
index 3406c20fec..79710e2fb3 100644
--- a/include/system/whpx-common.h
+++ b/include/system/whpx-common.h
@@ -8,7 +8,6 @@ struct AccelCPUState {
bool interruptable;
bool ready_for_pic_interrupt;
uint64_t tpr;
- uint64_t apic_base;
bool interruption_pending;
/* Must be the last field as it may have a tail */
WHV_RUN_VP_EXIT_CONTEXT exit_ctx;
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 012fa6d021..b055644580 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -139,7 +139,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = {
#ifdef TARGET_X86_64
WHvX64RegisterKernelGsBase,
#endif
- WHvX64RegisterApicBase,
/* WHvX64RegisterPat, */
WHvX64RegisterSysenterCs,
WHvX64RegisterSysenterEip,
@@ -420,7 +419,6 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
r86 = !(env->cr[0] & CR0_PE_MASK);
vcpu->tpr = cpu_get_apic_tpr(x86_cpu->apic_state);
- vcpu->apic_base = cpu_get_apic_base(x86_cpu->apic_state);
idx = 0;
@@ -538,9 +536,6 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
vcxt.values[idx++].Reg64 = env->kernelgsbase;
#endif
- assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
- vcxt.values[idx++].Reg64 = vcpu->apic_base;
-
/* WHvX64RegisterPat - Skipped */
assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
@@ -575,6 +570,12 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
hr);
}
+
+ if (level >= WHPX_LEVEL_FULL_STATE) {
+ WHV_REGISTER_VALUE apic_base = {};
+ apic_base.Reg64 = cpu_get_apic_base(X86_CPU(cpu)->apic_state);
+ whpx_set_reg(cpu, WHvX64RegisterApicBase, apic_base);
+ }
}
static int whpx_get_tsc(CPUState *cpu)
@@ -666,7 +667,7 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
struct whpx_register_set vcxt;
- uint64_t tpr, apic_base;
+ uint64_t tpr;
HRESULT hr;
int idx;
int idx_next;
@@ -798,13 +799,6 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
env->kernelgsbase = vcxt.values[idx++].Reg64;
#endif
- assert(whpx_register_names[idx] == WHvX64RegisterApicBase);
- apic_base = vcxt.values[idx++].Reg64;
- if (apic_base != vcpu->apic_base) {
- vcpu->apic_base = apic_base;
- cpu_set_apic_base(x86_cpu->apic_state, vcpu->apic_base);
- }
-
/* WHvX64RegisterPat - Skipped */
assert(whpx_register_names[idx] == WHvX64RegisterSysenterCs);
@@ -2082,8 +2076,7 @@ int whpx_vcpu_run(CPUState *cpu)
val = X86_CPU(cpu)->env.apic_bus_freq;
}
- if (!whpx_irqchip_in_kernel() &&
- vcpu->exit_ctx.MsrAccess.MsrNumber == MSR_IA32_APICBASE) {
+ if (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 */
@@ -2233,6 +2226,13 @@ int whpx_vcpu_run(CPUState *cpu)
} else {
reg_values[2].Reg32 &= ~CPUID_EXT_X2APIC;
}
+
+ /* CPUID[1:EDX].APIC is dynamic */
+ if (env->features[FEAT_1_EDX] & CPUID_APIC) {
+ reg_values[3].Reg32 |= CPUID_APIC;
+ } else {
+ reg_values[3].Reg32 &= ~CPUID_APIC;
+ }
}
/* Dynamic depending on XCR0 and XSS, so query DefaultResult */
@@ -2804,9 +2804,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
prop.X64MsrExitBitmap.UnhandledMsrs = 1;
- if (!whpx_irqchip_in_kernel()) {
- prop.X64MsrExitBitmap.ApicBaseMsrWrite = 1;
- }
+ prop.X64MsrExitBitmap.ApicBaseMsrWrite = 1;
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
diff --git a/target/i386/whpx/whpx-apic.c b/target/i386/whpx/whpx-apic.c
index f26ecaf6e8..65629ca45f 100644
--- a/target/i386/whpx/whpx-apic.c
+++ b/target/i386/whpx/whpx-apic.c
@@ -90,9 +90,70 @@ static void whpx_get_apic_state(APICCommonState *s,
apic_next_timer(s, s->initial_count_load_time);
}
-static int whpx_apic_set_base(APICCommonState *s, uint64_t val)
+static int apic_set_base_check(APICCommonState *s, uint64_t val)
{
- s->apicbase = val;
+ /* Enable x2apic when x2apic is not supported by CPU */
+ if (!cpu_has_x2apic_feature(&s->cpu->env) &&
+ val & MSR_IA32_APICBASE_EXTD) {
+ return -1;
+ }
+
+ /*
+ * Transition into invalid state
+ * (s->apicbase & MSR_IA32_APICBASE_ENABLE == 0) &&
+ * (s->apicbase & MSR_IA32_APICBASE_EXTD) == 1
+ */
+ if (!(val & MSR_IA32_APICBASE_ENABLE) &&
+ (val & MSR_IA32_APICBASE_EXTD)) {
+ return -1;
+ }
+
+ /* Invalid transition from disabled mode to x2APIC */
+ if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
+ !(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
+ (val & MSR_IA32_APICBASE_ENABLE) &&
+ (val & MSR_IA32_APICBASE_EXTD)) {
+ return -1;
+ }
+
+ /* Invalid transition from x2APIC to xAPIC */
+ if ((s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
+ (s->apicbase & MSR_IA32_APICBASE_EXTD) &&
+ (val & MSR_IA32_APICBASE_ENABLE) &&
+ !(val & MSR_IA32_APICBASE_EXTD)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int apic_set_base(APICCommonState *s, uint64_t val)
+{
+ if (apic_set_base_check(s, val) < 0) {
+ return -1;
+ }
+
+ s->apicbase = (val & MSR_IA32_APICBASE_BASE) |
+ (s->apicbase & (MSR_IA32_APICBASE_BSP | MSR_IA32_APICBASE_ENABLE));
+ if (!(val & MSR_IA32_APICBASE_ENABLE)) {
+ s->apicbase &= ~MSR_IA32_APICBASE_ENABLE;
+ cpu_clear_apic_feature(&s->cpu->env);
+ }
+
+ /* Transition from disabled mode to xAPIC */
+ if (!(s->apicbase & MSR_IA32_APICBASE_ENABLE) &&
+ (val & MSR_IA32_APICBASE_ENABLE)) {
+ s->apicbase |= MSR_IA32_APICBASE_ENABLE;
+ cpu_set_apic_feature(&s->cpu->env);
+ }
+
+ /* Transition from xAPIC to x2APIC */
+ if (cpu_has_x2apic_feature(&s->cpu->env) &&
+ !(s->apicbase & MSR_IA32_APICBASE_EXTD) &&
+ (val & MSR_IA32_APICBASE_EXTD)) {
+ s->apicbase |= MSR_IA32_APICBASE_EXTD;
+ }
+
return 0;
}
@@ -235,6 +296,10 @@ static void whpx_apic_mem_write(void *opaque, hwaddr addr,
static const MemoryRegionOps whpx_apic_io_ops = {
.read = whpx_apic_mem_read,
.write = whpx_apic_mem_write,
+ .impl.min_access_size = 1,
+ .impl.max_access_size = 4,
+ .valid.min_access_size = 1,
+ .valid.max_access_size = 4,
.endianness = DEVICE_LITTLE_ENDIAN,
};
@@ -262,7 +327,7 @@ static void whpx_apic_class_init(ObjectClass *klass, const void *data)
k->realize = whpx_apic_realize;
k->reset = whpx_apic_reset;
- k->set_base = whpx_apic_set_base;
+ k->set_base = apic_set_base;
k->set_tpr = whpx_apic_set_tpr;
k->get_tpr = whpx_apic_get_tpr;
k->post_load = whpx_apic_post_load;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 16/37] whpx: i386: set apicbase value only on success
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (14 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 15/37] whpx: i386: fix CPUID[1:EDX].APIC reporting Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 17/37] whpx: i386: unknown MSR configurability Mohamed Mediouni
` (21 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index b055644580..3426811c48 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2086,8 +2086,9 @@ int whpx_vcpu_run(CPUState *cpu)
int msr_ret = cpu_set_apic_base(X86_CPU(cpu)->apic_state, val);
if (msr_ret < 0) {
x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ } else {
+ whpx_set_reg(cpu, WHvX64RegisterApicBase, reg);
}
- whpx_set_reg(cpu, WHvX64RegisterApicBase, reg);
}
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 17/37] whpx: i386: unknown MSR configurability
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (15 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 16/37] whpx: i386: set apicbase value only on success Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 18/37] whpx: i386: enable GuestIdleReg enlightenment Mohamed Mediouni
` (20 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Add an option to inject back a GPF for unknown MSRs.
Keep it on by default for now as Linux expects accesses to some
AMD-specific MSRs to always succeed when on an AMD host.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
accel/whpx/whpx-common.c | 3 +++
include/system/whpx-all.h | 1 +
include/system/whpx-internal.h | 1 +
target/arm/whpx/whpx-all.c | 4 +++
target/i386/whpx/whpx-all.c | 45 ++++++++++++++++++++++++++++++++++
5 files changed, 54 insertions(+)
diff --git a/accel/whpx/whpx-common.c b/accel/whpx/whpx-common.c
index 59be996aef..497c03138e 100644
--- a/accel/whpx/whpx-common.c
+++ b/accel/whpx/whpx-common.c
@@ -538,6 +538,8 @@ static void whpx_accel_class_init(ObjectClass *oc, const void *data)
NULL, NULL);
object_class_property_set_description(oc, "hyperv",
"Configure Hyper-V enlightenments");
+
+ whpx_arch_accel_class_init(oc);
}
static void whpx_accel_instance_init(Object *obj)
@@ -552,6 +554,7 @@ static void whpx_accel_instance_init(Object *obj)
whpx->hyperv_enlightenments_required = false;
/* Value determined at whpx_accel_init */
whpx->hyperv_enlightenments_enabled = false;
+ whpx->ignore_unknown_msr = true;
}
static const TypeInfo whpx_accel_type = {
diff --git a/include/system/whpx-all.h b/include/system/whpx-all.h
index 2cbea71b14..4022571fff 100644
--- a/include/system/whpx-all.h
+++ b/include/system/whpx-all.h
@@ -20,6 +20,7 @@ void whpx_translate_cpu_breakpoints(
CPUState *cpu,
int cpu_breakpoint_count);
void whpx_arch_destroy_vcpu(CPUState *cpu);
+void whpx_arch_accel_class_init(ObjectClass *oc);
/* called by whpx-accel-ops */
bool whpx_arch_supports_guest_debug(void);
diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h
index cf782cf5f8..86639627b3 100644
--- a/include/system/whpx-internal.h
+++ b/include/system/whpx-internal.h
@@ -47,6 +47,7 @@ struct whpx_state {
bool hyperv_enlightenments_required;
bool hyperv_enlightenments_enabled;
+ bool ignore_unknown_msr;
};
extern struct whpx_state whpx_global;
diff --git a/target/arm/whpx/whpx-all.c b/target/arm/whpx/whpx-all.c
index 4019a513aa..94304a4230 100644
--- a/target/arm/whpx/whpx-all.c
+++ b/target/arm/whpx/whpx-all.c
@@ -823,6 +823,10 @@ void whpx_cpu_instance_init(CPUState *cs)
{
}
+void whpx_arch_accel_class_init(ObjectClass *oc)
+{
+}
+
int whpx_accel_init(AccelState *as, MachineState *ms)
{
struct whpx_state *whpx;
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 3426811c48..7f0ad6a24f 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2133,6 +2133,10 @@ int whpx_vcpu_run(CPUState *cpu)
vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite);
}
+ if (!is_known_msr && !whpx->ignore_unknown_msr) {
+ x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ }
+
hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
whpx->partition,
cpu->cpu_index,
@@ -2532,6 +2536,47 @@ void whpx_cpu_instance_init(CPUState *cs)
* Partition support
*/
+static void whpx_set_unknown_msr(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ struct whpx_state *whpx = &whpx_global;
+ OnOffAuto mode;
+
+ if (!visit_type_OnOffAuto(v, name, &mode, errp)) {
+ return;
+ }
+
+ switch (mode) {
+ case ON_OFF_AUTO_ON:
+ whpx->ignore_unknown_msr = true;
+ break;
+
+ case ON_OFF_AUTO_OFF:
+ whpx->ignore_unknown_msr = false;
+ break;
+
+ case ON_OFF_AUTO_AUTO:
+ whpx->ignore_unknown_msr = true;
+ break;
+ default:
+ /*
+ * The value was checked in visit_type_OnOffAuto() above. If
+ * we get here, then something is wrong in QEMU.
+ */
+ abort();
+ }
+}
+
+void whpx_arch_accel_class_init(ObjectClass *oc)
+{
+ object_class_property_add(oc, "ignore-unknown-msr", "OnOffAuto",
+ NULL, whpx_set_unknown_msr,
+ NULL, NULL);
+ object_class_property_set_description(oc, "ignore-unknown-msr",
+ "Configure unknown MSR behavior");
+}
+
int whpx_accel_init(AccelState *as, MachineState *ms)
{
struct whpx_state *whpx;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 18/37] whpx: i386: enable GuestIdleReg enlightenment
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (16 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 17/37] whpx: i386: unknown MSR configurability Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 19/37] whpx: i386: tighten APIC base validity check Mohamed Mediouni
` (19 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
This corresponds to HV_X64_MSR_GUEST_IDLE (0x400000f0).
This enlightenment is only available by the HV when using the Hyper-V LAPIC.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 7f0ad6a24f..eecc7f48ed 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2806,6 +2806,7 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
synthetic_features.Bank0.AccessIntrCtrlRegs = 1;
synthetic_features.Bank0.SyntheticClusterIpi = 1;
synthetic_features.Bank0.DirectSyntheticTimers = 1;
+ synthetic_features.Bank0.AccessGuestIdleReg = 1;
/*
* These technically work without the Hyper-V LAPIC
* but behave oddly for multi-core VMs.
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 19/37] whpx: i386: tighten APIC base validity check
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (17 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 18/37] whpx: i386: enable GuestIdleReg enlightenment Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 20/37] whpx: i386: ignore vpassist when kernel-irqchip=off Mohamed Mediouni
` (18 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index eecc7f48ed..521a94355d 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2078,6 +2078,9 @@ int whpx_vcpu_run(CPUState *cpu)
if (vcpu->exit_ctx.MsrAccess.MsrNumber == MSR_IA32_APICBASE) {
is_known_msr = 1;
+ if (val & MSR_IA32_APICBASE_RESERVED) {
+ x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ }
if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
/* Read path unreachable on Hyper-V */
abort();
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 20/37] whpx: i386: ignore vpassist when kernel-irqchip=off
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (18 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 19/37] whpx: i386: tighten APIC base validity check Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 21/37] target: i386: HLT type that ignores EFLAGS.IF Mohamed Mediouni
` (17 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Linux tries to set vpassist even when none of the enlightenments
using it are available.
So ignore the page it sets.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 521a94355d..cafcbf8bbb 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -51,6 +51,7 @@
#define HYPERV_APIC_BUS_FREQUENCY (200000000ULL)
/* for kernel-irqchip=off */
#define HV_X64_MSR_APIC_FREQUENCY 0x40000023
+#define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073
static bool is_modern_os = true;
@@ -2118,6 +2119,18 @@ int whpx_vcpu_run(CPUState *cpu)
}
}
}
+
+ /*
+ * Linux tries to use it anyway even when not exposed.
+ * Ignore the write as the VP assist page is not used.
+ */
+ if (vcpu->exit_ctx.MsrAccess.MsrNumber == HV_X64_MSR_VP_ASSIST_PAGE
+ && vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite
+ && !whpx_irqchip_in_kernel()
+ && whpx->hyperv_enlightenments_enabled) {
+ is_known_msr = 1;
+ }
+
/*
* For all unsupported MSR access we:
* ignore writes
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 21/37] target: i386: HLT type that ignores EFLAGS.IF
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (19 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 20/37] whpx: i386: ignore vpassist when kernel-irqchip=off Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-30 13:43 ` Paolo Bonzini
2026-04-22 21:42 ` [PATCH v3 22/37] whpx: i386: add HV_X64_MSR_GUEST_IDLE when !kernel-irqchip Mohamed Mediouni
` (16 subsequent siblings)
37 siblings, 1 reply; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
The TLFS says:
> A partition which possesses the AccessGuestIdleMsr privilege may trigger
> entry into the virtual processor idle sleep state through a read to the
> hypervisor-defined MSR HV_X64_MSR_GUEST_IDLE. The virtual processor will
> be woken when an interrupt arrives, regardless of whether the interrupt
> is enabled on the virtual processor or not.
Meanwhile, Windows 24H2+ calls this MSR anyway without the privilege being set.
Add the infrastructure to support it on the generic QEMU side.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/cpu.c | 10 ++++++----
target/i386/cpu.h | 2 ++
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index a5f1a1a8fd..c9ed9b7580 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -10474,13 +10474,15 @@ int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request)
(((env->hflags2 & HF2_VINTR_MASK) &&
(env->hflags2 & HF2_HIF_MASK)) ||
(!(env->hflags2 & HF2_VINTR_MASK) &&
- (env->eflags & IF_MASK &&
- !(env->hflags & HF_INHIBIT_IRQ_MASK))))) {
+ ((env->eflags & IF_MASK &&
+ !(env->hflags & HF_INHIBIT_IRQ_MASK))
+ || env->hflags2 & HF2_HYPERV_HLT_MASK)))) {
return CPU_INTERRUPT_HARD;
} else if (env->hflags2 & HF2_VGIF_MASK) {
if((interrupt_request & CPU_INTERRUPT_VIRQ) &&
- (env->eflags & IF_MASK) &&
- !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
+ ((env->eflags & IF_MASK &&
+ !(env->hflags & HF_INHIBIT_IRQ_MASK))
+ || env->hflags2 & HF2_HYPERV_HLT_MASK)) {
return CPU_INTERRUPT_VIRQ;
}
}
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 0b539155c4..67f508dc10 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -225,6 +225,7 @@ typedef enum X86Seg {
#define HF2_NPT_SHIFT 6 /* Nested Paging enabled */
#define HF2_IGNNE_SHIFT 7 /* Ignore CR0.NE=0 */
#define HF2_VGIF_SHIFT 8 /* Can take VIRQ*/
+#define HF2_HYPERV_HLT_SHIFT 9 /* Hyper-V HV_X64_MSR_GUEST_IDLE */
#define HF2_GIF_MASK (1 << HF2_GIF_SHIFT)
#define HF2_HIF_MASK (1 << HF2_HIF_SHIFT)
@@ -235,6 +236,7 @@ typedef enum X86Seg {
#define HF2_NPT_MASK (1 << HF2_NPT_SHIFT)
#define HF2_IGNNE_MASK (1 << HF2_IGNNE_SHIFT)
#define HF2_VGIF_MASK (1 << HF2_VGIF_SHIFT)
+#define HF2_HYPERV_HLT_MASK (1 << HF2_HYPERV_HLT_SHIFT)
#define CR0_PE_SHIFT 0
#define CR0_MP_SHIFT 1
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v3 21/37] target: i386: HLT type that ignores EFLAGS.IF
2026-04-22 21:42 ` [PATCH v3 21/37] target: i386: HLT type that ignores EFLAGS.IF Mohamed Mediouni
@ 2026-04-30 13:43 ` Paolo Bonzini
0 siblings, 0 replies; 45+ messages in thread
From: Paolo Bonzini @ 2026-04-30 13:43 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Roman Bolshakov,
Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan, Peter Maydell,
Zhao Liu
On 4/22/26 23:42, Mohamed Mediouni wrote:
> The TLFS says:
>
>> A partition which possesses the AccessGuestIdleMsr privilege may trigger
>> entry into the virtual processor idle sleep state through a read to the
>> hypervisor-defined MSR HV_X64_MSR_GUEST_IDLE. The virtual processor will
>> be woken when an interrupt arrives, regardless of whether the interrupt
>> is enabled on the virtual processor or not.
>
> Meanwhile, Windows 24H2+ calls this MSR anyway without the privilege being set.
>
> Add the infrastructure to support it on the generic QEMU side.
>
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> target/i386/cpu.c | 10 ++++++----
> target/i386/cpu.h | 2 ++
> 2 files changed, 8 insertions(+), 4 deletions(-)
>
> diff --git a/target/i386/cpu.c b/target/i386/cpu.c
> index a5f1a1a8fd..c9ed9b7580 100644
> --- a/target/i386/cpu.c
> +++ b/target/i386/cpu.c
> @@ -10474,13 +10474,15 @@ int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request)
> (((env->hflags2 & HF2_VINTR_MASK) &&
> (env->hflags2 & HF2_HIF_MASK)) ||
> (!(env->hflags2 & HF2_VINTR_MASK) &&
> - (env->eflags & IF_MASK &&
> - !(env->hflags & HF_INHIBIT_IRQ_MASK))))) {
> + ((env->eflags & IF_MASK &&
> + !(env->hflags & HF_INHIBIT_IRQ_MASK))
> + || env->hflags2 & HF2_HYPERV_HLT_MASK)))) {
> return CPU_INTERRUPT_HARD;
> } else if (env->hflags2 & HF2_VGIF_MASK) {
> if((interrupt_request & CPU_INTERRUPT_VIRQ) &&
> - (env->eflags & IF_MASK) &&
> - !(env->hflags & HF_INHIBIT_IRQ_MASK)) {
> + ((env->eflags & IF_MASK &&
> + !(env->hflags & HF_INHIBIT_IRQ_MASK))
> + || env->hflags2 & HF2_HYPERV_HLT_MASK)) {
This should be a new function, and for simplicity it can also be used in
hvf_inject_interrupts().
I'll do this when applying.
Paolo
> return CPU_INTERRUPT_VIRQ;
> }
> }
> diff --git a/target/i386/cpu.h b/target/i386/cpu.h
> index 0b539155c4..67f508dc10 100644
> --- a/target/i386/cpu.h
> +++ b/target/i386/cpu.h
> @@ -225,6 +225,7 @@ typedef enum X86Seg {
> #define HF2_NPT_SHIFT 6 /* Nested Paging enabled */
> #define HF2_IGNNE_SHIFT 7 /* Ignore CR0.NE=0 */
> #define HF2_VGIF_SHIFT 8 /* Can take VIRQ*/
> +#define HF2_HYPERV_HLT_SHIFT 9 /* Hyper-V HV_X64_MSR_GUEST_IDLE */
>
> #define HF2_GIF_MASK (1 << HF2_GIF_SHIFT)
> #define HF2_HIF_MASK (1 << HF2_HIF_SHIFT)
> @@ -235,6 +236,7 @@ typedef enum X86Seg {
> #define HF2_NPT_MASK (1 << HF2_NPT_SHIFT)
> #define HF2_IGNNE_MASK (1 << HF2_IGNNE_SHIFT)
> #define HF2_VGIF_MASK (1 << HF2_VGIF_SHIFT)
> +#define HF2_HYPERV_HLT_MASK (1 << HF2_HYPERV_HLT_SHIFT)
>
> #define CR0_PE_SHIFT 0
> #define CR0_MP_SHIFT 1
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v3 22/37] whpx: i386: add HV_X64_MSR_GUEST_IDLE when !kernel-irqchip
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (20 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 21/37] target: i386: HLT type that ignores EFLAGS.IF Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-30 13:21 ` Paolo Bonzini
2026-04-22 21:42 ` [PATCH v3 23/37] whpx: i386: some x2APIC awareness Mohamed Mediouni
` (15 subsequent siblings)
37 siblings, 1 reply; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Add support for an oddball HV_X64_MSR_GUEST_IDLE not-quite-an-HLT
that wakes the vCPU even if EFLAGS.IF is set.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 46 ++++++++++++++++++++++++++++++++++---
1 file changed, 43 insertions(+), 3 deletions(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index cafcbf8bbb..f03f5a5115 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -52,6 +52,7 @@
/* for kernel-irqchip=off */
#define HV_X64_MSR_APIC_FREQUENCY 0x40000023
#define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073
+#define HV_X64_MSR_GUEST_IDLE 0x400000f0
static bool is_modern_os = true;
@@ -1628,13 +1629,16 @@ static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid)
}
}
-static int whpx_handle_halt(CPUState *cpu)
+static int whpx_handle_halt_generic(CPUState *cpu)
{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+
int ret = 0;
bql_lock();
if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) &&
- (cpu_env(cpu)->eflags & IF_MASK)) &&
+ ((cpu_env(cpu)->eflags & IF_MASK) || env->hflags2 & HF2_HYPERV_HLT_MASK)) &&
!cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) {
cpu->exception_index = EXCP_HLT;
cpu->halted = true;
@@ -1645,6 +1649,27 @@ static int whpx_handle_halt(CPUState *cpu)
return ret;
}
+static int whpx_handle_halt(CPUState *cpu)
+{
+ int ret = 0;
+
+ ret = whpx_handle_halt_generic(cpu);
+
+ return ret;
+}
+
+static int whpx_handle_hyperv_guestidle(CPUState *cpu)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ int ret = 0;
+
+ env->hflags2 |= HF2_HYPERV_HLT_MASK;
+ ret = whpx_handle_halt_generic(cpu);
+
+ return ret;
+}
+
static void whpx_vcpu_kick_out_of_hlt(CPUState *cpu)
{
WHV_REGISTER_VALUE reg;
@@ -1848,9 +1873,10 @@ static void whpx_vcpu_process_async_events(CPUState *cpu)
}
if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) &&
- (env->eflags & IF_MASK)) ||
+ ((env->eflags & IF_MASK) || env->hflags2 & HF2_HYPERV_HLT_MASK)) ||
cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) {
cpu->halted = false;
+ env->hflags2 &= ~HF2_HYPERV_HLT_MASK;
}
if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SIPI)) {
@@ -2120,6 +2146,20 @@ int whpx_vcpu_run(CPUState *cpu)
}
}
+ /*
+ * Windows and Linux both use this MSR.
+ * Windows 11 25H2 uses it even when not advertised.
+ */
+ if (vcpu->exit_ctx.MsrAccess.MsrNumber == HV_X64_MSR_GUEST_IDLE
+ && !vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite
+ && !whpx_irqchip_in_kernel()
+ && whpx->hyperv_enlightenments_enabled) {
+ is_known_msr = 1;
+ whpx_bump_rip(cpu, &vcpu->exit_ctx);
+ ret = whpx_handle_hyperv_guestidle(cpu);
+ break;
+ }
+
/*
* Linux tries to use it anyway even when not exposed.
* Ignore the write as the VP assist page is not used.
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v3 22/37] whpx: i386: add HV_X64_MSR_GUEST_IDLE when !kernel-irqchip
2026-04-22 21:42 ` [PATCH v3 22/37] whpx: i386: add HV_X64_MSR_GUEST_IDLE when !kernel-irqchip Mohamed Mediouni
@ 2026-04-30 13:21 ` Paolo Bonzini
0 siblings, 0 replies; 45+ messages in thread
From: Paolo Bonzini @ 2026-04-30 13:21 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Roman Bolshakov,
Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan, Peter Maydell,
Zhao Liu
On 4/22/26 23:42, Mohamed Mediouni wrote:
> Add support for an oddball HV_X64_MSR_GUEST_IDLE not-quite-an-HLT
> that wakes the vCPU even if EFLAGS.IF is set.
>
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
A couple changes I am making anyway when applying.
> ---
> target/i386/whpx/whpx-all.c | 46 ++++++++++++++++++++++++++++++++++---
> 1 file changed, 43 insertions(+), 3 deletions(-)
>
> diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
> index cafcbf8bbb..f03f5a5115 100644
> --- a/target/i386/whpx/whpx-all.c
> +++ b/target/i386/whpx/whpx-all.c
> @@ -52,6 +52,7 @@
> /* for kernel-irqchip=off */
> #define HV_X64_MSR_APIC_FREQUENCY 0x40000023
> #define HV_X64_MSR_VP_ASSIST_PAGE 0x40000073
> +#define HV_X64_MSR_GUEST_IDLE 0x400000f0
>
> static bool is_modern_os = true;
>
> @@ -1628,13 +1629,16 @@ static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid)
> }
> }
>
> -static int whpx_handle_halt(CPUState *cpu)
> +static int whpx_handle_halt_generic(CPUState *cpu)
> {
> + X86CPU *x86_cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86_cpu->env;
> +
> int ret = 0;
>
> bql_lock();
> if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) &&
> - (cpu_env(cpu)->eflags & IF_MASK)) &&
> + ((cpu_env(cpu)->eflags & IF_MASK) || env->hflags2 & HF2_HYPERV_HLT_MASK)) &&
This needs parentheses on the right side.
> !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) {
> cpu->exception_index = EXCP_HLT;
> cpu->halted = true;
> @@ -1645,6 +1649,27 @@ static int whpx_handle_halt(CPUState *cpu)
> return ret;
> }
>
> +static int whpx_handle_halt(CPUState *cpu)
> +{
> + int ret = 0;
> +
> + ret = whpx_handle_halt_generic(cpu);
> +
> + return ret;
> +}
This is useless.
> +
> +static int whpx_handle_hyperv_guestidle(CPUState *cpu)
> +{
> + X86CPU *x86_cpu = X86_CPU(cpu);
> + CPUX86State *env = &x86_cpu->env;
> + int ret = 0;
> +
> + env->hflags2 |= HF2_HYPERV_HLT_MASK;
> + ret = whpx_handle_halt_generic(cpu);
> +
> + return ret;
> +}
> +
> static void whpx_vcpu_kick_out_of_hlt(CPUState *cpu)
> {
> WHV_REGISTER_VALUE reg;
> @@ -1848,9 +1873,10 @@ static void whpx_vcpu_process_async_events(CPUState *cpu)
> }
>
> if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) &&
> - (env->eflags & IF_MASK)) ||
> + ((env->eflags & IF_MASK) || env->hflags2 & HF2_HYPERV_HLT_MASK)) ||
More missing parentheses.
> cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) {
> cpu->halted = false;
> + env->hflags2 &= ~HF2_HYPERV_HLT_MASK;
> }
>
> if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SIPI)) {
> @@ -2120,6 +2146,20 @@ int whpx_vcpu_run(CPUState *cpu)
> }
> }
>
> + /*
> + * Windows and Linux both use this MSR.
> + * Windows 11 25H2 uses it even when not advertised.
> + */
> + if (vcpu->exit_ctx.MsrAccess.MsrNumber == HV_X64_MSR_GUEST_IDLE
> + && !vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite
> + && !whpx_irqchip_in_kernel()
> + && whpx->hyperv_enlightenments_enabled) {
> + is_known_msr = 1;
> + whpx_bump_rip(cpu, &vcpu->exit_ctx);
> + ret = whpx_handle_hyperv_guestidle(cpu);
> + break;
> + }
> +
> /*
> * Linux tries to use it anyway even when not exposed.
> * Ignore the write as the VP assist page is not used.
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v3 23/37] whpx: i386: some x2APIC awareness
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (21 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 22/37] whpx: i386: add HV_X64_MSR_GUEST_IDLE when !kernel-irqchip Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 24/37] whpx: i386: set WHvX64RegisterInitialApicId Mohamed Mediouni
` (14 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-apic.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/target/i386/whpx/whpx-apic.c b/target/i386/whpx/whpx-apic.c
index 65629ca45f..cc272f82a5 100644
--- a/target/i386/whpx/whpx-apic.c
+++ b/target/i386/whpx/whpx-apic.c
@@ -33,7 +33,11 @@ static void whpx_put_apic_state(APICCommonState *s,
int i;
memset(kapic, 0, sizeof(*kapic));
- kapic->fields[0x2].data = s->id << 24;
+ if (s->apicbase & MSR_IA32_APICBASE_EXTD) {
+ kapic->fields[0x2].data = s->initial_apic_id;
+ } else {
+ kapic->fields[0x2].data = s->id << 24;
+ }
kapic->fields[0x3].data = s->version | ((APIC_LVT_NB - 1) << 16);
kapic->fields[0x8].data = s->tpr;
kapic->fields[0xd].data = s->log_dest << 24;
@@ -61,7 +65,11 @@ static void whpx_get_apic_state(APICCommonState *s,
{
int i, v;
- s->id = kapic->fields[0x2].data >> 24;
+ if (s->apicbase & MSR_IA32_APICBASE_EXTD) {
+ assert(kapic->fields[0x2].data == s->initial_apic_id);
+ } else {
+ s->id = kapic->fields[0x2].data >> 24;
+ }
s->tpr = kapic->fields[0x8].data;
s->arb_id = kapic->fields[0x9].data;
s->log_dest = kapic->fields[0xd].data >> 24;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 24/37] whpx: i386: set WHvX64RegisterInitialApicId
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (22 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 23/37] whpx: i386: some x2APIC awareness Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 25/37] whpx: i386: Pause VM on fatal exception to be able to inspect state Mohamed Mediouni
` (13 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Keep Hyper-V aware of the initial APIC ID chosen.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index f03f5a5115..09ca7b5a53 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2475,6 +2475,11 @@ int whpx_init_vcpu(CPUState *cpu)
goto error;
}
+ if (!whpx_irqchip_in_kernel()) {
+ WHV_REGISTER_VALUE apic_id = {.Reg64 = x86_cpu->apic_state->initial_apic_id};
+ whpx_set_reg(cpu, WHvX64RegisterInitialApicId, apic_id);
+ }
+
/*
* vcpu's TSC frequency is either specified by user, or use the value
* provided by Hyper-V if the former is not present. In the latter case, we
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 25/37] whpx: i386: Pause VM on fatal exception to be able to inspect state
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (23 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 24/37] whpx: i386: set WHvX64RegisterInitialApicId Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 26/37] target/i386: emulate: use exception_payload for fault address Mohamed Mediouni
` (12 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 09ca7b5a53..8e54fcc403 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2387,7 +2387,7 @@ int whpx_vcpu_run(CPUState *cpu)
vcpu->exit_ctx.ExitReason);
whpx_get_registers(cpu, WHPX_LEVEL_FULL_STATE);
bql_lock();
- qemu_system_guest_panicked(cpu_get_crash_info(cpu));
+ vm_stop(RUN_STATE_PAUSED);
bql_unlock();
break;
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 26/37] target/i386: emulate: use exception_payload for fault address
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (24 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 25/37] whpx: i386: Pause VM on fatal exception to be able to inspect state Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-30 13:24 ` Paolo Bonzini
2026-04-22 21:42 ` [PATCH v3 27/37] target/i386: make xsave_buf present unconditionally Mohamed Mediouni
` (11 subsequent siblings)
37 siblings, 1 reply; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Instead of directly putting it in cr[2], put it in exception_payload.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/emulate/x86_mmu.c | 3 ++-
target/i386/whpx/whpx-all.c | 6 +++---
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/target/i386/emulate/x86_mmu.c b/target/i386/emulate/x86_mmu.c
index 007de582de..8d4371467f 100644
--- a/target/i386/emulate/x86_mmu.c
+++ b/target/i386/emulate/x86_mmu.c
@@ -277,7 +277,8 @@ static MMUTranslateResult x86_write_mem_ex(CPUState *cpu, void *data, target_ulo
translate_res = mmu_gva_to_gpa(cpu, gva, &gpa, translate_flags);
if (translate_res) {
int error_code = translate_res_to_error_code(translate_res, true, is_user(cpu));
- env->cr[2] = gva;
+ env->exception_has_payload = 1;
+ env->exception_payload = gva;
x86_emul_raise_exception(env, EXCP0E_PAGE, error_code);
return translate_res;
}
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 8e54fcc403..8d51293384 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -1903,11 +1903,11 @@ static void whpx_inject_exceptions(CPUState* cpu)
WHV_REGISTER_VALUE reg = {};
reg.ExceptionEvent.EventPending = 1;
reg.ExceptionEvent.EventType = WHvX64PendingEventException;
- reg.ExceptionEvent.DeliverErrorCode = 1;
+ reg.ExceptionEvent.DeliverErrorCode = env->has_error_code;
reg.ExceptionEvent.Vector = env->exception_nr;
reg.ExceptionEvent.ErrorCode = env->error_code;
- if (env->exception_nr == EXCP0E_PAGE) {
- reg.ExceptionEvent.ExceptionParameter = env->cr[2];
+ if (env->exception_has_payload) {
+ reg.ExceptionEvent.ExceptionParameter = env->exception_payload;
}
whpx_set_reg(cpu, WHvRegisterPendingEvent, reg);
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v3 26/37] target/i386: emulate: use exception_payload for fault address
2026-04-22 21:42 ` [PATCH v3 26/37] target/i386: emulate: use exception_payload for fault address Mohamed Mediouni
@ 2026-04-30 13:24 ` Paolo Bonzini
0 siblings, 0 replies; 45+ messages in thread
From: Paolo Bonzini @ 2026-04-30 13:24 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel, Magnus Kulke
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Roman Bolshakov,
Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan, Peter Maydell,
Zhao Liu
On 4/22/26 23:42, Mohamed Mediouni wrote:
> Instead of directly putting it in cr[2], put it in exception_payload.
Magnus, can you add on your todo list to handle exceptions from the
emulator in target/i386/mshv?
Thanks,
Paolo
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
> ---
> target/i386/emulate/x86_mmu.c | 3 ++-
> target/i386/whpx/whpx-all.c | 6 +++---
> 2 files changed, 5 insertions(+), 4 deletions(-)
>
> diff --git a/target/i386/emulate/x86_mmu.c b/target/i386/emulate/x86_mmu.c
> index 007de582de..8d4371467f 100644
> --- a/target/i386/emulate/x86_mmu.c
> +++ b/target/i386/emulate/x86_mmu.c
> @@ -277,7 +277,8 @@ static MMUTranslateResult x86_write_mem_ex(CPUState *cpu, void *data, target_ulo
> translate_res = mmu_gva_to_gpa(cpu, gva, &gpa, translate_flags);
> if (translate_res) {
> int error_code = translate_res_to_error_code(translate_res, true, is_user(cpu));
> - env->cr[2] = gva;
> + env->exception_has_payload = 1;
> + env->exception_payload = gva;
> x86_emul_raise_exception(env, EXCP0E_PAGE, error_code);
> return translate_res;
> }
> diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
> index 8e54fcc403..8d51293384 100644
> --- a/target/i386/whpx/whpx-all.c
> +++ b/target/i386/whpx/whpx-all.c
> @@ -1903,11 +1903,11 @@ static void whpx_inject_exceptions(CPUState* cpu)
> WHV_REGISTER_VALUE reg = {};
> reg.ExceptionEvent.EventPending = 1;
> reg.ExceptionEvent.EventType = WHvX64PendingEventException;
> - reg.ExceptionEvent.DeliverErrorCode = 1;
> + reg.ExceptionEvent.DeliverErrorCode = env->has_error_code;
> reg.ExceptionEvent.Vector = env->exception_nr;
> reg.ExceptionEvent.ErrorCode = env->error_code;
> - if (env->exception_nr == EXCP0E_PAGE) {
> - reg.ExceptionEvent.ExceptionParameter = env->cr[2];
> + if (env->exception_has_payload) {
> + reg.ExceptionEvent.ExceptionParameter = env->exception_payload;
> }
> whpx_set_reg(cpu, WHvRegisterPendingEvent, reg);
> }
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v3 27/37] target/i386: make xsave_buf present unconditionally
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (25 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 26/37] target/i386: emulate: use exception_payload for fault address Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 28/37] target/i386: add de/compaction to xsave_helper Mohamed Mediouni
` (10 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
This is necessary for the xsave_helper helpers to compile.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/cpu.h | 2 --
1 file changed, 2 deletions(-)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 67f508dc10..0af7bdf85a 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -2269,10 +2269,8 @@ typedef struct CPUArchState {
int64_t user_tsc_khz; /* for sanity check only */
uint64_t apic_bus_freq;
uint64_t tsc;
-#if defined(CONFIG_KVM) || defined(CONFIG_HVF)
void *xsave_buf;
uint32_t xsave_buf_len;
-#endif
#if defined(CONFIG_KVM)
struct kvm_nested_state *nested_state;
MemoryRegion *xen_vcpu_info_mr;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 28/37] target/i386: add de/compaction to xsave_helper
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (26 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 27/37] target/i386: make xsave_buf present unconditionally Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-30 13:31 ` Paolo Bonzini
2026-04-22 21:42 ` [PATCH v3 29/37] whpx: xsave support Mohamed Mediouni
` (9 subsequent siblings)
37 siblings, 1 reply; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini, Magnus Kulke
From: Magnus Kulke <magnuskulke@linux.microsoft.com>
HyperV use XSAVES which stores extended state in compacted format in
which components are packed contiguously, while QEMU's internal XSAVE
representation use the standard format in which each component is places
at a fixed offset. Hence for this purpose we add two conversion fn's to
the xsave helper to roundtrip XSAVE state in a migration.
- decompact_xsave_area(): converts compacted format to standard.
XSTATE_BV is masked to host XCR0 since IA32_XSS is managed
by the hypervisor.
- compact_xsave_area(): converts standard format back to compacted
format. XCOMP_BV is set from the host's CPUID 0xD.0 rather than the
guest's XCR0, as this is what the hypervisor expects.
Both functions use the host's CPUID leaf 0xD subleaves to determine component
sizes, offsets, and alignment requirements.
There are situations when the host advertises features that we want to
disable for the guest, e.g. AMX TILE. In this case we cannot rely on the
host's xcr0, but instead we use the feature mask that has been generated
in as part of the CPU realization process (x86_cpu_expand_features).
Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
[Fixup: made xsave_offset a size_t to fix macOS and OpenBSD builds]
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/cpu.h | 2 +
target/i386/xsave_helper.c | 256 +++++++++++++++++++++++++++++++++++++
2 files changed, 258 insertions(+)
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index 0af7bdf85a..80cdc1cb2a 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -3023,6 +3023,8 @@ void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen);
void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen);
uint32_t xsave_area_size(uint64_t mask, bool compacted);
void x86_update_hflags(CPUX86State* env);
+int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env);
+int compact_xsave_area(CPUX86State *env, void *buf, size_t buflen);
static inline bool hyperv_feat_enabled(X86CPU *cpu, int feat)
{
diff --git a/target/i386/xsave_helper.c b/target/i386/xsave_helper.c
index bab2258732..625bae103a 100644
--- a/target/i386/xsave_helper.c
+++ b/target/i386/xsave_helper.c
@@ -3,6 +3,7 @@
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "cpu.h"
@@ -293,3 +294,258 @@ void x86_cpu_xrstor_all_areas(X86CPU *cpu, const void *buf, uint32_t buflen)
}
#endif
}
+
+#define XSTATE_BV_IN_HDR offsetof(X86XSaveHeader, xstate_bv)
+#define XCOMP_BV_IN_HDR offsetof(X86XSaveHeader, xcomp_bvo)
+
+typedef struct X86XSaveAreaView {
+ /* 512 bytes */
+ X86LegacyXSaveArea legacy;
+ /* 64 bytes */
+ X86XSaveHeader header;
+ /* ...followed by individual xsave areas */
+} X86XSaveAreaView;
+
+#define XSAVE_XSTATE_BV_OFFSET offsetof(X86XSaveAreaView, header.xstate_bv)
+#define XSAVE_XCOMP_BV_OFFSET offsetof(X86XSaveAreaView, header.xcomp_bv)
+#define XSAVE_EXT_OFFSET (sizeof(X86LegacyXSaveArea) + \
+ sizeof(X86XSaveHeader))
+
+/**
+ * decompact_xsave_area - Convert compacted XSAVE format to standard format
+ * @buf: Source buffer containing compacted XSAVE data
+ * @buflen: Size of source buffer
+ * @env: CPU state where the standard format buffer will be written to
+ *
+ * Accelerator backends like MSHV might return XSAVE state in compacted format
+ * (XSAVEC). The state components have to be packed contiguously without gaps.
+ * The XSAVE qemu buffers are in standard format where each component has a
+ * fixed offset.
+ *
+ * Returns: 0 on success, negative errno on failure
+ */
+int decompact_xsave_area(const void *buf, size_t buflen, CPUX86State *env)
+{
+ uint64_t compacted_xstate_bv, compacted_xcomp_bv, compacted_layout_bv;
+ size_t xsave_offset;
+ uint64_t *xcomp_bv;
+ size_t i;
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t size, dst_off;
+ bool align64;
+ uint64_t guest_xcr0, *xstate_bv;
+
+ compacted_xstate_bv = *(uint64_t *)(buf + XSAVE_XSTATE_BV_OFFSET);
+ compacted_xcomp_bv = *(uint64_t *)(buf + XSAVE_XCOMP_BV_OFFSET);
+
+ /* This function only handles compacted format (bit 63 set) */
+ assert((compacted_xcomp_bv >> 63) & 1);
+
+ /* Low bits of XCOMP_BV describe which components are in the layout */
+ compacted_layout_bv = compacted_xcomp_bv & ~(1ULL << 63);
+
+ /* Zero out buffer, then copy legacy region (FP + SSE) and header as-is */
+ memset(env->xsave_buf, 0, env->xsave_buf_len);
+ memcpy(env->xsave_buf, buf, XSAVE_EXT_OFFSET);
+
+ /*
+ * We mask XSTATE_BV with the guest's supported XCR0 because:
+ * 1. Supervisor state (IA32_XSS) is hypervisor-managed, we don't use
+ * this state for migration.
+ * 2. Features disabled at partition creation (e.g. AMX) must be excluded
+ */
+ guest_xcr0 = ((uint64_t)env->features[FEAT_XSAVE_XCR0_HI] << 32) |
+ env->features[FEAT_XSAVE_XCR0_LO];
+ xstate_bv = (uint64_t *)(env->xsave_buf + XSAVE_XSTATE_BV_OFFSET);
+ *xstate_bv &= guest_xcr0;
+
+ /* Clear bit 63 - output is standard format, not compacted */
+ xcomp_bv = (uint64_t *)(env->xsave_buf + XSAVE_XCOMP_BV_OFFSET);
+ *xcomp_bv = *xcomp_bv & ~(1ULL << 63);
+
+ /*
+ * Process each extended state component in the compacted layout.
+ * Components 0 and 1 (FP and SSE) are in the legacy region, so we
+ * start at component 2. For each component:
+ * - Calculate its offset in the compacted source (contiguous layout)
+ * - Get its fixed offset in the standard destination from CPUID
+ * - Copy if the component has non-init state (bit set in XSTATE_BV)
+ */
+ xsave_offset = XSAVE_EXT_OFFSET;
+ for (i = 2; i < 63; i++) {
+ if (((compacted_layout_bv >> i) & 1) == 0) {
+ continue;
+ }
+
+ /* Query guest CPUID for this component's size and standard offset */
+ cpu_x86_cpuid(env, 0xD, i, &eax, &ebx, &ecx, &edx);
+
+ size = eax;
+ dst_off = ebx;
+ align64 = (ecx & (1u << 1)) != 0;
+
+ /* Component is in the layout but unknown to the guest CPUID model */
+ if (size == 0) {
+ /*
+ * The hypervisor might expose a component that has no
+ * representation in the guest CPUID model. We query the host to
+ * retrieve the size of the component, so we can skip over it.
+ */
+ host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx);
+ size = eax;
+ align64 = (ecx & (1u << 1)) != 0;
+ if (size == 0) {
+ error_report("xsave component %zu: size unknown to both "
+ "guest and host CPUID", i);
+ return -EINVAL;
+ }
+
+ if (align64) {
+ xsave_offset = QEMU_ALIGN_UP(xsave_offset, 64);
+ }
+
+ if (xsave_offset + size > buflen) {
+ error_report("xsave component %zu overruns source buffer: "
+ "offset=%zu size=%u buflen=%zu",
+ i, xsave_offset, size, buflen);
+ return -E2BIG;
+ }
+
+ xsave_offset += size;
+ continue;
+ }
+
+ if (align64) {
+ xsave_offset = QEMU_ALIGN_UP(xsave_offset, 64);
+ }
+
+ if ((xsave_offset + size) > buflen) {
+ error_report("xsave component %zu overruns source buffer: "
+ "offset=%zu size=%u buflen=%zu",
+ i, xsave_offset, size, buflen);
+ return -E2BIG;
+ }
+
+ if ((dst_off + size) > env->xsave_buf_len) {
+ error_report("xsave component %zu overruns destination buffer: "
+ "offset=%u size=%u buflen=%zu",
+ i, dst_off, size, (size_t)env->xsave_buf_len);
+ return -E2BIG;
+ }
+
+ /* Copy components marked present in XSTATE_BV to guest model */
+ if (((compacted_xstate_bv >> i) & 1) != 0) {
+ memcpy(env->xsave_buf + dst_off, buf + xsave_offset, size);
+ }
+
+ xsave_offset += size;
+ }
+
+ return 0;
+}
+
+/**
+ * compact_xsave_area - Convert standard XSAVE format to compacted format
+ * @env: CPU state containing the standard format XSAVE buffer
+ * @buf: Destination buffer for compacted XSAVE data (to send to hypervisor)
+ * @buflen: Size of destination buffer
+ *
+ * Accelerator backends like MSHV might expect XSAVE state in compacted format
+ * (XSAVEC). The state components are packed contiguously without gaps.
+ * The XSAVE qemu buffers are in standard format where each component has a
+ * fixed offset.
+ *
+ * This function converts from standard to compacted format, it accepts a
+ * pre-allocated destination buffer of sufficient size, it is the
+ * responsibility of the caller to ensure the buffer is big enough.
+ *
+ * Returns: total size of compacted XSAVE data written to @buf
+ */
+int compact_xsave_area(CPUX86State *env, void *buf, size_t buflen)
+{
+ uint64_t *xcomp_bv;
+ size_t i;
+ uint32_t eax, ebx, ecx, edx;
+ uint32_t size, src_off;
+ bool align64;
+ size_t compact_offset;
+ uint64_t host_xcr0_mask, guest_xcr0;
+
+ /* Zero out buffer, then copy legacy region (FP + SSE) and header as-is */
+ memset(buf, 0, buflen);
+ memcpy(buf, env->xsave_buf, XSAVE_EXT_OFFSET);
+
+ /*
+ * Set XCOMP_BV to indicate compacted format (bit 63) and which
+ * components are in the layout.
+ *
+ * We must explicitly set XCOMP_BV because x86_cpu_xsave_all_areas()
+ * produces standard format with XCOMP_BV=0 (buffer is zeroed and only
+ * XSTATE_BV is set in the header).
+ *
+ * XCOMP_BV must reflect the partition's XSAVE capability, not the
+ * guest's current XCR0 (env->xcr0). These differ b/c:
+ * - A guest's XCR0 is what the guest OS has enabled via XSETBV
+ * - The partition's XCR0 mask is the hypervisor's save/restore capability
+ *
+ * The hypervisor uses XSAVES which saves based on its capability, so the
+ * XCOMP_BV value in the buffer we send back must match that capability.
+ *
+ * We intersect the host XCR0 with the guest's supported XCR0 features
+ * (FEAT_XSAVE_XCR0_*) so that features disabled at partition creation
+ * (e.g. AMX) are excluded from the compacted layout.
+ */
+ host_cpuid(0xD, 0, &eax, &ebx, &ecx, &edx);
+ host_xcr0_mask = ((uint64_t)edx << 32) | eax;
+ guest_xcr0 = ((uint64_t)env->features[FEAT_XSAVE_XCR0_HI] << 32) |
+ env->features[FEAT_XSAVE_XCR0_LO];
+ host_xcr0_mask &= guest_xcr0;
+ xcomp_bv = buf + XSAVE_XCOMP_BV_OFFSET;
+ *xcomp_bv = host_xcr0_mask | (1ULL << 63);
+
+ /*
+ * Process each extended state component in the host's XCR0.
+ * The compacted layout must match XCOMP_BV (host capability).
+ *
+ * For each component:
+ * - Get its size and standard offset from host CPUID
+ * - Apply 64-byte alignment if required
+ * - Copy data only if guest has this component (bit set in env->xcr0)
+ * - Always advance offset to maintain correct layout
+ */
+ compact_offset = XSAVE_EXT_OFFSET;
+ for (i = 2; i < 63; i++) {
+ if (!((host_xcr0_mask >> i) & 1)) {
+ continue;
+ }
+
+ /* Query host CPUID for this component's size and standard offset */
+ host_cpuid(0xD, i, &eax, &ebx, &ecx, &edx);
+ size = eax;
+ src_off = ebx;
+ align64 = (ecx >> 1) & 1;
+
+ if (size == 0) {
+ /* Component in host xcr0 but unknown - shouldn't happen */
+ continue;
+ }
+
+ /* Apply 64-byte alignment if required by this component */
+ if (align64) {
+ compact_offset = QEMU_ALIGN_UP(compact_offset, 64);
+ }
+
+ /*
+ * Only copy data if guest has this component enabled in XCR0.
+ * Otherwise the component remains zeroed (init state), but we
+ * still advance the offset to maintain the correct layout.
+ */
+ if ((env->xcr0 >> i) & 1) {
+ memcpy(buf + compact_offset, env->xsave_buf + src_off, size);
+ }
+
+ compact_offset += size;
+ }
+
+ return compact_offset;
+}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v3 28/37] target/i386: add de/compaction to xsave_helper
2026-04-22 21:42 ` [PATCH v3 28/37] target/i386: add de/compaction to xsave_helper Mohamed Mediouni
@ 2026-04-30 13:31 ` Paolo Bonzini
0 siblings, 0 replies; 45+ messages in thread
From: Paolo Bonzini @ 2026-04-30 13:31 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Roman Bolshakov,
Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan, Peter Maydell,
Zhao Liu, Magnus Kulke
On 4/22/26 23:42, Mohamed Mediouni wrote:
> From: Magnus Kulke <magnuskulke@linux.microsoft.com>
>
> HyperV use XSAVES which stores extended state in compacted format in
> which components are packed contiguously, while QEMU's internal XSAVE
> representation use the standard format in which each component is places
> at a fixed offset. Hence for this purpose we add two conversion fn's to
> the xsave helper to roundtrip XSAVE state in a migration.
>
> - decompact_xsave_area(): converts compacted format to standard.
> XSTATE_BV is masked to host XCR0 since IA32_XSS is managed
> by the hypervisor.
>
> - compact_xsave_area(): converts standard format back to compacted
> format. XCOMP_BV is set from the host's CPUID 0xD.0 rather than the
> guest's XCR0, as this is what the hypervisor expects.
>
> Both functions use the host's CPUID leaf 0xD subleaves to determine component
> sizes, offsets, and alignment requirements.
This means we have to move xsave_helper.c under the individual hypervisors:
diff --git a/target/i386/meson.build b/target/i386/meson.build
index d385eafdf7e..14b1d2977d5 100644
--- a/target/i386/meson.build
+++ b/target/i386/meson.build
@@ -3,14 +3,13 @@ i386_ss.add(files(
'cpu.c',
'gdbstub.c',
'helper.c',
- 'xsave_helper.c',
'cpu-dump.c',
))
i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c', 'confidential-guest.c'))
# x86 cpu type
-i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c'))
-i386_ss.add(when: 'CONFIG_HVF', if_true: files('host-cpu.c'))
+i386_ss.add(when: 'CONFIG_KVM', if_true: files('xsave_helper.c', 'host-cpu.c'))
+i386_ss.add(when: 'CONFIG_HVF', if_true: files('xsave_helper.c', 'host-cpu.c'))
i386_ss.add(when: 'CONFIG_WHPX', if_true: files('host-cpu.c'))
i386_ss.add(when: 'CONFIG_NVMM', if_true: files('host-cpu.c'))
i386_ss.add(when: 'CONFIG_MSHV', if_true: files('host-cpu.c'))
Otherwise, TCG would not compile.
Paolo
^ permalink raw reply related [flat|nested] 45+ messages in thread
* [PATCH v3 29/37] whpx: xsave support
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (27 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 28/37] target/i386: add de/compaction to xsave_helper Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 30/37] whpx: i386: set APIC ID only when APIC present Mohamed Mediouni
` (8 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
include/system/whpx-internal.h | 16 ++
target/i386/whpx/whpx-all.c | 379 ++++++++++++++++++++++++---------
2 files changed, 292 insertions(+), 103 deletions(-)
diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h
index 86639627b3..0aae83bd7c 100644
--- a/include/system/whpx-internal.h
+++ b/include/system/whpx-internal.h
@@ -99,6 +99,22 @@ void whpx_apic_get(APICCommonState *s);
UINT32 StateSize)) \
X(HRESULT, WHvResetPartition, \
(WHV_PARTITION_HANDLE Partition)) \
+ X(HRESULT, WHvGetVirtualProcessorXsaveState, \
+ (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \
+ PVOID Buffer, \
+ UINT32 BufferSizeInBytes, UINT32 *BytesWritten)) \
+ X(HRESULT, WHvSetVirtualProcessorXsaveState, \
+ (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \
+ PVOID Buffer, \
+ UINT32 BufferSizeInBytes)) \
+ X(HRESULT, WHvGetVirtualProcessorState, \
+ (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \
+ WHV_VIRTUAL_PROCESSOR_STATE_TYPE StateType, PVOID Buffer, \
+ UINT32 BufferSizeInBytes, UINT32 *BytesWritten)) \
+ X(HRESULT, WHvSetVirtualProcessorState, \
+ (WHV_PARTITION_HANDLE Partition, UINT32 VpIndex, \
+ WHV_VIRTUAL_PROCESSOR_STATE_TYPE StateType, PVOID Buffer, \
+ UINT32 BufferSizeInBytes)) \
LIST_WINHVPLATFORM_FUNCTIONS_SUPPLEMENTAL_ARCH(X)
#define WHP_DEFINE_TYPE(return_type, function_name, signature) \
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 8d51293384..3562decfaf 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -10,6 +10,7 @@
#include "qemu/osdep.h"
#include "cpu.h"
+#include "qemu/typedefs.h"
#include "system/address-spaces.h"
#include "system/ioport.h"
#include "gdbstub/helpers.h"
@@ -20,6 +21,7 @@
#include "system/cpus.h"
#include "system/runstate.h"
#include "qemu/main-loop.h"
+#include "qemu/memalign.h"
#include "hw/core/boards.h"
#include "hw/intc/ioapic.h"
#include "hw/intc/i8259.h"
@@ -108,34 +110,6 @@ static const WHV_REGISTER_NAME whpx_register_names[] = {
* 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 */
WHvX64RegisterEfer,
#ifdef TARGET_X86_64
@@ -182,6 +156,36 @@ static const WHV_REGISTER_NAME whpx_register_names_for_vmexit[] = {
WHvX64RegisterR15,
};
+static const WHV_REGISTER_NAME whpx_register_names_legacy_fp[] = {
+ /* X64 Floating Point and Vector Registers (non-xsave) */
+ 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,
+};
+
struct whpx_register_set {
WHV_REGISTER_VALUE values[RTL_NUMBER_OF(whpx_register_names)];
};
@@ -392,6 +396,123 @@ static int whpx_set_tsc(CPUState *cpu)
return 0;
}
+static bool whpx_is_xsave_enabled(CPUState *cpu)
+{
+ CPUX86State *env = &X86_CPU(cpu)->env;
+ return env->cr[4] & CR4_OSXSAVE_MASK;
+}
+
+static size_t whpx_get_xsave_max_len(void)
+{
+ return whpx_get_supported_cpuid(0xd, 0, R_ECX);
+}
+
+static int whpx_set_xsave_state(const CPUState *cpu)
+{
+ struct whpx_state *whpx = &whpx_global;
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ HRESULT hr;
+ void *xsavec_buf;
+ size_t page = qemu_real_host_page_size();
+ size_t xsavec_buf_len;
+
+ /* allocate and populate compacted buffer */
+ xsavec_buf_len = whpx_get_xsave_max_len();
+ xsavec_buf = qemu_memalign(page, xsavec_buf_len);
+
+ /* save registers to standard format buffer */
+ x86_cpu_xsave_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
+
+ /* store compacted version of xsave area in xsavec_buf */
+ compact_xsave_area(env, xsavec_buf, xsavec_buf_len);
+
+ if (!whpx_is_legacy_os()) {
+ hr = whp_dispatch.WHvSetVirtualProcessorState(
+ whpx->partition, cpu->cpu_index,
+ WHvVirtualProcessorStateTypeXsaveState,
+ xsavec_buf,
+ xsavec_buf_len);
+ } else {
+ hr = whp_dispatch.WHvSetVirtualProcessorXsaveState(
+ whpx->partition, cpu->cpu_index,
+ xsavec_buf,
+ xsavec_buf_len);
+ }
+
+ qemu_vfree(xsavec_buf);
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
+ hr);
+ }
+
+ return 0;
+}
+
+static void whpx_set_legacy_fp_registers(CPUState *cpu, WHPXStateLevel level)
+{
+ struct whpx_state *whpx = &whpx_global;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ struct whpx_register_set vcxt;
+ HRESULT hr;
+ int idx = 0;
+ int i;
+ int idx_next;
+
+ assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
+
+ /* 16 XMM registers */
+ assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmm0);
+ idx_next = idx + 16;
+ for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
+ vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);
+ vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);
+ }
+ idx = idx_next;
+
+ /* 8 FP registers */
+ assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpMmx0);
+ for (i = 0; i < 8; i += 1, idx += 1) {
+ vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);
+ /* vcxt.values[idx].Fp.AsUINT128.High64 =
+ env->fpregs[i].mmx.MMX_Q(1);
+ */
+ }
+
+ /* FP control status register */
+ assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpControlStatus);
+ vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;
+ vcxt.values[idx].FpControlStatus.FpStatus =
+ (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
+ vcxt.values[idx].FpControlStatus.FpTag = 0;
+ for (i = 0; i < 8; ++i) {
+ vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;
+ }
+ vcxt.values[idx].FpControlStatus.Reserved = 0;
+ vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;
+ vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;
+ idx += 1;
+
+ /* XMM control status register */
+ assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmmControlStatus);
+ vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;
+ vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;
+ vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;
+ idx += 1;
+
+ hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
+ whpx->partition, cpu->cpu_index,
+ whpx_register_names_legacy_fp,
+ idx,
+ &vcxt.values[0]);
+
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to set virtual processor context, hr=%08lx",
+ hr);
+ }
+}
+
void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
{
struct whpx_state *whpx = &whpx_global;
@@ -491,45 +612,11 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
*/
whpx_set_xcrs(cpu);
- /* 16 XMM registers */
- assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
- idx_next = idx + 16;
- for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
- vcxt.values[idx].Reg128.Low64 = env->xmm_regs[i].ZMM_Q(0);
- vcxt.values[idx].Reg128.High64 = env->xmm_regs[i].ZMM_Q(1);
- }
- idx = idx_next;
-
- /* 8 FP registers */
- assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
- for (i = 0; i < 8; i += 1, idx += 1) {
- vcxt.values[idx].Fp.AsUINT128.Low64 = env->fpregs[i].mmx.MMX_Q(0);
- /* vcxt.values[idx].Fp.AsUINT128.High64 =
- env->fpregs[i].mmx.MMX_Q(1);
- */
- }
-
- /* FP control status register */
- assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
- vcxt.values[idx].FpControlStatus.FpControl = env->fpuc;
- vcxt.values[idx].FpControlStatus.FpStatus =
- (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
- vcxt.values[idx].FpControlStatus.FpTag = 0;
- for (i = 0; i < 8; ++i) {
- vcxt.values[idx].FpControlStatus.FpTag |= (!env->fptags[i]) << i;
+ if (whpx_is_xsave_enabled(cpu)) {
+ whpx_set_xsave_state(cpu);
+ } else {
+ whpx_set_legacy_fp_registers(cpu, level);
}
- vcxt.values[idx].FpControlStatus.Reserved = 0;
- vcxt.values[idx].FpControlStatus.LastFpOp = env->fpop;
- vcxt.values[idx].FpControlStatus.LastFpRip = env->fpip;
- idx += 1;
-
- /* XMM control status register */
- assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
- vcxt.values[idx].XmmControlStatus.LastFpRdp = 0;
- vcxt.values[idx].XmmControlStatus.XmmStatusControl = env->mxcsr;
- vcxt.values[idx].XmmControlStatus.XmmStatusControlMask = 0x0000ffff;
- idx += 1;
-
/* MSRs */
assert(whpx_register_names[idx] == WHvX64RegisterEfer);
vcxt.values[idx++].Reg64 = env->efer;
@@ -662,6 +749,110 @@ static void whpx_get_registers_for_vmexit(CPUState *cpu, WHPXStateLevel level)
x86_update_hflags(env);
}
+static void whpx_get_legacy_fp_registers(CPUState *cpu, WHPXStateLevel level)
+{
+ struct whpx_state *whpx = &whpx_global;
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ struct whpx_register_set vcxt;
+ HRESULT hr;
+ int i;
+ int idx;
+ int idx_next;
+
+ assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
+
+ hr = whp_dispatch.WHvGetVirtualProcessorRegisters(
+ whpx->partition, cpu->cpu_index,
+ whpx_register_names_legacy_fp,
+ RTL_NUMBER_OF(whpx_register_names_legacy_fp),
+ &vcxt.values[0]);
+
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to get virtual processor context, hr=%08lx",
+ hr);
+ }
+
+ idx = 0;
+ /* 16 XMM registers */
+ assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmm0);
+ idx_next = idx + 16;
+ for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
+ env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;
+ env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;
+ }
+ idx = idx_next;
+
+ /* 8 FP registers */
+ assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpMmx0);
+ for (i = 0; i < 8; i += 1, idx += 1) {
+ env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;
+ /* env->fpregs[i].mmx.MMX_Q(1) =
+ vcxt.values[idx].Fp.AsUINT128.High64;
+ */
+ }
+
+ /* FP control status register */
+ assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterFpControlStatus);
+ env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;
+ env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;
+ env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;
+ for (i = 0; i < 8; ++i) {
+ env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);
+ }
+ env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;
+ env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;
+ idx += 1;
+
+ /* XMM control status register */
+ assert(whpx_register_names_legacy_fp[idx] == WHvX64RegisterXmmControlStatus);
+ env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
+ idx += 1;
+}
+
+static int whpx_get_xsave_state(CPUState *cpu)
+{
+ struct whpx_state *whpx = &whpx_global;
+ X86CPU *x86cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86cpu->env;
+ int ret;
+ HRESULT hr;
+ void *xsavec_buf;
+ const size_t page = qemu_real_host_page_size();
+ size_t xsavec_buf_len = whpx_get_xsave_max_len();
+ UINT32 bytes_written;
+
+ xsavec_buf = qemu_memalign(page, xsavec_buf_len);
+ memset(xsavec_buf, 0, xsavec_buf_len);
+
+ if (!whpx_is_legacy_os()) {
+ hr = whp_dispatch.WHvGetVirtualProcessorState(
+ whpx->partition, cpu->cpu_index,
+ WHvVirtualProcessorStateTypeXsaveState,
+ xsavec_buf,
+ xsavec_buf_len, &bytes_written);
+ } else {
+ hr = whp_dispatch.WHvGetVirtualProcessorXsaveState(
+ whpx->partition, cpu->cpu_index,
+ xsavec_buf,
+ xsavec_buf_len, &bytes_written);
+ }
+ if (FAILED(hr) || bytes_written == 0) {
+ error_report("failed to get xsave state: %s", strerror(errno));
+ return -errno;
+ }
+
+ ret = decompact_xsave_area(xsavec_buf, xsavec_buf_len, env);
+ qemu_vfree(xsavec_buf);
+ if (ret < 0) {
+ error_report("failed to decompact xsave area");
+ return ret;
+ }
+ x86_cpu_xrstor_all_areas(x86cpu, env->xsave_buf, env->xsave_buf_len);
+
+ return 0;
+}
+
void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
{
struct whpx_state *whpx = &whpx_global;
@@ -758,40 +949,11 @@ void whpx_get_registers(CPUState *cpu, WHPXStateLevel level)
*/
whpx_get_xcrs(cpu);
- /* 16 XMM registers */
- assert(whpx_register_names[idx] == WHvX64RegisterXmm0);
- idx_next = idx + 16;
- for (i = 0; i < sizeof(env->xmm_regs) / sizeof(ZMMReg); i += 1, idx += 1) {
- env->xmm_regs[i].ZMM_Q(0) = vcxt.values[idx].Reg128.Low64;
- env->xmm_regs[i].ZMM_Q(1) = vcxt.values[idx].Reg128.High64;
- }
- idx = idx_next;
-
- /* 8 FP registers */
- assert(whpx_register_names[idx] == WHvX64RegisterFpMmx0);
- for (i = 0; i < 8; i += 1, idx += 1) {
- env->fpregs[i].mmx.MMX_Q(0) = vcxt.values[idx].Fp.AsUINT128.Low64;
- /* env->fpregs[i].mmx.MMX_Q(1) =
- vcxt.values[idx].Fp.AsUINT128.High64;
- */
- }
-
- /* FP control status register */
- assert(whpx_register_names[idx] == WHvX64RegisterFpControlStatus);
- env->fpuc = vcxt.values[idx].FpControlStatus.FpControl;
- env->fpstt = (vcxt.values[idx].FpControlStatus.FpStatus >> 11) & 0x7;
- env->fpus = vcxt.values[idx].FpControlStatus.FpStatus & ~0x3800;
- for (i = 0; i < 8; ++i) {
- env->fptags[i] = !((vcxt.values[idx].FpControlStatus.FpTag >> i) & 1);
+ if (whpx_is_xsave_enabled(cpu)) {
+ whpx_get_xsave_state(cpu);
+ } else {
+ whpx_get_legacy_fp_registers(cpu, level);
}
- env->fpop = vcxt.values[idx].FpControlStatus.LastFpOp;
- env->fpip = vcxt.values[idx].FpControlStatus.LastFpRip;
- idx += 1;
-
- /* XMM control status register */
- assert(whpx_register_names[idx] == WHvX64RegisterXmmControlStatus);
- env->mxcsr = vcxt.values[idx].XmmControlStatus.XmmStatusControl;
- idx += 1;
/* MSRs */
assert(whpx_register_names[idx] == WHvX64RegisterEfer);
@@ -1586,6 +1748,9 @@ void whpx_arch_destroy_vcpu(CPUState *cpu)
X86CPU *x86cpu = X86_CPU(cpu);
CPUX86State *env = &x86cpu->env;
g_free(env->emu_mmio_buf);
+ qemu_vfree(env->xsave_buf);
+ env->xsave_buf = NULL;
+ env->xsave_buf_len = 0;
}
/* Returns the address of the next instruction that is about to be executed. */
@@ -2446,6 +2611,9 @@ int whpx_init_vcpu(CPUState *cpu)
Error *local_error = NULL;
X86CPU *x86_cpu = X86_CPU(cpu);
CPUX86State *env = &x86_cpu->env;
+ X86XSaveHeader *header;
+ size_t page_size = qemu_real_host_page_size();
+ size_t xsave_len;
UINT64 freq = 0;
int ret;
@@ -2522,6 +2690,15 @@ int whpx_init_vcpu(CPUState *cpu)
qemu_add_vm_change_state_handler(whpx_cpu_update_state, env);
env->emu_mmio_buf = g_new(char, 4096);
+ /* Initialize XSAVE buffer page-aligned */
+ xsave_len = whpx_get_xsave_max_len();
+ env->xsave_buf = qemu_memalign(page_size, xsave_len);
+ env->xsave_buf_len = xsave_len;
+ memset(env->xsave_buf, 0, env->xsave_buf_len);
+
+ /* we need to set the compacted format bit in xsave header for Hyper-V */
+ header = (X86XSaveHeader *)(env->xsave_buf + sizeof(X86LegacyXSaveArea));
+ header->xcomp_bv = header->xstate_bv | (1ULL << 63);
return 0;
@@ -2722,10 +2899,6 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
error_report("WHPX: Failed to query XSAVE capability, hr=%08lx", hr);
}
- if (!whpx_has_xsave()) {
- printf("WHPX: Partition is not XSAVE capable\n");
- }
-
memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
prop.ProcessorCount = ms->smp.cpus;
hr = whp_dispatch.WHvSetPartitionProperty(
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 30/37] whpx: i386: set APIC ID only when APIC present
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (28 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 29/37] whpx: xsave support Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 31/37] whpx: i386: update migration blocker message Mohamed Mediouni
` (7 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
-M isapc doesn't have an APIC
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 3562decfaf..53ff0d3f7c 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2643,7 +2643,7 @@ int whpx_init_vcpu(CPUState *cpu)
goto error;
}
- if (!whpx_irqchip_in_kernel()) {
+ if (!whpx_irqchip_in_kernel() && x86_cpu->apic_state != NULL) {
WHV_REGISTER_VALUE apic_id = {.Reg64 = x86_cpu->apic_state->initial_apic_id};
whpx_set_reg(cpu, WHvX64RegisterInitialApicId, apic_id);
}
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 31/37] whpx: i386: update migration blocker message
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (29 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 30/37] whpx: i386: set APIC ID only when APIC present Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 32/37] whpx: i386: don't increment eip on MSR access raising GPF Mohamed Mediouni
` (6 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Quite a part of it is from older times...
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 53ff0d3f7c..6845f96ea6 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2622,8 +2622,8 @@ int whpx_init_vcpu(CPUState *cpu)
*/
if (whpx_migration_blocker == NULL) {
error_setg(&whpx_migration_blocker,
- "State blocked due to non-migratable CPUID feature support,"
- "dirty memory tracking support, and XSAVE/XRSTOR support");
+ "State blocked due to missing dirty memory tracking support,"
+ "And some system register/state save-restore ");
if (migrate_add_blocker(&whpx_migration_blocker, &local_error) < 0) {
error_report_err(local_error);
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 32/37] whpx: i386: don't increment eip on MSR access raising GPF
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (30 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 31/37] whpx: i386: update migration blocker message Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 33/37] target/i386: emulate, hvf: rdmsr/wrmsr GPF handling Mohamed Mediouni
` (5 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/whpx/whpx-all.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 6845f96ea6..830d8acd2b 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2240,6 +2240,7 @@ int whpx_vcpu_run(CPUState *cpu)
WHV_REGISTER_NAME reg_names[3];
UINT32 reg_count;
bool is_known_msr = 0;
+ bool raises_gpf = false;
uint64_t val;
if (vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
@@ -2272,6 +2273,7 @@ int whpx_vcpu_run(CPUState *cpu)
is_known_msr = 1;
if (val & MSR_IA32_APICBASE_RESERVED) {
x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ raises_gpf = true;
}
if (!vcpu->exit_ctx.MsrAccess.AccessInfo.IsWrite) {
/* Read path unreachable on Hyper-V */
@@ -2281,6 +2283,7 @@ int whpx_vcpu_run(CPUState *cpu)
int msr_ret = cpu_set_apic_base(X86_CPU(cpu)->apic_state, val);
if (msr_ret < 0) {
x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ raises_gpf = true;
} else {
whpx_set_reg(cpu, WHvX64RegisterApicBase, reg);
}
@@ -2300,6 +2303,7 @@ int whpx_vcpu_run(CPUState *cpu)
reg_values[1].Reg64 = val;
if (msr_ret < 0) {
x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ raises_gpf = true;
}
} else {
bql_lock();
@@ -2307,6 +2311,7 @@ int whpx_vcpu_run(CPUState *cpu)
bql_unlock();
if (msr_ret < 0) {
x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ raises_gpf = true;
}
}
}
@@ -2356,6 +2361,13 @@ int whpx_vcpu_run(CPUState *cpu)
if (!is_known_msr && !whpx->ignore_unknown_msr) {
x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ raises_gpf = true;
+ }
+
+ /* When a GPF is raised, do not change Rip. */
+ if (raises_gpf) {
+ reg_values[0].Reg64 =
+ vcpu->exit_ctx.VpContext.Rip;
}
hr = whp_dispatch.WHvSetVirtualProcessorRegisters(
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 33/37] target/i386: emulate, hvf: rdmsr/wrmsr GPF handling
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (31 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 32/37] whpx: i386: don't increment eip on MSR access raising GPF Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 34/37] whpx: i386: add feature to intercept #GP MSR accesses Mohamed Mediouni
` (4 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
In that case, the instruction pointer mustn't be incremented.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
target/i386/emulate/x86_emu.c | 10 ++++++----
target/i386/emulate/x86_emu.h | 4 ++--
target/i386/hvf/hvf-i386.h | 4 ++--
target/i386/hvf/hvf.c | 9 +++++++--
4 files changed, 17 insertions(+), 10 deletions(-)
diff --git a/target/i386/emulate/x86_emu.c b/target/i386/emulate/x86_emu.c
index c2da1a133f..c6ea854290 100644
--- a/target/i386/emulate/x86_emu.c
+++ b/target/i386/emulate/x86_emu.c
@@ -792,15 +792,17 @@ void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_c
static bool exec_rdmsr(CPUX86State *env, struct x86_decode *decode)
{
- emul_ops->simulate_rdmsr(env_cpu(env));
- env->eip += decode->len;
+ if (!emul_ops->simulate_rdmsr(env_cpu(env))) {
+ env->eip += decode->len;
+ }
return 0;
}
static bool exec_wrmsr(CPUX86State *env, struct x86_decode *decode)
{
- emul_ops->simulate_wrmsr(env_cpu(env));
- env->eip += decode->len;
+ if (!emul_ops->simulate_wrmsr(env_cpu(env))) {
+ env->eip += decode->len;
+ }
return 0;
}
diff --git a/target/i386/emulate/x86_emu.h b/target/i386/emulate/x86_emu.h
index a8d4c93098..b985240b90 100644
--- a/target/i386/emulate/x86_emu.h
+++ b/target/i386/emulate/x86_emu.h
@@ -31,8 +31,8 @@ struct x86_emul_ops {
target_ulong (*read_cr) (CPUState *cpu, int cr);
void (*handle_io)(CPUState *cpu, uint16_t port, void *data, int direction,
int size, int count);
- void (*simulate_rdmsr)(CPUState *cs);
- void (*simulate_wrmsr)(CPUState *cs);
+ bool (*simulate_rdmsr)(CPUState *cs);
+ bool (*simulate_wrmsr)(CPUState *cs);
bool (*is_protected_mode)(CPUState *cpu);
bool (*is_long_mode)(CPUState *cpu);
bool (*is_user_mode)(CPUState *cpu);
diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h
index 8c42ae6b01..b91c17e2fc 100644
--- a/target/i386/hvf/hvf-i386.h
+++ b/target/i386/hvf/hvf-i386.h
@@ -19,8 +19,8 @@
uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg);
void hvf_handle_io(CPUState *, uint16_t, void *, int, int, int);
-void hvf_simulate_rdmsr(CPUState *cpu);
-void hvf_simulate_wrmsr(CPUState *cpu);
+bool hvf_simulate_rdmsr(CPUState *cpu);
+bool hvf_simulate_wrmsr(CPUState *cpu);
/* Host specific functions */
int hvf_inject_interrupt(CPUArchState *env, int vector);
diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c
index c0d028b147..dfe7500010 100644
--- a/target/i386/hvf/hvf.c
+++ b/target/i386/hvf/hvf.c
@@ -536,7 +536,7 @@ void hvf_store_regs(CPUState *cs)
macvm_set_rip(cs, env->eip);
}
-void hvf_simulate_rdmsr(CPUState *cs)
+bool hvf_simulate_rdmsr(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
CPUX86State *env = &cpu->env;
@@ -557,6 +557,7 @@ void hvf_simulate_rdmsr(CPUState *cs)
ret = apic_msr_read(cpu->apic_state, index, &val);
if (ret < 0) {
x86_emul_raise_exception(env, EXCP0D_GPF, 0);
+ return 1;
}
break;
@@ -639,9 +640,10 @@ void hvf_simulate_rdmsr(CPUState *cs)
RAX(env) = (uint32_t)val;
RDX(env) = (uint32_t)(val >> 32);
+ return 0;
}
-void hvf_simulate_wrmsr(CPUState *cs)
+bool hvf_simulate_wrmsr(CPUState *cs)
{
X86CPU *cpu = X86_CPU(cs);
CPUX86State *env = &cpu->env;
@@ -657,6 +659,7 @@ void hvf_simulate_wrmsr(CPUState *cs)
r = cpu_set_apic_base(cpu->apic_state, data);
if (r < 0) {
x86_emul_raise_exception(env, EXCP0D_GPF, 0);
+ return 1;
}
break;
@@ -668,6 +671,7 @@ void hvf_simulate_wrmsr(CPUState *cs)
ret = apic_msr_write(cpu->apic_state, index, data);
if (ret < 0) {
x86_emul_raise_exception(env, EXCP0D_GPF, 0);
+ return 1;
}
break;
@@ -746,6 +750,7 @@ void hvf_simulate_wrmsr(CPUState *cs)
g_hypervisor_iface->wrmsr_handler(cs, msr, data);
printf("write msr %llx\n", RCX(cs));*/
+ return 0;
}
static int hvf_handle_vmexit(CPUState *cpu)
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 34/37] whpx: i386: add feature to intercept #GP MSR accesses
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (32 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 33/37] target/i386: emulate, hvf: rdmsr/wrmsr GPF handling Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 35/37] whpx: i386: nested virt settings Mohamed Mediouni
` (3 subsequent siblings)
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
It turns out they're not that uncommon, so have
a feature around to log those.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
accel/whpx/whpx-common.c | 1 +
include/system/whpx-internal.h | 1 +
target/i386/whpx/whpx-all.c | 183 +++++++++++++++++++++++++++++----
3 files changed, 166 insertions(+), 19 deletions(-)
diff --git a/accel/whpx/whpx-common.c b/accel/whpx/whpx-common.c
index 497c03138e..d846e08714 100644
--- a/accel/whpx/whpx-common.c
+++ b/accel/whpx/whpx-common.c
@@ -555,6 +555,7 @@ static void whpx_accel_instance_init(Object *obj)
/* Value determined at whpx_accel_init */
whpx->hyperv_enlightenments_enabled = false;
whpx->ignore_unknown_msr = true;
+ whpx->intercept_msr_gp = false;
}
static const TypeInfo whpx_accel_type = {
diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h
index 0aae83bd7c..15027a7d52 100644
--- a/include/system/whpx-internal.h
+++ b/include/system/whpx-internal.h
@@ -48,6 +48,7 @@ struct whpx_state {
bool hyperv_enlightenments_enabled;
bool ignore_unknown_msr;
+ bool intercept_msr_gp;
};
extern struct whpx_state whpx_global;
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 830d8acd2b..ea5d1e535c 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -1008,6 +1008,27 @@ static int emulate_instruction(CPUState *cpu, const uint8_t *insn_bytes, size_t
return 0;
}
+static int emulate_msr_instruction(CPUState *cpu,
+ const uint8_t *insn_bytes, size_t insn_len)
+{
+ X86CPU *x86_cpu = X86_CPU(cpu);
+ CPUX86State *env = &x86_cpu->env;
+ struct x86_decode decode = { 0 };
+ x86_insn_stream stream = { .bytes = insn_bytes, .len = insn_len };
+
+ whpx_get_registers(cpu, WHPX_LEVEL_FAST_RUNTIME_STATE);
+ decode_instruction_stream(env, &decode, &stream);
+
+ if (decode.cmd != X86_DECODE_CMD_RDMSR
+ && decode.cmd != X86_DECODE_CMD_WRMSR) {
+ return 1;
+ }
+
+ exec_instruction(env, &decode);
+ whpx_set_registers(cpu, WHPX_LEVEL_FAST_RUNTIME_STATE);
+ return 0;
+}
+
static int whpx_handle_mmio(CPUState *cpu, WHV_RUN_VP_EXIT_CONTEXT *exit_ctx)
{
WHV_MEMORY_ACCESS_CONTEXT *ctx = &exit_ctx->MemoryAccess;
@@ -1022,6 +1043,45 @@ static int whpx_handle_mmio(CPUState *cpu, WHV_RUN_VP_EXIT_CONTEXT *exit_ctx)
return 0;
}
+static int whpx_handle_msr_from_gpf(CPUState *cpu)
+{
+ WHV_VP_EXCEPTION_CONTEXT *ctx = &cpu->accel->exit_ctx.VpException;
+ int ret;
+
+ ret = emulate_msr_instruction(cpu, ctx->InstructionBytes, ctx->InstructionByteCount);
+ if (ret == 1) {
+ /* Not an MSR instruction */
+ return 1;
+ }
+
+ return 0;
+}
+
+static void whpx_inject_back_gpf(CPUState *cpu)
+{
+ WHV_VP_EXCEPTION_CONTEXT *ctx = &cpu->accel->exit_ctx.VpException;
+ WHV_REGISTER_VALUE reg = {};
+
+ if (ctx->ExceptionInfo.SoftwareException) {
+ /* TODO */
+ warn_report("Was asked to inject software exception.");
+ return;
+ }
+
+ if (ctx->ExceptionType != EXCP0D_GPF) {
+ warn_report("Was asked to inject exception other than GPF.");
+ return;
+ }
+
+ reg.ExceptionEvent.EventPending = 1;
+ reg.ExceptionEvent.EventType = WHvX64PendingEventException;
+ reg.ExceptionEvent.DeliverErrorCode = ctx->ExceptionInfo.ErrorCodeValid;
+ reg.ExceptionEvent.Vector = ctx->ExceptionType;
+ reg.ExceptionEvent.ErrorCode = ctx->ErrorCode;
+ reg.ExceptionEvent.ExceptionParameter = ctx->ExceptionParameter;
+ whpx_set_reg(cpu, WHvRegisterPendingEvent, reg);
+}
+
static void handle_io(CPUState *env, uint16_t port, void *buffer,
int direction, int size, int count)
{
@@ -1210,13 +1270,54 @@ static target_ulong read_cr(CPUState *cpu, int cr)
return val.Reg64;
}
+static bool whpx_simulate_rdmsr(CPUState *cs)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+ uint32_t msr = ECX(env);
+ uint64_t val = 0;
+
+ switch (msr) {
+ default:
+ error_report("WHPX: unknown msr 0x%x", msr);
+ x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ return 1;
+ break;
+ }
+
+ RAX(env) = (uint32_t)val;
+ RDX(env) = (uint32_t)(val >> 32);
+
+ return 0;
+}
+
+static bool whpx_simulate_wrmsr(CPUState *cs)
+{
+ X86CPU *cpu = X86_CPU(cs);
+ CPUX86State *env = &cpu->env;
+ uint32_t msr = ECX(env);
+ uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env);
+
+ switch (msr) {
+ default:
+ error_report("WHPX: unknown msr 0x%x val %llx", msr, data);
+ x86_emul_raise_exception(&X86_CPU(cpu)->env, EXCP0D_GPF, 0);
+ return 1;
+ break;
+ }
+
+ return 0;
+}
+
static const struct x86_emul_ops whpx_x86_emul_ops = {
.read_segment_descriptor = read_segment_descriptor,
.handle_io = handle_io,
.is_protected_mode = is_protected_mode,
.is_long_mode = is_long_mode,
.is_user_mode = is_user_mode,
- .read_cr = read_cr
+ .read_cr = read_cr,
+ .simulate_rdmsr = whpx_simulate_rdmsr,
+ .simulate_wrmsr = whpx_simulate_wrmsr
};
static void whpx_init_emu(void)
@@ -1356,6 +1457,18 @@ uint64_t whpx_get_supported_msr_feature(uint32_t index)
return 0;
}
+static UINT64 whpx_get_default_exceptions(void)
+{
+ struct whpx_state *whpx = &whpx_global;
+ UINT64 intercepts = 0;
+
+ if (whpx->intercept_msr_gp) {
+ intercepts |= 1UL << WHvX64ExceptionTypeGeneralProtectionFault;
+ }
+
+ return intercepts;
+}
+
/*
* Controls whether we should intercept various exceptions on the guest,
* namely breakpoint/single-step events.
@@ -1378,7 +1491,7 @@ HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions)
prop.ExtendedVmExits.X64MsrExit = 1;
prop.ExtendedVmExits.X64CpuidExit = 1;
- if (exceptions != 0) {
+ if (exceptions != 0 || whpx_get_default_exceptions() != 0) {
prop.ExtendedVmExits.ExceptionExit = 1;
}
@@ -1393,7 +1506,7 @@ HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions)
}
memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
- prop.ExceptionExitBitmap = exceptions;
+ prop.ExceptionExitBitmap = exceptions | whpx_get_default_exceptions();
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
@@ -1403,6 +1516,8 @@ HRESULT whpx_set_exception_exit_bitmap(UINT64 exceptions)
if (SUCCEEDED(hr)) {
whpx->exception_exit_bitmap = exceptions;
+ } else {
+ error_report("WHPX: Failed to set exception exit bitmap, hr=%08lx", hr);
}
return hr;
@@ -2530,6 +2645,15 @@ int whpx_vcpu_run(CPUState *cpu)
break;
}
case WHvRunVpExitReasonException:
+ if (vcpu->exit_ctx.VpException.ExceptionType ==
+ WHvX64ExceptionTypeGeneralProtectionFault) {
+ if (whpx_handle_msr_from_gpf(cpu)) {
+ whpx_inject_back_gpf(cpu);
+ }
+ ret = 0;
+ break;
+ }
+
whpx_get_registers(cpu, WHPX_LEVEL_FULL_STATE);
if ((vcpu->exit_ctx.VpException.ExceptionType ==
@@ -2818,6 +2942,38 @@ static void whpx_set_unknown_msr(Object *obj, Visitor *v,
}
}
+static void whpx_set_intercept_msr_gp(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ struct whpx_state *whpx = &whpx_global;
+ OnOffAuto mode;
+
+ if (!visit_type_OnOffAuto(v, name, &mode, errp)) {
+ return;
+ }
+
+ switch (mode) {
+ case ON_OFF_AUTO_ON:
+ whpx->intercept_msr_gp = true;
+ break;
+
+ case ON_OFF_AUTO_OFF:
+ whpx->intercept_msr_gp = false;
+ break;
+
+ case ON_OFF_AUTO_AUTO:
+ whpx->intercept_msr_gp = false;
+ break;
+ default:
+ /*
+ * The value was checked in visit_type_OnOffAuto() above. If
+ * we get here, then something is wrong in QEMU.
+ */
+ abort();
+ }
+}
+
void whpx_arch_accel_class_init(ObjectClass *oc)
{
object_class_property_add(oc, "ignore-unknown-msr", "OnOffAuto",
@@ -2825,6 +2981,11 @@ void whpx_arch_accel_class_init(ObjectClass *oc)
NULL, NULL);
object_class_property_set_description(oc, "ignore-unknown-msr",
"Configure unknown MSR behavior");
+ object_class_property_add(oc, "intercept-msr-gp", "OnOffAuto",
+ NULL, whpx_set_intercept_msr_gp,
+ NULL, NULL);
+ object_class_property_set_description(oc, "intercept-msr-gp",
+ "Intercept #GP to log erroring MSR accesses.");
}
int whpx_accel_init(AccelState *as, MachineState *ms)
@@ -3079,22 +3240,6 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
goto error;
}
- /* 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,
- WHvPartitionPropertyCodeExtendedVmExits,
- &prop,
- sizeof(WHV_PARTITION_PROPERTY));
- if (FAILED(hr)) {
- error_report("WHPX: Failed to enable extended VM exits, hr=%08lx", hr);
- ret = -EINVAL;
- goto error;
- }
-
memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
prop.X64MsrExitBitmap.UnhandledMsrs = 1;
prop.X64MsrExitBitmap.ApicBaseMsrWrite = 1;
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 35/37] whpx: i386: nested virt settings
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (33 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 34/37] whpx: i386: add feature to intercept #GP MSR accesses Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-30 13:44 ` Paolo Bonzini
2026-04-22 21:42 ` [PATCH v3 36/37] whpx: i386: add SeparateSecurityDomain flag and make default Mohamed Mediouni
` (2 subsequent siblings)
37 siblings, 1 reply; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
accel/whpx/whpx-common.c | 1 +
include/system/whpx-internal.h | 2 +
target/i386/whpx/whpx-all.c | 68 +++++++++++++++++++++++++++++++---
3 files changed, 66 insertions(+), 5 deletions(-)
diff --git a/accel/whpx/whpx-common.c b/accel/whpx/whpx-common.c
index d846e08714..305b2ae1d0 100644
--- a/accel/whpx/whpx-common.c
+++ b/accel/whpx/whpx-common.c
@@ -556,6 +556,7 @@ static void whpx_accel_instance_init(Object *obj)
whpx->hyperv_enlightenments_enabled = false;
whpx->ignore_unknown_msr = true;
whpx->intercept_msr_gp = false;
+ whpx->nested_virt_enabled = true;
}
static const TypeInfo whpx_accel_type = {
diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h
index 15027a7d52..4436cfe6ba 100644
--- a/include/system/whpx-internal.h
+++ b/include/system/whpx-internal.h
@@ -47,6 +47,8 @@ struct whpx_state {
bool hyperv_enlightenments_required;
bool hyperv_enlightenments_enabled;
+ bool nested_virt_enabled;
+
bool ignore_unknown_msr;
bool intercept_msr_gp;
};
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index ea5d1e535c..d17db73cff 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -2974,6 +2974,39 @@ static void whpx_set_intercept_msr_gp(Object *obj, Visitor *v,
}
}
+static void whpx_set_nested_virt(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ struct whpx_state *whpx = &whpx_global;
+ OnOffAuto mode;
+
+ if (!visit_type_OnOffAuto(v, name, &mode, errp)) {
+ return;
+ }
+
+ switch (mode) {
+ case ON_OFF_AUTO_ON:
+ whpx->nested_virt_enabled = true;
+ break;
+
+ case ON_OFF_AUTO_OFF:
+ whpx->nested_virt_enabled = false;
+ break;
+
+ case ON_OFF_AUTO_AUTO:
+ whpx->nested_virt_enabled = true;
+ break;
+ default:
+ /*
+ * The value was checked in visit_type_OnOffAuto() above. If
+ * we get here, then something is wrong in QEMU.
+ */
+ abort();
+ }
+}
+
+
void whpx_arch_accel_class_init(ObjectClass *oc)
{
object_class_property_add(oc, "ignore-unknown-msr", "OnOffAuto",
@@ -2986,6 +3019,11 @@ void whpx_arch_accel_class_init(ObjectClass *oc)
NULL, NULL);
object_class_property_set_description(oc, "intercept-msr-gp",
"Intercept #GP to log erroring MSR accesses.");
+ object_class_property_add(oc, "nested", "OnOffAuto",
+ NULL, whpx_set_nested_virt,
+ NULL, NULL);
+ object_class_property_set_description(oc, "intercept-msr-gp",
+ "Toggle nested virtualization off/on.");
}
int whpx_accel_init(AccelState *as, MachineState *ms)
@@ -3166,7 +3204,8 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
whpx_rdtsc_cap = processor_features.Bank0.RdtscpSupport;
whpx_invpcid_cap = processor_features.Bank0.InvpcidSupport;
- if (whpx_irqchip_in_kernel() && processor_features.Bank1.NestedVirtSupport) {
+ if (whpx_irqchip_in_kernel() && whpx->nested_virt_enabled
+ && processor_features.Bank1.NestedVirtSupport) {
memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
prop.NestedVirtualization = 1;
hr = whp_dispatch.WHvSetPartitionProperty(
@@ -3174,11 +3213,14 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
WHvPartitionPropertyCodeNestedVirtualization,
&prop,
sizeof(WHV_PARTITION_PROPERTY));
- if (FAILED(hr)) {
- error_report("WHPX: Failed to enable nested virtualization, hr=%08lx", hr);
- ret = -EINVAL;
- goto error;
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to enable nested virtualization, hr=%08lx", hr);
+ ret = -EINVAL;
+ goto error;
}
+ } else if (whpx->nested_virt_enabled) {
+ whpx->nested_virt_enabled = 0;
+ info_report("WHPX: nested virtualisation not available");
}
hr = whp_dispatch.WHvSetPartitionProperty(
@@ -3222,6 +3264,22 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
synthetic_features.Bank0.EnableExtendedGvaRangesForFlushVirtualAddressList = 1;
}
+ if (whpx->nested_virt_enabled) {
+ /* enlightened VMCS is an Intel-specific enlightenment. */
+ hr = whp_dispatch.WHvGetCapability(
+ WHvCapabilityCodeProcessorVendor, &whpx_cap,
+ sizeof(whpx_cap), &whpx_cap_size);
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to query processor vendor, hr=%08lx", hr);
+ ret = -ENOSPC;
+ goto error;
+ }
+ if (whpx_cap.ProcessorVendor == WHvProcessorVendorIntel) {
+ synthetic_features.Bank0.EnlightenedVmcs = 1;
+ synthetic_features.Bank0.NestedDebugCtl = 1;
+ }
+ }
+
if (is_modern_os && whpx->hyperv_enlightenments_allowed) {
whpx->hyperv_enlightenments_enabled = true;
hr = whp_dispatch.WHvSetPartitionProperty(
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v3 35/37] whpx: i386: nested virt settings
2026-04-22 21:42 ` [PATCH v3 35/37] whpx: i386: nested virt settings Mohamed Mediouni
@ 2026-04-30 13:44 ` Paolo Bonzini
2026-04-30 17:52 ` Mohamed Mediouni
0 siblings, 1 reply; 45+ messages in thread
From: Paolo Bonzini @ 2026-04-30 13:44 UTC (permalink / raw)
To: Mohamed Mediouni, qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Roman Bolshakov,
Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan, Peter Maydell,
Zhao Liu
On 4/22/26 23:42, Mohamed Mediouni wrote:
> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
This should use the VMX and SVM CPU features.
Paolo
> ---
> accel/whpx/whpx-common.c | 1 +
> include/system/whpx-internal.h | 2 +
> target/i386/whpx/whpx-all.c | 68 +++++++++++++++++++++++++++++++---
> 3 files changed, 66 insertions(+), 5 deletions(-)
>
> diff --git a/accel/whpx/whpx-common.c b/accel/whpx/whpx-common.c
> index d846e08714..305b2ae1d0 100644
> --- a/accel/whpx/whpx-common.c
> +++ b/accel/whpx/whpx-common.c
> @@ -556,6 +556,7 @@ static void whpx_accel_instance_init(Object *obj)
> whpx->hyperv_enlightenments_enabled = false;
> whpx->ignore_unknown_msr = true;
> whpx->intercept_msr_gp = false;
> + whpx->nested_virt_enabled = true;
> }
>
> static const TypeInfo whpx_accel_type = {
> diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h
> index 15027a7d52..4436cfe6ba 100644
> --- a/include/system/whpx-internal.h
> +++ b/include/system/whpx-internal.h
> @@ -47,6 +47,8 @@ struct whpx_state {
> bool hyperv_enlightenments_required;
> bool hyperv_enlightenments_enabled;
>
> + bool nested_virt_enabled;
> +
> bool ignore_unknown_msr;
> bool intercept_msr_gp;
> };
> diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
> index ea5d1e535c..d17db73cff 100644
> --- a/target/i386/whpx/whpx-all.c
> +++ b/target/i386/whpx/whpx-all.c
> @@ -2974,6 +2974,39 @@ static void whpx_set_intercept_msr_gp(Object *obj, Visitor *v,
> }
> }
>
> +static void whpx_set_nested_virt(Object *obj, Visitor *v,
> + const char *name, void *opaque,
> + Error **errp)
> +{
> + struct whpx_state *whpx = &whpx_global;
> + OnOffAuto mode;
> +
> + if (!visit_type_OnOffAuto(v, name, &mode, errp)) {
> + return;
> + }
> +
> + switch (mode) {
> + case ON_OFF_AUTO_ON:
> + whpx->nested_virt_enabled = true;
> + break;
> +
> + case ON_OFF_AUTO_OFF:
> + whpx->nested_virt_enabled = false;
> + break;
> +
> + case ON_OFF_AUTO_AUTO:
> + whpx->nested_virt_enabled = true;
> + break;
> + default:
> + /*
> + * The value was checked in visit_type_OnOffAuto() above. If
> + * we get here, then something is wrong in QEMU.
> + */
> + abort();
> + }
> +}
> +
> +
> void whpx_arch_accel_class_init(ObjectClass *oc)
> {
> object_class_property_add(oc, "ignore-unknown-msr", "OnOffAuto",
> @@ -2986,6 +3019,11 @@ void whpx_arch_accel_class_init(ObjectClass *oc)
> NULL, NULL);
> object_class_property_set_description(oc, "intercept-msr-gp",
> "Intercept #GP to log erroring MSR accesses.");
> + object_class_property_add(oc, "nested", "OnOffAuto",
> + NULL, whpx_set_nested_virt,
> + NULL, NULL);
> + object_class_property_set_description(oc, "intercept-msr-gp",
> + "Toggle nested virtualization off/on.");
> }
>
> int whpx_accel_init(AccelState *as, MachineState *ms)
> @@ -3166,7 +3204,8 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
> whpx_rdtsc_cap = processor_features.Bank0.RdtscpSupport;
> whpx_invpcid_cap = processor_features.Bank0.InvpcidSupport;
>
> - if (whpx_irqchip_in_kernel() && processor_features.Bank1.NestedVirtSupport) {
> + if (whpx_irqchip_in_kernel() && whpx->nested_virt_enabled
> + && processor_features.Bank1.NestedVirtSupport) {
> memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
> prop.NestedVirtualization = 1;
> hr = whp_dispatch.WHvSetPartitionProperty(
> @@ -3174,11 +3213,14 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
> WHvPartitionPropertyCodeNestedVirtualization,
> &prop,
> sizeof(WHV_PARTITION_PROPERTY));
> - if (FAILED(hr)) {
> - error_report("WHPX: Failed to enable nested virtualization, hr=%08lx", hr);
> - ret = -EINVAL;
> - goto error;
> + if (FAILED(hr)) {
> + error_report("WHPX: Failed to enable nested virtualization, hr=%08lx", hr);
> + ret = -EINVAL;
> + goto error;
> }
> + } else if (whpx->nested_virt_enabled) {
> + whpx->nested_virt_enabled = 0;
> + info_report("WHPX: nested virtualisation not available");
> }
>
> hr = whp_dispatch.WHvSetPartitionProperty(
> @@ -3222,6 +3264,22 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
> synthetic_features.Bank0.EnableExtendedGvaRangesForFlushVirtualAddressList = 1;
> }
>
> + if (whpx->nested_virt_enabled) {
> + /* enlightened VMCS is an Intel-specific enlightenment. */
> + hr = whp_dispatch.WHvGetCapability(
> + WHvCapabilityCodeProcessorVendor, &whpx_cap,
> + sizeof(whpx_cap), &whpx_cap_size);
> + if (FAILED(hr)) {
> + error_report("WHPX: Failed to query processor vendor, hr=%08lx", hr);
> + ret = -ENOSPC;
> + goto error;
> + }
> + if (whpx_cap.ProcessorVendor == WHvProcessorVendorIntel) {
> + synthetic_features.Bank0.EnlightenedVmcs = 1;
> + synthetic_features.Bank0.NestedDebugCtl = 1;
> + }
> + }
> +
> if (is_modern_os && whpx->hyperv_enlightenments_allowed) {
> whpx->hyperv_enlightenments_enabled = true;
> hr = whp_dispatch.WHvSetPartitionProperty(
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v3 35/37] whpx: i386: nested virt settings
2026-04-30 13:44 ` Paolo Bonzini
@ 2026-04-30 17:52 ` Mohamed Mediouni
0 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-30 17:52 UTC (permalink / raw)
To: Paolo Bonzini
Cc: qemu-devel, Pedro Barbuda, qemu-arm, Pierrick Bouvier,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu
> On 30. Apr 2026, at 15:44, Paolo Bonzini <pbonzini@redhat.com> wrote:
>
> On 4/22/26 23:42, Mohamed Mediouni wrote:
>> Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
>
> This should use the VMX and SVM CPU features.
>
> Paolo
Hi,
The problem is that the CPU comes way after this, and this info
is required for partition initialisation rather than later on.
(And the same problem applies for the `hyperv` accelerator property,
which is why we can’t use the existing -cpu [enlightenment] bits)
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v3 36/37] whpx: i386: add SeparateSecurityDomain flag and make default
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (34 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 35/37] whpx: i386: nested virt settings Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-22 21:42 ` [PATCH v3 37/37] whpx: i386: documentation update Mohamed Mediouni
2026-04-23 11:10 ` [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Paolo Bonzini
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
For workloads where isolation is less important, -accel whpx,ssd=off
will provide significantly higher MMIO performance.
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
accel/whpx/whpx-common.c | 1 +
include/system/whpx-internal.h | 1 +
target/i386/whpx/whpx-all.c | 63 ++++++++++++++++++++++++++++++++++
3 files changed, 65 insertions(+)
diff --git a/accel/whpx/whpx-common.c b/accel/whpx/whpx-common.c
index 305b2ae1d0..e5dabb6bdf 100644
--- a/accel/whpx/whpx-common.c
+++ b/accel/whpx/whpx-common.c
@@ -557,6 +557,7 @@ static void whpx_accel_instance_init(Object *obj)
whpx->ignore_unknown_msr = true;
whpx->intercept_msr_gp = false;
whpx->nested_virt_enabled = true;
+ whpx->separate_security_domain = true;
}
static const TypeInfo whpx_accel_type = {
diff --git a/include/system/whpx-internal.h b/include/system/whpx-internal.h
index 4436cfe6ba..08a49706ff 100644
--- a/include/system/whpx-internal.h
+++ b/include/system/whpx-internal.h
@@ -48,6 +48,7 @@ struct whpx_state {
bool hyperv_enlightenments_enabled;
bool nested_virt_enabled;
+ bool separate_security_domain;
bool ignore_unknown_msr;
bool intercept_msr_gp;
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index d17db73cff..035a8231b7 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -3006,6 +3006,38 @@ static void whpx_set_nested_virt(Object *obj, Visitor *v,
}
}
+static void whpx_set_ssd(Object *obj, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ struct whpx_state *whpx = &whpx_global;
+ OnOffAuto mode;
+
+ if (!visit_type_OnOffAuto(v, name, &mode, errp)) {
+ return;
+ }
+
+ switch (mode) {
+ case ON_OFF_AUTO_ON:
+ whpx->separate_security_domain = true;
+ break;
+
+ case ON_OFF_AUTO_OFF:
+ whpx->separate_security_domain = false;
+ break;
+
+ case ON_OFF_AUTO_AUTO:
+ whpx->separate_security_domain = true;
+ break;
+ default:
+ /*
+ * The value was checked in visit_type_OnOffAuto() above. If
+ * we get here, then something is wrong in QEMU.
+ */
+ abort();
+ }
+}
+
void whpx_arch_accel_class_init(ObjectClass *oc)
{
@@ -3024,6 +3056,11 @@ void whpx_arch_accel_class_init(ObjectClass *oc)
NULL, NULL);
object_class_property_set_description(oc, "intercept-msr-gp",
"Toggle nested virtualization off/on.");
+ object_class_property_add(oc, "ssd", "OnOffAuto",
+ NULL, whpx_set_ssd,
+ NULL, NULL);
+ object_class_property_set_description(oc, "ssd",
+ "Separate security domain");
}
int whpx_accel_init(AccelState *as, MachineState *ms)
@@ -3223,6 +3260,32 @@ int whpx_accel_init(AccelState *as, MachineState *ms)
info_report("WHPX: nested virtualisation not available");
}
+ /*
+ * The combination of separate security domain off
+ * and disabling specifically these features results
+ * in a significant vmexit performance improvement
+ * by skipping speculative execution mitigations.
+ */
+ if (!whpx->separate_security_domain) {
+ processor_features.Bank0.IbrsSupport = 0;
+ processor_features.Bank0.StibpSupport = 0;
+ processor_features.Bank0.IbpbSupport = 0;
+ processor_features.Bank0.SsbdSupport = 0;
+ processor_features.Bank0.IbrsAllSupport = 0;
+ processor_features.Bank1.PsfdSupport = 0;
+ memset(&prop, 0, sizeof(WHV_PARTITION_PROPERTY));
+ prop.SeparateSecurityDomain = 0;
+ hr = whp_dispatch.WHvSetPartitionProperty(
+ whpx->partition,
+ WHvPartitionPropertyCodeSeparateSecurityDomain,
+ &prop,
+ sizeof(WHV_PARTITION_PROPERTY));
+ if (FAILED(hr)) {
+ error_report("WHPX: failed to unset separate security domain, hr=%08lx", hr);
+ /* Some old Windows 10 releases didn't have this, so not fatal*/
+ }
+ }
+
hr = whp_dispatch.WHvSetPartitionProperty(
whpx->partition,
WHvPartitionPropertyCodeProcessorFeaturesBanks,
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v3 37/37] whpx: i386: documentation update
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (35 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 36/37] whpx: i386: add SeparateSecurityDomain flag and make default Mohamed Mediouni
@ 2026-04-22 21:42 ` Mohamed Mediouni
2026-04-23 11:10 ` [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Paolo Bonzini
37 siblings, 0 replies; 45+ messages in thread
From: Mohamed Mediouni @ 2026-04-22 21:42 UTC (permalink / raw)
To: qemu-devel
Cc: Pedro Barbuda, qemu-arm, Pierrick Bouvier, Mohamed Mediouni,
Roman Bolshakov, Michael S. Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Paolo Bonzini
Signed-off-by: Mohamed Mediouni <mohamed@unpredictable.fr>
---
docs/system/whpx.rst | 40 +++++++++++-----------------------------
1 file changed, 11 insertions(+), 29 deletions(-)
diff --git a/docs/system/whpx.rst b/docs/system/whpx.rst
index 3e1979028c..9909e86831 100644
--- a/docs/system/whpx.rst
+++ b/docs/system/whpx.rst
@@ -63,7 +63,7 @@ additional functionality compared to ``-device ramfb``, but is
incompatible with Windows's UEFI GOP implementation, which
expects a linear framebuffer to be available.
-Some tracing options
+Accelerator options
--------------------
x86_64
@@ -75,6 +75,11 @@ to undocumented MSRs.
``-d invalid_mem`` allows to trace accesses to unmapped
GPAs.
+``-accel whpx,ssd=off`` disables the separate security domain feature,
+as in a BTB flush when entering/exiting the guest. This results in a
+significant MMIO performance increase at the detriment of security
+mitigations.
+
Known issues on x86_64
----------------------
@@ -96,45 +101,22 @@ MMX, SSE or AVX instructions for access to MMIO memory ranges.
Attempts to run such guests will result in an ``Unimplemented handler``
warning for MMX and a failure to decode for newer instructions.
-``-M isapc``
-^^^^^^^^^^^^
-
-``-M isapc`` doesn't disable the Hyper-V LAPIC on its own yet. To
-be able to use that machine, use ``-accel whpx,hyperv=off,kernel-irqchip=off``.
-
-However, in QEMU 11.0, the guest will still be a 64-bit x86
-ISA machine with all the corresponding CPUID leaves exposed.
-
-gdbstub
-^^^^^^^
-
-As save/restore of xsave state is not currently present, state
-exposed through GDB will be incomplete.
-
-The same also applies to ``info registers``.
-
-``-cpu type`` ignored
-^^^^^^^^^^^^^^^^^^^^^
-
-In this release, -cpu is an ignored argument.
-
PIC interrupts on Windows 10
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
On Windows 10, a legacy PIC interrupt injected does not wake the guest
from an HLT when using the Hyper-V provided interrupt controller.
-This has been addressed in QEMU 11.0 on Windows 11 platforms but
-functionality to make it available on Windows 10 isn't present.
+As such, on Windows 10, using the Hyper-V interrupt controller is
+disabled by default. You can enable it via ``-M q35,pic=off`` which
+disables the PIC. In that configuration, using a UEFI is recommended.
-Workaround: for affected use cases, use ``-M kernel-irqchip=off``.
+On this release, ``-M kernel-irqchip=`` is not expected to be manually
+set during normal operation. It remains as a debugging option.
Known issues on Windows 11
^^^^^^^^^^^^^^^^^^^^^^^^^^
-Nested virtualisation-specific Hyper-V enlightenments are not
-currently exposed.
-
arm64
-----
--
2.50.1 (Apple Git-155)
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1
2026-04-22 21:41 [PATCH v3 00/37] WHPX x86 updates for QEMU 11.1 Mohamed Mediouni
` (36 preceding siblings ...)
2026-04-22 21:42 ` [PATCH v3 37/37] whpx: i386: documentation update Mohamed Mediouni
@ 2026-04-23 11:10 ` Paolo Bonzini
37 siblings, 0 replies; 45+ messages in thread
From: Paolo Bonzini @ 2026-04-23 11:10 UTC (permalink / raw)
To: Mohamed Mediouni
Cc: qemu-devel, Pedro Barbuda, qemu-arm, Pierrick Bouvier,
Roman Bolshakov, Michael S . Tsirkin, Wei Liu, Phil Dennis-Jordan,
Peter Maydell, Zhao Liu, Magnus Kulke
I have started reviewing this, thanks. Up to patch 17 it is good, though
we want to tighten the MSR configurability up in the future (please
look at KVM for ideas).
If you have any more changes, please either wait or at least post on top of
those first 17 patches. I'll focus my reviews on both this series and
MSHV in the next few days.
Paolo
^ permalink raw reply [flat|nested] 45+ messages in thread