Commit | Line | Data |
---|---|---|
6bc9a396 CL |
1 | /* |
2 | * arch/score/kernel/signal.c | |
3 | * | |
4 | * Score Processor version. | |
5 | * | |
6 | * Copyright (C) 2009 Sunplus Core Technology Co., Ltd. | |
7 | * Chen Liqin <liqin.chen@sunplusct.com> | |
8 | * Lennox Wu <lennox.wu@sunplusct.com> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, see the file COPYING, or write | |
22 | * to the Free Software Foundation, Inc., | |
23 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
24 | */ | |
25 | ||
26 | #include <linux/errno.h> | |
27 | #include <linux/signal.h> | |
28 | #include <linux/unistd.h> | |
29 | #include <linux/uaccess.h> | |
9fb24cc5 AB |
30 | |
31 | #include <asm/syscalls.h> | |
32 | #include <asm/ucontext.h> | |
6bc9a396 CL |
33 | |
34 | #include <asm/cacheflush.h> | |
35 | ||
36 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | |
37 | ||
38 | struct rt_sigframe { | |
39 | u32 rs_ass[4]; /* argument save space */ | |
40 | u32 rs_code[2]; /* signal trampoline */ | |
41 | struct siginfo rs_info; | |
42 | struct ucontext rs_uc; | |
43 | }; | |
44 | ||
45 | int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |
46 | { | |
47 | int err = 0; | |
48 | unsigned long reg; | |
49 | ||
50 | reg = regs->cp0_epc; err |= __put_user(reg, &sc->sc_pc); | |
51 | err |= __put_user(regs->cp0_psr, &sc->sc_psr); | |
52 | err |= __put_user(regs->cp0_condition, &sc->sc_condition); | |
53 | ||
54 | ||
55 | #define save_gp_reg(i) { \ | |
56 | reg = regs->regs[i]; \ | |
57 | err |= __put_user(reg, &sc->sc_regs[i]); \ | |
58 | } while (0) | |
59 | save_gp_reg(0); save_gp_reg(1); save_gp_reg(2); | |
60 | save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); | |
61 | save_gp_reg(6); save_gp_reg(7); save_gp_reg(8); | |
62 | save_gp_reg(9); save_gp_reg(10); save_gp_reg(11); | |
63 | save_gp_reg(12); save_gp_reg(13); save_gp_reg(14); | |
64 | save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); | |
65 | save_gp_reg(18); save_gp_reg(19); save_gp_reg(20); | |
66 | save_gp_reg(21); save_gp_reg(22); save_gp_reg(23); | |
67 | save_gp_reg(24); save_gp_reg(25); save_gp_reg(26); | |
68 | save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); | |
69 | #undef save_gp_reg | |
70 | ||
71 | reg = regs->ceh; err |= __put_user(reg, &sc->sc_mdceh); | |
72 | reg = regs->cel; err |= __put_user(reg, &sc->sc_mdcel); | |
73 | err |= __put_user(regs->cp0_ecr, &sc->sc_ecr); | |
74 | err |= __put_user(regs->cp0_ema, &sc->sc_ema); | |
75 | ||
76 | return err; | |
77 | } | |
78 | ||
79 | int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) | |
80 | { | |
81 | int err = 0; | |
82 | u32 reg; | |
83 | ||
84 | err |= __get_user(regs->cp0_epc, &sc->sc_pc); | |
85 | err |= __get_user(regs->cp0_condition, &sc->sc_condition); | |
86 | ||
87 | err |= __get_user(reg, &sc->sc_mdceh); | |
88 | regs->ceh = (int) reg; | |
89 | err |= __get_user(reg, &sc->sc_mdcel); | |
90 | regs->cel = (int) reg; | |
91 | ||
92 | err |= __get_user(reg, &sc->sc_psr); | |
93 | regs->cp0_psr = (int) reg; | |
94 | err |= __get_user(reg, &sc->sc_ecr); | |
95 | regs->cp0_ecr = (int) reg; | |
96 | err |= __get_user(reg, &sc->sc_ema); | |
97 | regs->cp0_ema = (int) reg; | |
98 | ||
99 | #define restore_gp_reg(i) do { \ | |
100 | err |= __get_user(reg, &sc->sc_regs[i]); \ | |
101 | regs->regs[i] = reg; \ | |
102 | } while (0) | |
103 | restore_gp_reg(0); restore_gp_reg(1); restore_gp_reg(2); | |
104 | restore_gp_reg(3); restore_gp_reg(4); restore_gp_reg(5); | |
105 | restore_gp_reg(6); restore_gp_reg(7); restore_gp_reg(8); | |
106 | restore_gp_reg(9); restore_gp_reg(10); restore_gp_reg(11); | |
107 | restore_gp_reg(12); restore_gp_reg(13); restore_gp_reg(14); | |
108 | restore_gp_reg(15); restore_gp_reg(16); restore_gp_reg(17); | |
109 | restore_gp_reg(18); restore_gp_reg(19); restore_gp_reg(20); | |
110 | restore_gp_reg(21); restore_gp_reg(22); restore_gp_reg(23); | |
111 | restore_gp_reg(24); restore_gp_reg(25); restore_gp_reg(26); | |
112 | restore_gp_reg(27); restore_gp_reg(28); restore_gp_reg(29); | |
113 | #undef restore_gp_reg | |
114 | ||
115 | return err; | |
116 | } | |
117 | ||
118 | /* | |
119 | * Determine which stack to use.. | |
120 | */ | |
121 | void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, | |
122 | size_t frame_size) | |
123 | { | |
124 | unsigned long sp; | |
125 | ||
126 | /* Default to using normal stack */ | |
127 | sp = regs->regs[0]; | |
128 | sp -= 32; | |
129 | ||
130 | /* This is the X/Open sanctioned signal stack switching. */ | |
131 | if ((ka->sa.sa_flags & SA_ONSTACK) && (!on_sig_stack(sp))) | |
132 | sp = current->sas_ss_sp + current->sas_ss_size; | |
133 | ||
a1f8213b | 134 | return (void __user*)((sp - frame_size) & ~7); |
6bc9a396 CL |
135 | } |
136 | ||
0402c91a | 137 | int score_sigaltstack(struct pt_regs *regs) |
6bc9a396 | 138 | { |
a1f8213b AB |
139 | const stack_t __user *uss = (const stack_t __user *) regs->regs[4]; |
140 | stack_t __user *uoss = (stack_t __user *) regs->regs[5]; | |
6bc9a396 CL |
141 | unsigned long usp = regs->regs[0]; |
142 | ||
143 | return do_sigaltstack(uss, uoss, usp); | |
144 | } | |
145 | ||
0402c91a | 146 | void score_rt_sigreturn(struct pt_regs *regs) |
6bc9a396 CL |
147 | { |
148 | struct rt_sigframe __user *frame; | |
149 | sigset_t set; | |
150 | stack_t st; | |
151 | int sig; | |
152 | ||
153 | frame = (struct rt_sigframe __user *) regs->regs[0]; | |
154 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | |
155 | goto badframe; | |
156 | if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set))) | |
157 | goto badframe; | |
158 | ||
159 | sigdelsetmask(&set, ~_BLOCKABLE); | |
160 | spin_lock_irq(¤t->sighand->siglock); | |
161 | current->blocked = set; | |
162 | recalc_sigpending(); | |
163 | spin_unlock_irq(¤t->sighand->siglock); | |
164 | ||
165 | sig = restore_sigcontext(regs, &frame->rs_uc.uc_mcontext); | |
166 | if (sig < 0) | |
167 | goto badframe; | |
168 | else if (sig) | |
169 | force_sig(sig, current); | |
170 | ||
171 | if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st))) | |
172 | goto badframe; | |
173 | ||
174 | /* It is more difficult to avoid calling this function than to | |
175 | call it and ignore errors. */ | |
176 | do_sigaltstack((stack_t __user *)&st, NULL, regs->regs[0]); | |
177 | ||
178 | __asm__ __volatile__( | |
179 | "mv\tr0, %0\n\t" | |
180 | "la\tr8, syscall_exit\n\t" | |
181 | "br\tr8\n\t" | |
182 | : : "r" (regs) : "r8"); | |
183 | ||
184 | badframe: | |
185 | force_sig(SIGSEGV, current); | |
186 | } | |
187 | ||
188 | int setup_rt_frame(struct k_sigaction *ka, struct pt_regs *regs, | |
189 | int signr, sigset_t *set, siginfo_t *info) | |
190 | { | |
a1f8213b | 191 | struct rt_sigframe __user *frame; |
6bc9a396 CL |
192 | int err = 0; |
193 | ||
194 | frame = get_sigframe(ka, regs, sizeof(*frame)); | |
195 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | |
196 | goto give_sigsegv; | |
197 | ||
198 | /* | |
199 | * Set up the return code ... | |
200 | * | |
201 | * li v0, __NR_rt_sigreturn | |
202 | * syscall | |
203 | */ | |
204 | err |= __put_user(0x87788000 + __NR_rt_sigreturn*2, | |
205 | frame->rs_code + 0); | |
206 | err |= __put_user(0x80008002, frame->rs_code + 1); | |
207 | flush_cache_sigtramp((unsigned long) frame->rs_code); | |
208 | ||
209 | err |= copy_siginfo_to_user(&frame->rs_info, info); | |
210 | err |= __put_user(0, &frame->rs_uc.uc_flags); | |
211 | err |= __put_user(0, &frame->rs_uc.uc_link); | |
a1f8213b | 212 | err |= __put_user((void __user *)current->sas_ss_sp, |
6bc9a396 CL |
213 | &frame->rs_uc.uc_stack.ss_sp); |
214 | err |= __put_user(sas_ss_flags(regs->regs[0]), | |
215 | &frame->rs_uc.uc_stack.ss_flags); | |
216 | err |= __put_user(current->sas_ss_size, | |
217 | &frame->rs_uc.uc_stack.ss_size); | |
218 | err |= setup_sigcontext(regs, &frame->rs_uc.uc_mcontext); | |
219 | err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set)); | |
220 | ||
221 | if (err) | |
222 | goto give_sigsegv; | |
223 | ||
224 | regs->regs[0] = (unsigned long) frame; | |
225 | regs->regs[3] = (unsigned long) frame->rs_code; | |
226 | regs->regs[4] = signr; | |
227 | regs->regs[5] = (unsigned long) &frame->rs_info; | |
228 | regs->regs[6] = (unsigned long) &frame->rs_uc; | |
229 | regs->regs[29] = (unsigned long) ka->sa.sa_handler; | |
230 | regs->cp0_epc = (unsigned long) ka->sa.sa_handler; | |
231 | ||
232 | return 0; | |
233 | ||
234 | give_sigsegv: | |
235 | if (signr == SIGSEGV) | |
236 | ka->sa.sa_handler = SIG_DFL; | |
237 | force_sig(SIGSEGV, current); | |
238 | return -EFAULT; | |
239 | } | |
240 | ||
241 | int handle_signal(unsigned long sig, siginfo_t *info, | |
242 | struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs) | |
243 | { | |
244 | int ret; | |
245 | ||
246 | if (regs->is_syscall) { | |
247 | switch (regs->regs[4]) { | |
248 | case ERESTART_RESTARTBLOCK: | |
249 | case ERESTARTNOHAND: | |
250 | regs->regs[4] = EINTR; | |
251 | break; | |
252 | case ERESTARTSYS: | |
253 | if (!(ka->sa.sa_flags & SA_RESTART)) { | |
254 | regs->regs[4] = EINTR; | |
255 | break; | |
256 | } | |
257 | case ERESTARTNOINTR: | |
258 | regs->regs[4] = regs->orig_r4; | |
259 | regs->regs[7] = regs->orig_r7; | |
260 | regs->cp0_epc -= 8; | |
261 | } | |
262 | ||
263 | regs->is_syscall = 0; | |
264 | } | |
265 | ||
266 | /* | |
267 | * Set up the stack frame | |
268 | */ | |
269 | ret = setup_rt_frame(ka, regs, sig, oldset, info); | |
270 | ||
271 | spin_lock_irq(¤t->sighand->siglock); | |
272 | sigorsets(¤t->blocked, ¤t->blocked, &ka->sa.sa_mask); | |
273 | if (!(ka->sa.sa_flags & SA_NODEFER)) | |
274 | sigaddset(¤t->blocked, sig); | |
275 | recalc_sigpending(); | |
276 | spin_unlock_irq(¤t->sighand->siglock); | |
277 | ||
278 | return ret; | |
279 | } | |
280 | ||
0402c91a | 281 | void do_signal(struct pt_regs *regs) |
6bc9a396 CL |
282 | { |
283 | struct k_sigaction ka; | |
284 | sigset_t *oldset; | |
285 | siginfo_t info; | |
286 | int signr; | |
287 | ||
288 | /* | |
289 | * We want the common case to go fast, which is why we may in certain | |
290 | * cases get here from kernel mode. Just return without doing anything | |
291 | * if so. | |
292 | */ | |
293 | if (!user_mode(regs)) | |
294 | return; | |
295 | ||
296 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | |
297 | oldset = ¤t->saved_sigmask; | |
298 | else | |
299 | oldset = ¤t->blocked; | |
300 | ||
301 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | |
302 | if (signr > 0) { | |
303 | /* Actually deliver the signal. */ | |
304 | if (handle_signal(signr, &info, &ka, oldset, regs) == 0) { | |
305 | /* | |
306 | * A signal was successfully delivered; the saved | |
307 | * sigmask will have been stored in the signal frame, | |
308 | * and will be restored by sigreturn, so we can simply | |
309 | * clear the TIF_RESTORE_SIGMASK flag. | |
310 | */ | |
311 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | |
312 | clear_thread_flag(TIF_RESTORE_SIGMASK); | |
313 | } | |
314 | ||
315 | return; | |
316 | } | |
317 | ||
318 | if (regs->is_syscall) { | |
319 | if (regs->regs[4] == ERESTARTNOHAND || | |
320 | regs->regs[4] == ERESTARTSYS || | |
321 | regs->regs[4] == ERESTARTNOINTR) { | |
322 | regs->regs[4] = regs->orig_r4; | |
323 | regs->regs[7] = regs->orig_r7; | |
324 | regs->cp0_epc -= 8; | |
325 | } | |
326 | ||
327 | if (regs->regs[4] == ERESTART_RESTARTBLOCK) { | |
328 | regs->regs[27] = __NR_restart_syscall; | |
329 | regs->regs[4] = regs->orig_r4; | |
330 | regs->regs[7] = regs->orig_r7; | |
331 | regs->cp0_epc -= 8; | |
332 | } | |
333 | ||
334 | regs->is_syscall = 0; /* Don't deal with this again. */ | |
335 | } | |
336 | ||
337 | /* | |
338 | * If there's no signal to deliver, we just put the saved sigmask | |
339 | * back | |
340 | */ | |
341 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | |
342 | clear_thread_flag(TIF_RESTORE_SIGMASK); | |
343 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | |
344 | } | |
345 | } | |
346 | ||
347 | /* | |
348 | * notification of userspace execution resumption | |
349 | * - triggered by the TIF_WORK_MASK flags | |
350 | */ | |
351 | asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused, | |
352 | __u32 thread_info_flags) | |
353 | { | |
354 | /* deal with pending signal delivery */ | |
355 | if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) | |
356 | do_signal(regs); | |
357 | } |