[PATCH] linux-user/mips: save/restore FCSR across signal delivery

Matt Turner posted 1 patch 5 days, 1 hour ago
Patches applied successfully (tree, apply log)
git fetch https://github.com/patchew-project/qemu tags/patchew/20260525152427.4120785-1-mattst88@gmail.com
Maintainers: Laurent Vivier <laurent@vivier.eu>, Helge Deller <deller@gmx.de>, Pierrick Bouvier <pierrick.bouvier@oss.qualcomm.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Aurelien Jarno <aurelien@aurel32.net>, Jiaxun Yang <jiaxun.yang@flygoat.com>, Aleksandar Rikalo <arikalo@gmail.com>
linux-user/mips/signal.c | 7 +++++++
target/mips/cpu.h        | 3 +++
target/mips/fpu.c        | 5 +++++
3 files changed, 15 insertions(+)
[PATCH] linux-user/mips: save/restore FCSR across signal delivery
Posted by Matt Turner 5 days, 1 hour ago
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
---
 linux-user/mips/signal.c | 7 +++++++
 target/mips/cpu.h        | 3 +++
 target/mips/fpu.c        | 5 +++++
 3 files changed, 15 insertions(+)

diff --git ./linux-user/mips/signal.c ./linux-user/mips/signal.c
index d69a5d73dd..1b10012726 100644
--- ./linux-user/mips/signal.c
+++ ./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 ./target/mips/cpu.h ./target/mips/cpu.h
index 346713705a..392406aff8 100644
--- ./target/mips/cpu.h
+++ ./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 ./target/mips/fpu.c ./target/mips/fpu.c
index c7c487c1f9..8b661865ca 100644
--- ./target/mips/fpu.c
+++ ./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",
-- 
2.53.0
Re: [PATCH] linux-user/mips: save/restore FCSR across signal delivery
Posted by Matt Turner 4 days, 18 hours ago
On Mon, May 25, 2026 at 11:24 AM Matt Turner <mattst88@gmail.com> wrote:
>
> 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 <mattst88@gmail.com>