From 3a0deb99bcd079f239710352b2d0bae23f0b7d7d Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Thu, 20 Aug 2026 20:35:27 +0800 Subject: [PATCH 1/2] LATX, fix: Clear stale LBT TOP after longjmp Some translator exits bypass the generated epilogue, and siglongjmp can restore a host context with LBT TOP mode still active. The next entry then loads physical x87 registers through the stale mapping and places values in the wrong slots for Minke.MI.Organ. After siglongjmp returns to cpu_exec(), check the saved FCSR TOP-mode bit. Clear both the saved bit and hardware mode only when it is set, and leave soft-float execution unchanged. Tests: - Run 20,000 x87 signal deliveries with no failures - Verify single, seven-thread, and nonzero-TOP signal restoration Signed-off-by: Wenqiang Wei --- accel/tcg/cpu-exec.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 0e6a156bcd..2ef01ba861 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -169,6 +169,8 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) #include "latx-options.h" #include "latx-signal.h" #include "reg-map.h" + +#define LBT_FCSR_TOP_MODE_MASK (1U << 6) #endif /* Execute a TB, and fix up the CPU state afterwards if necessary */ @@ -1154,6 +1156,20 @@ int cpu_exec(CPUState *cpu) g_assert(cpu == current_cpu); #endif +#ifdef CONFIG_LATX + CPUArchState *env = cpu->env_ptr; + + /* + * Some exits bypass the translated cleanup and siglongjmp restores a + * possibly active LBT TOP mapping. Clear both copies after the jump, + * before physical FPRs are loaded on the next entry. + */ + if (!option_softfpu && (env->fcsr & LBT_FCSR_TOP_MODE_MASK)) { + env->fcsr &= ~LBT_FCSR_TOP_MODE_MASK; + __asm__ volatile("x86clrtm" : : : "memory"); + } +#endif + #ifndef CONFIG_SOFTMMU tcg_debug_assert(!have_mmap_lock()); #endif From 6932b21d33cb9bbaaa08cd9c06b3a015348e1c91 Mon Sep 17 00:00:00 2001 From: Wenqiang Wei Date: Thu, 20 Aug 2026 20:36:30 +0800 Subject: [PATCH 2/2] LATX, opt: Avoid redundant TOP cleanup on syscall exits INT and syscall translators saved FCSR before register cleanup disabled LBT TOP mode. The longjmp exit therefore saw the saved mode as active and had to clear it again on every traditional syscall. Save FCSR after register cleanup on regular INT, traditional syscall tunnel, and x86-64 SYSCALL exits. This records the disabled mode, avoids the longjmp slow path, and removes an unused FCSR save from the optimized syscall tunnel. Tests: - Run 20,000 x87 signal deliveries with no failures - Run 2,000 Wine/.NET external-GC float round trips Signed-off-by: Wenqiang Wei --- target/i386/latx/translator/tr-misc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/i386/latx/translator/tr-misc.c b/target/i386/latx/translator/tr-misc.c index 12c9e8b663..e7747f962e 100644 --- a/target/i386/latx/translator/tr-misc.c +++ b/target/i386/latx/translator/tr-misc.c @@ -1244,6 +1244,7 @@ bool translate_int_syscall(IR1_INST *pir1) la_label(label_traditionanl); /* 1. store gpr as they will be used in do_syscall */ tr_save_registers_to_env(0xff, 0, 0, options_to_save()); + tr_save_fcsr_to_env(); /* 2. store intno to CPUState */ IR2_OPND intno = ra_alloc_itemp(); @@ -1297,13 +1298,13 @@ bool translate_int(IR1_INST *pir1) tr_save_x64_8_registers_to_env(0xff, option_save_xmm); la_label(not_64); #endif - tr_save_fcsr_to_env(); #ifdef CONFIG_LATX_SYSCALL_TUNNEL if (ir1_get_opnd(pir1, 0)->imm == 0x80) { return translate_int_syscall(pir1); } #endif tr_save_registers_to_env(0xff, 0xff, 0xff, options_to_save()); + tr_save_fcsr_to_env(); /* * store intno to CPUState */ IR2_OPND intno = ra_alloc_itemp(); @@ -1342,9 +1343,9 @@ bool translate_syscall(IR1_INST *pir1) #else bool translate_syscall(IR1_INST *pir1) { - tr_save_fcsr_to_env(); tr_save_registers_to_env(0xff, 0xff, 0xff, options_to_save()); tr_save_x64_8_registers_to_env(0xff, 0xff); + tr_save_fcsr_to_env(); /* store exception index(EXCP_SYSCALL) to CPUState */ IR2_OPND exception_index = ra_alloc_itemp();