diff options
author | James Hogan <james.hogan@imgtec.com> | 2012-10-09 05:54:27 -0400 |
---|---|---|
committer | James Hogan <james.hogan@imgtec.com> | 2013-03-02 15:09:21 -0500 |
commit | 262d96b0deb44ed58823447825d6efa5dddb4108 (patch) | |
tree | 03712555691c86256a409ceccf529f5bb7ebb37c /arch | |
parent | c438b58e65462cfff172b396d03d6bc45c971fca (diff) |
metag: Signal handling
Add signal handling code for metag.
Signed-off-by: James Hogan <james.hogan@imgtec.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/metag/include/uapi/asm/sigcontext.h | 31 | ||||
-rw-r--r-- | arch/metag/include/uapi/asm/siginfo.h | 8 | ||||
-rw-r--r-- | arch/metag/kernel/signal.c | 344 |
3 files changed, 383 insertions, 0 deletions
diff --git a/arch/metag/include/uapi/asm/sigcontext.h b/arch/metag/include/uapi/asm/sigcontext.h new file mode 100644 index 000000000000..ef79a910c1c4 --- /dev/null +++ b/arch/metag/include/uapi/asm/sigcontext.h | |||
@@ -0,0 +1,31 @@ | |||
1 | #ifndef _ASM_METAG_SIGCONTEXT_H | ||
2 | #define _ASM_METAG_SIGCONTEXT_H | ||
3 | |||
4 | #include <asm/ptrace.h> | ||
5 | |||
6 | /* | ||
7 | * In a sigcontext structure we need to store the active state of the | ||
8 | * user process so that it does not get trashed when we call the signal | ||
9 | * handler. That not really the same as a user context that we are | ||
10 | * going to store on syscall etc. | ||
11 | */ | ||
12 | struct sigcontext { | ||
13 | struct user_gp_regs regs; /* needs to be first */ | ||
14 | |||
15 | /* | ||
16 | * Catch registers describing a memory fault. | ||
17 | * If USER_GP_REGS_STATUS_CATCH_BIT is set in regs.status then catch | ||
18 | * buffers have been saved and will be replayed on sigreturn. | ||
19 | * Clear that bit to discard the catch state instead of replaying it. | ||
20 | */ | ||
21 | struct user_cb_regs cb; | ||
22 | |||
23 | /* | ||
24 | * Read pipeline state. This will get restored on sigreturn. | ||
25 | */ | ||
26 | struct user_rp_state rp; | ||
27 | |||
28 | unsigned long oldmask; | ||
29 | }; | ||
30 | |||
31 | #endif | ||
diff --git a/arch/metag/include/uapi/asm/siginfo.h b/arch/metag/include/uapi/asm/siginfo.h new file mode 100644 index 000000000000..b2e0c8b62aef --- /dev/null +++ b/arch/metag/include/uapi/asm/siginfo.h | |||
@@ -0,0 +1,8 @@ | |||
1 | #ifndef _METAG_SIGINFO_H | ||
2 | #define _METAG_SIGINFO_H | ||
3 | |||
4 | #define __ARCH_SI_TRAPNO | ||
5 | |||
6 | #include <asm-generic/siginfo.h> | ||
7 | |||
8 | #endif | ||
diff --git a/arch/metag/kernel/signal.c b/arch/metag/kernel/signal.c new file mode 100644 index 000000000000..3be61cf0b147 --- /dev/null +++ b/arch/metag/kernel/signal.c | |||
@@ -0,0 +1,344 @@ | |||
1 | /* | ||
2 | * Copyright (C) 1991,1992 Linus Torvalds | ||
3 | * Copyright (C) 2005-2012 Imagination Technologies Ltd. | ||
4 | * | ||
5 | * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson | ||
6 | * | ||
7 | */ | ||
8 | |||
9 | #include <linux/sched.h> | ||
10 | #include <linux/mm.h> | ||
11 | #include <linux/smp.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/signal.h> | ||
14 | #include <linux/errno.h> | ||
15 | #include <linux/wait.h> | ||
16 | #include <linux/ptrace.h> | ||
17 | #include <linux/unistd.h> | ||
18 | #include <linux/stddef.h> | ||
19 | #include <linux/personality.h> | ||
20 | #include <linux/uaccess.h> | ||
21 | #include <linux/tracehook.h> | ||
22 | |||
23 | #include <asm/ucontext.h> | ||
24 | #include <asm/cacheflush.h> | ||
25 | #include <asm/switch.h> | ||
26 | #include <asm/syscall.h> | ||
27 | #include <asm/syscalls.h> | ||
28 | |||
29 | #define REG_FLAGS ctx.SaveMask | ||
30 | #define REG_RETVAL ctx.DX[0].U0 | ||
31 | #define REG_SYSCALL ctx.DX[0].U1 | ||
32 | #define REG_SP ctx.AX[0].U0 | ||
33 | #define REG_ARG1 ctx.DX[3].U1 | ||
34 | #define REG_ARG2 ctx.DX[3].U0 | ||
35 | #define REG_ARG3 ctx.DX[2].U1 | ||
36 | #define REG_PC ctx.CurrPC | ||
37 | #define REG_RTP ctx.DX[4].U1 | ||
38 | |||
39 | struct rt_sigframe { | ||
40 | struct siginfo info; | ||
41 | struct ucontext uc; | ||
42 | unsigned long retcode[2]; | ||
43 | }; | ||
44 | |||
45 | static int restore_sigcontext(struct pt_regs *regs, | ||
46 | struct sigcontext __user *sc) | ||
47 | { | ||
48 | int err; | ||
49 | |||
50 | /* Always make any pending restarted system calls return -EINTR */ | ||
51 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
52 | |||
53 | err = metag_gp_regs_copyin(regs, 0, sizeof(struct user_gp_regs), NULL, | ||
54 | &sc->regs); | ||
55 | if (!err) | ||
56 | err = metag_cb_regs_copyin(regs, 0, | ||
57 | sizeof(struct user_cb_regs), NULL, | ||
58 | &sc->cb); | ||
59 | if (!err) | ||
60 | err = metag_rp_state_copyin(regs, 0, | ||
61 | sizeof(struct user_rp_state), NULL, | ||
62 | &sc->rp); | ||
63 | |||
64 | /* This is a user-mode context. */ | ||
65 | regs->REG_FLAGS |= TBICTX_PRIV_BIT; | ||
66 | |||
67 | return err; | ||
68 | } | ||
69 | |||
70 | long sys_rt_sigreturn(void) | ||
71 | { | ||
72 | /* NOTE - Meta stack goes UPWARDS - so we wind the stack back */ | ||
73 | struct pt_regs *regs = current_pt_regs(); | ||
74 | struct rt_sigframe __user *frame; | ||
75 | sigset_t set; | ||
76 | |||
77 | frame = (__force struct rt_sigframe __user *)(regs->REG_SP - | ||
78 | sizeof(*frame)); | ||
79 | |||
80 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
81 | goto badframe; | ||
82 | |||
83 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
84 | goto badframe; | ||
85 | |||
86 | set_current_blocked(&set); | ||
87 | |||
88 | if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) | ||
89 | goto badframe; | ||
90 | |||
91 | if (restore_altstack(&frame->uc.uc_stack)) | ||
92 | goto badframe; | ||
93 | |||
94 | return regs->REG_RETVAL; | ||
95 | |||
96 | badframe: | ||
97 | force_sig(SIGSEGV, current); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | static int setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs, | ||
103 | unsigned long mask) | ||
104 | { | ||
105 | int err; | ||
106 | |||
107 | err = metag_gp_regs_copyout(regs, 0, sizeof(struct user_gp_regs), NULL, | ||
108 | &sc->regs); | ||
109 | |||
110 | if (!err) | ||
111 | err = metag_cb_regs_copyout(regs, 0, | ||
112 | sizeof(struct user_cb_regs), NULL, | ||
113 | &sc->cb); | ||
114 | if (!err) | ||
115 | err = metag_rp_state_copyout(regs, 0, | ||
116 | sizeof(struct user_rp_state), NULL, | ||
117 | &sc->rp); | ||
118 | |||
119 | /* OK, clear that cbuf flag in the old context, or our stored | ||
120 | * catch buffer will be restored when we go to call the signal | ||
121 | * handler. Also clear out the CBRP RA/RD pipe bit incase | ||
122 | * that is pending as well! | ||
123 | * Note that as we have already stored this context, these | ||
124 | * flags will get restored on sigreturn to their original | ||
125 | * state. | ||
126 | */ | ||
127 | regs->REG_FLAGS &= ~(TBICTX_XCBF_BIT | TBICTX_CBUF_BIT | | ||
128 | TBICTX_CBRP_BIT); | ||
129 | |||
130 | /* Clear out the LSM_STEP bits in case we are in the middle of | ||
131 | * and MSET/MGET. | ||
132 | */ | ||
133 | regs->ctx.Flags &= ~TXSTATUS_LSM_STEP_BITS; | ||
134 | |||
135 | err |= __put_user(mask, &sc->oldmask); | ||
136 | |||
137 | return err; | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * Determine which stack to use.. | ||
142 | */ | ||
143 | static void __user *get_sigframe(struct k_sigaction *ka, unsigned long sp, | ||
144 | size_t frame_size) | ||
145 | { | ||
146 | /* Meta stacks grows upwards */ | ||
147 | if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags(sp) == 0)) | ||
148 | sp = current->sas_ss_sp; | ||
149 | |||
150 | sp = (sp + 7) & ~7; /* 8byte align stack */ | ||
151 | |||
152 | return (void __user *)sp; | ||
153 | } | ||
154 | |||
155 | static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
156 | sigset_t *set, struct pt_regs *regs) | ||
157 | { | ||
158 | struct rt_sigframe __user *frame; | ||
159 | int err = -EFAULT; | ||
160 | unsigned long code; | ||
161 | |||
162 | frame = get_sigframe(ka, regs->REG_SP, sizeof(*frame)); | ||
163 | if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) | ||
164 | goto out; | ||
165 | |||
166 | err = copy_siginfo_to_user(&frame->info, info); | ||
167 | |||
168 | /* Create the ucontext. */ | ||
169 | err |= __put_user(0, &frame->uc.uc_flags); | ||
170 | err |= __put_user(0, (unsigned long __user *)&frame->uc.uc_link); | ||
171 | err |= __save_altstack(&frame->uc.uc_stack, regs->REG_SP); | ||
172 | err |= setup_sigcontext(&frame->uc.uc_mcontext, | ||
173 | regs, set->sig[0]); | ||
174 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
175 | |||
176 | if (err) | ||
177 | goto out; | ||
178 | |||
179 | /* Set up to return from userspace. */ | ||
180 | |||
181 | /* MOV D1Re0 (D1.0), #__NR_rt_sigreturn */ | ||
182 | code = 0x03000004 | (__NR_rt_sigreturn << 3); | ||
183 | err |= __put_user(code, (unsigned long __user *)(&frame->retcode[0])); | ||
184 | |||
185 | /* SWITCH #__METAG_SW_SYS */ | ||
186 | code = __METAG_SW_ENCODING(SYS); | ||
187 | err |= __put_user(code, (unsigned long __user *)(&frame->retcode[1])); | ||
188 | |||
189 | if (err) | ||
190 | goto out; | ||
191 | |||
192 | /* Set up registers for signal handler */ | ||
193 | regs->REG_RTP = (unsigned long) frame->retcode; | ||
194 | regs->REG_SP = (unsigned long) frame + sizeof(*frame); | ||
195 | regs->REG_ARG1 = sig; | ||
196 | regs->REG_ARG2 = (unsigned long) &frame->info; | ||
197 | regs->REG_ARG3 = (unsigned long) &frame->uc; | ||
198 | regs->REG_PC = (unsigned long) ka->sa.sa_handler; | ||
199 | |||
200 | pr_debug("SIG deliver (%s:%d): sp=%p pc=%08x pr=%08x\n", | ||
201 | current->comm, current->pid, frame, regs->REG_PC, | ||
202 | regs->REG_RTP); | ||
203 | |||
204 | /* Now pass size of 'new code' into sigtramp so we can do a more | ||
205 | * effective cache flush - directed rather than 'full flush'. | ||
206 | */ | ||
207 | flush_cache_sigtramp(regs->REG_RTP, sizeof(frame->retcode)); | ||
208 | out: | ||
209 | if (err) { | ||
210 | force_sigsegv(sig, current); | ||
211 | return -EFAULT; | ||
212 | } | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static void handle_signal(unsigned long sig, siginfo_t *info, | ||
217 | struct k_sigaction *ka, struct pt_regs *regs) | ||
218 | { | ||
219 | sigset_t *oldset = sigmask_to_save(); | ||
220 | |||
221 | /* Set up the stack frame */ | ||
222 | if (setup_rt_frame(sig, ka, info, oldset, regs)) | ||
223 | return; | ||
224 | |||
225 | signal_delivered(sig, info, ka, regs, test_thread_flag(TIF_SINGLESTEP)); | ||
226 | } | ||
227 | |||
228 | /* | ||
229 | * Notes for Meta. | ||
230 | * We have moved from the old 2.4.9 SH way of using syscall_nr (in the stored | ||
231 | * context) to passing in the syscall flag on the stack. | ||
232 | * This is because having syscall_nr in our context does not fit with TBX, and | ||
233 | * corrupted the stack. | ||
234 | */ | ||
235 | static int do_signal(struct pt_regs *regs, int syscall) | ||
236 | { | ||
237 | unsigned int retval = 0, continue_addr = 0, restart_addr = 0; | ||
238 | struct k_sigaction ka; | ||
239 | siginfo_t info; | ||
240 | int signr; | ||
241 | int restart = 0; | ||
242 | |||
243 | /* | ||
244 | * By the end of rt_sigreturn the context describes the point that the | ||
245 | * signal was taken (which may happen to be just before a syscall if | ||
246 | * it's already been restarted). This should *never* be mistaken for a | ||
247 | * system call in need of restarting. | ||
248 | */ | ||
249 | if (syscall == __NR_rt_sigreturn) | ||
250 | syscall = -1; | ||
251 | |||
252 | /* Did we come from a system call? */ | ||
253 | if (syscall >= 0) { | ||
254 | continue_addr = regs->REG_PC; | ||
255 | restart_addr = continue_addr - 4; | ||
256 | retval = regs->REG_RETVAL; | ||
257 | |||
258 | /* | ||
259 | * Prepare for system call restart. We do this here so that a | ||
260 | * debugger will see the already changed PC. | ||
261 | */ | ||
262 | switch (retval) { | ||
263 | case -ERESTART_RESTARTBLOCK: | ||
264 | restart = -2; | ||
265 | case -ERESTARTNOHAND: | ||
266 | case -ERESTARTSYS: | ||
267 | case -ERESTARTNOINTR: | ||
268 | ++restart; | ||
269 | regs->REG_PC = restart_addr; | ||
270 | break; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * Get the signal to deliver. When running under ptrace, at this point | ||
276 | * the debugger may change all our registers ... | ||
277 | */ | ||
278 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
279 | /* | ||
280 | * Depending on the signal settings we may need to revert the decision | ||
281 | * to restart the system call. But skip this if a debugger has chosen to | ||
282 | * restart at a different PC. | ||
283 | */ | ||
284 | if (regs->REG_PC != restart_addr) | ||
285 | restart = 0; | ||
286 | if (signr > 0) { | ||
287 | if (unlikely(restart)) { | ||
288 | if (retval == -ERESTARTNOHAND | ||
289 | || retval == -ERESTART_RESTARTBLOCK | ||
290 | || (retval == -ERESTARTSYS | ||
291 | && !(ka.sa.sa_flags & SA_RESTART))) { | ||
292 | regs->REG_RETVAL = -EINTR; | ||
293 | regs->REG_PC = continue_addr; | ||
294 | } | ||
295 | } | ||
296 | |||
297 | /* Whee! Actually deliver the signal. */ | ||
298 | handle_signal(signr, &info, &ka, regs); | ||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | /* Handlerless -ERESTART_RESTARTBLOCK re-enters via restart_syscall */ | ||
303 | if (unlikely(restart < 0)) | ||
304 | regs->REG_SYSCALL = __NR_restart_syscall; | ||
305 | |||
306 | /* | ||
307 | * If there's no signal to deliver, we just put the saved sigmask back. | ||
308 | */ | ||
309 | restore_saved_sigmask(); | ||
310 | |||
311 | return restart; | ||
312 | } | ||
313 | |||
314 | int do_work_pending(struct pt_regs *regs, unsigned int thread_flags, | ||
315 | int syscall) | ||
316 | { | ||
317 | do { | ||
318 | if (likely(thread_flags & _TIF_NEED_RESCHED)) { | ||
319 | schedule(); | ||
320 | } else { | ||
321 | if (unlikely(!user_mode(regs))) | ||
322 | return 0; | ||
323 | local_irq_enable(); | ||
324 | if (thread_flags & _TIF_SIGPENDING) { | ||
325 | int restart = do_signal(regs, syscall); | ||
326 | if (unlikely(restart)) { | ||
327 | /* | ||
328 | * Restart without handlers. | ||
329 | * Deal with it without leaving | ||
330 | * the kernel space. | ||
331 | */ | ||
332 | return restart; | ||
333 | } | ||
334 | syscall = -1; | ||
335 | } else { | ||
336 | clear_thread_flag(TIF_NOTIFY_RESUME); | ||
337 | tracehook_notify_resume(regs); | ||
338 | } | ||
339 | } | ||
340 | local_irq_disable(); | ||
341 | thread_flags = current_thread_info()->flags; | ||
342 | } while (thread_flags & _TIF_WORK_MASK); | ||
343 | return 0; | ||
344 | } | ||