]>
Commit | Line | Data |
---|---|---|
befb7447 LV |
1 | /* |
2 | * Emulation of Linux signals | |
3 | * | |
4 | * Copyright (c) 2003 Fabrice Bellard | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
4495abcc LV |
19 | #include "qemu/osdep.h" |
20 | #include "qemu.h" | |
4495abcc LV |
21 | #include "signal-common.h" |
22 | #include "linux-user/trace.h" | |
23 | ||
24 | struct target_sigcontext { | |
25 | abi_ulong sc_mask; | |
26 | abi_ulong sc_usp; | |
27 | abi_ulong sc_d0; | |
28 | abi_ulong sc_d1; | |
29 | abi_ulong sc_a0; | |
30 | abi_ulong sc_a1; | |
31 | unsigned short sc_sr; | |
32 | abi_ulong sc_pc; | |
33 | }; | |
34 | ||
35 | struct target_sigframe | |
36 | { | |
37 | abi_ulong pretcode; | |
38 | int sig; | |
39 | int code; | |
40 | abi_ulong psc; | |
41 | char retcode[8]; | |
42 | abi_ulong extramask[TARGET_NSIG_WORDS-1]; | |
43 | struct target_sigcontext sc; | |
44 | }; | |
45 | ||
46 | typedef int target_greg_t; | |
47 | #define TARGET_NGREG 18 | |
48 | typedef target_greg_t target_gregset_t[TARGET_NGREG]; | |
49 | ||
50 | typedef struct target_fpregset { | |
51 | int f_fpcntl[3]; | |
52 | int f_fpregs[8*3]; | |
53 | } target_fpregset_t; | |
54 | ||
55 | struct target_mcontext { | |
56 | int version; | |
57 | target_gregset_t gregs; | |
58 | target_fpregset_t fpregs; | |
59 | }; | |
60 | ||
61 | #define TARGET_MCONTEXT_VERSION 2 | |
62 | ||
63 | struct target_ucontext { | |
64 | abi_ulong tuc_flags; | |
65 | abi_ulong tuc_link; | |
66 | target_stack_t tuc_stack; | |
67 | struct target_mcontext tuc_mcontext; | |
68 | abi_long tuc_filler[80]; | |
69 | target_sigset_t tuc_sigmask; | |
70 | }; | |
71 | ||
72 | struct target_rt_sigframe | |
73 | { | |
74 | abi_ulong pretcode; | |
75 | int sig; | |
76 | abi_ulong pinfo; | |
77 | abi_ulong puc; | |
78 | char retcode[8]; | |
79 | struct target_siginfo info; | |
80 | struct target_ucontext uc; | |
81 | }; | |
82 | ||
83 | static void setup_sigcontext(struct target_sigcontext *sc, CPUM68KState *env, | |
84 | abi_ulong mask) | |
85 | { | |
86 | uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); | |
87 | __put_user(mask, &sc->sc_mask); | |
88 | __put_user(env->aregs[7], &sc->sc_usp); | |
89 | __put_user(env->dregs[0], &sc->sc_d0); | |
90 | __put_user(env->dregs[1], &sc->sc_d1); | |
91 | __put_user(env->aregs[0], &sc->sc_a0); | |
92 | __put_user(env->aregs[1], &sc->sc_a1); | |
93 | __put_user(sr, &sc->sc_sr); | |
94 | __put_user(env->pc, &sc->sc_pc); | |
95 | } | |
96 | ||
97 | static void | |
98 | restore_sigcontext(CPUM68KState *env, struct target_sigcontext *sc) | |
99 | { | |
100 | int temp; | |
101 | ||
102 | __get_user(env->aregs[7], &sc->sc_usp); | |
103 | __get_user(env->dregs[0], &sc->sc_d0); | |
104 | __get_user(env->dregs[1], &sc->sc_d1); | |
105 | __get_user(env->aregs[0], &sc->sc_a0); | |
106 | __get_user(env->aregs[1], &sc->sc_a1); | |
107 | __get_user(env->pc, &sc->sc_pc); | |
108 | __get_user(temp, &sc->sc_sr); | |
109 | cpu_m68k_set_ccr(env, temp); | |
110 | } | |
111 | ||
112 | /* | |
113 | * Determine which stack to use.. | |
114 | */ | |
115 | static inline abi_ulong | |
116 | get_sigframe(struct target_sigaction *ka, CPUM68KState *regs, | |
117 | size_t frame_size) | |
118 | { | |
465e237b | 119 | abi_ulong sp; |
4495abcc | 120 | |
465e237b | 121 | sp = target_sigsp(get_sp_from_cpustate(regs), ka); |
4495abcc | 122 | |
4495abcc LV |
123 | |
124 | return ((sp - frame_size) & -8UL); | |
125 | } | |
126 | ||
127 | void setup_frame(int sig, struct target_sigaction *ka, | |
128 | target_sigset_t *set, CPUM68KState *env) | |
129 | { | |
130 | struct target_sigframe *frame; | |
131 | abi_ulong frame_addr; | |
132 | abi_ulong retcode_addr; | |
133 | abi_ulong sc_addr; | |
134 | int i; | |
135 | ||
136 | frame_addr = get_sigframe(ka, env, sizeof *frame); | |
137 | trace_user_setup_frame(env, frame_addr); | |
138 | if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { | |
139 | goto give_sigsegv; | |
140 | } | |
141 | ||
142 | __put_user(sig, &frame->sig); | |
143 | ||
144 | sc_addr = frame_addr + offsetof(struct target_sigframe, sc); | |
145 | __put_user(sc_addr, &frame->psc); | |
146 | ||
147 | setup_sigcontext(&frame->sc, env, set->sig[0]); | |
148 | ||
149 | for(i = 1; i < TARGET_NSIG_WORDS; i++) { | |
150 | __put_user(set->sig[i], &frame->extramask[i - 1]); | |
151 | } | |
152 | ||
153 | /* Set up to return from userspace. */ | |
154 | ||
155 | retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); | |
156 | __put_user(retcode_addr, &frame->pretcode); | |
157 | ||
158 | /* moveq #,d0; trap #0 */ | |
159 | ||
160 | __put_user(0x70004e40 + (TARGET_NR_sigreturn << 16), | |
161 | (uint32_t *)(frame->retcode)); | |
162 | ||
163 | /* Set up to return from userspace */ | |
164 | ||
165 | env->aregs[7] = frame_addr; | |
166 | env->pc = ka->_sa_handler; | |
167 | ||
168 | unlock_user_struct(frame, frame_addr, 1); | |
169 | return; | |
170 | ||
171 | give_sigsegv: | |
172 | force_sigsegv(sig); | |
173 | } | |
174 | ||
175 | static inline void target_rt_save_fpu_state(struct target_ucontext *uc, | |
176 | CPUM68KState *env) | |
177 | { | |
178 | int i; | |
179 | target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; | |
180 | ||
181 | __put_user(env->fpcr, &fpregs->f_fpcntl[0]); | |
182 | __put_user(env->fpsr, &fpregs->f_fpcntl[1]); | |
183 | /* fpiar is not emulated */ | |
184 | ||
185 | for (i = 0; i < 8; i++) { | |
186 | uint32_t high = env->fregs[i].d.high << 16; | |
187 | __put_user(high, &fpregs->f_fpregs[i * 3]); | |
188 | __put_user(env->fregs[i].d.low, | |
189 | (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); | |
190 | } | |
191 | } | |
192 | ||
193 | static inline int target_rt_setup_ucontext(struct target_ucontext *uc, | |
194 | CPUM68KState *env) | |
195 | { | |
196 | target_greg_t *gregs = uc->tuc_mcontext.gregs; | |
197 | uint32_t sr = (env->sr & 0xff00) | cpu_m68k_get_ccr(env); | |
198 | ||
199 | __put_user(TARGET_MCONTEXT_VERSION, &uc->tuc_mcontext.version); | |
200 | __put_user(env->dregs[0], &gregs[0]); | |
201 | __put_user(env->dregs[1], &gregs[1]); | |
202 | __put_user(env->dregs[2], &gregs[2]); | |
203 | __put_user(env->dregs[3], &gregs[3]); | |
204 | __put_user(env->dregs[4], &gregs[4]); | |
205 | __put_user(env->dregs[5], &gregs[5]); | |
206 | __put_user(env->dregs[6], &gregs[6]); | |
207 | __put_user(env->dregs[7], &gregs[7]); | |
208 | __put_user(env->aregs[0], &gregs[8]); | |
209 | __put_user(env->aregs[1], &gregs[9]); | |
210 | __put_user(env->aregs[2], &gregs[10]); | |
211 | __put_user(env->aregs[3], &gregs[11]); | |
212 | __put_user(env->aregs[4], &gregs[12]); | |
213 | __put_user(env->aregs[5], &gregs[13]); | |
214 | __put_user(env->aregs[6], &gregs[14]); | |
215 | __put_user(env->aregs[7], &gregs[15]); | |
216 | __put_user(env->pc, &gregs[16]); | |
217 | __put_user(sr, &gregs[17]); | |
218 | ||
219 | target_rt_save_fpu_state(uc, env); | |
220 | ||
221 | return 0; | |
222 | } | |
223 | ||
224 | static inline void target_rt_restore_fpu_state(CPUM68KState *env, | |
225 | struct target_ucontext *uc) | |
226 | { | |
227 | int i; | |
228 | target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; | |
229 | uint32_t fpcr; | |
230 | ||
231 | __get_user(fpcr, &fpregs->f_fpcntl[0]); | |
232 | cpu_m68k_set_fpcr(env, fpcr); | |
233 | __get_user(env->fpsr, &fpregs->f_fpcntl[1]); | |
234 | /* fpiar is not emulated */ | |
235 | ||
236 | for (i = 0; i < 8; i++) { | |
237 | uint32_t high; | |
238 | __get_user(high, &fpregs->f_fpregs[i * 3]); | |
239 | env->fregs[i].d.high = high >> 16; | |
240 | __get_user(env->fregs[i].d.low, | |
241 | (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); | |
242 | } | |
243 | } | |
244 | ||
245 | static inline int target_rt_restore_ucontext(CPUM68KState *env, | |
246 | struct target_ucontext *uc) | |
247 | { | |
248 | int temp; | |
249 | target_greg_t *gregs = uc->tuc_mcontext.gregs; | |
250 | ||
251 | __get_user(temp, &uc->tuc_mcontext.version); | |
252 | if (temp != TARGET_MCONTEXT_VERSION) | |
253 | goto badframe; | |
254 | ||
255 | /* restore passed registers */ | |
256 | __get_user(env->dregs[0], &gregs[0]); | |
257 | __get_user(env->dregs[1], &gregs[1]); | |
258 | __get_user(env->dregs[2], &gregs[2]); | |
259 | __get_user(env->dregs[3], &gregs[3]); | |
260 | __get_user(env->dregs[4], &gregs[4]); | |
261 | __get_user(env->dregs[5], &gregs[5]); | |
262 | __get_user(env->dregs[6], &gregs[6]); | |
263 | __get_user(env->dregs[7], &gregs[7]); | |
264 | __get_user(env->aregs[0], &gregs[8]); | |
265 | __get_user(env->aregs[1], &gregs[9]); | |
266 | __get_user(env->aregs[2], &gregs[10]); | |
267 | __get_user(env->aregs[3], &gregs[11]); | |
268 | __get_user(env->aregs[4], &gregs[12]); | |
269 | __get_user(env->aregs[5], &gregs[13]); | |
270 | __get_user(env->aregs[6], &gregs[14]); | |
271 | __get_user(env->aregs[7], &gregs[15]); | |
272 | __get_user(env->pc, &gregs[16]); | |
273 | __get_user(temp, &gregs[17]); | |
274 | cpu_m68k_set_ccr(env, temp); | |
275 | ||
276 | target_rt_restore_fpu_state(env, uc); | |
277 | ||
278 | return 0; | |
279 | ||
280 | badframe: | |
281 | return 1; | |
282 | } | |
283 | ||
284 | void setup_rt_frame(int sig, struct target_sigaction *ka, | |
285 | target_siginfo_t *info, | |
286 | target_sigset_t *set, CPUM68KState *env) | |
287 | { | |
288 | struct target_rt_sigframe *frame; | |
289 | abi_ulong frame_addr; | |
290 | abi_ulong retcode_addr; | |
291 | abi_ulong info_addr; | |
292 | abi_ulong uc_addr; | |
293 | int err = 0; | |
294 | int i; | |
295 | ||
296 | frame_addr = get_sigframe(ka, env, sizeof *frame); | |
297 | trace_user_setup_rt_frame(env, frame_addr); | |
298 | if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { | |
299 | goto give_sigsegv; | |
300 | } | |
301 | ||
302 | __put_user(sig, &frame->sig); | |
303 | ||
304 | info_addr = frame_addr + offsetof(struct target_rt_sigframe, info); | |
305 | __put_user(info_addr, &frame->pinfo); | |
306 | ||
307 | uc_addr = frame_addr + offsetof(struct target_rt_sigframe, uc); | |
308 | __put_user(uc_addr, &frame->puc); | |
309 | ||
310 | tswap_siginfo(&frame->info, info); | |
311 | ||
312 | /* Create the ucontext */ | |
313 | ||
314 | __put_user(0, &frame->uc.tuc_flags); | |
315 | __put_user(0, &frame->uc.tuc_link); | |
465e237b | 316 | target_save_altstack(&frame->uc.tuc_stack, env); |
4495abcc LV |
317 | err |= target_rt_setup_ucontext(&frame->uc, env); |
318 | ||
319 | if (err) | |
320 | goto give_sigsegv; | |
321 | ||
322 | for(i = 0; i < TARGET_NSIG_WORDS; i++) { | |
323 | __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); | |
324 | } | |
325 | ||
326 | /* Set up to return from userspace. */ | |
327 | ||
328 | retcode_addr = frame_addr + offsetof(struct target_sigframe, retcode); | |
329 | __put_user(retcode_addr, &frame->pretcode); | |
330 | ||
331 | /* moveq #,d0; notb d0; trap #0 */ | |
332 | ||
333 | __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn ^ 0xff) << 16), | |
334 | (uint32_t *)(frame->retcode + 0)); | |
335 | __put_user(0x4e40, (uint16_t *)(frame->retcode + 4)); | |
336 | ||
337 | if (err) | |
338 | goto give_sigsegv; | |
339 | ||
340 | /* Set up to return from userspace */ | |
341 | ||
342 | env->aregs[7] = frame_addr; | |
343 | env->pc = ka->_sa_handler; | |
344 | ||
345 | unlock_user_struct(frame, frame_addr, 1); | |
346 | return; | |
347 | ||
348 | give_sigsegv: | |
349 | unlock_user_struct(frame, frame_addr, 1); | |
350 | force_sigsegv(sig); | |
351 | } | |
352 | ||
353 | long do_sigreturn(CPUM68KState *env) | |
354 | { | |
355 | struct target_sigframe *frame; | |
356 | abi_ulong frame_addr = env->aregs[7] - 4; | |
357 | target_sigset_t target_set; | |
358 | sigset_t set; | |
359 | int i; | |
360 | ||
361 | trace_user_do_sigreturn(env, frame_addr); | |
362 | if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) | |
363 | goto badframe; | |
364 | ||
365 | /* set blocked signals */ | |
366 | ||
367 | __get_user(target_set.sig[0], &frame->sc.sc_mask); | |
368 | ||
369 | for(i = 1; i < TARGET_NSIG_WORDS; i++) { | |
370 | __get_user(target_set.sig[i], &frame->extramask[i - 1]); | |
371 | } | |
372 | ||
373 | target_to_host_sigset_internal(&set, &target_set); | |
374 | set_sigmask(&set); | |
375 | ||
376 | /* restore registers */ | |
377 | ||
378 | restore_sigcontext(env, &frame->sc); | |
379 | ||
380 | unlock_user_struct(frame, frame_addr, 0); | |
381 | return -TARGET_QEMU_ESIGRETURN; | |
382 | ||
383 | badframe: | |
384 | force_sig(TARGET_SIGSEGV); | |
385 | return -TARGET_QEMU_ESIGRETURN; | |
386 | } | |
387 | ||
388 | long do_rt_sigreturn(CPUM68KState *env) | |
389 | { | |
390 | struct target_rt_sigframe *frame; | |
391 | abi_ulong frame_addr = env->aregs[7] - 4; | |
392 | sigset_t set; | |
393 | ||
394 | trace_user_do_rt_sigreturn(env, frame_addr); | |
395 | if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) | |
396 | goto badframe; | |
397 | ||
398 | target_to_host_sigset(&set, &frame->uc.tuc_sigmask); | |
399 | set_sigmask(&set); | |
400 | ||
401 | /* restore registers */ | |
402 | ||
403 | if (target_rt_restore_ucontext(env, &frame->uc)) | |
404 | goto badframe; | |
405 | ||
406 | if (do_sigaltstack(frame_addr + | |
407 | offsetof(struct target_rt_sigframe, uc.tuc_stack), | |
408 | 0, get_sp_from_cpustate(env)) == -EFAULT) | |
409 | goto badframe; | |
410 | ||
411 | unlock_user_struct(frame, frame_addr, 0); | |
412 | return -TARGET_QEMU_ESIGRETURN; | |
413 | ||
414 | badframe: | |
415 | unlock_user_struct(frame, frame_addr, 0); | |
416 | force_sig(TARGET_SIGSEGV); | |
417 | return -TARGET_QEMU_ESIGRETURN; | |
418 | } |