On Tue, 26 May 2026 00:26:41 +0900,
Matt Turner wrote:
> 
> The SH4 FPSCR rounding-mode (RM) and denormal (DN) bits are not held
> only in env->fpscr: they are also reflected into the derived
> env->fp_status via set_float_rounding_mode()/set_flush_to_zero(). The
> guest keeps the two in sync by routing every write to FPSCR through
> helper_ld_fpscr().
> 
> restore_sigcontext() wrote the saved value straight into env->fpscr and
> never touched env->fp_status, so on sigreturn the interrupted code
> resumed with whatever FP rounding mode and flush-to-zero setting the
> signal handler last installed. (regs->flags = 0 forces the FR/SZ/PR TB
> flags to be recomputed, but fp_status is runtime float state, not a TB
> flag, so it was left stale.) This is the FP analogue of the T/M/Q bit
> problem just fixed for the integer status register.
> 
> Factor the FPSCR -> fp_status synchronisation out of helper_ld_fpscr()
> into cpu_load_fpscr() and use it from restore_sigcontext() so the
> rounding mode round-trips correctly across signal delivery.
> 
> Fixes: c3b5bc8ab3 ("SH4: Signal handling for the user space emulator, by 
> Magnus Damm.")
> Cc: [email protected]
> ---
>  linux-user/sh4/signal.c | 7 ++++++-
>  target/sh4/cpu.h        | 3 +++
>  target/sh4/op_helper.c  | 7 ++++++-
>  3 files changed, 15 insertions(+), 2 deletions(-)
> 
> diff --git ./linux-user/sh4/signal.c ./linux-user/sh4/signal.c
> index cc36425c49..00290d6e40 100644
> --- ./linux-user/sh4/signal.c
> +++ ./linux-user/sh4/signal.c
> @@ -173,7 +173,12 @@ static void restore_sigcontext(CPUSH4State *regs, struct 
> target_sigcontext *sc)
>      for (i=0; i<16; i++) {
>          __get_user(regs->fregs[i], &sc->sc_fpregs[i]);
>      }
> -    __get_user(regs->fpscr, &sc->sc_fpscr);
> +    /* Resync the derived float_status state, not just env->fpscr. */
> +    {
> +        uint32_t fpscr;
> +        __get_user(fpscr, &sc->sc_fpscr);
> +        cpu_load_fpscr(regs, fpscr);
> +    }
>      __get_user(regs->fpul, &sc->sc_fpul);
>  
>      regs->tra = -1;         /* disable syscall checks */
> diff --git ./target/sh4/cpu.h ./target/sh4/cpu.h
> index 4b0f3f6d97..3302702376 100644
> --- ./target/sh4/cpu.h
> +++ ./target/sh4/cpu.h
> @@ -379,4 +379,7 @@ static inline void cpu_write_sr(CPUSH4State *env, 
> uint32_t sr)
>      env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T));
>  }
>  
> +/* Set FPSCR and the derived float_status rounding/flush-to-zero state. */
> +void cpu_load_fpscr(CPUSH4State *env, uint32_t val);
> +
>  #endif /* SH4_CPU_H */
> diff --git ./target/sh4/op_helper.c ./target/sh4/op_helper.c
> index 669bc84cb6..cf0f80e4a5 100644
> --- ./target/sh4/op_helper.c
> +++ ./target/sh4/op_helper.c
> @@ -204,7 +204,7 @@ void helper_macw(CPUSH4State *env, int32_t arg0, int32_t 
> arg1)
>      }
>  }
>  
> -void helper_ld_fpscr(CPUSH4State *env, uint32_t val)
> +void cpu_load_fpscr(CPUSH4State *env, uint32_t val)
>  {
>      env->fpscr = val & FPSCR_MASK;
>      if ((val & FPSCR_RM_MASK) == FPSCR_RM_ZERO) {
> @@ -215,6 +215,11 @@ void helper_ld_fpscr(CPUSH4State *env, uint32_t val)
>      set_flush_to_zero((val & FPSCR_DN) != 0, &env->fp_status);
>  }
>  
> +void helper_ld_fpscr(CPUSH4State *env, uint32_t val)
> +{
> +    cpu_load_fpscr(env, val);
> +}
> +
>  static void update_fpscr(CPUSH4State *env, uintptr_t retaddr)
>  {
>      int xcpt, cause, enable;
> -- 
> 2.53.0
> 

Reviewed-by: Yoshinori Sato <[email protected]>

-- 
Yosinori Sato

Reply via email to