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>
This commit is contained in:
Matt Turner
2026-06-12 10:14:06 -04:00
committed by Helge Deller
parent 7e859bacea
commit 6858e3a71c

View File

@@ -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);