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>
Matt Turner committed
Jun 12, 2026 at 10:14 UTC
6858e3a71cc41510937bec0950eb4e42e33ba5f2
1 file changed
+88
-8
linux-user/xtensa/signal.c
+88
-8
@@ -21,6 +21,7 @@
21
#include "user-internals.h"
22
#include "signal-common.h"
23
#include "linux-user/trace.h"
24
+#include "target/xtensa/cpu.h"
25
26
struct target_sigcontext {
27
abi_ulong sc_pc;
@@ -43,10 +44,25 @@ struct target_ucontext {
44
target_sigset_t tuc_sigmask;
45
};
46
47
+struct target_xtensa_xtregs_fp {
48
+ uint32_t f[16];
49
+ uint32_t fcr;
50
+ uint32_t fsr;
51
+};
52
+
53
+struct target_xtensa_xtregs_dfp {
54
+ uint64_t f[16];
55
+ uint32_t fcr;
56
+ uint32_t fsr;
57
+};
58
+
59
struct target_rt_sigframe {
60
target_siginfo_t info;
61
struct target_ucontext uc;
49
- /* TODO: xtregs */
62
+ union {
63
+ struct target_xtensa_xtregs_fp fp;
64
+ struct target_xtensa_xtregs_dfp dfp;
65
+ } xtregs;
66
uint8_t retcode[6];
67
abi_ulong window[4];
68
};
@@ -107,6 +123,7 @@ static int flush_window_regs(CPUXtensaState *env)
123
}
124
125
static int setup_sigcontext(struct target_rt_sigframe *frame,
126
+ abi_ulong frame_addr,
127
CPUXtensaState *env)
128
{
129
struct target_sigcontext *sc = &frame->uc.tuc_mcontext;
@@ -123,8 +140,25 @@ static int setup_sigcontext(struct target_rt_sigframe *frame,
140
for (i = 0; i < 16; ++i) {
141
__put_user(env->regs[i], sc->sc_a + i);
142
}
126
- __put_user(0, &sc->sc_xtregs);
127
- /* TODO: xtregs */
143
+ if (xtensa_option_enabled(env->config, XTENSA_OPTION_DFP_COPROCESSOR)) {
144
+ for (i = 0; i < 16; ++i) {
145
+ __put_user(env->fregs[i].f64, &frame->xtregs.dfp.f[i]);
146
+ }
147
+ __put_user(env->uregs[FCR], &frame->xtregs.dfp.fcr);
148
+ __put_user(cpu_get_fsr(env), &frame->xtregs.dfp.fsr);
149
+ __put_user(frame_addr + offsetof(struct target_rt_sigframe, xtregs),
150
+ &sc->sc_xtregs);
151
+ } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_FP_COPROCESSOR)) {
152
+ for (i = 0; i < 16; ++i) {
153
+ __put_user(env->fregs[i].f32[FP_F32_LOW], &frame->xtregs.fp.f[i]);
154
+ }
155
+ __put_user(env->uregs[FCR], &frame->xtregs.fp.fcr);
156
+ __put_user(cpu_get_fsr(env), &frame->xtregs.fp.fsr);
157
+ __put_user(frame_addr + offsetof(struct target_rt_sigframe, xtregs),
158
+ &sc->sc_xtregs);
159
+ } else {
160
+ __put_user(0, &sc->sc_xtregs);
161
+ }
162
return 1;
163
}
164
@@ -190,7 +224,7 @@ void setup_rt_frame(int sig, struct target_sigaction *ka,
224
__put_user(0, &frame->uc.tuc_flags);
225
__put_user(0, &frame->uc.tuc_link);
226
target_save_altstack(&frame->uc.tuc_stack, env);
193
- if (!setup_sigcontext(frame, env)) {
227
+ if (!setup_sigcontext(frame, frame_addr, env)) {
228
unlock_user_struct(frame, frame_addr, 0);
229
goto give_sigsegv;
230
}
@@ -243,8 +277,8 @@ give_sigsegv:
277
force_sigsegv(sig);
278
}
279
246
-static void restore_sigcontext(CPUXtensaState *env,
247
- struct target_rt_sigframe *frame)
280
+static int restore_sigcontext(CPUXtensaState *env,
281
+ struct target_rt_sigframe *frame)
282
{
283
struct target_sigcontext *sc = &frame->uc.tuc_mcontext;
284
uint32_t ps;
@@ -266,7 +300,51 @@ static void restore_sigcontext(CPUXtensaState *env,
300
for (i = 0; i < 16; ++i) {
301
__get_user(env->regs[i], sc->sc_a + i);
302
}
269
- /* TODO: xtregs */
303
+ {
304
+ abi_ulong xtregs_addr;
305
+
306
+ __get_user(xtregs_addr, &sc->sc_xtregs);
307
+ if (xtregs_addr) {
308
+ if (xtensa_option_enabled(env->config,
309
+ XTENSA_OPTION_DFP_COPROCESSOR)) {
310
+ struct target_xtensa_xtregs_dfp *xtregs;
311
+ uint32_t fcr, fsr;
312
+
313
+ xtregs = lock_user(VERIFY_READ, xtregs_addr,
314
+ sizeof(*xtregs), 1);
315
+ if (!xtregs) {
316
+ return 0;
317
+ }
318
+ for (i = 0; i < 16; ++i) {
319
+ __get_user(env->fregs[i].f64, &xtregs->f[i]);
320
+ }
321
+ __get_user(fcr, &xtregs->fcr);
322
+ __get_user(fsr, &xtregs->fsr);
323
+ unlock_user(xtregs, xtregs_addr, 0);
324
+ cpu_set_fcr(env, fcr);
325
+ cpu_set_fsr(env, fsr);
326
+ } else if (xtensa_option_enabled(env->config,
327
+ XTENSA_OPTION_FP_COPROCESSOR)) {
328
+ struct target_xtensa_xtregs_fp *xtregs;
329
+ uint32_t fcr, fsr;
330
+
331
+ xtregs = lock_user(VERIFY_READ, xtregs_addr,
332
+ sizeof(*xtregs), 1);
333
+ if (!xtregs) {
334
+ return 0;
335
+ }
336
+ for (i = 0; i < 16; ++i) {
337
+ __get_user(env->fregs[i].f32[FP_F32_LOW], &xtregs->f[i]);
338
+ }
339
+ __get_user(fcr, &xtregs->fcr);
340
+ __get_user(fsr, &xtregs->fsr);
341
+ unlock_user(xtregs, xtregs_addr, 0);
342
+ cpu_set_fcr(env, fcr);
343
+ cpu_set_fsr(env, fsr);
344
+ }
345
+ }
346
+ }
347
+ return 1;
348
}
349
350
long do_rt_sigreturn(CPUXtensaState *env)
@@ -282,7 +360,9 @@ long do_rt_sigreturn(CPUXtensaState *env)
360
target_to_host_sigset(&set, &frame->uc.tuc_sigmask);
361
set_sigmask(&set);
362
285
- restore_sigcontext(env, frame);
363
+ if (!restore_sigcontext(env, frame)) {
364
+ goto badframe;
365
+ }
366
target_restore_altstack(&frame->uc.tuc_stack, env);
367
368
unlock_user_struct(frame, frame_addr, 0);