]> git.proxmox.com Git - mirror_qemu.git/blob - linux-user/loongarch64/signal.c
39ea82c8140207b18c09eea9e277efd0dfd5e89e
[mirror_qemu.git] / linux-user / loongarch64 / signal.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3 * LoongArch emulation of Linux signals
4 *
5 * Copyright (c) 2021 Loongson Technology Corporation Limited
6 */
7
8 #include "qemu/osdep.h"
9 #include "qemu.h"
10 #include "user-internals.h"
11 #include "signal-common.h"
12 #include "linux-user/trace.h"
13 #include "target/loongarch/internals.h"
14 #include "target/loongarch/vec.h"
15 #include "vdso-asmoffset.h"
16
17 /* FP context was used */
18 #define SC_USED_FP (1 << 0)
19
20 struct target_sigcontext {
21 abi_ulong sc_pc;
22 abi_ulong sc_regs[32];
23 abi_uint sc_flags;
24 abi_ulong sc_extcontext[0] QEMU_ALIGNED(16);
25 };
26
27 QEMU_BUILD_BUG_ON(sizeof(struct target_sigcontext) != sizeof_sigcontext);
28 QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_pc)
29 != offsetof_sigcontext_pc);
30 QEMU_BUILD_BUG_ON(offsetof(struct target_sigcontext, sc_regs)
31 != offsetof_sigcontext_gr);
32
33 #define FPU_CTX_MAGIC 0x46505501
34 #define FPU_CTX_ALIGN 8
35 struct target_fpu_context {
36 abi_ulong regs[32];
37 abi_ulong fcc;
38 abi_uint fcsr;
39 } QEMU_ALIGNED(FPU_CTX_ALIGN);
40
41 QEMU_BUILD_BUG_ON(offsetof(struct target_fpu_context, regs)
42 != offsetof_fpucontext_fr);
43
44 #define LSX_CTX_MAGIC 0x53580001
45 #define LSX_CTX_ALIGN 16
46 struct target_lsx_context {
47 abi_ulong regs[2 * 32];
48 abi_ulong fcc;
49 abi_uint fcsr;
50 } QEMU_ALIGNED(LSX_CTX_ALIGN);
51
52 #define LASX_CTX_MAGIC 0x41535801
53 #define LASX_CTX_ALIGN 32
54 struct target_lasx_context {
55 abi_ulong regs[4 * 32];
56 abi_ulong fcc;
57 abi_uint fcsr;
58 } QEMU_ALIGNED(LASX_CTX_ALIGN);
59
60 #define CONTEXT_INFO_ALIGN 16
61 struct target_sctx_info {
62 abi_uint magic;
63 abi_uint size;
64 abi_ulong padding;
65 } QEMU_ALIGNED(CONTEXT_INFO_ALIGN);
66
67 QEMU_BUILD_BUG_ON(sizeof(struct target_sctx_info) != sizeof_sctx_info);
68
69 struct target_ucontext {
70 abi_ulong tuc_flags;
71 abi_ptr tuc_link;
72 target_stack_t tuc_stack;
73 target_sigset_t tuc_sigmask;
74 uint8_t __unused[1024 / 8 - sizeof(target_sigset_t)];
75 struct target_sigcontext tuc_mcontext;
76 };
77
78 struct target_rt_sigframe {
79 struct target_siginfo rs_info;
80 struct target_ucontext rs_uc;
81 };
82
83 QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe)
84 != sizeof_rt_sigframe);
85 QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, rs_uc.tuc_mcontext)
86 != offsetof_sigcontext);
87
88 /*
89 * These two structures are not present in guest memory, are private
90 * to the signal implementation, but are largely copied from the
91 * kernel's signal implementation.
92 */
93 struct ctx_layout {
94 void *haddr;
95 abi_ptr gaddr;
96 unsigned int size;
97 };
98
99 struct extctx_layout {
100 unsigned long size;
101 unsigned int flags;
102 struct ctx_layout fpu;
103 struct ctx_layout lsx;
104 struct ctx_layout lasx;
105 struct ctx_layout end;
106 };
107
108 static abi_ptr extframe_alloc(struct extctx_layout *extctx,
109 struct ctx_layout *sctx, unsigned size,
110 unsigned align, abi_ptr orig_sp)
111 {
112 abi_ptr sp = orig_sp;
113
114 sp -= sizeof(struct target_sctx_info) + size;
115 align = MAX(align, CONTEXT_INFO_ALIGN);
116 sp = ROUND_DOWN(sp, align);
117 sctx->gaddr = sp;
118
119 size = orig_sp - sp;
120 sctx->size = size;
121 extctx->size += size;
122
123 return sp;
124 }
125
126 static abi_ptr setup_extcontext(CPULoongArchState *env,
127 struct extctx_layout *extctx, abi_ptr sp)
128 {
129 memset(extctx, 0, sizeof(struct extctx_layout));
130
131 /* Grow down, alloc "end" context info first. */
132 sp = extframe_alloc(extctx, &extctx->end, 0, CONTEXT_INFO_ALIGN, sp);
133
134 /* For qemu, there is no lazy fp context switch, so fp always present. */
135 extctx->flags = SC_USED_FP;
136
137 if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) {
138 sp = extframe_alloc(extctx, &extctx->lasx,
139 sizeof(struct target_lasx_context), LASX_CTX_ALIGN, sp);
140 } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) {
141 sp = extframe_alloc(extctx, &extctx->lsx,
142 sizeof(struct target_lsx_context), LSX_CTX_ALIGN, sp);
143 } else {
144 sp = extframe_alloc(extctx, &extctx->fpu,
145 sizeof(struct target_fpu_context), FPU_CTX_ALIGN, sp);
146 }
147
148 return sp;
149 }
150
151 static void setup_sigframe(CPULoongArchState *env,
152 struct target_sigcontext *sc,
153 struct extctx_layout *extctx)
154 {
155 struct target_sctx_info *info;
156 int i;
157
158 __put_user(extctx->flags, &sc->sc_flags);
159 __put_user(env->pc, &sc->sc_pc);
160 __put_user(0, &sc->sc_regs[0]);
161 for (i = 1; i < 32; ++i) {
162 __put_user(env->gpr[i], &sc->sc_regs[i]);
163 }
164
165 /*
166 * Set extension context
167 */
168
169 if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) {
170 struct target_lasx_context *lasx_ctx;
171 info = extctx->lasx.haddr;
172
173 __put_user(LASX_CTX_MAGIC, &info->magic);
174 __put_user(extctx->lasx.size, &info->size);
175
176 lasx_ctx = (struct target_lasx_context *)(info + 1);
177
178 for (i = 0; i < 32; ++i) {
179 __put_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]);
180 __put_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]);
181 __put_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]);
182 __put_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]);
183 }
184 __put_user(read_fcc(env), &lasx_ctx->fcc);
185 __put_user(env->fcsr0, &lasx_ctx->fcsr);
186 } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) {
187 struct target_lsx_context *lsx_ctx;
188 info = extctx->lsx.haddr;
189
190 __put_user(LSX_CTX_MAGIC, &info->magic);
191 __put_user(extctx->lsx.size, &info->size);
192
193 lsx_ctx = (struct target_lsx_context *)(info + 1);
194
195 for (i = 0; i < 32; ++i) {
196 __put_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]);
197 __put_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]);
198 }
199 __put_user(read_fcc(env), &lsx_ctx->fcc);
200 __put_user(env->fcsr0, &lsx_ctx->fcsr);
201 } else {
202 struct target_fpu_context *fpu_ctx;
203 info = extctx->fpu.haddr;
204
205 __put_user(FPU_CTX_MAGIC, &info->magic);
206 __put_user(extctx->fpu.size, &info->size);
207
208 fpu_ctx = (struct target_fpu_context *)(info + 1);
209
210 for (i = 0; i < 32; ++i) {
211 __put_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]);
212 }
213 __put_user(read_fcc(env), &fpu_ctx->fcc);
214 __put_user(env->fcsr0, &fpu_ctx->fcsr);
215 }
216
217 /*
218 * Set end context
219 */
220 info = extctx->end.haddr;
221 __put_user(0, &info->magic);
222 __put_user(0, &info->size);
223 }
224
225 static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame)
226 {
227 memset(extctx, 0, sizeof(*extctx));
228
229 while (1) {
230 abi_uint magic, size;
231
232 if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) {
233 return false;
234 }
235
236 switch (magic) {
237 case 0: /* END */
238 extctx->end.gaddr = frame;
239 extctx->end.size = size;
240 extctx->size += size;
241 return true;
242
243 case FPU_CTX_MAGIC:
244 if (size < (sizeof(struct target_sctx_info) +
245 sizeof(struct target_fpu_context))) {
246 return false;
247 }
248 extctx->fpu.gaddr = frame;
249 extctx->fpu.size = size;
250 extctx->size += size;
251 break;
252 case LSX_CTX_MAGIC:
253 if (size < (sizeof(struct target_sctx_info) +
254 sizeof(struct target_lsx_context))) {
255 return false;
256 }
257 extctx->lsx.gaddr = frame;
258 extctx->lsx.size = size;
259 extctx->size += size;
260 break;
261 case LASX_CTX_MAGIC:
262 if (size < (sizeof(struct target_sctx_info) +
263 sizeof(struct target_lasx_context))) {
264 return false;
265 }
266 extctx->lasx.gaddr = frame;
267 extctx->lasx.size = size;
268 extctx->size += size;
269 break;
270 default:
271 return false;
272 }
273
274 frame += size;
275 }
276 }
277
278 static void restore_sigframe(CPULoongArchState *env,
279 struct target_sigcontext *sc,
280 struct extctx_layout *extctx)
281 {
282 int i;
283 abi_ulong fcc;
284
285 __get_user(env->pc, &sc->sc_pc);
286 for (i = 1; i < 32; ++i) {
287 __get_user(env->gpr[i], &sc->sc_regs[i]);
288 }
289
290 if (extctx->lasx.haddr) {
291 struct target_lasx_context *lasx_ctx =
292 extctx->lasx.haddr + sizeof(struct target_sctx_info);
293
294 for (i = 0; i < 32; ++i) {
295 __get_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]);
296 __get_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]);
297 __get_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]);
298 __get_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]);
299 }
300 __get_user(fcc, &lasx_ctx->fcc);
301 write_fcc(env, fcc);
302 __get_user(env->fcsr0, &lasx_ctx->fcsr);
303 restore_fp_status(env);
304 } else if (extctx->lsx.haddr) {
305 struct target_lsx_context *lsx_ctx =
306 extctx->lsx.haddr + sizeof(struct target_sctx_info);
307
308 for (i = 0; i < 32; ++i) {
309 __get_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]);
310 __get_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]);
311 }
312 __get_user(fcc, &lsx_ctx->fcc);
313 write_fcc(env, fcc);
314 __get_user(env->fcsr0, &lsx_ctx->fcsr);
315 restore_fp_status(env);
316 } else if (extctx->fpu.haddr) {
317 struct target_fpu_context *fpu_ctx =
318 extctx->fpu.haddr + sizeof(struct target_sctx_info);
319
320 for (i = 0; i < 32; ++i) {
321 __get_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]);
322 }
323 __get_user(fcc, &fpu_ctx->fcc);
324 write_fcc(env, fcc);
325 __get_user(env->fcsr0, &fpu_ctx->fcsr);
326 restore_fp_status(env);
327 }
328 }
329
330 /*
331 * Determine which stack to use.
332 */
333 static abi_ptr get_sigframe(struct target_sigaction *ka,
334 CPULoongArchState *env,
335 struct extctx_layout *extctx)
336 {
337 abi_ulong sp;
338
339 sp = target_sigsp(get_sp_from_cpustate(env), ka);
340 sp = ROUND_DOWN(sp, 16);
341 sp = setup_extcontext(env, extctx, sp);
342 sp -= sizeof(struct target_rt_sigframe);
343
344 assert(QEMU_IS_ALIGNED(sp, 16));
345
346 return sp;
347 }
348
349 void setup_rt_frame(int sig, struct target_sigaction *ka,
350 target_siginfo_t *info,
351 target_sigset_t *set, CPULoongArchState *env)
352 {
353 struct target_rt_sigframe *frame;
354 struct extctx_layout extctx;
355 abi_ptr frame_addr;
356 int i;
357
358 frame_addr = get_sigframe(ka, env, &extctx);
359 trace_user_setup_rt_frame(env, frame_addr);
360
361 frame = lock_user(VERIFY_WRITE, frame_addr,
362 sizeof(*frame) + extctx.size, 0);
363 if (!frame) {
364 force_sigsegv(sig);
365 return;
366 }
367
368 if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE)) {
369 extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr);
370 extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
371 } else if (FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE)) {
372 extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr);
373 extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
374 } else {
375 extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
376 extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
377 }
378
379 tswap_siginfo(&frame->rs_info, info);
380
381 __put_user(0, &frame->rs_uc.tuc_flags);
382 __put_user(0, &frame->rs_uc.tuc_link);
383 target_save_altstack(&frame->rs_uc.tuc_stack, env);
384
385 setup_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx);
386
387 for (i = 0; i < TARGET_NSIG_WORDS; i++) {
388 __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
389 }
390
391 env->gpr[4] = sig;
392 env->gpr[5] = frame_addr + offsetof(struct target_rt_sigframe, rs_info);
393 env->gpr[6] = frame_addr + offsetof(struct target_rt_sigframe, rs_uc);
394 env->gpr[3] = frame_addr;
395 env->gpr[1] = default_rt_sigreturn;
396
397 env->pc = ka->_sa_handler;
398 unlock_user(frame, frame_addr, sizeof(*frame) + extctx.size);
399 }
400
401 long do_rt_sigreturn(CPULoongArchState *env)
402 {
403 struct target_rt_sigframe *frame;
404 struct extctx_layout extctx;
405 abi_ulong frame_addr;
406 sigset_t blocked;
407
408 frame_addr = env->gpr[3];
409 trace_user_do_rt_sigreturn(env, frame_addr);
410
411 if (!parse_extcontext(&extctx, frame_addr + sizeof(*frame))) {
412 goto badframe;
413 }
414
415 frame = lock_user(VERIFY_READ, frame_addr,
416 sizeof(*frame) + extctx.size, 1);
417 if (!frame) {
418 goto badframe;
419 }
420
421 if (extctx.lasx.gaddr) {
422 extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr);
423 } else if (extctx.lsx.gaddr) {
424 extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr);
425 } else if (extctx.fpu.gaddr) {
426 extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
427 }
428
429 target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
430 set_sigmask(&blocked);
431
432 restore_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx);
433
434 target_restore_altstack(&frame->rs_uc.tuc_stack, env);
435
436 unlock_user(frame, frame_addr, 0);
437 return -QEMU_ESIGRETURN;
438
439 badframe:
440 force_sig(TARGET_SIGSEGV);
441 return -QEMU_ESIGRETURN;
442 }
443
444 void setup_sigtramp(abi_ulong sigtramp_page)
445 {
446 uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0);
447 assert(tramp != NULL);
448
449 __put_user(0x03822c0b, tramp + 0); /* ori a7, zero, 0x8b */
450 __put_user(0x002b0000, tramp + 1); /* syscall 0 */
451
452 default_rt_sigreturn = sigtramp_page;
453 unlock_user(tramp, sigtramp_page, 8);
454 }