2 * Emulation of Linux signals
4 * Copyright (c) 2003 Fabrice Bellard
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.
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.
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/>.
19 #include "qemu/osdep.h"
21 #include "target_signal.h"
22 #include "signal-common.h"
23 #include "linux-user/trace.h"
25 struct target_sigcontext
{
36 struct target_sigframe
43 abi_ulong extramask
[TARGET_NSIG_WORDS
-1];
44 struct target_sigcontext sc
;
47 typedef int target_greg_t
;
48 #define TARGET_NGREG 18
49 typedef target_greg_t target_gregset_t
[TARGET_NGREG
];
51 typedef struct target_fpregset
{
56 struct target_mcontext
{
58 target_gregset_t gregs
;
59 target_fpregset_t fpregs
;
62 #define TARGET_MCONTEXT_VERSION 2
64 struct target_ucontext
{
67 target_stack_t tuc_stack
;
68 struct target_mcontext tuc_mcontext
;
69 abi_long tuc_filler
[80];
70 target_sigset_t tuc_sigmask
;
73 struct target_rt_sigframe
80 struct target_siginfo info
;
81 struct target_ucontext uc
;
84 static void setup_sigcontext(struct target_sigcontext
*sc
, CPUM68KState
*env
,
87 uint32_t sr
= (env
->sr
& 0xff00) | cpu_m68k_get_ccr(env
);
88 __put_user(mask
, &sc
->sc_mask
);
89 __put_user(env
->aregs
[7], &sc
->sc_usp
);
90 __put_user(env
->dregs
[0], &sc
->sc_d0
);
91 __put_user(env
->dregs
[1], &sc
->sc_d1
);
92 __put_user(env
->aregs
[0], &sc
->sc_a0
);
93 __put_user(env
->aregs
[1], &sc
->sc_a1
);
94 __put_user(sr
, &sc
->sc_sr
);
95 __put_user(env
->pc
, &sc
->sc_pc
);
99 restore_sigcontext(CPUM68KState
*env
, struct target_sigcontext
*sc
)
103 __get_user(env
->aregs
[7], &sc
->sc_usp
);
104 __get_user(env
->dregs
[0], &sc
->sc_d0
);
105 __get_user(env
->dregs
[1], &sc
->sc_d1
);
106 __get_user(env
->aregs
[0], &sc
->sc_a0
);
107 __get_user(env
->aregs
[1], &sc
->sc_a1
);
108 __get_user(env
->pc
, &sc
->sc_pc
);
109 __get_user(temp
, &sc
->sc_sr
);
110 cpu_m68k_set_ccr(env
, temp
);
114 * Determine which stack to use..
116 static inline abi_ulong
117 get_sigframe(struct target_sigaction
*ka
, CPUM68KState
*regs
,
122 sp
= target_sigsp(get_sp_from_cpustate(regs
), ka
);
125 return ((sp
- frame_size
) & -8UL);
128 void setup_frame(int sig
, struct target_sigaction
*ka
,
129 target_sigset_t
*set
, CPUM68KState
*env
)
131 struct target_sigframe
*frame
;
132 abi_ulong frame_addr
;
133 abi_ulong retcode_addr
;
137 frame_addr
= get_sigframe(ka
, env
, sizeof *frame
);
138 trace_user_setup_frame(env
, frame_addr
);
139 if (!lock_user_struct(VERIFY_WRITE
, frame
, frame_addr
, 0)) {
143 __put_user(sig
, &frame
->sig
);
145 sc_addr
= frame_addr
+ offsetof(struct target_sigframe
, sc
);
146 __put_user(sc_addr
, &frame
->psc
);
148 setup_sigcontext(&frame
->sc
, env
, set
->sig
[0]);
150 for(i
= 1; i
< TARGET_NSIG_WORDS
; i
++) {
151 __put_user(set
->sig
[i
], &frame
->extramask
[i
- 1]);
154 /* Set up to return from userspace. */
156 retcode_addr
= frame_addr
+ offsetof(struct target_sigframe
, retcode
);
157 __put_user(retcode_addr
, &frame
->pretcode
);
159 /* moveq #,d0; trap #0 */
161 __put_user(0x70004e40 + (TARGET_NR_sigreturn
<< 16),
162 (uint32_t *)(frame
->retcode
));
164 /* Set up to return from userspace */
166 env
->aregs
[7] = frame_addr
;
167 env
->pc
= ka
->_sa_handler
;
169 unlock_user_struct(frame
, frame_addr
, 1);
176 static inline void target_rt_save_fpu_state(struct target_ucontext
*uc
,
180 target_fpregset_t
*fpregs
= &uc
->tuc_mcontext
.fpregs
;
182 __put_user(env
->fpcr
, &fpregs
->f_fpcntl
[0]);
183 __put_user(env
->fpsr
, &fpregs
->f_fpcntl
[1]);
184 /* fpiar is not emulated */
186 for (i
= 0; i
< 8; i
++) {
187 uint32_t high
= env
->fregs
[i
].d
.high
<< 16;
188 __put_user(high
, &fpregs
->f_fpregs
[i
* 3]);
189 __put_user(env
->fregs
[i
].d
.low
,
190 (uint64_t *)&fpregs
->f_fpregs
[i
* 3 + 1]);
194 static inline int target_rt_setup_ucontext(struct target_ucontext
*uc
,
197 target_greg_t
*gregs
= uc
->tuc_mcontext
.gregs
;
198 uint32_t sr
= (env
->sr
& 0xff00) | cpu_m68k_get_ccr(env
);
200 __put_user(TARGET_MCONTEXT_VERSION
, &uc
->tuc_mcontext
.version
);
201 __put_user(env
->dregs
[0], &gregs
[0]);
202 __put_user(env
->dregs
[1], &gregs
[1]);
203 __put_user(env
->dregs
[2], &gregs
[2]);
204 __put_user(env
->dregs
[3], &gregs
[3]);
205 __put_user(env
->dregs
[4], &gregs
[4]);
206 __put_user(env
->dregs
[5], &gregs
[5]);
207 __put_user(env
->dregs
[6], &gregs
[6]);
208 __put_user(env
->dregs
[7], &gregs
[7]);
209 __put_user(env
->aregs
[0], &gregs
[8]);
210 __put_user(env
->aregs
[1], &gregs
[9]);
211 __put_user(env
->aregs
[2], &gregs
[10]);
212 __put_user(env
->aregs
[3], &gregs
[11]);
213 __put_user(env
->aregs
[4], &gregs
[12]);
214 __put_user(env
->aregs
[5], &gregs
[13]);
215 __put_user(env
->aregs
[6], &gregs
[14]);
216 __put_user(env
->aregs
[7], &gregs
[15]);
217 __put_user(env
->pc
, &gregs
[16]);
218 __put_user(sr
, &gregs
[17]);
220 target_rt_save_fpu_state(uc
, env
);
225 static inline void target_rt_restore_fpu_state(CPUM68KState
*env
,
226 struct target_ucontext
*uc
)
229 target_fpregset_t
*fpregs
= &uc
->tuc_mcontext
.fpregs
;
232 __get_user(fpcr
, &fpregs
->f_fpcntl
[0]);
233 cpu_m68k_set_fpcr(env
, fpcr
);
234 __get_user(env
->fpsr
, &fpregs
->f_fpcntl
[1]);
235 /* fpiar is not emulated */
237 for (i
= 0; i
< 8; i
++) {
239 __get_user(high
, &fpregs
->f_fpregs
[i
* 3]);
240 env
->fregs
[i
].d
.high
= high
>> 16;
241 __get_user(env
->fregs
[i
].d
.low
,
242 (uint64_t *)&fpregs
->f_fpregs
[i
* 3 + 1]);
246 static inline int target_rt_restore_ucontext(CPUM68KState
*env
,
247 struct target_ucontext
*uc
)
250 target_greg_t
*gregs
= uc
->tuc_mcontext
.gregs
;
252 __get_user(temp
, &uc
->tuc_mcontext
.version
);
253 if (temp
!= TARGET_MCONTEXT_VERSION
)
256 /* restore passed registers */
257 __get_user(env
->dregs
[0], &gregs
[0]);
258 __get_user(env
->dregs
[1], &gregs
[1]);
259 __get_user(env
->dregs
[2], &gregs
[2]);
260 __get_user(env
->dregs
[3], &gregs
[3]);
261 __get_user(env
->dregs
[4], &gregs
[4]);
262 __get_user(env
->dregs
[5], &gregs
[5]);
263 __get_user(env
->dregs
[6], &gregs
[6]);
264 __get_user(env
->dregs
[7], &gregs
[7]);
265 __get_user(env
->aregs
[0], &gregs
[8]);
266 __get_user(env
->aregs
[1], &gregs
[9]);
267 __get_user(env
->aregs
[2], &gregs
[10]);
268 __get_user(env
->aregs
[3], &gregs
[11]);
269 __get_user(env
->aregs
[4], &gregs
[12]);
270 __get_user(env
->aregs
[5], &gregs
[13]);
271 __get_user(env
->aregs
[6], &gregs
[14]);
272 __get_user(env
->aregs
[7], &gregs
[15]);
273 __get_user(env
->pc
, &gregs
[16]);
274 __get_user(temp
, &gregs
[17]);
275 cpu_m68k_set_ccr(env
, temp
);
277 target_rt_restore_fpu_state(env
, uc
);
285 void setup_rt_frame(int sig
, struct target_sigaction
*ka
,
286 target_siginfo_t
*info
,
287 target_sigset_t
*set
, CPUM68KState
*env
)
289 struct target_rt_sigframe
*frame
;
290 abi_ulong frame_addr
;
291 abi_ulong retcode_addr
;
297 frame_addr
= get_sigframe(ka
, env
, sizeof *frame
);
298 trace_user_setup_rt_frame(env
, frame_addr
);
299 if (!lock_user_struct(VERIFY_WRITE
, frame
, frame_addr
, 0)) {
303 __put_user(sig
, &frame
->sig
);
305 info_addr
= frame_addr
+ offsetof(struct target_rt_sigframe
, info
);
306 __put_user(info_addr
, &frame
->pinfo
);
308 uc_addr
= frame_addr
+ offsetof(struct target_rt_sigframe
, uc
);
309 __put_user(uc_addr
, &frame
->puc
);
311 tswap_siginfo(&frame
->info
, info
);
313 /* Create the ucontext */
315 __put_user(0, &frame
->uc
.tuc_flags
);
316 __put_user(0, &frame
->uc
.tuc_link
);
317 target_save_altstack(&frame
->uc
.tuc_stack
, env
);
318 err
|= target_rt_setup_ucontext(&frame
->uc
, env
);
323 for(i
= 0; i
< TARGET_NSIG_WORDS
; i
++) {
324 __put_user(set
->sig
[i
], &frame
->uc
.tuc_sigmask
.sig
[i
]);
327 /* Set up to return from userspace. */
329 retcode_addr
= frame_addr
+ offsetof(struct target_sigframe
, retcode
);
330 __put_user(retcode_addr
, &frame
->pretcode
);
332 /* moveq #,d0; notb d0; trap #0 */
334 __put_user(0x70004600 + ((TARGET_NR_rt_sigreturn
^ 0xff) << 16),
335 (uint32_t *)(frame
->retcode
+ 0));
336 __put_user(0x4e40, (uint16_t *)(frame
->retcode
+ 4));
341 /* Set up to return from userspace */
343 env
->aregs
[7] = frame_addr
;
344 env
->pc
= ka
->_sa_handler
;
346 unlock_user_struct(frame
, frame_addr
, 1);
350 unlock_user_struct(frame
, frame_addr
, 1);
354 long do_sigreturn(CPUM68KState
*env
)
356 struct target_sigframe
*frame
;
357 abi_ulong frame_addr
= env
->aregs
[7] - 4;
358 target_sigset_t target_set
;
362 trace_user_do_sigreturn(env
, frame_addr
);
363 if (!lock_user_struct(VERIFY_READ
, frame
, frame_addr
, 1))
366 /* set blocked signals */
368 __get_user(target_set
.sig
[0], &frame
->sc
.sc_mask
);
370 for(i
= 1; i
< TARGET_NSIG_WORDS
; i
++) {
371 __get_user(target_set
.sig
[i
], &frame
->extramask
[i
- 1]);
374 target_to_host_sigset_internal(&set
, &target_set
);
377 /* restore registers */
379 restore_sigcontext(env
, &frame
->sc
);
381 unlock_user_struct(frame
, frame_addr
, 0);
382 return -TARGET_QEMU_ESIGRETURN
;
385 force_sig(TARGET_SIGSEGV
);
386 return -TARGET_QEMU_ESIGRETURN
;
389 long do_rt_sigreturn(CPUM68KState
*env
)
391 struct target_rt_sigframe
*frame
;
392 abi_ulong frame_addr
= env
->aregs
[7] - 4;
395 trace_user_do_rt_sigreturn(env
, frame_addr
);
396 if (!lock_user_struct(VERIFY_READ
, frame
, frame_addr
, 1))
399 target_to_host_sigset(&set
, &frame
->uc
.tuc_sigmask
);
402 /* restore registers */
404 if (target_rt_restore_ucontext(env
, &frame
->uc
))
407 if (do_sigaltstack(frame_addr
+
408 offsetof(struct target_rt_sigframe
, uc
.tuc_stack
),
409 0, get_sp_from_cpustate(env
)) == -EFAULT
)
412 unlock_user_struct(frame
, frame_addr
, 0);
413 return -TARGET_QEMU_ESIGRETURN
;
416 unlock_user_struct(frame
, frame_addr
, 0);
417 force_sig(TARGET_SIGSEGV
);
418 return -TARGET_QEMU_ESIGRETURN
;