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