target/i386/whpx/whpx-all.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
WHPX is not properly handling processor reset:
- Does not reset partition, so several registers (e.g. Hypercall,
GuestOsId) carry values forward as "Locked" into the next session.
- Does not clear hypervisor-managed TSC deadline (live-lock).
- Does not re-arm the wait-for-SIPI trigger, so all CPUs start running
immediately after reset.
Some firmware may tolerate this, but OVMF does not. Typical result is
that boot hangs or ends in triple-fault, e.g.
qemu-system-x86_64: WHPX: Unexpected VP exit code 4
After this patch, I am able to boot to Windows desktop, reset, and boot
back to Windows desktop. Tried many times in sequence with no problems.
Used several different reset mechanisms (OS shutdown menu, external
"reset", watchdog reset) with no problems.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2402
Signed-off-by: Doug Cook <dcook@microsoft.com>
---
target/i386/whpx/whpx-all.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
index 634d542821..54f9b50536 100644
--- a/target/i386/whpx/whpx-all.c
+++ b/target/i386/whpx/whpx-all.c
@@ -528,6 +528,35 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
+ /* Assumes that cpu_synchronize_all_post_reset() runs cpu 0 first. */
+ if (level == WHPX_LEVEL_RESET_STATE && cpu->cpu_index == 0) {
+ hr = whp_dispatch.WHvResetPartition(whpx->partition);
+ if (FAILED(hr)) {
+ error_report("WHPX: Failed to reset partition, hr=%08lx", hr);
+ }
+ }
+
+ /* Reset hypervisor-owned TSC deadline. */
+ if (level == WHPX_LEVEL_RESET_STATE) {
+ WHV_REGISTER_VALUE tsc_deadline = {};
+
+ whpx_set_reg(cpu, WHvX64RegisterTscDeadline, tsc_deadline);
+ }
+
+ /*
+ * Re-arm partition's wait-for-SIPI state.
+ * whpx_vcpu_run() ignores cpu->halted, depends on SIPI for this.
+ * This must follow WHvResetPartition.
+ */
+ if (level == WHPX_LEVEL_RESET_STATE && whpx_irqchip_in_kernel()) {
+ WHV_REGISTER_VALUE activity = {};
+
+ whpx_get_reg(cpu, WHvRegisterInternalActivityState, &activity);
+ activity.InternalActivity.StartupSuspend = !cpu_is_bsp(x86_cpu);
+ activity.InternalActivity.HaltSuspend = 0;
+ whpx_set_reg(cpu, WHvRegisterInternalActivityState, activity);
+ }
+
/*
* Following MSRs have side effects on the guest or are too heavy for
* runtime. Limit them to full state update.
--
2.55.0.vfs.0.3
> On 18. Aug 2026, at 19:58, Doug Cook (WINDOWS) <dcook@microsoft.com> wrote:
>
> WHPX is not properly handling processor reset:
>
> - Does not reset partition, so several registers (e.g. Hypercall,
> GuestOsId) carry values forward as "Locked" into the next session.
> - Does not clear hypervisor-managed TSC deadline (live-lock).
> - Does not re-arm the wait-for-SIPI trigger, so all CPUs start running
> immediately after reset.
>
> Some firmware may tolerate this, but OVMF does not. Typical result is
> that boot hangs or ends in triple-fault, e.g.
>
> qemu-system-x86_64: WHPX: Unexpected VP exit code 4
>
> After this patch, I am able to boot to Windows desktop, reset, and boot
> back to Windows desktop. Tried many times in sequence with no problems.
> Used several different reset mechanisms (OS shutdown menu, external
> "reset", watchdog reset) with no problems.
>
> Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/2402
> Signed-off-by: Doug Cook <dcook@microsoft.com>
Hi,
Previous version of this concept at: https://patchew.org/QEMU/20260421205749.55060-1-mohamed@unpredictable.fr/ (with it intertwined in a common path for Arm support)
Using WHvResetPartition as a workaround for some state not being fully synchronised isn’t
ideal though...
> ---
> target/i386/whpx/whpx-all.c | 29 +++++++++++++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c
> index 634d542821..54f9b50536 100644
> --- a/target/i386/whpx/whpx-all.c
> +++ b/target/i386/whpx/whpx-all.c
> @@ -528,6 +528,35 @@ void whpx_set_registers(CPUState *cpu, WHPXStateLevel level)
>
> assert(cpu_is_stopped(cpu) || qemu_cpu_is_self(cpu));
>
> + /* Assumes that cpu_synchronize_all_post_reset() runs cpu 0 first. */
> + if (level == WHPX_LEVEL_RESET_STATE && cpu->cpu_index == 0) {
> + hr = whp_dispatch.WHvResetPartition(whpx->partition);
> + if (FAILED(hr)) {
> + error_report("WHPX: Failed to reset partition, hr=%08lx", hr);
> + }
> + }
> +
> + /* Reset hypervisor-owned TSC deadline. */
> + if (level == WHPX_LEVEL_RESET_STATE) {
> + WHV_REGISTER_VALUE tsc_deadline = {};
> +
> + whpx_set_reg(cpu, WHvX64RegisterTscDeadline, tsc_deadline);
> + }
> +
> + /*
> + * Re-arm partition's wait-for-SIPI state.
> + * whpx_vcpu_run() ignores cpu->halted, depends on SIPI for this.
> + * This must follow WHvResetPartition.
> + */
> + if (level == WHPX_LEVEL_RESET_STATE && whpx_irqchip_in_kernel()) {
> + WHV_REGISTER_VALUE activity = {};
> +
> + whpx_get_reg(cpu, WHvRegisterInternalActivityState, &activity);
> + activity.InternalActivity.StartupSuspend = !cpu_is_bsp(x86_cpu);
> + activity.InternalActivity.HaltSuspend = 0;
> + whpx_set_reg(cpu, WHvRegisterInternalActivityState, activity);
> + }
> +
> /*
> * Following MSRs have side effects on the guest or are too heavy for
> * runtime. Limit them to full state update.
> --
> 2.55.0.vfs.0.3
>
> > On 18. Aug 2026, at 19:58, Doug Cook (WINDOWS) > <dcook@microsoft.com> wrote: > > > > WHPX is not properly handling processor reset: > > > > - Does not reset partition, so several registers (e.g. Hypercall, > > GuestOsId) carry values forward as "Locked" into the next session. > > - Does not clear hypervisor-managed TSC deadline (live-lock). > > - Does not re-arm the wait-for-SIPI trigger, so all CPUs start running > > immediately after reset. > > > > Some firmware may tolerate this, but OVMF does not. Typical result is > > that boot hangs or ends in triple-fault, e.g. > > > > qemu-system-x86_64: WHPX: Unexpected VP exit code 4 > > > > After this patch, I am able to boot to Windows desktop, reset, and > > boot back to Windows desktop. Tried many times in sequence with no > problems. > > Used several different reset mechanisms (OS shutdown menu, external > > "reset", watchdog reset) with no problems. > > > > Resolves: > > https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgitl > > ab.com%2Fqemu-project%2Fqemu%2F- > %2Fwork_items%2F2402&data=05%7C02%7Cdc > > > ook%40microsoft.com%7Cebc7a2c3ae3641f12b8308defd533da1%7C72f98 > 8bf86f14 > > > 1af91ab2d7cd011db47%7C1%7C0%7C639226731139271283%7CUnknow > n%7CTWFpbGZsb > > > 3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXaW4zMiIsI > kFOIjo > > > iTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=TMLja23Uf76wlskB > p7w%2BiaS > > x190p%2FQblKks2awOf7C8%3D&reserved=0 > > Signed-off-by: Doug Cook <dcook@microsoft.com> > > Hi, > > Previous version of this concept at: > https://nam06.safelinks.protection.outlook.com/?url=https%3A%2F%2Fpatc > hew.org%2FQEMU%2F20260421205749.55060-1- > mohamed%40unpredictable.fr%2F&data=05%7C02%7Cdcook%40microsoft. > com%7Cebc7a2c3ae3641f12b8308defd533da1%7C72f988bf86f141af91ab > 2d7cd011db47%7C1%7C0%7C639226731139298436%7CUnknown%7CTW > FpbGZsb3d8eyJFbXB0eU1hcGkiOnRydWUsIlYiOiIwLjAuMDAwMCIsIlAiOiJXa > W4zMiIsIkFOIjoiTWFpbCIsIldUIjoyfQ%3D%3D%7C0%7C%7C%7C&sdata=tE > M4SLQ74SCFEwIW1LLW6GM2FGcEmzD5KDMVdo8mr14%3D&reserved=0 > (with it intertwined in a common path for Arm support) > > Using WHvResetPartition as a workaround for some state not being fully > synchronised isn't ideal though... As far as I can tell (from testing, not from experience), several important bits of state (Hypercall, GuestOsId) get "locked" and can only be reset this way. I would be happy to learn a better way, though. Thanks! Doug
© 2016 - 2026 Red Hat, Inc.