xref: /openbmc/qemu/linux-user/sh4/signal.c (revision 4d6d8a05)
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  */
19 #include "qemu/osdep.h"
20 #include "qemu.h"
21 #include "user-internals.h"
22 #include "signal-common.h"
23 #include "linux-user/trace.h"
24 
25 /*
26  * code and data structures from linux kernel:
27  * include/asm-sh/sigcontext.h
28  * arch/sh/kernel/signal.c
29  */
30 
31 struct target_sigcontext {
32     target_ulong  oldmask;
33 
34     /* CPU registers */
35     target_ulong  sc_gregs[16];
36     target_ulong  sc_pc;
37     target_ulong  sc_pr;
38     target_ulong  sc_sr;
39     target_ulong  sc_gbr;
40     target_ulong  sc_mach;
41     target_ulong  sc_macl;
42 
43     /* FPU registers */
44     target_ulong  sc_fpregs[16];
45     target_ulong  sc_xfpregs[16];
46     unsigned int sc_fpscr;
47     unsigned int sc_fpul;
48     unsigned int sc_ownedfp;
49 };
50 
51 struct target_sigframe
52 {
53     struct target_sigcontext sc;
54     target_ulong extramask[TARGET_NSIG_WORDS-1];
55 };
56 
57 
58 struct target_ucontext {
59     target_ulong tuc_flags;
60     struct target_ucontext *tuc_link;
61     target_stack_t tuc_stack;
62     struct target_sigcontext tuc_mcontext;
63     target_sigset_t tuc_sigmask;        /* mask last for extensibility */
64 };
65 
66 struct target_rt_sigframe
67 {
68     struct target_siginfo info;
69     struct target_ucontext uc;
70 };
71 
72 
73 #define MOVW(n)  (0x9300|((n)-2)) /* Move mem word at PC+n to R3 */
74 #define TRAP_NOARG 0xc310         /* Syscall w/no args (NR in R3) SH3/4 */
75 
get_sigframe(struct target_sigaction * ka,unsigned long sp,size_t frame_size)76 static abi_ulong get_sigframe(struct target_sigaction *ka,
77                               unsigned long sp, size_t frame_size)
78 {
79     sp = target_sigsp(sp, ka);
80 
81     return (sp - frame_size) & -8ul;
82 }
83 
84 /*
85  * Notice when we're in the middle of a gUSA region and reset.
86  * Note that this will only occur when #CF_PARALLEL is unset, as we
87  * will translate such sequences differently in a parallel context.
88  */
unwind_gusa(CPUSH4State * regs)89 static void unwind_gusa(CPUSH4State *regs)
90 {
91     /* If the stack pointer is sufficiently negative, and we haven't
92        completed the sequence, then reset to the entry to the region.  */
93     /* ??? The SH4 kernel checks for and address above 0xC0000000.
94        However, the page mappings in qemu linux-user aren't as restricted
95        and we wind up with the normal stack mapped above 0xF0000000.
96        That said, there is no reason why the kernel should be allowing
97        a gUSA region that spans 1GB.  Use a tighter check here, for what
98        can actually be enabled by the immediate move.  */
99     if (regs->gregs[15] >= -128u && regs->pc < regs->gregs[0]) {
100         /* Reset the PC to before the gUSA region, as computed from
101            R0 = region end, SP = -(region size), plus one more for the
102            insn that actually initializes SP to the region size.  */
103         regs->pc = regs->gregs[0] + regs->gregs[15] - 2;
104 
105         /* Reset the SP to the saved version in R1.  */
106         regs->gregs[15] = regs->gregs[1];
107     } else if (regs->gregs[15] >= -128u && regs->pc == regs->gregs[0]) {
108         /* If we are on the last instruction of a gUSA region, we must reset
109            the SP, otherwise we would be pushing the signal context to
110            invalid memory.  */
111         regs->gregs[15] = regs->gregs[1];
112     } else if (regs->flags & TB_FLAG_DELAY_SLOT) {
113         /* If we are in a delay slot, push the previous instruction.  */
114         regs->pc -= 2;
115     }
116 }
117 
setup_sigcontext(struct target_sigcontext * sc,CPUSH4State * regs,unsigned long mask)118 static void setup_sigcontext(struct target_sigcontext *sc,
119                              CPUSH4State *regs, unsigned long mask)
120 {
121     int i;
122 
123 #define COPY(x)         __put_user(regs->x, &sc->sc_##x)
124     COPY(gregs[0]); COPY(gregs[1]);
125     COPY(gregs[2]); COPY(gregs[3]);
126     COPY(gregs[4]); COPY(gregs[5]);
127     COPY(gregs[6]); COPY(gregs[7]);
128     COPY(gregs[8]); COPY(gregs[9]);
129     COPY(gregs[10]); COPY(gregs[11]);
130     COPY(gregs[12]); COPY(gregs[13]);
131     COPY(gregs[14]); COPY(gregs[15]);
132     COPY(gbr); COPY(mach);
133     COPY(macl); COPY(pr);
134     COPY(sr); COPY(pc);
135 #undef COPY
136 
137     for (i=0; i<16; i++) {
138         __put_user(regs->fregs[i], &sc->sc_fpregs[i]);
139     }
140     __put_user(regs->fpscr, &sc->sc_fpscr);
141     __put_user(regs->fpul, &sc->sc_fpul);
142 
143     /* non-iBCS2 extensions.. */
144     __put_user(mask, &sc->oldmask);
145 }
146 
restore_sigcontext(CPUSH4State * regs,struct target_sigcontext * sc)147 static void restore_sigcontext(CPUSH4State *regs, struct target_sigcontext *sc)
148 {
149     int i;
150 
151 #define COPY(x)         __get_user(regs->x, &sc->sc_##x)
152     COPY(gregs[0]); COPY(gregs[1]);
153     COPY(gregs[2]); COPY(gregs[3]);
154     COPY(gregs[4]); COPY(gregs[5]);
155     COPY(gregs[6]); COPY(gregs[7]);
156     COPY(gregs[8]); COPY(gregs[9]);
157     COPY(gregs[10]); COPY(gregs[11]);
158     COPY(gregs[12]); COPY(gregs[13]);
159     COPY(gregs[14]); COPY(gregs[15]);
160     COPY(gbr); COPY(mach);
161     COPY(macl); COPY(pr);
162     COPY(sr); COPY(pc);
163 #undef COPY
164 
165     for (i=0; i<16; i++) {
166         __get_user(regs->fregs[i], &sc->sc_fpregs[i]);
167     }
168     __get_user(regs->fpscr, &sc->sc_fpscr);
169     __get_user(regs->fpul, &sc->sc_fpul);
170 
171     regs->tra = -1;         /* disable syscall checks */
172     regs->flags = 0;
173 }
174 
setup_frame(int sig,struct target_sigaction * ka,target_sigset_t * set,CPUSH4State * regs)175 void setup_frame(int sig, struct target_sigaction *ka,
176                  target_sigset_t *set, CPUSH4State *regs)
177 {
178     struct target_sigframe *frame;
179     abi_ulong frame_addr;
180     int i;
181 
182     unwind_gusa(regs);
183 
184     frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
185     trace_user_setup_frame(regs, frame_addr);
186     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
187         goto give_sigsegv;
188     }
189 
190     setup_sigcontext(&frame->sc, regs, set->sig[0]);
191 
192     for (i = 0; i < TARGET_NSIG_WORDS - 1; i++) {
193         __put_user(set->sig[i + 1], &frame->extramask[i]);
194     }
195 
196     /* Set up to return from userspace.  If provided, use a stub
197        already in userspace.  */
198     if (ka->sa_flags & TARGET_SA_RESTORER) {
199         regs->pr = ka->sa_restorer;
200     } else {
201         regs->pr = default_sigreturn;
202     }
203 
204     /* Set up registers for signal handler */
205     regs->gregs[15] = frame_addr;
206     regs->gregs[4] = sig; /* Arg for signal handler */
207     regs->gregs[5] = 0;
208     regs->gregs[6] = frame_addr += offsetof(typeof(*frame), sc);
209     regs->pc = (unsigned long) ka->_sa_handler;
210     regs->flags &= ~(TB_FLAG_DELAY_SLOT_MASK | TB_FLAG_GUSA_MASK);
211 
212     unlock_user_struct(frame, frame_addr, 1);
213     return;
214 
215 give_sigsegv:
216     unlock_user_struct(frame, frame_addr, 1);
217     force_sigsegv(sig);
218 }
219 
setup_rt_frame(int sig,struct target_sigaction * ka,target_siginfo_t * info,target_sigset_t * set,CPUSH4State * regs)220 void setup_rt_frame(int sig, struct target_sigaction *ka,
221                     target_siginfo_t *info,
222                     target_sigset_t *set, CPUSH4State *regs)
223 {
224     struct target_rt_sigframe *frame;
225     abi_ulong frame_addr;
226     int i;
227 
228     unwind_gusa(regs);
229 
230     frame_addr = get_sigframe(ka, regs->gregs[15], sizeof(*frame));
231     trace_user_setup_rt_frame(regs, frame_addr);
232     if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) {
233         goto give_sigsegv;
234     }
235 
236     frame->info = *info;
237 
238     /* Create the ucontext.  */
239     __put_user(0, &frame->uc.tuc_flags);
240     __put_user(0, (unsigned long *)&frame->uc.tuc_link);
241     target_save_altstack(&frame->uc.tuc_stack, regs);
242     setup_sigcontext(&frame->uc.tuc_mcontext,
243                      regs, set->sig[0]);
244     for(i = 0; i < TARGET_NSIG_WORDS; i++) {
245         __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]);
246     }
247 
248     /* Set up to return from userspace.  If provided, use a stub
249        already in userspace.  */
250     if (ka->sa_flags & TARGET_SA_RESTORER) {
251         regs->pr = ka->sa_restorer;
252     } else {
253         regs->pr = default_rt_sigreturn;
254     }
255 
256     /* Set up registers for signal handler */
257     regs->gregs[15] = frame_addr;
258     regs->gregs[4] = sig; /* Arg for signal handler */
259     regs->gregs[5] = frame_addr + offsetof(typeof(*frame), info);
260     regs->gregs[6] = frame_addr + offsetof(typeof(*frame), uc);
261     regs->pc = (unsigned long) ka->_sa_handler;
262     regs->flags &= ~(TB_FLAG_DELAY_SLOT_MASK | TB_FLAG_GUSA_MASK);
263 
264     unlock_user_struct(frame, frame_addr, 1);
265     return;
266 
267 give_sigsegv:
268     unlock_user_struct(frame, frame_addr, 1);
269     force_sigsegv(sig);
270 }
271 
do_sigreturn(CPUSH4State * regs)272 long do_sigreturn(CPUSH4State *regs)
273 {
274     struct target_sigframe *frame;
275     abi_ulong frame_addr;
276     sigset_t blocked;
277     target_sigset_t target_set;
278     int i;
279 
280     frame_addr = regs->gregs[15];
281     trace_user_do_sigreturn(regs, frame_addr);
282     if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
283         goto badframe;
284     }
285 
286     __get_user(target_set.sig[0], &frame->sc.oldmask);
287     for(i = 1; i < TARGET_NSIG_WORDS; i++) {
288         __get_user(target_set.sig[i], &frame->extramask[i - 1]);
289     }
290 
291     target_to_host_sigset_internal(&blocked, &target_set);
292     set_sigmask(&blocked);
293 
294     restore_sigcontext(regs, &frame->sc);
295 
296     unlock_user_struct(frame, frame_addr, 0);
297     return -QEMU_ESIGRETURN;
298 
299 badframe:
300     unlock_user_struct(frame, frame_addr, 0);
301     force_sig(TARGET_SIGSEGV);
302     return -QEMU_ESIGRETURN;
303 }
304 
do_rt_sigreturn(CPUSH4State * regs)305 long do_rt_sigreturn(CPUSH4State *regs)
306 {
307     struct target_rt_sigframe *frame;
308     abi_ulong frame_addr;
309     sigset_t blocked;
310 
311     frame_addr = regs->gregs[15];
312     trace_user_do_rt_sigreturn(regs, frame_addr);
313     if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) {
314         goto badframe;
315     }
316 
317     target_to_host_sigset(&blocked, &frame->uc.tuc_sigmask);
318     set_sigmask(&blocked);
319 
320     restore_sigcontext(regs, &frame->uc.tuc_mcontext);
321     target_restore_altstack(&frame->uc.tuc_stack, regs);
322 
323     unlock_user_struct(frame, frame_addr, 0);
324     return -QEMU_ESIGRETURN;
325 
326 badframe:
327     unlock_user_struct(frame, frame_addr, 0);
328     force_sig(TARGET_SIGSEGV);
329     return -QEMU_ESIGRETURN;
330 }
331 
setup_sigtramp(abi_ulong sigtramp_page)332 void setup_sigtramp(abi_ulong sigtramp_page)
333 {
334     uint16_t *tramp = lock_user(VERIFY_WRITE, sigtramp_page, 2 * 6, 0);
335     assert(tramp != NULL);
336 
337     default_sigreturn = sigtramp_page;
338     __put_user(MOVW(2), &tramp[0]);
339     __put_user(TRAP_NOARG, &tramp[1]);
340     __put_user(TARGET_NR_sigreturn, &tramp[2]);
341 
342     default_rt_sigreturn = sigtramp_page + 6;
343     __put_user(MOVW(2), &tramp[3]);
344     __put_user(TRAP_NOARG, &tramp[4]);
345     __put_user(TARGET_NR_rt_sigreturn, &tramp[5]);
346 
347     unlock_user(tramp, sigtramp_page, 2 * 6);
348 }
349