From: Matt Turner Date: Fri, 12 Jun 2026 14:14:06 +0000 (-0400) Subject: linux-user/xtensa: save/restore FP registers across signal delivery X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6858e3a71cc41510937bec0950eb4e42e33ba5f2;p=thirdparty%2Fqemu.git 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 Reviewed-by: Richard Henderson Signed-off-by: Helge Deller --- 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);