blob: 67cbf4f574a85856b422d7e87d9f4fbde9167c7b [file] [log] [blame]
Peter Maydellda30d852011-12-05 19:42:18 +00001/*
2 * Emulation of Linux signals : sh4 specific code
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 */
19
20#include <stdlib.h>
21#include <stdio.h>
22#include <string.h>
23#include <stdarg.h>
24#include <unistd.h>
25#include <errno.h>
26#include <assert.h>
27#include <sys/ucontext.h>
28#include <sys/resource.h>
29
30#include "qemu.h"
31#include "qemu-common.h"
32#include "signal-common.h"
33#include "target_signal.h"
34
35/*
36 * code and data structures from linux kernel:
37 * include/asm-sh/sigcontext.h
38 * arch/sh/kernel/signal.c
39 */
40
41struct target_sigcontext {
42 target_ulong oldmask;
43
44 /* CPU registers */
45 target_ulong sc_gregs[16];
46 target_ulong sc_pc;
47 target_ulong sc_pr;
48 target_ulong sc_sr;
49 target_ulong sc_gbr;
50 target_ulong sc_mach;
51 target_ulong sc_macl;
52
53 /* FPU registers */
54 target_ulong sc_fpregs[16];
55 target_ulong sc_xfpregs[16];
56 unsigned int sc_fpscr;
57 unsigned int sc_fpul;
58 unsigned int sc_ownedfp;
59};
60
61struct target_sigframe
62{
63 struct target_sigcontext sc;
64 target_ulong extramask[TARGET_NSIG_WORDS-1];
65 uint16_t retcode[3];
66};
67
68
69struct target_ucontext {
70 target_ulong tuc_flags;
71 struct target_ucontext *tuc_link;
72 target_stack_t tuc_stack;
73 struct target_sigcontext tuc_mcontext;
74 target_sigset_t tuc_sigmask; /* mask last for extensibility */
75};
76
77struct target_rt_sigframe
78{
79 struct target_siginfo info;
80 struct target_ucontext uc;
81 uint16_t retcode[3];
82};
83
84
85#define MOVW(n) (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */
86#define TRAP_NOARG 0xc310 /* Syscall w/no args (NR in R3) SH3/4 */
87
88static abi_ulong get_sigframe(struct target_sigaction *ka,
89 unsigned long sp, size_t frame_size)
90{
91 if ((ka->sa_flags & TARGET_SA_ONSTACK) && (sas_ss_flags(sp) == 0)) {
92 sp = target_sigaltstack_used.ss_sp + target_sigaltstack_used.ss_size;
93 }
94
95 return (sp - frame_size) & -8ul;
96}
97
98static int setup_sigcontext(struct target_sigcontext *sc,
99 CPUState *regs, unsigned long mask)
100{
101 int err = 0;
102 int i;
103
104#define COPY(x) err |= __put_user(regs->x, &sc->sc_##x)
105 COPY(gregs[0]); COPY(gregs[1]);
106 COPY(gregs[2]); COPY(gregs[3]);
107 COPY(gregs[4]); COPY(gregs[5]);
108 COPY(gregs[6]); COPY(gregs[7]);
109 COPY(gregs[8]); COPY(gregs[9]);
110 COPY(gregs[10]); COPY(gregs[11]);
111 COPY(gregs[12]); COPY(gregs[13]);
112 COPY(gregs[14]); COPY(gregs[15]);
113 COPY(gbr); COPY(mach);
114 COPY(macl); COPY(pr);
115 COPY(sr); COPY(pc);
116#undef COPY
117
118 for (i=0; i<16; i++) {
119 err |= __put_user(regs->fregs[i], &sc->sc_fpregs[i]);
120 }
121 err |= __put_user(regs->fpscr, &sc->sc_fpscr);
122 err |= __put_user(regs->fpul, &sc->sc_fpul);
123
124 /* non-iBCS2 extensions.. */
125 err |= __put_user(mask, &sc->oldmask);
126
127 return err;
128}
129
130static int restore_sigcontext(CPUState *regs, struct target_sigcontext *sc,
131 target_ulong *r0_p)
132{
133 unsigned int err = 0;
134 int i;
135
136#define COPY(x) err |= __get_user(regs->x, &sc->sc_##x)
137 COPY(gregs[1]);
138 COPY(gregs[2]); COPY(gregs[3]);
139 COPY(gregs[4]); COPY(gregs[5]);
140 COPY(gregs[6]); COPY(gregs[7]);
141 COPY(gregs[8]); COPY(gregs[9]);
142 COPY(gregs[10]); COPY(gregs[11]);
143 COPY(gregs[12]); COPY(gregs[13]);
144 COPY(gregs[14]); COPY(gregs[15]);
145 COPY(gbr); COPY(mach);
146 COPY(macl); COPY(pr);
147 COPY(sr); COPY(pc);
148#undef COPY
149
150 for (i=0; i<16; i++) {
151 err |= __get_user(regs->fregs[i], &sc->sc_fpregs[i]);
152 }
153 err |= __get_user(regs->fpscr, &sc->sc_fpscr);
154 err |= __get_user(regs->fpul, &sc->sc_fpul);
155
156 regs->tra = -1; /* disable syscall checks */
157 err |= __get_user(*r0_p, &sc->sc_gregs[0]);
158 return err;
159}
160
161void setup_frame(int sig, struct target_sigaction *ka,
162 target_sigset_t *set, CPUState *regs)
163{
164 struct target_sigframe *frame;
165 abi_ulong frame_addr;
166 int i;
167 int err = 0;
168 int signal;
169
170 frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
171 if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
172 goto give_sigsegv;
173
174 signal = current_exec_domain_sig(sig);
175
176 err |= setup_sigcontext(&frame->sc, regs, set->sig[0]);
177
178 for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) {
179 err |= __put_user(set->sig[i + 1], &frame->extramask[i]);
180 }
181
182 /* Set up to return from userspace. If provided, use a stub
183 already in userspace. */
184 if (ka->sa_flags & TARGET_SA_RESTORER) {
185 regs->pr = (unsigned long) ka->sa_restorer;
186 } else {
187 /* Generate return code (system call to sigreturn) */
188 err |= __put_user(MOVW(2), &frame->retcode[0]);
189 err |= __put_user(TRAP_NOARG, &frame->retcode[1]);
190 err |= __put_user((TARGET_NR_sigreturn), &frame->retcode[2]);
191 regs->pr = (unsigned long) frame->retcode;
192 }
193
194 if (err)
195 goto give_sigsegv;
196
197 /* Set up registers for signal handler */
198 regs->gregs[15] = frame_addr;
199 regs->gregs[4] = signal; /* Arg for signal handler */
200 regs->gregs[5] = 0;
201 regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc);
202 regs->pc = (unsigned long) ka->_sa_handler;
203
204 unlock_user_struct(frame, frame_addr, 1);
205 return;
206
207give_sigsegv:
208 unlock_user_struct(frame, frame_addr, 1);
209 force_sig(TARGET_SIGSEGV);
210}
211
212void setup_rt_frame(int sig, struct target_sigaction *ka,
213 target_siginfo_t *info,
214 target_sigset_t *set, CPUState *regs)
215{
216 struct target_rt_sigframe *frame;
217 abi_ulong frame_addr;
218 int i;
219 int err = 0;
220 int signal;
221
222 frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
223 if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0))
224 goto give_sigsegv;
225
226 signal = current_exec_domain_sig(sig);
227
228 err |= copy_siginfo_to_user(&frame->info, info);
229
230 /* Create the ucontext. */
231 err |= __put_user(0, &frame->uc.tuc_flags);
232 err |= __put_user(0, (unsigned long *)&frame->uc.tuc_link);
233 err |= __put_user((unsigned long)target_sigaltstack_used.ss_sp,
234 &frame->uc.tuc_stack.ss_sp);
235 err |= __put_user(sas_ss_flags(regs->gregs[15]),
236 &frame->uc.tuc_stack.ss_flags);
237 err |= __put_user(target_sigaltstack_used.ss_size,
238 &frame->uc.tuc_stack.ss_size);
239 err |= setup_sigcontext(&frame->uc.tuc_mcontext,
240 regs, set->sig[0]);
241 for(i = 0; i < TARGET_NSIG_WORDS; i++) {
242 err |= __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
243 }
244
245 /* Set up to return from userspace. If provided, use a stub
246 already in userspace. */
247 if (ka->sa_flags & TARGET_SA_RESTORER) {
248 regs->pr = (unsigned long) ka->sa_restorer;
249 } else {
250 /* Generate return code (system call to sigreturn) */
251 err |= __put_user(MOVW(2), &frame->retcode[0]);
252 err |= __put_user(TRAP_NOARG, &frame->retcode[1]);
253 err |= __put_user((TARGET_NR_rt_sigreturn), &frame->retcode[2]);
254 regs->pr = (unsigned long) frame->retcode;
255 }
256
257 if (err)
258 goto give_sigsegv;
259
260 /* Set up registers for signal handler */
261 regs->gregs[15] = frame_addr;
262 regs->gregs[4] = signal; /* Arg for signal handler */
263 regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info);
264 regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc);
265 regs->pc = (unsigned long) ka->_sa_handler;
266
267 unlock_user_struct(frame, frame_addr, 1);
268 return;
269
270give_sigsegv:
271 unlock_user_struct(frame, frame_addr, 1);
272 force_sig(TARGET_SIGSEGV);
273}
274
275long do_sigreturn(CPUState *regs)
276{
277 struct target_sigframe *frame;
278 abi_ulong frame_addr;
279 sigset_t blocked;
280 target_sigset_t target_set;
281 target_ulong r0;
282 int i;
283 int err = 0;
284
285#if defined(DEBUG_SIGNAL)
286 fprintf(stderr, "do_sigreturn\n");
287#endif
288 frame_addr = regs->gregs[15];
289 if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
290 goto badframe;
291
292 err |= __get_user(target_set.sig[0], &frame->sc.oldmask);
293 for(i = 1; i < TARGET_NSIG_WORDS; i++) {
294 err |= (__get_user(target_set.sig[i], &frame->extramask[i - 1]));
295 }
296
297 if (err)
298 goto badframe;
299
300 target_to_host_sigset_internal(&blocked, &target_set);
301 sigprocmask(SIG_SETMASK, &blocked, NULL);
302
303 if (restore_sigcontext(regs, &frame->sc, &r0))
304 goto badframe;
305
306 unlock_user_struct(frame, frame_addr, 0);
307 return r0;
308
309badframe:
310 unlock_user_struct(frame, frame_addr, 0);
311 force_sig(TARGET_SIGSEGV);
312 return 0;
313}
314
315long do_rt_sigreturn(CPUState *regs)
316{
317 struct target_rt_sigframe *frame;
318 abi_ulong frame_addr;
319 sigset_t blocked;
320 target_ulong r0;
321
322#if defined(DEBUG_SIGNAL)
323 fprintf(stderr, "do_rt_sigreturn\n");
324#endif
325 frame_addr = regs->gregs[15];
326 if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1))
327 goto badframe;
328
329 target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask);
330 sigprocmask(SIG_SETMASK, &blocked, NULL);
331
332 if (restore_sigcontext(regs, &frame->uc.tuc_mcontext, &r0))
333 goto badframe;
334
335 if (do_sigaltstack(frame_addr +
336 offsetof(struct target_rt_sigframe, uc.tuc_stack),
337 0, get_sp_from_cpustate(regs)) == -EFAULT)
338 goto badframe;
339
340 unlock_user_struct(frame, frame_addr, 0);
341 return r0;
342
343badframe:
344 unlock_user_struct(frame, frame_addr, 0);
345 force_sig(TARGET_SIGSEGV);
346 return 0;
347}