]> git.proxmox.com Git - mirror_qemu.git/blob - linux-user/loongarch64/signal.c
Merge tag 'migration-20231031-pull-request' of https://gitlab.com/juan.quintela/qemu...
[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 uint64_t sc_pc;
22 uint64_t sc_regs[32];
23 uint32_t sc_flags;
24 uint64_t 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 uint64_t regs[32];
37 uint64_t fcc;
38 uint32_t 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 CONTEXT_INFO_ALIGN 16
45 struct target_sctx_info {
46 uint32_t magic;
47 uint32_t size;
48 uint64_t padding;
49 } QEMU_ALIGNED(CONTEXT_INFO_ALIGN);
50
51 QEMU_BUILD_BUG_ON(sizeof(struct target_sctx_info) != sizeof_sctx_info);
52
53 struct target_ucontext {
54 abi_ulong tuc_flags;
55 abi_ptr tuc_link;
56 target_stack_t tuc_stack;
57 target_sigset_t tuc_sigmask;
58 uint8_t __unused[1024 / 8 - sizeof(target_sigset_t)];
59 struct target_sigcontext tuc_mcontext;
60 };
61
62 struct target_rt_sigframe {
63 struct target_siginfo rs_info;
64 struct target_ucontext rs_uc;
65 };
66
67 QEMU_BUILD_BUG_ON(sizeof(struct target_rt_sigframe)
68 != sizeof_rt_sigframe);
69 QEMU_BUILD_BUG_ON(offsetof(struct target_rt_sigframe, rs_uc.tuc_mcontext)
70 != offsetof_sigcontext);
71
72 /*
73 * These two structures are not present in guest memory, are private
74 * to the signal implementation, but are largely copied from the
75 * kernel's signal implementation.
76 */
77 struct ctx_layout {
78 void *haddr;
79 abi_ptr gaddr;
80 unsigned int size;
81 };
82
83 struct extctx_layout {
84 unsigned int size;
85 unsigned int flags;
86 struct ctx_layout fpu;
87 struct ctx_layout end;
88 };
89
90 static abi_ptr extframe_alloc(struct extctx_layout *extctx,
91 struct ctx_layout *sctx, unsigned size,
92 unsigned align, abi_ptr orig_sp)
93 {
94 abi_ptr sp = orig_sp;
95
96 sp -= sizeof(struct target_sctx_info) + size;
97 align = MAX(align, CONTEXT_INFO_ALIGN);
98 sp = ROUND_DOWN(sp, align);
99 sctx->gaddr = sp;
100
101 size = orig_sp - sp;
102 sctx->size = size;
103 extctx->size += size;
104
105 return sp;
106 }
107
108 static abi_ptr setup_extcontext(struct extctx_layout *extctx, abi_ptr sp)
109 {
110 memset(extctx, 0, sizeof(struct extctx_layout));
111
112 /* Grow down, alloc "end" context info first. */
113 sp = extframe_alloc(extctx, &extctx->end, 0, CONTEXT_INFO_ALIGN, sp);
114
115 /* For qemu, there is no lazy fp context switch, so fp always present. */
116 extctx->flags = SC_USED_FP;
117 sp = extframe_alloc(extctx, &extctx->fpu,
118 sizeof(struct target_rt_sigframe), FPU_CTX_ALIGN, sp);
119
120 return sp;
121 }
122
123 static void setup_sigframe(CPULoongArchState *env,
124 struct target_sigcontext *sc,
125 struct extctx_layout *extctx)
126 {
127 struct target_sctx_info *info;
128 struct target_fpu_context *fpu_ctx;
129 int i;
130
131 __put_user(extctx->flags, &sc->sc_flags);
132 __put_user(env->pc, &sc->sc_pc);
133 __put_user(0, &sc->sc_regs[0]);
134 for (i = 1; i < 32; ++i) {
135 __put_user(env->gpr[i], &sc->sc_regs[i]);
136 }
137
138 /*
139 * Set fpu context
140 */
141 info = extctx->fpu.haddr;
142 __put_user(FPU_CTX_MAGIC, &info->magic);
143 __put_user(extctx->fpu.size, &info->size);
144
145 fpu_ctx = (struct target_fpu_context *)(info + 1);
146 for (i = 0; i < 32; ++i) {
147 __put_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[i]);
148 }
149 __put_user(read_fcc(env), &fpu_ctx->fcc);
150 __put_user(env->fcsr0, &fpu_ctx->fcsr);
151
152 /*
153 * Set end context
154 */
155 info = extctx->end.haddr;
156 __put_user(0, &info->magic);
157 __put_user(extctx->end.size, &info->size);
158 }
159
160 static bool parse_extcontext(struct extctx_layout *extctx, abi_ptr frame)
161 {
162 memset(extctx, 0, sizeof(*extctx));
163
164 while (1) {
165 uint32_t magic, size;
166
167 if (get_user_u32(magic, frame) || get_user_u32(size, frame + 4)) {
168 return false;
169 }
170
171 switch (magic) {
172 case 0: /* END */
173 extctx->end.gaddr = frame;
174 extctx->end.size = size;
175 extctx->size += size;
176 return true;
177
178 case FPU_CTX_MAGIC:
179 if (size < (sizeof(struct target_sctx_info) +
180 sizeof(struct target_fpu_context))) {
181 return false;
182 }
183 extctx->fpu.gaddr = frame;
184 extctx->fpu.size = size;
185 extctx->size += size;
186 break;
187 default:
188 return false;
189 }
190
191 frame += size;
192 }
193 }
194
195 static void restore_sigframe(CPULoongArchState *env,
196 struct target_sigcontext *sc,
197 struct extctx_layout *extctx)
198 {
199 int i;
200
201 __get_user(env->pc, &sc->sc_pc);
202 for (i = 1; i < 32; ++i) {
203 __get_user(env->gpr[i], &sc->sc_regs[i]);
204 }
205
206 if (extctx->fpu.haddr) {
207 struct target_fpu_context *fpu_ctx =
208 extctx->fpu.haddr + sizeof(struct target_sctx_info);
209 uint64_t fcc;
210
211 for (i = 0; i < 32; ++i) {
212 __get_user(env->fpr[i].vreg.D(0), &fpu_ctx->regs[i]);
213 }
214 __get_user(fcc, &fpu_ctx->fcc);
215 write_fcc(env, fcc);
216 __get_user(env->fcsr0, &fpu_ctx->fcsr);
217 restore_fp_status(env);
218 }
219 }
220
221 /*
222 * Determine which stack to use.
223 */
224 static abi_ptr get_sigframe(struct target_sigaction *ka,
225 CPULoongArchState *env,
226 struct extctx_layout *extctx)
227 {
228 abi_ulong sp;
229
230 sp = target_sigsp(get_sp_from_cpustate(env), ka);
231 sp = ROUND_DOWN(sp, 16);
232 sp = setup_extcontext(extctx, sp);
233 sp -= sizeof(struct target_rt_sigframe);
234
235 assert(QEMU_IS_ALIGNED(sp, 16));
236
237 return sp;
238 }
239
240 void setup_rt_frame(int sig, struct target_sigaction *ka,
241 target_siginfo_t *info,
242 target_sigset_t *set, CPULoongArchState *env)
243 {
244 struct target_rt_sigframe *frame;
245 struct extctx_layout extctx;
246 abi_ptr frame_addr;
247 int i;
248
249 frame_addr = get_sigframe(ka, env, &extctx);
250 trace_user_setup_rt_frame(env, frame_addr);
251
252 frame = lock_user(VERIFY_WRITE, frame_addr,
253 sizeof(*frame) + extctx.size, 0);
254 if (!frame) {
255 force_sigsegv(sig);
256 return;
257 }
258 extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
259 extctx.end.haddr = (void *)frame + (extctx.end.gaddr - frame_addr);
260
261 tswap_siginfo(&frame->rs_info, info);
262
263 __put_user(0, &frame->rs_uc.tuc_flags);
264 __put_user(0, &frame->rs_uc.tuc_link);
265 target_save_altstack(&frame->rs_uc.tuc_stack, env);
266
267 setup_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx);
268
269 for (i = 0; i < TARGET_NSIG_WORDS; i++) {
270 __put_user(set->sig[i], &frame->rs_uc.tuc_sigmask.sig[i]);
271 }
272
273 env->gpr[4] = sig;
274 env->gpr[5] = frame_addr + offsetof(struct target_rt_sigframe, rs_info);
275 env->gpr[6] = frame_addr + offsetof(struct target_rt_sigframe, rs_uc);
276 env->gpr[3] = frame_addr;
277 env->gpr[1] = default_rt_sigreturn;
278
279 env->pc = ka->_sa_handler;
280 unlock_user(frame, frame_addr, sizeof(*frame) + extctx.size);
281 }
282
283 long do_rt_sigreturn(CPULoongArchState *env)
284 {
285 struct target_rt_sigframe *frame;
286 struct extctx_layout extctx;
287 abi_ulong frame_addr;
288 sigset_t blocked;
289
290 frame_addr = env->gpr[3];
291 trace_user_do_rt_sigreturn(env, frame_addr);
292
293 if (!parse_extcontext(&extctx, frame_addr + sizeof(*frame))) {
294 goto badframe;
295 }
296
297 frame = lock_user(VERIFY_READ, frame_addr,
298 sizeof(*frame) + extctx.size, 1);
299 if (!frame) {
300 goto badframe;
301 }
302 if (extctx.fpu.gaddr) {
303 extctx.fpu.haddr = (void *)frame + (extctx.fpu.gaddr - frame_addr);
304 }
305
306 target_to_host_sigset(&blocked, &frame->rs_uc.tuc_sigmask);
307 set_sigmask(&blocked);
308
309 restore_sigframe(env, &frame->rs_uc.tuc_mcontext, &extctx);
310
311 target_restore_altstack(&frame->rs_uc.tuc_stack, env);
312
313 unlock_user(frame, frame_addr, 0);
314 return -QEMU_ESIGRETURN;
315
316 badframe:
317 force_sig(TARGET_SIGSEGV);
318 return -QEMU_ESIGRETURN;
319 }
320
321 void setup_sigtramp(abi_ulong sigtramp_page)
322 {
323 uint32_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 8, 0);
324 assert(tramp != NULL);
325
326 __put_user(0x03822c0b, tramp + 0); /* ori a7, zero, 0x8b */
327 __put_user(0x002b0000, tramp + 1); /* syscall 0 */
328
329 default_rt_sigreturn = sigtramp_page;
330 unlock_user(tramp, sigtramp_page, 8);
331 }