The Xtensa FCR rounding-mode (RM) bits are not held only in
env->uregs[FCR]: they are also reflected into env->fp_status via
set_float_rounding_mode().  Similarly, FSR exception flags are mirrored
into env->fp_status.  Every write to FCR/FSR goes through
helper_wur_fpu{2k,}_fcr / helper_wur_fpu_fsr which keep the two in sync.

restore_sigcontext() previously wrote nothing back for xtregs, leaving
env->fp_status with whatever rounding mode and exception flags the
signal handler last installed.  Any FP instruction in the interrupted
code could then operate with the wrong mode.

Add a minimal target_xtensa_xtregs sub-frame carrying FCR and FSR.
setup_sigcontext() saves the current values and points sc_xtregs at the
sub-frame when XTENSA_OPTION_FP_COPROCESSOR or
XTENSA_OPTION_DFP_COPROCESSOR is present (both use env->fp_status driven
by FCR/FSR). restore_sigcontext() reads sc_xtregs; if non-zero it
restores FCR and FSR through cpu_set_fcr()/cpu_set_fsr() so fp_status
round-trips correctly across signal delivery.

Signed-off-by: Matt Turner <[email protected]>
---
 linux-user/xtensa/signal.c | 36 +++++++++++++++++++++++++++++++-----
 1 file changed, 31 insertions(+), 5 deletions(-)

diff --git ./linux-user/xtensa/signal.c ./linux-user/xtensa/signal.c
index ef8b0c3a27..fef23952b7 100644
--- ./linux-user/xtensa/signal.c
+++ ./linux-user/xtensa/signal.c
@@ -21,6 +21,7 @@
 #include "user-internals.h"
 #include "signal-common.h"
 #include "linux-user/trace.h"
+#include "target/xtensa/cpu.h"
 
 struct target_sigcontext {
     abi_ulong sc_pc;
@@ -43,10 +44,15 @@ struct target_ucontext {
     target_sigset_t tuc_sigmask;
 };
 
+struct target_xtensa_xtregs {
+    uint32_t fcr;
+    uint32_t fsr;
+};
+
 struct target_rt_sigframe {
     target_siginfo_t info;
     struct target_ucontext uc;
-    /* TODO: xtregs */
+    struct target_xtensa_xtregs xtregs;
     uint8_t retcode[6];
     abi_ulong window[4];
 };
@@ -107,6 +113,7 @@ static int flush_window_regs(CPUXtensaState *env)
 }
 
 static int setup_sigcontext(struct target_rt_sigframe *frame,
+                            abi_ulong frame_addr,
                             CPUXtensaState *env)
 {
     struct target_sigcontext *sc = &frame->uc.tuc_mcontext;
@@ -123,8 +130,15 @@ static int setup_sigcontext(struct target_rt_sigframe 
*frame,
     for (i = 0; i < 16; ++i) {
         __put_user(env->regs[i], sc->sc_a + i);
     }
-    __put_user(0, &sc->sc_xtregs);
-    /* TODO: xtregs */
+    if (xtensa_option_enabled(env->config, XTENSA_OPTION_FP_COPROCESSOR) ||
+        xtensa_option_enabled(env->config, XTENSA_OPTION_DFP_COPROCESSOR)) {
+        __put_user(env->uregs[FCR], &frame->xtregs.fcr);
+        __put_user(cpu_get_fsr(env), &frame->xtregs.fsr);
+        __put_user(frame_addr + offsetof(struct target_rt_sigframe, xtregs),
+                   &sc->sc_xtregs);
+    } else {
+        __put_user(0, &sc->sc_xtregs);
+    }
     return 1;
 }
 
@@ -190,7 +204,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
     __put_user(0, &frame->uc.tuc_flags);
     __put_user(0, &frame->uc.tuc_link);
     target_save_altstack(&frame->uc.tuc_stack, env);
-    if (!setup_sigcontext(frame, env)) {
+    if (!setup_sigcontext(frame, frame_addr, env)) {
         unlock_user_struct(frame, frame_addr, 0);
         goto give_sigsegv;
     }
@@ -266,7 +280,19 @@ static void restore_sigcontext(CPUXtensaState *env,
     for (i = 0; i < 16; ++i) {
         __get_user(env->regs[i], sc->sc_a + i);
     }
-    /* TODO: xtregs */
+    {
+        abi_ulong xtregs_addr;
+
+        __get_user(xtregs_addr, &sc->sc_xtregs);
+        if (xtregs_addr) {
+            uint32_t fcr, fsr;
+
+            __get_user(fcr, &frame->xtregs.fcr);
+            __get_user(fsr, &frame->xtregs.fsr);
+            cpu_set_fcr(env, fcr);
+            cpu_set_fsr(env, fsr);
+        }
+    }
 }
 
 long do_rt_sigreturn(CPUXtensaState *env)
-- 
2.53.0


Reply via email to