From 84b920ccb5ee5287747af2d36d1ece6367b6a40e Mon Sep 17 00:00:00 2001 From: Matt Turner Date: Mon, 25 May 2026 11:24:27 -0400 Subject: [PATCH] linux-user/mips: save/restore FCSR across signal delivery QEMU keeps the MIPS FPU control/status register (FCSR, fcr31) in env->active_fpu.fcr31. The rounding mode, flush-to-zero (FS), and NaN-2008 mode bits in fcr31 are reflected into the derived env->active_fpu.fp_status via set_float_rounding_mode() and friends; every architectural write to FCSR goes through helper_ctc1() which calls restore_fp_status() to keep the two in sync. Both target_sigcontext variants (O32 and N32/N64) have an sc_fpc_csr field that holds FCSR, but setup_sigcontext() never wrote it and restore_sigcontext() never read it. As a result: - The signal frame always delivered sc_fpc_csr == 0 to the handler, so sigaction(SA_SIGINFO) handlers that inspect the interrupted context see the wrong FCSR. - On sigreturn, active_fpu.fcr31 retained whatever value the signal handler last installed (if any), and active_fpu.fp_status was never resynced. Interrupted code resumed with the wrong rounding mode, FS flag, and NaN-2008 semantics. Fix setup_sigcontext() to save fcr31 into sc_fpc_csr. Fix restore_sigcontext() to read it back (masked to fcr31_rw_bitmask as the kernel does) and call cpu_mips_restore_fp_status() to resync fp_status from the restored fcr31. Add cpu_mips_restore_fp_status() in target/mips/fpu.c (which already defines ieee_rm and includes fpu_helper.h), and declare it in cpu.h. Fixes: 084d0497a0 ("mips-linux-user: Save and restore fpu and dsp from sigcontext") Cc: qemu-stable@nongnu.org Signed-off-by: Matt Turner Signed-off-by: Helge Deller --- linux-user/mips/signal.c | 7 +++++++ target/mips/cpu.h | 3 +++ target/mips/fpu.c | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/linux-user/mips/signal.c b/linux-user/mips/signal.c index d69a5d73dd..1b10012726 100644 --- a/linux-user/mips/signal.c +++ b/linux-user/mips/signal.c @@ -134,6 +134,7 @@ static inline void setup_sigcontext(CPUMIPSState *regs, for (i = 0; i < 32; ++i) { __put_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); } + __put_user(regs->active_fpu.fcr31, &sc->sc_fpc_csr); } static inline void @@ -165,6 +166,12 @@ restore_sigcontext(CPUMIPSState *regs, struct target_sigcontext *sc) for (i = 0; i < 32; ++i) { __get_user(regs->active_fpu.fpr[i].d, &sc->sc_fpregs[i]); } + { + uint32_t fcr31; + __get_user(fcr31, &sc->sc_fpc_csr); + regs->active_fpu.fcr31 = fcr31 & regs->active_fpu.fcr31_rw_bitmask; + cpu_mips_restore_fp_status(regs); + } } /* diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 346713705a..392406aff8 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -1384,6 +1384,9 @@ void cpu_mips_clock_init(MIPSCPU *cpu); /* helper.c */ target_ulong exception_resume_pc(CPUMIPSState *env); +/* fpu.c */ +void cpu_mips_restore_fp_status(CPUMIPSState *env); + /** * mips_cpu_create_with_clock: * @typename: a MIPS CPU type. diff --git a/target/mips/fpu.c b/target/mips/fpu.c index c7c487c1f9..8b661865ca 100644 --- a/target/mips/fpu.c +++ b/target/mips/fpu.c @@ -17,6 +17,11 @@ const FloatRoundMode ieee_rm[4] = { float_round_down }; +void cpu_mips_restore_fp_status(CPUMIPSState *env) +{ + restore_fp_status(env); +} + const char fregnames[32][4] = { "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",