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