master
c 458 lines 13.7 KB
Raw
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 CPUSysState *sys = env_sys(env);
130
131 memset(extctx, 0, sizeof(struct extctx_layout));
132
133 /* Grow down, alloc "end" context info first. */
134 sp = extframe_alloc(extctx, &extctx->end, 0, CONTEXT_INFO_ALIGN, sp);
135
136 /* For qemu, there is no lazy fp context switch, so fp always present. */
137 extctx->flags = SC_USED_FP;
138
139 if (FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, ASXE)) {
140 sp = extframe_alloc(extctx, &extctx->lasx,
141 sizeof(struct target_lasx_context), LASX_CTX_ALIGN, sp);
142 } else if (FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, SXE)) {
143 sp = extframe_alloc(extctx, &extctx->lsx,
144 sizeof(struct target_lsx_context), LSX_CTX_ALIGN, sp);
145 } else {
146 sp = extframe_alloc(extctx, &extctx->fpu,
147 sizeof(struct target_fpu_context), FPU_CTX_ALIGN, sp);
148 }
149
150 return sp;
151 }
152
153 static void setup_sigframe(CPULoongArchState *env,
154 struct target_sigcontext *sc,
155 struct extctx_layout *extctx)
156 {
157 CPUSysState *sys = env_sys(env);
158 struct target_sctx_info *info;
159 int i;
160
161 __put_user(extctx->flags, &sc->sc_flags);
162 __put_user(env->pc, &sc->sc_pc);
163 __put_user(0, &sc->sc_regs[0]);
164 for (i = 1; i < 32; ++i) {
165 __put_user(env->gpr[i], &sc->sc_regs[i]);
166 }
167
168 /*
169 * Set extension context
170 */
171
172 if (FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, ASXE)) {
173 struct target_lasx_context *lasx_ctx;
174 info = extctx->lasx.haddr;
175
176 __put_user(LASX_CTX_MAGIC, &info->magic);
177 __put_user(extctx->lasx.size, &info->size);
178
179 lasx_ctx = (struct target_lasx_context *)(info + 1);
180
181 for (i = 0; i < 32; ++i) {
182 __put_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]);
183 __put_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]);
184 __put_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]);
185 __put_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]);
186 }
187 __put_user(read_fcc(env), &lasx_ctx->fcc);
188 __put_user(env->fcsr0, &lasx_ctx->fcsr);
189 } else if (FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, SXE)) {
190 struct target_lsx_context *lsx_ctx;
191 info = extctx->lsx.haddr;
192
193 __put_user(LSX_CTX_MAGIC, &info->magic);
194 __put_user(extctx->lsx.size, &info->size);
195
196 lsx_ctx = (struct target_lsx_context *)(info + 1);
197
198 for (i = 0; i < 32; ++i) {
199 __put_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]);
200 __put_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]);
201 }
202 __put_user(read_fcc(env), &lsx_ctx->fcc);
203 __put_user(env->fcsr0, &lsx_ctx->fcsr);
204 } else {
205 struct target_fpu_context *fpu_ctx;
206 info = extctx->fpu.haddr;
207
208 __put_user(FPU_CTX_MAGIC, &info->magic);
209 __put_user(extctx->fpu.size, &info->size);
210
211 fpu_ctx = (struct target_fpu_context *)(info + 1);
212
213 for (i = 0; i < 32; ++i) {
214 __put_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]);
215 }
216 __put_user(read_fcc(env), &fpu_ctx->fcc);
217 __put_user(env->fcsr0, &fpu_ctx->fcsr);
218 }
219
220 /*
221 * Set end context
222 */
223 info = extctx->end.haddr;
224 __put_user(0, &info->magic);
225 __put_user(0, &info->size);
226 }
227
228 static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame)
229 {
230 memset(extctx, 0, sizeof(*extctx));
231
232 while (1) {
233 abi_uint magic, size;
234
235 if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) {
236 return false;
237 }
238
239 switch (magic) {
240 case 0: /* END */
241 extctx->end.gaddr = frame;
242 extctx->end.size = size;
243 extctx->size += size;
244 return true;
245
246 case FPU_CTX_MAGIC:
247 if (size < (sizeof(struct target_sctx_info) +
248 sizeof(struct target_fpu_context))) {
249 return false;
250 }
251 extctx->fpu.gaddr = frame;
252 extctx->fpu.size = size;
253 extctx->size += size;
254 break;
255 case LSX_CTX_MAGIC:
256 if (size < (sizeof(struct target_sctx_info) +
257 sizeof(struct target_lsx_context))) {
258 return false;
259 }
260 extctx->lsx.gaddr = frame;
261 extctx->lsx.size = size;
262 extctx->size += size;
263 break;
264 case LASX_CTX_MAGIC:
265 if (size < (sizeof(struct target_sctx_info) +
266 sizeof(struct target_lasx_context))) {
267 return false;
268 }
269 extctx->lasx.gaddr = frame;
270 extctx->lasx.size = size;
271 extctx->size += size;
272 break;
273 default:
274 return false;
275 }
276
277 frame += size;
278 }
279 }
280
281 static void restore_sigframe(CPULoongArchState *env,
282 struct target_sigcontext *sc,
283 struct extctx_layout *extctx)
284 {
285 int i;
286 abi_ulong fcc;
287
288 __get_user(env->pc, &sc->sc_pc);
289 for (i = 1; i < 32; ++i) {
290 __get_user(env->gpr[i], &sc->sc_regs[i]);
291 }
292
293 if (extctx->lasx.haddr) {
294 struct target_lasx_context *lasx_ctx =
295 extctx->lasx.haddr + sizeof(struct target_sctx_info);
296
297 for (i = 0; i < 32; ++i) {
298 __get_user(env->fpr[i].vreg.UD(0), &lasx_ctx->regs[4 * i]);
299 __get_user(env->fpr[i].vreg.UD(1), &lasx_ctx->regs[4 * i + 1]);
300 __get_user(env->fpr[i].vreg.UD(2), &lasx_ctx->regs[4 * i + 2]);
301 __get_user(env->fpr[i].vreg.UD(3), &lasx_ctx->regs[4 * i + 3]);
302 }
303 __get_user(fcc, &lasx_ctx->fcc);
304 write_fcc(env, fcc);
305 __get_user(env->fcsr0, &lasx_ctx->fcsr);
306 restore_fp_status(env);
307 } else if (extctx->lsx.haddr) {
308 struct target_lsx_context *lsx_ctx =
309 extctx->lsx.haddr + sizeof(struct target_sctx_info);
310
311 for (i = 0; i < 32; ++i) {
312 __get_user(env->fpr[i].vreg.UD(0), &lsx_ctx->regs[2 * i]);
313 __get_user(env->fpr[i].vreg.UD(1), &lsx_ctx->regs[2 * i + 1]);
314 }
315 __get_user(fcc, &lsx_ctx->fcc);
316 write_fcc(env, fcc);
317 __get_user(env->fcsr0, &lsx_ctx->fcsr);
318 restore_fp_status(env);
319 } else if (extctx->fpu.haddr) {
320 struct target_fpu_context *fpu_ctx =
321 extctx->fpu.haddr + sizeof(struct target_sctx_info);
322
323 for (i = 0; i < 32; ++i) {
324 __get_user(env->fpr[i].vreg.UD(0), &fpu_ctx->regs[i]);
325 }
326 __get_user(fcc, &fpu_ctx->fcc);
327 write_fcc(env, fcc);
328 __get_user(env->fcsr0, &fpu_ctx->fcsr);
329 restore_fp_status(env);
330 }
331 }
332
333 /*
334 * Determine which stack to use.
335 */
336 static abi_ptr get_sigframe(struct target_sigaction *ka,
337 CPULoongArchState *env,
338 struct extctx_layout *extctx)
339 {
340 abi_ulong sp;
341
342 sp = target_sigsp(get_sp_from_cpustate(env), ka);
343 sp = ROUND_DOWN(sp, 16);
344 sp = setup_extcontext(env, extctx, sp);
345 sp -= sizeof(struct target_rt_sigframe);
346
347 assert(QEMU_IS_ALIGNED(sp, 16));
348
349 return sp;
350 }
351
352 void setup_rt_frame(int sig, struct target_sigaction *ka,
353 target_siginfo_t *info,
354 target_sigset_t *set, CPULoongArchState *env)
355 {
356 CPUSysState *sys = env_sys(env);
357 struct target_rt_sigframe *frame;
358 struct extctx_layout extctx;
359 abi_ptr frame_addr;
360 int i;
361
362 frame_addr = get_sigframe(ka, env, &extctx);
363 trace_user_setup_rt_frame(env, frame_addr);
364
365 frame = lock_user(VERIFY_WRITE, frame_addr,
366 sizeof(*frame) + extctx.size, 0);
367 if (!frame) {
368 force_sigsegv(sig);
369 return;
370 }
371
372 if (FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, ASXE)) {
373 extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr);
374 extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
375 } else if (FIELD_EX64(sys->CSR_EUEN, CSR_EUEN, SXE)) {
376 extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr);
377 extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
378 } else {
379 extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
380 extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
381 }
382
383 frame->rs_info = *info;
384
385 __put_user(0, &frame->rs_uc.tuc_flags);
386 __put_user(0, &frame->rs_uc.tuc_link);
387 target_save_altstack(&frame->rs_uc.tuc_stack, env);
388
389 setup_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx);
390
391 for (i = 0; i < TARGET_NSIG_WORDS; i++) {
392 __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
393 }
394
395 env->gpr[4] = sig;
396 env->gpr[5] = frame_addr + offsetof(struct target_rt_sigframe, rs_info);
397 env->gpr[6] = frame_addr + offsetof(struct target_rt_sigframe, rs_uc);
398 env->gpr[3] = frame_addr;
399 env->gpr[1] = default_rt_sigreturn;
400
401 env->pc = ka->_sa_handler;
402 unlock_user(frame, frame_addr, sizeof(*frame) + extctx.size);
403 }
404
405 long do_rt_sigreturn(CPULoongArchState *env)
406 {
407 struct target_rt_sigframe *frame;
408 struct extctx_layout extctx;
409 abi_ulong frame_addr;
410 sigset_t blocked;
411
412 frame_addr = env->gpr[3];
413 trace_user_do_rt_sigreturn(env, frame_addr);
414
415 if (!parse_extcontext(&extctx, frame_addr + sizeof(*frame))) {
416 goto badframe;
417 }
418
419 frame = lock_user(VERIFY_READ, frame_addr,
420 sizeof(*frame) + extctx.size, 1);
421 if (!frame) {
422 goto badframe;
423 }
424
425 if (extctx.lasx.gaddr) {
426 extctx.lasx.haddr = (void *)frame + (extctx.lasx.gaddr - frame_addr);
427 } else if (extctx.lsx.gaddr) {
428 extctx.lsx.haddr = (void *)frame + (extctx.lsx.gaddr - frame_addr);
429 } else if (extctx.fpu.gaddr) {
430 extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
431 }
432
433 target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
434 set_sigmask(&blocked);
435
436 restore_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx);
437
438 target_restore_altstack(&frame->rs_uc.tuc_stack, env);
439
440 unlock_user(frame, frame_addr, 0);
441 return -QEMU_ESIGRETURN;
442
443 badframe:
444 force_sig(TARGET_SIGSEGV);
445 return -QEMU_ESIGRETURN;
446 }
447
448 void setup_sigtramp(abi_ulong sigtramp_page)
449 {
450 uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0);
451 assert(tramp != NULL);
452
453 __put_user(0x03822c0b, tramp + 0); /* ori a7, zero, 0x8b */
454 __put_user(0x002b0000, tramp + 1); /* syscall 0 */
455
456 default_rt_sigreturn = sigtramp_page;
457 unlock_user(tramp, sigtramp_page, 8);
458 }