Commit 6858e3a71c for qemu.org
commit 6858e3a71cc41510937bec0950eb4e42e33ba5f2
Author: Matt Turner <mattst88@gmail.com>
Date: Fri Jun 12 10:14:06 2026 -0400
linux-user/xtensa: save/restore FP registers across signal delivery
Add support for saving and restoring f0-f15 across signal delivery.
The target_xtensa_xtregs_fp struct carries 32-bit f-regs for cores
with XTENSA_OPTION_FP_COPROCESSOR; target_xtensa_xtregs_dfp carries
64-bit f-regs for cores with XTENSA_OPTION_DFP_COPROCESSOR.
Lock the xtregs region via lock_user before reading on sigreturn,
since sc_xtregs is a user-space pointer that may lie outside the
locked sigframe.
Signed-off-by: Matt Turner <mattst88@gmail.com>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Helge Deller <deller@gmx.de>
diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c
index ef8b0c3a27..e3f9da322b 100644
--- a/linux-user/xtensa/signal.c
+++ b/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,25 @@ struct target_ucontext {
target_sigset_t tuc_sigmask;
};
+struct target_xtensa_xtregs_fp {
+ uint32_t f[16];
+ uint32_t fcr;
+ uint32_t fsr;
+};
+
+struct target_xtensa_xtregs_dfp {
+ uint64_t f[16];
+ uint32_t fcr;
+ uint32_t fsr;
+};
+
struct target_rt_sigframe {
target_siginfo_t info;
struct target_ucontext uc;
- /* TODO: xtregs */
+ union {
+ struct target_xtensa_xtregs_fp fp;
+ struct target_xtensa_xtregs_dfp dfp;
+ } xtregs;
uint8_t retcode[6];
abi_ulong window[4];
};
@@ -107,6 +123,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 +140,25 @@ 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_DFP_COPROCESSOR)) {
+ for (i = 0; i < 16; ++i) {
+ __put_user(env->fregs[i].f64, &frame->xtregs.dfp.f[i]);
+ }
+ __put_user(env->uregs[FCR], &frame->xtregs.dfp.fcr);
+ __put_user(cpu_get_fsr(env), &frame->xtregs.dfp.fsr);
+ __put_user(frame_addr + offsetof(struct target_rt_sigframe, xtregs),
+ &sc->sc_xtregs);
+ } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_FP_COPROCESSOR)) {
+ for (i = 0; i < 16; ++i) {
+ __put_user(env->fregs[i].f32[FP_F32_LOW], &frame->xtregs.fp.f[i]);
+ }
+ __put_user(env->uregs[FCR], &frame->xtregs.fp.fcr);
+ __put_user(cpu_get_fsr(env), &frame->xtregs.fp.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 +224,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;
}
@@ -243,8 +277,8 @@ give_sigsegv:
force_sigsegv(sig);
}
-static void restore_sigcontext(CPUXtensaState *env,
- struct target_rt_sigframe *frame)
+static int restore_sigcontext(CPUXtensaState *env,
+ struct target_rt_sigframe *frame)
{
struct target_sigcontext *sc = &frame->uc.tuc_mcontext;
uint32_t ps;
@@ -266,7 +300,51 @@ 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) {
+ if (xtensa_option_enabled(env->config,
+ XTENSA_OPTION_DFP_COPROCESSOR)) {
+ struct target_xtensa_xtregs_dfp *xtregs;
+ uint32_t fcr, fsr;
+
+ xtregs = lock_user(VERIFY_READ, xtregs_addr,
+ sizeof(*xtregs), 1);
+ if (!xtregs) {
+ return 0;
+ }
+ for (i = 0; i < 16; ++i) {
+ __get_user(env->fregs[i].f64, &xtregs->f[i]);
+ }
+ __get_user(fcr, &xtregs->fcr);
+ __get_user(fsr, &xtregs->fsr);
+ unlock_user(xtregs, xtregs_addr, 0);
+ cpu_set_fcr(env, fcr);
+ cpu_set_fsr(env, fsr);
+ } else if (xtensa_option_enabled(env->config,
+ XTENSA_OPTION_FP_COPROCESSOR)) {
+ struct target_xtensa_xtregs_fp *xtregs;
+ uint32_t fcr, fsr;
+
+ xtregs = lock_user(VERIFY_READ, xtregs_addr,
+ sizeof(*xtregs), 1);
+ if (!xtregs) {
+ return 0;
+ }
+ for (i = 0; i < 16; ++i) {
+ __get_user(env->fregs[i].f32[FP_F32_LOW], &xtregs->f[i]);
+ }
+ __get_user(fcr, &xtregs->fcr);
+ __get_user(fsr, &xtregs->fsr);
+ unlock_user(xtregs, xtregs_addr, 0);
+ cpu_set_fcr(env, fcr);
+ cpu_set_fsr(env, fsr);
+ }
+ }
+ }
+ return 1;
}
long do_rt_sigreturn(CPUXtensaState *env)
@@ -282,7 +360,9 @@ long do_rt_sigreturn(CPUXtensaState *env)
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
set_sigmask(&set);
- restore_sigcontext(env, frame);
+ if (!restore_sigcontext(env, frame)) {
+ goto badframe;
+ }
target_restore_altstack(&frame->uc.tuc_stack, env);
unlock_user_struct(frame, frame_addr, 0);